1 | //======================================================================== |
2 | // |
3 | // FoFiTrueType.cc |
4 | // |
5 | // Copyright 1999-2003 Glyph & Cog, LLC |
6 | // |
7 | //======================================================================== |
8 | |
9 | //======================================================================== |
10 | // |
11 | // Modified under the Poppler project - http://poppler.freedesktop.org |
12 | // |
13 | // All changes made under the Poppler project to this file are licensed |
14 | // under GPL version 2 or later |
15 | // |
16 | // Copyright (C) 2006 Takashi Iwai <tiwai@suse.de> |
17 | // Copyright (C) 2007 Koji Otani <sho@bbr.jp> |
18 | // Copyright (C) 2007 Carlos Garcia Campos <carlosgc@gnome.org> |
19 | // Copyright (C) 2008, 2009, 2012, 2014-2022 Albert Astals Cid <aacid@kde.org> |
20 | // Copyright (C) 2008 Tomas Are Haavet <tomasare@gmail.com> |
21 | // Copyright (C) 2012 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
22 | // Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com> |
23 | // Copyright (C) 2014 Thomas Freitag <Thomas.Freitag@alfa.de> |
24 | // Copyright (C) 2015 Aleksei Volkov <Aleksei Volkov> |
25 | // Copyright (C) 2015, 2016 William Bader <williambader@hotmail.com> |
26 | // Copyright (C) 2018 Adam Reichold <adam.reichold@t-online.de> |
27 | // Copyright (C) 2022 Zachary Travis <ztravis@everlaw.com> |
28 | // Copyright (C) 2022 Oliver Sander <oliver.sander@tu-dresden.de> |
29 | // |
30 | // To see a description of the changes please see the Changelog file that |
31 | // came with your tarball or type make ChangeLog if you are building from git |
32 | // |
33 | //======================================================================== |
34 | |
35 | #include <config.h> |
36 | |
37 | #include <cstdlib> |
38 | #include <cstring> |
39 | #include <climits> |
40 | #include <algorithm> |
41 | #include "goo/gmem.h" |
42 | #include "goo/GooLikely.h" |
43 | #include "goo/GooString.h" |
44 | #include "FoFiType1C.h" |
45 | #include "FoFiTrueType.h" |
46 | #include "poppler/Error.h" |
47 | |
48 | // |
49 | // Terminology |
50 | // ----------- |
51 | // |
52 | // character code = number used as an element of a text string |
53 | // |
54 | // character name = glyph name = name for a particular glyph within a |
55 | // font |
56 | // |
57 | // glyph index = GID = position (within some internal table in the font) |
58 | // where the instructions to draw a particular glyph are |
59 | // stored |
60 | // |
61 | // Type 1 fonts |
62 | // ------------ |
63 | // |
64 | // Type 1 fonts contain: |
65 | // |
66 | // Encoding: array of glyph names, maps char codes to glyph names |
67 | // |
68 | // Encoding[charCode] = charName |
69 | // |
70 | // CharStrings: dictionary of instructions, keyed by character names, |
71 | // maps character name to glyph data |
72 | // |
73 | // CharStrings[charName] = glyphData |
74 | // |
75 | // TrueType fonts |
76 | // -------------- |
77 | // |
78 | // TrueType fonts contain: |
79 | // |
80 | // 'cmap' table: mapping from character code to glyph index; there may |
81 | // be multiple cmaps in a TrueType font |
82 | // |
83 | // cmap[charCode] = gid |
84 | // |
85 | // 'post' table: mapping from glyph index to glyph name |
86 | // |
87 | // post[gid] = glyphName |
88 | // |
89 | // Type 42 fonts |
90 | // ------------- |
91 | // |
92 | // Type 42 fonts contain: |
93 | // |
94 | // Encoding: array of glyph names, maps char codes to glyph names |
95 | // |
96 | // Encoding[charCode] = charName |
97 | // |
98 | // CharStrings: dictionary of glyph indexes, keyed by character names, |
99 | // maps character name to glyph index |
100 | // |
101 | // CharStrings[charName] = gid |
102 | // |
103 | |
104 | //------------------------------------------------------------------------ |
105 | |
106 | #define ttcfTag 0x74746366 |
107 | |
108 | //------------------------------------------------------------------------ |
109 | |
110 | struct TrueTypeTable |
111 | { |
112 | unsigned int tag; |
113 | unsigned int checksum; |
114 | int offset; |
115 | int origOffset; |
116 | int len; |
117 | }; |
118 | |
119 | struct TrueTypeCmap |
120 | { |
121 | int platform; |
122 | int encoding; |
123 | int offset; |
124 | int len; |
125 | int fmt; |
126 | }; |
127 | |
128 | struct TrueTypeLoca |
129 | { |
130 | int idx; |
131 | int origOffset; |
132 | int newOffset; |
133 | int len; |
134 | }; |
135 | |
136 | #define cmapTag 0x636d6170 |
137 | #define glyfTag 0x676c7966 |
138 | #define headTag 0x68656164 |
139 | #define hheaTag 0x68686561 |
140 | #define hmtxTag 0x686d7478 |
141 | #define locaTag 0x6c6f6361 |
142 | #define nameTag 0x6e616d65 |
143 | #define os2Tag 0x4f532f32 |
144 | #define postTag 0x706f7374 |
145 | #define vrt2Tag 0x76727432 |
146 | #define vertTag 0x76657274 |
147 | |
148 | struct cmpTrueTypeLocaOffsetFunctor |
149 | { |
150 | bool operator()(const TrueTypeLoca loca1, const TrueTypeLoca loca2) |
151 | { |
152 | if (loca1.origOffset == loca2.origOffset) { |
153 | return loca1.idx < loca2.idx; |
154 | } |
155 | return loca1.origOffset < loca2.origOffset; |
156 | } |
157 | }; |
158 | |
159 | struct cmpTrueTypeLocaIdxFunctor |
160 | { |
161 | bool operator()(const TrueTypeLoca loca1, const TrueTypeLoca loca2) { return loca1.idx < loca2.idx; } |
162 | }; |
163 | |
164 | struct cmpTrueTypeTableTagFunctor |
165 | { |
166 | bool operator()(const TrueTypeTable &tab1, const TrueTypeTable &tab2) { return tab1.tag < tab2.tag; } |
167 | }; |
168 | |
169 | //------------------------------------------------------------------------ |
170 | |
171 | struct T42Table |
172 | { |
173 | const char *tag; // 4-byte tag |
174 | bool required; // required by the TrueType spec? |
175 | }; |
176 | |
177 | // TrueType tables to be embedded in Type 42 fonts. |
178 | // NB: the table names must be in alphabetical order here. |
179 | #define nT42Tables 11 |
180 | static const T42Table t42Tables[nT42Tables] = { { .tag: "cvt " , .required: true }, { .tag: "fpgm" , .required: true }, { .tag: "glyf" , .required: true }, { .tag: "head" , .required: true }, { .tag: "hhea" , .required: true }, { .tag: "hmtx" , .required: true }, |
181 | { .tag: "loca" , .required: true }, { .tag: "maxp" , .required: true }, { .tag: "prep" , .required: true }, { .tag: "vhea" , .required: false }, { .tag: "vmtx" , .required: false } }; |
182 | #define t42HeadTable 3 |
183 | #define t42LocaTable 6 |
184 | #define t42GlyfTable 2 |
185 | #define t42VheaTable 9 |
186 | #define t42VmtxTable 10 |
187 | |
188 | //------------------------------------------------------------------------ |
189 | |
190 | // Glyph names in some arbitrary standard order that Apple uses for |
191 | // their TrueType fonts. |
192 | static const char *macGlyphNames[258] = { ".notdef" , |
193 | "null" , |
194 | "CR" , |
195 | "space" , |
196 | "exclam" , |
197 | "quotedbl" , |
198 | "numbersign" , |
199 | "dollar" , |
200 | "percent" , |
201 | "ampersand" , |
202 | "quotesingle" , |
203 | "parenleft" , |
204 | "parenright" , |
205 | "asterisk" , |
206 | "plus" , |
207 | "comma" , |
208 | "hyphen" , |
209 | "period" , |
210 | "slash" , |
211 | "zero" , |
212 | "one" , |
213 | "two" , |
214 | "three" , |
215 | "four" , |
216 | "five" , |
217 | "six" , |
218 | "seven" , |
219 | "eight" , |
220 | "nine" , |
221 | "colon" , |
222 | "semicolon" , |
223 | "less" , |
224 | "equal" , |
225 | "greater" , |
226 | "question" , |
227 | "at" , |
228 | "A" , |
229 | "B" , |
230 | "C" , |
231 | "D" , |
232 | "E" , |
233 | "F" , |
234 | "G" , |
235 | "H" , |
236 | "I" , |
237 | "J" , |
238 | "K" , |
239 | "L" , |
240 | "M" , |
241 | "N" , |
242 | "O" , |
243 | "P" , |
244 | "Q" , |
245 | "R" , |
246 | "S" , |
247 | "T" , |
248 | "U" , |
249 | "V" , |
250 | "W" , |
251 | "X" , |
252 | "Y" , |
253 | "Z" , |
254 | "bracketleft" , |
255 | "backslash" , |
256 | "bracketright" , |
257 | "asciicircum" , |
258 | "underscore" , |
259 | "grave" , |
260 | "a" , |
261 | "b" , |
262 | "c" , |
263 | "d" , |
264 | "e" , |
265 | "f" , |
266 | "g" , |
267 | "h" , |
268 | "i" , |
269 | "j" , |
270 | "k" , |
271 | "l" , |
272 | "m" , |
273 | "n" , |
274 | "o" , |
275 | "p" , |
276 | "q" , |
277 | "r" , |
278 | "s" , |
279 | "t" , |
280 | "u" , |
281 | "v" , |
282 | "w" , |
283 | "x" , |
284 | "y" , |
285 | "z" , |
286 | "braceleft" , |
287 | "bar" , |
288 | "braceright" , |
289 | "asciitilde" , |
290 | "Adieresis" , |
291 | "Aring" , |
292 | "Ccedilla" , |
293 | "Eacute" , |
294 | "Ntilde" , |
295 | "Odieresis" , |
296 | "Udieresis" , |
297 | "aacute" , |
298 | "agrave" , |
299 | "acircumflex" , |
300 | "adieresis" , |
301 | "atilde" , |
302 | "aring" , |
303 | "ccedilla" , |
304 | "eacute" , |
305 | "egrave" , |
306 | "ecircumflex" , |
307 | "edieresis" , |
308 | "iacute" , |
309 | "igrave" , |
310 | "icircumflex" , |
311 | "idieresis" , |
312 | "ntilde" , |
313 | "oacute" , |
314 | "ograve" , |
315 | "ocircumflex" , |
316 | "odieresis" , |
317 | "otilde" , |
318 | "uacute" , |
319 | "ugrave" , |
320 | "ucircumflex" , |
321 | "udieresis" , |
322 | "dagger" , |
323 | "degree" , |
324 | "cent" , |
325 | "sterling" , |
326 | "section" , |
327 | "bullet" , |
328 | "paragraph" , |
329 | "germandbls" , |
330 | "registered" , |
331 | "copyright" , |
332 | "trademark" , |
333 | "acute" , |
334 | "dieresis" , |
335 | "notequal" , |
336 | "AE" , |
337 | "Oslash" , |
338 | "infinity" , |
339 | "plusminus" , |
340 | "lessequal" , |
341 | "greaterequal" , |
342 | "yen" , |
343 | "mu" , |
344 | "partialdiff" , |
345 | "summation" , |
346 | "product" , |
347 | "pi" , |
348 | "integral" , |
349 | "ordfeminine" , |
350 | "ordmasculine" , |
351 | "Omega" , |
352 | "ae" , |
353 | "oslash" , |
354 | "questiondown" , |
355 | "exclamdown" , |
356 | "logicalnot" , |
357 | "radical" , |
358 | "florin" , |
359 | "approxequal" , |
360 | "increment" , |
361 | "guillemotleft" , |
362 | "guillemotright" , |
363 | "ellipsis" , |
364 | "nbspace" , |
365 | "Agrave" , |
366 | "Atilde" , |
367 | "Otilde" , |
368 | "OE" , |
369 | "oe" , |
370 | "endash" , |
371 | "emdash" , |
372 | "quotedblleft" , |
373 | "quotedblright" , |
374 | "quoteleft" , |
375 | "quoteright" , |
376 | "divide" , |
377 | "lozenge" , |
378 | "ydieresis" , |
379 | "Ydieresis" , |
380 | "fraction" , |
381 | "currency" , |
382 | "guilsinglleft" , |
383 | "guilsinglright" , |
384 | "fi" , |
385 | "fl" , |
386 | "daggerdbl" , |
387 | "periodcentered" , |
388 | "quotesinglbase" , |
389 | "quotedblbase" , |
390 | "perthousand" , |
391 | "Acircumflex" , |
392 | "Ecircumflex" , |
393 | "Aacute" , |
394 | "Edieresis" , |
395 | "Egrave" , |
396 | "Iacute" , |
397 | "Icircumflex" , |
398 | "Idieresis" , |
399 | "Igrave" , |
400 | "Oacute" , |
401 | "Ocircumflex" , |
402 | "applelogo" , |
403 | "Ograve" , |
404 | "Uacute" , |
405 | "Ucircumflex" , |
406 | "Ugrave" , |
407 | "dotlessi" , |
408 | "circumflex" , |
409 | "tilde" , |
410 | "overscore" , |
411 | "breve" , |
412 | "dotaccent" , |
413 | "ring" , |
414 | "cedilla" , |
415 | "hungarumlaut" , |
416 | "ogonek" , |
417 | "caron" , |
418 | "Lslash" , |
419 | "lslash" , |
420 | "Scaron" , |
421 | "scaron" , |
422 | "Zcaron" , |
423 | "zcaron" , |
424 | "brokenbar" , |
425 | "Eth" , |
426 | "eth" , |
427 | "Yacute" , |
428 | "yacute" , |
429 | "Thorn" , |
430 | "thorn" , |
431 | "minus" , |
432 | "multiply" , |
433 | "onesuperior" , |
434 | "twosuperior" , |
435 | "threesuperior" , |
436 | "onehalf" , |
437 | "onequarter" , |
438 | "threequarters" , |
439 | "franc" , |
440 | "Gbreve" , |
441 | "gbreve" , |
442 | "Idot" , |
443 | "Scedilla" , |
444 | "scedilla" , |
445 | "Cacute" , |
446 | "cacute" , |
447 | "Ccaron" , |
448 | "ccaron" , |
449 | "dmacron" }; |
450 | |
451 | //------------------------------------------------------------------------ |
452 | // FoFiTrueType |
453 | //------------------------------------------------------------------------ |
454 | |
455 | std::unique_ptr<FoFiTrueType> FoFiTrueType::make(const unsigned char *fileA, int lenA, int faceIndexA) |
456 | { |
457 | // Cannot use std::make_unique, because the constructor is private |
458 | auto ff = new FoFiTrueType(fileA, lenA, false, faceIndexA); |
459 | if (!ff->parsedOk) { |
460 | delete ff; |
461 | return nullptr; |
462 | } |
463 | return std::unique_ptr<FoFiTrueType>(ff); |
464 | } |
465 | |
466 | std::unique_ptr<FoFiTrueType> FoFiTrueType::load(const char *fileName, int faceIndexA) |
467 | { |
468 | char *fileA; |
469 | int lenA; |
470 | |
471 | if (!(fileA = FoFiBase::readFile(fileName, fileLen: &lenA))) { |
472 | return nullptr; |
473 | } |
474 | // Cannot use std::make_unique, because the constructor is private |
475 | auto ff = new FoFiTrueType((unsigned char *)fileA, lenA, true, faceIndexA); |
476 | if (!ff->parsedOk) { |
477 | delete ff; |
478 | return nullptr; |
479 | } |
480 | return std::unique_ptr<FoFiTrueType>(ff); |
481 | } |
482 | |
483 | FoFiTrueType::FoFiTrueType(const unsigned char *fileA, int lenA, bool freeFileDataA, int faceIndexA) : FoFiBase(fileA, lenA, freeFileDataA) |
484 | { |
485 | tables = nullptr; |
486 | nTables = 0; |
487 | cmaps = nullptr; |
488 | nCmaps = 0; |
489 | parsedOk = false; |
490 | faceIndex = faceIndexA; |
491 | gsubFeatureTable = 0; |
492 | gsubLookupList = 0; |
493 | |
494 | parse(); |
495 | } |
496 | |
497 | FoFiTrueType::~FoFiTrueType() |
498 | { |
499 | gfree(p: tables); |
500 | gfree(p: cmaps); |
501 | } |
502 | |
503 | int FoFiTrueType::getNumCmaps() const |
504 | { |
505 | return nCmaps; |
506 | } |
507 | |
508 | int FoFiTrueType::getCmapPlatform(int i) const |
509 | { |
510 | return cmaps[i].platform; |
511 | } |
512 | |
513 | int FoFiTrueType::getCmapEncoding(int i) const |
514 | { |
515 | return cmaps[i].encoding; |
516 | } |
517 | |
518 | int FoFiTrueType::findCmap(int platform, int encoding) const |
519 | { |
520 | int i; |
521 | |
522 | for (i = 0; i < nCmaps; ++i) { |
523 | if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) { |
524 | return i; |
525 | } |
526 | } |
527 | return -1; |
528 | } |
529 | |
530 | int FoFiTrueType::mapCodeToGID(int i, unsigned int c) const |
531 | { |
532 | int gid; |
533 | unsigned int segCnt, segEnd, segStart, segDelta, segOffset; |
534 | unsigned int cmapFirst, cmapLen; |
535 | int pos, a, b, m; |
536 | unsigned int high, low, idx; |
537 | bool ok; |
538 | |
539 | if (i < 0 || i >= nCmaps) { |
540 | return 0; |
541 | } |
542 | ok = true; |
543 | pos = cmaps[i].offset; |
544 | switch (cmaps[i].fmt) { |
545 | case 0: |
546 | if (c + 6 >= (unsigned int)cmaps[i].len) { |
547 | return 0; |
548 | } |
549 | gid = getU8(pos: cmaps[i].offset + 6 + c, ok: &ok); |
550 | break; |
551 | case 2: |
552 | high = c >> 8; |
553 | low = c & 0xFFU; |
554 | idx = getU16BE(pos: pos + 6 + high * 2, ok: &ok); |
555 | segStart = getU16BE(pos: pos + 6 + 512 + idx, ok: &ok); |
556 | segCnt = getU16BE(pos: pos + 6 + 512 + idx + 2, ok: &ok); |
557 | segDelta = getS16BE(pos: pos + 6 + 512 + idx + 4, ok: &ok); |
558 | segOffset = getU16BE(pos: pos + 6 + 512 + idx + 6, ok: &ok); |
559 | if (low < segStart || low >= segStart + segCnt) { |
560 | gid = 0; |
561 | } else { |
562 | int val = getU16BE(pos: pos + 6 + 512 + idx + 6 + segOffset + (low - segStart) * 2, ok: &ok); |
563 | gid = val == 0 ? 0 : (val + segDelta) & 0xFFFFU; |
564 | } |
565 | break; |
566 | case 4: |
567 | segCnt = getU16BE(pos: pos + 6, ok: &ok) / 2; |
568 | a = -1; |
569 | b = segCnt - 1; |
570 | segEnd = getU16BE(pos: pos + 14 + 2 * b, ok: &ok); |
571 | if (c > segEnd) { |
572 | // malformed font -- the TrueType spec requires the last segEnd |
573 | // to be 0xffff |
574 | return 0; |
575 | } |
576 | // invariant: seg[a].end < code <= seg[b].end |
577 | while (b - a > 1 && ok) { |
578 | m = (a + b) / 2; |
579 | segEnd = getU16BE(pos: pos + 14 + 2 * m, ok: &ok); |
580 | if (segEnd < c) { |
581 | a = m; |
582 | } else { |
583 | b = m; |
584 | } |
585 | } |
586 | segStart = getU16BE(pos: pos + 16 + 2 * segCnt + 2 * b, ok: &ok); |
587 | segDelta = getU16BE(pos: pos + 16 + 4 * segCnt + 2 * b, ok: &ok); |
588 | segOffset = getU16BE(pos: pos + 16 + 6 * segCnt + 2 * b, ok: &ok); |
589 | if (c < segStart) { |
590 | return 0; |
591 | } |
592 | if (segOffset == 0) { |
593 | gid = (c + segDelta) & 0xffff; |
594 | } else { |
595 | gid = getU16BE(pos: pos + 16 + 6 * segCnt + 2 * b + segOffset + 2 * (c - segStart), ok: &ok); |
596 | if (gid != 0) { |
597 | gid = (gid + segDelta) & 0xffff; |
598 | } |
599 | } |
600 | break; |
601 | case 6: |
602 | cmapFirst = getU16BE(pos: pos + 6, ok: &ok); |
603 | cmapLen = getU16BE(pos: pos + 8, ok: &ok); |
604 | if (c < cmapFirst || c >= cmapFirst + cmapLen) { |
605 | return 0; |
606 | } |
607 | gid = getU16BE(pos: pos + 10 + 2 * (c - cmapFirst), ok: &ok); |
608 | break; |
609 | case 12: |
610 | case 13: |
611 | segCnt = getU32BE(pos: pos + 12, ok: &ok); |
612 | a = -1; |
613 | b = segCnt - 1; |
614 | segEnd = getU32BE(pos: pos + 16 + 12 * b + 4, ok: &ok); |
615 | if (c > segEnd) { |
616 | return 0; |
617 | } |
618 | // invariant: seg[a].end < code <= seg[b].end |
619 | while (b - a > 1 && ok) { |
620 | m = (a + b) / 2; |
621 | segEnd = getU32BE(pos: pos + 16 + 12 * m + 4, ok: &ok); |
622 | if (segEnd < c) { |
623 | a = m; |
624 | } else { |
625 | b = m; |
626 | } |
627 | } |
628 | segStart = getU32BE(pos: pos + 16 + 12 * b, ok: &ok); |
629 | segDelta = getU32BE(pos: pos + 16 + 12 * b + 8, ok: &ok); |
630 | if (c < segStart) { |
631 | return 0; |
632 | } |
633 | // In format 12, the glyph codes increment through |
634 | // each segment; in format 13 the same glyph code is used |
635 | // for an entire segment. |
636 | gid = segDelta + (cmaps[i].fmt == 12 ? (c - segStart) : 0); |
637 | break; |
638 | default: |
639 | return 0; |
640 | } |
641 | if (!ok) { |
642 | return 0; |
643 | } |
644 | return gid; |
645 | } |
646 | |
647 | int FoFiTrueType::mapNameToGID(const char *name) const |
648 | { |
649 | const auto gid = nameToGID.find(x: name); |
650 | if (gid == nameToGID.end()) { |
651 | return 0; |
652 | } |
653 | return gid->second; |
654 | } |
655 | |
656 | bool FoFiTrueType::getCFFBlock(char **start, int *length) const |
657 | { |
658 | int i; |
659 | |
660 | if (!openTypeCFF || !tables) { |
661 | return false; |
662 | } |
663 | i = seekTable(tag: "CFF " ); |
664 | if (i < 0 || !checkRegion(pos: tables[i].offset, size: tables[i].len)) { |
665 | return false; |
666 | } |
667 | *start = (char *)file + tables[i].offset; |
668 | *length = tables[i].len; |
669 | return true; |
670 | } |
671 | |
672 | int *FoFiTrueType::getCIDToGIDMap(int *nCIDs) const |
673 | { |
674 | char *start; |
675 | int length; |
676 | FoFiType1C *ff; |
677 | int *map; |
678 | |
679 | *nCIDs = 0; |
680 | if (!getCFFBlock(start: &start, length: &length)) { |
681 | return nullptr; |
682 | } |
683 | if (!(ff = FoFiType1C::make(fileA: (unsigned char *)start, lenA: length))) { |
684 | return nullptr; |
685 | } |
686 | map = ff->getCIDToGIDMap(nCIDs); |
687 | delete ff; |
688 | return map; |
689 | } |
690 | |
691 | int FoFiTrueType::getEmbeddingRights() const |
692 | { |
693 | int i, fsType; |
694 | bool ok; |
695 | |
696 | if ((i = seekTable(tag: "OS/2" )) < 0) { |
697 | return 4; |
698 | } |
699 | ok = true; |
700 | fsType = getU16BE(pos: tables[i].offset + 8, ok: &ok); |
701 | if (!ok) { |
702 | return 4; |
703 | } |
704 | if (fsType & 0x0008) { |
705 | return 2; |
706 | } |
707 | if (fsType & 0x0004) { |
708 | return 1; |
709 | } |
710 | if (fsType & 0x0002) { |
711 | return 0; |
712 | } |
713 | return 3; |
714 | } |
715 | |
716 | void FoFiTrueType::getFontMatrix(double *mat) const |
717 | { |
718 | char *start; |
719 | int length; |
720 | FoFiType1C *ff; |
721 | |
722 | if (!getCFFBlock(start: &start, length: &length)) { |
723 | return; |
724 | } |
725 | if (!(ff = FoFiType1C::make(fileA: (unsigned char *)start, lenA: length))) { |
726 | return; |
727 | } |
728 | ff->getFontMatrix(mat); |
729 | delete ff; |
730 | } |
731 | |
732 | void FoFiTrueType::convertToType42(const char *psName, char **encoding, int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const |
733 | { |
734 | int maxUsedGlyph; |
735 | bool ok; |
736 | |
737 | if (openTypeCFF) { |
738 | return; |
739 | } |
740 | |
741 | // write the header |
742 | ok = true; |
743 | std::unique_ptr<GooString> buf = GooString::format(fmt: "%!PS-TrueTypeFont-{0:2g}\n" , (double)getS32BE(pos: 0, ok: &ok) / 65536.0); |
744 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
745 | |
746 | // begin the font dictionary |
747 | (*outputFunc)(outputStream, "10 dict begin\n" , 14); |
748 | (*outputFunc)(outputStream, "/FontName /" , 11); |
749 | (*outputFunc)(outputStream, psName, strlen(s: psName)); |
750 | (*outputFunc)(outputStream, " def\n" , 5); |
751 | (*outputFunc)(outputStream, "/FontType 42 def\n" , 17); |
752 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n" , 30); |
753 | buf = GooString::format(fmt: "/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n" , bbox[0], bbox[1], bbox[2], bbox[3]); |
754 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
755 | (*outputFunc)(outputStream, "/PaintType 0 def\n" , 17); |
756 | |
757 | // write the guts of the dictionary |
758 | cvtEncoding(encoding, outputFunc, outputStream); |
759 | cvtCharStrings(encoding, codeToGID, outputFunc, outputStream); |
760 | cvtSfnts(outputFunc, outputStream, name: nullptr, needVerticalMetrics: false, maxUsedGlyph: &maxUsedGlyph); |
761 | |
762 | // end the dictionary and define the font |
763 | (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n" , 40); |
764 | } |
765 | |
766 | void FoFiTrueType::convertToType1(const char *psName, const char **newEncoding, bool ascii, FoFiOutputFunc outputFunc, void *outputStream) const |
767 | { |
768 | char *start; |
769 | int length; |
770 | FoFiType1C *ff; |
771 | |
772 | if (!getCFFBlock(start: &start, length: &length)) { |
773 | return; |
774 | } |
775 | if (!(ff = FoFiType1C::make(fileA: (unsigned char *)start, lenA: length))) { |
776 | return; |
777 | } |
778 | ff->convertToType1(psName, newEncoding, ascii, outputFunc, outputStream); |
779 | delete ff; |
780 | } |
781 | |
782 | void FoFiTrueType::convertToCIDType2(const char *psName, const int *cidMap, int nCIDs, bool needVerticalMetrics, FoFiOutputFunc outputFunc, void *outputStream) const |
783 | { |
784 | int cid, maxUsedGlyph; |
785 | bool ok; |
786 | int i, j, k; |
787 | |
788 | if (openTypeCFF) { |
789 | return; |
790 | } |
791 | |
792 | // write the header |
793 | ok = true; |
794 | std::unique_ptr<GooString> buf = GooString::format(fmt: "%!PS-TrueTypeFont-{0:2g}\n" , (double)getS32BE(pos: 0, ok: &ok) / 65536.0); |
795 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
796 | |
797 | // begin the font dictionary |
798 | (*outputFunc)(outputStream, "20 dict begin\n" , 14); |
799 | (*outputFunc)(outputStream, "/CIDFontName /" , 14); |
800 | (*outputFunc)(outputStream, psName, strlen(s: psName)); |
801 | (*outputFunc)(outputStream, " def\n" , 5); |
802 | (*outputFunc)(outputStream, "/CIDFontType 2 def\n" , 19); |
803 | (*outputFunc)(outputStream, "/FontType 42 def\n" , 17); |
804 | (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n" , 32); |
805 | (*outputFunc)(outputStream, " /Registry (Adobe) def\n" , 24); |
806 | (*outputFunc)(outputStream, " /Ordering (Identity) def\n" , 27); |
807 | (*outputFunc)(outputStream, " /Supplement 0 def\n" , 20); |
808 | (*outputFunc)(outputStream, " end def\n" , 10); |
809 | (*outputFunc)(outputStream, "/GDBytes 2 def\n" , 15); |
810 | if (cidMap) { |
811 | buf = GooString::format(fmt: "/CIDCount {0:d} def\n" , nCIDs); |
812 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
813 | if (nCIDs > 32767) { |
814 | (*outputFunc)(outputStream, "/CIDMap [" , 9); |
815 | for (i = 0; i < nCIDs; i += 32768 - 16) { |
816 | (*outputFunc)(outputStream, "<\n" , 2); |
817 | for (j = 0; j < 32768 - 16 && i + j < nCIDs; j += 16) { |
818 | (*outputFunc)(outputStream, " " , 2); |
819 | for (k = 0; k < 16 && i + j + k < nCIDs; ++k) { |
820 | cid = cidMap[i + j + k]; |
821 | buf = GooString::format(fmt: "{0:02x}{1:02x}" , (cid >> 8) & 0xff, cid & 0xff); |
822 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
823 | } |
824 | (*outputFunc)(outputStream, "\n" , 1); |
825 | } |
826 | (*outputFunc)(outputStream, " >" , 3); |
827 | } |
828 | (*outputFunc)(outputStream, "\n" , 1); |
829 | (*outputFunc)(outputStream, "] def\n" , 6); |
830 | } else { |
831 | (*outputFunc)(outputStream, "/CIDMap <\n" , 10); |
832 | for (i = 0; i < nCIDs; i += 16) { |
833 | (*outputFunc)(outputStream, " " , 2); |
834 | for (j = 0; j < 16 && i + j < nCIDs; ++j) { |
835 | cid = cidMap[i + j]; |
836 | buf = GooString::format(fmt: "{0:02x}{1:02x}" , (cid >> 8) & 0xff, cid & 0xff); |
837 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
838 | } |
839 | (*outputFunc)(outputStream, "\n" , 1); |
840 | } |
841 | (*outputFunc)(outputStream, "> def\n" , 6); |
842 | } |
843 | } else { |
844 | // direct mapping - just fill the string(s) with s[i]=i |
845 | buf = GooString::format(fmt: "/CIDCount {0:d} def\n" , nGlyphs); |
846 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
847 | if (nGlyphs > 32767) { |
848 | (*outputFunc)(outputStream, "/CIDMap [\n" , 10); |
849 | for (i = 0; i < nGlyphs; i += 32767) { |
850 | j = nGlyphs - i < 32767 ? nGlyphs - i : 32767; |
851 | buf = GooString::format(fmt: " {0:d} string 0 1 {1:d} {{\n" , 2 * j, j - 1); |
852 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
853 | buf = GooString::format(fmt: " 2 copy dup 2 mul exch {0:d} add -8 bitshift put\n" , i); |
854 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
855 | buf = GooString::format(fmt: " 1 index exch dup 2 mul 1 add exch {0:d} add" |
856 | " 255 and put\n" , |
857 | i); |
858 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
859 | (*outputFunc)(outputStream, " } for\n" , 8); |
860 | } |
861 | (*outputFunc)(outputStream, "] def\n" , 6); |
862 | } else { |
863 | buf = GooString::format(fmt: "/CIDMap {0:d} string\n" , 2 * nGlyphs); |
864 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
865 | buf = GooString::format(fmt: " 0 1 {0:d} {{\n" , nGlyphs - 1); |
866 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
867 | (*outputFunc)(outputStream, " 2 copy dup 2 mul exch -8 bitshift put\n" , 42); |
868 | (*outputFunc)(outputStream, " 1 index exch dup 2 mul 1 add exch 255 and put\n" , 50); |
869 | (*outputFunc)(outputStream, " } for\n" , 8); |
870 | (*outputFunc)(outputStream, "def\n" , 4); |
871 | } |
872 | } |
873 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n" , 30); |
874 | buf = GooString::format(fmt: "/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n" , bbox[0], bbox[1], bbox[2], bbox[3]); |
875 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
876 | (*outputFunc)(outputStream, "/PaintType 0 def\n" , 17); |
877 | (*outputFunc)(outputStream, "/Encoding [] readonly def\n" , 26); |
878 | (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n" , 30); |
879 | (*outputFunc)(outputStream, " /.notdef 0 def\n" , 17); |
880 | (*outputFunc)(outputStream, " end readonly def\n" , 19); |
881 | |
882 | // write the guts of the dictionary |
883 | cvtSfnts(outputFunc, outputStream, name: nullptr, needVerticalMetrics, maxUsedGlyph: &maxUsedGlyph); |
884 | |
885 | // end the dictionary and define the font |
886 | (*outputFunc)(outputStream, "CIDFontName currentdict end /CIDFont defineresource pop\n" , 56); |
887 | } |
888 | |
889 | void FoFiTrueType::convertToCIDType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const |
890 | { |
891 | char *start; |
892 | int length; |
893 | FoFiType1C *ff; |
894 | |
895 | if (!getCFFBlock(start: &start, length: &length)) { |
896 | return; |
897 | } |
898 | if (!(ff = FoFiType1C::make(fileA: (unsigned char *)start, lenA: length))) { |
899 | return; |
900 | } |
901 | ff->convertToCIDType0(psName, codeMap: cidMap, nCodes: nCIDs, outputFunc, outputStream); |
902 | delete ff; |
903 | } |
904 | |
905 | void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, bool needVerticalMetrics, int *maxValidGlyph, FoFiOutputFunc outputFunc, void *outputStream) const |
906 | { |
907 | GooString *sfntsName; |
908 | int maxUsedGlyph, n, i, j; |
909 | |
910 | *maxValidGlyph = -1; |
911 | |
912 | if (openTypeCFF) { |
913 | return; |
914 | } |
915 | |
916 | // write the Type 42 sfnts array |
917 | sfntsName = (new GooString(psName))->append(str: "_sfnts" ); |
918 | cvtSfnts(outputFunc, outputStream, name: sfntsName, needVerticalMetrics, maxUsedGlyph: &maxUsedGlyph); |
919 | delete sfntsName; |
920 | |
921 | // write the descendant Type 42 fonts |
922 | // (The following is a kludge: nGlyphs is the glyph count from the |
923 | // maxp table; maxUsedGlyph is the max glyph number that has a |
924 | // non-zero-length description, from the loca table. The problem is |
925 | // that some TrueType font subsets fail to change the glyph count, |
926 | // i.e., nGlyphs is much larger than maxUsedGlyph+1, which results |
927 | // in an unnecessarily huge Type 0 font. But some other PDF files |
928 | // have fonts with only zero or one used glyph, and a content stream |
929 | // that refers to one of the unused glyphs -- this results in PS |
930 | // errors if we simply use maxUsedGlyph+1 for the Type 0 font. So |
931 | // we compromise by always defining at least 256 glyphs.) |
932 | // Some fonts have a large nGlyphs but maxUsedGlyph of 0. |
933 | // These fonts might reference any glyph. |
934 | // Return the last written glyph number in maxValidGlyph. |
935 | // PSOutputDev::drawString() can use maxValidGlyph to avoid |
936 | // referencing zero-length glyphs that we trimmed. |
937 | // This allows pdftops to avoid writing huge files while still |
938 | // handling the rare PDF that uses a zero-length glyph. |
939 | if (cidMap) { |
940 | n = nCIDs; |
941 | } else if (nGlyphs > maxUsedGlyph + 256) { |
942 | if (maxUsedGlyph <= 255) { |
943 | n = 256; |
944 | } else { |
945 | n = maxUsedGlyph + 1; |
946 | } |
947 | } else { |
948 | n = nGlyphs; |
949 | } |
950 | *maxValidGlyph = n - 1; |
951 | for (i = 0; i < n; i += 256) { |
952 | (*outputFunc)(outputStream, "10 dict begin\n" , 14); |
953 | (*outputFunc)(outputStream, "/FontName /" , 11); |
954 | (*outputFunc)(outputStream, psName, strlen(s: psName)); |
955 | std::unique_ptr<GooString> buf = GooString::format(fmt: "_{0:02x} def\n" , i >> 8); |
956 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
957 | (*outputFunc)(outputStream, "/FontType 42 def\n" , 17); |
958 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n" , 30); |
959 | buf = GooString::format(fmt: "/FontBBox [{0:d} {1:d} {2:d} {3:d}] def\n" , bbox[0], bbox[1], bbox[2], bbox[3]); |
960 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
961 | (*outputFunc)(outputStream, "/PaintType 0 def\n" , 17); |
962 | (*outputFunc)(outputStream, "/sfnts " , 7); |
963 | (*outputFunc)(outputStream, psName, strlen(s: psName)); |
964 | (*outputFunc)(outputStream, "_sfnts def\n" , 11); |
965 | (*outputFunc)(outputStream, "/Encoding 256 array\n" , 20); |
966 | for (j = 0; j < 256 && i + j < n; ++j) { |
967 | buf = GooString::format(fmt: "dup {0:d} /c{1:02x} put\n" , j, j); |
968 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
969 | } |
970 | (*outputFunc)(outputStream, "readonly def\n" , 13); |
971 | (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n" , 32); |
972 | (*outputFunc)(outputStream, "/.notdef 0 def\n" , 15); |
973 | for (j = 0; j < 256 && i + j < n; ++j) { |
974 | buf = GooString::format(fmt: "/c{0:02x} {1:d} def\n" , j, cidMap ? cidMap[i + j] : i + j); |
975 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
976 | } |
977 | (*outputFunc)(outputStream, "end readonly def\n" , 17); |
978 | (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n" , 40); |
979 | } |
980 | |
981 | // write the Type 0 parent font |
982 | (*outputFunc)(outputStream, "16 dict begin\n" , 14); |
983 | (*outputFunc)(outputStream, "/FontName /" , 11); |
984 | (*outputFunc)(outputStream, psName, strlen(s: psName)); |
985 | (*outputFunc)(outputStream, " def\n" , 5); |
986 | (*outputFunc)(outputStream, "/FontType 0 def\n" , 16); |
987 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n" , 30); |
988 | (*outputFunc)(outputStream, "/FMapType 2 def\n" , 16); |
989 | (*outputFunc)(outputStream, "/Encoding [\n" , 12); |
990 | for (i = 0; i < n; i += 256) { |
991 | const std::unique_ptr<GooString> buf = GooString::format(fmt: "{0:d}\n" , i >> 8); |
992 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
993 | } |
994 | (*outputFunc)(outputStream, "] def\n" , 6); |
995 | (*outputFunc)(outputStream, "/FDepVector [\n" , 14); |
996 | for (i = 0; i < n; i += 256) { |
997 | (*outputFunc)(outputStream, "/" , 1); |
998 | (*outputFunc)(outputStream, psName, strlen(s: psName)); |
999 | const std::unique_ptr<GooString> buf = GooString::format(fmt: "_{0:02x} findfont\n" , i >> 8); |
1000 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
1001 | } |
1002 | (*outputFunc)(outputStream, "] def\n" , 6); |
1003 | (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n" , 40); |
1004 | } |
1005 | |
1006 | void FoFiTrueType::convertToType0(const char *psName, int *cidMap, int nCIDs, FoFiOutputFunc outputFunc, void *outputStream) const |
1007 | { |
1008 | char *start; |
1009 | int length; |
1010 | FoFiType1C *ff; |
1011 | |
1012 | if (!getCFFBlock(start: &start, length: &length)) { |
1013 | return; |
1014 | } |
1015 | if (!(ff = FoFiType1C::make(fileA: (unsigned char *)start, lenA: length))) { |
1016 | return; |
1017 | } |
1018 | ff->convertToType0(psName, codeMap: cidMap, nCodes: nCIDs, outputFunc, outputStream); |
1019 | delete ff; |
1020 | } |
1021 | |
1022 | void FoFiTrueType::cvtEncoding(char **encoding, FoFiOutputFunc outputFunc, void *outputStream) const |
1023 | { |
1024 | const char *name; |
1025 | int i; |
1026 | |
1027 | (*outputFunc)(outputStream, "/Encoding 256 array\n" , 20); |
1028 | if (encoding) { |
1029 | for (i = 0; i < 256; ++i) { |
1030 | if (!(name = encoding[i])) { |
1031 | name = ".notdef" ; |
1032 | } |
1033 | const std::unique_ptr<GooString> buf = GooString::format(fmt: "dup {0:d} /" , i); |
1034 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
1035 | (*outputFunc)(outputStream, name, strlen(s: name)); |
1036 | (*outputFunc)(outputStream, " put\n" , 5); |
1037 | } |
1038 | } else { |
1039 | for (i = 0; i < 256; ++i) { |
1040 | const std::unique_ptr<GooString> buf = GooString::format(fmt: "dup {0:d} /c{1:02x} put\n" , i, i); |
1041 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
1042 | } |
1043 | } |
1044 | (*outputFunc)(outputStream, "readonly def\n" , 13); |
1045 | } |
1046 | |
1047 | void FoFiTrueType::cvtCharStrings(char **encoding, const int *codeToGID, FoFiOutputFunc outputFunc, void *outputStream) const |
1048 | { |
1049 | const char *name; |
1050 | char buf2[16]; |
1051 | int i, k; |
1052 | |
1053 | // always define '.notdef' |
1054 | (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n" , 32); |
1055 | (*outputFunc)(outputStream, "/.notdef 0 def\n" , 15); |
1056 | |
1057 | // if there's no 'cmap' table, punt |
1058 | if (nCmaps == 0) { |
1059 | goto err; |
1060 | } |
1061 | |
1062 | // map char name to glyph index: |
1063 | // 1. use encoding to map name to char code |
1064 | // 2. use codeToGID to map char code to glyph index |
1065 | // N.B. We do this in reverse order because font subsets can have |
1066 | // weird encodings that use the same character name twice, and |
1067 | // the first definition is probably the one we want. |
1068 | k = 0; // make gcc happy |
1069 | for (i = 255; i >= 0; --i) { |
1070 | if (encoding) { |
1071 | name = encoding[i]; |
1072 | } else { |
1073 | sprintf(s: buf2, format: "c%02x" , i); |
1074 | name = buf2; |
1075 | } |
1076 | if (name && strcmp(s1: name, s2: ".notdef" )) { |
1077 | k = codeToGID[i]; |
1078 | // note: Distiller (maybe Adobe's PS interpreter in general) |
1079 | // doesn't like TrueType fonts that have CharStrings entries |
1080 | // which point to nonexistent glyphs, hence the (k < nGlyphs) |
1081 | // test |
1082 | if (k > 0 && k < nGlyphs) { |
1083 | (*outputFunc)(outputStream, "/" , 1); |
1084 | (*outputFunc)(outputStream, name, strlen(s: name)); |
1085 | const std::unique_ptr<GooString> buf = GooString::format(fmt: " {0:d} def\n" , k); |
1086 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
1087 | } |
1088 | } |
1089 | } |
1090 | |
1091 | err: |
1092 | (*outputFunc)(outputStream, "end readonly def\n" , 17); |
1093 | } |
1094 | |
1095 | void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, void *outputStream, const GooString *name, bool needVerticalMetrics, int *maxUsedGlyph) const |
1096 | { |
1097 | unsigned char headData[54]; |
1098 | TrueTypeLoca *locaTable; |
1099 | unsigned char *locaData; |
1100 | TrueTypeTable newTables[nT42Tables]; |
1101 | unsigned char tableDir[12 + nT42Tables * 16]; |
1102 | bool ok; |
1103 | unsigned int checksum; |
1104 | int nNewTables; |
1105 | int glyfTableLen, length, pos, glyfPos, i, j, k, vmtxTabLength; |
1106 | unsigned char vheaTab[36] = { |
1107 | 0, 1, 0, 0, // table version number |
1108 | 0, 0, // ascent |
1109 | 0, 0, // descent |
1110 | 0, 0, // reserved |
1111 | 0, 0, // max advance height |
1112 | 0, 0, // min top side bearing |
1113 | 0, 0, // min bottom side bearing |
1114 | 0, 0, // y max extent |
1115 | 0, 0, // caret slope rise |
1116 | 0, 1, // caret slope run |
1117 | 0, 0, // caret offset |
1118 | 0, 0, // reserved |
1119 | 0, 0, // reserved |
1120 | 0, 0, // reserved |
1121 | 0, 0, // reserved |
1122 | 0, 0, // metric data format |
1123 | 0, 1 // number of advance heights in vmtx table |
1124 | }; |
1125 | unsigned char *vmtxTab; |
1126 | bool needVhea, needVmtx; |
1127 | int advance; |
1128 | |
1129 | *maxUsedGlyph = -1; |
1130 | |
1131 | // construct the 'head' table, zero out the font checksum |
1132 | i = seekTable(tag: "head" ); |
1133 | if (i < 0 || i >= nTables) { |
1134 | return; |
1135 | } |
1136 | pos = tables[i].offset; |
1137 | if (!checkRegion(pos, size: 54)) { |
1138 | return; |
1139 | } |
1140 | memcpy(dest: headData, src: file + pos, n: 54); |
1141 | headData[8] = headData[9] = headData[10] = headData[11] = (unsigned char)0; |
1142 | |
1143 | // check for a bogus loca format field in the 'head' table |
1144 | // (I've encountered fonts with loca format set to 0x0100 instead of 0x0001) |
1145 | if (locaFmt != 0 && locaFmt != 1) { |
1146 | headData[50] = 0; |
1147 | headData[51] = 1; |
1148 | } |
1149 | |
1150 | // read the original 'loca' table, pad entries out to 4 bytes, and |
1151 | // sort it into proper order -- some (non-compliant) fonts have |
1152 | // out-of-order loca tables; in order to correctly handle the case |
1153 | // where (compliant) fonts have empty entries in the middle of the |
1154 | // table, cmpTrueTypeLocaOffset uses offset as its primary sort key, |
1155 | // and idx as its secondary key (ensuring that adjacent entries with |
1156 | // the same pos value remain in the same order) |
1157 | locaTable = (TrueTypeLoca *)gmallocn(count: nGlyphs + 1, size: sizeof(TrueTypeLoca)); |
1158 | i = seekTable(tag: "loca" ); |
1159 | pos = tables[i].offset; |
1160 | i = seekTable(tag: "glyf" ); |
1161 | glyfTableLen = tables[i].len; |
1162 | ok = true; |
1163 | for (i = 0; i <= nGlyphs; ++i) { |
1164 | locaTable[i].idx = i; |
1165 | if (locaFmt) { |
1166 | locaTable[i].origOffset = (int)getU32BE(pos: pos + i * 4, ok: &ok); |
1167 | } else { |
1168 | locaTable[i].origOffset = 2 * getU16BE(pos: pos + i * 2, ok: &ok); |
1169 | } |
1170 | if (locaTable[i].origOffset > glyfTableLen) { |
1171 | locaTable[i].origOffset = glyfTableLen; |
1172 | } |
1173 | } |
1174 | std::sort(first: locaTable, last: locaTable + nGlyphs + 1, comp: cmpTrueTypeLocaOffsetFunctor()); |
1175 | for (i = 0; i < nGlyphs; ++i) { |
1176 | locaTable[i].len = locaTable[i + 1].origOffset - locaTable[i].origOffset; |
1177 | } |
1178 | locaTable[nGlyphs].len = 0; |
1179 | std::sort(first: locaTable, last: locaTable + nGlyphs + 1, comp: cmpTrueTypeLocaIdxFunctor()); |
1180 | pos = 0; |
1181 | for (i = 0; i <= nGlyphs; ++i) { |
1182 | locaTable[i].newOffset = pos; |
1183 | |
1184 | int newPos; |
1185 | if (unlikely(checkedAdd(pos, locaTable[i].len, &newPos))) { |
1186 | ok = false; |
1187 | } else { |
1188 | pos = newPos; |
1189 | if (pos & 3) { |
1190 | pos += 4 - (pos & 3); |
1191 | } |
1192 | } |
1193 | if (locaTable[i].len > 0) { |
1194 | *maxUsedGlyph = i; |
1195 | } |
1196 | } |
1197 | |
1198 | // construct the new 'loca' table |
1199 | locaData = (unsigned char *)gmallocn(count: nGlyphs + 1, size: (locaFmt ? 4 : 2)); |
1200 | for (i = 0; i <= nGlyphs; ++i) { |
1201 | pos = locaTable[i].newOffset; |
1202 | if (locaFmt) { |
1203 | locaData[4 * i] = (unsigned char)(pos >> 24); |
1204 | locaData[4 * i + 1] = (unsigned char)(pos >> 16); |
1205 | locaData[4 * i + 2] = (unsigned char)(pos >> 8); |
1206 | locaData[4 * i + 3] = (unsigned char)pos; |
1207 | } else { |
1208 | locaData[2 * i] = (unsigned char)(pos >> 9); |
1209 | locaData[2 * i + 1] = (unsigned char)(pos >> 1); |
1210 | } |
1211 | } |
1212 | |
1213 | // count the number of tables |
1214 | nNewTables = 0; |
1215 | for (i = 0; i < nT42Tables; ++i) { |
1216 | if (t42Tables[i].required || seekTable(tag: t42Tables[i].tag) >= 0) { |
1217 | ++nNewTables; |
1218 | } |
1219 | } |
1220 | vmtxTab = nullptr; // make gcc happy |
1221 | vmtxTabLength = 0; |
1222 | advance = 0; // make gcc happy |
1223 | if (needVerticalMetrics) { |
1224 | needVhea = seekTable(tag: "vhea" ) < 0; |
1225 | needVmtx = seekTable(tag: "vmtx" ) < 0; |
1226 | if (needVhea || needVmtx) { |
1227 | i = seekTable(tag: "head" ); |
1228 | advance = getU16BE(pos: tables[i].offset + 18, ok: &ok); // units per em |
1229 | if (needVhea) { |
1230 | ++nNewTables; |
1231 | } |
1232 | if (needVmtx) { |
1233 | ++nNewTables; |
1234 | } |
1235 | } |
1236 | } |
1237 | |
1238 | // construct the new table headers, including table checksums |
1239 | // (pad each table out to a multiple of 4 bytes) |
1240 | pos = 12 + nNewTables * 16; |
1241 | k = 0; |
1242 | for (i = 0; i < nT42Tables; ++i) { |
1243 | length = -1; |
1244 | checksum = 0; // make gcc happy |
1245 | if (i == t42HeadTable) { |
1246 | length = 54; |
1247 | checksum = computeTableChecksum(data: headData, length: 54); |
1248 | } else if (i == t42LocaTable) { |
1249 | length = (nGlyphs + 1) * (locaFmt ? 4 : 2); |
1250 | checksum = computeTableChecksum(data: locaData, length); |
1251 | } else if (i == t42GlyfTable) { |
1252 | length = 0; |
1253 | checksum = 0; |
1254 | glyfPos = tables[seekTable(tag: "glyf" )].offset; |
1255 | for (j = 0; j < nGlyphs; ++j) { |
1256 | length += locaTable[j].len; |
1257 | if (length & 3) { |
1258 | length += 4 - (length & 3); |
1259 | } |
1260 | if (checkRegion(pos: glyfPos + locaTable[j].origOffset, size: locaTable[j].len)) { |
1261 | checksum += computeTableChecksum(data: file + glyfPos + locaTable[j].origOffset, length: locaTable[j].len); |
1262 | } |
1263 | } |
1264 | } else { |
1265 | if ((j = seekTable(tag: t42Tables[i].tag)) >= 0) { |
1266 | length = tables[j].len; |
1267 | if (checkRegion(pos: tables[j].offset, size: length)) { |
1268 | checksum = computeTableChecksum(data: file + tables[j].offset, length); |
1269 | } |
1270 | } else if (needVerticalMetrics && i == t42VheaTable) { |
1271 | vheaTab[10] = advance / 256; // max advance height |
1272 | vheaTab[11] = advance % 256; |
1273 | length = sizeof(vheaTab); |
1274 | checksum = computeTableChecksum(data: vheaTab, length); |
1275 | } else if (needVerticalMetrics && i == t42VmtxTable) { |
1276 | length = 4 + (nGlyphs - 1) * 2; |
1277 | vmtxTabLength = length; |
1278 | vmtxTab = (unsigned char *)gmalloc(size: length); |
1279 | vmtxTab[0] = advance / 256; |
1280 | vmtxTab[1] = advance % 256; |
1281 | for (j = 2; j < length; j += 2) { |
1282 | vmtxTab[j] = 0; |
1283 | vmtxTab[j + 1] = 0; |
1284 | } |
1285 | checksum = computeTableChecksum(data: vmtxTab, length); |
1286 | } else if (t42Tables[i].required) { |
1287 | //~ error(-1, "Embedded TrueType font is missing a required table ('%s')", |
1288 | //~ t42Tables[i].tag); |
1289 | length = 0; |
1290 | checksum = 0; |
1291 | } |
1292 | } |
1293 | if (length >= 0) { |
1294 | newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) | ((t42Tables[i].tag[1] & 0xff) << 16) | ((t42Tables[i].tag[2] & 0xff) << 8) | (t42Tables[i].tag[3] & 0xff); |
1295 | newTables[k].checksum = checksum; |
1296 | newTables[k].offset = pos; |
1297 | newTables[k].len = length; |
1298 | int newPos; |
1299 | if (unlikely(checkedAdd(pos, length, &newPos))) { |
1300 | ok = false; |
1301 | } else { |
1302 | pos = newPos; |
1303 | if (pos & 3) { |
1304 | pos += 4 - (length & 3); |
1305 | } |
1306 | } |
1307 | ++k; |
1308 | } |
1309 | } |
1310 | if (unlikely(k < nNewTables)) { |
1311 | error(category: errSyntaxWarning, pos: -1, msg: "unexpected number of tables" ); |
1312 | nNewTables = k; |
1313 | } |
1314 | |
1315 | // construct the table directory |
1316 | tableDir[0] = 0x00; // sfnt version |
1317 | tableDir[1] = 0x01; |
1318 | tableDir[2] = 0x00; |
1319 | tableDir[3] = 0x00; |
1320 | tableDir[4] = 0; // numTables |
1321 | tableDir[5] = nNewTables; |
1322 | tableDir[6] = 0; // searchRange |
1323 | tableDir[7] = (unsigned char)128; |
1324 | tableDir[8] = 0; // entrySelector |
1325 | tableDir[9] = 3; |
1326 | tableDir[10] = 0; // rangeShift |
1327 | tableDir[11] = (unsigned char)(16 * nNewTables - 128); |
1328 | pos = 12; |
1329 | for (i = 0; i < nNewTables; ++i) { |
1330 | tableDir[pos] = (unsigned char)(newTables[i].tag >> 24); |
1331 | tableDir[pos + 1] = (unsigned char)(newTables[i].tag >> 16); |
1332 | tableDir[pos + 2] = (unsigned char)(newTables[i].tag >> 8); |
1333 | tableDir[pos + 3] = (unsigned char)newTables[i].tag; |
1334 | tableDir[pos + 4] = (unsigned char)(newTables[i].checksum >> 24); |
1335 | tableDir[pos + 5] = (unsigned char)(newTables[i].checksum >> 16); |
1336 | tableDir[pos + 6] = (unsigned char)(newTables[i].checksum >> 8); |
1337 | tableDir[pos + 7] = (unsigned char)newTables[i].checksum; |
1338 | tableDir[pos + 8] = (unsigned char)(newTables[i].offset >> 24); |
1339 | tableDir[pos + 9] = (unsigned char)(newTables[i].offset >> 16); |
1340 | tableDir[pos + 10] = (unsigned char)(newTables[i].offset >> 8); |
1341 | tableDir[pos + 11] = (unsigned char)newTables[i].offset; |
1342 | tableDir[pos + 12] = (unsigned char)(newTables[i].len >> 24); |
1343 | tableDir[pos + 13] = (unsigned char)(newTables[i].len >> 16); |
1344 | tableDir[pos + 14] = (unsigned char)(newTables[i].len >> 8); |
1345 | tableDir[pos + 15] = (unsigned char)newTables[i].len; |
1346 | pos += 16; |
1347 | } |
1348 | |
1349 | // compute the font checksum and store it in the head table |
1350 | checksum = computeTableChecksum(data: tableDir, length: 12 + nNewTables * 16); |
1351 | for (i = 0; i < nNewTables; ++i) { |
1352 | checksum += newTables[i].checksum; |
1353 | } |
1354 | checksum = 0xb1b0afba - checksum; // because the TrueType spec says so |
1355 | headData[8] = (unsigned char)(checksum >> 24); |
1356 | headData[9] = (unsigned char)(checksum >> 16); |
1357 | headData[10] = (unsigned char)(checksum >> 8); |
1358 | headData[11] = (unsigned char)checksum; |
1359 | |
1360 | // start the sfnts array |
1361 | if (name) { |
1362 | (*outputFunc)(outputStream, "/" , 1); |
1363 | (*outputFunc)(outputStream, name->c_str(), name->getLength()); |
1364 | (*outputFunc)(outputStream, " [\n" , 3); |
1365 | } else { |
1366 | (*outputFunc)(outputStream, "/sfnts [\n" , 9); |
1367 | } |
1368 | |
1369 | // write the table directory |
1370 | dumpString(s: tableDir, length: 12 + nNewTables * 16, outputFunc, outputStream); |
1371 | |
1372 | // write the tables |
1373 | for (i = 0; i < nNewTables; ++i) { |
1374 | if (i == t42HeadTable) { |
1375 | dumpString(s: headData, length: 54, outputFunc, outputStream); |
1376 | } else if (i == t42LocaTable) { |
1377 | length = (nGlyphs + 1) * (locaFmt ? 4 : 2); |
1378 | dumpString(s: locaData, length, outputFunc, outputStream); |
1379 | } else if (i == t42GlyfTable) { |
1380 | glyfPos = tables[seekTable(tag: "glyf" )].offset; |
1381 | for (j = 0; j < nGlyphs; ++j) { |
1382 | if (locaTable[j].len > 0 && checkRegion(pos: glyfPos + locaTable[j].origOffset, size: locaTable[j].len)) { |
1383 | dumpString(s: file + glyfPos + locaTable[j].origOffset, length: locaTable[j].len, outputFunc, outputStream); |
1384 | } |
1385 | } |
1386 | } else { |
1387 | // length == 0 means the table is missing and the error was |
1388 | // already reported during the construction of the table |
1389 | // headers |
1390 | if ((length = newTables[i].len) > 0) { |
1391 | if ((j = seekTable(tag: t42Tables[i].tag)) >= 0 && checkRegion(pos: tables[j].offset, size: tables[j].len)) { |
1392 | dumpString(s: file + tables[j].offset, length: tables[j].len, outputFunc, outputStream); |
1393 | } else if (needVerticalMetrics && i == t42VheaTable) { |
1394 | if (unlikely(length > (int)sizeof(vheaTab))) { |
1395 | error(category: errSyntaxWarning, pos: -1, msg: "length bigger than vheaTab size" ); |
1396 | length = sizeof(vheaTab); |
1397 | } |
1398 | dumpString(s: vheaTab, length, outputFunc, outputStream); |
1399 | } else if (needVerticalMetrics && i == t42VmtxTable) { |
1400 | if (unlikely(length > vmtxTabLength)) { |
1401 | error(category: errSyntaxWarning, pos: -1, msg: "length bigger than vmtxTab size" ); |
1402 | length = vmtxTabLength; |
1403 | } |
1404 | dumpString(s: vmtxTab, length, outputFunc, outputStream); |
1405 | } |
1406 | } |
1407 | } |
1408 | } |
1409 | |
1410 | // end the sfnts array |
1411 | (*outputFunc)(outputStream, "] def\n" , 6); |
1412 | |
1413 | gfree(p: locaData); |
1414 | gfree(p: locaTable); |
1415 | if (vmtxTab) { |
1416 | gfree(p: vmtxTab); |
1417 | } |
1418 | } |
1419 | |
1420 | void FoFiTrueType::dumpString(const unsigned char *s, int length, FoFiOutputFunc outputFunc, void *outputStream) const |
1421 | { |
1422 | int pad, i, j; |
1423 | |
1424 | (*outputFunc)(outputStream, "<" , 1); |
1425 | for (i = 0; i < length; i += 32) { |
1426 | for (j = 0; j < 32 && i + j < length; ++j) { |
1427 | const std::unique_ptr<GooString> buf = GooString::format(fmt: "{0:02x}" , s[i + j] & 0xff); |
1428 | (*outputFunc)(outputStream, buf->c_str(), buf->getLength()); |
1429 | } |
1430 | if (i % (65536 - 32) == 65536 - 64) { |
1431 | (*outputFunc)(outputStream, ">\n<" , 3); |
1432 | } else if (i + 32 < length) { |
1433 | (*outputFunc)(outputStream, "\n" , 1); |
1434 | } |
1435 | } |
1436 | if (length & 3) { |
1437 | pad = 4 - (length & 3); |
1438 | for (i = 0; i < pad; ++i) { |
1439 | (*outputFunc)(outputStream, "00" , 2); |
1440 | } |
1441 | } |
1442 | // add an extra zero byte because the Adobe Type 42 spec says so |
1443 | (*outputFunc)(outputStream, "00>\n" , 4); |
1444 | } |
1445 | |
1446 | unsigned int FoFiTrueType::computeTableChecksum(const unsigned char *data, int length) const |
1447 | { |
1448 | unsigned int checksum, word; |
1449 | int i; |
1450 | |
1451 | checksum = 0; |
1452 | for (i = 0; i + 3 < length; i += 4) { |
1453 | word = ((data[i] & 0xff) << 24) + ((data[i + 1] & 0xff) << 16) + ((data[i + 2] & 0xff) << 8) + (data[i + 3] & 0xff); |
1454 | checksum += word; |
1455 | } |
1456 | if (length & 3) { |
1457 | word = 0; |
1458 | i = length & ~3; |
1459 | switch (length & 3) { |
1460 | case 3: |
1461 | word |= (data[i + 2] & 0xff) << 8; |
1462 | // fallthrough |
1463 | case 2: |
1464 | word |= (data[i + 1] & 0xff) << 16; |
1465 | // fallthrough |
1466 | case 1: |
1467 | word |= (data[i] & 0xff) << 24; |
1468 | break; |
1469 | } |
1470 | checksum += word; |
1471 | } |
1472 | return checksum; |
1473 | } |
1474 | |
1475 | void FoFiTrueType::parse() |
1476 | { |
1477 | unsigned int topTag; |
1478 | int pos, ver, i, j; |
1479 | |
1480 | parsedOk = true; |
1481 | |
1482 | // look for a collection (TTC) |
1483 | topTag = getU32BE(pos: 0, ok: &parsedOk); |
1484 | if (!parsedOk) { |
1485 | return; |
1486 | } |
1487 | if (topTag == ttcfTag) { |
1488 | /* TTC font */ |
1489 | int dircount; |
1490 | |
1491 | dircount = getU32BE(pos: 8, ok: &parsedOk); |
1492 | if (!parsedOk) { |
1493 | return; |
1494 | } |
1495 | if (!dircount) { |
1496 | parsedOk = false; |
1497 | return; |
1498 | } |
1499 | |
1500 | if (faceIndex >= dircount) { |
1501 | faceIndex = 0; |
1502 | } |
1503 | pos = getU32BE(pos: 12 + faceIndex * 4, ok: &parsedOk); |
1504 | if (!parsedOk) { |
1505 | return; |
1506 | } |
1507 | } else { |
1508 | pos = 0; |
1509 | } |
1510 | |
1511 | // check the sfnt version |
1512 | ver = getU32BE(pos, ok: &parsedOk); |
1513 | if (!parsedOk) { |
1514 | return; |
1515 | } |
1516 | openTypeCFF = ver == 0x4f54544f; // 'OTTO' |
1517 | |
1518 | // read the table directory |
1519 | nTables = getU16BE(pos: pos + 4, ok: &parsedOk); |
1520 | if (!parsedOk) { |
1521 | return; |
1522 | } |
1523 | tables = (TrueTypeTable *)gmallocn(count: nTables, size: sizeof(TrueTypeTable)); |
1524 | pos += 12; |
1525 | j = 0; |
1526 | for (i = 0; i < nTables; ++i) { |
1527 | tables[j].tag = getU32BE(pos, ok: &parsedOk); |
1528 | tables[j].checksum = getU32BE(pos: pos + 4, ok: &parsedOk); |
1529 | tables[j].offset = (int)getU32BE(pos: pos + 8, ok: &parsedOk); |
1530 | tables[j].len = (int)getU32BE(pos: pos + 12, ok: &parsedOk); |
1531 | if (unlikely((tables[j].offset < 0) || (tables[j].len < 0) || (tables[j].offset < INT_MAX - tables[j].len) || (tables[j].len > INT_MAX - tables[j].offset) |
1532 | || (tables[j].offset + tables[j].len >= tables[j].offset && tables[j].offset + tables[j].len <= len))) { |
1533 | // ignore any bogus entries in the table directory |
1534 | ++j; |
1535 | } |
1536 | pos += 16; |
1537 | } |
1538 | if (nTables != j) { |
1539 | nTables = j; |
1540 | tables = (TrueTypeTable *)greallocn_checkoverflow(p: tables, count: nTables, size: sizeof(TrueTypeTable)); |
1541 | } |
1542 | if (!parsedOk || tables == nullptr) { |
1543 | parsedOk = false; |
1544 | return; |
1545 | } |
1546 | |
1547 | // check for tables that are required by both the TrueType spec and |
1548 | // the Type 42 spec |
1549 | if (seekTable(tag: "head" ) < 0 || seekTable(tag: "hhea" ) < 0 || seekTable(tag: "maxp" ) < 0 || (!openTypeCFF && seekTable(tag: "loca" ) < 0) || (!openTypeCFF && seekTable(tag: "glyf" ) < 0) || (openTypeCFF && (seekTable(tag: "CFF " ) < 0 && seekTable(tag: "CFF2" ) < 0))) { |
1550 | parsedOk = false; |
1551 | return; |
1552 | } |
1553 | |
1554 | // read the cmaps |
1555 | if ((i = seekTable(tag: "cmap" )) >= 0) { |
1556 | pos = tables[i].offset + 2; |
1557 | nCmaps = getU16BE(pos, ok: &parsedOk); |
1558 | pos += 2; |
1559 | if (!parsedOk) { |
1560 | return; |
1561 | } |
1562 | cmaps = (TrueTypeCmap *)gmallocn(count: nCmaps, size: sizeof(TrueTypeCmap)); |
1563 | for (j = 0; j < nCmaps; ++j) { |
1564 | cmaps[j].platform = getU16BE(pos, ok: &parsedOk); |
1565 | cmaps[j].encoding = getU16BE(pos: pos + 2, ok: &parsedOk); |
1566 | cmaps[j].offset = tables[i].offset + getU32BE(pos: pos + 4, ok: &parsedOk); |
1567 | pos += 8; |
1568 | cmaps[j].fmt = getU16BE(pos: cmaps[j].offset, ok: &parsedOk); |
1569 | cmaps[j].len = getU16BE(pos: cmaps[j].offset + 2, ok: &parsedOk); |
1570 | } |
1571 | if (!parsedOk) { |
1572 | return; |
1573 | } |
1574 | } else { |
1575 | nCmaps = 0; |
1576 | } |
1577 | |
1578 | // get the number of glyphs from the maxp table |
1579 | i = seekTable(tag: "maxp" ); |
1580 | nGlyphs = getU16BE(pos: tables[i].offset + 4, ok: &parsedOk); |
1581 | if (!parsedOk) { |
1582 | return; |
1583 | } |
1584 | |
1585 | // get the bbox and loca table format from the head table |
1586 | i = seekTable(tag: "head" ); |
1587 | bbox[0] = getS16BE(pos: tables[i].offset + 36, ok: &parsedOk); |
1588 | bbox[1] = getS16BE(pos: tables[i].offset + 38, ok: &parsedOk); |
1589 | bbox[2] = getS16BE(pos: tables[i].offset + 40, ok: &parsedOk); |
1590 | bbox[3] = getS16BE(pos: tables[i].offset + 42, ok: &parsedOk); |
1591 | locaFmt = getS16BE(pos: tables[i].offset + 50, ok: &parsedOk); |
1592 | if (!parsedOk) { |
1593 | return; |
1594 | } |
1595 | |
1596 | // read the post table |
1597 | readPostTable(); |
1598 | } |
1599 | |
1600 | void FoFiTrueType::readPostTable() |
1601 | { |
1602 | std::string name; |
1603 | int tablePos, postFmt, stringIdx, stringPos; |
1604 | bool ok; |
1605 | int i, j, n, m; |
1606 | |
1607 | ok = true; |
1608 | if ((i = seekTable(tag: "post" )) < 0) { |
1609 | return; |
1610 | } |
1611 | tablePos = tables[i].offset; |
1612 | postFmt = getU32BE(pos: tablePos, ok: &ok); |
1613 | if (!ok) { |
1614 | goto err; |
1615 | } |
1616 | if (postFmt == 0x00010000) { |
1617 | nameToGID.reserve(n: 258); |
1618 | for (i = 0; i < 258; ++i) { |
1619 | nameToGID.emplace(args&: macGlyphNames[i], args&: i); |
1620 | } |
1621 | } else if (postFmt == 0x00020000) { |
1622 | nameToGID.reserve(n: 258); |
1623 | n = getU16BE(pos: tablePos + 32, ok: &ok); |
1624 | if (!ok) { |
1625 | goto err; |
1626 | } |
1627 | if (n > nGlyphs) { |
1628 | n = nGlyphs; |
1629 | } |
1630 | stringIdx = 0; |
1631 | stringPos = tablePos + 34 + 2 * n; |
1632 | for (i = 0; i < n; ++i) { |
1633 | ok = true; |
1634 | j = getU16BE(pos: tablePos + 34 + 2 * i, ok: &ok); |
1635 | if (j < 258) { |
1636 | nameToGID[macGlyphNames[j]] = i; |
1637 | } else { |
1638 | j -= 258; |
1639 | if (j != stringIdx) { |
1640 | for (stringIdx = 0, stringPos = tablePos + 34 + 2 * n; stringIdx < j; ++stringIdx, stringPos += 1 + getU8(pos: stringPos, ok: &ok)) { |
1641 | ; |
1642 | } |
1643 | if (!ok) { |
1644 | continue; |
1645 | } |
1646 | } |
1647 | m = getU8(pos: stringPos, ok: &ok); |
1648 | if (!ok || !checkRegion(pos: stringPos + 1, size: m)) { |
1649 | continue; |
1650 | } |
1651 | name.assign(s: (char *)&file[stringPos + 1], n: m); |
1652 | nameToGID[name] = i; |
1653 | ++stringIdx; |
1654 | stringPos += 1 + m; |
1655 | } |
1656 | } |
1657 | } else if (postFmt == 0x00028000) { |
1658 | nameToGID.reserve(n: 258); |
1659 | for (i = 0; i < nGlyphs; ++i) { |
1660 | j = getU8(pos: tablePos + 32 + i, ok: &ok); |
1661 | if (!ok) { |
1662 | continue; |
1663 | } |
1664 | if (j < 258) { |
1665 | nameToGID[macGlyphNames[j]] = i; |
1666 | } |
1667 | } |
1668 | } |
1669 | |
1670 | return; |
1671 | |
1672 | err: |
1673 | nameToGID.clear(); |
1674 | } |
1675 | |
1676 | int FoFiTrueType::seekTable(const char *tag) const |
1677 | { |
1678 | unsigned int tagI; |
1679 | int i; |
1680 | |
1681 | tagI = ((tag[0] & 0xff) << 24) | ((tag[1] & 0xff) << 16) | ((tag[2] & 0xff) << 8) | (tag[3] & 0xff); |
1682 | for (i = 0; i < nTables; ++i) { |
1683 | if (tables[i].tag == tagI) { |
1684 | return i; |
1685 | } |
1686 | } |
1687 | return -1; |
1688 | } |
1689 | |
1690 | unsigned int FoFiTrueType::charToTag(const char *tagName) |
1691 | { |
1692 | int n = strlen(s: tagName); |
1693 | unsigned int tag = 0; |
1694 | int i; |
1695 | |
1696 | if (n > 4) { |
1697 | n = 4; |
1698 | } |
1699 | for (i = 0; i < n; i++) { |
1700 | tag <<= 8; |
1701 | tag |= tagName[i] & 0xff; |
1702 | } |
1703 | for (; i < 4; i++) { |
1704 | tag <<= 8; |
1705 | tag |= ' '; |
1706 | } |
1707 | return tag; |
1708 | } |
1709 | |
1710 | /* |
1711 | setup GSUB table data |
1712 | Only supporting vertical text substitution. |
1713 | */ |
1714 | int FoFiTrueType::setupGSUB(const char *scriptName) |
1715 | { |
1716 | return setupGSUB(scriptName, languageName: nullptr); |
1717 | } |
1718 | |
1719 | /* |
1720 | setup GSUB table data |
1721 | Only supporting vertical text substitution. |
1722 | */ |
1723 | int FoFiTrueType::setupGSUB(const char *scriptName, const char *languageName) |
1724 | { |
1725 | unsigned int gsubTable; |
1726 | unsigned int i; |
1727 | unsigned int scriptList, featureList; |
1728 | unsigned int scriptCount; |
1729 | unsigned int tag; |
1730 | unsigned int scriptTable = 0; |
1731 | unsigned int langSys; |
1732 | unsigned int featureCount; |
1733 | unsigned int featureIndex; |
1734 | unsigned int ftable = 0; |
1735 | unsigned int llist; |
1736 | unsigned int scriptTag; |
1737 | int x; |
1738 | unsigned int pos; |
1739 | |
1740 | if (scriptName == nullptr) { |
1741 | gsubFeatureTable = 0; |
1742 | return 0; |
1743 | } |
1744 | scriptTag = charToTag(tagName: scriptName); |
1745 | /* read GSUB Header */ |
1746 | if ((x = seekTable(tag: "GSUB" )) < 0) { |
1747 | return 0; /* GSUB table not found */ |
1748 | } |
1749 | gsubTable = tables[x].offset; |
1750 | pos = gsubTable + 4; |
1751 | scriptList = getU16BE(pos, ok: &parsedOk); |
1752 | pos += 2; |
1753 | featureList = getU16BE(pos, ok: &parsedOk); |
1754 | pos += 2; |
1755 | llist = getU16BE(pos, ok: &parsedOk); |
1756 | |
1757 | gsubLookupList = llist + gsubTable; /* change to offset from top of file */ |
1758 | /* read script list table */ |
1759 | pos = gsubTable + scriptList; |
1760 | scriptCount = getU16BE(pos, ok: &parsedOk); |
1761 | pos += 2; |
1762 | /* find script */ |
1763 | for (i = 0; i < scriptCount; i++) { |
1764 | tag = getU32BE(pos, ok: &parsedOk); |
1765 | pos += 4; |
1766 | scriptTable = getU16BE(pos, ok: &parsedOk); |
1767 | pos += 2; |
1768 | if (tag == scriptTag) { |
1769 | /* found */ |
1770 | break; |
1771 | } |
1772 | } |
1773 | if (i >= scriptCount) { |
1774 | /* not found */ |
1775 | return 0; |
1776 | } |
1777 | |
1778 | /* read script table */ |
1779 | /* use default language system */ |
1780 | pos = gsubTable + scriptList + scriptTable; |
1781 | langSys = 0; |
1782 | if (languageName) { |
1783 | unsigned int langTag = charToTag(tagName: languageName); |
1784 | unsigned int langCount = getU16BE(pos: pos + 2, ok: &parsedOk); |
1785 | for (i = 0; i < langCount && langSys == 0; i++) { |
1786 | tag = getU32BE(pos: pos + 4 + i * (4 + 2), ok: &parsedOk); |
1787 | if (tag == langTag) { |
1788 | langSys = getU16BE(pos: pos + 4 + i * (4 + 2) + 4, ok: &parsedOk); |
1789 | } |
1790 | } |
1791 | } |
1792 | if (langSys == 0) { |
1793 | /* default language system */ |
1794 | langSys = getU16BE(pos, ok: &parsedOk); |
1795 | } |
1796 | |
1797 | /* read LangSys table */ |
1798 | if (langSys == 0) { |
1799 | /* no default LangSys */ |
1800 | return 0; |
1801 | } |
1802 | |
1803 | pos = gsubTable + scriptList + scriptTable + langSys + 2; |
1804 | featureIndex = getU16BE(pos, ok: &parsedOk); /* ReqFeatureIndex */ |
1805 | pos += 2; |
1806 | |
1807 | if (featureIndex != 0xffff) { |
1808 | unsigned int tpos; |
1809 | /* read feature record */ |
1810 | tpos = gsubTable + featureList; |
1811 | featureCount = getU16BE(pos: tpos, ok: &parsedOk); |
1812 | tpos = gsubTable + featureList + 2 + featureIndex * (4 + 2); |
1813 | tag = getU32BE(pos: tpos, ok: &parsedOk); |
1814 | tpos += 4; |
1815 | if (tag == vrt2Tag) { |
1816 | /* vrt2 is preferred, overwrite vert */ |
1817 | ftable = getU16BE(pos: tpos, ok: &parsedOk); |
1818 | /* convert to offset from file top */ |
1819 | gsubFeatureTable = ftable + gsubTable + featureList; |
1820 | return 0; |
1821 | } else if (tag == vertTag) { |
1822 | ftable = getU16BE(pos: tpos, ok: &parsedOk); |
1823 | } |
1824 | } |
1825 | featureCount = getU16BE(pos, ok: &parsedOk); |
1826 | pos += 2; |
1827 | /* find 'vrt2' or 'vert' feature */ |
1828 | for (i = 0; i < featureCount; i++) { |
1829 | unsigned int oldPos; |
1830 | |
1831 | featureIndex = getU16BE(pos, ok: &parsedOk); |
1832 | pos += 2; |
1833 | oldPos = pos; /* save position */ |
1834 | /* read feature record */ |
1835 | pos = gsubTable + featureList + 2 + featureIndex * (4 + 2); |
1836 | tag = getU32BE(pos, ok: &parsedOk); |
1837 | pos += 4; |
1838 | if (tag == vrt2Tag) { |
1839 | /* vrt2 is preferred, overwrite vert */ |
1840 | ftable = getU16BE(pos, ok: &parsedOk); |
1841 | break; |
1842 | } else if (ftable == 0 && tag == vertTag) { |
1843 | ftable = getU16BE(pos, ok: &parsedOk); |
1844 | } |
1845 | pos = oldPos; /* restore old position */ |
1846 | } |
1847 | if (ftable == 0) { |
1848 | /* vert nor vrt2 are not found */ |
1849 | return 0; |
1850 | } |
1851 | /* convert to offset from file top */ |
1852 | gsubFeatureTable = ftable + gsubTable + featureList; |
1853 | return 0; |
1854 | } |
1855 | |
1856 | unsigned int FoFiTrueType::doMapToVertGID(unsigned int orgGID) |
1857 | { |
1858 | unsigned int lookupCount; |
1859 | unsigned int lookupListIndex; |
1860 | unsigned int i; |
1861 | unsigned int gid = 0; |
1862 | unsigned int pos; |
1863 | |
1864 | pos = gsubFeatureTable + 2; |
1865 | lookupCount = getU16BE(pos, ok: &parsedOk); |
1866 | pos += 2; |
1867 | for (i = 0; i < lookupCount; i++) { |
1868 | lookupListIndex = getU16BE(pos, ok: &parsedOk); |
1869 | pos += 2; |
1870 | if ((gid = scanLookupList(listIndex: lookupListIndex, orgGID)) != 0) { |
1871 | break; |
1872 | } |
1873 | } |
1874 | return gid; |
1875 | } |
1876 | |
1877 | unsigned int FoFiTrueType::mapToVertGID(unsigned int orgGID) |
1878 | { |
1879 | unsigned int mapped; |
1880 | |
1881 | if (gsubFeatureTable == 0) { |
1882 | return orgGID; |
1883 | } |
1884 | if ((mapped = doMapToVertGID(orgGID)) != 0) { |
1885 | return mapped; |
1886 | } |
1887 | return orgGID; |
1888 | } |
1889 | |
1890 | unsigned int FoFiTrueType::scanLookupList(unsigned int listIndex, unsigned int orgGID) |
1891 | { |
1892 | unsigned int lookupTable; |
1893 | unsigned int subTableCount; |
1894 | unsigned int subTable; |
1895 | unsigned int i; |
1896 | unsigned int gid = 0; |
1897 | unsigned int pos; |
1898 | |
1899 | if (gsubLookupList == 0) { |
1900 | return 0; /* no lookup list */ |
1901 | } |
1902 | pos = gsubLookupList + 2 + listIndex * 2; |
1903 | lookupTable = getU16BE(pos, ok: &parsedOk); |
1904 | /* read lookup table */ |
1905 | pos = gsubLookupList + lookupTable + 4; |
1906 | subTableCount = getU16BE(pos, ok: &parsedOk); |
1907 | pos += 2; |
1908 | ; |
1909 | for (i = 0; i < subTableCount; i++) { |
1910 | subTable = getU16BE(pos, ok: &parsedOk); |
1911 | pos += 2; |
1912 | if ((gid = scanLookupSubTable(subTable: gsubLookupList + lookupTable + subTable, orgGID)) != 0) { |
1913 | break; |
1914 | } |
1915 | } |
1916 | return gid; |
1917 | } |
1918 | |
1919 | unsigned int FoFiTrueType::scanLookupSubTable(unsigned int subTable, unsigned int orgGID) |
1920 | { |
1921 | unsigned int format; |
1922 | unsigned int coverage; |
1923 | int delta; |
1924 | int glyphCount; |
1925 | unsigned int substitute; |
1926 | unsigned int gid = 0; |
1927 | int coverageIndex; |
1928 | int pos; |
1929 | |
1930 | pos = subTable; |
1931 | format = getU16BE(pos, ok: &parsedOk); |
1932 | pos += 2; |
1933 | coverage = getU16BE(pos, ok: &parsedOk); |
1934 | pos += 2; |
1935 | if ((coverageIndex = checkGIDInCoverage(coverage: subTable + coverage, orgGID)) >= 0) { |
1936 | switch (format) { |
1937 | case 1: |
1938 | /* format 1 */ |
1939 | delta = getS16BE(pos, ok: &parsedOk); |
1940 | pos += 2; |
1941 | gid = orgGID + delta; |
1942 | break; |
1943 | case 2: |
1944 | /* format 2 */ |
1945 | glyphCount = getS16BE(pos, ok: &parsedOk); |
1946 | pos += 2; |
1947 | if (glyphCount > coverageIndex) { |
1948 | pos += coverageIndex * 2; |
1949 | substitute = getU16BE(pos, ok: &parsedOk); |
1950 | gid = substitute; |
1951 | } |
1952 | break; |
1953 | default: |
1954 | /* unknown format */ |
1955 | break; |
1956 | } |
1957 | } |
1958 | return gid; |
1959 | } |
1960 | |
1961 | int FoFiTrueType::checkGIDInCoverage(unsigned int coverage, unsigned int orgGID) |
1962 | { |
1963 | int index = -1; |
1964 | unsigned int format; |
1965 | unsigned int count; |
1966 | unsigned int i; |
1967 | unsigned int pos; |
1968 | |
1969 | pos = coverage; |
1970 | format = getU16BE(pos, ok: &parsedOk); |
1971 | pos += 2; |
1972 | switch (format) { |
1973 | case 1: |
1974 | count = getU16BE(pos, ok: &parsedOk); |
1975 | pos += 2; |
1976 | // In some poor CJK fonts, key GIDs are not sorted, |
1977 | // thus we cannot finish checking even when the range |
1978 | // including orgGID seems to have already passed. |
1979 | for (i = 0; i < count; i++) { |
1980 | unsigned int gid; |
1981 | |
1982 | gid = getU16BE(pos, ok: &parsedOk); |
1983 | pos += 2; |
1984 | if (gid == orgGID) { |
1985 | /* found */ |
1986 | index = i; |
1987 | break; |
1988 | } |
1989 | } |
1990 | break; |
1991 | case 2: |
1992 | count = getU16BE(pos, ok: &parsedOk); |
1993 | pos += 2; |
1994 | for (i = 0; i < count; i++) { |
1995 | unsigned int startGID, endGID; |
1996 | unsigned int startIndex; |
1997 | |
1998 | startGID = getU16BE(pos, ok: &parsedOk); |
1999 | pos += 2; |
2000 | endGID = getU16BE(pos, ok: &parsedOk); |
2001 | pos += 2; |
2002 | startIndex = getU16BE(pos, ok: &parsedOk); |
2003 | pos += 2; |
2004 | // In some poor CJK fonts, key GIDs are not sorted, |
2005 | // thus we cannot finish checking even when the range |
2006 | // including orgGID seems to have already passed. |
2007 | if (startGID <= orgGID && orgGID <= endGID) { |
2008 | /* found */ |
2009 | index = startIndex + orgGID - startGID; |
2010 | break; |
2011 | } |
2012 | } |
2013 | break; |
2014 | default: |
2015 | break; |
2016 | } |
2017 | return index; |
2018 | } |
2019 | |