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