]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/GfxFont.cc
d1148cf212efc270471e9ec3a456435c2f34ca15
[evince.git] / pdf / xpdf / GfxFont.cc
1 //========================================================================
2 //
3 // GfxFont.cc
4 //
5 // Copyright 1996 Derek B. Noonburg
6 //
7 //========================================================================
8
9 #ifdef __GNUC__
10 #pragma implementation
11 #endif
12
13 #include <stdlib.h>
14 #include <stddef.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include "GString.h"
19 #include "gmem.h"
20 #include "gfile.h"
21 #include "config.h"
22 #include "Object.h"
23 #include "Array.h"
24 #include "Dict.h"
25 #include "Error.h"
26 #include "Params.h"
27 #include "GfxFont.h"
28
29 #include "FontInfo.h"
30 #if JAPANESE_SUPPORT
31 #include "CMapInfo.h"
32 #endif
33
34 //------------------------------------------------------------------------
35
36 static int CDECL cmpWidthExcep(const void *w1, const void *w2);
37
38 //------------------------------------------------------------------------
39
40 static Gushort *defCharWidths[12] = {
41   courierWidths,
42   courierObliqueWidths,
43   courierBoldWidths,
44   courierBoldObliqueWidths,
45   helveticaWidths,
46   helveticaObliqueWidths,
47   helveticaBoldWidths,
48   helveticaBoldObliqueWidths,
49   timesRomanWidths,
50   timesItalicWidths,
51   timesBoldWidths,
52   timesBoldItalicWidths
53 };
54
55 //------------------------------------------------------------------------
56 // GfxFontEncoding
57 //------------------------------------------------------------------------
58
59 inline int GfxFontEncoding::hash(char *name) {
60   int h;
61
62   h = name[0];
63   if (name[1])
64     h = h * 61 + name[1];
65   return h % gfxFontEncHashSize;
66 }
67
68 GfxFontEncoding::GfxFontEncoding() {
69   int i;
70
71   encoding = (char **)gmalloc(256 * sizeof(char *));
72   freeEnc = gTrue;
73   for (i = 0; i < 256; ++i)
74     encoding[i] = NULL;
75   for (i = 0; i < gfxFontEncHashSize; ++i)
76     hashTab[i] = -1;
77 }
78
79 GfxFontEncoding::GfxFontEncoding(char **encoding1, int encSize) {
80   int i;
81
82   encoding = encoding1;
83   freeEnc = gFalse;
84   for (i = 0; i < gfxFontEncHashSize; ++i)
85     hashTab[i] = -1;
86   for (i = 0; i < encSize; ++i) {
87     if (encoding[i])
88       addChar1(i, encoding[i]);
89   }
90 }
91
92 void GfxFontEncoding::addChar(int code, char *name) {
93   int h, i;
94
95   // replace character associated with code
96   if (encoding[code]) {
97     h = hash(encoding[code]);
98     for (i = 0; i < gfxFontEncHashSize; ++i) {
99       if (hashTab[h] == code) {
100         hashTab[h] = -2;
101         break;
102       }
103       if (++h == gfxFontEncHashSize)
104         h = 0;
105     }
106     gfree(encoding[code]);
107   }
108
109   // associate name with code
110   encoding[code] = name;
111
112   // insert name in hash table
113   addChar1(code, name);
114 }
115
116 void GfxFontEncoding::addChar1(int code, char *name) {
117   int h, i, code2;
118
119   // insert name in hash table
120   h = hash(name); 
121   for (i = 0; i < gfxFontEncHashSize; ++i) {
122     code2 = hashTab[h];
123     if (code2 < 0) {
124       hashTab[h] = code;
125       break;
126     } else if (encoding[code2] && !strcmp(encoding[code2], name)) {
127       // keep the highest code for each char -- this is needed because
128       // X won't display chars with codes < 32
129       if (code > code2)
130         hashTab[h] = code;
131       break;
132     }
133     if (++h == gfxFontEncHashSize)
134       h = 0;
135   }
136 }
137
138 GfxFontEncoding::~GfxFontEncoding() {
139   int i;
140
141   if (freeEnc) {
142     for (i = 0; i < 256; ++i) {
143       if (encoding[i])
144         gfree(encoding[i]);
145     }
146     gfree(encoding);
147   }
148 }
149
150 int GfxFontEncoding::getCharCode(char *name) {
151   int h, i, code;
152
153   h = hash(name);
154   for (i = 0; i < gfxFontEncHashSize; ++i) {
155     code = hashTab[h];
156     if (code == -1 ||
157         (code > 0 && encoding[code] && !strcmp(encoding[code], name)))
158       return code;
159     if (++h >= gfxFontEncHashSize)
160       h = 0;
161   }
162   return -1;
163 }
164
165 //------------------------------------------------------------------------
166 // GfxFont
167 //------------------------------------------------------------------------
168
169 GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) {
170   BuiltinFont *builtinFont;
171   char buf[256];
172   Object obj1, obj2, obj3;
173   char *p1, *p2;
174   int i;
175
176   // get font tag and ID
177   tag = new GString(tag1);
178   id = id1;
179
180   // get base font name
181   name = NULL;
182   fontDict->lookup("BaseFont", &obj1);
183   if (obj1.isName())
184     name = new GString(obj1.getName());
185   obj1.free();
186
187   // is it a built-in font?
188   builtinFont = NULL;
189   if (name) {
190     for (i = 0; i < numBuiltinFonts; ++i) {
191       if (!strcmp(builtinFonts[i].name, name->getCString())) {
192         builtinFont = &builtinFonts[i];
193         break;
194       }
195     }
196   }
197
198   // get font type
199   type = fontUnknownType;
200   fontDict->lookup("Subtype", &obj1);
201   if (obj1.isName("Type1"))
202     type = fontType1;
203   else if (obj1.isName("Type3"))
204     type = fontType3;
205   else if (obj1.isName("TrueType"))
206     type = fontTrueType;
207   else if (obj1.isName("Type0"))
208     type = fontType0;
209   obj1.free();
210   is16 = gFalse;
211
212   // get info from font descriptor
213   // for flags: assume Times-Roman (or TimesNewRoman), but
214   // explicitly check for Arial and CourierNew -- certain PDF
215   // generators apparently don't include FontDescriptors for Arial,
216   // TimesNewRoman, and CourierNew
217   flags = fontSerif;   // assume Times-Roman by default
218   if (type == fontTrueType && !name->cmp("Arial"))
219     flags = 0;
220   else if (type == fontTrueType && !name->cmp("CourierNew"))
221     flags = fontFixedWidth;
222   embFontID.num = -1;
223   embFontID.gen = -1;
224   embFontName = NULL;
225   extFontFile = NULL;
226   fontDict->lookup("FontDescriptor", &obj1);
227   if (obj1.isDict()) {
228
229     // flags
230     obj1.dictLookup("Flags", &obj2);
231     if (obj2.isInt())
232       flags = obj2.getInt();
233     obj2.free();
234
235     // embedded Type 1 font file and font name
236     if (type == fontType1) {
237       obj1.dictLookupNF("FontFile", &obj2);
238       if (obj2.isRef()) {
239         embFontID = obj2.getRef();
240
241         // get font name from the font file itself since font subsets
242         // sometimes use the 'AAAAAA+foo' name and sometimes use just 'foo'
243         obj2.fetch(&obj3);
244         if (obj3.isStream()) {
245           obj3.streamReset();
246           for (i = 0; i < 64; ++i) {
247             obj3.streamGetLine(buf, sizeof(buf));
248             if (!strncmp(buf, "/FontName", 9)) {
249               if ((p1 = strchr(buf+9, '/'))) {
250                 ++p1;
251                 for (p2 = p1; *p2 && !isspace(*p2); ++p2) ;
252                 embFontName = new GString(p1, p2 - p1);
253               }
254               break;
255             }
256           }
257         }
258         obj3.free();
259         obj2.free();
260
261         // couldn't find font name so just use the one in the PDF font
262         // descriptor
263         if (!embFontName) {
264           obj1.dictLookup("FontName", &obj2);
265           if (obj2.isName())
266             embFontName = new GString(obj2.getName());
267         }
268       }
269       obj2.free();
270
271     // embedded TrueType font file
272     } else if (type == fontTrueType) {
273       obj1.dictLookupNF("FontFile2", &obj2);
274       if (obj2.isRef())
275         embFontID = obj2.getRef();
276       obj2.free();
277     }
278   }
279   obj1.free();
280
281   // get font matrix
282   fontMat[0] = fontMat[3] = 1;
283   fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
284   if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
285     for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
286       if (obj1.arrayGet(i, &obj2)->isNum())
287         fontMat[i] = obj2.getNum();
288       obj2.free();
289     }
290   }
291   obj1.free();
292
293   // get encoding and character widths
294   if (type == fontType0) {
295     getType0EncAndWidths(fontDict);
296   } else if (builtinFont) {
297     makeEncoding(fontDict, builtinFont->encoding);
298     makeWidths(fontDict, builtinFont->encoding, builtinFont->widths);
299   } else {
300     makeEncoding(fontDict, NULL);
301     makeWidths(fontDict, NULL, NULL);
302   }
303 }
304
305 GfxFont::~GfxFont() {
306   delete tag;
307   if (name)
308     delete name;
309   if (!is16 && encoding)
310     delete encoding;
311   if (embFontName)
312     delete embFontName;
313   if (extFontFile)
314     delete extFontFile;
315   if (is16)
316     gfree(widths16.exceps);
317 }
318
319 double GfxFont::getWidth(GString *s) {
320   double w;
321   int i;
322
323   w = 0;
324   for (i = 0; i < s->getLength(); ++i)
325     w += widths[s->getChar(i) & 0xff];
326   return w;
327 }
328
329 double GfxFont::getWidth16(int c) {
330   double w;
331   int a, b, m;
332
333   w = widths16.defWidth;
334   a = -1;
335   b = widths16.numExceps;
336   // invariant: widths16.exceps[a].last < c < widths16.exceps[b].first
337   while (b - a > 1) {
338     m = (a + b) / 2;
339     if (widths16.exceps[m].last < c) {
340       a = m;
341     } else if (c < widths16.exceps[m].first) {
342       b = m;
343     } else {
344       w = widths16.exceps[m].width;
345       break;
346     }
347   }
348   return w;
349 }
350
351 double GfxFont::getWidth16(GString *s) {
352   double w;
353   int c;
354   int i;
355
356   w = 0;
357   for (i = 0; i < s->getLength(); i += 2) {
358     c = (s->getChar(i) << 8) + s->getChar(i+1);
359     w += getWidth16(c);
360   }
361   return w;
362 }
363
364 void GfxFont::makeEncoding(Dict *fontDict, GfxFontEncoding *builtinEncoding) {
365   GfxFontEncoding *baseEnc;
366   Object obj1, obj2, obj3;
367   char *charName;
368   int code, i;
369
370   // start with empty encoding
371   encoding = new GfxFontEncoding();
372
373   // get encoding from font dict
374   fontDict->lookup("Encoding", &obj1);
375
376   // encoding specified by dictionary
377   if (obj1.isDict()) {
378     obj1.dictLookup("BaseEncoding", &obj2);
379     baseEnc = makeEncoding1(obj2, fontDict, builtinEncoding);
380     obj2.free();
381     obj1.dictLookup("Differences", &obj2);
382     if (obj2.isArray()) {
383       code = 0;
384       for (i = 0; i < obj2.arrayGetLength(); ++i) {
385         obj2.arrayGet(i, &obj3);
386         if (obj3.isInt()) {
387           code = obj3.getInt();
388         } else if (obj3.isName()) {
389           if (code < 256)
390             encoding->addChar(code, copyString(obj3.getName()));
391           ++code;
392         } else {
393           error(-1, "Wrong type in font encoding resource differences (%s)",
394                 obj3.getTypeName());
395         }
396         obj3.free();
397       }
398     }
399     obj2.free();
400
401   // encoding specified by name or null
402   } else {
403     baseEnc = makeEncoding1(obj1, fontDict, builtinEncoding);
404   }
405
406   // free the font dict encoding
407   obj1.free();
408
409   // merge base encoding and differences;
410   for (code = 0; code < 256; ++code) {
411     if (!encoding->getCharName(code)) {
412       if ((charName = baseEnc->getCharName(code)))
413         encoding->addChar(code, copyString(charName));
414     }
415   }
416 }
417
418 GfxFontEncoding *GfxFont::makeEncoding1(Object obj, Dict *fontDict,
419                                         GfxFontEncoding *builtinEncoding) {
420   GfxFontEncoding *enc;
421   GBool haveEncoding;
422   Object obj1, obj2;
423   char **path;
424   FILE *f;
425   FileStream *str;
426
427   // MacRoman, WinAnsi, or Standard encoding
428   if (obj.isName("MacRomanEncoding")) {
429     enc = &macRomanEncoding;
430   } else if (obj.isName("WinAnsiEncoding")) {
431     enc = &winAnsiEncoding;
432   } else if (obj.isName("StandardEncoding")) {
433     enc = &standardEncoding;
434
435   // use the built-in font encoding if possible
436   } else if (builtinEncoding) {
437     enc = builtinEncoding;
438
439   // check font type
440   } else {
441
442     // Type 1 font: try to get encoding from font file
443     if (type == fontType1) {
444
445       // default to using standard encoding
446       enc = &standardEncoding;
447
448       // is there an external font file?
449       haveEncoding = gFalse;
450       if (name) {
451         for (path = fontPath; *path; ++path) {
452           extFontFile = appendToPath(new GString(*path), name->getCString());
453           f = fopen(extFontFile->getCString(), "rb");
454           if (!f) {
455             extFontFile->append(".pfb");
456             f = fopen(extFontFile->getCString(), "rb");
457           }
458           if (!f) {
459             extFontFile->del(extFontFile->getLength() - 4, 4);
460             extFontFile->append(".pfa");
461             f = fopen(extFontFile->getCString(), "rb");
462           }
463           if (f) {
464             obj1.initNull();
465             str = new FileStream(f, 0, -1, &obj1);
466             getType1Encoding(str);
467             delete str;
468             fclose(f);
469             haveEncoding = gTrue;
470             break;
471           }
472           delete extFontFile;
473           extFontFile = NULL;
474         }
475       }
476
477       // is there an embedded font file?
478       // (this has to be checked after the external font because
479       // XOutputDev needs the encoding from the external font)
480       if (!haveEncoding && embFontID.num >= 0) {
481         obj1.initRef(embFontID.num, embFontID.gen);
482         obj1.fetch(&obj2);
483         if (obj2.isStream())
484           getType1Encoding(obj2.getStream());
485         obj2.free();
486         obj1.free();
487       }
488
489     // TrueType font: use Mac encoding
490     } else if (type == fontTrueType) {
491       enc = &macRomanEncoding;
492
493     // not Type 1 or TrueType: just use the standard encoding
494     } else {
495       enc = &standardEncoding;
496     }
497   }
498
499   return enc;
500 }
501
502 void GfxFont::getType1Encoding(Stream *str) {
503   char buf[256];
504   char *p;
505   GBool found;
506   int code, i;
507
508   // look for encoding in font file
509   str->reset();
510   found = gFalse;
511   for (i = 0; i < 100; ++i) {
512     if (!str->getLine(buf, sizeof(buf)))
513       break;
514     if (!strncmp(buf, "/Encoding StandardEncoding def", 30))
515       break;
516     if (!strncmp(buf, "/Encoding 256 array", 19)) {
517       found = gTrue;
518       break;
519     }
520   }
521
522   // found the encoding, grab it
523   if (found) {
524     for (i = 0; i < 300; ++i) {
525       if (!str->getLine(buf, sizeof(buf)))
526         break;
527       p = strtok(buf, " \t");
528       if (p && !strcmp(p, "dup")) {
529         if ((p = strtok(NULL, " \t"))) {
530           code = atoi(p);
531           if ((p = strtok(NULL, " \t"))) {
532             if (p[0] == '/')
533               encoding->addChar(code, copyString(p+1));
534           }
535         }
536       }
537     }
538     //~ look for getinterval/putinterval junk
539   }
540 }
541
542 void GfxFont::makeWidths(Dict *fontDict, GfxFontEncoding *builtinEncoding,
543                          Gushort *builtinWidths) {
544   Object obj1, obj2;
545   int firstChar, lastChar;
546   int code, code2;
547   char *charName;
548   Gushort *defWidths;
549   int index;
550   double mult;
551
552   // initialize all widths to zero
553   for (code = 0; code < 256; ++code)
554     widths[code] = 0;
555
556   // use widths from built-in font
557   if (builtinEncoding) {
558     code2 = 0; // to make gcc happy
559     for (code = 0; code < 256; ++code) {
560       if ((charName = encoding->getCharName(code)) &&
561           (code2 = builtinEncoding->getCharCode(charName)) >= 0)
562         widths[code] = builtinWidths[code2] * 0.001;
563     }
564
565   // get widths from font dict
566   } else {
567     fontDict->lookup("FirstChar", &obj1);
568     firstChar = obj1.isInt() ? obj1.getInt() : 0;
569     obj1.free();
570     fontDict->lookup("LastChar", &obj1);
571     lastChar = obj1.isInt() ? obj1.getInt() : 255;
572     obj1.free();
573     if (type == fontType3)
574       mult = fontMat[0];
575     else
576       mult = 0.001;
577     fontDict->lookup("Widths", &obj1);
578     if (obj1.isArray()) {
579       for (code = firstChar; code <= lastChar; ++code) {
580         obj1.arrayGet(code - firstChar, &obj2);
581         if (obj2.isNum())
582           widths[code] = obj2.getNum() * mult;
583         obj2.free();
584       }
585     } else {
586
587       // couldn't find widths -- use defaults 
588 #if 0
589       //~ certain PDF generators apparently don't include widths
590       //~ for Arial and TimesNewRoman -- and this error message
591       //~ is a nuisance
592       error(-1, "No character widths resource for non-builtin font");
593 #endif
594       if (isFixedWidth())
595         index = 0;
596       else if (isSerif())
597         index = 8;
598       else
599         index = 4;
600       if (isBold())
601         index += 2;
602       if (isItalic())
603         index += 1;
604       defWidths = defCharWidths[index];
605       code2 = 0; // to make gcc happy
606       for (code = 0; code < 256; ++code) {
607         if ((charName = encoding->getCharName(code)) &&
608             (code2 = standardEncoding.getCharCode(charName)) >= 0)
609           widths[code] = defWidths[code2] * 0.001;
610       }
611     }
612     obj1.free();
613   }
614 }
615
616 void GfxFont::getType0EncAndWidths(Dict *fontDict) {
617   Object obj1, obj2, obj3, obj4, obj5, obj6;
618   int excepsSize;
619   int i, j, k, n;
620
621   fontDict->lookup("DescendantFonts", &obj1);
622   if (!obj1.isArray() || obj1.arrayGetLength() != 1) {
623     error(-1, "Bad DescendantFonts entry for Type 0 font");
624     goto err1;
625   }
626   obj1.arrayGet(0, &obj2);
627   if (!obj2.isDict("Font")) {
628     error(-1, "Bad descendant font of Type 0 font");
629     goto err2;
630   }
631
632   obj2.dictLookup("CIDSystemInfo", &obj3);
633   if (!obj3.isDict()) {
634     error(-1, "Bad CIDSystemInfo in Type 0 font descendant");
635     goto err3;
636   }
637   obj3.dictLookup("Registry", &obj4);
638   obj3.dictLookup("Ordering", &obj5);
639   if (obj4.isString() && obj5.isString()) {
640     if (obj4.getString()->cmp("Adobe") == 0 &&
641         obj5.getString()->cmp("Japan1") == 0) {
642 #if JAPANESE_SUPPORT
643       is16 = gTrue;
644       enc16.charSet = font16AdobeJapan12;
645 #else
646       error(-1, "Xpdf was compiled without Japanese font support");
647       goto err4;
648 #endif
649     } else {
650       error(-1, "Uknown Type 0 character set: %s-%s",
651             obj4.getString()->getCString(), obj5.getString()->getCString());
652       goto err4;
653     }
654   } else {
655     error(-1, "Unknown Type 0 character set");
656     goto err4;
657   }
658   obj5.free();
659   obj4.free();
660   obj3.free();
661
662   obj2.dictLookup("DW", &obj3);
663   if (obj3.isInt())
664     widths16.defWidth = obj3.getInt() * 0.001;
665   else
666     widths16.defWidth = 1.0;
667   obj3.free();
668
669   widths16.exceps = NULL;
670   widths16.numExceps = 0;
671   obj2.dictLookup("W", &obj3);
672   if (obj3.isArray()) {
673     excepsSize = 0;
674     k = 0;
675     i = 0;
676     while (i+1 < obj3.arrayGetLength()) {
677       obj3.arrayGet(i, &obj4);
678       obj3.arrayGet(i+1, &obj5);
679       if (obj4.isInt() && obj5.isInt()) {
680         obj3.arrayGet(i+2, &obj6);
681         if (!obj6.isNum()) {
682           error(-1, "Bad widths array in Type 0 font");
683           obj6.free();
684           obj5.free();
685           obj4.free();
686           break;
687         }
688         if (k == excepsSize) {
689           excepsSize += 16;
690           widths16.exceps = (GfxFontWidthExcep *)
691                         grealloc(widths16.exceps,
692                                  excepsSize * sizeof(GfxFontWidthExcep));
693         }
694         widths16.exceps[k].first = obj4.getInt();
695         widths16.exceps[k].last = obj5.getInt();
696         widths16.exceps[k].width = obj6.getNum() * 0.001;
697         obj6.free();
698         ++k;
699         i += 3;
700       } else if (obj4.isInt() && obj5.isArray()) {
701         if (k + obj5.arrayGetLength() >= excepsSize) {
702           excepsSize = (k + obj5.arrayGetLength() + 15) & ~15;
703           widths16.exceps = (GfxFontWidthExcep *)
704                         grealloc(widths16.exceps,
705                                  excepsSize * sizeof(GfxFontWidthExcep));
706         }
707         n = obj4.getInt();
708         for (j = 0; j < obj5.arrayGetLength(); ++j) {
709           obj5.arrayGet(j, &obj6);
710           if (!obj6.isNum()) {
711             error(-1, "Bad widths array in Type 0 font");
712             obj6.free();
713             break;
714           }
715           widths16.exceps[k].first = widths16.exceps[k].last = n++;
716           widths16.exceps[k].width = obj6.getNum() * 0.001;
717           obj6.free();
718           ++k;
719         }
720         i += 2;
721       } else {
722         error(-1, "Bad widths array in Type 0 font");
723         obj6.free();
724         obj5.free();
725         obj4.free();
726         break;
727       }
728       obj5.free();
729       obj4.free();
730     }
731     widths16.numExceps = k;
732     if (k > 0)
733       qsort(widths16.exceps, k, sizeof(GfxFontWidthExcep), &cmpWidthExcep);
734   }
735   obj3.free();
736
737   obj2.free();
738   obj1.free();
739
740   fontDict->lookup("Encoding", &obj1);
741   if (!obj1.isName()) {
742     error(-1, "Bad encoding for Type 0 font");
743     goto err1;
744   }
745 #if JAPANESE_SUPPORT
746   if (enc16.charSet == font16AdobeJapan12) {
747     for (i = 0; gfxFontEnc16Tab[i].name; ++i) {
748       if (!strcmp(obj1.getName(), gfxFontEnc16Tab[i].name))
749         break;
750     }
751     if (!gfxFontEnc16Tab[i].name) {
752       error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font",
753             obj1.getName());
754       goto err1;
755     }
756     enc16.enc = gfxFontEnc16Tab[i].enc;
757   }
758 #endif
759   obj1.free();
760
761   return;
762
763  err4:
764   obj5.free();
765   obj4.free();
766  err3:
767   obj3.free();
768  err2:
769   obj2.free();
770  err1:
771   obj1.free();
772   makeEncoding(fontDict, NULL);
773   makeWidths(fontDict, NULL, NULL);
774 }
775
776 static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
777   return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first;
778 }
779
780 //------------------------------------------------------------------------
781 // GfxFontDict
782 //------------------------------------------------------------------------
783
784 GfxFontDict::GfxFontDict(Dict *fontDict) {
785   int i;
786   Object obj1, obj2;
787
788   numFonts = fontDict->getLength();
789   fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
790   for (i = 0; i < numFonts; ++i) {
791     fontDict->getValNF(i, &obj1);
792     obj1.fetch(&obj2);
793     if (obj1.isRef() && obj2.isDict("Font")) {
794       fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(),
795                              obj2.getDict());
796     } else {
797       error(-1, "font resource is not a dictionary");
798       fonts[i] = NULL;
799     }
800     obj1.free();
801     obj2.free();
802   }
803 }
804
805 GfxFontDict::~GfxFontDict() {
806   int i;
807
808   for (i = 0; i < numFonts; ++i)
809     delete fonts[i];
810   gfree(fonts);
811 }
812
813 GfxFont *GfxFontDict::lookup(char *tag) {
814   int i;
815
816   for (i = 0; i < numFonts; ++i) {
817     if (fonts[i]->matches(tag))
818       return fonts[i];
819   }
820   return NULL;
821 }