home *** CD-ROM | disk | FTP | other *** search
/ Enigma Amiga Life 110 / EnigmaAmiga110CD.iso / indispensabili / utility / apdf / xpdf-0.80 / xpdf / gfxfont.cc < prev    next >
C/C++ Source or Header  |  1999-06-23  |  20KB  |  835 lines

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