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