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