Back to index

tetex-bin  3.0
GfxFont.cc
Go to the documentation of this file.
00001 //========================================================================
00002 //
00003 // GfxFont.cc
00004 //
00005 // Copyright 1996-2003 Glyph & Cog, LLC
00006 //
00007 //========================================================================
00008 
00009 #include <aconf.h>
00010 
00011 #ifdef USE_GCC_PRAGMAS
00012 #pragma implementation
00013 #endif
00014 
00015 #include <stdio.h>
00016 #include <stdlib.h>
00017 #include <string.h>
00018 #include <ctype.h>
00019 #include "gmem.h"
00020 #include "Error.h"
00021 #include "Object.h"
00022 #include "Dict.h"
00023 #include "GlobalParams.h"
00024 #include "CMap.h"
00025 #include "CharCodeToUnicode.h"
00026 #include "FontEncodingTables.h"
00027 #include "BuiltinFontTables.h"
00028 #include "FoFiType1.h"
00029 #include "FoFiType1C.h"
00030 #include "FoFiTrueType.h"
00031 #include "GfxFont.h"
00032 
00033 //------------------------------------------------------------------------
00034 
00035 struct StdFontMapEntry {
00036   char *altName;
00037   char *properName;
00038 };
00039 
00040 // Acrobat 4.0 and earlier substituted Base14-compatible fonts without
00041 // providing Widths and a FontDescriptor, so we munge the names into
00042 // the proper Base14 names.  This table is from implementation note 44
00043 // in the PDF 1.4 spec, with some additions based on empirical
00044 // evidence.
00045 static StdFontMapEntry stdFontMap[] = {
00046   { "Arial",                        "Helvetica" },
00047   { "Arial,Bold",                   "Helvetica-Bold" },
00048   { "Arial,BoldItalic",             "Helvetica-BoldOblique" },
00049   { "Arial,Italic",                 "Helvetica-Oblique" },
00050   { "Arial-Bold",                   "Helvetica-Bold" },
00051   { "Arial-BoldItalic",             "Helvetica-BoldOblique" },
00052   { "Arial-BoldItalicMT",           "Helvetica-BoldOblique" },
00053   { "Arial-BoldMT",                 "Helvetica-Bold" },
00054   { "Arial-Italic",                 "Helvetica-Oblique" },
00055   { "Arial-ItalicMT",               "Helvetica-Oblique" },
00056   { "ArialMT",                      "Helvetica" },
00057   { "Courier,Bold",                 "Courier-Bold" },
00058   { "Courier,Italic",               "Courier-Oblique" },
00059   { "Courier,BoldItalic",           "Courier-BoldOblique" },
00060   { "CourierNew",                   "Courier" },
00061   { "CourierNew,Bold",              "Courier-Bold" },
00062   { "CourierNew,BoldItalic",        "Courier-BoldOblique" },
00063   { "CourierNew,Italic",            "Courier-Oblique" },
00064   { "CourierNew-Bold",              "Courier-Bold" },
00065   { "CourierNew-BoldItalic",        "Courier-BoldOblique" },
00066   { "CourierNew-Italic",            "Courier-Oblique" },
00067   { "CourierNewPS-BoldItalicMT",    "Courier-BoldOblique" },
00068   { "CourierNewPS-BoldMT",          "Courier-Bold" },
00069   { "CourierNewPS-ItalicMT",        "Courier-Oblique" },
00070   { "CourierNewPSMT",               "Courier" },
00071   { "Helvetica,Bold",               "Helvetica-Bold" },
00072   { "Helvetica,BoldItalic",         "Helvetica-BoldOblique" },
00073   { "Helvetica,Italic",             "Helvetica-Oblique" },
00074   { "Helvetica-BoldItalic",         "Helvetica-BoldOblique" },
00075   { "Helvetica-Italic",             "Helvetica-Oblique" },
00076   { "Symbol,Bold",                  "Symbol" },
00077   { "Symbol,BoldItalic",            "Symbol" },
00078   { "Symbol,Italic",                "Symbol" },
00079   { "TimesNewRoman",                "Times-Roman" },
00080   { "TimesNewRoman,Bold",           "Times-Bold" },
00081   { "TimesNewRoman,BoldItalic",     "Times-BoldItalic" },
00082   { "TimesNewRoman,Italic",         "Times-Italic" },
00083   { "TimesNewRoman-Bold",           "Times-Bold" },
00084   { "TimesNewRoman-BoldItalic",     "Times-BoldItalic" },
00085   { "TimesNewRoman-Italic",         "Times-Italic" },
00086   { "TimesNewRomanPS",              "Times-Roman" },
00087   { "TimesNewRomanPS-Bold",         "Times-Bold" },
00088   { "TimesNewRomanPS-BoldItalic",   "Times-BoldItalic" },
00089   { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" },
00090   { "TimesNewRomanPS-BoldMT",       "Times-Bold" },
00091   { "TimesNewRomanPS-Italic",       "Times-Italic" },
00092   { "TimesNewRomanPS-ItalicMT",     "Times-Italic" },
00093   { "TimesNewRomanPSMT",            "Times-Roman" },
00094   { "TimesNewRomanPSMT,Bold",       "Times-Bold" },
00095   { "TimesNewRomanPSMT,BoldItalic", "Times-BoldItalic" },
00096   { "TimesNewRomanPSMT,Italic",     "Times-Italic" }
00097 };
00098 
00099 //------------------------------------------------------------------------
00100 // GfxFont
00101 //------------------------------------------------------------------------
00102 
00103 GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) {
00104   GString *nameA;
00105   GfxFont *font;
00106   Object obj1;
00107 
00108   // get base font name
00109   nameA = NULL;
00110   fontDict->lookup("BaseFont", &obj1);
00111   if (obj1.isName()) {
00112     nameA = new GString(obj1.getName());
00113   }
00114   obj1.free();
00115 
00116   // get font type
00117   font = NULL;
00118   fontDict->lookup("Subtype", &obj1);
00119   if (obj1.isName("Type1") || obj1.isName("MMType1")) {
00120     font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict);
00121   } else if (obj1.isName("Type1C")) {
00122     font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict);
00123   } else if (obj1.isName("Type3")) {
00124     font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict);
00125   } else if (obj1.isName("TrueType")) {
00126     font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict);
00127   } else if (obj1.isName("Type0")) {
00128     font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict);
00129   } else {
00130     error(-1, "Unknown font type: '%s'",
00131          obj1.isName() ? obj1.getName() : "???");
00132     font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict);
00133   }
00134   obj1.free();
00135 
00136   return font;
00137 }
00138 
00139 GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) {
00140   ok = gFalse;
00141   tag = new GString(tagA);
00142   id = idA;
00143   name = nameA;
00144   origName = nameA;
00145   embFontName = NULL;
00146   extFontFile = NULL;
00147 }
00148 
00149 GfxFont::~GfxFont() {
00150   delete tag;
00151   if (origName && origName != name) {
00152     delete origName;
00153   }
00154   if (name) {
00155     delete name;
00156   }
00157   if (embFontName) {
00158     delete embFontName;
00159   }
00160   if (extFontFile) {
00161     delete extFontFile;
00162   }
00163 }
00164 
00165 void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) {
00166   Object obj1, obj2, obj3, obj4;
00167   double t;
00168   int i;
00169 
00170   // assume Times-Roman by default (for substitution purposes)
00171   flags = fontSerif;
00172 
00173   embFontID.num = -1;
00174   embFontID.gen = -1;
00175   missingWidth = 0;
00176 
00177   if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) {
00178 
00179     // get flags
00180     if (obj1.dictLookup("Flags", &obj2)->isInt()) {
00181       flags = obj2.getInt();
00182     }
00183     obj2.free();
00184 
00185     // get name
00186     obj1.dictLookup("FontName", &obj2);
00187     if (obj2.isName()) {
00188       embFontName = new GString(obj2.getName());
00189     }
00190     obj2.free();
00191 
00192     // look for embedded font file
00193     if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) {
00194       if (type == fontType1) {
00195        embFontID = obj2.getRef();
00196       } else {
00197        error(-1, "Mismatch between font type and embedded font file");
00198       }
00199     }
00200     obj2.free();
00201     if (embFontID.num == -1 &&
00202        obj1.dictLookupNF("FontFile2", &obj2)->isRef()) {
00203       if (type == fontTrueType || type == fontCIDType2) {
00204        embFontID = obj2.getRef();
00205       } else {
00206        error(-1, "Mismatch between font type and embedded font file");
00207       }
00208     }
00209     obj2.free();
00210     if (embFontID.num == -1 &&
00211        obj1.dictLookupNF("FontFile3", &obj2)->isRef()) {
00212       if (obj2.fetch(xref, &obj3)->isStream()) {
00213        obj3.streamGetDict()->lookup("Subtype", &obj4);
00214        if (obj4.isName("Type1")) {
00215          if (type == fontType1) {
00216            embFontID = obj2.getRef();
00217          } else {
00218            error(-1, "Mismatch between font type and embedded font file");
00219          }
00220        } else if (obj4.isName("Type1C")) {
00221          if (type == fontType1) {
00222            type = fontType1C;
00223            embFontID = obj2.getRef();
00224          } else if (type == fontType1C) {
00225            embFontID = obj2.getRef();
00226          } else {
00227            error(-1, "Mismatch between font type and embedded font file");
00228          }
00229        } else if (obj4.isName("TrueType")) {
00230          if (type == fontTrueType) {
00231            embFontID = obj2.getRef();
00232          } else {
00233            error(-1, "Mismatch between font type and embedded font file");
00234          }
00235        } else if (obj4.isName("CIDFontType0C")) {
00236          if (type == fontCIDType0) {
00237            type = fontCIDType0C;
00238            embFontID = obj2.getRef();
00239          } else {
00240            error(-1, "Mismatch between font type and embedded font file");
00241          }
00242        } else {
00243          error(-1, "Unknown embedded font type '%s'",
00244               obj4.isName() ? obj4.getName() : "???");
00245        }
00246        obj4.free();
00247       }
00248       obj3.free();
00249     }
00250     obj2.free();
00251 
00252     // look for MissingWidth
00253     obj1.dictLookup("MissingWidth", &obj2);
00254     if (obj2.isNum()) {
00255       missingWidth = obj2.getNum();
00256     }
00257     obj2.free();
00258 
00259     // get Ascent and Descent
00260     obj1.dictLookup("Ascent", &obj2);
00261     if (obj2.isNum()) {
00262       t = 0.001 * obj2.getNum();
00263       // some broken font descriptors set ascent and descent to 0
00264       if (t != 0) {
00265        ascent = t;
00266       }
00267     }
00268     obj2.free();
00269     obj1.dictLookup("Descent", &obj2);
00270     if (obj2.isNum()) {
00271       t = 0.001 * obj2.getNum();
00272       // some broken font descriptors set ascent and descent to 0
00273       if (t != 0) {
00274        descent = t;
00275       }
00276       // some broken font descriptors specify a positive descent
00277       if (descent > 0) {
00278        descent = -descent;
00279       }
00280     }
00281     obj2.free();
00282 
00283     // font FontBBox
00284     if (obj1.dictLookup("FontBBox", &obj2)->isArray()) {
00285       for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) {
00286        if (obj2.arrayGet(i, &obj3)->isNum()) {
00287          fontBBox[i] = 0.001 * obj3.getNum();
00288        }
00289        obj3.free();
00290       }
00291     }
00292     obj2.free();
00293 
00294   }
00295   obj1.free();
00296 }
00297 
00298 CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits,
00299                                          CharCodeToUnicode *ctu) {
00300   GString *buf;
00301   Object obj1;
00302   int c;
00303 
00304   if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) {
00305     obj1.free();
00306     return NULL;
00307   }
00308   buf = new GString();
00309   obj1.streamReset();
00310   while ((c = obj1.streamGetChar()) != EOF) {
00311     buf->append(c);
00312   }
00313   obj1.streamClose();
00314   obj1.free();
00315   if (ctu) {
00316     ctu->mergeCMap(buf, nBits);
00317   } else {
00318     ctu = CharCodeToUnicode::parseCMap(buf, nBits);
00319   }
00320   delete buf;
00321   return ctu;
00322 }
00323 
00324 void GfxFont::findExtFontFile() {
00325   static char *type1Exts[] = { ".pfa", ".pfb", ".ps", "", NULL };
00326   static char *ttExts[] = { ".ttf", NULL };
00327 
00328   if (name) {
00329     if (type == fontType1) {
00330       extFontFile = globalParams->findFontFile(name, type1Exts);
00331     } else if (type == fontTrueType) {
00332       extFontFile = globalParams->findFontFile(name, ttExts);
00333     }
00334   }
00335 }
00336 
00337 char *GfxFont::readExtFontFile(int *len) {
00338   FILE *f;
00339   char *buf;
00340 
00341   if (!(f = fopen(extFontFile->getCString(), "rb"))) {
00342     error(-1, "External font file '%s' vanished", extFontFile->getCString());
00343     return NULL;
00344   }
00345   fseek(f, 0, SEEK_END);
00346   *len = (int)ftell(f);
00347   fseek(f, 0, SEEK_SET);
00348   buf = (char *)gmalloc(*len);
00349   if ((int)fread(buf, 1, *len, f) != *len) {
00350     error(-1, "Error reading external font file '%s'",
00351          extFontFile->getCString());
00352   }
00353   fclose(f);
00354   return buf;
00355 }
00356 
00357 char *GfxFont::readEmbFontFile(XRef *xref, int *len) {
00358   char *buf;
00359   Object obj1, obj2;
00360   Stream *str;
00361   int c;
00362   int size, i;
00363 
00364   obj1.initRef(embFontID.num, embFontID.gen);
00365   obj1.fetch(xref, &obj2);
00366   if (!obj2.isStream()) {
00367     error(-1, "Embedded font file is not a stream");
00368     obj2.free();
00369     obj1.free();
00370     embFontID.num = -1;
00371     return NULL;
00372   }
00373   str = obj2.getStream();
00374 
00375   buf = NULL;
00376   i = size = 0;
00377   str->reset();
00378   while ((c = str->getChar()) != EOF) {
00379     if (i == size) {
00380       size += 4096;
00381       buf = (char *)grealloc(buf, size);
00382     }
00383     buf[i++] = c;
00384   }
00385   *len = i;
00386   str->close();
00387 
00388   obj2.free();
00389   obj1.free();
00390 
00391   return buf;
00392 }
00393 
00394 //------------------------------------------------------------------------
00395 // Gfx8BitFont
00396 //------------------------------------------------------------------------
00397 
00398 Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
00399                       GfxFontType typeA, Dict *fontDict):
00400   GfxFont(tagA, idA, nameA)
00401 {
00402   BuiltinFont *builtinFont;
00403   char **baseEnc;
00404   GBool baseEncFromFontFile;
00405   char *buf;
00406   int len;
00407   FoFiType1 *ffT1;
00408   FoFiType1C *ffT1C;
00409   int code, code2;
00410   char *charName;
00411   GBool missing, hex;
00412   Unicode toUnicode[256];
00413   CharCodeToUnicode *utu, *ctu2;
00414   Unicode uBuf[8];
00415   double mul;
00416   int firstChar, lastChar;
00417   Gushort w;
00418   Object obj1, obj2, obj3;
00419   int n, i, a, b, m;
00420 
00421   type = typeA;
00422   ctu = NULL;
00423 
00424   // do font name substitution for various aliases of the Base 14 font
00425   // names
00426   if (name) {
00427     a = 0;
00428     b = sizeof(stdFontMap) / sizeof(StdFontMapEntry);
00429     // invariant: stdFontMap[a].altName <= name < stdFontMap[b].altName
00430     while (b - a > 1) {
00431       m = (a + b) / 2;
00432       if (name->cmp(stdFontMap[m].altName) >= 0) {
00433        a = m;
00434       } else {
00435        b = m;
00436       }
00437     }
00438     if (!name->cmp(stdFontMap[a].altName)) {
00439       name = new GString(stdFontMap[a].properName);
00440     }
00441   }
00442 
00443   // is it a built-in font?
00444   builtinFont = NULL;
00445   if (name) {
00446     for (i = 0; i < nBuiltinFonts; ++i) {
00447       if (!name->cmp(builtinFonts[i].name)) {
00448        builtinFont = &builtinFonts[i];
00449        break;
00450       }
00451     }
00452   }
00453 
00454   // default ascent/descent values
00455   if (builtinFont) {
00456     ascent = 0.001 * builtinFont->ascent;
00457     descent = 0.001 * builtinFont->descent;
00458     fontBBox[0] = 0.001 * builtinFont->bbox[0];
00459     fontBBox[1] = 0.001 * builtinFont->bbox[1];
00460     fontBBox[2] = 0.001 * builtinFont->bbox[2];
00461     fontBBox[3] = 0.001 * builtinFont->bbox[3];
00462   } else {
00463     ascent = 0.95;
00464     descent = -0.35;
00465     fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
00466   }
00467 
00468   // get info from font descriptor
00469   readFontDescriptor(xref, fontDict);
00470 
00471   // look for an external font file
00472   findExtFontFile();
00473 
00474   // get font matrix
00475   fontMat[0] = fontMat[3] = 1;
00476   fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0;
00477   if (fontDict->lookup("FontMatrix", &obj1)->isArray()) {
00478     for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) {
00479       if (obj1.arrayGet(i, &obj2)->isNum()) {
00480        fontMat[i] = obj2.getNum();
00481       }
00482       obj2.free();
00483     }
00484   }
00485   obj1.free();
00486 
00487   // get Type 3 bounding box, font definition, and resources
00488   if (type == fontType3) {
00489     if (fontDict->lookup("FontBBox", &obj1)->isArray()) {
00490       for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) {
00491        if (obj1.arrayGet(i, &obj2)->isNum()) {
00492          fontBBox[i] = obj2.getNum();
00493        }
00494        obj2.free();
00495       }
00496     }
00497     obj1.free();
00498     if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) {
00499       error(-1, "Missing or invalid CharProcs dictionary in Type 3 font");
00500       charProcs.free();
00501     }
00502     if (!fontDict->lookup("Resources", &resources)->isDict()) {
00503       resources.free();
00504     }
00505   }
00506 
00507   //----- build the font encoding -----
00508 
00509   // Encodings start with a base encoding, which can come from
00510   // (in order of priority):
00511   //   1. FontDict.Encoding or FontDict.Encoding.BaseEncoding
00512   //        - MacRoman / MacExpert / WinAnsi / Standard
00513   //   2. embedded or external font file
00514   //   3. default:
00515   //        - builtin --> builtin encoding
00516   //        - TrueType --> MacRomanEncoding
00517   //        - others --> StandardEncoding
00518   // and then add a list of differences (if any) from
00519   // FontDict.Encoding.Differences.
00520 
00521   // check FontDict for base encoding
00522   hasEncoding = gFalse;
00523   usesMacRomanEnc = gFalse;
00524   baseEnc = NULL;
00525   baseEncFromFontFile = gFalse;
00526   fontDict->lookup("Encoding", &obj1);
00527   if (obj1.isDict()) {
00528     obj1.dictLookup("BaseEncoding", &obj2);
00529     if (obj2.isName("MacRomanEncoding")) {
00530       hasEncoding = gTrue;
00531       usesMacRomanEnc = gTrue;
00532       baseEnc = macRomanEncoding;
00533     } else if (obj2.isName("MacExpertEncoding")) {
00534       hasEncoding = gTrue;
00535       baseEnc = macExpertEncoding;
00536     } else if (obj2.isName("WinAnsiEncoding")) {
00537       hasEncoding = gTrue;
00538       baseEnc = winAnsiEncoding;
00539     } else if (obj2.isName("StandardEncoding")) {
00540       hasEncoding = gTrue;
00541       baseEnc = standardEncoding;
00542     }
00543     obj2.free();
00544   } else if (obj1.isName("MacRomanEncoding")) {
00545     hasEncoding = gTrue;
00546     usesMacRomanEnc = gTrue;
00547     baseEnc = macRomanEncoding;
00548   } else if (obj1.isName("MacExpertEncoding")) {
00549     hasEncoding = gTrue;
00550     baseEnc = macExpertEncoding;
00551   } else if (obj1.isName("WinAnsiEncoding")) {
00552     hasEncoding = gTrue;
00553     baseEnc = winAnsiEncoding;
00554   } else if (obj1.isName("StandardEncoding")) {
00555     hasEncoding = gTrue;
00556     baseEnc = standardEncoding;
00557   }
00558 
00559   // check embedded or external font file for base encoding
00560   // (only for Type 1 fonts - trying to get an encoding out of a
00561   // TrueType font is a losing proposition)
00562   ffT1 = NULL;
00563   ffT1C = NULL;
00564   buf = NULL;
00565   if (type == fontType1 && (extFontFile || embFontID.num >= 0)) {
00566     if (extFontFile) {
00567       ffT1 = FoFiType1::load(extFontFile->getCString());
00568     } else {
00569       buf = readEmbFontFile(xref, &len);
00570       ffT1 = FoFiType1::make(buf, len);
00571     }
00572     if (ffT1) {
00573       if (ffT1->getName()) {
00574        if (embFontName) {
00575          delete embFontName;
00576        }
00577        embFontName = new GString(ffT1->getName());
00578       }
00579       if (!baseEnc) {
00580        baseEnc = ffT1->getEncoding();
00581        baseEncFromFontFile = gTrue;
00582       }
00583     }
00584   } else if (type == fontType1C && (extFontFile || embFontID.num >= 0)) {
00585     if (extFontFile) {
00586       ffT1C = FoFiType1C::load(extFontFile->getCString());
00587     } else {
00588       buf = readEmbFontFile(xref, &len);
00589       ffT1C = FoFiType1C::make(buf, len);
00590     }
00591     if (ffT1C) {
00592       if (ffT1C->getName()) {
00593        if (embFontName) {
00594          delete embFontName;
00595        }
00596        embFontName = new GString(ffT1C->getName());
00597       }
00598       if (!baseEnc) {
00599        baseEnc = ffT1C->getEncoding();
00600        baseEncFromFontFile = gTrue;
00601       }
00602     }
00603   }
00604   if (buf) {
00605     gfree(buf);
00606   }
00607 
00608   // get default base encoding
00609   if (!baseEnc) {
00610     if (builtinFont) {
00611       baseEnc = builtinFont->defaultBaseEnc;
00612       hasEncoding = gTrue;
00613     } else if (type == fontTrueType) {
00614       baseEnc = winAnsiEncoding;
00615     } else {
00616       baseEnc = standardEncoding;
00617     }
00618   }
00619 
00620   // copy the base encoding
00621   for (i = 0; i < 256; ++i) {
00622     enc[i] = baseEnc[i];
00623     if ((encFree[i] = baseEncFromFontFile) && enc[i]) {
00624       enc[i] = copyString(baseEnc[i]);
00625     }
00626   }
00627 
00628   // some Type 1C font files have empty encodings, which can break the
00629   // T1C->T1 conversion (since the 'seac' operator depends on having
00630   // the accents in the encoding), so we fill in any gaps from
00631   // StandardEncoding
00632   if (type == fontType1C && (extFontFile || embFontID.num >= 0) &&
00633       baseEncFromFontFile) {
00634     for (i = 0; i < 256; ++i) {
00635       if (!enc[i] && standardEncoding[i]) {
00636        enc[i] = standardEncoding[i];
00637        encFree[i] = gFalse;
00638       }
00639     }
00640   }
00641 
00642   // merge differences into encoding
00643   if (obj1.isDict()) {
00644     obj1.dictLookup("Differences", &obj2);
00645     if (obj2.isArray()) {
00646       hasEncoding = gTrue;
00647       code = 0;
00648       for (i = 0; i < obj2.arrayGetLength(); ++i) {
00649        obj2.arrayGet(i, &obj3);
00650        if (obj3.isInt()) {
00651          code = obj3.getInt();
00652        } else if (obj3.isName()) {
00653          if (code >= 0 && code < 256) {
00654            if (encFree[code]) {
00655              gfree(enc[code]);
00656            }
00657            enc[code] = copyString(obj3.getName());
00658            encFree[code] = gTrue;
00659          }
00660          ++code;
00661        } else {
00662          error(-1, "Wrong type in font encoding resource differences (%s)",
00663               obj3.getTypeName());
00664        }
00665        obj3.free();
00666       }
00667     }
00668     obj2.free();
00669   }
00670   obj1.free();
00671   if (ffT1) {
00672     delete ffT1;
00673   }
00674   if (ffT1C) {
00675     delete ffT1C;
00676   }
00677 
00678   //----- build the mapping to Unicode -----
00679 
00680   // pass 1: use the name-to-Unicode mapping table
00681   missing = hex = gFalse;
00682   for (code = 0; code < 256; ++code) {
00683     if ((charName = enc[code])) {
00684       if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) &&
00685          strcmp(charName, ".notdef")) {
00686        // if it wasn't in the name-to-Unicode table, check for a
00687        // name that looks like 'Axx' or 'xx', where 'A' is any letter
00688        // and 'xx' is two hex digits
00689        if ((strlen(charName) == 3 &&
00690             isalpha(charName[0]) &&
00691             isxdigit(charName[1]) && isxdigit(charName[2]) &&
00692             ((charName[1] >= 'a' && charName[1] <= 'f') ||
00693              (charName[1] >= 'A' && charName[1] <= 'F') ||
00694              (charName[2] >= 'a' && charName[2] <= 'f') ||
00695              (charName[2] >= 'A' && charName[2] <= 'F'))) ||
00696            (strlen(charName) == 2 &&
00697             isxdigit(charName[0]) && isxdigit(charName[1]) &&
00698             ((charName[0] >= 'a' && charName[0] <= 'f') ||
00699              (charName[0] >= 'A' && charName[0] <= 'F') ||
00700              (charName[1] >= 'a' && charName[1] <= 'f') ||
00701              (charName[1] >= 'A' && charName[1] <= 'F')))) {
00702          hex = gTrue;
00703        }
00704        missing = gTrue;
00705       }
00706     } else {
00707       toUnicode[code] = 0;
00708     }
00709   }
00710 
00711   // pass 2: try to fill in the missing chars, looking for names of
00712   // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B'
00713   // are any letters, 'xx' is two hex digits, and 'nn' is 2-4
00714   // decimal digits
00715   if (missing && globalParams->getMapNumericCharNames()) {
00716     for (code = 0; code < 256; ++code) {
00717       if ((charName = enc[code]) && !toUnicode[code] &&
00718          strcmp(charName, ".notdef")) {
00719        n = strlen(charName);
00720        code2 = -1;
00721        if (hex && n == 3 && isalpha(charName[0]) &&
00722            isxdigit(charName[1]) && isxdigit(charName[2])) {
00723          sscanf(charName+1, "%x", &code2);
00724        } else if (hex && n == 2 &&
00725                  isxdigit(charName[0]) && isxdigit(charName[1])) {
00726          sscanf(charName, "%x", &code2);
00727        } else if (!hex && n >= 2 && n <= 4 &&
00728                  isdigit(charName[0]) && isdigit(charName[1])) {
00729          code2 = atoi(charName);
00730        } else if (n >= 3 && n <= 5 &&
00731                  isdigit(charName[1]) && isdigit(charName[2])) {
00732          code2 = atoi(charName+1);
00733        } else if (n >= 4 && n <= 6 &&
00734                  isdigit(charName[2]) && isdigit(charName[3])) {
00735          code2 = atoi(charName+2);
00736        }
00737        if (code2 >= 0 && code2 <= 0xff) {
00738          toUnicode[code] = (Unicode)code2;
00739        }
00740       }
00741     }
00742   }
00743 
00744   // construct the char code -> Unicode mapping object
00745   ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode);
00746 
00747   // merge in a ToUnicode CMap, if there is one -- this overwrites
00748   // existing entries in ctu, i.e., the ToUnicode CMap takes
00749   // precedence, but the other encoding info is allowed to fill in any
00750   // holes
00751   readToUnicodeCMap(fontDict, 8, ctu);
00752 
00753   // look for a Unicode-to-Unicode mapping
00754   if (name && (utu = globalParams->getUnicodeToUnicode(name))) {
00755     for (i = 0; i < 256; ++i) {
00756       toUnicode[i] = 0;
00757     }
00758     ctu2 = CharCodeToUnicode::make8BitToUnicode(toUnicode);
00759     for (i = 0; i < 256; ++i) {
00760       n = ctu->mapToUnicode((CharCode)i, uBuf, 8);
00761       if (n >= 1) {
00762        n = utu->mapToUnicode((CharCode)uBuf[0], uBuf, 8);
00763        if (n >= 1) {
00764          ctu2->setMapping((CharCode)i, uBuf, n);
00765        }
00766       }
00767     }
00768     utu->decRefCnt();
00769     delete ctu;
00770     ctu = ctu2;
00771   }
00772 
00773   //----- get the character widths -----
00774 
00775   // initialize all widths
00776   for (code = 0; code < 256; ++code) {
00777     widths[code] = missingWidth * 0.001;
00778   }
00779 
00780   // use widths from font dict, if present
00781   fontDict->lookup("FirstChar", &obj1);
00782   firstChar = obj1.isInt() ? obj1.getInt() : 0;
00783   obj1.free();
00784   if (firstChar < 0 || firstChar > 255) {
00785     firstChar = 0;
00786   }
00787   fontDict->lookup("LastChar", &obj1);
00788   lastChar = obj1.isInt() ? obj1.getInt() : 255;
00789   obj1.free();
00790   if (lastChar < 0 || lastChar > 255) {
00791     lastChar = 255;
00792   }
00793   mul = (type == fontType3) ? fontMat[0] : 0.001;
00794   fontDict->lookup("Widths", &obj1);
00795   if (obj1.isArray()) {
00796     flags |= fontFixedWidth;
00797     if (obj1.arrayGetLength() < lastChar - firstChar + 1) {
00798       lastChar = firstChar + obj1.arrayGetLength() - 1;
00799     }
00800     for (code = firstChar; code <= lastChar; ++code) {
00801       obj1.arrayGet(code - firstChar, &obj2);
00802       if (obj2.isNum()) {
00803        widths[code] = obj2.getNum() * mul;
00804        if (widths[code] != widths[firstChar]) {
00805          flags &= ~fontFixedWidth;
00806        }
00807       }
00808       obj2.free();
00809     }
00810 
00811   // use widths from built-in font
00812   } else if (builtinFont) {
00813     // this is a kludge for broken PDF files that encode char 32
00814     // as .notdef
00815     if (builtinFont->widths->getWidth("space", &w)) {
00816       widths[32] = 0.001 * w;
00817     }
00818     for (code = 0; code < 256; ++code) {
00819       if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
00820        widths[code] = 0.001 * w;
00821       }
00822     }
00823 
00824   // couldn't find widths -- use defaults 
00825   } else {
00826     // this is technically an error -- the Widths entry is required
00827     // for all but the Base-14 fonts -- but certain PDF generators
00828     // apparently don't include widths for Arial and TimesNewRoman
00829     if (isFixedWidth()) {
00830       i = 0;
00831     } else if (isSerif()) {
00832       i = 8;
00833     } else {
00834       i = 4;
00835     }
00836     if (isBold()) {
00837       i += 2;
00838     }
00839     if (isItalic()) {
00840       i += 1;
00841     }
00842     builtinFont = builtinFontSubst[i];
00843     // this is a kludge for broken PDF files that encode char 32
00844     // as .notdef
00845     if (builtinFont->widths->getWidth("space", &w)) {
00846       widths[32] = 0.001 * w;
00847     }
00848     for (code = 0; code < 256; ++code) {
00849       if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) {
00850        widths[code] = 0.001 * w;
00851       }
00852     }
00853   }
00854   obj1.free();
00855 
00856   ok = gTrue;
00857 }
00858 
00859 Gfx8BitFont::~Gfx8BitFont() {
00860   int i;
00861 
00862   for (i = 0; i < 256; ++i) {
00863     if (encFree[i] && enc[i]) {
00864       gfree(enc[i]);
00865     }
00866   }
00867   ctu->decRefCnt();
00868   if (charProcs.isDict()) {
00869     charProcs.free();
00870   }
00871   if (resources.isDict()) {
00872     resources.free();
00873   }
00874 }
00875 
00876 int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code,
00877                           Unicode *u, int uSize, int *uLen,
00878                           double *dx, double *dy, double *ox, double *oy) {
00879   CharCode c;
00880 
00881   *code = c = (CharCode)(*s & 0xff);
00882   *uLen = ctu->mapToUnicode(c, u, uSize);
00883   *dx = widths[c];
00884   *dy = *ox = *oy = 0;
00885   return 1;
00886 }
00887 
00888 CharCodeToUnicode *Gfx8BitFont::getToUnicode() {
00889   ctu->incRefCnt();
00890   return ctu;
00891 }
00892 
00893 Gushort *Gfx8BitFont::getCodeToGIDMap(FoFiTrueType *ff) {
00894   Gushort *map;
00895   int cmapPlatform, cmapEncoding;
00896   int unicodeCmap, macRomanCmap, msSymbolCmap, cmap;
00897   GBool useMacRoman, useUnicode;
00898   char *charName;
00899   Unicode u;
00900   int code, i, n;
00901 
00902   map = (Gushort *)gmalloc(256 * sizeof(Gushort));
00903   for (i = 0; i < 256; ++i) {
00904     map[i] = 0;
00905   }
00906 
00907   // To match up with the Adobe-defined behaviour, we choose a cmap
00908   // like this:
00909   // 1. If the PDF font has an encoding:
00910   //    1a. If the PDF font specified MacRomanEncoding and the
00911   //        TrueType font has a Macintosh Roman cmap, use it, and
00912   //        reverse map the char names through MacRomanEncoding to
00913   //        get char codes.
00914   //    1b. If the TrueType font has a Microsoft Unicode cmap or a
00915   //        non-Microsoft Unicode cmap, use it, and use the Unicode
00916   //        indexes, not the char codes.
00917   //    1c. If the PDF font is symbolic and the TrueType font has a
00918   //        Microsoft Symbol cmap, use it, and use char codes
00919   //        directly (possibly with an offset of 0xf000).
00920   //    1d. If the TrueType font has a Macintosh Roman cmap, use it,
00921   //        as in case 1a.
00922   // 2. If the PDF font does not have an encoding:
00923   //    2a. If the TrueType font has a Macintosh Roman cmap, use it,
00924   //        and use char codes directly (possibly with an offset of
00925   //        0xf000).
00926   //    2b. If the TrueType font has a Microsoft Symbol cmap, use it,
00927   //        and use char codes directly (possible with an offset of
00928   //        0xf000).
00929   // 3. If none of these rules apply, use the first cmap and hope for
00930   //    the best (this shouldn't happen).
00931   unicodeCmap = macRomanCmap = msSymbolCmap = -1;
00932   for (i = 0; i < ff->getNumCmaps(); ++i) {
00933     cmapPlatform = ff->getCmapPlatform(i);
00934     cmapEncoding = ff->getCmapEncoding(i);
00935     if ((cmapPlatform == 3 && cmapEncoding == 1) ||
00936        cmapPlatform == 0) {
00937       unicodeCmap = i;
00938     } else if (cmapPlatform == 1 && cmapEncoding == 0) {
00939       macRomanCmap = i;
00940     } else if (cmapPlatform == 3 && cmapEncoding == 0) {
00941       msSymbolCmap = i;
00942     }
00943   }
00944   cmap = 0;
00945   useMacRoman = gFalse;
00946   useUnicode = gFalse;
00947   if (hasEncoding) {
00948     if (usesMacRomanEnc && macRomanCmap >= 0) {
00949       cmap = macRomanCmap;
00950       useMacRoman = gTrue;
00951     } else if (unicodeCmap >= 0) {
00952       cmap = unicodeCmap;
00953       useUnicode = gTrue;
00954     } else if ((flags & fontSymbolic) && msSymbolCmap >= 0) {
00955       cmap = msSymbolCmap;
00956     } else if (macRomanCmap >= 0) {
00957       cmap = macRomanCmap;
00958       useMacRoman = gTrue;
00959     }
00960   } else {
00961     if (macRomanCmap >= 0) {
00962       cmap = macRomanCmap;
00963     } else if (msSymbolCmap >= 0) {
00964       cmap = msSymbolCmap;
00965     }
00966   }
00967 
00968   // reverse map the char names through MacRomanEncoding, then map the
00969   // char codes through the cmap
00970   if (useMacRoman) {
00971     for (i = 0; i < 256; ++i) {
00972       if ((charName = enc[i])) {
00973        if ((code = globalParams->getMacRomanCharCode(charName))) {
00974          map[i] = ff->mapCodeToGID(cmap, code);
00975        }
00976       }
00977     }
00978 
00979   // map Unicode through the cmap
00980   } else if (useUnicode) {
00981     for (i = 0; i < 256; ++i) {
00982       if ((n = ctu->mapToUnicode((CharCode)i, &u, 1))) {
00983        map[i] = ff->mapCodeToGID(cmap, u);
00984       }
00985     }
00986 
00987   // map the char codes through the cmap, possibly with an offset of
00988   // 0xf000
00989   } else {
00990     for (i = 0; i < 256; ++i) {
00991       if (!(map[i] = ff->mapCodeToGID(cmap, i))) {
00992        map[i] = ff->mapCodeToGID(cmap, 0xf000 + i);
00993       }
00994     }
00995   }
00996 
00997   // try the TrueType 'post' table to handle any unmapped characters
00998   for (i = 0; i < 256; ++i) {
00999     if (!map[i] && (charName = enc[i])) {
01000       map[i] = (Gushort)(int)ff->mapNameToGID(charName);
01001     }
01002   }
01003 
01004   return map;
01005 }
01006 
01007 Dict *Gfx8BitFont::getCharProcs() {
01008   return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL;
01009 }
01010 
01011 Object *Gfx8BitFont::getCharProc(int code, Object *proc) {
01012   if (enc[code] && charProcs.isDict()) {
01013     charProcs.dictLookup(enc[code], proc);
01014   } else {
01015     proc->initNull();
01016   }
01017   return proc;
01018 }
01019 
01020 Dict *Gfx8BitFont::getResources() {
01021   return resources.isDict() ? resources.getDict() : (Dict *)NULL;
01022 }
01023 
01024 //------------------------------------------------------------------------
01025 // GfxCIDFont
01026 //------------------------------------------------------------------------
01027 
01028 static int CDECL cmpWidthExcep(const void *w1, const void *w2) {
01029   return ((GfxFontCIDWidthExcep *)w1)->first -
01030          ((GfxFontCIDWidthExcep *)w2)->first;
01031 }
01032 
01033 static int CDECL cmpWidthExcepV(const void *w1, const void *w2) {
01034   return ((GfxFontCIDWidthExcepV *)w1)->first -
01035          ((GfxFontCIDWidthExcepV *)w2)->first;
01036 }
01037 
01038 GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA,
01039                      Dict *fontDict):
01040   GfxFont(tagA, idA, nameA)
01041 {
01042   Dict *desFontDict;
01043   GString *collection, *cMapName;
01044   Object desFontDictObj;
01045   Object obj1, obj2, obj3, obj4, obj5, obj6;
01046   int c1, c2;
01047   int excepsSize, i, j, k;
01048 
01049   ascent = 0.95;
01050   descent = -0.35;
01051   fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0;
01052   cMap = NULL;
01053   ctu = NULL;
01054   widths.defWidth = 1.0;
01055   widths.defHeight = -1.0;
01056   widths.defVY = 0.880;
01057   widths.exceps = NULL;
01058   widths.nExceps = 0;
01059   widths.excepsV = NULL;
01060   widths.nExcepsV = 0;
01061   cidToGID = NULL;
01062   cidToGIDLen = 0;
01063 
01064   // get the descendant font
01065   if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) {
01066     error(-1, "Missing DescendantFonts entry in Type 0 font");
01067     obj1.free();
01068     goto err1;
01069   }
01070   if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) {
01071     error(-1, "Bad descendant font in Type 0 font");
01072     goto err3;
01073   }
01074   obj1.free();
01075   desFontDict = desFontDictObj.getDict();
01076 
01077   // font type
01078   if (!desFontDict->lookup("Subtype", &obj1)) {
01079     error(-1, "Missing Subtype entry in Type 0 descendant font");
01080     goto err3;
01081   }
01082   if (obj1.isName("CIDFontType0")) {
01083     type = fontCIDType0;
01084   } else if (obj1.isName("CIDFontType2")) {
01085     type = fontCIDType2;
01086   } else {
01087     error(-1, "Unknown Type 0 descendant font type '%s'",
01088          obj1.isName() ? obj1.getName() : "???");
01089     goto err3;
01090   }
01091   obj1.free();
01092 
01093   // get info from font descriptor
01094   readFontDescriptor(xref, desFontDict);
01095 
01096   // look for an external font file
01097   findExtFontFile();
01098 
01099   //----- encoding info -----
01100 
01101   // char collection
01102   if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) {
01103     error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font");
01104     goto err3;
01105   }
01106   obj1.dictLookup("Registry", &obj2);
01107   obj1.dictLookup("Ordering", &obj3);
01108   if (!obj2.isString() || !obj3.isString()) {
01109     error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font");
01110     goto err4;
01111   }
01112   collection = obj2.getString()->copy()->append('-')->append(obj3.getString());
01113   obj3.free();
01114   obj2.free();
01115   obj1.free();
01116 
01117   // look for a ToUnicode CMap
01118   if (!(ctu = readToUnicodeCMap(fontDict, 16, NULL))) {
01119 
01120     // the "Adobe-Identity" and "Adobe-UCS" collections don't have
01121     // cidToUnicode files
01122     if (collection->cmp("Adobe-Identity") &&
01123        collection->cmp("Adobe-UCS")) {
01124 
01125       // look for a user-supplied .cidToUnicode file
01126       if (!(ctu = globalParams->getCIDToUnicode(collection))) {
01127        error(-1, "Unknown character collection '%s'",
01128              collection->getCString());
01129        delete collection;
01130        goto err2;
01131       }
01132     }
01133   }
01134 
01135   // encoding (i.e., CMap)
01136   //~ need to handle a CMap stream here
01137   //~ also need to deal with the UseCMap entry in the stream dict
01138   if (!fontDict->lookup("Encoding", &obj1)->isName()) {
01139     error(-1, "Missing or invalid Encoding entry in Type 0 font");
01140     delete collection;
01141     goto err3;
01142   }
01143   cMapName = new GString(obj1.getName());
01144   obj1.free();
01145   if (!(cMap = globalParams->getCMap(collection, cMapName))) {
01146     error(-1, "Unknown CMap '%s' for character collection '%s'",
01147          cMapName->getCString(), collection->getCString());
01148     delete collection;
01149     delete cMapName;
01150     goto err2;
01151   }
01152   delete collection;
01153   delete cMapName;
01154 
01155   // CIDToGIDMap (for embedded TrueType fonts)
01156   if (type == fontCIDType2) {
01157     desFontDict->lookup("CIDToGIDMap", &obj1);
01158     if (obj1.isStream()) {
01159       cidToGIDLen = 0;
01160       i = 64;
01161       cidToGID = (Gushort *)gmalloc(i * sizeof(Gushort));
01162       obj1.streamReset();
01163       while ((c1 = obj1.streamGetChar()) != EOF &&
01164             (c2 = obj1.streamGetChar()) != EOF) {
01165        if (cidToGIDLen == i) {
01166          i *= 2;
01167          cidToGID = (Gushort *)grealloc(cidToGID, i * sizeof(Gushort));
01168        }
01169        cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2);
01170       }
01171     } else if (!obj1.isName("Identity") && !obj1.isNull()) {
01172       error(-1, "Invalid CIDToGIDMap entry in CID font");
01173     }
01174     obj1.free();
01175   }
01176 
01177   //----- character metrics -----
01178 
01179   // default char width
01180   if (desFontDict->lookup("DW", &obj1)->isInt()) {
01181     widths.defWidth = obj1.getInt() * 0.001;
01182   }
01183   obj1.free();
01184 
01185   // char width exceptions
01186   if (desFontDict->lookup("W", &obj1)->isArray()) {
01187     excepsSize = 0;
01188     i = 0;
01189     while (i + 1 < obj1.arrayGetLength()) {
01190       obj1.arrayGet(i, &obj2);
01191       obj1.arrayGet(i + 1, &obj3);
01192       if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) {
01193        if (obj1.arrayGet(i + 2, &obj4)->isNum()) {
01194          if (widths.nExceps == excepsSize) {
01195            excepsSize += 16;
01196            widths.exceps = (GfxFontCIDWidthExcep *)
01197              grealloc(widths.exceps,
01198                      excepsSize * sizeof(GfxFontCIDWidthExcep));
01199          }
01200          widths.exceps[widths.nExceps].first = obj2.getInt();
01201          widths.exceps[widths.nExceps].last = obj3.getInt();
01202          widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
01203          ++widths.nExceps;
01204        } else {
01205          error(-1, "Bad widths array in Type 0 font");
01206        }
01207        obj4.free();
01208        i += 3;
01209       } else if (obj2.isInt() && obj3.isArray()) {
01210        if (widths.nExceps + obj3.arrayGetLength() > excepsSize) {
01211          excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15;
01212          widths.exceps = (GfxFontCIDWidthExcep *)
01213            grealloc(widths.exceps,
01214                    excepsSize * sizeof(GfxFontCIDWidthExcep));
01215        }
01216        j = obj2.getInt();
01217        for (k = 0; k < obj3.arrayGetLength(); ++k) {
01218          if (obj3.arrayGet(k, &obj4)->isNum()) {
01219            widths.exceps[widths.nExceps].first = j;
01220            widths.exceps[widths.nExceps].last = j;
01221            widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001;
01222            ++j;
01223            ++widths.nExceps;
01224          } else {
01225            error(-1, "Bad widths array in Type 0 font");
01226          }
01227          obj4.free();
01228        }
01229        i += 2;
01230       } else {
01231        error(-1, "Bad widths array in Type 0 font");
01232        ++i;
01233       }
01234       obj3.free();
01235       obj2.free();
01236     }
01237     qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep),
01238          &cmpWidthExcep);
01239   }
01240   obj1.free();
01241 
01242   // default metrics for vertical font
01243   if (desFontDict->lookup("DW2", &obj1)->isArray() &&
01244       obj1.arrayGetLength() == 2) {
01245     if (obj1.arrayGet(0, &obj2)->isNum()) {
01246       widths.defVY = obj2.getNum() * 0.001;
01247     }
01248     obj2.free();
01249     if (obj1.arrayGet(1, &obj2)->isNum()) {
01250       widths.defHeight = obj2.getNum() * 0.001;
01251     }
01252     obj2.free();
01253   }
01254   obj1.free();
01255 
01256   // char metric exceptions for vertical font
01257   if (desFontDict->lookup("W2", &obj1)->isArray()) {
01258     excepsSize = 0;
01259     i = 0;
01260     while (i + 1 < obj1.arrayGetLength()) {
01261       obj1.arrayGet(i, &obj2);
01262       obj1.arrayGet(i+ 1, &obj3);
01263       if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) {
01264        if (obj1.arrayGet(i + 2, &obj4)->isNum() &&
01265            obj1.arrayGet(i + 3, &obj5)->isNum() &&
01266            obj1.arrayGet(i + 4, &obj6)->isNum()) {
01267          if (widths.nExcepsV == excepsSize) {
01268            excepsSize += 16;
01269            widths.excepsV = (GfxFontCIDWidthExcepV *)
01270              grealloc(widths.excepsV,
01271                      excepsSize * sizeof(GfxFontCIDWidthExcepV));
01272          }
01273          widths.excepsV[widths.nExcepsV].first = obj2.getInt();
01274          widths.excepsV[widths.nExcepsV].last = obj3.getInt();
01275          widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001;
01276          widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001;
01277          widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001;
01278          ++widths.nExcepsV;
01279        } else {
01280          error(-1, "Bad widths (W2) array in Type 0 font");
01281        }
01282        obj6.free();
01283        obj5.free();
01284        obj4.free();
01285        i += 5;
01286       } else if (obj2.isInt() && obj3.isArray()) {
01287        if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) {
01288          excepsSize =
01289            (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15;
01290          widths.excepsV = (GfxFontCIDWidthExcepV *)
01291            grealloc(widths.excepsV,
01292                    excepsSize * sizeof(GfxFontCIDWidthExcepV));
01293        }
01294        j = obj2.getInt();
01295        for (k = 0; k < obj3.arrayGetLength(); k += 3) {
01296          if (obj3.arrayGet(k, &obj4)->isNum() &&
01297              obj3.arrayGet(k+1, &obj5)->isNum() &&
01298              obj3.arrayGet(k+2, &obj6)->isNum()) {
01299            widths.excepsV[widths.nExceps].first = j;
01300            widths.excepsV[widths.nExceps].last = j;
01301            widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001;
01302            widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001;
01303            widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001;
01304            ++j;
01305            ++widths.nExcepsV;
01306          } else {
01307            error(-1, "Bad widths (W2) array in Type 0 font");
01308          }
01309          obj6.free();
01310          obj5.free();
01311          obj4.free();
01312        }
01313        i += 2;
01314       } else {
01315        error(-1, "Bad widths (W2) array in Type 0 font");
01316        ++i;
01317       }
01318       obj3.free();
01319       obj2.free();
01320     }
01321     qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV),
01322          &cmpWidthExcepV);
01323   }
01324   obj1.free();
01325 
01326   desFontDictObj.free();
01327   ok = gTrue;
01328   return;
01329 
01330  err4:
01331   obj3.free();
01332   obj2.free();
01333  err3:
01334   obj1.free();
01335  err2:
01336   desFontDictObj.free();
01337  err1:;
01338 }
01339 
01340 GfxCIDFont::~GfxCIDFont() {
01341   if (cMap) {
01342     cMap->decRefCnt();
01343   }
01344   if (ctu) {
01345     ctu->decRefCnt();
01346   }
01347   gfree(widths.exceps);
01348   gfree(widths.excepsV);
01349   if (cidToGID) {
01350     gfree(cidToGID);
01351   }
01352 }
01353 
01354 int GfxCIDFont::getNextChar(char *s, int len, CharCode *code,
01355                          Unicode *u, int uSize, int *uLen,
01356                          double *dx, double *dy, double *ox, double *oy) {
01357   CID cid;
01358   double w, h, vx, vy;
01359   int n, a, b, m;
01360 
01361   if (!cMap) {
01362     *code = 0;
01363     *uLen = 0;
01364     *dx = *dy = 0;
01365     return 1;
01366   }
01367 
01368   *code = (CharCode)(cid = cMap->getCID(s, len, &n));
01369   if (ctu) {
01370     *uLen = ctu->mapToUnicode(cid, u, uSize);
01371   } else {
01372     *uLen = 0;
01373   }
01374 
01375   // horizontal
01376   if (cMap->getWMode() == 0) {
01377     w = widths.defWidth;
01378     h = vx = vy = 0;
01379     if (widths.nExceps > 0 && cid >= widths.exceps[0].first) {
01380       a = 0;
01381       b = widths.nExceps;
01382       // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first
01383       while (b - a > 1) {
01384        m = (a + b) / 2;
01385        if (widths.exceps[m].first <= cid) {
01386          a = m;
01387        } else {
01388          b = m;
01389        }
01390       }
01391       if (cid <= widths.exceps[a].last) {
01392        w = widths.exceps[a].width;
01393       }
01394     }
01395 
01396   // vertical
01397   } else {
01398     w = 0;
01399     h = widths.defHeight;
01400     vx = widths.defWidth / 2;
01401     vy = widths.defVY;
01402     if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) {
01403       a = 0;
01404       b = widths.nExcepsV;
01405       // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first
01406       while (b - a > 1) {
01407        m = (a + b) / 2;
01408        if (widths.excepsV[m].last <= cid) {
01409          a = m;
01410        } else {
01411          b = m;
01412        }
01413       }
01414       if (cid <= widths.excepsV[a].last) {
01415        h = widths.excepsV[a].height;
01416        vx = widths.excepsV[a].vx;
01417        vy = widths.excepsV[a].vy;
01418       }
01419     }
01420   }
01421 
01422   *dx = w;
01423   *dy = h;
01424   *ox = vx;
01425   *oy = vy;
01426 
01427   return n;
01428 }
01429 
01430 int GfxCIDFont::getWMode() {
01431   return cMap ? cMap->getWMode() : 0;
01432 }
01433 
01434 CharCodeToUnicode *GfxCIDFont::getToUnicode() {
01435   if (ctu) {
01436     ctu->incRefCnt();
01437   }
01438   return ctu;
01439 }
01440 
01441 GString *GfxCIDFont::getCollection() {
01442   return cMap ? cMap->getCollection() : (GString *)NULL;
01443 }
01444 
01445 //------------------------------------------------------------------------
01446 // GfxFontDict
01447 //------------------------------------------------------------------------
01448 
01449 GfxFontDict::GfxFontDict(XRef *xref, Ref *fontDictRef, Dict *fontDict) {
01450   int i;
01451   Object obj1, obj2;
01452   Ref r;
01453 
01454   numFonts = fontDict->getLength();
01455   fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *));
01456   for (i = 0; i < numFonts; ++i) {
01457     fontDict->getValNF(i, &obj1);
01458     obj1.fetch(xref, &obj2);
01459     if (obj2.isDict()) {
01460       if (obj1.isRef()) {
01461        r = obj1.getRef();
01462       } else {
01463        // no indirect reference for this font, so invent a unique one
01464        // (legal generation numbers are five digits, so any 6-digit
01465        // number would be safe)
01466        r.num = i;
01467        if (fontDictRef) {
01468          r.gen = 100000 + fontDictRef->num;
01469        } else {
01470          r.gen = 999999;
01471        }
01472       }
01473       fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i),
01474                                r, obj2.getDict());
01475       if (fonts[i] && !fonts[i]->isOk()) {
01476        delete fonts[i];
01477        fonts[i] = NULL;
01478       }
01479     } else {
01480       error(-1, "font resource is not a dictionary");
01481       fonts[i] = NULL;
01482     }
01483     obj1.free();
01484     obj2.free();
01485   }
01486 }
01487 
01488 GfxFontDict::~GfxFontDict() {
01489   int i;
01490 
01491   for (i = 0; i < numFonts; ++i) {
01492     if (fonts[i]) {
01493       delete fonts[i];
01494     }
01495   }
01496   gfree(fonts);
01497 }
01498 
01499 GfxFont *GfxFontDict::lookup(char *tag) {
01500   int i;
01501 
01502   for (i = 0; i < numFonts; ++i) {
01503     if (fonts[i] && fonts[i]->matches(tag)) {
01504       return fonts[i];
01505     }
01506   }
01507   return NULL;
01508 }