From 2a393c134fe3fe8eb85bf818cb7ad6ae4396322a Mon Sep 17 00:00:00 2001 From: Martin Kretzschmar Date: Wed, 18 Sep 2002 22:20:42 +0000 Subject: Synched with Xpdf 1.01 --- (limited to 'pdf/xpdf/GfxFont.cc') diff --git a/pdf/xpdf/GfxFont.cc b/pdf/xpdf/GfxFont.cc index 16b311b..8dcd8e7 100644 --- a/pdf/xpdf/GfxFont.cc +++ b/pdf/xpdf/GfxFont.cc @@ -2,7 +2,7 @@ // // GfxFont.cc // -// Copyright 1996 Derek B. Noonburg +// Copyright 1996-2002 Glyph & Cog, LLC // //======================================================================== @@ -10,471 +10,593 @@ #pragma implementation #endif -#include -#include +#include #include +#include #include #include -#include "GString.h" #include "gmem.h" -#include "gfile.h" -#include "config.h" +#include "Error.h" #include "Object.h" -#include "Array.h" #include "Dict.h" -#include "Error.h" -#include "Params.h" +#include "GlobalParams.h" +#include "CMap.h" +#include "CharCodeToUnicode.h" +#include "FontEncodingTables.h" +#include "BuiltinFontTables.h" #include "FontFile.h" #include "GfxFont.h" -#include "FontInfo.h" -#if JAPANESE_SUPPORT -#include "Japan12CMapInfo.h" -#endif -#if CHINESE_GB_SUPPORT -#include "GB12CMapInfo.h" -#endif -#if CHINESE_CNS_SUPPORT -#include "CNS13CMapInfo.h" -#endif - //------------------------------------------------------------------------ -static int CDECL cmpWidthExcep(const void *w1, const void *w2); -static int CDECL cmpWidthExcepV(const void *w1, const void *w2); - -//------------------------------------------------------------------------ +struct StdFontMapEntry { + char *altName; + char *properName; +}; -static Gushort *defCharWidths[12] = { - courierWidths, - courierObliqueWidths, - courierBoldWidths, - courierBoldObliqueWidths, - helveticaWidths, - helveticaObliqueWidths, - helveticaBoldWidths, - helveticaBoldObliqueWidths, - timesRomanWidths, - timesItalicWidths, - timesBoldWidths, - timesBoldItalicWidths +static StdFontMapEntry stdFontMap[] = { + { "Arial", "Helvetica" }, + { "Arial,Bold", "Helvetica-Bold" }, + { "Arial,BoldItalic", "Helvetica-BoldOblique" }, + { "Arial,Italic", "Helvetica-Oblique" }, + { "Arial-Bold", "Helvetica-Bold" }, + { "Arial-BoldItalic", "Helvetica-BoldOblique" }, + { "Arial-BoldItalicMT", "Helvetica-BoldOblique" }, + { "Arial-BoldMT", "Helvetica-Bold" }, + { "Arial-Italic", "Helvetica-Oblique" }, + { "Arial-ItalicMT", "Helvetica-Oblique" }, + { "ArialMT", "Helvetica" }, + { "Courier,Bold", "Courier-Bold" }, + { "Courier,Italic", "Courier-Oblique" }, + { "Courier,BoldItalic", "Courier-BoldOblique" }, + { "CourierNew", "Courier" }, + { "CourierNew,Bold", "Courier-Bold" }, + { "CourierNew,BoldItalic", "Courier-BoldOblique" }, + { "CourierNew,Italic", "Courier-Oblique" }, + { "CourierNew-Bold", "Courier-Bold" }, + { "CourierNew-BoldItalic", "Courier-BoldOblique" }, + { "CourierNew-Italic", "Courier-Oblique" }, + { "CourierNewPS-BoldItalicMT", "Courier-BoldOblique" }, + { "CourierNewPS-BoldMT", "Courier-Bold" }, + { "CourierNewPS-ItalicMT", "Courier-Oblique" }, + { "CourierNewPSMT", "Courier" }, + { "Helvetica,Bold", "Helvetica-Bold" }, + { "Helvetica,BoldItalic", "Helvetica-BoldOblique" }, + { "Helvetica,Italic", "Helvetica-Oblique" }, + { "Helvetica-BoldItalic", "Helvetica-BoldOblique" }, + { "Helvetica-Italic", "Helvetica-Oblique" }, + { "TimesNewRoman", "Times-Roman" }, + { "TimesNewRoman,Bold", "Times-Bold" }, + { "TimesNewRoman,BoldItalic", "Times-BoldItalic" }, + { "TimesNewRoman,Italic", "Times-Italic" }, + { "TimesNewRoman-Bold", "Times-Bold" }, + { "TimesNewRoman-BoldItalic", "Times-BoldItalic" }, + { "TimesNewRoman-Italic", "Times-Italic" }, + { "TimesNewRomanPS", "Times-Roman" }, + { "TimesNewRomanPS-Bold", "Times-Bold" }, + { "TimesNewRomanPS-BoldItalic", "Times-BoldItalic" }, + { "TimesNewRomanPS-BoldItalicMT", "Times-BoldItalic" }, + { "TimesNewRomanPS-BoldMT", "Times-Bold" }, + { "TimesNewRomanPS-Italic", "Times-Italic" }, + { "TimesNewRomanPS-ItalicMT", "Times-Italic" }, + { "TimesNewRomanPSMT", "Times-Roman" } }; //------------------------------------------------------------------------ // GfxFont //------------------------------------------------------------------------ -GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) { - BuiltinFont *builtinFont; - Object obj1, obj2, obj3, obj4; - int missingWidth; - char *name2, *p; - int i; +GfxFont *GfxFont::makeFont(XRef *xref, char *tagA, Ref idA, Dict *fontDict) { + GString *nameA; + GfxFont *font; + Object obj1; - // get font tag and ID - tag = new GString(tag1); - id = id1; + // get base font name + nameA = NULL; + fontDict->lookup("BaseFont", &obj1); + if (obj1.isName()) { + nameA = new GString(obj1.getName()); + } + obj1.free(); // get font type - type = fontUnknownType; + font = NULL; fontDict->lookup("Subtype", &obj1); - if (obj1.isName("Type1")) - type = fontType1; - else if (obj1.isName("Type1C")) - type = fontType1C; - else if (obj1.isName("Type3")) - type = fontType3; - else if (obj1.isName("TrueType")) - type = fontTrueType; - else if (obj1.isName("Type0")) - type = fontType0; + if (obj1.isName("Type1") || obj1.isName("MMType1")) { + font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1, fontDict); + } else if (obj1.isName("Type1C")) { + font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType1C, fontDict); + } else if (obj1.isName("Type3")) { + font = new Gfx8BitFont(xref, tagA, idA, nameA, fontType3, fontDict); + } else if (obj1.isName("TrueType")) { + font = new Gfx8BitFont(xref, tagA, idA, nameA, fontTrueType, fontDict); + } else if (obj1.isName("Type0")) { + font = new GfxCIDFont(xref, tagA, idA, nameA, fontDict); + } else { + error(-1, "Unknown font type: '%s'", + obj1.isName() ? obj1.getName() : "???"); + font = new Gfx8BitFont(xref, tagA, idA, nameA, fontUnknownType, fontDict); + } obj1.free(); - is16 = gFalse; - // get base font name - name = NULL; - fontDict->lookup("BaseFont", &obj1); - if (obj1.isName()) - name = new GString(obj1.getName()); - obj1.free(); + return font; +} - // Newer Adobe tools are using Base14-compatible TrueType fonts - // without embedding them, so munge the names into the equivalent - // PostScript names. This is a kludge -- it would be nice if Adobe - // followed their own spec. - if (type == fontTrueType) { - p = name->getCString(); - name2 = NULL; - if (!strncmp(p, "Arial", 5)) { - if (!strcmp(p+5, ",Bold")) { - name2 = "Helvetica-Bold"; - } else if (!strcmp(p+5, ",Italic")) { - name2 = "Helvetica-Oblique"; - } else if (!strcmp(p+5, ",BoldItalic")) { - name2 = "Helvetica-BoldOblique"; - } else { - name2 = "Helvetica"; - } - } else if (!strncmp(p, "TimesNewRoman", 13)) { - if (!strcmp(p+13, ",Bold")) { - name2 = "Times-Bold"; - } else if (!strcmp(p+13, ",Italic")) { - name2 = "Times-Italic"; - } else if (!strcmp(p+13, ",BoldItalic")) { - name2 = "Times-BoldItalic"; - } else { - name2 = "Times-Roman"; - } - } else if (!strncmp(p, "CourierNew", 10)) { - if (!strcmp(p+10, ",Bold")) { - name2 = "Courier-Bold"; - } else if (!strcmp(p+10, ",Italic")) { - name2 = "Courier-Oblique"; - } else if (!strcmp(p+10, ",BoldItalic")) { - name2 = "Courier-BoldOblique"; - } else { - name2 = "Courier"; - } - } - if (name2) { - delete name; - name = new GString(name2); - } - } +GfxFont::GfxFont(char *tagA, Ref idA, GString *nameA) { + ok = gFalse; + tag = new GString(tagA); + id = idA; + name = nameA; + embFontName = NULL; + extFontFile = NULL; +} - // is it a built-in font? - builtinFont = NULL; +GfxFont::~GfxFont() { + delete tag; if (name) { - for (i = 0; i < numBuiltinFonts; ++i) { - if (!strcmp(builtinFonts[i].name, name->getCString())) { - builtinFont = &builtinFonts[i]; - break; - } - } + delete name; + } + if (embFontName) { + delete embFontName; } + if (extFontFile) { + delete extFontFile; + } +} + +void GfxFont::readFontDescriptor(XRef *xref, Dict *fontDict) { + Object obj1, obj2, obj3, obj4; + double t; + int i; // assume Times-Roman by default (for substitution purposes) flags = fontSerif; - // get info from font descriptor - embFontName = NULL; embFontID.num = -1; embFontID.gen = -1; missingWidth = 0; - fontDict->lookup("FontDescriptor", &obj1); - if (obj1.isDict()) { + + if (fontDict->lookup("FontDescriptor", &obj1)->isDict()) { // get flags - obj1.dictLookup("Flags", &obj2); - if (obj2.isInt()) + if (obj1.dictLookup("Flags", &obj2)->isInt()) { flags = obj2.getInt(); + } obj2.free(); // get name obj1.dictLookup("FontName", &obj2); - if (obj2.isName()) + if (obj2.isName()) { embFontName = new GString(obj2.getName()); + } obj2.free(); // look for embedded font file - if (type == fontType1) { - obj1.dictLookupNF("FontFile", &obj2); - if (obj2.isRef()) + if (obj1.dictLookupNF("FontFile", &obj2)->isRef()) { + if (type == fontType1) { embFontID = obj2.getRef(); - obj2.free(); + } else { + error(-1, "Mismatch between font type and embedded font file"); + } } - if (embFontID.num == -1 && type == fontTrueType) { - obj1.dictLookupNF("FontFile2", &obj2); - if (obj2.isRef()) + obj2.free(); + if (embFontID.num == -1 && + obj1.dictLookupNF("FontFile2", &obj2)->isRef()) { + if (type == fontTrueType || type == fontCIDType2) { embFontID = obj2.getRef(); - obj2.free(); + } else { + error(-1, "Mismatch between font type and embedded font file"); + } } - if (embFontID.num == -1) { - obj1.dictLookupNF("FontFile3", &obj2); - if (obj2.isRef()) { - embFontID = obj2.getRef(); - obj2.fetch(&obj3); - if (obj3.isStream()) { - obj3.streamGetDict()->lookup("Subtype", &obj4); - if (obj4.isName("Type1")) - type = fontType1; - else if (obj4.isName("Type1C")) + obj2.free(); + if (embFontID.num == -1 && + obj1.dictLookupNF("FontFile3", &obj2)->isRef()) { + if (obj2.fetch(xref, &obj3)->isStream()) { + obj3.streamGetDict()->lookup("Subtype", &obj4); + if (obj4.isName("Type1")) { + if (type == fontType1) { + embFontID = obj2.getRef(); + } else { + error(-1, "Mismatch between font type and embedded font file"); + } + } else if (obj4.isName("Type1C")) { + if (type == fontType1) { type = fontType1C; - else if (obj4.isName("Type3")) - type = fontType3; - else if (obj4.isName("TrueType")) - type = fontTrueType; - else if (obj4.isName("Type0")) - type = fontType0; - obj4.free(); + embFontID = obj2.getRef(); + } else if (type == fontType1C) { + embFontID = obj2.getRef(); + } else { + error(-1, "Mismatch between font type and embedded font file"); + } + } else if (obj4.isName("TrueType")) { + if (type == fontTrueType) { + embFontID = obj2.getRef(); + } else { + error(-1, "Mismatch between font type and embedded font file"); + } + } else if (obj4.isName("CIDFontType0C")) { + if (type == fontCIDType0) { + type = fontCIDType0C; + embFontID = obj2.getRef(); + } else { + error(-1, "Mismatch between font type and embedded font file"); + } + } else { + error(-1, "Unknown embedded font type '%s'", + obj4.isName() ? obj4.getName() : "???"); } - obj3.free(); + obj4.free(); } - obj2.free(); + obj3.free(); } + obj2.free(); // look for MissingWidth obj1.dictLookup("MissingWidth", &obj2); - if (obj2.isInt()) { - missingWidth = obj2.getInt(); + if (obj2.isNum()) { + missingWidth = obj2.getNum(); } obj2.free(); - } - obj1.free(); - // get Type3 font definition - if (type == fontType3) { - fontDict->lookup("CharProcs", &charProcs); - if (!charProcs.isDict()) { - error(-1, "Missing or invalid CharProcs dictionary in Type 3 font"); - charProcs.free(); + // get Ascent and Descent + obj1.dictLookup("Ascent", &obj2); + if (obj2.isNum()) { + t = 0.001 * obj2.getNum(); + // some broken font descriptors set ascent and descent to 0 + if (t != 0) { + ascent = t; + } } - } - - // look for an external font file - extFontFile = NULL; - if (type == fontType1 && name) - findExtFontFile(); + obj2.free(); + obj1.dictLookup("Descent", &obj2); + if (obj2.isNum()) { + t = 0.001 * obj2.getNum(); + // some broken font descriptors set ascent and descent to 0 + if (t != 0) { + descent = t; + } + } + obj2.free(); - // get font matrix - fontMat[0] = fontMat[3] = 1; - fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; - if (fontDict->lookup("FontMatrix", &obj1)->isArray()) { - for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) { - if (obj1.arrayGet(i, &obj2)->isNum()) - fontMat[i] = obj2.getNum(); - obj2.free(); + // font FontBBox + if (obj1.dictLookup("FontBBox", &obj2)->isArray()) { + for (i = 0; i < 4 && i < obj2.arrayGetLength(); ++i) { + if (obj2.arrayGet(i, &obj3)->isNum()) { + fontBBox[i] = 0.001 * obj3.getNum(); + } + obj3.free(); + } } + obj2.free(); + } obj1.free(); +} - // get encoding and character widths - if (type == fontType0) { - getType0EncAndWidths(fontDict); - } else { - getEncAndWidths(fontDict, builtinFont, missingWidth); +CharCodeToUnicode *GfxFont::readToUnicodeCMap(Dict *fontDict, int nBits) { + CharCodeToUnicode *ctu; + GString *buf; + Object obj1; + int c; + + if (!fontDict->lookup("ToUnicode", &obj1)->isStream()) { + obj1.free(); + return NULL; } + buf = new GString(); + obj1.streamReset(); + while ((c = obj1.streamGetChar()) != EOF) { + buf->append(c); + } + obj1.streamClose(); + obj1.free(); + ctu = CharCodeToUnicode::parseCMap(buf, nBits); + delete buf; + return ctu; } -GfxFont::~GfxFont() { - delete tag; +void GfxFont::findExtFontFile() { if (name) { - delete name; - } - if (!is16 && encoding) { - delete encoding; - } - if (embFontName) { - delete embFontName; - } - if (extFontFile) { - delete extFontFile; + if (type == fontType1) { + extFontFile = globalParams->findFontFile(name, ".pfa", ".pfb"); + } else if (type == fontTrueType) { + extFontFile = globalParams->findFontFile(name, ".ttf", NULL); + } } - if (charProcs.isDict()) { - charProcs.free(); +} + +char *GfxFont::readExtFontFile(int *len) { + FILE *f; + char *buf; + + if (!(f = fopen(extFontFile->getCString(), "rb"))) { + error(-1, "External font file '%s' vanished", extFontFile->getCString()); + return NULL; } - if (is16) { - gfree(widths16.exceps); - gfree(widths16.excepsV); + fseek(f, 0, SEEK_END); + *len = (int)ftell(f); + fseek(f, 0, SEEK_SET); + buf = (char *)gmalloc(*len); + if ((int)fread(buf, 1, *len, f) != *len) { + error(-1, "Error reading external font file '%s'", extFontFile); } + fclose(f); + return buf; } -double GfxFont::getWidth(GString *s) { - double w; - int i; +char *GfxFont::readEmbFontFile(XRef *xref, int *len) { + char *buf; + Object obj1, obj2; + Stream *str; + int c; + int size, i; - w = 0; - for (i = 0; i < s->getLength(); ++i) - w += widths[s->getChar(i) & 0xff]; - return w; -} + obj1.initRef(embFontID.num, embFontID.gen); + obj1.fetch(xref, &obj2); + if (!obj2.isStream()) { + error(-1, "Embedded font file is not a stream"); + obj2.free(); + obj1.free(); + embFontID.num = -1; + return NULL; + } + str = obj2.getStream(); -double GfxFont::getWidth16(int c) { - double w; - int a, b, m; - - w = widths16.defWidth; - a = -1; - b = widths16.numExceps; - // invariant: widths16.exceps[a].last < c < widths16.exceps[b].first - while (b - a > 1) { - m = (a + b) / 2; - if (widths16.exceps[m].last < c) { - a = m; - } else if (c < widths16.exceps[m].first) { - b = m; - } else { - w = widths16.exceps[m].width; - break; + buf = NULL; + i = size = 0; + str->reset(); + while ((c = str->getChar()) != EOF) { + if (i == size) { + size += 4096; + buf = (char *)grealloc(buf, size); } + buf[i++] = c; } - return w; + *len = i; + str->close(); + + obj2.free(); + obj1.free(); + + return buf; } -double GfxFont::getHeight16(int c) { - double h; - int a, b, m; - - h = widths16.defHeight; - a = -1; - b = widths16.numExcepsV; - // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first - while (b - a > 1) { - m = (a + b) / 2; - if (widths16.excepsV[m].last < c) { - a = m; - } else if (c < widths16.excepsV[m].first) { - b = m; - } else { - h = widths16.excepsV[m].height; - break; +//------------------------------------------------------------------------ +// Gfx8BitFont +//------------------------------------------------------------------------ + +Gfx8BitFont::Gfx8BitFont(XRef *xref, char *tagA, Ref idA, GString *nameA, + GfxFontType typeA, Dict *fontDict): + GfxFont(tagA, idA, nameA) +{ + BuiltinFont *builtinFont; + char **baseEnc; + GBool baseEncFromFontFile; + char *buf; + int len; + FontFile *fontFile; + int code, code2; + char *charName; + GBool missing, hex; + Unicode toUnicode[256]; + double mul; + int firstChar, lastChar; + Gushort w; + Object obj1, obj2, obj3; + int n, i, a, b, m; + + type = typeA; + ctu = NULL; + + // Acrobat 4.0 and earlier substituted Base14-compatible fonts + // without providing Widths and a FontDescriptor, so we munge the + // names into the proper Base14 names. (This table is from + // implementation note 44 in the PDF 1.4 spec.) + if (name) { + a = 0; + b = sizeof(stdFontMap) / sizeof(StdFontMapEntry); + // invariant: stdFontMap[a].altName <= name < stdFontMap[b].altName + while (b - a > 1) { + m = (a + b) / 2; + if (name->cmp(stdFontMap[m].altName) >= 0) { + a = m; + } else { + b = m; + } + } + if (!name->cmp(stdFontMap[a].altName)) { + delete name; + name = new GString(stdFontMap[a].properName); } } - return h; -} -double GfxFont::getOriginX16(int c) { - double vx; - int a, b, m; - - vx = widths16.defWidth / 2; - a = -1; - b = widths16.numExcepsV; - // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first - while (b - a > 1) { - m = (a + b) / 2; - if (widths16.excepsV[m].last < c) { - a = m; - } else if (c < widths16.excepsV[m].first) { - b = m; - } else { - vx = widths16.excepsV[m].vx; - break; + // is it a built-in font? + builtinFont = NULL; + if (name) { + for (i = 0; i < nBuiltinFonts; ++i) { + if (!name->cmp(builtinFonts[i].name)) { + builtinFont = &builtinFonts[i]; + break; + } } } - return vx; -} -double GfxFont::getOriginY16(int c) { - double vy; - int a, b, m; - - vy = widths16.defVY; - a = -1; - b = widths16.numExcepsV; - // invariant: widths16.excepsV[a].last < c < widths16.excepsV[b].first - while (b - a > 1) { - m = (a + b) / 2; - if (widths16.excepsV[m].last < c) { - a = m; - } else if (c < widths16.excepsV[m].first) { - b = m; - } else { - vy = widths16.excepsV[m].vy; - break; + // default ascent/descent values + if (builtinFont) { + ascent = 0.001 * builtinFont->ascent; + descent = 0.001 * builtinFont->descent; + fontBBox[0] = 0.001 * builtinFont->bbox[0]; + fontBBox[1] = 0.001 * builtinFont->bbox[1]; + fontBBox[2] = 0.001 * builtinFont->bbox[2]; + fontBBox[3] = 0.001 * builtinFont->bbox[3]; + } else { + ascent = 0.95; + descent = -0.35; + fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; + } + + // get info from font descriptor + readFontDescriptor(xref, fontDict); + + // look for an external font file + findExtFontFile(); + + // get font matrix + fontMat[0] = fontMat[3] = 1; + fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; + if (fontDict->lookup("FontMatrix", &obj1)->isArray()) { + for (i = 0; i < 6 && i < obj1.arrayGetLength(); ++i) { + if (obj1.arrayGet(i, &obj2)->isNum()) { + fontMat[i] = obj2.getNum(); + } + obj2.free(); } } - return vy; -} + obj1.free(); -Object *GfxFont::getCharProc(int code, Object *proc) { - if (charProcs.isDict()) { - charProcs.dictLookup(encoding->getCharName(code), proc); - } else { - proc->initNull(); + // get Type 3 bounding box, font definition, and resources + if (type == fontType3) { + if (fontDict->lookup("FontBBox", &obj1)->isArray()) { + for (i = 0; i < 4 && i < obj1.arrayGetLength(); ++i) { + if (obj1.arrayGet(i, &obj2)->isNum()) { + fontBBox[i] = obj2.getNum(); + } + obj2.free(); + } + } + obj1.free(); + if (!fontDict->lookup("CharProcs", &charProcs)->isDict()) { + error(-1, "Missing or invalid CharProcs dictionary in Type 3 font"); + charProcs.free(); + } + if (!fontDict->lookup("Resources", &resources)->isDict()) { + resources.free(); + } } - return proc; -} -void GfxFont::getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont, - int missingWidth) { - Object obj1, obj2, obj3; - char *buf; - int len; - FontFile *fontFile; - int code, i; + //----- build the font encoding ----- // Encodings start with a base encoding, which can come from // (in order of priority): // 1. FontDict.Encoding or FontDict.Encoding.BaseEncoding - // - MacRoman / WinAnsi / Standard - // 2. embedded font file + // - MacRoman / MacExpert / WinAnsi / Standard + // 2. embedded or external font file // 3. default: // - builtin --> builtin encoding // - TrueType --> MacRomanEncoding // - others --> StandardEncoding - // and then add a list of differences from + // and then add a list of differences (if any) from // FontDict.Encoding.Differences. // check FontDict for base encoding - encoding = NULL; + hasEncoding = gFalse; + baseEnc = NULL; + baseEncFromFontFile = gFalse; fontDict->lookup("Encoding", &obj1); if (obj1.isDict()) { obj1.dictLookup("BaseEncoding", &obj2); if (obj2.isName("MacRomanEncoding")) { - encoding = macRomanEncoding.copy(); + hasEncoding = gTrue; + baseEnc = macRomanEncoding; + } else if (obj2.isName("MacExpertEncoding")) { + hasEncoding = gTrue; + baseEnc = macExpertEncoding; } else if (obj2.isName("WinAnsiEncoding")) { - encoding = winAnsiEncoding.copy(); + hasEncoding = gTrue; + baseEnc = winAnsiEncoding; } else if (obj2.isName("StandardEncoding")) { - encoding = standardEncoding.copy(); + hasEncoding = gTrue; + baseEnc = standardEncoding; } obj2.free(); } else if (obj1.isName("MacRomanEncoding")) { - encoding = macRomanEncoding.copy(); + hasEncoding = gTrue; + baseEnc = macRomanEncoding; + } else if (obj1.isName("MacExpertEncoding")) { + hasEncoding = gTrue; + baseEnc = macExpertEncoding; } else if (obj1.isName("WinAnsiEncoding")) { - encoding = winAnsiEncoding.copy(); + hasEncoding = gTrue; + baseEnc = winAnsiEncoding; } else if (obj1.isName("StandardEncoding")) { - encoding = standardEncoding.copy(); + hasEncoding = gTrue; + baseEnc = standardEncoding; } - obj1.free(); // check embedded or external font file for base encoding + // (only for Type 1 fonts - trying to get an encoding out of a + // TrueType font is a losing proposition) + fontFile = NULL; + buf = NULL; if ((type == fontType1 || type == fontType1C) && (extFontFile || embFontID.num >= 0)) { - if (extFontFile) + if (extFontFile) { buf = readExtFontFile(&len); - else - buf = readEmbFontFile(&len); + } else { + buf = readEmbFontFile(xref, &len); + } if (buf) { - if (type == fontType1) + if (type == fontType1C && !strncmp(buf, "%!", 2)) { + // various tools (including Adobe's) occasionally embed Type 1 + // fonts but label them Type 1C + type = fontType1; + } + if (type == fontType1) { fontFile = new Type1FontFile(buf, len); - else + } else { fontFile = new Type1CFontFile(buf, len); + } if (fontFile->getName()) { - if (embFontName) + if (embFontName) { delete embFontName; + } embFontName = new GString(fontFile->getName()); } - if (!encoding) - encoding = fontFile->getEncoding(gTrue); - delete fontFile; + if (!baseEnc) { + baseEnc = fontFile->getEncoding(); + baseEncFromFontFile = gTrue; + } gfree(buf); } } // get default base encoding - if (!encoding) { - if (builtinFont) - encoding = builtinFont->encoding->copy(); - else if (type == fontTrueType) - encoding = macRomanEncoding.copy(); - else - encoding = standardEncoding.copy(); + if (!baseEnc) { + if (builtinFont) { + baseEnc = builtinFont->defaultBaseEnc; + } else if (type == fontTrueType) { + baseEnc = macRomanEncoding; + } else { + baseEnc = standardEncoding; + } + } + + // copy the base encoding + for (i = 0; i < 256; ++i) { + enc[i] = baseEnc[i]; + if ((encFree[i] = baseEncFromFontFile) && enc[i]) { + enc[i] = copyString(baseEnc[i]); + } } // merge differences into encoding - fontDict->lookup("Encoding", &obj1); if (obj1.isDict()) { obj1.dictLookup("Differences", &obj2); if (obj2.isArray()) { + hasEncoding = gTrue; code = 0; for (i = 0; i < obj2.arrayGetLength(); ++i) { obj2.arrayGet(i, &obj3); if (obj3.isInt()) { code = obj3.getInt(); } else if (obj3.isName()) { - if (code < 256) - encoding->addChar(code, copyString(obj3.getName())); + if (code < 256) { + if (encFree[code]) { + gfree(enc[code]); + } + enc[code] = copyString(obj3.getName()); + encFree[code] = gTrue; + } ++code; } else { error(-1, "Wrong type in font encoding resource differences (%s)", @@ -486,499 +608,636 @@ void GfxFont::getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont, obj2.free(); } obj1.free(); - - // get character widths - if (builtinFont) - makeWidths(fontDict, builtinFont->encoding, builtinFont->widths, - missingWidth); - else - makeWidths(fontDict, NULL, NULL, missingWidth); -} - -void GfxFont::findExtFontFile() { - char **path; - FILE *f; - - for (path = fontPath; *path; ++path) { - extFontFile = appendToPath(new GString(*path), name->getCString()); - f = fopen(extFontFile->getCString(), "rb"); - if (!f) { - extFontFile->append(".pfb"); - f = fopen(extFontFile->getCString(), "rb"); - } - if (!f) { - extFontFile->del(extFontFile->getLength() - 4, 4); - extFontFile->append(".pfa"); - f = fopen(extFontFile->getCString(), "rb"); - } - if (f) { - fclose(f); - break; - } - delete extFontFile; - extFontFile = NULL; + if (fontFile) { + delete fontFile; } -} -char *GfxFont::readExtFontFile(int *len) { - FILE *f; - char *buf; + //----- build the mapping to Unicode ----- - if (!(f = fopen(extFontFile->getCString(), "rb"))) { - error(-1, "Internal: external font file '%s' vanished", extFontFile); - return NULL; - } - fseek(f, 0, SEEK_END); - *len = (int)ftell(f); - fseek(f, 0, SEEK_SET); - buf = (char *)gmalloc(*len); - if ((int)fread(buf, 1, *len, f) != *len) - error(-1, "Error reading external font file '%s'", extFontFile); - fclose(f); - return buf; -} + // look for a ToUnicode CMap + if (!(ctu = readToUnicodeCMap(fontDict, 8))) { -char *GfxFont::readEmbFontFile(int *len) { - char *buf; - Object obj1, obj2; - Stream *str; - int c; - int size, i; + // no ToUnicode CMap, so use the char names - obj1.initRef(embFontID.num, embFontID.gen); - obj1.fetch(&obj2); - if (!obj2.isStream()) { - error(-1, "Embedded font file is not a stream"); - obj2.free(); - obj1.free(); - embFontID.num = -1; - return NULL; - } - str = obj2.getStream(); - - buf = NULL; - i = size = 0; - str->reset(); - while ((c = str->getChar()) != EOF) { - if (i == size) { - size += 4096; - buf = (char *)grealloc(buf, size); + // pass 1: use the name-to-Unicode mapping table + missing = hex = gFalse; + for (code = 0; code < 256; ++code) { + if ((charName = enc[code])) { + if (!(toUnicode[code] = globalParams->mapNameToUnicode(charName)) && + strcmp(charName, ".notdef")) { + // if it wasn't in the name-to-Unicode table, check for a + // name that looks like 'Axx' or 'xx', where 'A' is any letter + // and 'xx' is two hex digits + if ((strlen(charName) == 3 && + isalpha(charName[0]) && + isxdigit(charName[1]) && isxdigit(charName[2]) && + ((charName[1] >= 'a' && charName[1] <= 'f') || + (charName[1] >= 'A' && charName[1] <= 'F') || + (charName[2] >= 'a' && charName[2] <= 'f') || + (charName[2] >= 'A' && charName[2] <= 'F'))) || + (strlen(charName) == 2 && + isxdigit(charName[0]) && isxdigit(charName[1]) && + ((charName[0] >= 'a' && charName[0] <= 'f') || + (charName[0] >= 'A' && charName[0] <= 'F') || + (charName[1] >= 'a' && charName[1] <= 'f') || + (charName[1] >= 'A' && charName[1] <= 'F')))) { + hex = gTrue; + } + missing = gTrue; + } + } else { + toUnicode[code] = 0; + } } - buf[i++] = c; - } - *len = i; - str->close(); - obj2.free(); - obj1.free(); + // pass 2: try to fill in the missing chars, looking for names of + // the form 'Axx', 'xx', 'Ann', 'ABnn', or 'nn', where 'A' and 'B' + // are any letters, 'xx' is two hex digits, and 'nn' is 2-4 + // decimal digits + if (missing && globalParams->getMapNumericCharNames()) { + for (code = 0; code < 256; ++code) { + if ((charName = enc[code]) && !toUnicode[code] && + strcmp(charName, ".notdef")) { + n = strlen(charName); + code2 = -1; + if (hex && n == 3 && isalpha(charName[0]) && + isxdigit(charName[1]) && isxdigit(charName[2])) { + sscanf(charName+1, "%x", &code2); + } else if (hex && n == 2 && + isxdigit(charName[0]) && isxdigit(charName[1])) { + sscanf(charName, "%x", &code2); + } else if (!hex && n >= 2 && n <= 4 && + isdigit(charName[0]) && isdigit(charName[1])) { + code2 = atoi(charName); + } else if (n >= 3 && n <= 5 && + isdigit(charName[1]) && isdigit(charName[2])) { + code2 = atoi(charName+1); + } else if (n >= 4 && n <= 6 && + isdigit(charName[2]) && isdigit(charName[3])) { + code2 = atoi(charName+2); + } + if (code2 >= 0 && code2 <= 0xff) { + toUnicode[code] = (Unicode)code2; + } + } + } + } - return buf; -} + ctu = CharCodeToUnicode::make8BitToUnicode(toUnicode); + } -void GfxFont::makeWidths(Dict *fontDict, FontEncoding *builtinEncoding, - Gushort *builtinWidths, int missingWidth) { - Object obj1, obj2; - int firstChar, lastChar; - int code, code2; - char *charName; - Gushort *defWidths; - int index; - double mult; + //----- get the character widths ----- // initialize all widths for (code = 0; code < 256; ++code) { widths[code] = missingWidth * 0.001; } + // use widths from font dict, if present + fontDict->lookup("FirstChar", &obj1); + firstChar = obj1.isInt() ? obj1.getInt() : 0; + obj1.free(); + fontDict->lookup("LastChar", &obj1); + lastChar = obj1.isInt() ? obj1.getInt() : 255; + obj1.free(); + mul = (type == fontType3) ? fontMat[0] : 0.001; + fontDict->lookup("Widths", &obj1); + if (obj1.isArray()) { + flags |= fontFixedWidth; + for (code = firstChar; code <= lastChar; ++code) { + obj1.arrayGet(code - firstChar, &obj2); + if (obj2.isNum()) { + widths[code] = obj2.getNum() * mul; + if (widths[code] != widths[firstChar]) { + flags &= ~fontFixedWidth; + } + } + obj2.free(); + } + // use widths from built-in font - if (builtinEncoding) { - code2 = 0; // to make gcc happy + } else if (builtinFont) { + // this is a kludge for broken PDF files that encode char 32 + // as .notdef + if (builtinFont->widths->getWidth("space", &w)) { + widths[32] = 0.001 * w; + } for (code = 0; code < 256; ++code) { - if ((charName = encoding->getCharName(code)) && - (code2 = builtinEncoding->getCharCode(charName)) >= 0) - widths[code] = builtinWidths[code2] * 0.001; + if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) { + widths[code] = 0.001 * w; + } } - // get widths from font dict + // couldn't find widths -- use defaults } else { - fontDict->lookup("FirstChar", &obj1); - firstChar = obj1.isInt() ? obj1.getInt() : 0; - obj1.free(); - fontDict->lookup("LastChar", &obj1); - lastChar = obj1.isInt() ? obj1.getInt() : 255; - obj1.free(); - if (type == fontType3) - mult = fontMat[0]; - else - mult = 0.001; - fontDict->lookup("Widths", &obj1); - if (obj1.isArray()) { - for (code = firstChar; code <= lastChar; ++code) { - obj1.arrayGet(code - firstChar, &obj2); - if (obj2.isNum()) - widths[code] = obj2.getNum() * mult; - obj2.free(); - } + // this is technically an error -- the Widths entry is required + // for all but the Base-14 fonts -- but certain PDF generators + // apparently don't include widths for Arial and TimesNewRoman + if (isFixedWidth()) { + i = 0; + } else if (isSerif()) { + i = 8; } else { - - // couldn't find widths -- use defaults -#if 0 //~ - //~ certain PDF generators apparently don't include widths - //~ for Arial and TimesNewRoman -- and this error message - //~ is a nuisance - error(-1, "No character widths resource for non-builtin font"); -#endif - if (isFixedWidth()) - index = 0; - else if (isSerif()) - index = 8; - else - index = 4; - if (isBold()) - index += 2; - if (isItalic()) - index += 1; - defWidths = defCharWidths[index]; - code2 = 0; // to make gcc happy - for (code = 0; code < 256; ++code) { - if ((charName = encoding->getCharName(code)) && - (code2 = standardEncoding.getCharCode(charName)) >= 0) - widths[code] = defWidths[code2] * 0.001; + i = 4; + } + if (isBold()) { + i += 2; + } + if (isItalic()) { + i += 1; + } + builtinFont = builtinFontSubst[i]; + // this is a kludge for broken PDF files that encode char 32 + // as .notdef + if (builtinFont->widths->getWidth("space", &w)) { + widths[32] = 0.001 * w; + } + for (code = 0; code < 256; ++code) { + if (enc[code] && builtinFont->widths->getWidth(enc[code], &w)) { + widths[code] = 0.001 * w; } } - obj1.free(); } + obj1.free(); + + ok = gTrue; } -void GfxFont::getType0EncAndWidths(Dict *fontDict) { - Object obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8; - int excepsSize; - int i, j, k, n; +Gfx8BitFont::~Gfx8BitFont() { + int i; - widths16.exceps = NULL; - widths16.excepsV = NULL; + for (i = 0; i < 256; ++i) { + if (encFree[i] && enc[i]) { + gfree(enc[i]); + } + } + ctu->decRefCnt(); + if (charProcs.isDict()) { + charProcs.free(); + } + if (resources.isDict()) { + resources.free(); + } +} + +int Gfx8BitFont::getNextChar(char *s, int len, CharCode *code, + Unicode *u, int uSize, int *uLen, + double *dx, double *dy, double *ox, double *oy) { + CharCode c; + + *code = c = (CharCode)(*s & 0xff); + *uLen = ctu->mapToUnicode(c, u, uSize); + *dx = widths[c]; + *dy = *ox = *oy = 0; + return 1; +} + +CharCodeToUnicode *Gfx8BitFont::getToUnicode() { + ctu->incRefCnt(); + return ctu; +} + +Dict *Gfx8BitFont::getCharProcs() { + return charProcs.isDict() ? charProcs.getDict() : (Dict *)NULL; +} + +Object *Gfx8BitFont::getCharProc(int code, Object *proc) { + if (charProcs.isDict()) { + charProcs.dictLookup(enc[code], proc); + } else { + proc->initNull(); + } + return proc; +} + +Dict *Gfx8BitFont::getResources() { + return resources.isDict() ? resources.getDict() : (Dict *)NULL; +} + +//------------------------------------------------------------------------ +// GfxCIDFont +//------------------------------------------------------------------------ - // get the CIDFont - fontDict->lookup("DescendantFonts", &obj1); - if (!obj1.isArray() || obj1.arrayGetLength() != 1) { - error(-1, "Bad DescendantFonts entry for Type 0 font"); +static int cmpWidthExcep(const void *w1, const void *w2) { + return ((GfxFontCIDWidthExcep *)w1)->first - + ((GfxFontCIDWidthExcep *)w2)->first; +} + +static int cmpWidthExcepV(const void *w1, const void *w2) { + return ((GfxFontCIDWidthExcepV *)w1)->first - + ((GfxFontCIDWidthExcepV *)w2)->first; +} + +GfxCIDFont::GfxCIDFont(XRef *xref, char *tagA, Ref idA, GString *nameA, + Dict *fontDict): + GfxFont(tagA, idA, nameA) +{ + Dict *desFontDict; + GString *collection, *cMapName; + Object desFontDictObj; + Object obj1, obj2, obj3, obj4, obj5, obj6; + int c1, c2; + int excepsSize, i, j, k; + + ascent = 0.95; + descent = -0.35; + fontBBox[0] = fontBBox[1] = fontBBox[2] = fontBBox[3] = 0; + cMap = NULL; + ctu = NULL; + widths.defWidth = 1.0; + widths.defHeight = -1.0; + widths.defVY = 0.880; + widths.exceps = NULL; + widths.nExceps = 0; + widths.excepsV = NULL; + widths.nExcepsV = 0; + cidToGID = NULL; + cidToGIDLen = 0; + + // get the descendant font + if (!fontDict->lookup("DescendantFonts", &obj1)->isArray()) { + error(-1, "Missing DescendantFonts entry in Type 0 font"); + obj1.free(); goto err1; } - obj1.arrayGet(0, &obj2); - if (!obj2.isDict()) { - error(-1, "Bad descendant font of Type 0 font"); - goto err2; + if (!obj1.arrayGet(0, &desFontDictObj)->isDict()) { + error(-1, "Bad descendant font in Type 0 font"); + goto err3; } + obj1.free(); + desFontDict = desFontDictObj.getDict(); - // get font info - obj2.dictLookup("CIDSystemInfo", &obj3); - if (!obj3.isDict()) { - error(-1, "Bad CIDSystemInfo in Type 0 font descendant"); + // font type + if (!desFontDict->lookup("Subtype", &obj1)) { + error(-1, "Missing Subtype entry in Type 0 descendant font"); goto err3; } - obj3.dictLookup("Registry", &obj4); - obj3.dictLookup("Ordering", &obj5); - if (obj4.isString() && obj5.isString()) { - if (obj4.getString()->cmp("Adobe") == 0 && - obj5.getString()->cmp("Japan1") == 0) { -#if JAPANESE_SUPPORT - is16 = gTrue; - enc16.charSet = font16AdobeJapan12; -#else - error(-1, "Xpdf was compiled without Japanese font support"); - goto err4; -#endif - } else if (obj4.getString()->cmp("Adobe") == 0 && - obj5.getString()->cmp("GB1") == 0) { -#if CHINESE_GB_SUPPORT - is16 = gTrue; - enc16.charSet = font16AdobeGB12; -#else - error(-1, "Xpdf was compiled without Chinese GB font support"); - goto err4; -#endif - } else if (obj4.getString()->cmp("Adobe") == 0 && - obj5.getString()->cmp("CNS1") == 0) { -#if CHINESE_CNS_SUPPORT - is16 = gTrue; - enc16.charSet = font16AdobeCNS13; -#else - error(-1, "Xpdf was compiled without Chinese CNS font support"); - goto err4; -#endif - } else { - error(-1, "Uknown Type 0 character set: %s-%s", - obj4.getString()->getCString(), obj5.getString()->getCString()); - goto err4; - } + if (obj1.isName("CIDFontType0")) { + type = fontCIDType0; + } else if (obj1.isName("CIDFontType2")) { + type = fontCIDType2; } else { - error(-1, "Unknown Type 0 character set"); + error(-1, "Unknown Type 0 descendant font type '%s'", + obj1.isName() ? obj1.getName() : "???"); + goto err3; + } + obj1.free(); + + // get info from font descriptor + readFontDescriptor(xref, desFontDict); + + // look for an external font file + findExtFontFile(); + + //----- encoding info ----- + + // char collection + if (!desFontDict->lookup("CIDSystemInfo", &obj1)->isDict()) { + error(-1, "Missing CIDSystemInfo dictionary in Type 0 descendant font"); + goto err3; + } + obj1.dictLookup("Registry", &obj2); + obj1.dictLookup("Ordering", &obj3); + if (!obj2.isString() || !obj3.isString()) { + error(-1, "Invalid CIDSystemInfo dictionary in Type 0 descendant font"); goto err4; } - obj5.free(); - obj4.free(); + collection = obj2.getString()->copy()->append('-')->append(obj3.getString()); obj3.free(); + obj2.free(); + obj1.free(); - // get default char width - obj2.dictLookup("DW", &obj3); - if (obj3.isInt()) - widths16.defWidth = obj3.getInt() * 0.001; - else - widths16.defWidth = 1.0; - obj3.free(); + // look for a ToUnicode CMap + if (!(ctu = readToUnicodeCMap(fontDict, 16))) { - // get default char metrics for vertical font - obj2.dictLookup("DW2", &obj3); - widths16.defVY = 0.880; - widths16.defHeight = -1; - if (obj3.isArray() && obj3.arrayGetLength() == 2) { - obj3.arrayGet(0, &obj4); - if (obj4.isInt()) { - widths16.defVY = obj4.getInt() * 0.001; + // the "Adobe-Identity" and "Adobe-UCS" collections don't have + // cidToUnicode files + if (collection->cmp("Adobe-Identity") && + collection->cmp("Adobe-UCS")) { + + // look for a user-supplied .cidToUnicode file + if (!(ctu = globalParams->getCIDToUnicode(collection))) { + error(-1, "Unknown character collection '%s'", + collection->getCString()); + delete collection; + goto err2; + } } - obj4.free(); - obj3.arrayGet(1, &obj4); - if (obj4.isInt()) { - widths16.defHeight = obj4.getInt() * 0.001; + } + + // encoding (i.e., CMap) + //~ need to handle a CMap stream here + //~ also need to deal with the UseCMap entry in the stream dict + if (!fontDict->lookup("Encoding", &obj1)->isName()) { + error(-1, "Missing or invalid Encoding entry in Type 0 font"); + delete collection; + goto err3; + } + cMapName = new GString(obj1.getName()); + obj1.free(); + if (!(cMap = globalParams->getCMap(collection, cMapName))) { + error(-1, "Unknown CMap '%s' for character collection '%s'", + cMapName->getCString(), collection->getCString()); + delete collection; + delete cMapName; + goto err2; + } + delete collection; + delete cMapName; + + // CIDToGIDMap (for embedded TrueType fonts) + if (type == fontCIDType2) { + fontDict->lookup("CIDToGIDMap", &obj1); + if (obj1.isStream()) { + cidToGIDLen = 0; + i = 64; + cidToGID = (Gushort *)gmalloc(i * sizeof(Gushort)); + obj1.streamReset(); + while ((c1 = obj1.streamGetChar()) != EOF && + (c2 = obj1.streamGetChar()) != EOF) { + if (cidToGIDLen == i) { + i *= 2; + cidToGID = (Gushort *)grealloc(cidToGID, i * sizeof(Gushort)); + } + cidToGID[cidToGIDLen++] = (Gushort)((c1 << 8) + c2); + } + } else if (!obj1.isName("Identity") && !obj1.isNull()) { + error(-1, "Invalid CIDToGIDMap entry in CID font"); } - obj4.free(); + obj1.free(); } - obj3.free(); - // get char width exceptions - widths16.exceps = NULL; - widths16.numExceps = 0; - obj2.dictLookup("W", &obj3); - if (obj3.isArray()) { + //----- character metrics ----- + + // default char width + if (desFontDict->lookup("DW", &obj1)->isInt()) { + widths.defWidth = obj1.getInt() * 0.001; + } + obj1.free(); + + // char width exceptions + if (desFontDict->lookup("W", &obj1)->isArray()) { excepsSize = 0; - k = 0; i = 0; - while (i+1 < obj3.arrayGetLength()) { - obj3.arrayGet(i, &obj4); - obj3.arrayGet(i+1, &obj5); - if (obj4.isInt() && obj5.isInt()) { - obj3.arrayGet(i+2, &obj6); - if (!obj6.isNum()) { + while (i + 1 < obj1.arrayGetLength()) { + obj1.arrayGet(i, &obj2); + obj1.arrayGet(i + 1, &obj3); + if (obj2.isInt() && obj3.isInt() && i + 2 < obj1.arrayGetLength()) { + if (obj1.arrayGet(i + 2, &obj4)->isNum()) { + if (widths.nExceps == excepsSize) { + excepsSize += 16; + widths.exceps = (GfxFontCIDWidthExcep *) + grealloc(widths.exceps, + excepsSize * sizeof(GfxFontCIDWidthExcep)); + } + widths.exceps[widths.nExceps].first = obj2.getInt(); + widths.exceps[widths.nExceps].last = obj3.getInt(); + widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; + ++widths.nExceps; + } else { error(-1, "Bad widths array in Type 0 font"); - obj6.free(); - obj5.free(); - obj4.free(); - break; } - if (k == excepsSize) { - excepsSize += 16; - widths16.exceps = (GfxFontWidthExcep *) - grealloc(widths16.exceps, - excepsSize * sizeof(GfxFontWidthExcep)); - } - widths16.exceps[k].first = obj4.getInt(); - widths16.exceps[k].last = obj5.getInt(); - widths16.exceps[k].width = obj6.getNum() * 0.001; - obj6.free(); - ++k; + obj4.free(); i += 3; - } else if (obj4.isInt() && obj5.isArray()) { - if (k + obj5.arrayGetLength() >= excepsSize) { - excepsSize = (k + obj5.arrayGetLength() + 15) & ~15; - widths16.exceps = (GfxFontWidthExcep *) - grealloc(widths16.exceps, - excepsSize * sizeof(GfxFontWidthExcep)); + } else if (obj2.isInt() && obj3.isArray()) { + if (widths.nExceps + obj3.arrayGetLength() > excepsSize) { + excepsSize = (widths.nExceps + obj3.arrayGetLength() + 15) & ~15; + widths.exceps = (GfxFontCIDWidthExcep *) + grealloc(widths.exceps, + excepsSize * sizeof(GfxFontCIDWidthExcep)); } - n = obj4.getInt(); - for (j = 0; j < obj5.arrayGetLength(); ++j) { - obj5.arrayGet(j, &obj6); - if (!obj6.isNum()) { + j = obj2.getInt(); + for (k = 0; k < obj3.arrayGetLength(); ++k) { + if (obj3.arrayGet(k, &obj4)->isNum()) { + widths.exceps[widths.nExceps].first = j; + widths.exceps[widths.nExceps].last = j; + widths.exceps[widths.nExceps].width = obj4.getNum() * 0.001; + ++j; + ++widths.nExceps; + } else { error(-1, "Bad widths array in Type 0 font"); - obj6.free(); - break; } - widths16.exceps[k].first = widths16.exceps[k].last = n++; - widths16.exceps[k].width = obj6.getNum() * 0.001; - obj6.free(); - ++k; + obj4.free(); } i += 2; } else { error(-1, "Bad widths array in Type 0 font"); - obj6.free(); - obj5.free(); - obj4.free(); - break; + ++i; } - obj5.free(); - obj4.free(); + obj3.free(); + obj2.free(); } - widths16.numExceps = k; - if (k > 0) - qsort(widths16.exceps, k, sizeof(GfxFontWidthExcep), &cmpWidthExcep); + qsort(widths.exceps, widths.nExceps, sizeof(GfxFontCIDWidthExcep), + &cmpWidthExcep); } - obj3.free(); + obj1.free(); + + // default metrics for vertical font + if (desFontDict->lookup("DW2", &obj1)->isArray() && + obj1.arrayGetLength() == 2) { + if (obj1.arrayGet(0, &obj2)->isNum()) { + widths.defVY = obj1.getNum() * 0.001; + } + obj2.free(); + if (obj1.arrayGet(1, &obj2)->isNum()) { + widths.defHeight = obj1.getNum() * 0.001; + } + obj2.free(); + } + obj1.free(); - // get char metric exceptions for vertical font - widths16.excepsV = NULL; - widths16.numExcepsV = 0; - obj2.dictLookup("W2", &obj3); - if (obj3.isArray()) { + // char metric exceptions for vertical font + if (desFontDict->lookup("W2", &obj1)->isArray()) { excepsSize = 0; - k = 0; i = 0; - while (i+1 < obj3.arrayGetLength()) { - obj3.arrayGet(i, &obj4); - obj3.arrayGet(i+1, &obj5); - if (obj4.isInt() && obj5.isInt()) { - obj3.arrayGet(i+2, &obj6); - obj3.arrayGet(i+3, &obj7); - obj3.arrayGet(i+4, &obj8); - if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) { + while (i + 1 < obj1.arrayGetLength()) { + obj1.arrayGet(0, &obj2); + obj2.arrayGet(0, &obj3); + if (obj2.isInt() && obj3.isInt() && i + 4 < obj1.arrayGetLength()) { + if (obj1.arrayGet(i + 2, &obj4)->isNum() && + obj1.arrayGet(i + 3, &obj5)->isNum() && + obj1.arrayGet(i + 4, &obj6)->isNum()) { + if (widths.nExcepsV == excepsSize) { + excepsSize += 16; + widths.excepsV = (GfxFontCIDWidthExcepV *) + grealloc(widths.excepsV, + excepsSize * sizeof(GfxFontCIDWidthExcepV)); + } + widths.excepsV[widths.nExcepsV].first = obj2.getInt(); + widths.excepsV[widths.nExcepsV].last = obj3.getInt(); + widths.excepsV[widths.nExcepsV].height = obj4.getNum() * 0.001; + widths.excepsV[widths.nExcepsV].vx = obj5.getNum() * 0.001; + widths.excepsV[widths.nExcepsV].vy = obj6.getNum() * 0.001; + ++widths.nExcepsV; + } else { error(-1, "Bad widths (W2) array in Type 0 font"); - obj8.free(); - obj7.free(); - obj6.free(); - obj5.free(); - obj4.free(); - break; } - if (k == excepsSize) { - excepsSize += 16; - widths16.excepsV = (GfxFontWidthExcepV *) - grealloc(widths16.excepsV, - excepsSize * sizeof(GfxFontWidthExcepV)); - } - widths16.excepsV[k].first = obj4.getInt(); - widths16.excepsV[k].last = obj5.getInt(); - widths16.excepsV[k].height = obj6.getNum() * 0.001; - widths16.excepsV[k].vx = obj7.getNum() * 0.001; - widths16.excepsV[k].vy = obj8.getNum() * 0.001; - obj8.free(); - obj7.free(); obj6.free(); - ++k; + obj5.free(); + obj4.free(); i += 5; - } else if (obj4.isInt() && obj5.isArray()) { - if (k + obj5.arrayGetLength() / 3 >= excepsSize) { - excepsSize = (k + obj5.arrayGetLength() / 3 + 15) & ~15; - widths16.excepsV = (GfxFontWidthExcepV *) - grealloc(widths16.excepsV, - excepsSize * sizeof(GfxFontWidthExcepV)); + } else if (obj2.isInt() && obj3.isArray()) { + if (widths.nExcepsV + obj3.arrayGetLength() / 3 > excepsSize) { + excepsSize = + (widths.nExcepsV + obj3.arrayGetLength() / 3 + 15) & ~15; + widths.excepsV = (GfxFontCIDWidthExcepV *) + grealloc(widths.excepsV, + excepsSize * sizeof(GfxFontCIDWidthExcepV)); } - n = obj4.getInt(); - for (j = 0; j < obj5.arrayGetLength(); j += 3) { - obj5.arrayGet(j, &obj6); - obj5.arrayGet(j+1, &obj7); - obj5.arrayGet(j+1, &obj8); - if (!obj6.isNum() || !obj7.isNum() || !obj8.isNum()) { + j = obj2.getInt(); + for (k = 0; k < obj3.arrayGetLength(); ++k) { + if (obj3.arrayGet(k, &obj4)->isNum() && + obj3.arrayGet(k, &obj5)->isNum() && + obj3.arrayGet(k, &obj6)->isNum()) { + widths.excepsV[widths.nExceps].first = j; + widths.excepsV[widths.nExceps].last = j; + widths.excepsV[widths.nExceps].height = obj4.getNum() * 0.001; + widths.excepsV[widths.nExceps].vx = obj5.getNum() * 0.001; + widths.excepsV[widths.nExceps].vy = obj6.getNum() * 0.001; + ++j; + ++widths.nExcepsV; + } else { error(-1, "Bad widths (W2) array in Type 0 font"); - obj6.free(); - break; } - widths16.excepsV[k].first = widths16.exceps[k].last = n++; - widths16.excepsV[k].height = obj6.getNum() * 0.001; - widths16.excepsV[k].vx = obj7.getNum() * 0.001; - widths16.excepsV[k].vy = obj8.getNum() * 0.001; - obj8.free(); - obj7.free(); obj6.free(); - ++k; + obj5.free(); + obj4.free(); } i += 2; } else { - error(-1, "Bad widths array in Type 0 font"); - obj5.free(); - obj4.free(); - break; + error(-1, "Bad widths (W2) array in Type 0 font"); + ++i; } - obj5.free(); - obj4.free(); - } - widths16.numExcepsV = k; - if (k > 0) { - qsort(widths16.excepsV, k, sizeof(GfxFontWidthExcepV), &cmpWidthExcepV); + obj3.free(); + obj2.free(); } + qsort(widths.excepsV, widths.nExcepsV, sizeof(GfxFontCIDWidthExcepV), + &cmpWidthExcepV); } - obj3.free(); + obj1.free(); + + desFontDictObj.free(); + ok = gTrue; + return; + err4: + obj3.free(); obj2.free(); + err3: obj1.free(); + err2: + desFontDictObj.free(); + err1:; +} - // get encoding (CMap) - fontDict->lookup("Encoding", &obj1); - if (!obj1.isName()) { - error(-1, "Bad encoding for Type 0 font"); - goto err1; +GfxCIDFont::~GfxCIDFont() { + if (cMap) { + cMap->decRefCnt(); } -#if JAPANESE_SUPPORT - if (enc16.charSet == font16AdobeJapan12) { - for (i = 0; gfxJapan12Tab[i].name; ++i) { - if (!strcmp(obj1.getName(), gfxJapan12Tab[i].name)) - break; - } - if (!gfxJapan12Tab[i].name) { - error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font", - obj1.getName()); - goto err1; - } - enc16.enc = gfxJapan12Tab[i].enc; + if (ctu) { + ctu->decRefCnt(); } -#endif -#if CHINESE_GB_SUPPORT - if (enc16.charSet == font16AdobeGB12) { - for (i = 0; gfxGB12Tab[i].name; ++i) { - if (!strcmp(obj1.getName(), gfxGB12Tab[i].name)) - break; - } - if (!gfxGB12Tab[i].name) { - error(-1, "Unknown encoding '%s' for Adobe-GB1-2 font", - obj1.getName()); - goto err1; - } - enc16.enc = gfxGB12Tab[i].enc; + gfree(widths.exceps); + gfree(widths.excepsV); + if (cidToGID) { + gfree(cidToGID); } -#endif -#if CHINESE_CNS_SUPPORT - if (enc16.charSet == font16AdobeCNS13) { - for (i = 0; gfxCNS13Tab[i].name; ++i) { - if (!strcmp(obj1.getName(), gfxCNS13Tab[i].name)) - break; +} + +int GfxCIDFont::getNextChar(char *s, int len, CharCode *code, + Unicode *u, int uSize, int *uLen, + double *dx, double *dy, double *ox, double *oy) { + CID cid; + double w, h, vx, vy; + int n, a, b, m; + + if (!cMap) { + *code = 0; + *uLen = 0; + *dx = *dy = 0; + return 1; + } + + *code = (CharCode)(cid = cMap->getCID(s, len, &n)); + if (ctu) { + *uLen = ctu->mapToUnicode(cid, u, uSize); + } else { + *uLen = 0; + } + + // horizontal + if (cMap->getWMode() == 0) { + w = widths.defWidth; + h = vx = vy = 0; + if (widths.nExceps > 0 && cid >= widths.exceps[0].first) { + a = 0; + b = widths.nExceps; + // invariant: widths.exceps[a].first <= cid < widths.exceps[b].first + while (b - a > 1) { + m = (a + b) / 2; + if (widths.exceps[m].first <= cid) { + a = m; + } else { + b = m; + } + } + if (cid <= widths.exceps[a].last) { + w = widths.exceps[a].width; + } } - if (!gfxCNS13Tab[i].name) { - error(-1, "Unknown encoding '%s' for Adobe-CNS1-3 font", - obj1.getName()); - goto err1; + + // vertical + } else { + w = 0; + h = widths.defHeight; + vx = widths.defWidth / 2; + vy = widths.defVY; + if (widths.nExcepsV > 0 && cid >= widths.excepsV[0].first) { + a = 0; + b = widths.nExcepsV; + // invariant: widths.excepsV[a].first <= cid < widths.excepsV[b].first + while (b - a > 1) { + m = (a + b) / 2; + if (widths.excepsV[m].last <= cid) { + a = m; + } else { + b = m; + } + } + if (cid <= widths.excepsV[a].last) { + h = widths.excepsV[a].height; + vx = widths.excepsV[a].vx; + vy = widths.excepsV[a].vy; + } } - enc16.enc = gfxCNS13Tab[i].enc; } -#endif - obj1.free(); - return; + *dx = w; + *dy = h; + *ox = vx; + *oy = vy; - err4: - obj5.free(); - obj4.free(); - err3: - obj3.free(); - err2: - obj2.free(); - err1: - obj1.free(); - //~ fix this --> add 16-bit font support to FontFile - encoding = new FontEncoding(); - makeWidths(fontDict, NULL, NULL, 0); + return n; +} + +int GfxCIDFont::getWMode() { + return cMap ? cMap->getWMode() : 0; } -static int CDECL cmpWidthExcep(const void *w1, const void *w2) { - return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first; +CharCodeToUnicode *GfxCIDFont::getToUnicode() { + ctu->incRefCnt(); + return ctu; } -static int CDECL cmpWidthExcepV(const void *w1, const void *w2) { - return ((GfxFontWidthExcepV *)w1)->first - ((GfxFontWidthExcepV *)w2)->first; +GString *GfxCIDFont::getCollection() { + return cMap ? cMap->getCollection() : (GString *)NULL; } //------------------------------------------------------------------------ // GfxFontDict //------------------------------------------------------------------------ -GfxFontDict::GfxFontDict(Dict *fontDict) { +GfxFontDict::GfxFontDict(XRef *xref, Dict *fontDict) { int i; Object obj1, obj2; @@ -986,12 +1245,16 @@ GfxFontDict::GfxFontDict(Dict *fontDict) { fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *)); for (i = 0; i < numFonts; ++i) { fontDict->getValNF(i, &obj1); - obj1.fetch(&obj2); + obj1.fetch(xref, &obj2); if (obj1.isRef() && obj2.isDict()) { - fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(), - obj2.getDict()); + fonts[i] = GfxFont::makeFont(xref, fontDict->getKey(i), + obj1.getRef(), obj2.getDict()); + if (fonts[i] && !fonts[i]->isOk()) { + delete fonts[i]; + fonts[i] = NULL; + } } else { - error(-1, "font resource is not a dictionary"); + error(-1, "font resource is not a dictionary reference"); fonts[i] = NULL; } obj1.free(); @@ -1002,8 +1265,11 @@ GfxFontDict::GfxFontDict(Dict *fontDict) { GfxFontDict::~GfxFontDict() { int i; - for (i = 0; i < numFonts; ++i) - delete fonts[i]; + for (i = 0; i < numFonts; ++i) { + if (fonts[i]) { + delete fonts[i]; + } + } gfree(fonts); } @@ -1011,8 +1277,9 @@ GfxFont *GfxFontDict::lookup(char *tag) { int i; for (i = 0; i < numFonts; ++i) { - if (fonts[i]->matches(tag)) + if (fonts[i] && fonts[i]->matches(tag)) { return fonts[i]; + } } return NULL; } -- cgit v0.9.1