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