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