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