1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        pdffontparsertruetype.cpp
3 // Purpose:
4 // Author:      Ulrich Telle
5 // Created:     2009-03-04
6 // Copyright:   (c) Ulrich Telle
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 /// \file pdffontparsertruetype.cpp Implementation of TrueType/OpenType font parsing support classes
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 #include <wx/filename.h>
25 #include <wx/filesys.h>
26 #include <wx/fontutil.h>
27 #include <wx/zstream.h>
28 
29 #include "wx/pdffontdataopentype.h"
30 #include "wx/pdffontdatatruetype.h"
31 #include "wx/pdffontparsertruetype.h"
32 #include "wx/pdfencoding.h"
33 
34 // --- Windows specific loading of TrueType font data
35 
36 #ifdef __WXMSW__
37 
38 #include <stdio.h>
39 #include <windows.h>
40 
41 #include "wx/msw/private.h"
42 
43 static USHORT
ReadUShort(BYTE * p)44 ReadUShort(BYTE* p)
45 {
46   return ((USHORT)p[0] <<  8) + ((USHORT)p[1]);
47 }
48 
49 static DWORD
ReadDWord(BYTE * p)50 ReadDWord(BYTE* p)
51 {
52   return ((LONG)p[0] << 24) + ((LONG)p[1] << 16) + ((LONG)p[2] << 8) + ((LONG)p[3]);
53 }
54 
ReadTag(BYTE * p)55 static DWORD ReadTag(BYTE* p)
56 {
57   return ((LONG)p[3] << 24) + ((LONG)p[2] << 16) + ((LONG)p[1] << 8) + ((LONG)p[0]);
58 }
59 
60 static void
WriteDWord(BYTE * p,DWORD dw)61 WriteDWord(BYTE* p, DWORD dw)
62 {
63   p[0] = (BYTE)((dw >> 24 ) & 0xFF);
64   p[1] = (BYTE)((dw >> 16 ) & 0xFF);
65   p[2] = (BYTE)((dw >>  8 ) & 0xFF);
66   p[3] = (BYTE)((dw       ) & 0xFF);
67 }
68 
69 static DWORD
RoundUpToDWord(DWORD val)70 RoundUpToDWord(DWORD val)
71 {
72   return (val + 3) & ~3;
73 }
74 
75 #define TTC_FILE 0x66637474
76 
77 const DWORD sizeOfFixedHeader     = 12;
78 const DWORD offsetOfTableCount    = 4;
79 
80 const DWORD sizeOfTableEntry      = 16;
81 const DWORD offsetOfTableTag      = 0;
82 const DWORD offsetOfTableChecksum = 4;
83 const DWORD offsetOfTableOffset   = 8;
84 const DWORD offsetOfTableLength   = 12;
85 
86 static bool
ExtractFontData(HDC hdc,DWORD & fontDataSize,BYTE * & fontData)87 ExtractFontData(HDC hdc, DWORD& fontDataSize, BYTE*& fontData)
88 {
89   bool ok = false;
90   fontData = NULL;
91   fontDataSize = 0;
92 
93   // Check if font is in TrueType collection
94   if (GetFontData(hdc, TTC_FILE, 0, NULL, 0) != GDI_ERROR)
95   {
96     // Extract font data from TTC (TrueType Collection)
97     // 1. Read number of tables in the font (ushort value at offset 2)
98     USHORT nTables;
99     BYTE uShortBuf[2];
100     if (GetFontData(hdc, 0, 4, uShortBuf, 2) == GDI_ERROR)
101     {
102       wxLogError(wxString(wxS("ExtractFontData: ")) +
103                  wxString::Format(_("Error %d on reading number of tables from TTC."), GetLastError()));
104       return false;
105     }
106     nTables = ReadUShort(uShortBuf);
107 
108     // 2. Calculate memory needed for the whole font header and read it into buffer
109     DWORD headerSize = sizeOfFixedHeader + nTables * sizeOfTableEntry;
110     BYTE* fontHeader = new BYTE[headerSize];
111     if (!fontHeader)
112     {
113       wxLogError(wxString(wxS("ExtractFontData: ")) +
114                  wxString(_("Out of memory while extracting from TTC.")));
115       return false;
116     }
117     if (GetFontData(hdc, 0, 0, fontHeader, headerSize) == GDI_ERROR)
118     {
119       delete [] fontHeader;
120       wxLogError(wxString(wxS("ExtractFontData: ")) +
121                  wxString::Format(_("Error %d on reading font header from TTC."), GetLastError()));
122       return false;
123     }
124 
125     // 3. Calculate total font size.
126     //    Tables are padded to 4-byte boundaries, so length should be rounded up to dword.
127     DWORD bufferSize = headerSize;
128     USHORT i;
129     for (i = 0; i < nTables; i++)
130     {
131       DWORD tableLength = ReadDWord(fontHeader + sizeOfFixedHeader + i * sizeOfTableEntry + offsetOfTableLength);
132       if (i < nTables - 1)
133       {
134         bufferSize += RoundUpToDWord(tableLength);
135       }
136       else
137       {
138         bufferSize += tableLength;
139       }
140     }
141 
142     // 4. Copying header into target buffer.
143     //    Patch offsets with correct values while copying data.
144     BYTE* buffer = new BYTE[bufferSize];
145     if (buffer == NULL)
146     {
147       delete [] fontHeader;
148       wxLogError(wxString(wxS("ExtractFontData: ")) +
149                  wxString(_("Out of memory while extracting from TTC.")));
150       return false;
151     }
152     memcpy(buffer, fontHeader, headerSize);
153 
154     // 5. Get table data from GDI, write it into known place
155     //    inside target buffer and fix offset value.
156     DWORD runningOffset = headerSize;
157 
158     for (i = 0; i < nTables; i++)
159     {
160       BYTE* entryData  = fontHeader + sizeOfFixedHeader + i * sizeOfTableEntry;
161       DWORD tableTag    = ReadTag(entryData + offsetOfTableTag);
162       DWORD tableLength = ReadDWord(entryData + offsetOfTableLength);
163 
164       // Write new offset for this table.
165       WriteDWord(buffer + sizeOfFixedHeader + i * sizeOfTableEntry + offsetOfTableOffset, runningOffset);
166 
167       // Get font data from GDI and place it into target buffer
168       if (GetFontData(hdc, tableTag, 0, buffer + runningOffset, tableLength) == GDI_ERROR)
169       {
170         delete [] buffer;
171         delete [] fontHeader;
172         wxLogError(wxString(wxS("ExtractFontData: ")) +
173                    wxString::Format(_("Error %d on reading table data from TTC."), GetLastError()));
174         return false;
175       }
176       runningOffset += tableLength;
177 
178       // Pad tables (except last) with zero's
179       if (i < nTables - 1)
180       {
181         while (runningOffset % 4 != 0)
182         {
183           buffer[runningOffset] = 0;
184           ++runningOffset;
185         }
186       }
187     }
188     delete [] fontHeader;
189     fontDataSize = bufferSize;
190     fontData     = buffer;
191     ok = true;
192   }
193   else
194   {
195     // Check if font is TrueType
196     DWORD bufferSize = GetFontData(hdc, 0, 0, NULL, 0);
197     if (bufferSize != GDI_ERROR)
198     {
199       BYTE* buffer = new BYTE[bufferSize];
200       if (buffer != NULL)
201       {
202         ok = (GetFontData(hdc, 0, 0, buffer, bufferSize) != GDI_ERROR);
203         if (ok)
204         {
205           fontDataSize = bufferSize;
206           fontData = buffer;
207         }
208         else
209         {
210           delete [] buffer;
211           wxLogError(wxString(wxS("ExtractFontData: ")) +
212                      wxString::Format(_("Error %d on extracting font data."), GetLastError()));
213         }
214       }
215       else
216       {
217         wxLogError(wxString(wxS("ExtractFontData: ")) +
218                    wxString(_("Out of memory.")));
219       }
220     }
221   }
222   return ok;
223 }
224 
225 wxMemoryInputStream*
LoadTrueTypeFontStream(const wxFont & font)226 wxPdfFontParserTrueType::LoadTrueTypeFontStream(const wxFont& font)
227 {
228   wxMemoryInputStream* fontStream = NULL;
229   LOGFONT lf = font.GetNativeFontInfo()->lf;
230 
231   HDC hdc = CreateCompatibleDC(0);
232   HFONT hFont = CreateFontIndirect(&lf);
233   /*HFONT oldfont = (HFONT)*/ SelectObject(hdc, hFont);
234 
235   DWORD fontDataSize;
236   BYTE* fontData;
237   bool ok = ExtractFontData(hdc, fontDataSize, fontData);
238   if (ok)
239   {
240     wxMemoryOutputStream oStream;
241     oStream.Write(fontData, fontDataSize);
242     oStream.Close();
243     fontStream = new wxMemoryInputStream(oStream);
244     delete [] fontData;
245   }
246   DeleteDC(hdc);
247   DeleteObject(hFont);
248 
249   return fontStream;
250 }
251 
252 #endif
253 
254 // --- Parsing of TrueType and OpenType
255 
256 // The components of table 'head'.
257 class wxPdfFontHeader
258 {
259 public:
260   int   m_flags;
261   int   m_unitsPerEm;
262   short m_xMin;
263   short m_yMin;
264   short m_xMax;
265   short m_yMax;
266   int   m_macStyle;
267 };
268 
269 // The components of table 'hhea'.
270 class wxPdfHorizontalHeader
271 {
272 public:
273   short m_ascender;
274   short m_descender;
275   short m_lineGap;
276   int   m_advanceWidthMax;
277   short m_minLeftSideBearing;
278   short m_minRightSideBearing;
279   short m_xMaxExtent;
280   short m_caretSlopeRise;
281   short m_caretSlopeRun;
282   int   m_numberOfHMetrics;
283 };
284 
285 // The components of table 'OS/2'.
286 class wxPdfWindowsMetrics
287 {
288 public:
289   short m_xAvgCharWidth;
290   int   m_usWeightClass;
291   int   m_usWidthClass;
292   short m_fsType;
293   short m_ySubscriptXSize;
294   short m_ySubscriptYSize;
295   short m_ySubscriptXOffset;
296   short m_ySubscriptYOffset;
297   short m_ySuperscriptXSize;
298   short m_ySuperscriptYSize;
299   short m_ySuperscriptXOffset;
300   short m_ySuperscriptYOffset;
301   short m_yStrikeoutSize;
302   short m_yStrikeoutPosition;
303   short m_sFamilyClass;
304   char  m_panose[10];
305   char  m_achVendID[4];
306   int   m_fsSelection;
307   int   m_usFirstCharIndex;
308   int   m_usLastCharIndex;
309   short m_sTypoAscender;
310   short m_sTypoDescender;
311   short m_sTypoLineGap;
312   int   m_usWinAscent;
313   int   m_usWinDescent;
314   int   m_ulCodePageRange1;
315   int   m_ulCodePageRange2;
316   int   m_sxHeight;
317   int   m_sCapHeight;
318 };
319 
320 #if 0
321 static const wxChar codePages[] =
322 {
323   /*  0 */ "1252 Latin 1",                 // wxFONTENCODING_CP1252
324   /*  1 */ "1250 Latin 2: Eastern Europe", // wxFONTENCODING_CP1250
325   /*  2 */ "1251 Cyrillic",                // wxFONTENCODING_CP1251
326   /*  3 */ "1253 Greek",                   // wxFONTENCODING_CP1253
327   /*  4 */ "1254 Turkish",                 // wxFONTENCODING_CP1254
328   /*  5 */ "1255 Hebrew",                  // wxFONTENCODING_CP1255
329   /*  6 */ "1256 Arabic",                  // wxFONTENCODING_CP1256
330   /*  7 */ "1257 Windows Baltic",          // wxFONTENCODING_CP1257
331   /*  8 */ "1258 Vietnamese",              //
332   /*  9 */ null,
333   /* 10 */ null,
334   /* 11 */ null,
335   /* 12 */ null,
336   /* 13 */ null,
337   /* 14 */ null,
338   /* 15 */ null,
339   /* 16 */ "874 Thai",                     // wxFONTENCODING_CP874
340   /* 17 */ "932 JIS/Japan",                // wxFONTENCODING_CP932
341   /* 18 */ "936 Chinese: Simplified",      // wxFONTENCODING_CP936
342   /* 19 */ "949 Korean Wansung",           // wxFONTENCODING_CP949
343   /* 20 */ "950 Chinese: Traditional",     // wxFONTENCODING_CP950
344   /* 21 */ "1361 Korean Johab",            //
345   /* 22 */ null,
346   /* 23 */ null,
347   /* 24 */ null,
348   /* 25 */ null,
349   /* 26 */ null,
350   /* 27 */ null,
351   /* 28 */ null,
352   /* 29 */ "Macintosh Character Set (US Roman)", //
353   /* 30 */ "OEM Character Set",                  //
354   /* 31 */ "Symbol Character Set",               //
355   /*  0 */ null,
356   /*  1 */ null,
357   /*  2 */ null,
358   /*  3 */ null,
359   /*  4 */ null,
360   /*  5 */ null,
361   /*  6 */ null,
362   /*  7 */ null,
363   /*  8 */ null,
364   /*  9 */ null,
365   /* 10 */ null,
366   /* 11 */ null,
367   /* 12 */ null,
368   /* 13 */ null,
369   /* 14 */ null,
370   /* 15 */ null,
371   /* 16 */ "869 IBM Greek",                   //
372   /* 17 */ "866 MS-DOS Russian",              // wxFONTENCODING_CP866
373   /* 18 */ "865 MS-DOS Nordic",               //
374   /* 19 */ "864 Arabic",                      //
375   /* 20 */ "863 MS-DOS Canadian French",      //
376   /* 21 */ "862 Hebrew",                      //
377   /* 22 */ "861 MS-DOS Icelandic",            //
378   /* 23 */ "860 MS-DOS Portuguese",           //
379   /* 24 */ "857 IBM Turkish",                 //
380   /* 25 */ "855 IBM Cyrillic",                // wxFONTENCODING_CP855
381   /* 26 */ "852 Latin 2",                     // wxFONTENCODING_CP852
382   /* 27 */ "775 MS-DOS Baltic",               //
383   /* 28 */ "737 Greek; former 437 G",         //
384   /* 29 */ "708 Arabic; ASMO 708",            //
385   /* 30 */ "850 WE/Latin 1",                  // wxFONTENCODING_CP850
386   /* 31 */ "437 US"                           // wxFONTENCODING_CP437
387 };
388 #endif
389 
wxPdfFontParserTrueType()390 wxPdfFontParserTrueType::wxPdfFontParserTrueType()
391   : wxPdfFontParser()
392 {
393   m_tableDirectory = new wxPdfTableDirectory();
394   m_isFixedPitch = false;
395   m_cmap10 = NULL;
396   m_cmap31 = NULL;
397   m_cmapExt = NULL;
398   m_kp = NULL;
399   m_isMacCoreText = false;
400   m_savedStream = NULL;
401 }
402 
~wxPdfFontParserTrueType()403 wxPdfFontParserTrueType::~wxPdfFontParserTrueType()
404 {
405   wxPdfCMap::iterator cMapIter;
406   if (m_cmap10 != NULL)
407   {
408     for (cMapIter = m_cmap10->begin(); cMapIter != m_cmap10->end(); cMapIter++)
409     {
410       if (cMapIter->second != NULL)
411       {
412         delete cMapIter->second;
413       }
414     }
415     delete m_cmap10;
416   }
417   if (m_cmap31 != NULL)
418   {
419     for (cMapIter = m_cmap31->begin(); cMapIter != m_cmap31->end(); cMapIter++)
420     {
421       if (cMapIter->second != NULL)
422       {
423         delete cMapIter->second;
424       }
425     }
426     delete m_cmap31;
427   }
428   if (m_cmapExt != NULL)
429   {
430     for (cMapIter = m_cmapExt->begin(); cMapIter != m_cmapExt->end(); cMapIter++)
431     {
432       if (cMapIter->second != NULL)
433       {
434         delete cMapIter->second;
435       }
436     }
437     delete m_cmapExt;
438   }
439 
440   ClearTableDirectory();
441   delete m_tableDirectory;
442 }
443 
444 void
ClearTableDirectory()445 wxPdfFontParserTrueType::ClearTableDirectory()
446 {
447   wxPdfTableDirectory::iterator entry = m_tableDirectory->begin();
448   for (entry = m_tableDirectory->begin(); entry != m_tableDirectory->end(); entry++)
449   {
450     if (entry->second != NULL)
451     {
452       delete entry->second;
453       entry->second = NULL;
454     }
455   }
456 }
457 
458 void
LockTable(const wxString & tableName)459 wxPdfFontParserTrueType::LockTable(const wxString& tableName)
460 {
461 #if defined(__WXMAC__)
462 #if wxPDFMACOSX_HAS_CORE_TEXT
463   if (m_isMacCoreText)
464   {
465     wxCharBuffer asciiTableName = tableName.ToAscii();
466     const char* localTableName = asciiTableName;
467     CTFontTableTag tableTag = localTableName[0] << 24 |
468                               localTableName[1] << 16 |
469                               localTableName[2] << 8 |
470                               localTableName[3];
471     m_tableRef.reset(CTFontCopyTable(m_fontRef, tableTag, 0));
472     const UInt8* tableData = CFDataGetBytePtr(m_tableRef);
473     CFIndex      tableLen  = CFDataGetLength(m_tableRef);
474     m_savedStream = m_inFont;
475     m_inFont = new wxMemoryInputStream((const char*) tableData, (size_t) tableLen);
476   }
477 #else
478   wxUnusedVar(tableName);
479 #endif
480 #else
481   wxUnusedVar(tableName);
482 #endif
483 }
484 
485 void
ReleaseTable()486 wxPdfFontParserTrueType::ReleaseTable()
487 {
488 #if defined(__WXMAC__)
489 #if wxPDFMACOSX_HAS_CORE_TEXT
490   if (m_isMacCoreText)
491   {
492     delete m_inFont;
493     m_inFont = m_savedStream;
494   }
495 #endif
496 #endif
497 }
498 
499 int
CalculateChecksum(const char * b,size_t length)500 wxPdfFontParserTrueType::CalculateChecksum(const char* b, size_t length)
501 {
502   size_t len = length / 4;
503   int d0 = 0;
504   int d1 = 0;
505   int d2 = 0;
506   int d3 = 0;
507   size_t ptr = 0;
508   size_t k;
509   for (k = 0; k < len; ++k)
510   {
511     d3 += (int)b[ptr++] & 0xff;
512     d2 += (int)b[ptr++] & 0xff;
513     d1 += (int)b[ptr++] & 0xff;
514     d0 += (int)b[ptr++] & 0xff;
515   }
516   return d0 + (d1 << 8) + (d2 << 16) + (d3 << 24);
517 }
518 
519 int
GetCollectionFontCount(const wxString & fontFileName)520 wxPdfFontParserTrueType::GetCollectionFontCount(const wxString& fontFileName)
521 {
522   int count = 0;
523   wxFileName fileName(fontFileName);
524   wxFileSystem fs;
525 
526   wxFSFile* fontFile = fs.OpenFile(wxFileSystem::FileNameToURL(fileName));
527   if (fontFile != NULL)
528   {
529     m_inFont = fontFile->GetStream();
530     m_inFont->SeekI(0);
531 
532     // Check for TrueType collection
533     if (fileName.GetExt().Lower().IsSameAs(wxS("ttc")))
534     {
535       wxString mainTag = ReadString(4);
536       if (mainTag == wxS("ttcf"))
537       {
538         SkipBytes(4);
539         count = ReadInt();
540       }
541     }
542     delete fontFile;
543   }
544   return count;
545 }
546 
547 wxPdfFontData*
IdentifyFont(const wxString & fontFileName,int fontIndex)548 wxPdfFontParserTrueType::IdentifyFont(const wxString& fontFileName, int fontIndex)
549 {
550   bool ok = true;
551   wxPdfFontData* fontData = NULL;
552   m_fileName = fontFileName;
553   wxFileName fileName(fontFileName);
554   wxFileSystem fs;
555 
556   // Open font file
557   wxFSFile* fontFile = fs.OpenFile(wxFileSystem::FileNameToURL(fileName));
558   if (fontFile != NULL)
559   {
560     m_inFont = fontFile->GetStream();
561     m_inFont->SeekI(0);
562 
563     // Check for TrueType collection
564     if (fileName.GetExt().Lower().IsSameAs(wxS("ttc")))
565     {
566       if (fontIndex >= 0)
567       {
568         wxString mainTag = ReadString(4);
569         if (mainTag == wxS("ttcf"))
570         {
571           SkipBytes(4);
572           int dirCount = ReadInt();
573           if (fontIndex < dirCount)
574           {
575             SkipBytes(fontIndex * 4);
576             m_directoryOffset = ReadInt();
577           }
578           else
579           {
580             wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
581                        wxString::Format(_("Font index %d out of range for font file '%s'."), fontIndex, fontFileName.c_str()));
582             ok = false;
583           }
584         }
585         else
586         {
587           wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: '")) +
588                      wxString::Format(_("Font file '%s' not a valid TrueType collection (TTC) file."), fontFileName.c_str()));
589           ok = false;
590         }
591       }
592       else
593       {
594         wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
595                    wxString::Format(_("Font index %d out of range for font file '%s'."), fontIndex, fontFileName.c_str()));
596         ok = false;
597       }
598     }
599     else
600     {
601       m_directoryOffset = 0;
602       fontIndex = 0;
603     }
604 
605     // Identify single font
606     if (ok)
607     {
608       fontData = IdentifyFont();
609       if (fontData != NULL)
610       {
611         fontData->SetFontFileName(m_fileName);
612         fontData->SetFontIndex(fontIndex);
613       }
614       else
615       {
616         wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
617                    wxString::Format(_("Reading of font directory failed for font file '%s'."), fontFileName.c_str()));
618       }
619     }
620     delete fontFile;
621   }
622   else
623   {
624     wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
625                wxString::Format(_("Font file '%s' not accessible."), fontFileName.c_str()));
626   }
627   return fontData;
628 }
629 
630 #ifdef __WXMSW__
631 wxPdfFontData*
IdentifyFont(const wxFont & font)632 wxPdfFontParserTrueType::IdentifyFont(const wxFont& font)
633 {
634   bool ok = true;
635   wxPdfFontData* fontData = NULL;
636   m_fileName = wxEmptyString;
637 
638   wxMemoryInputStream* fontStream = LoadTrueTypeFontStream(font);
639   if (fontStream != NULL)
640   {
641     m_inFont = fontStream;
642     m_inFont->SeekI(0);
643 
644     m_directoryOffset = 0;
645 
646     // Identify single font
647     if (ok)
648     {
649       fontData = IdentifyFont();
650       if (fontData != NULL)
651       {
652         fontData->SetFont(font);
653         fontData->SetFontIndex(0);
654       }
655       else
656       {
657         wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
658                    wxString::Format(_("Reading of font directory failed for font file '%s'."), font.GetFaceName().c_str()));
659       }
660     }
661     delete fontStream;
662   }
663   else
664   {
665     wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
666                wxString::Format(_("Font file '%s' not accessible."), font.GetFaceName().c_str()));
667   }
668   return fontData;
669 }
670 #endif
671 
672 #if defined(__WXMAC__)
673 wxPdfFontData*
IdentifyFont(const wxFont & font)674 wxPdfFontParserTrueType::IdentifyFont(const wxFont& font)
675 {
676   wxPdfFontData* fontData = NULL;
677 #if wxPDFMACOSX_HAS_CORE_TEXT
678 #if wxCHECK_VERSION(2,9,0)
679 // wxWidgets 2.9.x or higher
680   m_fontRef = font.OSXGetCTFont();
681 #else // wxWidgets 2.8.x
682   m_fontRef = (const void*) font.MacGetCTFont();
683 #endif
684 
685   m_isMacCoreText = true;
686   m_fileName = wxEmptyString;
687 
688   m_inFont = NULL;
689   m_directoryOffset = 0;
690 
691   // Identify single font
692   fontData = IdentifyFont();
693   if (fontData != NULL)
694   {
695     fontData->SetFont(font);
696     fontData->SetFontIndex(0);
697   }
698   else
699   {
700     wxLogError(wxString(wxS("wxPdfFontParserTrueType::IdentifyFont: ")) +
701                wxString::Format(_("Reading of font directory failed for font file '%s'."), font.GetFaceName().c_str()));
702   }
703 #endif
704   return fontData;
705 }
706 #endif
707 
708 wxPdfFontData*
IdentifyFont()709 wxPdfFontParserTrueType::IdentifyFont()
710 {
711   wxPdfFontData* fontData = NULL;
712 #if wxUSE_UNICODE
713   if (ReadTableDirectory())
714   {
715     if (CheckTables())
716     {
717       CheckCff();
718       if (m_cff)
719       {
720         wxPdfFontDataOpenTypeUnicode* otfFontData =  new wxPdfFontDataOpenTypeUnicode();
721         otfFontData->SetCffOffset(m_cffOffset);
722         otfFontData->SetCffLength(m_cffLength);
723         fontData = otfFontData;
724       }
725       else
726       {
727         fontData = new wxPdfFontDataTrueTypeUnicode();
728       }
729 
730       // Get PostScript name, font family, font names and font style
731       fontData->SetName(GetBaseFont());
732       fontData->SetFamily(GetEnglishName(1));
733       fontData->SetFullNames(GetUniqueNames(4));
734       fontData->SetStyle(GetEnglishName(2));
735       m_fontName = fontData->GetName();
736 
737       CheckRestrictions();
738       fontData->SetEmbedSupported(m_embedAllowed);
739       fontData->SetSubsetSupported(m_subsetAllowed);
740     }
741   }
742 #endif
743   return fontData;
744 }
745 
746 bool
LoadFontData(wxPdfFontData * fontData)747 wxPdfFontParserTrueType::LoadFontData(wxPdfFontData* fontData)
748 {
749   bool ok = false;
750   if (fontData != NULL)
751   {
752     wxFSFile* fontFile = NULL;
753     wxMemoryInputStream* fontStream = NULL;
754     m_inFont = NULL;
755     int fontIndex = fontData->GetFontIndex();
756     m_fileName = fontData->GetFontFileName();
757     m_fontName = fontData->GetName();
758     if (!m_fileName.IsEmpty())
759     {
760       wxFileName fileName(m_fileName);
761       wxFileSystem fs;
762       fontFile = fs.OpenFile(wxFileSystem::FileNameToURL(fileName));
763       if (fontFile != NULL)
764       {
765         m_inFont = fontFile->GetStream();
766       }
767     }
768     else
769     {
770 #if defined(__WXMSW__)
771       wxFont font = fontData->GetFont();
772       if (font.IsOk())
773       {
774         fontStream = LoadTrueTypeFontStream(font);
775         if (fontStream != NULL)
776         {
777           m_inFont = fontStream;
778         }
779       }
780 #elif defined(__WXMAC__)
781 #if wxPDFMACOSX_HAS_CORE_TEXT
782       wxFont font = fontData->GetFont();
783       if (font.IsOk())
784       {
785 #if wxCHECK_VERSION(2,9,0)
786         // wxWidgets 2.9.x or higher
787         m_fontRef = font.OSXGetCTFont();
788 #else // wxWidgets 2.8.x
789         m_fontRef = (const void*) font.MacGetCTFont();
790 #endif
791         m_isMacCoreText = true;
792         fontStream = new wxMemoryInputStream("dummy", 5);
793         m_inFont = fontStream;
794       }
795 #endif
796 #endif
797     }
798     if (m_inFont != NULL)
799     {
800       m_inFont->SeekI(0);
801 
802       if (fontIndex >= 0)
803       {
804         // Check whether the font file is a TrueType collection
805         wxString mainTag = ReadString(4);
806         if (mainTag == wxS("ttcf"))
807         {
808           SkipBytes(4);
809           int dirCount = ReadInt();
810           if (fontIndex < dirCount)
811           {
812             SkipBytes(fontIndex * 4);
813             m_directoryOffset = ReadInt();
814             ok = true;
815           }
816           else
817           {
818             wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
819                        wxString::Format(_("Font index %d out of range for font file '%s'."), fontIndex, m_fileName.c_str()));
820           }
821         }
822         else
823         {
824           m_directoryOffset = 0;
825           ok = (fontIndex == 0);
826           if (!ok)
827           {
828             wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: '")) +
829                        wxString::Format(_("Font file '%s' not a valid TrueType collection (TTC) file."), m_fileName.c_str()));
830           }
831         }
832       }
833       else
834       {
835         wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
836                    wxString::Format(_("Font index %d out of range for font file '%s'."), fontIndex, m_fileName.c_str()));
837       }
838 
839       // Load data for a single font
840       if (ok)
841       {
842         if (ReadTableDirectory())
843         {
844           if (CheckTables())
845           {
846             CheckCff();
847             if (m_cff)
848             {
849               ok = fontData->GetType().IsSameAs(wxS("OpenTypeUnicode"));
850             }
851             else
852             {
853               ok = fontData->GetType().IsSameAs(wxS("TrueTypeUnicode"));
854             }
855             if (ok)
856             {
857               ok = PrepareFontData(fontData);
858             }
859             else
860             {
861               wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
862                          wxString::Format(_("Wrong font data type '%s' for font file '%s'."), fontData->GetType().c_str(), m_fileName.c_str()));
863             }
864           }
865           else
866           {
867             wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
868                        wxString::Format(_("Missing font tables for font file '%s'."), m_fileName.c_str()));
869           }
870         }
871         else
872         {
873           wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
874                      wxString::Format(_("Reading of font directory failed for font file '%s'."), m_fileName.c_str()));
875         }
876       }
877       if (fontFile != NULL)
878       {
879         delete fontFile;
880       }
881       if (fontStream != NULL)
882       {
883         delete fontStream;
884       }
885     }
886     else
887     {
888       wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
889                  wxString::Format(_("Font file '%s' not accessible."), m_fileName.c_str()));
890     }
891   }
892   else
893   {
894     wxLogError(wxString(wxS("wxPdfFontParserTrueType::LoadFontData: ")) +
895                wxString(_("No font data instance given.")));
896   }
897   return ok;
898 }
899 
900 bool
PrepareFontData(wxPdfFontData * fontData)901 wxPdfFontParserTrueType::PrepareFontData(wxPdfFontData* fontData)
902 {
903   bool ok = ReadMaps();
904   if (ok)
905   {
906     wxPdfGlyphWidthMap* widths = new wxPdfGlyphWidthMap();
907     wxPdfChar2GlyphMap* glyphs = new wxPdfChar2GlyphMap();
908 
909     wxPdfCMap* cMap;
910     if (m_cmapExt != NULL)
911       cMap = m_cmapExt;
912     else if (!m_fontSpecific && m_cmap31 != NULL)
913       cMap = m_cmap31;
914     else if (m_fontSpecific && m_cmap10 != NULL)
915       cMap = m_cmap10;
916     else if (m_cmap31 != NULL)
917       cMap = m_cmap31;
918     else
919       cMap = m_cmap10;
920     wxPdfCMap::iterator cMapIter;
921     int cc;
922     wxPdfCMapEntry* cMapEntry;
923     for (cMapIter = cMap->begin(); cMapIter != cMap->end(); cMapIter++)
924     {
925       cc = cMapIter->first;
926       cMapEntry = cMapIter->second;
927       (*widths)[cc] = cMapEntry->m_width;
928       (*glyphs)[cc] = cMapEntry->m_glyph;
929     }
930 
931     fontData->SetGlyphWidthMap(widths);
932     fontData->SetChar2GlyphMap(glyphs);
933     fontData->SetGlyphWidths(m_glyphWidths);
934     fontData->SetKernPairMap(m_kp);
935     fontData->SetDescription(m_fd);
936   }
937 
938   m_inFont->SeekI(0);
939   size_t len = (m_cff) ? m_cffLength : m_inFont->GetSize();
940   fontData->SetSize1(len);
941 #if wxUSE_UNICODE
942   fontData->CreateDefaultEncodingConv();
943 #endif
944   return ok;
945 }
946 
947 bool
ReadTableDirectory()948 wxPdfFontParserTrueType::ReadTableDirectory()
949 {
950   ClearTableDirectory();
951   bool ok = true;
952   if (!m_isMacCoreText)
953   {
954     m_inFont->SeekI(m_directoryOffset);
955     int id = ReadInt();
956     //        TrueType            OpenType            Mac TrueType
957     if (id == 0x00010000 || id == 0x4F54544F || id == 0x74727565)
958     {
959       int num_tables = ReadUShort();
960       SkipBytes(6);
961       int k;
962       for (k = 0; k < num_tables; ++k)
963       {
964         wxString tag = ReadString(4);
965         wxPdfTableDirectoryEntry* tableLocation = new wxPdfTableDirectoryEntry();
966         tableLocation->m_checksum = ReadInt();
967         tableLocation->m_offset = ReadInt();
968         tableLocation->m_length = ReadInt();
969         (*m_tableDirectory)[tag] = tableLocation;
970       }
971     }
972     else
973     {
974       if (!m_fileName.IsEmpty())
975       {
976         wxLogError(wxString(wxS("wxPdfFontParserTrueType::ReadTableDirectory: '")) +
977                    wxString::Format(_("Font file '%s' not a valid TrueType (TTF) or OpenType (OTF) file."), m_fileName.c_str()));
978       }
979       ok = false;
980     }
981   }
982   else
983   {
984 #if defined(__WXMAC__)
985 #if wxPDFMACOSX_HAS_CORE_TEXT
986     CFArrayRef tables = CTFontCopyAvailableTables(m_fontRef, 0);
987     CFIndex nTables = CFArrayGetCount(tables);
988     CFIndex n;
989     for (n = 0; n < nTables; ++n)
990     {
991       CTFontTableTag tag = (CTFontTableTag)(uintptr_t) CFArrayGetValueAtIndex(tables, n);
992       CFDataRef    tableRef  = CTFontCopyTable(m_fontRef, tag, 0);
993       const UInt8* tableData = CFDataGetBytePtr(tableRef);
994       CFIndex      tableLen  = CFDataGetLength(tableRef);
995       int checksum = CalculateChecksum((const char*) tableData, (size_t) tableLen);
996       CFRelease(tableRef);
997       char asciiTag[5];
998       asciiTag[0] = (tag >> 24) & 0xff;
999       asciiTag[1] = (tag >> 16) & 0xff;
1000       asciiTag[2] = (tag >>  8) & 0xff;
1001       asciiTag[3] =  tag        & 0xff;
1002       asciiTag[4] = 0;
1003       wxString tableTag = wxString::FromAscii(asciiTag);
1004       wxPdfTableDirectoryEntry* tableLocation = new wxPdfTableDirectoryEntry();
1005       tableLocation->m_checksum = checksum;
1006       tableLocation->m_offset = 0;
1007       tableLocation->m_length = tableLen;
1008       (*m_tableDirectory)[tableTag] = tableLocation;
1009     }
1010 #else
1011     ok = false;
1012 #endif
1013 #endif
1014   }
1015   return ok;
1016 }
1017 
1018 static const wxChar* checkTableNames[] = {
1019   wxS("cmap"), wxS("head"), wxS("hhea"), wxS("hmtx"), wxS("name"),
1020   wxS("post"),
1021   wxS("glyf"), wxS("loca"),
1022   NULL
1023 };
1024 
1025 bool
CheckTables()1026 wxPdfFontParserTrueType::CheckTables()
1027 {
1028   bool ok = true;
1029   int maxTableCount = (m_tableDirectory->find(wxS("CFF ")) == m_tableDirectory->end()) ? 8 : 6;
1030   int tableCount = 0;
1031   while (ok && tableCount < maxTableCount && checkTableNames[tableCount] != NULL)
1032   {
1033     if (m_tableDirectory->find(checkTableNames[tableCount]) == m_tableDirectory->end())
1034     {
1035       ok = false;
1036     }
1037     ++tableCount;
1038   }
1039   return ok;
1040 }
1041 
1042 void
CheckCff()1043 wxPdfFontParserTrueType::CheckCff()
1044 {
1045   wxPdfTableDirectoryEntry* tableLocation;
1046   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("CFF "));
1047   if (entry != m_tableDirectory->end())
1048   {
1049     tableLocation = entry->second;
1050     m_cff = true;
1051     m_cffOffset = tableLocation->m_offset;
1052     m_cffLength = tableLocation->m_length;
1053   }
1054   else
1055   {
1056     m_cff = false;
1057     m_cffOffset = 0;
1058     m_cffLength = 0;
1059   }
1060 }
1061 
1062 void
CheckRestrictions()1063 wxPdfFontParserTrueType::CheckRestrictions()
1064 {
1065   wxPdfTableDirectoryEntry* tableLocation;
1066   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("OS/2"));
1067   if (entry != m_tableDirectory->end())
1068   {
1069     tableLocation = entry->second;
1070     LockTable(wxS("OS/2"));
1071     m_inFont->SeekI(tableLocation->m_offset+8);
1072     short fsType = ReadShort();
1073     bool rl = (fsType & 0x0002) != 0; // restricted license
1074     bool pp = (fsType & 0x0004) != 0; // preview and print embedding
1075     bool e  = (fsType & 0x0008) != 0; // editable embedding
1076     bool ns = (fsType & 0x0100) != 0; // no subsetting
1077     bool b  = (fsType & 0x0200) != 0; // bitmap embedding only
1078     m_embedAllowed = !((rl && !pp && !e) || b);
1079     m_subsetAllowed = !ns;
1080     ReleaseTable();
1081   }
1082   else
1083   {
1084     m_embedAllowed = true;
1085     m_subsetAllowed = true;
1086   }
1087 }
1088 
1089 wxString
GetBaseFont()1090 wxPdfFontParserTrueType::GetBaseFont()
1091 {
1092   wxString fontName = wxEmptyString;
1093   wxPdfTableDirectoryEntry* tableLocation;
1094   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("name"));
1095   if (entry != m_tableDirectory->end())
1096   {
1097     tableLocation = entry->second;
1098     LockTable(wxS("name"));
1099     m_inFont->SeekI(tableLocation->m_offset+2);
1100     int numRecords = ReadUShort();
1101     int startOfStorage = ReadUShort();
1102     int k;
1103     for (k = 0; k < numRecords; k++)
1104     {
1105       int platformID = ReadUShort();
1106       /* int platformEncodingID = */ ReadUShort();
1107       /* int languageID = */ ReadUShort();
1108       int nameID = ReadUShort();
1109       int length = ReadUShort();
1110       int offset = ReadUShort();
1111       if (nameID == 6)
1112       {
1113         m_inFont->SeekI(tableLocation->m_offset + startOfStorage + offset);
1114         if (platformID == 0 || platformID == 3)
1115         {
1116           fontName = ReadUnicodeString(length);
1117         }
1118         else
1119         {
1120           fontName = ReadString(length);
1121         }
1122         break;
1123       }
1124     }
1125     if (fontName.IsEmpty())
1126     {
1127       wxFileName::SplitPath(m_fileName, NULL, &fontName, NULL);
1128       fontName.Replace(wxS(" "), wxS("-"));
1129     }
1130     ReleaseTable();
1131   }
1132   else
1133   {
1134     wxLogError(wxString(wxS("wxPdfFontParserTrueType::GetBaseFont: "))+
1135                wxString::Format(_("Table 'name' does not exist in font file '%s'."), m_fileName.c_str()));
1136   }
1137   return fontName;
1138 }
1139 
1140 bool
ReadMaps()1141 wxPdfFontParserTrueType::ReadMaps()
1142 {
1143   wxPdfFontHeader head;
1144   wxPdfHorizontalHeader hhea;
1145   wxPdfWindowsMetrics os_2;
1146   int realOS2TypoDescender;
1147 
1148   wxPdfTableDirectoryEntry* tableLocation;
1149   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("head"));
1150   if (entry == m_tableDirectory->end())
1151   {
1152     wxLogError(wxString(wxS("wxPdfFontParser::ReadMaps: ")) +
1153                wxString::Format(_("Table 'head' does not exist in '%s,%s'."), m_fileName.c_str(), m_style.c_str()));
1154     return false;
1155   }
1156   tableLocation = entry->second;
1157   LockTable(wxS("head"));
1158   m_inFont->SeekI(tableLocation->m_offset+16);
1159   head.m_flags = ReadUShort();
1160   head.m_unitsPerEm = ReadUShort();
1161   SkipBytes(16);
1162   head.m_xMin = ReadShort();
1163   head.m_yMin = ReadShort();
1164   head.m_xMax = ReadShort();
1165   head.m_yMax = ReadShort();
1166   head.m_macStyle = ReadUShort();
1167   ReleaseTable();
1168 
1169   entry = m_tableDirectory->find(wxS("hhea"));
1170   if (entry == m_tableDirectory->end())
1171   {
1172     wxLogError(wxString(wxS("wxPdfFontParser::ReadMaps: ")) +
1173                wxString::Format(_("Table 'hhea' does not exist in '%s,%s'."), m_fileName.c_str(), m_style.c_str()));
1174     return false;
1175   }
1176   tableLocation = entry->second;
1177   LockTable(wxS("hhea"));
1178   m_inFont->SeekI(tableLocation->m_offset+4);
1179   hhea.m_ascender = ReadShort();
1180   hhea.m_descender = ReadShort();
1181   hhea.m_lineGap = ReadShort();
1182   hhea.m_advanceWidthMax = ReadUShort();
1183   hhea.m_minLeftSideBearing = ReadShort();
1184   hhea.m_minRightSideBearing = ReadShort();
1185   hhea.m_xMaxExtent = ReadShort();
1186   hhea.m_caretSlopeRise = ReadShort();
1187   hhea.m_caretSlopeRun = ReadShort();
1188   SkipBytes(12);
1189   hhea.m_numberOfHMetrics = ReadUShort();
1190   ReleaseTable();
1191 
1192   entry = m_tableDirectory->find(wxS("OS/2"));
1193   if (entry != m_tableDirectory->end())
1194   {
1195     tableLocation = entry->second;
1196     LockTable(wxS("OS/2"));
1197     m_inFont->SeekI(tableLocation->m_offset);
1198     int version = ReadUShort();
1199     os_2.m_xAvgCharWidth = ReadShort();
1200     os_2.m_usWeightClass = ReadUShort();
1201     os_2.m_usWidthClass = ReadUShort();
1202     os_2.m_fsType = ReadShort();
1203     os_2.m_ySubscriptXSize = ReadShort();
1204     os_2.m_ySubscriptYSize = ReadShort();
1205     os_2.m_ySubscriptXOffset = ReadShort();
1206     os_2.m_ySubscriptYOffset = ReadShort();
1207     os_2.m_ySuperscriptXSize = ReadShort();
1208     os_2.m_ySuperscriptYSize = ReadShort();
1209     os_2.m_ySuperscriptXOffset = ReadShort();
1210     os_2.m_ySuperscriptYOffset = ReadShort();
1211     os_2.m_yStrikeoutSize = ReadShort();
1212     os_2.m_yStrikeoutPosition = ReadShort();
1213     os_2.m_sFamilyClass = ReadShort();
1214     m_inFont->Read(os_2.m_panose, 10);
1215     SkipBytes(16);
1216     m_inFont->Read(os_2.m_achVendID, 4);
1217     os_2.m_fsSelection = ReadUShort();
1218     os_2.m_usFirstCharIndex = ReadUShort();
1219     os_2.m_usLastCharIndex = ReadUShort();
1220     os_2.m_sTypoAscender = ReadShort();
1221     os_2.m_sTypoDescender = ReadShort();
1222     realOS2TypoDescender = os_2.m_sTypoDescender;
1223     if (os_2.m_sTypoDescender > 0)
1224     {
1225       os_2.m_sTypoDescender = (short)(-os_2.m_sTypoDescender);
1226     }
1227     os_2.m_sTypoLineGap = ReadShort();
1228     os_2.m_usWinAscent = ReadUShort();
1229     os_2.m_usWinDescent = ReadUShort();
1230     os_2.m_ulCodePageRange1 = 0;
1231     os_2.m_ulCodePageRange2 = 0;
1232     if (version > 0)
1233     {
1234       os_2.m_ulCodePageRange1 = ReadInt();
1235       os_2.m_ulCodePageRange2 = ReadInt();
1236     }
1237     if (version > 1)
1238     {
1239       os_2.m_sxHeight = ReadShort();
1240       os_2.m_sCapHeight = ReadShort();
1241     }
1242     else
1243     {
1244       os_2.m_sxHeight = 0;
1245       os_2.m_sCapHeight = (int)(0.7 * head.m_unitsPerEm);
1246     }
1247     ReleaseTable();
1248     m_fd.SetOpenTypeMetrics(
1249       (int) (hhea.m_ascender      * 1000 / head.m_unitsPerEm),
1250       (int) (hhea.m_descender     * 1000 / head.m_unitsPerEm),
1251       (int) (hhea.m_lineGap       * 1000 / head.m_unitsPerEm),
1252       (int) (os_2.m_sTypoAscender * 1000 / head.m_unitsPerEm),
1253       (int) (realOS2TypoDescender * 1000 / head.m_unitsPerEm),
1254       (int) (os_2.m_sTypoLineGap  * 1000 / head.m_unitsPerEm),
1255       (int) (os_2.m_usWinAscent   * 1000 / head.m_unitsPerEm),
1256       (int) (os_2.m_usWinDescent  * 1000 / head.m_unitsPerEm)
1257     );
1258   }
1259   else
1260   {
1261     // TODO: check definition of hhea values, convert if necessary
1262     os_2.m_sTypoAscender = hhea.m_ascender;
1263     os_2.m_sTypoDescender = hhea.m_descender;
1264     if (os_2.m_sTypoDescender > 0)
1265     {
1266       os_2.m_sTypoDescender = (short)(-os_2.m_sTypoDescender);
1267     }
1268     os_2.m_sxHeight = 0;
1269     os_2.m_sCapHeight = (int)(0.7 * head.m_unitsPerEm);
1270     if (os_2.m_sCapHeight > os_2.m_sTypoAscender)
1271     {
1272       os_2.m_sCapHeight = os_2.m_sTypoAscender;
1273     }
1274   }
1275 
1276   int underlinePosition = -100;
1277   int underlineThickness = 50;
1278 
1279   double italicAngle;
1280   entry = m_tableDirectory->find(wxS("post"));
1281   if (entry == m_tableDirectory->end())
1282   {
1283     static double pi = 4. * atan(1.0);
1284     double caretSlopeRun = (double) hhea.m_caretSlopeRun;
1285     double caretSlopeRise = (double) hhea.m_caretSlopeRise;
1286     italicAngle = -atan2(caretSlopeRun, caretSlopeRise) * 180. / pi;
1287   }
1288   else
1289   {
1290     tableLocation = entry->second;
1291     LockTable(wxS("post"));
1292     m_inFont->SeekI(tableLocation->m_offset+4);
1293     short mantissa = ReadShort();
1294     int fraction = ReadUShort();
1295     italicAngle = (double) mantissa + (double) fraction / 16384.0;
1296     underlinePosition = ReadShort();
1297     underlineThickness = ReadShort();
1298     m_isFixedPitch = ReadInt() != 0;
1299     ReleaseTable();
1300   }
1301 
1302   ReadGlyphWidths(hhea.m_numberOfHMetrics, head.m_unitsPerEm);
1303   ReadKerning(head.m_unitsPerEm);
1304 
1305   m_fd.SetAscent((int) (os_2.m_sTypoAscender * 1000 / head.m_unitsPerEm));
1306   m_fd.SetDescent((int) (os_2.m_sTypoDescender * 1000 / head.m_unitsPerEm));
1307   m_fd.SetCapHeight((int) (os_2.m_sCapHeight * 1000 / head.m_unitsPerEm));
1308   m_fd.SetXHeight((int) (os_2.m_sxHeight * 1000 / head.m_unitsPerEm));
1309   m_fd.SetItalicAngle((int) italicAngle);
1310   m_fd.SetStemV(80);
1311   m_fd.SetUnderlinePosition((int) (underlinePosition * 1000 / head.m_unitsPerEm));
1312   m_fd.SetUnderlineThickness((int) (underlineThickness * 1000 / head.m_unitsPerEm));
1313   m_fd.SetMissingWidth(GetGlyphWidth(0));
1314 
1315 #if 0
1316   int      m_stemV;                   ///< StemV
1317 #endif
1318 
1319   int flags = 0;
1320   if (m_isFixedPitch)
1321   {
1322     flags |= 1;
1323   }
1324   if ((head.m_macStyle & 2) != 0)
1325   {
1326     flags |= 64;
1327   }
1328   if ((head.m_macStyle & 1) != 0)
1329   {
1330     flags |= 262144;
1331   }
1332   m_fd.SetFlags(flags);
1333 
1334   wxString fbb = wxString::Format(wxS("[%d %d %d %d]"),
1335                    (int) (head.m_xMin * 1000 / head.m_unitsPerEm),
1336                    (int) (head.m_yMin * 1000 / head.m_unitsPerEm),
1337                    (int) (head.m_xMax * 1000 / head.m_unitsPerEm),
1338                    (int) (head.m_yMax * 1000 / head.m_unitsPerEm));
1339   m_fd.SetFontBBox(fbb);
1340 
1341   entry = m_tableDirectory->find(wxS("cmap"));
1342   if (entry == m_tableDirectory->end())
1343   {
1344     wxLogError(wxString(wxS("wxPdfFontParser::ReadMaps: ")) +
1345                wxString::Format(_("Table 'cmap' does not exist in '%s,%s'."), m_fileName.c_str(), m_style.c_str()));
1346     return false;
1347   }
1348   tableLocation = entry->second;
1349   LockTable(wxS("cmap"));
1350   m_inFont->SeekI(tableLocation->m_offset);
1351   SkipBytes(2);
1352   int num_tables = ReadUShort();
1353   m_fontSpecific = false;
1354   int map10 = 0;
1355   int map31 = 0;
1356   int map30 = 0;
1357   int mapExt = 0;
1358   int k;
1359   for (k = 0; k < num_tables; ++k)
1360   {
1361     int platId = ReadUShort();
1362     int platSpecId = ReadUShort();
1363     int offset = ReadInt();
1364     if (platId == 3 && platSpecId == 0)
1365     {
1366       m_fontSpecific = true;
1367       map30 = offset;
1368     }
1369     else if (platId == 3 && platSpecId == 1)
1370     {
1371       map31 = offset;
1372     }
1373     else if (platId == 3 && platSpecId == 10)
1374     {
1375       mapExt = offset;
1376     }
1377     else if (platId == 1 && platSpecId == 0)
1378     {
1379       map10 = offset;
1380     }
1381     else if (platId == 0) // Apple Unicode
1382     {
1383       // Formats 0, 2, 4, 6, 8, 10, 12
1384       if (platSpecId < 4)
1385       {
1386         map31 = offset;
1387       }
1388       else if (platSpecId == 4) // UCS32
1389       {
1390         mapExt = offset;
1391       }
1392     }
1393   }
1394   if (map10 > 0 && map30 <= 0)
1395   {
1396     m_inFont->SeekI(tableLocation->m_offset + map10);
1397     int format = ReadUShort();
1398     switch (format)
1399     {
1400       case 0:
1401         m_cmap10 = ReadFormat0();
1402         break;
1403       case 4:
1404         m_cmap10 = ReadFormat4();
1405         break;
1406       case 6:
1407         m_cmap10 = ReadFormat6();
1408         break;
1409     }
1410   }
1411   if (map31 > 0)
1412   {
1413     m_inFont->SeekI(tableLocation->m_offset + map31);
1414     int format = ReadUShort();
1415     if (format == 4)
1416     {
1417       m_cmap31 = ReadFormat4();
1418     }
1419   }
1420   if (map30 > 0)
1421   {
1422     m_inFont->SeekI(tableLocation->m_offset + map30);
1423     int format = ReadUShort();
1424     if (format == 4)
1425     {
1426       m_cmap10 = ReadFormat4();
1427     }
1428   }
1429   if (mapExt > 0)
1430   {
1431     m_inFont->SeekI(tableLocation->m_offset + mapExt);
1432     int format = ReadUShort();
1433     switch (format)
1434     {
1435       case 0:
1436         m_cmapExt = ReadFormat0();
1437         break;
1438       case 4:
1439         m_cmapExt = ReadFormat4();
1440         break;
1441       case 6:
1442         m_cmapExt = ReadFormat6();
1443         break;
1444       case 12:
1445         m_cmapExt = ReadFormat12();
1446         break;
1447     }
1448   }
1449   ReleaseTable();
1450 
1451   flags = m_fd.GetFlags();
1452   flags |= m_fontSpecific ? 4 : 32;
1453   m_fd.SetFlags(flags);
1454 
1455   bool ok = (m_cmap10 != NULL) || (m_cmap31 != NULL) || (m_cmapExt != NULL);
1456   if (!ok)
1457   {
1458     wxLogError(wxString(wxS("wxPdfFontParserTrueType::ReadMaps: ")) +
1459                wxString::Format(_("No valid 'cmap' table found for font '%s'."),  m_fontName.c_str()));
1460   }
1461 
1462   return ok;
1463 }
1464 
1465 bool
ReadGlyphWidths(int numberOfHMetrics,int unitsPerEm)1466 wxPdfFontParserTrueType::ReadGlyphWidths(int numberOfHMetrics, int unitsPerEm)
1467 {
1468   wxPdfTableDirectoryEntry* tableLocation;
1469   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("hmtx"));
1470   if (entry == m_tableDirectory->end())
1471   {
1472     wxLogError(wxString(wxS("wxPdfFontParser::ReadGlyphWidths: ")) +
1473                wxString::Format(_("Table 'hmtx' does not exist in '%s,%s'."), m_fileName.c_str(), m_style.c_str()));
1474     return false;
1475   }
1476   tableLocation = entry->second;
1477   LockTable(wxS("hmtx"));
1478   m_inFont->SeekI(tableLocation->m_offset);
1479 
1480   m_glyphWidths.SetCount(numberOfHMetrics);
1481   int k;
1482   for (k = 0; k < numberOfHMetrics; k++)
1483   {
1484     m_glyphWidths[k] = (ReadUShort() * 1000) / unitsPerEm;
1485     ReadUShort();
1486   }
1487   ReleaseTable();
1488   return true;
1489 }
1490 
1491 wxPdfCMap*
ReadFormat0()1492 wxPdfFontParserTrueType::ReadFormat0()
1493 {
1494   wxPdfCMap* h = new wxPdfCMap();
1495   SkipBytes(4);
1496   int k;
1497   for (k = 0; k < 256; k++)
1498   {
1499     wxPdfCMapEntry* r = new wxPdfCMapEntry();
1500     r->m_glyph = (int) ReadByte();
1501     r->m_width = GetGlyphWidth(r->m_glyph);
1502     (*h)[k] = r;
1503   }
1504   return h;
1505 }
1506 
1507 wxPdfCMap*
ReadFormat4()1508 wxPdfFontParserTrueType::ReadFormat4()
1509 {
1510   wxPdfCMap* h = new wxPdfCMap();
1511   int tableLength = ReadUShort();
1512   SkipBytes(2);
1513   int segCount = ReadUShort() / 2;
1514   int glyphIdCount = tableLength / 2 - 8 - segCount * 4;
1515   SkipBytes(6);
1516 
1517   int* endCount   = new int[segCount];
1518   int* startCount = new int[segCount];
1519   int* idDelta    = new int[segCount];
1520   int* idRO       = new int[segCount];
1521   int* glyphId    = new int[glyphIdCount];
1522 
1523   int k;
1524   for (k = 0; k < segCount; k++)
1525   {
1526     endCount[k] = ReadUShort();
1527   }
1528   SkipBytes(2);
1529   for (k = 0; k < segCount; k++)
1530   {
1531     startCount[k] = ReadUShort();
1532   }
1533   for (k = 0; k < segCount; k++)
1534   {
1535     idDelta[k] = ReadUShort();
1536   }
1537   for (k = 0; k < segCount; k++)
1538   {
1539     idRO[k] = ReadUShort();
1540   }
1541   for (k = 0; k < glyphIdCount; k++)
1542   {
1543     glyphId[k] = ReadUShort();
1544   }
1545   for (k = 0; k < segCount; k++)
1546   {
1547     int glyph;
1548     int j;
1549     for (j = startCount[k]; j <= endCount[k] && j != 0xFFFF; j++)
1550     {
1551       if (idRO[k] == 0)
1552       {
1553         glyph = (j + idDelta[k]) & 0xFFFF;
1554       }
1555       else
1556       {
1557         int idx = k + idRO[k] / 2 - segCount + j - startCount[k];
1558         if (idx >= glyphIdCount)
1559           continue;
1560         glyph = (glyphId[idx] + idDelta[k]) & 0xFFFF;
1561       }
1562       wxPdfCMapEntry* r = new wxPdfCMapEntry();
1563       r->m_glyph = glyph;
1564       r->m_width = GetGlyphWidth(r->m_glyph);
1565       int idx = m_fontSpecific ? ((j & 0xff00) == 0xf000 ? j & 0xff : j) : j;
1566       (*h)[idx] = r;
1567 //      wxLogMessage(wxS("C %ld G %ld"), idx, glyph);
1568     }
1569   }
1570 
1571   delete [] endCount;
1572   delete [] startCount;
1573   delete [] idDelta;
1574   delete [] idRO;
1575   delete [] glyphId;
1576 
1577   return h;
1578 }
1579 
1580 wxPdfCMap*
ReadFormat6()1581 wxPdfFontParserTrueType::ReadFormat6()
1582 {
1583   wxPdfCMap* h = new wxPdfCMap();
1584   SkipBytes(4);
1585   int startCode = ReadUShort();
1586   int codeCount = ReadUShort();
1587   int k;
1588   for (k = 0; k < codeCount; k++)
1589   {
1590     wxPdfCMapEntry* r = new wxPdfCMapEntry();
1591     r->m_glyph = ReadUShort();
1592     r->m_width = GetGlyphWidth(r->m_glyph);
1593     (*h)[k+startCode] = r;
1594   }
1595   return h;
1596 }
1597 
1598 wxPdfCMap*
ReadFormat12()1599 wxPdfFontParserTrueType::ReadFormat12()
1600 {
1601   wxPdfCMap* h = new wxPdfCMap();
1602   SkipBytes(2);
1603   /* int tableLength = */ ReadInt();
1604   SkipBytes(4);
1605   int nGroups = ReadInt();
1606 
1607   for (int k = 0; k < nGroups; k++)
1608   {
1609     int startCharCode = ReadInt();
1610     int endCharCode = ReadInt();
1611     int startGlyphID = ReadInt();
1612     int i;
1613     for (i = startCharCode; i <= endCharCode; ++i)
1614     {
1615       wxPdfCMapEntry* r = new wxPdfCMapEntry();
1616       r->m_glyph = startGlyphID;
1617       r->m_width = GetGlyphWidth(r->m_glyph);
1618       (*h)[i] = r;
1619       ++startGlyphID;
1620     }
1621   }
1622   return h;
1623 }
1624 
1625 void
ReadKerning(int unitsPerEm)1626 wxPdfFontParserTrueType::ReadKerning(int unitsPerEm)
1627 {
1628   wxPdfTableDirectoryEntry* tableLocation;
1629   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("kern"));
1630   if (entry != m_tableDirectory->end())
1631   {
1632     tableLocation = entry->second;
1633     LockTable(wxS("kern"));
1634     m_kp = new wxPdfKernPairMap();
1635     wxPdfKernWidthMap* kwMap = NULL;
1636     wxPdfKernWidthMap::iterator kw;
1637     wxUint32 u1, u2;
1638     wxUint32 u1prev = 0;
1639 
1640     m_inFont->SeekI(tableLocation->m_offset+2);
1641     int nTables = ReadUShort();
1642     int checkpoint = tableLocation->m_offset + 4;
1643     int length = 0;
1644     int j, k;
1645     for (k = 0; k < nTables; ++k)
1646     {
1647       checkpoint += length;
1648       m_inFont->SeekI(checkpoint);
1649       SkipBytes(2);
1650       length = ReadUShort();
1651       int coverage = ReadUShort();
1652       if ((coverage & 0xfff7) == 0x0001)
1653       {
1654         int nPairs = ReadUShort();
1655         SkipBytes(6);
1656         for (j = 0; j < nPairs; ++j)
1657         {
1658           u1 = ReadUShort();
1659           u2 = ReadUShort();
1660           int value = ((int) ReadShort() * 1000) / unitsPerEm;
1661           if (u1 != u1prev)
1662           {
1663             u1prev = u1;
1664             wxPdfKernPairMap::iterator kp = (*m_kp).find(u1);
1665             if (kp == (*m_kp).end())
1666             {
1667               kwMap = new wxPdfKernWidthMap();
1668               (*m_kp)[u1] = kwMap;
1669             }
1670             else
1671             {
1672               kwMap = kp->second;
1673             }
1674           }
1675           (*kwMap)[u2] = value;
1676         }
1677       }
1678     }
1679     ReleaseTable();
1680   }
1681 }
1682 
1683 int
GetGlyphWidth(unsigned int glyph)1684 wxPdfFontParserTrueType::GetGlyphWidth(unsigned int glyph)
1685 {
1686   if (glyph >= m_glyphWidths.GetCount())
1687   {
1688     glyph = (unsigned int) m_glyphWidths.GetCount() - 1;
1689   }
1690   return m_glyphWidths[glyph];
1691 }
1692 
1693 wxArrayString
GetUniqueNames(int id)1694 wxPdfFontParserTrueType::GetUniqueNames(int id)
1695 {
1696   wxArrayString uniqueNames;
1697   wxArrayString names = GetNames(id);
1698   size_t j;
1699   for (j = 0; j < names.GetCount(); ++j)
1700   {
1701     if (uniqueNames.Index(names[j], false) == wxNOT_FOUND)
1702     {
1703       uniqueNames.Add(names[j]);
1704     }
1705   }
1706   return uniqueNames;
1707 }
1708 
1709 wxArrayString
GetNames(int id,bool namesOnly)1710 wxPdfFontParserTrueType::GetNames(int id, bool namesOnly)
1711 {
1712   wxArrayString names;
1713   wxPdfTableDirectoryEntry* tableLocation;
1714   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("name"));
1715   if (entry != m_tableDirectory->end())
1716   {
1717     tableLocation = entry->second;
1718     LockTable(wxS("name"));
1719     m_inFont->SeekI(tableLocation->m_offset+2);
1720     int numRecords = ReadUShort();
1721     int startOfStorage = ReadUShort();
1722     int k;
1723     for (k = 0; k < numRecords; k++)
1724     {
1725       int platformID = ReadUShort();
1726       int platformEncodingID = ReadUShort();
1727       int languageID = ReadUShort();
1728       int nameID = ReadUShort();
1729       int length = ReadUShort();
1730       int offset = ReadUShort();
1731       if (nameID == id)
1732       {
1733         off_t pos = m_inFont->TellI();
1734         m_inFont->SeekI(tableLocation->m_offset + startOfStorage + offset);
1735         wxString name;
1736         if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1))
1737         {
1738           name = ReadUnicodeString(length);
1739         }
1740         else
1741         {
1742           name = ReadString(length);
1743         }
1744         if (!namesOnly)
1745         {
1746           names.Add(wxString::Format(wxS("%d"), platformID));
1747           names.Add(wxString::Format(wxS("%d"), platformEncodingID));
1748           names.Add(wxString::Format(wxS("%d"), languageID));
1749         }
1750         names.Add(name);
1751         m_inFont->SeekI(pos);
1752       }
1753     }
1754     ReleaseTable();
1755   }
1756   else
1757   {
1758     wxLogError(wxString(wxS("wxPdfFontParser::GetNames: ")) +
1759                wxString::Format(_("Table 'name' does not exist in '%s,%s'."), m_fileName.c_str(), m_style.c_str()));
1760   }
1761   return names;
1762 }
1763 
1764 wxString
GetEnglishName(int id)1765 wxPdfFontParserTrueType::GetEnglishName(int id)
1766 {
1767   wxString englishName = wxEmptyString;
1768   wxPdfTableDirectoryEntry* tableLocation;
1769   wxPdfTableDirectory::iterator entry = m_tableDirectory->find(wxS("name"));
1770   if (entry != m_tableDirectory->end())
1771   {
1772     tableLocation = entry->second;
1773     LockTable(wxS("name"));
1774     m_inFont->SeekI(tableLocation->m_offset+2);
1775     int numRecords = ReadUShort();
1776     int startOfStorage = ReadUShort();
1777     bool ready = false;
1778     int k;
1779     for (k = 0; !ready && (k < numRecords); k++)
1780     {
1781       int platformID = ReadUShort();
1782       int platformEncodingID = ReadUShort();
1783       int languageID = ReadUShort();
1784       int nameID = ReadUShort();
1785       int length = ReadUShort();
1786       int offset = ReadUShort();
1787       if (nameID == id)
1788       {
1789         off_t pos = m_inFont->TellI();
1790         m_inFont->SeekI(tableLocation->m_offset + startOfStorage + offset);
1791         wxString name;
1792         if (platformID == 0 || platformID == 3 || (platformID == 2 && platformEncodingID == 1))
1793         {
1794           name = ReadUnicodeString(length);
1795         }
1796         else
1797         {
1798           name = ReadString(length);
1799         }
1800         if (!ready)
1801         {
1802           if (languageID == 0)
1803           {
1804             englishName = name;
1805             ready = true;
1806           }
1807           else if (languageID == 1033)
1808           {
1809             englishName = name;
1810           }
1811         }
1812         m_inFont->SeekI(pos);
1813       }
1814     }
1815     ReleaseTable();
1816   }
1817   else
1818   {
1819     wxLogError(wxString(wxS("wxPdfFontParserTrueType::GetEnglishName: ")) +
1820                wxString::Format(_("Table 'name' does not exist in '%s,%s'."), m_fileName.c_str(), m_style.c_str()));
1821   }
1822   return englishName;
1823 }
1824