1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        pdffontdataopentype.cpp
3 // Purpose:
4 // Author:      Ulrich Telle
5 // Created:     2008-08-10
6 // Copyright:   (c) Ulrich Telle
7 // Licence:     wxWindows licence
8 ///////////////////////////////////////////////////////////////////////////////
9 
10 /// \file pdffontdataopentype.cpp Implementation of wxPdfFontDataOpenTypeUnicode class
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/zstream.h>
27 
28 #include "wx/pdfarraytypes.h"
29 #include "wx/pdffontdataopentype.h"
30 #include "wx/pdffontparsertruetype.h"
31 
32 #include "wx/pdffontsubsetcff.h"
33 
34 #if wxUSE_UNICODE
35 
wxPdfFontDataOpenTypeUnicode()36 wxPdfFontDataOpenTypeUnicode::wxPdfFontDataOpenTypeUnicode()
37   : wxPdfFontData()
38 {
39   m_type = wxS("OpenTypeUnicode");
40   m_gw   = NULL;
41   m_conv = NULL;
42   m_cff = true;
43 
44   m_embedRequired = true;
45   m_embedSupported = true;
46   m_subsetSupported = true;
47 }
48 
~wxPdfFontDataOpenTypeUnicode()49 wxPdfFontDataOpenTypeUnicode::~wxPdfFontDataOpenTypeUnicode()
50 {
51   // delete m_cw;
52   if (m_conv != NULL)
53   {
54     delete m_conv;
55   }
56   if (m_gw != NULL)
57   {
58     delete m_gw;
59   }
60 }
61 
62 void
CreateDefaultEncodingConv()63 wxPdfFontDataOpenTypeUnicode::CreateDefaultEncodingConv()
64 {
65   if (m_conv == NULL)
66   {
67     m_conv = new wxMBConvUTF16BE();
68   }
69 }
70 
71 bool
LoadFontMetrics(wxXmlNode * root)72 wxPdfFontDataOpenTypeUnicode::LoadFontMetrics(wxXmlNode* root)
73 {
74   bool bName  = false,
75        bDesc  = false,
76        bFile  = true,
77        bSize  = true,
78        bWidth = false;
79   wxString value;
80   long number;
81   wxXmlNode *child = root->GetChildren();
82   while (child)
83   {
84     // parse the children
85     if (child->GetName() == wxS("font-name"))
86     {
87       m_name = GetNodeContent(child);
88       m_style = FindStyleFromName(m_name);
89       bName = m_name.Length() > 0;
90     }
91     else if (child->GetName() == wxS("encoding"))
92     {
93       m_enc = GetNodeContent(child);
94     }
95     else if (child->GetName() == wxS("description"))
96     {
97       bDesc = GetFontDescription(child, m_desc);
98     }
99     else if (child->GetName() == wxS("diff"))
100     {
101       m_diffs = GetNodeContent(child);
102     }
103     else if (child->GetName() == wxS("file"))
104     {
105 #if wxCHECK_VERSION(2,9,0)
106       value = child->GetAttribute(wxS("ctg"), wxS(""));
107 #else
108       value = child->GetPropVal(wxS("ctg"), wxS(""));
109 #endif
110       if (value.Length() > 0)
111       {
112         bFile = true;
113         m_ctg = value;
114 #if wxCHECK_VERSION(2,9,0)
115         value = child->GetAttribute(wxS("name"), wxS(""));
116 #else
117         value = child->GetPropVal(wxS("name"), wxS(""));
118 #endif
119         if (value.Length() > 0)
120         {
121           m_file = value;
122 #if wxCHECK_VERSION(2,9,0)
123           value = child->GetAttribute(wxS("originalsize"), wxS(""));
124 #else
125           value = child->GetPropVal(wxS("originalsize"), wxS(""));
126 #endif
127           if (value.Length() > 0 && value.ToLong(&number))
128           {
129             bFile = true;
130             m_size1 = number;
131           }
132           else
133           {
134             bFile = false;
135             m_file = wxS("");
136           }
137         }
138       }
139       else
140       {
141         bFile = false;
142         m_file = wxS("");
143         m_ctg = wxS("");
144       }
145     }
146     else if (child->GetName() == wxS("widths"))
147     {
148 #if wxCHECK_VERSION(2,9,0)
149       wxString subsetting = child->GetAttribute(wxS("subsetting"), wxS("disabled"));
150 #else
151       wxString subsetting = child->GetPropVal(wxS("subsetting"), wxS("disabled"));
152 #endif
153       m_subsetSupported = (subsetting == wxS("enabled"));
154       bWidth = true;
155       m_cw = new wxPdfGlyphWidthMap();
156       if (m_subsetSupported)
157       {
158         m_gn = new wxPdfChar2GlyphMap();
159       }
160       const wxXmlNode *charNode = child->GetChildren();
161       while (charNode)
162       {
163         wxString strId, strGn, strWidth;
164         long charId, glyph, charWidth;
165         if (charNode->GetName() == wxS("char"))
166         {
167 #if wxCHECK_VERSION(2,9,0)
168           strId = charNode->GetAttribute(wxS("id"), wxS(""));
169           if (m_subsetSupported)
170           {
171             strGn = charNode->GetAttribute(wxS("gn"), wxS(""));
172           }
173           strWidth = charNode->GetAttribute(wxS("width"), wxS(""));
174 #else
175           strId = charNode->GetPropVal(wxS("id"), wxS(""));
176           if (m_subsetSupported)
177           {
178             strGn = charNode->GetPropVal(wxS("gn"), wxS(""));
179           }
180           strWidth = charNode->GetPropVal(wxS("width"), wxS(""));
181 #endif
182           if (strId.Length() > 0 && strId.ToLong(&charId) &&
183               strWidth.Length() > 0 && strWidth.ToLong(&charWidth))
184           {
185             (*m_cw)[charId] = charWidth;
186             if (m_subsetSupported)
187             {
188               if (strGn.Length() > 0 && strGn.ToLong(&glyph))
189               {
190                 (*m_gn)[charId] = glyph;
191               }
192               else
193               {
194                 (*m_gn)[charId] = 0;
195               }
196             }
197           }
198         }
199         charNode = charNode->GetNext();
200       }
201     }
202     child = child->GetNext();
203   }
204   CreateDefaultEncodingConv();
205 
206   m_initialized = (bName && bDesc && bFile && bSize && bWidth);
207   if (m_initialized)
208   {
209     wxFileName fileName(m_file);
210     m_initialized = fileName.MakeAbsolute(m_path) && fileName.FileExists() && fileName.IsFileReadable();
211     if (m_initialized)
212     {
213       fileName.Assign(m_ctg);
214       m_initialized = fileName.MakeAbsolute(m_path) && fileName.FileExists() && fileName.IsFileReadable();
215     }
216   }
217   if (m_initialized && m_gn == NULL)
218   {
219     // We now always need a cid to gid mapping whether subsetting is enabled or not
220     // So we read the CTG file produced by MakeFont and create the map
221     bool compressed = m_ctg.Lower().Right(2) == wxS(".z");
222     wxFileName fileName(m_ctg);
223     fileName.MakeAbsolute(m_path);
224     wxFileSystem fs;
225     wxFSFile* ctgFile = fs.OpenFile(wxFileSystem::FileNameToURL(fileName));
226     wxInputStream* ctgStream = NULL;
227     if (ctgFile)
228     {
229       ctgStream = ctgFile->GetStream();
230     }
231     else
232     {
233       m_initialized = false;
234       // usually this should not happen since file accessability was already checked
235       wxLogError(wxString(wxS("wxPdfFontDataOpenTypeUnicode::LoadFontMetrics: ")) +
236                  wxString::Format(_("CTG file '%s' not found."), fileName.GetFullPath().c_str()));
237     }
238     if (ctgStream)
239     {
240       size_t ctgLen;
241       unsigned char* cc2gn = NULL;
242       if (compressed)
243       {
244         wxZlibInputStream zin(*ctgStream);
245         wxMemoryOutputStream zout;
246         zout.Write(zin);
247         zout.Close();
248         wxMemoryInputStream cid2gidStream(zout);
249         ctgLen = cid2gidStream.GetSize();
250         cc2gn = new unsigned char[ctgLen];
251         cid2gidStream.Read(cc2gn, ctgLen);
252       }
253       else
254       {
255         ctgLen = ctgStream->GetSize();
256         cc2gn = new unsigned char[ctgLen];
257         ctgStream->Read(cc2gn, ctgLen);
258       }
259       delete ctgFile;
260 
261       // Create the cid to gid mapping
262       m_gn = new wxPdfChar2GlyphMap();
263       size_t charId;
264       for (charId = 0; charId < 0xFFFF; ++charId)
265       {
266         int glyph = (cc2gn[2*charId] << 8) + cc2gn[2*charId+1];
267         if (glyph != 0)
268         {
269           (*m_gn)[charId] = glyph;
270         }
271       }
272       delete [] cc2gn;
273     }
274   }
275   return m_initialized;
276 }
277 
278 bool
Initialize()279 wxPdfFontDataOpenTypeUnicode::Initialize()
280 {
281   bool ok = true;
282   if (!IsInitialized())
283   {
284     wxPdfFontParserTrueType fontParser;
285     ok = fontParser.LoadFontData(this);
286     m_initialized = ok;
287   }
288   return ok;
289 }
290 
291 void
SetGlyphWidths(const wxPdfArrayUint16 & glyphWidths)292 wxPdfFontDataOpenTypeUnicode::SetGlyphWidths(const wxPdfArrayUint16& glyphWidths)
293 {
294   if (m_gw == NULL)
295   {
296     m_gw = new wxPdfArrayUint16();
297   }
298   *m_gw = glyphWidths;
299 }
300 
301 double
GetStringWidth(const wxString & s,const wxPdfEncoding * encoding,bool withKerning) const302 wxPdfFontDataOpenTypeUnicode::GetStringWidth(const wxString& s, const wxPdfEncoding* encoding, bool withKerning) const
303 {
304   wxUnusedVar(encoding);
305   // Get width of a string in the current font
306   double w = 0;
307 
308   wxPdfGlyphWidthMap::iterator charIter;
309   wxString::const_iterator ch;
310   for (ch = s.begin(); ch != s.end(); ++ch)
311   {
312     wxChar c = *ch;
313     charIter = m_cw->find(c);
314     if (charIter != m_cw->end())
315     {
316       w += charIter->second;
317     }
318     else
319     {
320       w += m_desc.GetMissingWidth();
321     }
322   }
323   if (withKerning)
324   {
325     int kerningWidth = GetKerningWidth(s);
326     if (kerningWidth != 0)
327     {
328       w += (double) kerningWidth;
329     }
330   }
331   return w / 1000;
332 }
333 
334 bool
CanShow(const wxString & s,const wxPdfEncoding * encoding) const335 wxPdfFontDataOpenTypeUnicode::CanShow(const wxString& s, const wxPdfEncoding* encoding) const
336 {
337   wxUnusedVar(encoding);
338   bool canShow = true;
339   wxString::const_iterator ch;
340   for (ch = s.begin(); canShow && ch != s.end(); ++ch)
341   {
342     canShow = (m_gn->find(*ch) != m_gn->end());
343   }
344   return canShow;
345 }
346 
347 wxString
ConvertCID2GID(const wxString & s,const wxPdfEncoding * encoding,wxPdfSortedArrayInt * usedGlyphs,wxPdfChar2GlyphMap * subsetGlyphs) const348 wxPdfFontDataOpenTypeUnicode::ConvertCID2GID(const wxString& s,
349                                              const wxPdfEncoding* encoding,
350                                              wxPdfSortedArrayInt* usedGlyphs,
351                                              wxPdfChar2GlyphMap* subsetGlyphs) const
352 {
353   wxUnusedVar(encoding);
354   bool doSubsetting = usedGlyphs != NULL && subsetGlyphs != NULL;
355   wxString t;
356   wxPdfChar2GlyphMap::const_iterator charIter;
357   wxUint32 glyph, subsetGlyph;
358   wxString::const_iterator ch;
359   for (ch = s.begin(); ch != s.end(); ++ch)
360   {
361     charIter = m_gn->find(*ch);
362     if (charIter != m_gn->end())
363     {
364       glyph = charIter->second;
365       if (doSubsetting)
366       {
367         if (usedGlyphs->Index(glyph) != wxNOT_FOUND)
368         {
369           glyph = (*subsetGlyphs)[glyph];
370         }
371         else
372         {
373           subsetGlyph = (wxUint32) usedGlyphs->GetCount();
374           (*subsetGlyphs)[glyph] = subsetGlyph;
375           usedGlyphs->Add(glyph);
376           glyph = subsetGlyph;
377         }
378       }
379 #if wxCHECK_VERSION(2,9,0)
380       t.Append(wxUniChar(glyph));
381 #else
382       t.Append(wxChar(glyph));
383 #endif
384     }
385     else
386     {
387 #if wxCHECK_VERSION(2,9,0)
388       t.Append(wxUniChar(0));
389 #else
390       t.Append(wxChar(0));
391 #endif
392     }
393   }
394   return t;
395 }
396 
397 wxString
ConvertGlyph(wxUint32 glyph,const wxPdfEncoding * encoding,wxPdfSortedArrayInt * usedGlyphs,wxPdfChar2GlyphMap * subsetGlyphs) const398 wxPdfFontDataOpenTypeUnicode::ConvertGlyph(wxUint32 glyph,
399                                            const wxPdfEncoding* encoding,
400                                            wxPdfSortedArrayInt* usedGlyphs,
401                                            wxPdfChar2GlyphMap* subsetGlyphs) const
402 {
403   wxUnusedVar(encoding);
404   wxString t = wxEmptyString;
405   if (m_gw != NULL && glyph < m_gw->size())
406   {
407     bool doSubsetting = usedGlyphs != NULL && subsetGlyphs != NULL;
408     wxUint32 subsetGlyph;
409     if (doSubsetting)
410     {
411       if (usedGlyphs->Index(glyph) != wxNOT_FOUND)
412       {
413         glyph = (*subsetGlyphs)[glyph];
414       }
415       else
416       {
417         subsetGlyph = (wxUint32) usedGlyphs->GetCount();
418         (*subsetGlyphs)[glyph] = subsetGlyph;
419         usedGlyphs->Add(glyph);
420         glyph = subsetGlyph;
421       }
422     }
423 #if wxCHECK_VERSION(2,9,0)
424     t.Append(wxUniChar(glyph));
425 #else
426     t.Append(wxChar(glyph));
427 #endif
428   }
429   else
430   {
431 #if wxCHECK_VERSION(2,9,0)
432     t.Append(wxUniChar(0));
433 #else
434     t.Append(wxChar(0));
435 #endif
436   }
437   return t;
438 }
439 
440 wxString
GetWidthsAsString(bool subset,wxPdfSortedArrayInt * usedGlyphs,wxPdfChar2GlyphMap * subsetGlyphs) const441 wxPdfFontDataOpenTypeUnicode::GetWidthsAsString(bool subset, wxPdfSortedArrayInt* usedGlyphs, wxPdfChar2GlyphMap* subsetGlyphs) const
442 {
443   wxString s = wxString(wxS("["));
444   wxUint32 glyph;
445   wxPdfChar2GlyphMap::const_iterator glyphIter;
446   wxPdfGlyphWidthMap::iterator charIter;
447   for (charIter = m_cw->begin(); charIter != m_cw->end(); charIter++)
448   {
449     glyphIter = m_gn->find(charIter->first);
450     if (glyphIter != m_gn->end())
451     {
452       glyph = glyphIter->second;
453     }
454     else
455     {
456       glyph = 0;
457     }
458     if (glyph != 0 && (!subset || usedGlyphs == NULL ||
459                        (subset && SubsetSupported() && (usedGlyphs->Index(glyph) != wxNOT_FOUND))))
460     {
461       if (subset)
462       {
463         glyph = (*subsetGlyphs)[glyph];
464       }
465       // define a specific width for each individual CID
466       s += wxString::Format(wxS("%u [%u] "), glyph, charIter->second);
467     }
468   }
469   s += wxString(wxS("]"));
470   return s;
471 }
472 
473 size_t
WriteFontData(wxOutputStream * fontData,wxPdfSortedArrayInt * usedGlyphs,wxPdfChar2GlyphMap * subsetGlyphs)474 wxPdfFontDataOpenTypeUnicode::WriteFontData(wxOutputStream* fontData, wxPdfSortedArrayInt* usedGlyphs, wxPdfChar2GlyphMap* subsetGlyphs)
475 {
476 #if defined(__WXMAC__)
477 #if wxPDFMACOSX_HAS_CORE_TEXT
478   wxCFRef<CFDataRef> tableRef;
479 #endif
480 #endif
481   size_t fontSize1 = 0;
482   wxFSFile* fontFile = NULL;
483   wxInputStream* fontStream = NULL;
484   bool compressed = false;
485   wxFileName fileName;
486   if (m_fontFileName.IsEmpty())
487   {
488 #if defined(__WXMSW__)
489     if (m_file.IsEmpty() && m_font.IsOk())
490     {
491       fontStream = wxPdfFontParserTrueType::LoadTrueTypeFontStream(m_font);
492     }
493     else
494 #elif defined(__WXMAC__)
495 #if wxPDFMACOSX_HAS_CORE_TEXT
496     if (m_file.IsEmpty() && m_font.IsOk())
497     {
498 #if wxCHECK_VERSION(2,9,0)
499       // wxWidgets 2.9.x or higher
500       CTFontRef fontRef = m_font.OSXGetCTFont();
501 #else // wxWidgets 2.8.x
502       CTFontRef fontRef = (const void*) m_font.MacGetCTFont();
503 #endif
504       tableRef.reset(CTFontCopyTable(fontRef, kCTFontTableCFF, 0));
505       const UInt8* tableData = CFDataGetBytePtr(tableRef);
506       CFIndex      tableLen  = CFDataGetLength(tableRef);
507       fontStream = new wxMemoryInputStream((const char*) tableData, (size_t) tableLen);
508     }
509     else
510 #endif
511 #endif
512     {
513       // Font data preprocessed by MakeFont
514       compressed = m_file.Lower().Right(2) == wxS(".z");
515       fileName = m_file;
516       fileName.MakeAbsolute(m_path);
517     }
518   }
519   else
520   {
521     fileName = m_fontFileName;
522   }
523 
524   if (fileName.IsOk())
525   {
526     // Open font file
527     wxFileSystem fs;
528     fontFile = fs.OpenFile(wxFileSystem::FileNameToURL(fileName));
529     if (fontFile)
530     {
531       fontStream = fontFile->GetStream();
532     }
533     else
534     {
535       // usually this should not happen since file accessability was already checked
536       wxLogError(wxString(wxS("wxPdfFontDataOpenTypeUnicode::WriteFontData: ")) +
537                  wxString::Format(_("Font file '%s' not found."), fileName.GetFullPath().c_str()));
538     }
539   }
540 
541   if (fontStream != NULL)
542   {
543     if (usedGlyphs != NULL)
544     {
545       if (compressed)
546       {
547         // Uncompress font file
548         wxZlibInputStream zCompressed(*fontStream);
549         wxMemoryOutputStream zUncompressed;
550         zUncompressed.Write(zCompressed);
551         zUncompressed.Close();
552         fontStream = new wxMemoryInputStream(zUncompressed);
553       }
554       else
555       {
556         // Extract CFF stream from font file
557         char* buffer = new char[m_cffLength];
558         fontStream->SeekI(m_cffOffset);
559         fontStream->Read(buffer, m_cffLength);
560         wxMemoryOutputStream cffStream;
561         cffStream.Write(buffer, m_cffLength);
562         cffStream.Close();
563         delete [] buffer;
564         fontStream = new wxMemoryInputStream(cffStream);
565       }
566 
567       // Assemble subset
568       wxPdfFontSubsetCff subset(fileName.GetFullPath());
569       wxMemoryOutputStream* subsetStream = subset.CreateSubset(fontStream, subsetGlyphs, false);
570       if (fontStream != NULL)
571       {
572         delete fontStream;
573       }
574 
575       // Write font subset data
576       wxZlibOutputStream zFontData(*fontData);
577       wxMemoryInputStream tmp(*subsetStream);
578       fontSize1 = tmp.GetSize();
579       zFontData.Write(tmp);
580       zFontData.Close();
581       delete subsetStream;
582     }
583     else
584     {
585       if (!compressed)
586       {
587         // Extract CFF stream from font file
588         char* buffer = new char[m_cffLength];
589         fontStream->SeekI(m_cffOffset);
590         fontStream->Read(buffer, m_cffLength);
591         wxZlibOutputStream zFontData(*fontData);
592         zFontData.Write(buffer, m_cffLength);
593         zFontData.Close();
594         delete [] buffer;
595       }
596       else
597       {
598         fontSize1 = GetSize1();
599         fontData->Write(*fontStream);
600       }
601     }
602   }
603 
604   if (fontFile != NULL)
605   {
606     delete fontFile;
607   }
608 
609   return fontSize1;
610 }
611 
612 size_t
WriteUnicodeMap(wxOutputStream * mapData,const wxPdfEncoding * encoding,wxPdfSortedArrayInt * usedGlyphs,wxPdfChar2GlyphMap * subsetGlyphs)613 wxPdfFontDataOpenTypeUnicode::WriteUnicodeMap(wxOutputStream* mapData,
614                                               const wxPdfEncoding* encoding,
615                                               wxPdfSortedArrayInt* usedGlyphs,
616                                               wxPdfChar2GlyphMap* subsetGlyphs)
617 {
618   wxUnusedVar(encoding);
619   wxPdfGlyphList glyphList(wxPdfFontData::CompareGlyphListEntries);
620   wxPdfChar2GlyphMap::const_iterator charIter = m_gn->begin();
621   for (charIter = m_gn->begin(); charIter != m_gn->end(); ++charIter)
622   {
623     if (usedGlyphs != NULL)
624     {
625       int usedGlyphIndex = usedGlyphs->Index(charIter->second);
626       if (usedGlyphIndex != wxNOT_FOUND)
627       {
628         wxPdfGlyphListEntry* glEntry = new wxPdfGlyphListEntry();
629         if (subsetGlyphs != NULL)
630         {
631           glEntry->m_gid = (*subsetGlyphs)[charIter->second];
632         }
633         else
634         {
635           glEntry->m_gid = charIter->second;
636         }
637         glEntry->m_uid = charIter->first;
638         glyphList.Add(glEntry);
639       }
640     }
641     else
642     {
643       wxPdfGlyphListEntry* glEntry = new wxPdfGlyphListEntry();
644       glEntry->m_gid = charIter->second;
645       glEntry->m_uid = charIter->first;
646       glyphList.Add(glEntry);
647     }
648   }
649   wxMemoryOutputStream toUnicode;
650   WriteToUnicode(glyphList, toUnicode);
651   wxMemoryInputStream inUnicode(toUnicode);
652   wxZlibOutputStream zUnicodeMap(*mapData);
653   zUnicodeMap.Write(inUnicode);
654   zUnicodeMap.Close();
655 
656   WX_CLEAR_ARRAY(glyphList);
657 
658   return 0;
659 }
660 
661 #endif // wxUSE_UNICODE
662