filters

GfxFont.cc

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