Diffstat (limited to 'noncore/unsupported/qpdf/xpdf/GfxFont.cc') (more/less context) (show whitespace changes)
-rw-r--r-- | noncore/unsupported/qpdf/xpdf/GfxFont.cc | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/noncore/unsupported/qpdf/xpdf/GfxFont.cc b/noncore/unsupported/qpdf/xpdf/GfxFont.cc new file mode 100644 index 0000000..518f97b --- a/dev/null +++ b/noncore/unsupported/qpdf/xpdf/GfxFont.cc | |||
@@ -0,0 +1,1247 @@ | |||
1 | //======================================================================== | ||
2 | // | ||
3 | // GfxFont.cc | ||
4 | // | ||
5 | // Copyright 1996-2001 Derek B. Noonburg | ||
6 | // | ||
7 | //======================================================================== | ||
8 | |||
9 | #ifdef __GNUC__ | ||
10 | #pragma implementation | ||
11 | #endif | ||
12 | |||
13 | #include <aconf.h> | ||
14 | #include <stdio.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <string.h> | ||
17 | #include <ctype.h> | ||
18 | #include "gmem.h" | ||
19 | #include "Error.h" | ||
20 | #include "Object.h" | ||
21 | #include "Dict.h" | ||
22 | #include "GlobalParams.h" | ||
23 | #include "CMap.h" | ||
24 | #include "CharCodeToUnicode.h" | ||
25 | #include "FontEncodingTables.h" | ||
26 | #include "BuiltinFontTables.h" | ||
27 | #include "FontFile.h" | ||
28 | #include "GfxFont.h" | ||
29 | |||
30 | //------------------------------------------------------------------------ | ||
31 | |||
32 | struct StdFontMapEntry { | ||
33 | char *altName; | ||
34 | char *properName; | ||
35 | }; | ||
36 | |||
37 | static StdFontMapEntry stdFontMap[] = { | ||
38 | { "Arial", "Helvetica" }, | ||
39 | { "Arial,Bold", "Helvetica-Bold" }, | ||
40 | { "Arial,BoldItalic", "Helvetica-BoldOblique" }, | ||
41 | { "Arial,Italic", "Helvetica-Oblique" }, | ||
42 | { "Arial-Bold", "Helvetica-Bold" }, | ||
43 | { "Arial-BoldItalic", "Helvetica-BoldOblique" }, | ||
44 | { "Arial-BoldItalicMT", "Helvetica-BoldOblique" }, | ||
45 | { "Arial-BoldMT", "Helvetica-Bold" }, | ||
46 | { "Arial-Italic", "Helvetica-Oblique" }, | ||
47 | { "Arial-ItalicMT", "Helvetica-Oblique" }, | ||
48 | { "ArialMT", "Helvetica" }, | ||
49 | { "Courier,Bold", "Courier-Bold" }, | ||
50 | { "Courier,Italic", "Courier-Oblique" }, | ||
51 | { "Courier,BoldItalic", "Courier-BoldOblique" }, | ||
52 | { "CourierNew", "Courier" }, | ||
53 | { "CourierNew,Bold", "Courier-Bold" }, | ||
54 | { "CourierNew,BoldItalic", "Courier-BoldOblique" }, | ||
55 | { "CourierNew,Italic", "Courier-Oblique" }, | ||
56 | { "CourierNew-Bold", "Courier-Bold" }, | ||
57 | { "CourierNew-BoldItalic", "Courier-BoldOblique" }, | ||
58 | { "CourierNew-Italic", "Courier-Oblique" }, | ||
59 | { "CourierNewPS-BoldItalicMT", "Courier-BoldOblique" }, | ||
60 | { "CourierNewPS-BoldMT", "Courier-Bold" }, | ||
61 | { "CourierNewPS-ItalicMT", "Courier-Oblique" }, | ||
62 | { "CourierNewPSMT", "Courier" }, | ||
63 | { "Helvetica,Bold", "Helvetica-Bold" }, | ||
64 | { "Helvetica,BoldItalic", "Helvetica-BoldOblique" }, | ||
65 | { "Helvetica,Italic", "Helvetica-Oblique" }, | ||
66 | { "Helvetica-BoldItalic", "Helvetica-BoldOblique" }, | ||
67 | { "Helvetica-Italic", "Helvetica-Oblique" }, | ||
68 | { "TimesNewRoman", "Times-Roman" }, | ||
69 | { "TimesNewRoman,Bold", "Times-Bold" }, | ||
70 | { "TimesNewRoman,BoldItalic", "Times-BoldItalic" }, | ||
71 | { "TimesNewRoman,Italic", "Times-Italic" }, | ||
72 | { "TimesNewRoman-Bold", "Times-Bold" }, | ||
73 | { "TimesNewRoman-BoldItalic", "Times-BoldItalic" }, | ||
74 | { "TimesNewRoman-Italic", "Times-Italic" }, | ||
75 | { "TimesNewRomanPS", "Times-Roman" }, | ||
76 | { "TimesNewRomanPS-Bold", "Times-Bold" }, | ||
77 | { "TimesNewRomanPS-BoldItalic", "Times-BoldItalic" }, | ||
78 | { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" }, | ||
79 | { "TimesNewRomanPS-BoldMT", "Times-Bold" }, | ||
80 | { "TimesNewRomanPS-Italic", "Times-Italic" }, | ||
81 | { "TimesNewRomanPS-ItalicMT", "Times-Italic" }, | ||
82 | { "TimesNewRomanPSMT", "Times-Roman" } | ||
83 | }; | ||
84 | |||
85 | //------------------------------------------------------------------------ | ||
86 | // GfxFont | ||
87 | //------------------------------------------------------------------------ | ||
88 | |||
89 | GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { | ||
90 | GString *nameA; | ||
91 | GfxFont *font; | ||
92 | Object obj1; | ||
93 | |||
94 | // get base font name | ||
95 | nameA = NULL; | ||
96 | fontDict->lookup("BaseFont", &obj1); | ||
97 | if (obj1.isName()) { | ||
98 | nameA = new GString(obj1.getName()); | ||
99 | } | ||
100 | obj1.free(); | ||
101 | |||
102 | // get font type | ||
103 | font = NULL; | ||
104 | fontDict->lookup("Subtype", &obj1); | ||
105 | if (obj1.isName("Type1") || obj1.isName("MMType1")) { | ||
106 | font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict); | ||
107 | } else if (obj1.isName("Type1C")) { | ||
108 | font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict); | ||
109 | } else if (obj1.isName("Type3")) { | ||
110 | font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict); | ||
111 | } else if (obj1.isName("TrueType")) { | ||
112 | font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict); | ||
113 | } else if (obj1.isName("Type0")) { | ||
114 | font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict); | ||
115 | } else { | ||
116 | error(-1, "Unknown font type: '%s'", | ||
117 | obj1.isName() ? obj1.getName() : "???"); | ||
118 | font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict); | ||
119 | } | ||
120 | obj1.free(); | ||
121 | |||
122 | return font; | ||
123 | } | ||
124 | |||
125 | GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) { | ||
126 | ok = gFalse; | ||
127 | tag = new GString(tagA); | ||
128 | id = idA; | ||
129 | name = nameA; | ||
130 | embFontName = NULL; | ||
131 | extFontFile = NULL; | ||
132 | } | ||
133 | |||
134 | GfxFont::~GfxFont() { | ||
135 | delete tag; | ||
136 | if (name) { | ||
137 | delete name; | ||
138 | } | ||
139 | if (embFontName) { | ||
140 | delete embFontName; | ||
141 | } | ||
142 | if (extFontFile) { | ||
143 | delete extFontFile; | ||
144 | } | ||
145 | } | ||
146 | |||
147 | void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { | ||
148 | Object obj1, obj2, obj3, obj4; | ||
149 | fouble t; | ||
150 | int i; | ||
151 | |||
152 | // assume Times-Roman by default (for substitution purposes) | ||
153 | flags = fontSerif; | ||
154 | |||
155 | embFontID.num = -1; | ||
156 | embFontID.gen = -1; | ||
157 | missingWidth = 0; | ||
158 | |||
159 | if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) { | ||
160 | |||
161 | // get flags | ||
162 | if (obj1.dictLookup("Flags", &obj2)->isInt()) { | ||
163 | flags = obj2.getInt(); | ||
164 | } | ||
165 | obj2.free(); | ||
166 | |||
167 | // get name | ||
168 | obj1.dictLookup("FontName", &obj2); | ||
169 | if (obj2.isName()) { | ||
170 | embFontName = new GString(obj2.getName()); | ||
171 | } | ||
172 | obj2.free(); | ||
173 | |||
174 | // look for embedded font file | ||
175 | if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) { | ||
176 | if (type == fontType1) { | ||
177 | embFontID = obj2.getRef(); | ||
178 | } else { | ||
179 | error(-1, "Mismatch between font type and embedded font file"); | ||
180 | } | ||
181 | } | ||
182 | obj2.free(); | ||
183 | if (embFontID.num == -1 && | ||
184 | obj1.dictLookupNF("FontFile2", &obj2)->isRef()) { | ||
185 | if (type == fontTrueType || type == fontCIDType2) { | ||
186 | embFontID = obj2.getRef(); | ||
187 | } else { | ||
188 | error(-1, "Mismatch between font type and embedded font file"); | ||
189 | } | ||
190 | } | ||
191 | obj2.free(); | ||
192 | if (embFontID.num == -1 && | ||
193 | obj1.dictLookupNF("FontFile3", &obj2)->isRef()) { | ||
194 | if (obj2.fetch(xref, &obj3)->isStream()) { | ||
195 | obj3.streamGetDict()->lookup("Subtype", &obj4); | ||
196 | if (obj4.isName("Type1")) { | ||
197 | if (type == fontType1) { | ||
198 | embFontID = obj2.getRef(); | ||
199 | } else { | ||
200 | error(-1, "Mismatch between font type and embedded font file"); | ||
201 | } | ||
202 | } else if (obj4.isName("Type1C")) { | ||
203 | if (type == fontType1) { | ||
204 | type = fontType1C; | ||
205 | embFontID = obj2.getRef(); | ||
206 | } else if (type == fontType1C) { | ||
207 | embFontID = obj2.getRef(); | ||
208 | } else { | ||
209 | error(-1, "Mismatch between font type and embedded font file"); | ||
210 | } | ||
211 | } else if (obj4.isName("TrueType")) { | ||
212 | if (type == fontTrueType) { | ||
213 | embFontID = obj2.getRef(); | ||
214 | } else { | ||
215 | error(-1, "Mismatch between font type and embedded font file"); | ||
216 | } | ||
217 | } else if (obj4.isName("CIDFontType0C")) { | ||
218 | if (type == fontCIDType0) { | ||
219 | type = fontCIDType0C; | ||
220 | embFontID = obj2.getRef(); | ||
221 | } else { | ||
222 | error(-1, "Mismatch between font type and embedded font file"); | ||
223 | } | ||
224 | } else { | ||
225 | error(-1, "Unknown embedded font type '%s'", | ||
226 | obj4.isName() ? obj4.getName() : "???"); | ||
227 | } | ||
228 | obj4.free(); | ||
229 | } | ||
230 | obj3.free(); | ||
231 | } | ||
232 | obj2.free(); | ||
233 | |||
234 | // look for MissingWidth | ||
235 | obj1.dictLookup("MissingWidth", &obj2); | ||
236 | if (obj2.isNum()) { | ||
237 | missingWidth = obj2.getNum(); | ||
238 | } | ||
239 | obj2.free(); | ||
240 | |||
241 | // get Ascent and Descent | ||
242 | obj1.dictLookup("Ascent", &obj2); | ||
243 | if (obj2.isNum()) { | ||
244 | t = 0.001 * obj2.getNum(); | ||
245 | // some broken font descriptors set ascent and descent to 0 | ||
246 | if (t != 0) { | ||
247 | ascent = t; | ||
248 | } | ||
249 | } | ||
250 | obj2.free(); | ||
251 | obj1.dictLookup("Descent", &obj2); | ||
252 | if (obj2.isNum()) { | ||
253 | t = 0.001 * obj2.getNum(); | ||
254 | // some broken font descriptors set ascent and descent to 0 | ||
255 | if (t != 0) { | ||
256 | descent = t; | ||
257 | } | ||
258 | } | ||
259 | obj2.free(); | ||
260 | |||
261 | // font FontBBox | ||
262 | if (obj1.dictLookup("FontBBox", &obj2)->isArray()) { | ||
263 | for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) { | ||
264 | if (obj2.arrayGet(i, &obj3)->isNum()) { | ||
265 | fontBBox[i] = 0.001 * obj3.getNum(); | ||
266 | } | ||
267 | obj3.free(); | ||
268 | } | ||
269 | } | ||
270 | obj2.free(); | ||
271 | |||
272 | } | ||
273 | obj1.free(); | ||
274 | } | ||
275 | |||
276 | CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) { | ||
277 | CharCodeToUnicode *ctu; | ||
278 | GString *buf; | ||
279 | Object obj1; | ||
280 | int c; | ||
281 | |||
282 | if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) { | ||
283 | obj1.free(); | ||
284 | return NULL; | ||
285 | } | ||
286 | buf = new GString(); | ||
287 | obj1.streamReset(); | ||
288 | while ((c = obj1.streamGetChar()) != EOF) { | ||
289 | buf->append(c); | ||
290 | } | ||
291 | obj1.streamClose(); | ||
292 | obj1.free(); | ||
293 | ctu = CharCodeToUnicode::parseCMap(buf, nBits); | ||
294 | delete buf; | ||
295 | return ctu; | ||
296 | } | ||
297 | |||
298 | void GfxFont::findExtFontFile() { | ||
299 | if (name) { | ||
300 | if (type == fontType1) { | ||
301 | extFontFile = globalParams->findFontFile(name, ".pfa", ".pfb"); | ||
302 | } else if (type == fontTrueType) { | ||
303 | extFontFile = globalParams->findFontFile(name, ".ttf", NULL); | ||
304 | } | ||
305 | } | ||
306 | } | ||
307 | |||
308 | char *GfxFont::readExtFontFile(int *len) { | ||
309 | FILE *f; | ||
310 | char *buf; | ||
311 | |||
312 | if (!(f = fopen(extFontFile->getCString(), "rb"))) { | ||
313 | error(-1, "External font file '%s' vanished", extFontFile->getCString()); | ||
314 | return NULL; | ||
315 | } | ||
316 | fseek(f, 0, SEEK_END); | ||
317 | *len = (int)ftell(f); | ||
318 | fseek(f, 0, SEEK_SET); | ||
319 | buf = (char *)gmalloc(*len); | ||
320 | if ((int)fread(buf, 1, *len, f) != *len) { | ||
321 | error(-1, "Error reading external font file '%s'", extFontFile); | ||
322 | } | ||
323 | fclose(f); | ||
324 | return buf; | ||
325 | } | ||
326 | |||
327 | char *GfxFont::readEmbFontFile(XRef *xref, int *len) { | ||
328 | char *buf; | ||
329 | Object obj1, obj2; | ||
330 | Stream *str; | ||
331 | int c; | ||
332 | int size, i; | ||
333 | |||
334 | obj1.initRef(embFontID.num, embFontID.gen); | ||
335 | obj1.fetch(xref, &obj2); | ||
336 | if (!obj2.isStream()) { | ||
337 | error(-1, "Embedded font file is not a stream"); | ||
338 | obj2.free(); | ||
339 | obj1.free(); | ||
340 | embFontID.num = -1; | ||
341 | return NULL; | ||
342 | } | ||
343 | str = obj2.getStream(); | ||
344 | |||
345 | buf = NULL; | ||
346 | i = size = 0; | ||
347 | str->reset(); | ||
348 | while ((c = str->getChar()) != EOF) { | ||
349 | if (i == size) { | ||
350 | size += 4096; | ||
351 | buf = (char *)grealloc(buf, size); | ||
352 | } | ||
353 | buf[i++] = c; | ||
354 | } | ||
355 | *len = i; | ||
356 | str->close(); | ||
357 | |||
358 | obj2.free(); | ||
359 | obj1.free(); | ||
360 | |||
361 | return buf; | ||
362 | } | ||
363 | |||
364 | //------------------------------------------------------------------------ | ||
365 | // Gfx8BitFont | ||
366 | //------------------------------------------------------------------------ | ||
367 | |||
368 | Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, | ||
369 | GfxFontType typeA, Dict *fontDict): | ||
370 | GfxFont(tagA, idA, nameA) | ||
371 | { | ||
372 | BuiltinFont *builtinFont; | ||
373 | char **baseEnc; | ||
374 | GBool baseEncFromFontFile; | ||
375 | char *buf; | ||
376 | int len; | ||
377 | FontFile *fontFile; | ||
378 | int code, code2; | ||
379 | char *charName; | ||
380 | GBool missing, hex; | ||
381 | Unicode toUnicode[256]; | ||
382 | fouble mul; | ||
383 | int firstChar, lastChar; | ||
384 | Gushort w; | ||
385 | Object obj1, obj2, obj3; | ||
386 | int n, i, a, b, m; | ||
387 | |||
388 | type = typeA; | ||
389 | ctu = NULL; | ||
390 | |||
391 | // Acrobat 4.0 and earlier substituted Base14-compatible fonts | ||
392 | // without providing Widths and a FontDescriptor, so we munge the | ||
393 | // names into the proper Base14 names. (This table is from | ||
394 | // implementation note 44 in the PDF 1.4 spec.) | ||
395 | if (name) { | ||
396 | a = 0; | ||
397 | b = sizeof(stdFontMap) / sizeof(StdFontMapEntry); | ||
398 | // invariant: stdFontMap[a].altName <= name < stdFontMap[b].altName | ||
399 | while (b - a > 1) { | ||
400 | m = (a + b) / 2; | ||
401 | if (name->cmp(stdFontMap[m].altName) >= 0) { | ||
402 | a = m; | ||
403 | } else { | ||
404 | b = m; | ||
405 | } | ||
406 | } | ||
407 | if (!name->cmp(stdFontMap[a].altName)) { | ||
408 | delete name; | ||
409 | name = new GString(stdFontMap[a].properName); | ||
410 | } | ||
411 | } | ||
412 | |||
413 | // is it a built-in font? | ||
414 | builtinFont = NULL; | ||
415 | if (name) { | ||
416 | for (i = 0; i < nBuiltinFonts; ++i) { | ||
417 | if (!name->cmp(builtinFonts[i].name)) { | ||
418 | builtinFont = &builtinFonts[i]; | ||
419 | break; | ||
420 | } | ||
421 | } | ||
422 | } | ||
423 | |||
424 | // default ascent/descent values | ||
425 | if (builtinFont) { | ||
426 | ascent = 0.001 * builtinFont->ascent; | ||
427 | descent = 0.001 * builtinFont->descent; | ||
428 | fontBBox[0] = 0.001 * builtinFont->bbox[0]; | ||
429 | fontBBox[1] = 0.001 * builtinFont->bbox[1]; | ||
430 | fontBBox[2] = 0.001 * builtinFont->bbox[2]; | ||
431 | fontBBox[3] = 0.001 * builtinFont->bbox[3]; | ||
432 | } else { | ||
433 | ascent = 0.95; | ||
434 | descent = -0.35; | ||
435 | fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; | ||
436 | } | ||
437 | |||
438 | // get info from font descriptor | ||
439 | readFontDescriptor(xref, fontDict); | ||
440 | |||
441 | // look for an external font file | ||
442 | findExtFontFile(); | ||
443 | |||
444 | // get font matrix | ||
445 | fontMat[0] = fontMat[3] = 1; | ||
446 | fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; | ||
447 | if (fontDict->lookup("FontMatrix", &obj1)->isArray()) { | ||
448 | for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) { | ||
449 | if (obj1.arrayGet(i, &obj2)->isNum()) { | ||
450 | fontMat[i] = obj2.getNum(); | ||
451 | } | ||
452 | obj2.free(); | ||
453 | } | ||
454 | } | ||
455 | obj1.free(); | ||
456 | |||
457 | // get Type3 font definition | ||
458 | if (type == fontType3) { | ||
459 | fontDict->lookup("CharProcs", &charProcs); | ||
460 | if (!charProcs.isDict()) { | ||
461 | error(-1, "Missing or invalid CharProcs dictionary in Type 3 font"); | ||
462 | charProcs.free(); | ||
463 | } | ||
464 | } | ||
465 | |||
466 | //----- build the font encoding ----- | ||
467 | |||
468 | // Encodings start with a base encoding, which can come from | ||
469 | // (in order of priority): | ||
470 | // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding | ||
471 | // - MacRoman / MacExpert / WinAnsi / Standard | ||
472 | // 2. embedded or external font file | ||
473 | // 3. default: | ||
474 | // - builtin --> builtin encoding | ||
475 | // - TrueType --> MacRomanEncoding | ||
476 | // - others --> StandardEncoding | ||
477 | // and then add a list of differences (if any) from | ||
478 | // FontDict.Encoding.Differences. | ||
479 | |||
480 | // check FontDict for base encoding | ||
481 | hasEncoding = gFalse; | ||
482 | baseEnc = NULL; | ||
483 | baseEncFromFontFile = gFalse; | ||
484 | fontDict->lookup("Encoding", &obj1); | ||
485 | if (obj1.isDict()) { | ||
486 | obj1.dictLookup("BaseEncoding", &obj2); | ||
487 | if (obj2.isName("MacRomanEncoding")) { | ||
488 | hasEncoding = gTrue; | ||
489 | baseEnc = macRomanEncoding; | ||
490 | } else if (obj2.isName("MacExpertEncoding")) { | ||
491 | hasEncoding = gTrue; | ||
492 | baseEnc = macExpertEncoding; | ||
493 | } else if (obj2.isName("WinAnsiEncoding")) { | ||
494 | hasEncoding = gTrue; | ||
495 | baseEnc = winAnsiEncoding; | ||
496 | } else if (obj2.isName("StandardEncoding")) { | ||
497 | hasEncoding = gTrue; | ||
498 | baseEnc = standardEncoding; | ||
499 | } | ||
500 | obj2.free(); | ||
501 | } else if (obj1.isName("MacRomanEncoding")) { | ||
502 | hasEncoding = gTrue; | ||
503 | baseEnc = macRomanEncoding; | ||
504 | } else if (obj1.isName("MacExpertEncoding")) { | ||
505 | hasEncoding = gTrue; | ||
506 | baseEnc = macExpertEncoding; | ||
507 | } else if (obj1.isName("WinAnsiEncoding")) { | ||
508 | hasEncoding = gTrue; | ||
509 | baseEnc = winAnsiEncoding; | ||
510 | } else if (obj1.isName("StandardEncoding")) { | ||
511 | hasEncoding = gTrue; | ||
512 | baseEnc = standardEncoding; | ||
513 | } | ||
514 | |||
515 | // check embedded or external font file for base encoding | ||
516 | fontFile = NULL; | ||
517 | buf = NULL; | ||
518 | if ((type == fontType1 || type == fontType1C || type == fontTrueType) && | ||
519 | (extFontFile || embFontID.num >= 0)) { | ||
520 | if (extFontFile) { | ||
521 | buf = readExtFontFile(&len); | ||
522 | } else { | ||
523 | buf = readEmbFontFile(xref, &len); | ||
524 | } | ||
525 | if (buf) { | ||
526 | #if 0 | ||
527 | if (type == fontType1) { | ||
528 | fontFile = new Type1FontFile(buf, len); | ||
529 | } else if (type == fontType1C) { | ||
530 | fontFile = new Type1CFontFile(buf, len); | ||
531 | } else { | ||
532 | fontFile = new TrueTypeFontFile(buf, len); | ||
533 | } | ||
534 | if (fontFile->getName()) { | ||
535 | if (embFontName) { | ||
536 | delete embFontName; | ||
537 | } | ||
538 | embFontName = new GString(fontFile->getName()); | ||
539 | } | ||
540 | if (!baseEnc) { | ||
541 | baseEnc = fontFile->getEncoding(); | ||
542 | baseEncFromFontFile = gTrue; | ||
543 | } | ||
544 | #endif | ||
545 | gfree(buf); | ||
546 | } | ||
547 | } | ||
548 | |||
549 | // get default base encoding | ||
550 | if (!baseEnc) { | ||
551 | if (builtinFont) { | ||
552 | baseEnc = builtinFont->defaultBaseEnc; | ||
553 | } else if (type == fontTrueType) { | ||
554 | baseEnc = macRomanEncoding; | ||
555 | } else { | ||
556 | baseEnc = standardEncoding; | ||
557 | } | ||
558 | } | ||
559 | |||
560 | // copy the base encoding | ||
561 | for (i = 0; i < 256; ++i) { | ||
562 | enc[i] = baseEnc[i]; | ||
563 | if ((encFree[i] = baseEncFromFontFile) && enc[i]) { | ||
564 | enc[i] = copyString(baseEnc[i]); | ||
565 | } | ||
566 | } | ||
567 | |||
568 | // merge differences into encoding | ||
569 | if (obj1.isDict()) { | ||
570 | obj1.dictLookup("Differences", &obj2); | ||
571 | if (obj2.isArray()) { | ||
572 | code = 0; | ||
573 | for (i = 0; i < obj2.arrayGetLength(); ++i) { | ||
574 | obj2.arrayGet(i, &obj3); | ||
575 | if (obj3.isInt()) { | ||
576 | code = obj3.getInt(); | ||
577 | } else if (obj3.isName()) { | ||
578 | if (code < 256) { | ||
579 | if (encFree[code]) { | ||
580 | gfree(enc[code]); | ||
581 | } | ||
582 | enc[code] = copyString(obj3.getName()); | ||
583 | encFree[code] = gTrue; | ||
584 | } | ||
585 | ++code; | ||
586 | } else { | ||
587 | error(-1, "Wrong type in font encoding resource differences (%s)", | ||
588 | obj3.getTypeName()); | ||
589 | } | ||
590 | obj3.free(); | ||
591 | } | ||
592 | } | ||
593 | obj2.free(); | ||
594 | } | ||
595 | obj1.free(); | ||
596 | if (fontFile) { | ||
597 | delete fontFile; | ||
598 | } | ||
599 | |||
600 | //----- build the mapping to Unicode ----- | ||
601 | |||
602 | // look for a ToUnicode CMap | ||
603 | if (!(ctu = readToUnicodeCMap(fontDict, 8))) { | ||
604 | |||
605 | // no ToUnicode CMap, so use the char names | ||
606 | |||
607 | // pass 1: use the name-to-Unicode mapping table | ||
608 | missing = hex = gFalse; | ||
609 | for (code = 0; code < 256; ++code) { | ||
610 | if ((charName = enc[code])) { | ||
611 | if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) && | ||
612 | strcmp(charName, ".notdef")) { | ||
613 | // if it wasn't in the name-to-Unicode table, check for a | ||
614 | // name that looks like 'Axx' or 'xx', where 'A' is any letter | ||
615 | // and 'xx' is two hex digits | ||
616 | if ((strlen(charName) == 3 && | ||
617 | isalpha(charName[0]) && | ||
618 | isxdigit(charName[1]) && isxdigit(charName[2]) && | ||
619 | ((charName[1] >= 'a' && charName[1] <= 'f') || | ||
620 | (charName[1] >= 'A' && charName[1] <= 'F') || | ||
621 | (charName[2] >= 'a' && charName[2] <= 'f') || | ||
622 | (charName[2] >= 'A' && charName[2] <= 'F'))) || | ||
623 | (strlen(charName) == 2 && | ||
624 | isxdigit(charName[0]) && isxdigit(charName[1]) && | ||
625 | ((charName[0] >= 'a' && charName[0] <= 'f') || | ||
626 | (charName[0] >= 'A' && charName[0] <= 'F') || | ||
627 | (charName[1] >= 'a' && charName[1] <= 'f') || | ||
628 | (charName[1] >= 'A' && charName[1] <= 'F')))) { | ||
629 | hex = gTrue; | ||
630 | } | ||
631 | missing = gTrue; | ||
632 | } | ||
633 | } else { | ||
634 | toUnicode[code] = 0; | ||
635 | } | ||
636 | } | ||
637 | |||
638 | // pass 2: try to fill in the missing chars, looking for names of | ||
639 | // the form 'Axx', 'xx', 'Ann', or 'nn', where 'A' is any letter, | ||
640 | // 'xx' is two hex digits, and 'nn' is 2-4 decimal digits | ||
641 | if (missing && globalParams->getMapNumericCharNames()) { | ||
642 | for (code = 0; code < 256; ++code) { | ||
643 | if ((charName = enc[code]) && !toUnicode[code] && | ||
644 | strcmp(charName, ".notdef")) { | ||
645 | n = strlen(charName); | ||
646 | code2 = -1; | ||
647 | if (hex && n == 3 && isalpha(charName[0]) && | ||
648 | isxdigit(charName[1]) && isxdigit(charName[2])) { | ||
649 | sscanf(charName+1, "%x", &code2); | ||
650 | } else if (hex && n == 2 && | ||
651 | isxdigit(charName[0]) && isxdigit(charName[1])) { | ||
652 | sscanf(charName, "%x", &code2); | ||
653 | } else if (!hex && n >= 2 && n <= 4 && | ||
654 | isdigit(charName[0]) && isdigit(charName[1])) { | ||
655 | code2 = atoi(charName); | ||
656 | } else if (n >= 3 && n <= 5 && | ||
657 | isdigit(charName[1]) && isdigit(charName[2])) { | ||
658 | code2 = atoi(charName+1); | ||
659 | } | ||
660 | if (code2 >= 0 && code2 <= 0xff) { | ||
661 | toUnicode[code] = (Unicode)code2; | ||
662 | } | ||
663 | } | ||
664 | } | ||
665 | } | ||
666 | |||
667 | ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode); | ||
668 | } | ||
669 | |||
670 | //----- get the character widths ----- | ||
671 | |||
672 | // initialize all widths | ||
673 | for (code = 0; code < 256; ++code) { | ||
674 | widths[code] = missingWidth * 0.001; | ||
675 | } | ||
676 | |||
677 | // use widths from font dict, if present | ||
678 | fontDict->lookup("FirstChar", &obj1); | ||
679 | firstChar = obj1.isInt() ? obj1.getInt() : 0; | ||
680 | obj1.free(); | ||
681 | fontDict->lookup("LastChar", &obj1); | ||
682 | lastChar = obj1.isInt() ? obj1.getInt() : 255; | ||
683 | obj1.free(); | ||
684 | mul = (type == fontType3) ? fontMat[0] : fouble(0.001); | ||
685 | fontDict->lookup("Widths", &obj1); | ||
686 | if (obj1.isArray()) { | ||
687 | for (code = firstChar; code <= lastChar; ++code) { | ||
688 | obj1.arrayGet(code - firstChar, &obj2); | ||
689 | if (obj2.isNum()) { | ||
690 | widths[code] = obj2.getNum() * mul; | ||
691 | } | ||
692 | obj2.free(); | ||
693 | } | ||
694 | |||
695 | // use widths from built-in font | ||
696 | } else if (builtinFont) { | ||
697 | // this is a kludge for broken PDF files that encode char 32 | ||
698 | // as .notdef | ||
699 | if (builtinFont->widths->getWidth("space", &w)) { | ||
700 | widths[32] = 0.001 * w; | ||
701 | } | ||
702 | for (code = 0; code < 256; ++code) { | ||
703 | if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) { | ||
704 | widths[code] = 0.001 * w; | ||
705 | } | ||
706 | } | ||
707 | |||
708 | // couldn't find widths -- use defaults | ||
709 | } else { | ||
710 | // this is technically an error -- the Widths entry is required | ||
711 | // for all but the Base-14 fonts -- but certain PDF generators | ||
712 | // apparently don't include widths for Arial and TimesNewRoman | ||
713 | if (isFixedWidth()) { | ||
714 | i = 0; | ||
715 | } else if (isSerif()) { | ||
716 | i = 8; | ||
717 | } else { | ||
718 | i = 4; | ||
719 | } | ||
720 | if (isBold()) { | ||
721 | i += 2; | ||
722 | } | ||
723 | if (isItalic()) { | ||
724 | i += 1; | ||
725 | } | ||
726 | builtinFont = builtinFontSubst[i]; | ||
727 | // this is a kludge for broken PDF files that encode char 32 | ||
728 | // as .notdef | ||
729 | if (builtinFont->widths->getWidth("space", &w)) { | ||
730 | widths[32] = 0.001 * w; | ||
731 | } | ||
732 | for (code = 0; code < 256; ++code) { | ||
733 | if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) { | ||
734 | widths[code] = 0.001 * w; | ||
735 | } | ||
736 | } | ||
737 | } | ||
738 | obj1.free(); | ||
739 | |||
740 | ok = gTrue; | ||
741 | } | ||
742 | |||
743 | Gfx8BitFont::~Gfx8BitFont() { | ||
744 | int i; | ||
745 | |||
746 | for (i = 0; i < 256; ++i) { | ||
747 | if (encFree[i] && enc[i]) { | ||
748 | gfree(enc[i]); | ||
749 | } | ||
750 | } | ||
751 | ctu->decRefCnt(); | ||
752 | if (charProcs.isDict()) { | ||
753 | charProcs.free(); | ||
754 | } | ||
755 | } | ||
756 | |||
757 | int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code, | ||
758 | Unicode *u, int uSize, int *uLen, | ||
759 | fouble *dx, fouble *dy, fouble *ox, fouble *oy) { | ||
760 | CharCode c; | ||
761 | |||
762 | *code = c = (CharCode)(*s & 0xff); | ||
763 | *uLen = ctu->mapToUnicode(c, u, uSize); | ||
764 | *dx = widths[c]; | ||
765 | *dy = *ox = *oy = 0; | ||
766 | return 1; | ||
767 | } | ||
768 | |||
769 | CharCodeToUnicode *Gfx8BitFont::getToUnicode() { | ||
770 | ctu->incRefCnt(); | ||
771 | return ctu; | ||
772 | } | ||
773 | |||
774 | Object *Gfx8BitFont::getCharProc(int code, Object *proc) { | ||
775 | if (charProcs.isDict()) { | ||
776 | charProcs.dictLookup(enc[code], proc); | ||
777 | } else { | ||
778 | proc->initNull(); | ||
779 | } | ||
780 | return proc; | ||
781 | } | ||
782 | |||
783 | //------------------------------------------------------------------------ | ||
784 | // GfxCIDFont | ||
785 | //------------------------------------------------------------------------ | ||
786 | |||
787 | static int cmpWidthExcep(const void *w1, const void *w2) { | ||
788 | return ((GfxFontCIDWidthExcep *)w1)->first - | ||
789 | ((GfxFontCIDWidthExcep *)w2)->first; | ||
790 | } | ||
791 | |||
792 | static int cmpWidthExcepV(const void *w1, const void *w2) { | ||
793 | return ((GfxFontCIDWidthExcepV *)w1)->first - | ||
794 | ((GfxFontCIDWidthExcepV *)w2)->first; | ||
795 | } | ||
796 | |||
797 | GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, | ||
798 | Dict *fontDict): | ||
799 | GfxFont(tagA, idA, nameA) | ||
800 | { | ||
801 | Dict *desFontDict; | ||
802 | GString *collection, *cMapName; | ||
803 | Object desFontDictObj; | ||
804 | Object obj1, obj2, obj3, obj4, obj5, obj6; | ||
805 | int c1, c2; | ||
806 | int excepsSize, i, j, k; | ||
807 | |||
808 | ascent = 0.95; | ||
809 | descent = -0.35; | ||
810 | fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; | ||
811 | cMap = NULL; | ||
812 | ctu = NULL; | ||
813 | widths.defWidth = 1.0; | ||
814 | widths.defHeight = -1.0; | ||
815 | widths.defVY = 0.880; | ||
816 | widths.exceps = NULL; | ||
817 | widths.nExceps = 0; | ||
818 | widths.excepsV = NULL; | ||
819 | widths.nExcepsV = 0; | ||
820 | cidToGID = NULL; | ||
821 | cidToGIDLen = 0; | ||
822 | |||
823 | // get the descendant font | ||
824 | if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) { | ||
825 | error(-1, "Missing DescendantFonts entry in Type 0 font"); | ||
826 | obj1.free(); | ||
827 | goto err1; | ||
828 | } | ||
829 | if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) { | ||
830 | error(-1, "Bad descendant font in Type 0 font"); | ||
831 | goto err3; | ||
832 | } | ||
833 | obj1.free(); | ||
834 | desFontDict = desFontDictObj.getDict(); | ||
835 | |||
836 | // font type | ||
837 | if (!desFontDict->lookup("Subtype", &obj1)) { | ||
838 | error(-1, "Missing Subtype entry in Type 0 descendant font"); | ||
839 | goto err3; | ||
840 | } | ||
841 | if (obj1.isName("CIDFontType0")) { | ||
842 | type = fontCIDType0; | ||
843 | } else if (obj1.isName("CIDFontType2")) { | ||
844 | type = fontCIDType2; | ||
845 | } else { | ||
846 | error(-1, "Unknown Type 0 descendant font type '%s'", | ||
847 | obj1.isName() ? obj1.getName() : "???"); | ||
848 | goto err3; | ||
849 | } | ||
850 | obj1.free(); | ||
851 | |||
852 | // get info from font descriptor | ||
853 | readFontDescriptor(xref, desFontDict); | ||
854 | |||
855 | // look for an external font file | ||
856 | findExtFontFile(); | ||
857 | |||
858 | //----- encoding info ----- | ||
859 | |||
860 | // char collection | ||
861 | if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) { | ||
862 | error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font"); | ||
863 | goto err3; | ||
864 | } | ||
865 | obj1.dictLookup("Registry", &obj2); | ||
866 | obj1.dictLookup("Ordering", &obj3); | ||
867 | if (!obj2.isString() || !obj3.isString()) { | ||
868 | error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); | ||
869 | goto err4; | ||
870 | } | ||
871 | collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); | ||
872 | obj3.free(); | ||
873 | obj2.free(); | ||
874 | obj1.free(); | ||
875 | |||
876 | // look for a ToUnicode CMap | ||
877 | if (!(ctu = readToUnicodeCMap(fontDict, 16))) { | ||
878 | |||
879 | // the "Adobe-Identity" and "Adobe-UCS" collections don't have | ||
880 | // cidToUnicode files | ||
881 | if (collection->cmp("Adobe-Identity") && | ||
882 | collection->cmp("Adobe-UCS")) { | ||
883 | |||
884 | // look for a user-supplied .cidToUnicode file | ||
885 | if (!(ctu = globalParams->getCIDToUnicode(collection))) { | ||
886 | error(-1, "Unknown character collection '%s'", | ||
887 | collection->getCString()); | ||
888 | delete collection; | ||
889 | goto err2; | ||
890 | } | ||
891 | } | ||
892 | } | ||
893 | |||
894 | // encoding (i.e., CMap) | ||
895 | //~ need to handle a CMap stream here | ||
896 | //~ also need to deal with the UseCMap entry in the stream dict | ||
897 | if (!fontDict->lookup("Encoding", &obj1)->isName()) { | ||
898 | error(-1, "Missing or invalid Encoding entry in Type 0 font"); | ||
899 | delete collection; | ||
900 | goto err3; | ||
901 | } | ||
902 | cMapName = new GString(obj1.getName()); | ||
903 | obj1.free(); | ||
904 | if (!(cMap = globalParams->getCMap(collection, cMapName))) { | ||
905 | error(-1, "Unknown CMap '%s' for character collection '%s'", | ||
906 | cMapName->getCString(), collection->getCString()); | ||
907 | delete collection; | ||
908 | delete cMapName; | ||
909 | goto err2; | ||
910 | } | ||
911 | delete collection; | ||
912 | delete cMapName; | ||
913 | |||
914 | // CIDToGIDMap (for embedded TrueType fonts) | ||
915 | if (type == fontCIDType2) { | ||
916 | fontDict->lookup("CIDToGIDMap", &obj1); | ||
917 | if (obj1.isStream()) { | ||
918 | cidToGIDLen = 0; | ||
919 | i = 64; | ||
920 | cidToGID = (Gushort *)gmalloc(i * sizeof(Gushort)); | ||
921 | obj1.streamReset(); | ||
922 | while ((c1 = obj1.streamGetChar()) != EOF && | ||
923 | (c2 = obj1.streamGetChar()) != EOF) { | ||
924 | if (cidToGIDLen == i) { | ||
925 | i *= 2; | ||
926 | cidToGID = (Gushort *)grealloc(cidToGID, i * sizeof(Gushort)); | ||
927 | } | ||
928 | cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2); | ||
929 | } | ||
930 | } else if (!obj1.isName("Identity") && !obj1.isNull()) { | ||
931 | error(-1, "Invalid CIDToGIDMap entry in CID font"); | ||
932 | } | ||
933 | obj1.free(); | ||
934 | } | ||
935 | |||
936 | //----- character metrics ----- | ||
937 | |||
938 | // default char width | ||
939 | if (desFontDict->lookup("DW", &obj1)->isInt()) { | ||
940 | widths.defWidth = obj1.getInt() * 0.001; | ||
941 | } | ||
942 | obj1.free(); | ||
943 | |||
944 | // char width exceptions | ||
945 | if (desFontDict->lookup("W", &obj1)->isArray()) { | ||
946 | excepsSize = 0; | ||
947 | i = 0; | ||
948 | while (i + 1 < obj1.arrayGetLength()) { | ||
949 | obj1.arrayGet(i, &obj2); | ||
950 | obj1.arrayGet(i + 1, &obj3); | ||
951 | if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) { | ||
952 | if (obj1.arrayGet(i + 2, &obj4)->isNum()) { | ||
953 | if (widths.nExceps == excepsSize) { | ||
954 | excepsSize += 16; | ||
955 | widths.exceps = (GfxFontCIDWidthExcep *) | ||
956 | grealloc(widths.exceps, | ||
957 | excepsSize * sizeof(GfxFontCIDWidthExcep)); | ||
958 | } | ||
959 | widths.exceps[widths.nExceps].first = obj2.getInt(); | ||
960 | widths.exceps[widths.nExceps].last = obj3.getInt(); | ||
961 | widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; | ||
962 | ++widths.nExceps; | ||
963 | } else { | ||
964 | error(-1, "Bad widths array in Type 0 font"); | ||
965 | } | ||
966 | obj4.free(); | ||
967 | i += 3; | ||
968 | } else if (obj2.isInt() && obj3.isArray()) { | ||
969 | if (widths.nExceps + obj3.arrayGetLength() > excepsSize) { | ||
970 | excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15; | ||
971 | widths.exceps = (GfxFontCIDWidthExcep *) | ||
972 | grealloc(widths.exceps, | ||
973 | excepsSize * sizeof(GfxFontCIDWidthExcep)); | ||
974 | } | ||
975 | j = obj2.getInt(); | ||
976 | for (k = 0; k < obj3.arrayGetLength(); ++k) { | ||
977 | if (obj3.arrayGet(k, &obj4)->isNum()) { | ||
978 | widths.exceps[widths.nExceps].first = j; | ||
979 | widths.exceps[widths.nExceps].last = j; | ||
980 | widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; | ||
981 | ++j; | ||
982 | ++widths.nExceps; | ||
983 | } else { | ||
984 | error(-1, "Bad widths array in Type 0 font"); | ||
985 | } | ||
986 | obj4.free(); | ||
987 | } | ||
988 | i += 2; | ||
989 | } else { | ||
990 | error(-1, "Bad widths array in Type 0 font"); | ||
991 | ++i; | ||
992 | } | ||
993 | obj3.free(); | ||
994 | obj2.free(); | ||
995 | } | ||
996 | qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep), | ||
997 | &cmpWidthExcep); | ||
998 | } | ||
999 | obj1.free(); | ||
1000 | |||
1001 | // default metrics for vertical font | ||
1002 | if (desFontDict->lookup("DW2", &obj1)->isArray() && | ||
1003 | obj1.arrayGetLength() == 2) { | ||
1004 | if (obj1.arrayGet(0, &obj2)->isNum()) { | ||
1005 | widths.defVY = obj1.getNum() * 0.001; | ||
1006 | } | ||
1007 | obj2.free(); | ||
1008 | if (obj1.arrayGet(1, &obj2)->isNum()) { | ||
1009 | widths.defHeight = obj1.getNum() * 0.001; | ||
1010 | } | ||
1011 | obj2.free(); | ||
1012 | } | ||
1013 | obj1.free(); | ||
1014 | |||
1015 | // char metric exceptions for vertical font | ||
1016 | if (desFontDict->lookup("W2", &obj1)->isArray()) { | ||
1017 | excepsSize = 0; | ||
1018 | i = 0; | ||
1019 | while (i + 1 < obj1.arrayGetLength()) { | ||
1020 | obj1.arrayGet(0, &obj2); | ||
1021 | obj2.arrayGet(0, &obj3); | ||
1022 | if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) { | ||
1023 | if (obj1.arrayGet(i + 2, &obj4)->isNum() && | ||
1024 | obj1.arrayGet(i + 3, &obj5)->isNum() && | ||
1025 | obj1.arrayGet(i + 4, &obj6)->isNum()) { | ||
1026 | if (widths.nExcepsV == excepsSize) { | ||
1027 | excepsSize += 16; | ||
1028 | widths.excepsV = (GfxFontCIDWidthExcepV *) | ||
1029 | grealloc(widths.excepsV, | ||
1030 | excepsSize * sizeof(GfxFontCIDWidthExcepV)); | ||
1031 | } | ||
1032 | widths.excepsV[widths.nExcepsV].first = obj2.getInt(); | ||
1033 | widths.excepsV[widths.nExcepsV].last = obj3.getInt(); | ||
1034 | widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001; | ||
1035 | widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001; | ||
1036 | widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001; | ||
1037 | ++widths.nExcepsV; | ||
1038 | } else { | ||
1039 | error(-1, "Bad widths (W2) array in Type 0 font"); | ||
1040 | } | ||
1041 | obj6.free(); | ||
1042 | obj5.free(); | ||
1043 | obj4.free(); | ||
1044 | i += 5; | ||
1045 | } else if (obj2.isInt() && obj3.isArray()) { | ||
1046 | if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) { | ||
1047 | excepsSize = | ||
1048 | (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15; | ||
1049 | widths.excepsV = (GfxFontCIDWidthExcepV *) | ||
1050 | grealloc(widths.excepsV, | ||
1051 | excepsSize * sizeof(GfxFontCIDWidthExcepV)); | ||
1052 | } | ||
1053 | j = obj2.getInt(); | ||
1054 | for (k = 0; k < obj3.arrayGetLength(); ++k) { | ||
1055 | if (obj3.arrayGet(k, &obj4)->isNum() && | ||
1056 | obj3.arrayGet(k, &obj5)->isNum() && | ||
1057 | obj3.arrayGet(k, &obj6)->isNum()) { | ||
1058 | widths.excepsV[widths.nExceps].first = j; | ||
1059 | widths.excepsV[widths.nExceps].last = j; | ||
1060 | widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001; | ||
1061 | widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001; | ||
1062 | widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001; | ||
1063 | ++j; | ||
1064 | ++widths.nExcepsV; | ||
1065 | } else { | ||
1066 | error(-1, "Bad widths (W2) array in Type 0 font"); | ||
1067 | } | ||
1068 | obj6.free(); | ||
1069 | obj5.free(); | ||
1070 | obj4.free(); | ||
1071 | } | ||
1072 | i += 2; | ||
1073 | } else { | ||
1074 | error(-1, "Bad widths (W2) array in Type 0 font"); | ||
1075 | ++i; | ||
1076 | } | ||
1077 | obj3.free(); | ||
1078 | obj2.free(); | ||
1079 | } | ||
1080 | qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV), | ||
1081 | &cmpWidthExcepV); | ||
1082 | } | ||
1083 | obj1.free(); | ||
1084 | |||
1085 | desFontDictObj.free(); | ||
1086 | ok = gTrue; | ||
1087 | return; | ||
1088 | |||
1089 | err4: | ||
1090 | obj3.free(); | ||
1091 | obj2.free(); | ||
1092 | err3: | ||
1093 | obj1.free(); | ||
1094 | err2: | ||
1095 | desFontDictObj.free(); | ||
1096 | err1:; | ||
1097 | } | ||
1098 | |||
1099 | GfxCIDFont::~GfxCIDFont() { | ||
1100 | if (cMap) { | ||
1101 | cMap->decRefCnt(); | ||
1102 | } | ||
1103 | if (ctu) { | ||
1104 | ctu->decRefCnt(); | ||
1105 | } | ||
1106 | gfree(widths.exceps); | ||
1107 | gfree(widths.excepsV); | ||
1108 | if (cidToGID) { | ||
1109 | gfree(cidToGID); | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | int GfxCIDFont::getNextChar(char *s, int len, CharCode *code, | ||
1114 | Unicode *u, int uSize, int *uLen, | ||
1115 | fouble *dx, fouble *dy, fouble *ox, fouble *oy) { | ||
1116 | CID cid; | ||
1117 | fouble w, h, vx, vy; | ||
1118 | int n, a, b, m; | ||
1119 | |||
1120 | if (!cMap) { | ||
1121 | *code = 0; | ||
1122 | *uLen = 0; | ||
1123 | *dx = *dy = 0; | ||
1124 | return 1; | ||
1125 | } | ||
1126 | |||
1127 | *code = (CharCode)(cid = cMap->getCID(s, len, &n)); | ||
1128 | if (ctu) { | ||
1129 | *uLen = ctu->mapToUnicode(cid, u, uSize); | ||
1130 | } else { | ||
1131 | *uLen = 0; | ||
1132 | } | ||
1133 | |||
1134 | // horizontal | ||
1135 | if (cMap->getWMode() == 0) { | ||
1136 | w = widths.defWidth; | ||
1137 | h = vx = vy = 0; | ||
1138 | if (widths.nExceps > 0 && cid >= widths.exceps[0].first) { | ||
1139 | a = 0; | ||
1140 | b = widths.nExceps; | ||
1141 | // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first | ||
1142 | while (b - a > 1) { | ||
1143 | m = (a + b) / 2; | ||
1144 | if (widths.exceps[m].first <= cid) { | ||
1145 | a = m; | ||
1146 | } else { | ||
1147 | b = m; | ||
1148 | } | ||
1149 | } | ||
1150 | if (cid <= widths.exceps[a].last) { | ||
1151 | w = widths.exceps[a].width; | ||
1152 | } | ||
1153 | } | ||
1154 | |||
1155 | // vertical | ||
1156 | } else { | ||
1157 | w = 0; | ||
1158 | h = widths.defHeight; | ||
1159 | vx = widths.defWidth / 2; | ||
1160 | vy = widths.defVY; | ||
1161 | if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) { | ||
1162 | a = 0; | ||
1163 | b = widths.nExcepsV; | ||
1164 | // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first | ||
1165 | while (b - a > 1) { | ||
1166 | m = (a + b) / 2; | ||
1167 | if (widths.excepsV[m].last <= cid) { | ||
1168 | a = m; | ||
1169 | } else { | ||
1170 | b = m; | ||
1171 | } | ||
1172 | } | ||
1173 | if (cid <= widths.excepsV[a].last) { | ||
1174 | h = widths.excepsV[a].height; | ||
1175 | vx = widths.excepsV[a].vx; | ||
1176 | vy = widths.excepsV[a].vy; | ||
1177 | } | ||
1178 | } | ||
1179 | } | ||
1180 | |||
1181 | *dx = w; | ||
1182 | *dy = h; | ||
1183 | *ox = vx; | ||
1184 | *oy = vy; | ||
1185 | |||
1186 | return n; | ||
1187 | } | ||
1188 | |||
1189 | CharCodeToUnicode *GfxCIDFont::getToUnicode() { | ||
1190 | ctu->incRefCnt(); | ||
1191 | return ctu; | ||
1192 | } | ||
1193 | |||
1194 | GString *GfxCIDFont::getCollection() { | ||
1195 | return cMap ? cMap->getCollection() : (GString *)NULL; | ||
1196 | } | ||
1197 | |||
1198 | //------------------------------------------------------------------------ | ||
1199 | // GfxFontDict | ||
1200 | //------------------------------------------------------------------------ | ||
1201 | |||
1202 | GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) { | ||
1203 | int i; | ||
1204 | Object obj1, obj2; | ||
1205 | |||
1206 | numFonts = fontDict->getLength(); | ||
1207 | fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *)); | ||
1208 | for (i = 0; i < numFonts; ++i) { | ||
1209 | fontDict->getValNF(i, &obj1); | ||
1210 | obj1.fetch(xref, &obj2); | ||
1211 | if (obj1.isRef() && obj2.isDict()) { | ||
1212 | fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i), | ||
1213 | obj1.getRef(), obj2.getDict()); | ||
1214 | if (fonts[i] && !fonts[i]->isOk()) { | ||
1215 | delete fonts[i]; | ||
1216 | fonts[i] = NULL; | ||
1217 | } | ||
1218 | } else { | ||
1219 | error(-1, "font resource is not a dictionary"); | ||
1220 | fonts[i] = NULL; | ||
1221 | } | ||
1222 | obj1.free(); | ||
1223 | obj2.free(); | ||
1224 | } | ||
1225 | } | ||
1226 | |||
1227 | GfxFontDict::~GfxFontDict() { | ||
1228 | int i; | ||
1229 | |||
1230 | for (i = 0; i < numFonts; ++i) { | ||
1231 | if (fonts[i]) { | ||
1232 | delete fonts[i]; | ||
1233 | } | ||
1234 | } | ||
1235 | gfree(fonts); | ||
1236 | } | ||
1237 | |||
1238 | GfxFont *GfxFontDict::lookup(char *tag) { | ||
1239 | int i; | ||
1240 | |||
1241 | for (i = 0; i < numFonts; ++i) { | ||
1242 | if (fonts[i] && fonts[i]->matches(tag)) { | ||
1243 | return fonts[i]; | ||
1244 | } | ||
1245 | } | ||
1246 | return NULL; | ||
1247 | } | ||