]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | //======================================================================== |
2 | // | |
3 | // FoFiTrueType.cc | |
4 | // | |
5 | // Copyright 1999-2003 Glyph & Cog, LLC | |
6 | // | |
7 | //======================================================================== | |
8 | ||
9 | #include <config.h> | |
10 | ||
11 | #ifdef USE_GCC_PRAGMAS | |
12 | #pragma implementation | |
13 | #endif | |
14 | ||
15 | #include <stdlib.h> | |
16 | #include <string.h> | |
17 | #include "gtypes.h" | |
18 | #include "gmem.h" | |
19 | #include "GString.h" | |
20 | #include "GHash.h" | |
21 | #include "FoFiTrueType.h" | |
22 | ||
23 | // | |
24 | // Terminology | |
25 | // ----------- | |
26 | // | |
27 | // character code = number used as an element of a text string | |
28 | // | |
29 | // character name = glyph name = name for a particular glyph within a | |
30 | // font | |
31 | // | |
32 | // glyph index = GID = position (within some internal table in the font) | |
33 | // where the instructions to draw a particular glyph are | |
34 | // stored | |
35 | // | |
36 | // Type 1 fonts | |
37 | // ------------ | |
38 | // | |
39 | // Type 1 fonts contain: | |
40 | // | |
41 | // Encoding: array of glyph names, maps char codes to glyph names | |
42 | // | |
43 | // Encoding[charCode] = charName | |
44 | // | |
45 | // CharStrings: dictionary of instructions, keyed by character names, | |
46 | // maps character name to glyph data | |
47 | // | |
48 | // CharStrings[charName] = glyphData | |
49 | // | |
50 | // TrueType fonts | |
51 | // -------------- | |
52 | // | |
53 | // TrueType fonts contain: | |
54 | // | |
55 | // 'cmap' table: mapping from character code to glyph index; there may | |
56 | // be multiple cmaps in a TrueType font | |
57 | // | |
58 | // cmap[charCode] = gid | |
59 | // | |
60 | // 'post' table: mapping from glyph index to glyph name | |
61 | // | |
62 | // post[gid] = glyphName | |
63 | // | |
64 | // Type 42 fonts | |
65 | // ------------- | |
66 | // | |
67 | // Type 42 fonts contain: | |
68 | // | |
69 | // Encoding: array of glyph names, maps char codes to glyph names | |
70 | // | |
71 | // Encoding[charCode] = charName | |
72 | // | |
73 | // CharStrings: dictionary of glyph indexes, keyed by character names, | |
74 | // maps character name to glyph index | |
75 | // | |
76 | // CharStrings[charName] = gid | |
77 | // | |
78 | ||
79 | //------------------------------------------------------------------------ | |
80 | ||
81 | #define ttcfTag 0x74746366 | |
82 | ||
83 | //------------------------------------------------------------------------ | |
84 | ||
85 | struct TrueTypeTable { | |
86 | Guint tag; | |
87 | Guint checksum; | |
88 | int offset; | |
89 | int origOffset; | |
90 | int len; | |
91 | }; | |
92 | ||
93 | struct TrueTypeCmap { | |
94 | int platform; | |
95 | int encoding; | |
96 | int offset; | |
97 | int len; | |
98 | int fmt; | |
99 | }; | |
100 | ||
101 | struct TrueTypeLoca { | |
102 | int idx; | |
103 | int origOffset; | |
104 | int newOffset; | |
105 | int len; | |
106 | }; | |
107 | ||
108 | #define cmapTag 0x636d6170 | |
109 | #define glyfTag 0x676c7966 | |
110 | #define headTag 0x68656164 | |
111 | #define locaTag 0x6c6f6361 | |
112 | #define nameTag 0x6e616d65 | |
113 | #define postTag 0x706f7374 | |
114 | ||
115 | static int cmpTrueTypeLocaOffset(const void *p1, const void *p2) { | |
116 | TrueTypeLoca *loca1 = (TrueTypeLoca *)p1; | |
117 | TrueTypeLoca *loca2 = (TrueTypeLoca *)p2; | |
118 | ||
119 | if (loca1->origOffset == loca2->origOffset) { | |
120 | return loca1->idx - loca2->idx; | |
121 | } | |
122 | return loca1->origOffset - loca2->origOffset; | |
123 | } | |
124 | ||
125 | static int cmpTrueTypeLocaIdx(const void *p1, const void *p2) { | |
126 | TrueTypeLoca *loca1 = (TrueTypeLoca *)p1; | |
127 | TrueTypeLoca *loca2 = (TrueTypeLoca *)p2; | |
128 | ||
129 | return loca1->idx - loca2->idx; | |
130 | } | |
131 | ||
132 | static int cmpTrueTypeTableTag(const void *p1, const void *p2) { | |
133 | TrueTypeTable *tab1 = (TrueTypeTable *)p1; | |
134 | TrueTypeTable *tab2 = (TrueTypeTable *)p2; | |
135 | ||
136 | return (int)tab1->tag - (int)tab2->tag; | |
137 | } | |
138 | ||
139 | //------------------------------------------------------------------------ | |
140 | ||
141 | struct T42Table { | |
142 | char *tag; // 4-byte tag | |
143 | GBool required; // required by the TrueType spec? | |
144 | }; | |
145 | ||
146 | // TrueType tables to be embedded in Type 42 fonts. | |
147 | // NB: the table names must be in alphabetical order here. | |
148 | #define nT42Tables 11 | |
149 | static T42Table t42Tables[nT42Tables] = { | |
150 | { "cvt ", gTrue }, | |
151 | { "fpgm", gTrue }, | |
152 | { "glyf", gTrue }, | |
153 | { "head", gTrue }, | |
154 | { "hhea", gTrue }, | |
155 | { "hmtx", gTrue }, | |
156 | { "loca", gTrue }, | |
157 | { "maxp", gTrue }, | |
158 | { "prep", gTrue }, | |
159 | { "vhea", gFalse }, | |
160 | { "vmtx", gFalse } | |
161 | }; | |
162 | #define t42HeadTable 3 | |
163 | #define t42LocaTable 6 | |
164 | #define t42GlyfTable 2 | |
165 | #define t42VheaTable 9 | |
166 | #define t42VmtxTable 10 | |
167 | ||
168 | //------------------------------------------------------------------------ | |
169 | ||
170 | // Glyph names in some arbitrary standard order that Apple uses for | |
171 | // their TrueType fonts. | |
172 | static char *macGlyphNames[258] = { | |
173 | ".notdef", "null", "CR", "space", | |
174 | "exclam", "quotedbl", "numbersign", "dollar", | |
175 | "percent", "ampersand", "quotesingle", "parenleft", | |
176 | "parenright", "asterisk", "plus", "comma", | |
177 | "hyphen", "period", "slash", "zero", | |
178 | "one", "two", "three", "four", | |
179 | "five", "six", "seven", "eight", | |
180 | "nine", "colon", "semicolon", "less", | |
181 | "equal", "greater", "question", "at", | |
182 | "A", "B", "C", "D", | |
183 | "E", "F", "G", "H", | |
184 | "I", "J", "K", "L", | |
185 | "M", "N", "O", "P", | |
186 | "Q", "R", "S", "T", | |
187 | "U", "V", "W", "X", | |
188 | "Y", "Z", "bracketleft", "backslash", | |
189 | "bracketright", "asciicircum", "underscore", "grave", | |
190 | "a", "b", "c", "d", | |
191 | "e", "f", "g", "h", | |
192 | "i", "j", "k", "l", | |
193 | "m", "n", "o", "p", | |
194 | "q", "r", "s", "t", | |
195 | "u", "v", "w", "x", | |
196 | "y", "z", "braceleft", "bar", | |
197 | "braceright", "asciitilde", "Adieresis", "Aring", | |
198 | "Ccedilla", "Eacute", "Ntilde", "Odieresis", | |
199 | "Udieresis", "aacute", "agrave", "acircumflex", | |
200 | "adieresis", "atilde", "aring", "ccedilla", | |
201 | "eacute", "egrave", "ecircumflex", "edieresis", | |
202 | "iacute", "igrave", "icircumflex", "idieresis", | |
203 | "ntilde", "oacute", "ograve", "ocircumflex", | |
204 | "odieresis", "otilde", "uacute", "ugrave", | |
205 | "ucircumflex", "udieresis", "dagger", "degree", | |
206 | "cent", "sterling", "section", "bullet", | |
207 | "paragraph", "germandbls", "registered", "copyright", | |
208 | "trademark", "acute", "dieresis", "notequal", | |
209 | "AE", "Oslash", "infinity", "plusminus", | |
210 | "lessequal", "greaterequal", "yen", "mu1", | |
211 | "partialdiff", "summation", "product", "pi", | |
212 | "integral", "ordfeminine", "ordmasculine", "Ohm", | |
213 | "ae", "oslash", "questiondown", "exclamdown", | |
214 | "logicalnot", "radical", "florin", "approxequal", | |
215 | "increment", "guillemotleft", "guillemotright", "ellipsis", | |
216 | "nbspace", "Agrave", "Atilde", "Otilde", | |
217 | "OE", "oe", "endash", "emdash", | |
218 | "quotedblleft", "quotedblright", "quoteleft", "quoteright", | |
219 | "divide", "lozenge", "ydieresis", "Ydieresis", | |
220 | "fraction", "currency", "guilsinglleft", "guilsinglright", | |
221 | "fi", "fl", "daggerdbl", "periodcentered", | |
222 | "quotesinglbase", "quotedblbase", "perthousand", "Acircumflex", | |
223 | "Ecircumflex", "Aacute", "Edieresis", "Egrave", | |
224 | "Iacute", "Icircumflex", "Idieresis", "Igrave", | |
225 | "Oacute", "Ocircumflex", "applelogo", "Ograve", | |
226 | "Uacute", "Ucircumflex", "Ugrave", "dotlessi", | |
227 | "circumflex", "tilde", "overscore", "breve", | |
228 | "dotaccent", "ring", "cedilla", "hungarumlaut", | |
229 | "ogonek", "caron", "Lslash", "lslash", | |
230 | "Scaron", "scaron", "Zcaron", "zcaron", | |
231 | "brokenbar", "Eth", "eth", "Yacute", | |
232 | "yacute", "Thorn", "thorn", "minus", | |
233 | "multiply", "onesuperior", "twosuperior", "threesuperior", | |
234 | "onehalf", "onequarter", "threequarters", "franc", | |
235 | "Gbreve", "gbreve", "Idot", "Scedilla", | |
236 | "scedilla", "Cacute", "cacute", "Ccaron", | |
237 | "ccaron", "dmacron" | |
238 | }; | |
239 | ||
240 | //------------------------------------------------------------------------ | |
241 | // FoFiTrueType | |
242 | //------------------------------------------------------------------------ | |
243 | ||
244 | FoFiTrueType *FoFiTrueType::make(char *fileA, int lenA) { | |
245 | FoFiTrueType *ff; | |
246 | ||
247 | ff = new FoFiTrueType(fileA, lenA, gFalse); | |
248 | if (!ff->parsedOk) { | |
249 | delete ff; | |
250 | return NULL; | |
251 | } | |
252 | return ff; | |
253 | } | |
254 | ||
255 | FoFiTrueType *FoFiTrueType::load(char *fileName) { | |
256 | FoFiTrueType *ff; | |
257 | char *fileA; | |
258 | int lenA; | |
259 | ||
260 | if (!(fileA = FoFiBase::readFile(fileName, &lenA))) { | |
261 | return NULL; | |
262 | } | |
263 | ff = new FoFiTrueType(fileA, lenA, gTrue); | |
264 | if (!ff->parsedOk) { | |
265 | delete ff; | |
266 | return NULL; | |
267 | } | |
268 | return ff; | |
269 | } | |
270 | ||
271 | FoFiTrueType::FoFiTrueType(char *fileA, int lenA, GBool freeFileDataA): | |
272 | FoFiBase(fileA, lenA, freeFileDataA) | |
273 | { | |
274 | tables = NULL; | |
275 | nTables = 0; | |
276 | cmaps = NULL; | |
277 | nCmaps = 0; | |
278 | nameToGID = NULL; | |
279 | parsedOk = gFalse; | |
280 | ||
281 | parse(); | |
282 | } | |
283 | ||
284 | FoFiTrueType::~FoFiTrueType() { | |
285 | gfree(tables); | |
286 | gfree(cmaps); | |
287 | if (nameToGID) { | |
288 | delete nameToGID; | |
289 | } | |
290 | } | |
291 | ||
292 | int FoFiTrueType::getNumCmaps() { | |
293 | return nCmaps; | |
294 | } | |
295 | ||
296 | int FoFiTrueType::getCmapPlatform(int i) { | |
297 | return cmaps[i].platform; | |
298 | } | |
299 | ||
300 | int FoFiTrueType::getCmapEncoding(int i) { | |
301 | return cmaps[i].encoding; | |
302 | } | |
303 | ||
304 | int FoFiTrueType::findCmap(int platform, int encoding) { | |
305 | int i; | |
306 | ||
307 | for (i = 0; i < nCmaps; ++i) { | |
308 | if (cmaps[i].platform == platform && cmaps[i].encoding == encoding) { | |
309 | return i; | |
310 | } | |
311 | } | |
312 | return -1; | |
313 | } | |
314 | ||
315 | Gushort FoFiTrueType::mapCodeToGID(int i, int c) { | |
316 | Gushort gid; | |
317 | int segCnt, segEnd, segStart, segDelta, segOffset; | |
318 | int cmapFirst, cmapLen; | |
319 | int pos, a, b, m; | |
320 | GBool ok; | |
321 | ||
322 | if (i < 0 || i >= nCmaps) { | |
323 | return 0; | |
324 | } | |
325 | ok = gTrue; | |
326 | pos = cmaps[i].offset; | |
327 | switch (cmaps[i].fmt) { | |
328 | case 0: | |
329 | if (c < 0 || c >= cmaps[i].len - 6) { | |
330 | return 0; | |
331 | } | |
332 | gid = getU8(cmaps[i].offset + 6 + c, &ok); | |
333 | break; | |
334 | case 4: | |
335 | segCnt = getU16BE(pos + 6, &ok) / 2; | |
336 | a = -1; | |
337 | b = segCnt - 1; | |
338 | segEnd = getU16BE(pos + 14 + 2*b, &ok); | |
339 | if (c > segEnd) { | |
340 | // malformed font -- the TrueType spec requires the last segEnd | |
341 | // to be 0xffff | |
342 | return 0; | |
343 | } | |
344 | // invariant: seg[a].end < code <= seg[b].end | |
345 | while (b - a > 1 && ok) { | |
346 | m = (a + b) / 2; | |
347 | segEnd = getU16BE(pos + 14 + 2*m, &ok); | |
348 | if (segEnd < c) { | |
349 | a = m; | |
350 | } else { | |
351 | b = m; | |
352 | } | |
353 | } | |
354 | segStart = getU16BE(pos + 16 + 2*segCnt + 2*b, &ok); | |
355 | segDelta = getU16BE(pos + 16 + 4*segCnt + 2*b, &ok); | |
356 | segOffset = getU16BE(pos + 16 + 6*segCnt + 2*b, &ok); | |
357 | if (c < segStart) { | |
358 | return 0; | |
359 | } | |
360 | if (segOffset == 0) { | |
361 | gid = (c + segDelta) & 0xffff; | |
362 | } else { | |
363 | gid = getU16BE(pos + 16 + 6*segCnt + 2*b + | |
364 | segOffset + 2 * (c - segStart), &ok); | |
365 | if (gid != 0) { | |
366 | gid = (gid + segDelta) & 0xffff; | |
367 | } | |
368 | } | |
369 | break; | |
370 | case 6: | |
371 | cmapFirst = getU16BE(pos + 6, &ok); | |
372 | cmapLen = getU16BE(pos + 8, &ok); | |
373 | if (c < cmapFirst || c >= cmapFirst + cmapLen) { | |
374 | return 0; | |
375 | } | |
376 | gid = getU16BE(pos + 10 + 2 * (c - cmapFirst), &ok); | |
377 | break; | |
378 | default: | |
379 | return 0; | |
380 | } | |
381 | if (!ok) { | |
382 | return 0; | |
383 | } | |
384 | return gid; | |
385 | } | |
386 | ||
387 | int FoFiTrueType::mapNameToGID(char *name) { | |
388 | if (!nameToGID) { | |
389 | return 0; | |
390 | } | |
391 | return nameToGID->lookupInt(name); | |
392 | } | |
393 | ||
394 | int FoFiTrueType::getEmbeddingRights() { | |
395 | int i, fsType; | |
396 | GBool ok; | |
397 | ||
398 | if ((i = seekTable("OS/2")) < 0) { | |
399 | return 4; | |
400 | } | |
401 | ok = gTrue; | |
402 | fsType = getU16BE(tables[i].offset + 8, &ok); | |
403 | if (!ok) { | |
404 | return 4; | |
405 | } | |
406 | if (fsType & 0x0008) { | |
407 | return 2; | |
408 | } | |
409 | if (fsType & 0x0004) { | |
410 | return 1; | |
411 | } | |
412 | if (fsType & 0x0002) { | |
413 | return 0; | |
414 | } | |
415 | return 3; | |
416 | } | |
417 | ||
418 | void FoFiTrueType::convertToType42(char *psName, char **encoding, | |
419 | Gushort *codeToGID, | |
420 | FoFiOutputFunc outputFunc, | |
421 | void *outputStream) { | |
422 | char buf[512]; | |
423 | GBool ok; | |
424 | ||
425 | // write the header | |
426 | ok = gTrue; | |
427 | sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0); | |
428 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
429 | ||
430 | // begin the font dictionary | |
431 | (*outputFunc)(outputStream, "10 dict begin\n", 14); | |
432 | (*outputFunc)(outputStream, "/FontName /", 11); | |
433 | (*outputFunc)(outputStream, psName, strlen(psName)); | |
434 | (*outputFunc)(outputStream, " def\n", 5); | |
435 | (*outputFunc)(outputStream, "/FontType 42 def\n", 17); | |
436 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
437 | sprintf(buf, "/FontBBox [%d %d %d %d] def\n", | |
438 | bbox[0], bbox[1], bbox[2], bbox[3]); | |
439 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
440 | (*outputFunc)(outputStream, "/PaintType 0 def\n", 17); | |
441 | ||
442 | // write the guts of the dictionary | |
443 | cvtEncoding(encoding, outputFunc, outputStream); | |
444 | cvtCharStrings(encoding, codeToGID, outputFunc, outputStream); | |
445 | cvtSfnts(outputFunc, outputStream, NULL, gFalse); | |
446 | ||
447 | // end the dictionary and define the font | |
448 | (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); | |
449 | } | |
450 | ||
451 | void FoFiTrueType::convertToCIDType2(char *psName, | |
452 | Gushort *cidMap, int nCIDs, | |
453 | GBool needVerticalMetrics, | |
454 | FoFiOutputFunc outputFunc, | |
455 | void *outputStream) { | |
456 | char buf[512]; | |
457 | Gushort cid; | |
458 | GBool ok; | |
459 | int i, j, k; | |
460 | ||
461 | // write the header | |
462 | ok = gTrue; | |
463 | sprintf(buf, "%%!PS-TrueTypeFont-%g\n", (double)getS32BE(0, &ok) / 65536.0); | |
464 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
465 | ||
466 | // begin the font dictionary | |
467 | (*outputFunc)(outputStream, "20 dict begin\n", 14); | |
468 | (*outputFunc)(outputStream, "/CIDFontName /", 14); | |
469 | (*outputFunc)(outputStream, psName, strlen(psName)); | |
470 | (*outputFunc)(outputStream, " def\n", 5); | |
471 | (*outputFunc)(outputStream, "/CIDFontType 2 def\n", 19); | |
472 | (*outputFunc)(outputStream, "/FontType 42 def\n", 17); | |
473 | (*outputFunc)(outputStream, "/CIDSystemInfo 3 dict dup begin\n", 32); | |
474 | (*outputFunc)(outputStream, " /Registry (Adobe) def\n", 24); | |
475 | (*outputFunc)(outputStream, " /Ordering (Identity) def\n", 27); | |
476 | (*outputFunc)(outputStream, " /Supplement 0 def\n", 20); | |
477 | (*outputFunc)(outputStream, " end def\n", 10); | |
478 | (*outputFunc)(outputStream, "/GDBytes 2 def\n", 15); | |
479 | if (cidMap) { | |
480 | sprintf(buf, "/CIDCount %d def\n", nCIDs); | |
481 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
482 | if (nCIDs > 32767) { | |
483 | (*outputFunc)(outputStream, "/CIDMap [", 9); | |
484 | for (i = 0; i < nCIDs; i += 32768 - 16) { | |
485 | (*outputFunc)(outputStream, "<\n", 2); | |
486 | for (j = 0; j < 32768 - 16 && i+j < nCIDs; j += 16) { | |
487 | (*outputFunc)(outputStream, " ", 2); | |
488 | for (k = 0; k < 16 && i+j+k < nCIDs; ++k) { | |
489 | cid = cidMap[i+j+k]; | |
490 | sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff); | |
491 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
492 | } | |
493 | (*outputFunc)(outputStream, "\n", 1); | |
494 | } | |
495 | (*outputFunc)(outputStream, " >", 3); | |
496 | } | |
497 | (*outputFunc)(outputStream, "\n", 1); | |
498 | (*outputFunc)(outputStream, "] def\n", 6); | |
499 | } else { | |
500 | (*outputFunc)(outputStream, "/CIDMap <\n", 10); | |
501 | for (i = 0; i < nCIDs; i += 16) { | |
502 | (*outputFunc)(outputStream, " ", 2); | |
503 | for (j = 0; j < 16 && i+j < nCIDs; ++j) { | |
504 | cid = cidMap[i+j]; | |
505 | sprintf(buf, "%02x%02x", (cid >> 8) & 0xff, cid & 0xff); | |
506 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
507 | } | |
508 | (*outputFunc)(outputStream, "\n", 1); | |
509 | } | |
510 | (*outputFunc)(outputStream, "> def\n", 6); | |
511 | } | |
512 | } else { | |
513 | // direct mapping - just fill the string(s) with s[i]=i | |
514 | sprintf(buf, "/CIDCount %d def\n", nGlyphs); | |
515 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
516 | if (nGlyphs > 32767) { | |
517 | (*outputFunc)(outputStream, "/CIDMap [\n", 10); | |
518 | for (i = 0; i < nGlyphs; i += 32767) { | |
519 | j = nGlyphs - i < 32767 ? nGlyphs - i : 32767; | |
520 | sprintf(buf, " %d string 0 1 %d {\n", 2 * j, j - 1); | |
521 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
522 | sprintf(buf, " 2 copy dup 2 mul exch %d add -8 bitshift put\n", i); | |
523 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
524 | sprintf(buf, " 1 index exch dup 2 mul 1 add exch %d add" | |
525 | " 255 and put\n", i); | |
526 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
527 | (*outputFunc)(outputStream, " } for\n", 8); | |
528 | } | |
529 | (*outputFunc)(outputStream, "] def\n", 6); | |
530 | } else { | |
531 | sprintf(buf, "/CIDMap %d string\n", 2 * nGlyphs); | |
532 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
533 | sprintf(buf, " 0 1 %d {\n", nGlyphs - 1); | |
534 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
535 | (*outputFunc)(outputStream, | |
536 | " 2 copy dup 2 mul exch -8 bitshift put\n", 42); | |
537 | (*outputFunc)(outputStream, | |
538 | " 1 index exch dup 2 mul 1 add exch 255 and put\n", 50); | |
539 | (*outputFunc)(outputStream, " } for\n", 8); | |
540 | (*outputFunc)(outputStream, "def\n", 4); | |
541 | } | |
542 | } | |
543 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
544 | sprintf(buf, "/FontBBox [%d %d %d %d] def\n", | |
545 | bbox[0], bbox[1], bbox[2], bbox[3]); | |
546 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
547 | (*outputFunc)(outputStream, "/PaintType 0 def\n", 17); | |
548 | (*outputFunc)(outputStream, "/Encoding [] readonly def\n", 26); | |
549 | (*outputFunc)(outputStream, "/CharStrings 1 dict dup begin\n", 30); | |
550 | (*outputFunc)(outputStream, " /.notdef 0 def\n", 17); | |
551 | (*outputFunc)(outputStream, " end readonly def\n", 19); | |
552 | ||
553 | // write the guts of the dictionary | |
554 | cvtSfnts(outputFunc, outputStream, NULL, needVerticalMetrics); | |
555 | ||
556 | // end the dictionary and define the font | |
557 | (*outputFunc)(outputStream, | |
558 | "CIDFontName currentdict end /CIDFont defineresource pop\n", | |
559 | 56); | |
560 | } | |
561 | ||
562 | void FoFiTrueType::convertToType0(char *psName, Gushort *cidMap, int nCIDs, | |
563 | GBool needVerticalMetrics, | |
564 | FoFiOutputFunc outputFunc, | |
565 | void *outputStream) { | |
566 | char buf[512]; | |
567 | GString *sfntsName; | |
568 | int n, i, j; | |
569 | ||
570 | // write the Type 42 sfnts array | |
571 | sfntsName = (new GString(psName))->append("_sfnts"); | |
572 | cvtSfnts(outputFunc, outputStream, sfntsName, needVerticalMetrics); | |
573 | delete sfntsName; | |
574 | ||
575 | // write the descendant Type 42 fonts | |
576 | n = cidMap ? nCIDs : nGlyphs; | |
577 | for (i = 0; i < n; i += 256) { | |
578 | (*outputFunc)(outputStream, "10 dict begin\n", 14); | |
579 | (*outputFunc)(outputStream, "/FontName /", 11); | |
580 | (*outputFunc)(outputStream, psName, strlen(psName)); | |
581 | sprintf(buf, "_%02x def\n", i >> 8); | |
582 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
583 | (*outputFunc)(outputStream, "/FontType 42 def\n", 17); | |
584 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
585 | sprintf(buf, "/FontBBox [%d %d %d %d] def\n", | |
586 | bbox[0], bbox[1], bbox[2], bbox[3]); | |
587 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
588 | (*outputFunc)(outputStream, "/PaintType 0 def\n", 17); | |
589 | (*outputFunc)(outputStream, "/sfnts ", 7); | |
590 | (*outputFunc)(outputStream, psName, strlen(psName)); | |
591 | (*outputFunc)(outputStream, "_sfnts def\n", 11); | |
592 | (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); | |
593 | for (j = 0; j < 256 && i+j < n; ++j) { | |
594 | sprintf(buf, "dup %d /c%02x put\n", j, j); | |
595 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
596 | } | |
597 | (*outputFunc)(outputStream, "readonly def\n", 13); | |
598 | (*outputFunc)(outputStream, "/CharStrings 257 dict dup begin\n", 32); | |
599 | (*outputFunc)(outputStream, "/.notdef 0 def\n", 15); | |
600 | for (j = 0; j < 256 && i+j < n; ++j) { | |
601 | sprintf(buf, "/c%02x %d def\n", j, cidMap ? cidMap[i+j] : i+j); | |
602 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
603 | } | |
604 | (*outputFunc)(outputStream, "end readonly def\n", 17); | |
605 | (*outputFunc)(outputStream, | |
606 | "FontName currentdict end definefont pop\n", 40); | |
607 | } | |
608 | ||
609 | // write the Type 0 parent font | |
610 | (*outputFunc)(outputStream, "16 dict begin\n", 14); | |
611 | (*outputFunc)(outputStream, "/FontName /", 11); | |
612 | (*outputFunc)(outputStream, psName, strlen(psName)); | |
613 | (*outputFunc)(outputStream, " def\n", 5); | |
614 | (*outputFunc)(outputStream, "/FontType 0 def\n", 16); | |
615 | (*outputFunc)(outputStream, "/FontMatrix [1 0 0 1 0 0] def\n", 30); | |
616 | (*outputFunc)(outputStream, "/FMapType 2 def\n", 16); | |
617 | (*outputFunc)(outputStream, "/Encoding [\n", 12); | |
618 | for (i = 0; i < n; i += 256) { | |
619 | sprintf(buf, "%d\n", i >> 8); | |
620 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
621 | } | |
622 | (*outputFunc)(outputStream, "] def\n", 6); | |
623 | (*outputFunc)(outputStream, "/FDepVector [\n", 14); | |
624 | for (i = 0; i < n; i += 256) { | |
625 | (*outputFunc)(outputStream, "/", 1); | |
626 | (*outputFunc)(outputStream, psName, strlen(psName)); | |
627 | sprintf(buf, "_%02x findfont\n", i >> 8); | |
628 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
629 | } | |
630 | (*outputFunc)(outputStream, "] def\n", 6); | |
631 | (*outputFunc)(outputStream, "FontName currentdict end definefont pop\n", 40); | |
632 | } | |
633 | ||
634 | void FoFiTrueType::writeTTF(FoFiOutputFunc outputFunc, | |
635 | void *outputStream, char *name, | |
636 | Gushort *codeToGID) { | |
637 | // this substitute cmap table maps char codes 0000-ffff directly to | |
638 | // glyphs 0000-ffff | |
639 | static char cmapTab[36] = { | |
640 | 0, 0, // table version number | |
641 | 0, 1, // number of encoding tables | |
642 | 0, 1, // platform ID | |
643 | 0, 0, // encoding ID | |
644 | 0, 0, 0, 12, // offset of subtable | |
645 | 0, 4, // subtable format | |
646 | 0, 24, // subtable length | |
647 | 0, 0, // subtable version | |
648 | 0, 2, // segment count * 2 | |
649 | 0, 2, // 2 * 2 ^ floor(log2(segCount)) | |
650 | 0, 0, // floor(log2(segCount)) | |
651 | 0, 0, // 2*segCount - 2*2^floor(log2(segCount)) | |
652 | (char)0xff, (char)0xff, // endCount[0] | |
653 | 0, 0, // reserved | |
654 | 0, 0, // startCount[0] | |
655 | 0, 0, // idDelta[0] | |
656 | 0, 0 // pad to a mulitple of four bytes | |
657 | }; | |
658 | static char nameTab[8] = { | |
659 | 0, 0, // format | |
660 | 0, 0, // number of name records | |
661 | 0, 6, // offset to start of string storage | |
662 | 0, 0 // pad to multiple of four bytes | |
663 | }; | |
664 | static char postTab[32] = { | |
665 | 0, 1, 0, 0, // format | |
666 | 0, 0, 0, 0, // italic angle | |
667 | 0, 0, // underline position | |
668 | 0, 0, // underline thickness | |
669 | 0, 0, 0, 0, // fixed pitch | |
670 | 0, 0, 0, 0, // min Type 42 memory | |
671 | 0, 0, 0, 0, // max Type 42 memory | |
672 | 0, 0, 0, 0, // min Type 1 memory | |
673 | 0, 0, 0, 0 // max Type 1 memory | |
674 | }; | |
675 | GBool missingCmap, missingName, missingPost, unsortedLoca, badCmapLen; | |
676 | int nZeroLengthTables; | |
677 | TrueTypeLoca *locaTable; | |
678 | TrueTypeTable *newTables; | |
679 | char *newNameTab, *newCmapTab; | |
680 | int nNewTables, cmapIdx, cmapLen, glyfLen, newNameLen, newCmapLen, next; | |
681 | Guint locaChecksum, glyfChecksum, fileChecksum; | |
682 | char *tableDir; | |
683 | char locaBuf[4], checksumBuf[4]; | |
684 | GBool ok; | |
685 | Guint t; | |
686 | int pos, i, j, k, n; | |
687 | ||
688 | // check for missing tables | |
689 | missingCmap = (cmapIdx = seekTable("cmap")) < 0; | |
690 | missingName = seekTable("name") < 0; | |
691 | missingPost = seekTable("post") < 0; | |
692 | ||
693 | // read the loca table, check to see if it's sorted | |
694 | locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca)); | |
695 | unsortedLoca = gFalse; | |
696 | i = seekTable("loca"); | |
697 | pos = tables[i].offset; | |
698 | ok = gTrue; | |
699 | for (i = 0; i <= nGlyphs; ++i) { | |
700 | if (locaFmt) { | |
701 | locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok); | |
702 | } else { | |
703 | locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok); | |
704 | } | |
705 | if (i > 0 && locaTable[i].origOffset < locaTable[i-1].origOffset) { | |
706 | unsortedLoca = gTrue; | |
707 | } | |
708 | locaTable[i].idx = i; | |
709 | } | |
710 | ||
711 | // check for zero-length tables | |
712 | nZeroLengthTables = 0; | |
713 | for (i = 0; i < nTables; ++i) { | |
714 | if (tables[i].len == 0) { | |
715 | ++nZeroLengthTables; | |
716 | } | |
717 | } | |
718 | ||
719 | // check for an incorrect cmap table length | |
720 | badCmapLen = gFalse; | |
721 | cmapLen = 0; // make gcc happy | |
722 | if (!missingCmap) { | |
723 | cmapLen = cmaps[0].offset + cmaps[0].len; | |
724 | for (i = 1; i < nCmaps; ++i) { | |
725 | if (cmaps[i].offset + cmaps[i].len > cmapLen) { | |
726 | cmapLen = cmaps[i].offset + cmaps[i].len; | |
727 | } | |
728 | } | |
729 | cmapLen -= tables[cmapIdx].offset; | |
730 | if (cmapLen > tables[cmapIdx].len) { | |
731 | badCmapLen = gTrue; | |
732 | } | |
733 | } | |
734 | ||
735 | // if nothing is broken, just write the TTF file as is | |
736 | if (!missingCmap && !missingName && !missingPost && !unsortedLoca && | |
737 | !badCmapLen && nZeroLengthTables == 0 && !name && !codeToGID) { | |
738 | (*outputFunc)(outputStream, (char *)file, len); | |
739 | goto done1; | |
740 | } | |
741 | ||
742 | // sort the 'loca' table: some (non-compliant) fonts have | |
743 | // out-of-order loca tables; in order to correctly handle the case | |
744 | // where (compliant) fonts have empty entries in the middle of the | |
745 | // table, cmpTrueTypeLocaOffset uses offset as its primary sort key, | |
746 | // and idx as its secondary key (ensuring that adjacent entries with | |
747 | // the same pos value remain in the same order) | |
748 | glyfLen = 0; // make gcc happy | |
749 | if (unsortedLoca) { | |
750 | qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca), | |
751 | &cmpTrueTypeLocaOffset); | |
752 | for (i = 0; i < nGlyphs; ++i) { | |
753 | locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset; | |
754 | } | |
755 | locaTable[nGlyphs].len = 0; | |
756 | qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca), | |
757 | &cmpTrueTypeLocaIdx); | |
758 | pos = 0; | |
759 | for (i = 0; i <= nGlyphs; ++i) { | |
760 | locaTable[i].newOffset = pos; | |
761 | pos += locaTable[i].len; | |
762 | if (pos & 3) { | |
763 | pos += 4 - (pos & 3); | |
764 | } | |
765 | } | |
766 | glyfLen = pos; | |
767 | } | |
768 | ||
769 | // compute checksums for the loca and glyf tables | |
770 | locaChecksum = glyfChecksum = 0; | |
771 | if (unsortedLoca) { | |
772 | if (locaFmt) { | |
773 | for (j = 0; j <= nGlyphs; ++j) { | |
774 | locaChecksum += locaTable[j].newOffset; | |
775 | } | |
776 | } else { | |
777 | for (j = 0; j <= nGlyphs; j += 2) { | |
778 | locaChecksum += locaTable[j].newOffset << 16; | |
779 | if (j + 1 <= nGlyphs) { | |
780 | locaChecksum += locaTable[j+1].newOffset; | |
781 | } | |
782 | } | |
783 | } | |
784 | pos = tables[seekTable("glyf")].offset; | |
785 | for (j = 0; j < nGlyphs; ++j) { | |
786 | n = locaTable[j].len; | |
787 | if (n > 0) { | |
788 | k = locaTable[j].origOffset; | |
789 | if (checkRegion(pos + k, n)) { | |
790 | glyfChecksum += computeTableChecksum(file + pos + k, n); | |
791 | } | |
792 | } | |
793 | } | |
794 | } | |
795 | ||
796 | // construct the new name table | |
797 | if (name) { | |
798 | n = strlen(name); | |
799 | newNameLen = (6 + 4*12 + 2 * (3*n + 7) + 3) & ~3; | |
800 | newNameTab = (char *)gmalloc(newNameLen); | |
801 | memset(newNameTab, 0, newNameLen); | |
802 | newNameTab[0] = 0; // format selector | |
803 | newNameTab[1] = 0; | |
804 | newNameTab[2] = 0; // number of name records | |
805 | newNameTab[3] = 4; | |
806 | newNameTab[4] = 0; // offset to start of string storage | |
807 | newNameTab[5] = 6 + 4*12; | |
808 | next = 0; | |
809 | for (i = 0; i < 4; ++i) { | |
810 | newNameTab[6 + i*12 + 0] = 0; // platform ID = Microsoft | |
811 | newNameTab[6 + i*12 + 1] = 3; | |
812 | newNameTab[6 + i*12 + 2] = 0; // encoding ID = Unicode | |
813 | newNameTab[6 + i*12 + 3] = 1; | |
814 | newNameTab[6 + i*12 + 4] = 0x04; // language ID = American English | |
815 | newNameTab[6 + i*12 + 5] = 0x09; | |
816 | newNameTab[6 + i*12 + 6] = 0; // name ID | |
817 | newNameTab[6 + i*12 + 7] = i + 1; | |
818 | newNameTab[6 + i*12 + 8] = i+1 == 2 ? 0 : ((2*n) >> 8); // string length | |
819 | newNameTab[6 + i*12 + 9] = i+1 == 2 ? 14 : ((2*n) & 0xff); | |
820 | newNameTab[6 + i*12 + 10] = next >> 8; // string offset | |
821 | newNameTab[6 + i*12 + 11] = next & 0xff; | |
822 | if (i+1 == 2) { | |
823 | memcpy(newNameTab + 6 + 4*12 + next, "\0R\0e\0g\0u\0l\0a\0r", 14); | |
824 | next += 14; | |
825 | } else { | |
826 | for (j = 0; j < n; ++j) { | |
827 | newNameTab[6 + 4*12 + next + 2*j] = 0; | |
828 | newNameTab[6 + 4*12 + next + 2*j + 1] = name[j]; | |
829 | } | |
830 | next += 2*n; | |
831 | } | |
832 | } | |
833 | } else { | |
834 | newNameLen = 0; | |
835 | newNameTab = NULL; | |
836 | } | |
837 | ||
838 | // construct the new cmap table | |
839 | if (codeToGID) { | |
840 | newCmapLen = 44 + 256 * 2; | |
841 | newCmapTab = (char *)gmalloc(newCmapLen); | |
842 | newCmapTab[0] = 0; // table version number = 0 | |
843 | newCmapTab[1] = 0; | |
844 | newCmapTab[2] = 0; // number of encoding tables = 1 | |
845 | newCmapTab[3] = 1; | |
846 | newCmapTab[4] = 0; // platform ID = Microsoft | |
847 | newCmapTab[5] = 3; | |
848 | newCmapTab[6] = 0; // encoding ID = Unicode | |
849 | newCmapTab[7] = 1; | |
850 | newCmapTab[8] = 0; // offset of subtable | |
851 | newCmapTab[9] = 0; | |
852 | newCmapTab[10] = 0; | |
853 | newCmapTab[11] = 12; | |
854 | newCmapTab[12] = 0; // subtable format = 4 | |
855 | newCmapTab[13] = 4; | |
856 | newCmapTab[14] = 0x02; // subtable length | |
857 | newCmapTab[15] = 0x20; | |
858 | newCmapTab[16] = 0; // subtable version = 0 | |
859 | newCmapTab[17] = 0; | |
860 | newCmapTab[18] = 0; // segment count * 2 | |
861 | newCmapTab[19] = 4; | |
862 | newCmapTab[20] = 0; // 2 * 2 ^ floor(log2(segCount)) | |
863 | newCmapTab[21] = 4; | |
864 | newCmapTab[22] = 0; // floor(log2(segCount)) | |
865 | newCmapTab[23] = 1; | |
866 | newCmapTab[24] = 0; // 2*segCount - 2*2^floor(log2(segCount)) | |
867 | newCmapTab[25] = 0; | |
868 | newCmapTab[26] = 0x00; // endCount[0] | |
869 | newCmapTab[27] = (char)0xff; | |
870 | newCmapTab[28] = (char)0xff; // endCount[1] | |
871 | newCmapTab[29] = (char)0xff; | |
872 | newCmapTab[30] = 0; // reserved | |
873 | newCmapTab[31] = 0; | |
874 | newCmapTab[32] = 0x00; // startCount[0] | |
875 | newCmapTab[33] = 0x00; | |
876 | newCmapTab[34] = (char)0xff; // startCount[1] | |
877 | newCmapTab[35] = (char)0xff; | |
878 | newCmapTab[36] = 0; // idDelta[0] | |
879 | newCmapTab[37] = 0; | |
880 | newCmapTab[38] = 0; // idDelta[1] | |
881 | newCmapTab[39] = 1; | |
882 | newCmapTab[40] = 0; // idRangeOffset[0] | |
883 | newCmapTab[41] = 4; | |
884 | newCmapTab[42] = 0; // idRangeOffset[1] | |
885 | newCmapTab[43] = 0; | |
886 | for (i = 0; i < 256; ++i) { | |
887 | newCmapTab[44 + 2*i] = codeToGID[i] >> 8; | |
888 | newCmapTab[44 + 2*i + 1] = codeToGID[i] & 0xff; | |
889 | } | |
890 | } else { | |
891 | newCmapLen = 0; | |
892 | newCmapTab = NULL; | |
893 | } | |
894 | ||
895 | // construct the new table directory: | |
896 | // - keep all original tables with non-zero length | |
897 | // - fix the cmap table's length, if necessary | |
898 | // - add missing tables | |
899 | // - sort the table by tag | |
900 | // - compute new table positions, including 4-byte alignment | |
901 | // - (re)compute table checksums | |
902 | nNewTables = nTables - nZeroLengthTables + | |
903 | (missingCmap ? 1 : 0) + (missingName ? 1 : 0) + | |
904 | (missingPost ? 1 : 0); | |
905 | newTables = (TrueTypeTable *)gmallocn(nNewTables, sizeof(TrueTypeTable)); | |
906 | j = 0; | |
907 | for (i = 0; i < nTables; ++i) { | |
908 | if (tables[i].len > 0) { | |
909 | newTables[j] = tables[i]; | |
910 | newTables[j].origOffset = tables[i].offset; | |
911 | if (checkRegion(tables[i].offset, newTables[i].len)) { | |
912 | newTables[j].checksum = | |
913 | computeTableChecksum(file + tables[i].offset, tables[i].len); | |
914 | if (tables[i].tag == headTag) { | |
915 | // don't include the file checksum | |
916 | newTables[j].checksum -= getU32BE(tables[i].offset + 8, &ok); | |
917 | } | |
918 | } | |
919 | if (newTables[j].tag == cmapTag && codeToGID) { | |
920 | newTables[j].len = newCmapLen; | |
921 | newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab, | |
922 | newCmapLen); | |
923 | } else if (newTables[j].tag == cmapTag && badCmapLen) { | |
924 | newTables[j].len = cmapLen; | |
925 | } else if (newTables[j].tag == locaTag && unsortedLoca) { | |
926 | newTables[j].len = (nGlyphs + 1) * (locaFmt ? 4 : 2); | |
927 | newTables[j].checksum = locaChecksum; | |
928 | } else if (newTables[j].tag == glyfTag && unsortedLoca) { | |
929 | newTables[j].len = glyfLen; | |
930 | newTables[j].checksum = glyfChecksum; | |
931 | } else if (newTables[j].tag == nameTag && name) { | |
932 | newTables[j].len = newNameLen; | |
933 | newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab, | |
934 | newNameLen); | |
935 | } | |
936 | ++j; | |
937 | } | |
938 | } | |
939 | if (missingCmap) { | |
940 | newTables[j].tag = cmapTag; | |
941 | if (codeToGID) { | |
942 | newTables[j].checksum = computeTableChecksum((Guchar *)newCmapTab, | |
943 | newCmapLen); | |
944 | newTables[j].len = newCmapLen; | |
945 | } else { | |
946 | newTables[j].checksum = computeTableChecksum((Guchar *)cmapTab, | |
947 | sizeof(cmapTab)); | |
948 | newTables[j].len = sizeof(cmapTab); | |
949 | } | |
950 | ++j; | |
951 | } | |
952 | if (missingName) { | |
953 | newTables[j].tag = nameTag; | |
954 | if (name) { | |
955 | newTables[j].checksum = computeTableChecksum((Guchar *)newNameTab, | |
956 | newNameLen); | |
957 | newTables[j].len = newNameLen; | |
958 | } else { | |
959 | newTables[j].checksum = computeTableChecksum((Guchar *)nameTab, | |
960 | sizeof(nameTab)); | |
961 | newTables[j].len = sizeof(nameTab); | |
962 | } | |
963 | ++j; | |
964 | } | |
965 | if (missingPost) { | |
966 | newTables[j].tag = postTag; | |
967 | newTables[j].checksum = computeTableChecksum((Guchar *)postTab, | |
968 | sizeof(postTab)); | |
969 | newTables[j].len = sizeof(postTab); | |
970 | ++j; | |
971 | } | |
972 | qsort(newTables, nNewTables, sizeof(TrueTypeTable), | |
973 | &cmpTrueTypeTableTag); | |
974 | pos = 12 + nNewTables * 16; | |
975 | for (i = 0; i < nNewTables; ++i) { | |
976 | newTables[i].offset = pos; | |
977 | pos += newTables[i].len; | |
978 | if (pos & 3) { | |
979 | pos += 4 - (pos & 3); | |
980 | } | |
981 | } | |
982 | ||
983 | // write the table directory | |
984 | tableDir = (char *)gmalloc(12 + nNewTables * 16); | |
985 | tableDir[0] = 0x00; // sfnt version | |
986 | tableDir[1] = 0x01; | |
987 | tableDir[2] = 0x00; | |
988 | tableDir[3] = 0x00; | |
989 | tableDir[4] = (char)((nNewTables >> 8) & 0xff); // numTables | |
990 | tableDir[5] = (char)(nNewTables & 0xff); | |
991 | for (i = -1, t = (Guint)nNewTables; t; ++i, t >>= 1) ; | |
992 | t = 1 << (4 + i); | |
993 | tableDir[6] = (char)((t >> 8) & 0xff); // searchRange | |
994 | tableDir[7] = (char)(t & 0xff); | |
995 | tableDir[8] = (char)((i >> 8) & 0xff); // entrySelector | |
996 | tableDir[9] = (char)(i & 0xff); | |
997 | t = nNewTables * 16 - t; | |
998 | tableDir[10] = (char)((t >> 8) & 0xff); // rangeShift | |
999 | tableDir[11] = (char)(t & 0xff); | |
1000 | pos = 12; | |
1001 | for (i = 0; i < nNewTables; ++i) { | |
1002 | tableDir[pos ] = (char)(newTables[i].tag >> 24); | |
1003 | tableDir[pos+ 1] = (char)(newTables[i].tag >> 16); | |
1004 | tableDir[pos+ 2] = (char)(newTables[i].tag >> 8); | |
1005 | tableDir[pos+ 3] = (char) newTables[i].tag; | |
1006 | tableDir[pos+ 4] = (char)(newTables[i].checksum >> 24); | |
1007 | tableDir[pos+ 5] = (char)(newTables[i].checksum >> 16); | |
1008 | tableDir[pos+ 6] = (char)(newTables[i].checksum >> 8); | |
1009 | tableDir[pos+ 7] = (char) newTables[i].checksum; | |
1010 | tableDir[pos+ 8] = (char)(newTables[i].offset >> 24); | |
1011 | tableDir[pos+ 9] = (char)(newTables[i].offset >> 16); | |
1012 | tableDir[pos+10] = (char)(newTables[i].offset >> 8); | |
1013 | tableDir[pos+11] = (char) newTables[i].offset; | |
1014 | tableDir[pos+12] = (char)(newTables[i].len >> 24); | |
1015 | tableDir[pos+13] = (char)(newTables[i].len >> 16); | |
1016 | tableDir[pos+14] = (char)(newTables[i].len >> 8); | |
1017 | tableDir[pos+15] = (char) newTables[i].len; | |
1018 | pos += 16; | |
1019 | } | |
1020 | (*outputFunc)(outputStream, tableDir, 12 + nNewTables * 16); | |
1021 | ||
1022 | // compute the file checksum | |
1023 | fileChecksum = computeTableChecksum((Guchar *)tableDir, | |
1024 | 12 + nNewTables * 16); | |
1025 | for (i = 0; i < nNewTables; ++i) { | |
1026 | fileChecksum += newTables[i].checksum; | |
1027 | } | |
1028 | fileChecksum = 0xb1b0afba - fileChecksum; | |
1029 | ||
1030 | // write the tables | |
1031 | for (i = 0; i < nNewTables; ++i) { | |
1032 | if (newTables[i].tag == headTag) { | |
1033 | if (checkRegion(newTables[i].origOffset, newTables[i].len)) { | |
1034 | (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, 8); | |
1035 | checksumBuf[0] = fileChecksum >> 24; | |
1036 | checksumBuf[1] = fileChecksum >> 16; | |
1037 | checksumBuf[2] = fileChecksum >> 8; | |
1038 | checksumBuf[3] = fileChecksum; | |
1039 | (*outputFunc)(outputStream, checksumBuf, 4); | |
1040 | (*outputFunc)(outputStream, | |
1041 | (char *)file + newTables[i].origOffset + 12, | |
1042 | newTables[i].len - 12); | |
1043 | } else { | |
1044 | for (j = 0; j < newTables[i].len; ++j) { | |
1045 | (*outputFunc)(outputStream, "\0", 1); | |
1046 | } | |
1047 | } | |
1048 | } else if (newTables[i].tag == cmapTag && codeToGID) { | |
1049 | (*outputFunc)(outputStream, newCmapTab, newTables[i].len); | |
1050 | } else if (newTables[i].tag == cmapTag && missingCmap) { | |
1051 | (*outputFunc)(outputStream, cmapTab, newTables[i].len); | |
1052 | } else if (newTables[i].tag == nameTag && name) { | |
1053 | (*outputFunc)(outputStream, newNameTab, newTables[i].len); | |
1054 | } else if (newTables[i].tag == nameTag && missingName) { | |
1055 | (*outputFunc)(outputStream, nameTab, newTables[i].len); | |
1056 | } else if (newTables[i].tag == postTag && missingPost) { | |
1057 | (*outputFunc)(outputStream, postTab, newTables[i].len); | |
1058 | } else if (newTables[i].tag == locaTag && unsortedLoca) { | |
1059 | for (j = 0; j <= nGlyphs; ++j) { | |
1060 | if (locaFmt) { | |
1061 | locaBuf[0] = (char)(locaTable[j].newOffset >> 24); | |
1062 | locaBuf[1] = (char)(locaTable[j].newOffset >> 16); | |
1063 | locaBuf[2] = (char)(locaTable[j].newOffset >> 8); | |
1064 | locaBuf[3] = (char) locaTable[j].newOffset; | |
1065 | (*outputFunc)(outputStream, locaBuf, 4); | |
1066 | } else { | |
1067 | locaBuf[0] = (char)(locaTable[j].newOffset >> 9); | |
1068 | locaBuf[1] = (char)(locaTable[j].newOffset >> 1); | |
1069 | (*outputFunc)(outputStream, locaBuf, 2); | |
1070 | } | |
1071 | } | |
1072 | } else if (newTables[i].tag == glyfTag && unsortedLoca) { | |
1073 | pos = tables[seekTable("glyf")].offset; | |
1074 | for (j = 0; j < nGlyphs; ++j) { | |
1075 | n = locaTable[j].len; | |
1076 | if (n > 0) { | |
1077 | k = locaTable[j].origOffset; | |
1078 | if (checkRegion(pos + k, n)) { | |
1079 | (*outputFunc)(outputStream, (char *)file + pos + k, n); | |
1080 | } else { | |
1081 | for (k = 0; k < n; ++k) { | |
1082 | (*outputFunc)(outputStream, "\0", 1); | |
1083 | } | |
1084 | } | |
1085 | if ((k = locaTable[j].len & 3)) { | |
1086 | (*outputFunc)(outputStream, "\0\0\0\0", 4 - k); | |
1087 | } | |
1088 | } | |
1089 | } | |
1090 | } else { | |
1091 | if (checkRegion(newTables[i].origOffset, newTables[i].len)) { | |
1092 | (*outputFunc)(outputStream, (char *)file + newTables[i].origOffset, | |
1093 | newTables[i].len); | |
1094 | } else { | |
1095 | for (j = 0; j < newTables[i].len; ++j) { | |
1096 | (*outputFunc)(outputStream, "\0", 1); | |
1097 | } | |
1098 | } | |
1099 | } | |
1100 | if (newTables[i].len & 3) { | |
1101 | (*outputFunc)(outputStream, "\0\0\0", 4 - (newTables[i].len & 3)); | |
1102 | } | |
1103 | } | |
1104 | ||
1105 | gfree(newCmapTab); | |
1106 | gfree(newNameTab); | |
1107 | gfree(tableDir); | |
1108 | gfree(newTables); | |
1109 | done1: | |
1110 | gfree(locaTable); | |
1111 | } | |
1112 | ||
1113 | void FoFiTrueType::cvtEncoding(char **encoding, | |
1114 | FoFiOutputFunc outputFunc, | |
1115 | void *outputStream) { | |
1116 | char *name; | |
1117 | char buf[64]; | |
1118 | int i; | |
1119 | ||
1120 | (*outputFunc)(outputStream, "/Encoding 256 array\n", 20); | |
1121 | if (encoding) { | |
1122 | for (i = 0; i < 256; ++i) { | |
1123 | if (!(name = encoding[i])) { | |
1124 | name = ".notdef"; | |
1125 | } | |
1126 | sprintf(buf, "dup %d /", i); | |
1127 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
1128 | (*outputFunc)(outputStream, name, strlen(name)); | |
1129 | (*outputFunc)(outputStream, " put\n", 5); | |
1130 | } | |
1131 | } else { | |
1132 | for (i = 0; i < 256; ++i) { | |
1133 | sprintf(buf, "dup %d /c%02x put\n", i, i); | |
1134 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
1135 | } | |
1136 | } | |
1137 | (*outputFunc)(outputStream, "readonly def\n", 13); | |
1138 | } | |
1139 | ||
1140 | void FoFiTrueType::cvtCharStrings(char **encoding, | |
1141 | Gushort *codeToGID, | |
1142 | FoFiOutputFunc outputFunc, | |
1143 | void *outputStream) { | |
1144 | char *name; | |
1145 | char buf[64], buf2[16]; | |
1146 | int i, k; | |
1147 | ||
1148 | // always define '.notdef' | |
1149 | (*outputFunc)(outputStream, "/CharStrings 256 dict dup begin\n", 32); | |
1150 | (*outputFunc)(outputStream, "/.notdef 0 def\n", 15); | |
1151 | ||
1152 | // if there's no 'cmap' table, punt | |
1153 | if (nCmaps == 0) { | |
1154 | goto err; | |
1155 | } | |
1156 | ||
1157 | // map char name to glyph index: | |
1158 | // 1. use encoding to map name to char code | |
1159 | // 2. use codeToGID to map char code to glyph index | |
1160 | // N.B. We do this in reverse order because font subsets can have | |
1161 | // weird encodings that use the same character name twice, and | |
1162 | // the first definition is probably the one we want. | |
1163 | k = 0; // make gcc happy | |
1164 | for (i = 255; i >= 0; --i) { | |
1165 | if (encoding) { | |
1166 | name = encoding[i]; | |
1167 | } else { | |
1168 | sprintf(buf2, "c%02x", i); | |
1169 | name = buf2; | |
1170 | } | |
1171 | if (name && strcmp(name, ".notdef")) { | |
1172 | k = codeToGID[i]; | |
1173 | // note: Distiller (maybe Adobe's PS interpreter in general) | |
1174 | // doesn't like TrueType fonts that have CharStrings entries | |
1175 | // which point to nonexistent glyphs, hence the (k < nGlyphs) | |
1176 | // test | |
1177 | if (k > 0 && k < nGlyphs) { | |
1178 | (*outputFunc)(outputStream, "/", 1); | |
1179 | (*outputFunc)(outputStream, name, strlen(name)); | |
1180 | sprintf(buf, " %d def\n", k); | |
1181 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
1182 | } | |
1183 | } | |
1184 | } | |
1185 | ||
1186 | err: | |
1187 | (*outputFunc)(outputStream, "end readonly def\n", 17); | |
1188 | } | |
1189 | ||
1190 | void FoFiTrueType::cvtSfnts(FoFiOutputFunc outputFunc, | |
1191 | void *outputStream, GString *name, | |
1192 | GBool needVerticalMetrics) { | |
1193 | Guchar headData[54]; | |
1194 | TrueTypeLoca *locaTable; | |
1195 | Guchar *locaData; | |
1196 | TrueTypeTable newTables[nT42Tables]; | |
1197 | Guchar tableDir[12 + nT42Tables*16]; | |
1198 | GBool ok; | |
1199 | Guint checksum; | |
1200 | int nNewTables; | |
1201 | int length, pos, glyfPos, i, j, k; | |
1202 | Guchar vheaTab[36] = { | |
1203 | 0, 1, 0, 0, // table version number | |
1204 | 0, 0, // ascent | |
1205 | 0, 0, // descent | |
1206 | 0, 0, // reserved | |
1207 | 0, 0, // max advance height | |
1208 | 0, 0, // min top side bearing | |
1209 | 0, 0, // min bottom side bearing | |
1210 | 0, 0, // y max extent | |
1211 | 0, 0, // caret slope rise | |
1212 | 0, 1, // caret slope run | |
1213 | 0, 0, // caret offset | |
1214 | 0, 0, // reserved | |
1215 | 0, 0, // reserved | |
1216 | 0, 0, // reserved | |
1217 | 0, 0, // reserved | |
1218 | 0, 0, // metric data format | |
1219 | 0, 1 // number of advance heights in vmtx table | |
1220 | }; | |
1221 | Guchar *vmtxTab; | |
1222 | GBool needVhea, needVmtx; | |
1223 | int advance; | |
1224 | ||
1225 | // construct the 'head' table, zero out the font checksum | |
1226 | i = seekTable("head"); | |
1227 | pos = tables[i].offset; | |
1228 | if (!checkRegion(pos, 54)) { | |
1229 | return; | |
1230 | } | |
1231 | memcpy(headData, file + pos, 54); | |
1232 | headData[8] = headData[9] = headData[10] = headData[11] = (Guchar)0; | |
1233 | ||
1234 | // read the original 'loca' table, pad entries out to 4 bytes, and | |
1235 | // sort it into proper order -- some (non-compliant) fonts have | |
1236 | // out-of-order loca tables; in order to correctly handle the case | |
1237 | // where (compliant) fonts have empty entries in the middle of the | |
1238 | // table, cmpTrueTypeLocaPos uses offset as its primary sort key, | |
1239 | // and idx as its secondary key (ensuring that adjacent entries with | |
1240 | // the same pos value remain in the same order) | |
1241 | locaTable = (TrueTypeLoca *)gmallocn(nGlyphs + 1, sizeof(TrueTypeLoca)); | |
1242 | i = seekTable("loca"); | |
1243 | pos = tables[i].offset; | |
1244 | ok = gTrue; | |
1245 | for (i = 0; i <= nGlyphs; ++i) { | |
1246 | locaTable[i].idx = i; | |
1247 | if (locaFmt) { | |
1248 | locaTable[i].origOffset = (int)getU32BE(pos + i*4, &ok); | |
1249 | } else { | |
1250 | locaTable[i].origOffset = 2 * getU16BE(pos + i*2, &ok); | |
1251 | } | |
1252 | } | |
1253 | qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca), | |
1254 | &cmpTrueTypeLocaOffset); | |
1255 | for (i = 0; i < nGlyphs; ++i) { | |
1256 | locaTable[i].len = locaTable[i+1].origOffset - locaTable[i].origOffset; | |
1257 | } | |
1258 | locaTable[nGlyphs].len = 0; | |
1259 | qsort(locaTable, nGlyphs + 1, sizeof(TrueTypeLoca), | |
1260 | &cmpTrueTypeLocaIdx); | |
1261 | pos = 0; | |
1262 | for (i = 0; i <= nGlyphs; ++i) { | |
1263 | locaTable[i].newOffset = pos; | |
1264 | pos += locaTable[i].len; | |
1265 | if (pos & 3) { | |
1266 | pos += 4 - (pos & 3); | |
1267 | } | |
1268 | } | |
1269 | ||
1270 | // construct the new 'loca' table | |
1271 | locaData = (Guchar *)gmallocn(nGlyphs + 1, (locaFmt ? 4 : 2)); | |
1272 | for (i = 0; i <= nGlyphs; ++i) { | |
1273 | pos = locaTable[i].newOffset; | |
1274 | if (locaFmt) { | |
1275 | locaData[4*i ] = (Guchar)(pos >> 24); | |
1276 | locaData[4*i+1] = (Guchar)(pos >> 16); | |
1277 | locaData[4*i+2] = (Guchar)(pos >> 8); | |
1278 | locaData[4*i+3] = (Guchar) pos; | |
1279 | } else { | |
1280 | locaData[2*i ] = (Guchar)(pos >> 9); | |
1281 | locaData[2*i+1] = (Guchar)(pos >> 1); | |
1282 | } | |
1283 | } | |
1284 | ||
1285 | // count the number of tables | |
1286 | nNewTables = 0; | |
1287 | for (i = 0; i < nT42Tables; ++i) { | |
1288 | if (t42Tables[i].required || | |
1289 | seekTable(t42Tables[i].tag) >= 0) { | |
1290 | ++nNewTables; | |
1291 | } | |
1292 | } | |
1293 | vmtxTab = NULL; // make gcc happy | |
1294 | advance = 0; // make gcc happy | |
1295 | if (needVerticalMetrics) { | |
1296 | needVhea = seekTable("vhea") < 0; | |
1297 | needVmtx = seekTable("vmtx") < 0; | |
1298 | if (needVhea || needVmtx) { | |
1299 | i = seekTable("head"); | |
1300 | advance = getU16BE(tables[i].offset + 18, &ok); // units per em | |
1301 | if (needVhea) { | |
1302 | ++nNewTables; | |
1303 | } | |
1304 | if (needVmtx) { | |
1305 | ++nNewTables; | |
1306 | } | |
1307 | } | |
1308 | } | |
1309 | ||
1310 | // construct the new table headers, including table checksums | |
1311 | // (pad each table out to a multiple of 4 bytes) | |
1312 | pos = 12 + nNewTables*16; | |
1313 | k = 0; | |
1314 | for (i = 0; i < nT42Tables; ++i) { | |
1315 | length = -1; | |
1316 | checksum = 0; // make gcc happy | |
1317 | if (i == t42HeadTable) { | |
1318 | length = 54; | |
1319 | checksum = computeTableChecksum(headData, 54); | |
1320 | } else if (i == t42LocaTable) { | |
1321 | length = (nGlyphs + 1) * (locaFmt ? 4 : 2); | |
1322 | checksum = computeTableChecksum(locaData, length); | |
1323 | } else if (i == t42GlyfTable) { | |
1324 | length = 0; | |
1325 | checksum = 0; | |
1326 | glyfPos = tables[seekTable("glyf")].offset; | |
1327 | for (j = 0; j < nGlyphs; ++j) { | |
1328 | length += locaTable[j].len; | |
1329 | if (length & 3) { | |
1330 | length += 4 - (length & 3); | |
1331 | } | |
1332 | if (checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { | |
1333 | checksum += | |
1334 | computeTableChecksum(file + glyfPos + locaTable[j].origOffset, | |
1335 | locaTable[j].len); | |
1336 | } | |
1337 | } | |
1338 | } else { | |
1339 | if ((j = seekTable(t42Tables[i].tag)) >= 0) { | |
1340 | length = tables[j].len; | |
1341 | if (checkRegion(tables[j].offset, length)) { | |
1342 | checksum = computeTableChecksum(file + tables[j].offset, length); | |
1343 | } | |
1344 | } else if (needVerticalMetrics && i == t42VheaTable) { | |
1345 | vheaTab[10] = advance / 256; // max advance height | |
1346 | vheaTab[11] = advance % 256; | |
1347 | length = sizeof(vheaTab); | |
1348 | checksum = computeTableChecksum(vheaTab, length); | |
1349 | } else if (needVerticalMetrics && i == t42VmtxTable) { | |
1350 | length = 4 + (nGlyphs - 1) * 4; | |
1351 | vmtxTab = (Guchar *)gmalloc(length); | |
1352 | vmtxTab[0] = advance / 256; | |
1353 | vmtxTab[1] = advance % 256; | |
1354 | for (j = 2; j < length; j += 2) { | |
1355 | vmtxTab[j] = 0; | |
1356 | vmtxTab[j+1] = 0; | |
1357 | } | |
1358 | checksum = computeTableChecksum(vmtxTab, length); | |
1359 | } else if (t42Tables[i].required) { | |
1360 | //~ error(-1, "Embedded TrueType font is missing a required table ('%s')", | |
1361 | //~ t42Tables[i].tag); | |
1362 | length = 0; | |
1363 | checksum = 0; | |
1364 | } | |
1365 | } | |
1366 | if (length >= 0) { | |
1367 | newTables[k].tag = ((t42Tables[i].tag[0] & 0xff) << 24) | | |
1368 | ((t42Tables[i].tag[1] & 0xff) << 16) | | |
1369 | ((t42Tables[i].tag[2] & 0xff) << 8) | | |
1370 | (t42Tables[i].tag[3] & 0xff); | |
1371 | newTables[k].checksum = checksum; | |
1372 | newTables[k].offset = pos; | |
1373 | newTables[k].len = length; | |
1374 | pos += length; | |
1375 | if (pos & 3) { | |
1376 | pos += 4 - (length & 3); | |
1377 | } | |
1378 | ++k; | |
1379 | } | |
1380 | } | |
1381 | ||
1382 | // construct the table directory | |
1383 | tableDir[0] = 0x00; // sfnt version | |
1384 | tableDir[1] = 0x01; | |
1385 | tableDir[2] = 0x00; | |
1386 | tableDir[3] = 0x00; | |
1387 | tableDir[4] = 0; // numTables | |
1388 | tableDir[5] = nNewTables; | |
1389 | tableDir[6] = 0; // searchRange | |
1390 | tableDir[7] = (Guchar)128; | |
1391 | tableDir[8] = 0; // entrySelector | |
1392 | tableDir[9] = 3; | |
1393 | tableDir[10] = 0; // rangeShift | |
1394 | tableDir[11] = (Guchar)(16 * nNewTables - 128); | |
1395 | pos = 12; | |
1396 | for (i = 0; i < nNewTables; ++i) { | |
1397 | tableDir[pos ] = (Guchar)(newTables[i].tag >> 24); | |
1398 | tableDir[pos+ 1] = (Guchar)(newTables[i].tag >> 16); | |
1399 | tableDir[pos+ 2] = (Guchar)(newTables[i].tag >> 8); | |
1400 | tableDir[pos+ 3] = (Guchar) newTables[i].tag; | |
1401 | tableDir[pos+ 4] = (Guchar)(newTables[i].checksum >> 24); | |
1402 | tableDir[pos+ 5] = (Guchar)(newTables[i].checksum >> 16); | |
1403 | tableDir[pos+ 6] = (Guchar)(newTables[i].checksum >> 8); | |
1404 | tableDir[pos+ 7] = (Guchar) newTables[i].checksum; | |
1405 | tableDir[pos+ 8] = (Guchar)(newTables[i].offset >> 24); | |
1406 | tableDir[pos+ 9] = (Guchar)(newTables[i].offset >> 16); | |
1407 | tableDir[pos+10] = (Guchar)(newTables[i].offset >> 8); | |
1408 | tableDir[pos+11] = (Guchar) newTables[i].offset; | |
1409 | tableDir[pos+12] = (Guchar)(newTables[i].len >> 24); | |
1410 | tableDir[pos+13] = (Guchar)(newTables[i].len >> 16); | |
1411 | tableDir[pos+14] = (Guchar)(newTables[i].len >> 8); | |
1412 | tableDir[pos+15] = (Guchar) newTables[i].len; | |
1413 | pos += 16; | |
1414 | } | |
1415 | ||
1416 | // compute the font checksum and store it in the head table | |
1417 | checksum = computeTableChecksum(tableDir, 12 + nNewTables*16); | |
1418 | for (i = 0; i < nNewTables; ++i) { | |
1419 | checksum += newTables[i].checksum; | |
1420 | } | |
1421 | checksum = 0xb1b0afba - checksum; // because the TrueType spec says so | |
1422 | headData[ 8] = (Guchar)(checksum >> 24); | |
1423 | headData[ 9] = (Guchar)(checksum >> 16); | |
1424 | headData[10] = (Guchar)(checksum >> 8); | |
1425 | headData[11] = (Guchar) checksum; | |
1426 | ||
1427 | // start the sfnts array | |
1428 | if (name) { | |
1429 | (*outputFunc)(outputStream, "/", 1); | |
1430 | (*outputFunc)(outputStream, name->getCString(), name->getLength()); | |
1431 | (*outputFunc)(outputStream, " [\n", 3); | |
1432 | } else { | |
1433 | (*outputFunc)(outputStream, "/sfnts [\n", 9); | |
1434 | } | |
1435 | ||
1436 | // write the table directory | |
1437 | dumpString(tableDir, 12 + nNewTables*16, outputFunc, outputStream); | |
1438 | ||
1439 | // write the tables | |
1440 | for (i = 0; i < nNewTables; ++i) { | |
1441 | if (i == t42HeadTable) { | |
1442 | dumpString(headData, 54, outputFunc, outputStream); | |
1443 | } else if (i == t42LocaTable) { | |
1444 | length = (nGlyphs + 1) * (locaFmt ? 4 : 2); | |
1445 | dumpString(locaData, length, outputFunc, outputStream); | |
1446 | } else if (i == t42GlyfTable) { | |
1447 | glyfPos = tables[seekTable("glyf")].offset; | |
1448 | for (j = 0; j < nGlyphs; ++j) { | |
1449 | if (locaTable[j].len > 0 && | |
1450 | checkRegion(glyfPos + locaTable[j].origOffset, locaTable[j].len)) { | |
1451 | dumpString(file + glyfPos + locaTable[j].origOffset, | |
1452 | locaTable[j].len, outputFunc, outputStream); | |
1453 | } | |
1454 | } | |
1455 | } else { | |
1456 | // length == 0 means the table is missing and the error was | |
1457 | // already reported during the construction of the table | |
1458 | // headers | |
1459 | if ((length = newTables[i].len) > 0) { | |
1460 | if ((j = seekTable(t42Tables[i].tag)) >= 0 && | |
1461 | checkRegion(tables[j].offset, tables[j].len)) { | |
1462 | dumpString(file + tables[j].offset, tables[j].len, | |
1463 | outputFunc, outputStream); | |
1464 | } else if (needVerticalMetrics && i == t42VheaTable) { | |
1465 | dumpString(vheaTab, length, outputFunc, outputStream); | |
1466 | } else if (needVerticalMetrics && i == t42VmtxTable) { | |
1467 | dumpString(vmtxTab, length, outputFunc, outputStream); | |
1468 | gfree(vmtxTab); | |
1469 | } | |
1470 | } | |
1471 | } | |
1472 | } | |
1473 | ||
1474 | // end the sfnts array | |
1475 | (*outputFunc)(outputStream, "] def\n", 6); | |
1476 | ||
1477 | gfree(locaData); | |
1478 | gfree(locaTable); | |
1479 | } | |
1480 | ||
1481 | void FoFiTrueType::dumpString(Guchar *s, int length, | |
1482 | FoFiOutputFunc outputFunc, | |
1483 | void *outputStream) { | |
1484 | char buf[64]; | |
1485 | int pad, i, j; | |
1486 | ||
1487 | (*outputFunc)(outputStream, "<", 1); | |
1488 | for (i = 0; i < length; i += 32) { | |
1489 | for (j = 0; j < 32 && i+j < length; ++j) { | |
1490 | sprintf(buf, "%02X", s[i+j] & 0xff); | |
1491 | (*outputFunc)(outputStream, buf, strlen(buf)); | |
1492 | } | |
1493 | if (i % (65536 - 32) == 65536 - 64) { | |
1494 | (*outputFunc)(outputStream, ">\n<", 3); | |
1495 | } else if (i+32 < length) { | |
1496 | (*outputFunc)(outputStream, "\n", 1); | |
1497 | } | |
1498 | } | |
1499 | if (length & 3) { | |
1500 | pad = 4 - (length & 3); | |
1501 | for (i = 0; i < pad; ++i) { | |
1502 | (*outputFunc)(outputStream, "00", 2); | |
1503 | } | |
1504 | } | |
1505 | // add an extra zero byte because the Adobe Type 42 spec says so | |
1506 | (*outputFunc)(outputStream, "00>\n", 4); | |
1507 | } | |
1508 | ||
1509 | Guint FoFiTrueType::computeTableChecksum(Guchar *data, int length) { | |
1510 | Guint checksum, word; | |
1511 | int i; | |
1512 | ||
1513 | checksum = 0; | |
1514 | for (i = 0; i+3 < length; i += 4) { | |
1515 | word = ((data[i ] & 0xff) << 24) + | |
1516 | ((data[i+1] & 0xff) << 16) + | |
1517 | ((data[i+2] & 0xff) << 8) + | |
1518 | (data[i+3] & 0xff); | |
1519 | checksum += word; | |
1520 | } | |
1521 | if (length & 3) { | |
1522 | word = 0; | |
1523 | i = length & ~3; | |
1524 | switch (length & 3) { | |
1525 | case 3: | |
1526 | word |= (data[i+2] & 0xff) << 8; | |
1527 | case 2: | |
1528 | word |= (data[i+1] & 0xff) << 16; | |
1529 | case 1: | |
1530 | word |= (data[i ] & 0xff) << 24; | |
1531 | break; | |
1532 | } | |
1533 | checksum += word; | |
1534 | } | |
1535 | return checksum; | |
1536 | } | |
1537 | ||
1538 | void FoFiTrueType::parse() { | |
1539 | Guint topTag; | |
1540 | int pos, i, j; | |
1541 | ||
1542 | parsedOk = gTrue; | |
1543 | ||
1544 | // look for a collection (TTC) | |
1545 | topTag = getU32BE(0, &parsedOk); | |
1546 | if (!parsedOk) { | |
1547 | return; | |
1548 | } | |
1549 | if (topTag == ttcfTag) { | |
1550 | pos = getU32BE(12, &parsedOk); | |
1551 | if (!parsedOk) { | |
1552 | return; | |
1553 | } | |
1554 | } else { | |
1555 | pos = 0; | |
1556 | } | |
1557 | ||
1558 | // read the table directory | |
1559 | nTables = getU16BE(pos + 4, &parsedOk); | |
1560 | if (!parsedOk) { | |
1561 | return; | |
1562 | } | |
1563 | tables = (TrueTypeTable *)gmallocn(nTables, sizeof(TrueTypeTable)); | |
1564 | pos += 12; | |
1565 | for (i = 0; i < nTables; ++i) { | |
1566 | tables[i].tag = getU32BE(pos, &parsedOk); | |
1567 | tables[i].checksum = getU32BE(pos + 4, &parsedOk); | |
1568 | tables[i].offset = (int)getU32BE(pos + 8, &parsedOk); | |
1569 | tables[i].len = (int)getU32BE(pos + 12, &parsedOk); | |
1570 | if (tables[i].offset + tables[i].len < tables[i].offset || | |
1571 | tables[i].offset + tables[i].len > len) { | |
1572 | parsedOk = gFalse; | |
1573 | } | |
1574 | pos += 16; | |
1575 | } | |
1576 | if (!parsedOk) { | |
1577 | return; | |
1578 | } | |
1579 | ||
1580 | // check for tables that are required by both the TrueType spec and | |
1581 | // the Type 42 spec | |
1582 | if (seekTable("head") < 0 || | |
1583 | seekTable("hhea") < 0 || | |
1584 | seekTable("loca") < 0 || | |
1585 | seekTable("maxp") < 0 || | |
1586 | seekTable("glyf") < 0 || | |
1587 | seekTable("hmtx") < 0) { | |
1588 | parsedOk = gFalse; | |
1589 | return; | |
1590 | } | |
1591 | ||
1592 | // read the cmaps | |
1593 | if ((i = seekTable("cmap")) >= 0) { | |
1594 | pos = tables[i].offset + 2; | |
1595 | nCmaps = getU16BE(pos, &parsedOk); | |
1596 | pos += 2; | |
1597 | if (!parsedOk) { | |
1598 | return; | |
1599 | } | |
1600 | cmaps = (TrueTypeCmap *)gmallocn(nCmaps, sizeof(TrueTypeCmap)); | |
1601 | for (j = 0; j < nCmaps; ++j) { | |
1602 | cmaps[j].platform = getU16BE(pos, &parsedOk); | |
1603 | cmaps[j].encoding = getU16BE(pos + 2, &parsedOk); | |
1604 | cmaps[j].offset = tables[i].offset + getU32BE(pos + 4, &parsedOk); | |
1605 | pos += 8; | |
1606 | cmaps[j].fmt = getU16BE(cmaps[j].offset, &parsedOk); | |
1607 | cmaps[j].len = getU16BE(cmaps[j].offset + 2, &parsedOk); | |
1608 | } | |
1609 | if (!parsedOk) { | |
1610 | return; | |
1611 | } | |
1612 | } else { | |
1613 | nCmaps = 0; | |
1614 | } | |
1615 | ||
1616 | // get the number of glyphs from the maxp table | |
1617 | i = seekTable("maxp"); | |
1618 | nGlyphs = getU16BE(tables[i].offset + 4, &parsedOk); | |
1619 | if (!parsedOk) { | |
1620 | return; | |
1621 | } | |
1622 | ||
1623 | // get the bbox and loca table format from the head table | |
1624 | i = seekTable("head"); | |
1625 | bbox[0] = getS16BE(tables[i].offset + 36, &parsedOk); | |
1626 | bbox[1] = getS16BE(tables[i].offset + 38, &parsedOk); | |
1627 | bbox[2] = getS16BE(tables[i].offset + 40, &parsedOk); | |
1628 | bbox[3] = getS16BE(tables[i].offset + 42, &parsedOk); | |
1629 | locaFmt = getS16BE(tables[i].offset + 50, &parsedOk); | |
1630 | if (!parsedOk) { | |
1631 | return; | |
1632 | } | |
1633 | ||
1634 | // make sure the loca table is sane (correct length and entries are | |
1635 | // in bounds) | |
1636 | i = seekTable("loca"); | |
1637 | if (tables[i].len < (nGlyphs + 1) * (locaFmt ? 4 : 2)) { | |
1638 | parsedOk = gFalse; | |
1639 | return; | |
1640 | } | |
1641 | for (j = 0; j <= nGlyphs; ++j) { | |
1642 | if (locaFmt) { | |
1643 | pos = (int)getU32BE(tables[i].offset + j*4, &parsedOk); | |
1644 | } else { | |
1645 | pos = getU16BE(tables[i].offset + j*2, &parsedOk); | |
1646 | } | |
1647 | if (pos < 0 || pos > len) { | |
1648 | parsedOk = gFalse; | |
1649 | } | |
1650 | } | |
1651 | if (!parsedOk) { | |
1652 | return; | |
1653 | } | |
1654 | ||
1655 | // read the post table | |
1656 | readPostTable(); | |
1657 | } | |
1658 | ||
1659 | void FoFiTrueType::readPostTable() { | |
1660 | GString *name; | |
1661 | int tablePos, postFmt, stringIdx, stringPos; | |
1662 | GBool ok; | |
1663 | int i, j, n, m; | |
1664 | ||
1665 | ok = gTrue; | |
1666 | if ((i = seekTable("post")) < 0) { | |
1667 | return; | |
1668 | } | |
1669 | tablePos = tables[i].offset; | |
1670 | postFmt = getU32BE(tablePos, &ok); | |
1671 | if (!ok) { | |
1672 | goto err; | |
1673 | } | |
1674 | if (postFmt == 0x00010000) { | |
1675 | nameToGID = new GHash(gTrue); | |
1676 | for (i = 0; i < 258; ++i) { | |
1677 | nameToGID->add(new GString(macGlyphNames[i]), i); | |
1678 | } | |
1679 | } else if (postFmt == 0x00020000) { | |
1680 | nameToGID = new GHash(gTrue); | |
1681 | n = getU16BE(tablePos + 32, &ok); | |
1682 | if (!ok) { | |
1683 | goto err; | |
1684 | } | |
1685 | if (n > nGlyphs) { | |
1686 | n = nGlyphs; | |
1687 | } | |
1688 | stringIdx = 0; | |
1689 | stringPos = tablePos + 34 + 2*n; | |
1690 | for (i = 0; i < n; ++i) { | |
1691 | j = getU16BE(tablePos + 34 + 2*i, &ok); | |
1692 | if (j < 258) { | |
1693 | nameToGID->removeInt(macGlyphNames[j]); | |
1694 | nameToGID->add(new GString(macGlyphNames[j]), i); | |
1695 | } else { | |
1696 | j -= 258; | |
1697 | if (j != stringIdx) { | |
1698 | for (stringIdx = 0, stringPos = tablePos + 34 + 2*n; | |
1699 | stringIdx < j; | |
1700 | ++stringIdx, stringPos += 1 + getU8(stringPos, &ok)) ; | |
1701 | if (!ok) { | |
1702 | goto err; | |
1703 | } | |
1704 | } | |
1705 | m = getU8(stringPos, &ok); | |
1706 | if (!ok || !checkRegion(stringPos + 1, m)) { | |
1707 | goto err; | |
1708 | } | |
1709 | name = new GString((char *)&file[stringPos + 1], m); | |
1710 | nameToGID->removeInt(name); | |
1711 | nameToGID->add(name, i); | |
1712 | ++stringIdx; | |
1713 | stringPos += 1 + m; | |
1714 | } | |
1715 | } | |
1716 | } else if (postFmt == 0x00028000) { | |
1717 | nameToGID = new GHash(gTrue); | |
1718 | for (i = 0; i < nGlyphs; ++i) { | |
1719 | j = getU8(tablePos + 32 + i, &ok); | |
1720 | if (!ok) { | |
1721 | goto err; | |
1722 | } | |
1723 | if (j < 258) { | |
1724 | nameToGID->removeInt(macGlyphNames[j]); | |
1725 | nameToGID->add(new GString(macGlyphNames[j]), i); | |
1726 | } | |
1727 | } | |
1728 | } | |
1729 | ||
1730 | return; | |
1731 | ||
1732 | err: | |
1733 | if (nameToGID) { | |
1734 | delete nameToGID; | |
1735 | nameToGID = NULL; | |
1736 | } | |
1737 | } | |
1738 | ||
1739 | int FoFiTrueType::seekTable(char *tag) { | |
1740 | Guint tagI; | |
1741 | int i; | |
1742 | ||
1743 | tagI = ((tag[0] & 0xff) << 24) | | |
1744 | ((tag[1] & 0xff) << 16) | | |
1745 | ((tag[2] & 0xff) << 8) | | |
1746 | (tag[3] & 0xff); | |
1747 | for (i = 0; i < nTables; ++i) { | |
1748 | if (tables[i].tag == tagI) { | |
1749 | return i; | |
1750 | } | |
1751 | } | |
1752 | return -1; | |
1753 | } |