1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        pdffontsubsettruetype.cpp
3 // Purpose:
4 // Author:      Ulrich Telle
5 // Created:     2006-11-20
6 // Copyright:   (c) Ulrich Telle
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 /// \file pdffontsubsettruetype.cpp Implementation of subset support for TrueType fonts
11 
12 // For compilers that support precompilation, includes <wx.h>.
13 #include <wx/wxprec.h>
14 
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20 #include <wx/wx.h>
21 #endif
22 
23 // includes
24 
25 #include "wx/pdfarraytypes.h"
26 #include "wx/pdffontsubsettruetype.h"
27 
wxPdfFontSubsetTrueType(const wxString & fileName,int fontIndex,bool isMacCoreText)28 wxPdfFontSubsetTrueType::wxPdfFontSubsetTrueType(const wxString& fileName,
29                                                  int fontIndex, bool isMacCoreText)
30   : wxPdfFontParserTrueType()
31 
32 {
33   m_fileName = fileName;
34   m_fontIndex = fontIndex;
35   m_includeCmap = false;
36   m_isMacCoreText = isMacCoreText;
37 }
38 
~wxPdfFontSubsetTrueType()39 wxPdfFontSubsetTrueType::~wxPdfFontSubsetTrueType()
40 {
41   delete [] m_newGlyfTable;
42   delete [] m_newLocaTableStream;
43   delete [] m_newLocaTable;
44   delete [] m_locaTable;
45 }
46 
47 #if defined(__WXMAC__)
48 #if wxPDFMACOSX_HAS_CORE_TEXT
49 
50 void
SetCTFontRef(const wxFont & font)51 wxPdfFontSubsetTrueType::SetCTFontRef(const wxFont& font)
52 {
53 #if wxCHECK_VERSION(2,9,0)
54   m_fontRef = font.OSXGetCTFont();
55 #else // wxWidgets 2.8.x
56   m_fontRef = (const void*) font.MacGetCTFont();
57 #endif
58 }
59 
60 #endif
61 #endif
62 
63 wxMemoryOutputStream*
CreateSubset(wxInputStream * inFont,wxPdfSortedArrayInt * usedGlyphs,bool includeCmap)64 wxPdfFontSubsetTrueType::CreateSubset(wxInputStream* inFont, wxPdfSortedArrayInt* usedGlyphs, bool includeCmap)
65 {
66   m_inFont = inFont;
67   m_usedGlyphs = usedGlyphs;
68   m_includeCmap = includeCmap;
69   m_outFont = NULL;
70 
71   m_inFont->SeekI(0);
72   m_directoryOffset = 0;
73   wxString mainTag = ReadString(4);
74   if (mainTag == wxS("ttcf"))
75   {
76     SkipBytes(4);
77     int dirCount = ReadInt();
78     if (m_fontIndex < dirCount)
79     {
80       m_fontIndex = 0;
81     }
82     SkipBytes(m_fontIndex * 4);
83     m_directoryOffset = ReadInt();
84   }
85 
86   if (ReadTableDirectory())
87   {
88     if (ReadLocaTable())
89     {
90       if (CheckGlyphs())
91       {
92         CreateNewTables();
93         WriteSubsetFont();
94       }
95     }
96   }
97   return m_outFont;
98 }
99 
100 static const int HEAD_LOCA_FORMAT_OFFSET = 51;
101 
102 bool
ReadLocaTable()103 wxPdfFontSubsetTrueType::ReadLocaTable()
104 {
105   bool ok = false;
106 
107   wxPdfTableDirectoryEntry* tableLocation;
108   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("head"));
109   if (entry != m_tableDirectory->end())
110   {
111     tableLocation = entry->second;
112     LockTable(wxS("head"));
113     m_inFont->SeekI(tableLocation->m_offset + HEAD_LOCA_FORMAT_OFFSET);
114     m_locaTableIsShort = (ReadUShort() == 0);
115     ReleaseTable();
116 
117     entry = m_tableDirectory->find(wxS("loca"));
118     if (entry != m_tableDirectory->end())
119     {
120       tableLocation = entry->second;
121       LockTable(wxS("loca"));
122       m_inFont->SeekI(tableLocation->m_offset);
123       m_locaTableSize = (m_locaTableIsShort) ? tableLocation->m_length / 2 : tableLocation->m_length / 4;
124       m_locaTable = new int[m_locaTableSize];
125       size_t k;
126       for (k = 0; k < m_locaTableSize; k++)
127       {
128         m_locaTable[k] = (m_locaTableIsShort) ? ReadUShort() * 2 : ReadInt();
129       }
130       ok = true;
131       ReleaseTable();
132     }
133     else
134     {
135       wxLogError(wxString(wxS("wxPdfFontSubsetTrueType::ReadLocaTable: ")) +
136                  wxString::Format(_("Table 'loca' does not exist in '%s'."), m_fileName.c_str()));
137     }
138   }
139   else
140   {
141     wxLogError(wxString(wxS("wxPdfFontSubsetTrueType::ReadLocaTable: ")) +
142                wxString::Format(_("Table 'head' does not exist in '%s'."), m_fileName.c_str()));
143   }
144   return ok;
145 }
146 
147 bool
CheckGlyphs()148 wxPdfFontSubsetTrueType::CheckGlyphs()
149 {
150   bool ok = false;
151   wxPdfTableDirectoryEntry* tableLocation;
152   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("glyf"));
153   if (entry != m_tableDirectory->end())
154   {
155     tableLocation = entry->second;
156     LockTable(wxS("glyf"));
157     int glyph0 = 0;
158     if (m_usedGlyphs->Index(glyph0) == wxNOT_FOUND)
159     {
160       m_usedGlyphs->Add(glyph0);
161     }
162     m_glyfTableOffset = tableLocation->m_offset;
163     size_t k;
164     for (k = 0; k < m_usedGlyphs->GetCount(); k++)
165     {
166       FindGlyphComponents(m_usedGlyphs->Item(k));
167     }
168     ok = true;
169     ReleaseTable();
170   }
171   else
172   {
173     wxLogError(wxString(wxS("wxPdfFontSubsetTrueType::CheckGlyphs: ")) +
174                wxString::Format(_("Table 'glyf' does not exist in '%s'."), m_fileName.c_str()));
175   }
176   return ok;
177 }
178 
179 static const int FLAG_HAS_WORD_ARGS       =   1;
180 static const int FLAG_HAS_SCALE           =   8;
181 static const int FLAG_HAS_MORE_COMPONENTS =  32;
182 static const int FLAG_HAS_SCALE_XY        =  64;
183 static const int FLAG_HAS_TWO_BY_TWO      = 128;
184 
185 void
FindGlyphComponents(int glyph)186 wxPdfFontSubsetTrueType::FindGlyphComponents(int glyph)
187 {
188 
189   int glyphOffset = m_locaTable[glyph];
190   if (glyphOffset == m_locaTable[glyph + 1])
191   {
192     // glyph has no contour
193     return;
194   }
195   m_inFont->SeekI(m_glyfTableOffset + glyphOffset);
196   int numberContours = ReadShort();
197   if (numberContours >= 0)
198   {
199     // glyph has contours (not glyph components)
200     return;
201   }
202   SkipBytes(8);
203   for (;;)
204   {
205     int flags = ReadUShort();
206     int glyphComponent = (int) ReadUShort();
207     if (m_usedGlyphs->Index(glyphComponent) == wxNOT_FOUND)
208     {
209       m_usedGlyphs->Add(glyphComponent);
210     }
211     if ((flags & FLAG_HAS_MORE_COMPONENTS) == 0)
212     {
213       // glyph does not have more components
214       return;
215     }
216     int skip = ((flags & FLAG_HAS_WORD_ARGS) != 0) ? 4 : 2;
217     if ((flags & FLAG_HAS_SCALE) != 0)
218     {
219       skip += 2;
220     }
221     else if ((flags & FLAG_HAS_SCALE_XY) != 0)
222     {
223       skip += 4;
224     }
225     if ((flags & FLAG_HAS_TWO_BY_TWO) != 0)
226     {
227       skip += 8;
228     }
229     SkipBytes(skip);
230   }
231 }
232 
233 void
CreateNewTables()234 wxPdfFontSubsetTrueType::CreateNewTables()
235 {
236   size_t usedGlyphCount = m_usedGlyphs->GetCount();
237   size_t k;
238   m_newLocaTable = new int[m_locaTableSize];
239 
240   // Calculate new 'glyf' table size
241   m_newGlyfTableSize = 0;
242   for (k = 0; k < usedGlyphCount; k++)
243   {
244     int glyph = (*m_usedGlyphs)[k];
245     m_newGlyfTableSize += m_locaTable[glyph + 1] - m_locaTable[glyph];
246   }
247   m_newGlyfTableRealSize = m_newGlyfTableSize;
248   m_newGlyfTableSize =  (m_newGlyfTableSize + 3) & (~3);
249   m_newGlyfTable = new char[m_newGlyfTableSize];
250 
251   // Initialize new 'glyf' table
252   for (k = 0; k < m_newGlyfTableSize; k++)
253   {
254     m_newGlyfTable[k] = 0;
255   }
256 
257   // Copy used glyphs to new 'glyf' table
258   LockTable(wxS("glyf"));
259   int newGlyphOffset = 0;
260   size_t glyphIndex = 0;
261   for (k = 0; k < m_locaTableSize; k++)
262   {
263     m_newLocaTable[k] = newGlyphOffset;
264     if (glyphIndex < usedGlyphCount && (size_t)(*m_usedGlyphs)[glyphIndex] == k)
265     {
266       glyphIndex++;
267       m_newLocaTable[k] = newGlyphOffset;
268       int glyphOffset = m_locaTable[k];
269       int glyphLength = m_locaTable[k + 1] - glyphOffset;
270       if (glyphLength > 0)
271       {
272         m_inFont->SeekI(m_glyfTableOffset + glyphOffset);
273         m_inFont->Read(&m_newGlyfTable[newGlyphOffset], glyphLength);
274         newGlyphOffset += glyphLength;
275       }
276     }
277   }
278   ReleaseTable();
279 
280   // Convert new 'loca' table to byte stream
281   m_locaTableRealSize = (m_locaTableIsShort) ? m_locaTableSize * 2 : m_locaTableSize * 4;
282   m_newLocaTableStreamSize = (m_locaTableRealSize + 3) & (~3);
283   m_newLocaTableStream = new char[m_newLocaTableStreamSize];
284   for (k = 0; k < m_newLocaTableStreamSize; k++)
285   {
286     m_newLocaTableStream[k] = 0;
287   }
288   int offset = 0;
289   for (k = 0; k < m_locaTableSize; k++)
290   {
291     if (m_locaTableIsShort)
292     {
293       WriteShortToBuffer(m_newLocaTable[k] / 2, &m_newLocaTableStream[offset]);
294       offset += 2;
295     }
296     else
297     {
298       WriteIntToBuffer(m_newLocaTable[k], &m_newLocaTableStream[offset]);
299       offset += 4;
300     }
301   }
302 }
303 
304 static const wxChar* tableNamesDefault[] = {
305   wxS("cvt "), wxS("fpgm"), wxS("glyf"), wxS("head"),
306   wxS("hhea"), wxS("hmtx"), wxS("loca"), wxS("maxp"), wxS("prep"),
307   NULL
308 };
309 static const wxChar* tableNamesCmap[] = {
310   wxS("cmap"), wxS("cvt "), wxS("fpgm"), wxS("glyf"), wxS("head"),
311   wxS("hhea"), wxS("hmtx"), wxS("loca"), wxS("maxp"), wxS("prep"),
312   NULL
313 };
314 #if 0
315 static const wxChar* tableNamesExtra[] = {
316   wxS("OS/2"), wxS("cmap"), wxS("cvt "), wxS("fpgm"), wxS("glyf"), wxS("head"),
317   wxS("hhea"), wxS("hmtx"), wxS("loca"), wxS("maxp"), wxS("name"), wxS("prep"),
318   NULL
319 };
320 #endif
321 
322 //                                   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
323 static const int entrySelectors[] = {0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4};
324 
325 void
WriteSubsetFont()326 wxPdfFontSubsetTrueType::WriteSubsetFont()
327 {
328   wxPdfTableDirectoryEntry* tableLocation;
329   wxPdfTableDirectory::iterator entry;
330   int k;
331   const wxChar** tableNames = (m_includeCmap) ? tableNamesCmap : tableNamesDefault;
332   int tableCount = 0;
333   while (tableNames[tableCount] != NULL)
334   {
335     tableCount++;
336   }
337 
338   int tablesUsed = 2;
339   int tableLength = 0;
340   for (k = 0; k < tableCount; k++)
341   {
342     wxString name = tableNames[k];
343     if (name != wxS("glyf") && name != wxS("loca"))
344     {
345       entry = m_tableDirectory->find(name);
346       if (entry != m_tableDirectory->end())
347       {
348         tableLocation = entry->second;
349         ++tablesUsed;
350       }
351     }
352   }
353 
354   // Write header and table directory
355   int tableOffset = 16 * tablesUsed + 12;
356   m_outFont = new wxMemoryOutputStream();
357   WriteInt(0x00010000);
358   WriteShort(tablesUsed);
359 
360   int selector = entrySelectors[tablesUsed];
361   WriteShort((1 << selector) * 16);
362   WriteShort(selector);
363   WriteShort((tablesUsed - (1 << selector)) * 16);
364   for (k = 0; k < tableCount; ++k)
365   {
366     wxString name = tableNames[k];
367     entry = m_tableDirectory->find(name);
368     if (entry != m_tableDirectory->end())
369     {
370       tableLocation = entry->second;
371       WriteString(name);
372       if (name == wxS("glyf"))
373       {
374         WriteInt(CalculateChecksum(m_newGlyfTable, m_newGlyfTableSize));
375         tableLength = (int) m_newGlyfTableRealSize;
376       }
377       else if (name == wxS("loca"))
378       {
379         WriteInt(CalculateChecksum(m_newLocaTableStream, m_newLocaTableStreamSize));
380         tableLength = (int) m_locaTableRealSize;
381       }
382       else
383       {
384         WriteInt(tableLocation->m_checksum);
385         tableLength = tableLocation->m_length;
386       }
387       WriteInt(tableOffset);
388       WriteInt(tableLength);
389       tableOffset += (tableLength + 3) & (~3);
390     }
391   }
392 
393   // Write new 'loca' and 'glyf' tables and copy all others
394   for (k = 0; k < tableCount; k++)
395   {
396     wxString name = tableNames[k];
397     entry = m_tableDirectory->find(name);
398     if (entry != m_tableDirectory->end())
399     {
400       tableLocation = entry->second;
401       if (name == wxS("glyf"))
402       {
403         m_outFont->Write(m_newGlyfTable, m_newGlyfTableSize);
404       }
405       else if (name == wxS("loca"))
406       {
407         m_outFont->Write(m_newLocaTableStream, m_newLocaTableStreamSize);
408       }
409       else
410       {
411         char buffer[1024];
412         LockTable(name);
413         m_inFont->SeekI(tableLocation->m_offset);
414         tableLength = tableLocation->m_length;
415         int bufferLength;
416         while (tableLength > 0)
417         {
418           bufferLength = (tableLength > 1024) ? 1024 : tableLength;
419           m_inFont->Read(buffer, bufferLength);
420           m_outFont->Write(buffer, bufferLength);
421           tableLength -= bufferLength;
422         }
423         int paddingLength = ((tableLocation->m_length + 3) & (~3)) - tableLocation->m_length;
424         if (paddingLength > 0)
425         {
426           int pad;
427           for (pad = 0; pad < paddingLength; pad++)
428           {
429             buffer[pad] = 0;
430           }
431           m_outFont->Write(buffer, paddingLength);
432         }
433         ReleaseTable();
434       }
435     }
436   }
437 }
438 
439 void
WriteShort(int n)440 wxPdfFontSubsetTrueType::WriteShort(int n)
441 {
442   char buffer[2];
443   WriteShortToBuffer(n, buffer);
444   m_outFont->Write(buffer, 2);
445 }
446 
447 void
WriteShortToBuffer(int n,char buffer[2])448 wxPdfFontSubsetTrueType::WriteShortToBuffer(int n, char buffer[2])
449 {
450   buffer[0] = (char)((n >> 8) & 0xff);
451   buffer[1] = (char)((n     ) & 0xff);
452 }
453 
454 void
WriteInt(int n)455 wxPdfFontSubsetTrueType::WriteInt(int n)
456 {
457   char buffer[4];
458   WriteIntToBuffer(n, buffer);
459   m_outFont->Write(buffer, 4);
460 }
461 
462 void
WriteIntToBuffer(int n,char buffer[4])463 wxPdfFontSubsetTrueType::WriteIntToBuffer(int n, char buffer[4])
464 {
465   buffer[0] = (char)((n >> 24) & 0xff);
466   buffer[1] = (char)((n >> 16) & 0xff);
467   buffer[2] = (char)((n >>  8) & 0xff);
468   buffer[3] = (char)((n      ) & 0xff);
469 }
470 
471 void
WriteString(const wxString & s)472 wxPdfFontSubsetTrueType::WriteString(const wxString& s)
473 {
474   size_t len = s.Length();;
475   char* buffer = new char[len];
476   size_t j;
477   for (j = 0; j < len; j++)
478   {
479     buffer[j] = s[j];
480   }
481   m_outFont->Write(buffer, len);
482   delete [] buffer;
483 }
484