X-Git-Url: https://www.fi.muni.cz/~kas/git//home/kas/public_html/git/?a=blobdiff_plain;f=pdf%2Fxpdf%2FGfxFont.cc;h=16b311b3890fe156d66cfb14c9a1cc4e7d023267;hb=7aac8dc8533347e21311b15186e0af82f1b22fd6;hp=d1148cf212efc270471e9ec3a456435c2f34ca15;hpb=d9f9a6449f377b4c933b75d57541b19c6d088994;p=evince.git diff --git a/pdf/xpdf/GfxFont.cc b/pdf/xpdf/GfxFont.cc index d1148cf2..16b311b3 100644 --- a/pdf/xpdf/GfxFont.cc +++ b/pdf/xpdf/GfxFont.cc @@ -24,16 +24,24 @@ #include "Dict.h" #include "Error.h" #include "Params.h" +#include "FontFile.h" #include "GfxFont.h" #include "FontInfo.h" #if JAPANESE_SUPPORT -#include "CMapInfo.h" +#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); //------------------------------------------------------------------------ @@ -52,131 +60,37 @@ static Gushort *defCharWidths[12] = { timesBoldItalicWidths }; -//------------------------------------------------------------------------ -// GfxFontEncoding -//------------------------------------------------------------------------ - -inline int GfxFontEncoding::hash(char *name) { - int h; - - h = name[0]; - if (name[1]) - h = h * 61 + name[1]; - return h % gfxFontEncHashSize; -} - -GfxFontEncoding::GfxFontEncoding() { - int i; - - encoding = (char **)gmalloc(256 * sizeof(char *)); - freeEnc = gTrue; - for (i = 0; i < 256; ++i) - encoding[i] = NULL; - for (i = 0; i < gfxFontEncHashSize; ++i) - hashTab[i] = -1; -} - -GfxFontEncoding::GfxFontEncoding(char **encoding1, int encSize) { - int i; - - encoding = encoding1; - freeEnc = gFalse; - for (i = 0; i < gfxFontEncHashSize; ++i) - hashTab[i] = -1; - for (i = 0; i < encSize; ++i) { - if (encoding[i]) - addChar1(i, encoding[i]); - } -} - -void GfxFontEncoding::addChar(int code, char *name) { - int h, i; - - // replace character associated with code - if (encoding[code]) { - h = hash(encoding[code]); - for (i = 0; i < gfxFontEncHashSize; ++i) { - if (hashTab[h] == code) { - hashTab[h] = -2; - break; - } - if (++h == gfxFontEncHashSize) - h = 0; - } - gfree(encoding[code]); - } - - // associate name with code - encoding[code] = name; - - // insert name in hash table - addChar1(code, name); -} - -void GfxFontEncoding::addChar1(int code, char *name) { - int h, i, code2; - - // insert name in hash table - h = hash(name); - for (i = 0; i < gfxFontEncHashSize; ++i) { - code2 = hashTab[h]; - if (code2 < 0) { - hashTab[h] = code; - break; - } else if (encoding[code2] && !strcmp(encoding[code2], name)) { - // keep the highest code for each char -- this is needed because - // X won't display chars with codes < 32 - if (code > code2) - hashTab[h] = code; - break; - } - if (++h == gfxFontEncHashSize) - h = 0; - } -} - -GfxFontEncoding::~GfxFontEncoding() { - int i; - - if (freeEnc) { - for (i = 0; i < 256; ++i) { - if (encoding[i]) - gfree(encoding[i]); - } - gfree(encoding); - } -} - -int GfxFontEncoding::getCharCode(char *name) { - int h, i, code; - - h = hash(name); - for (i = 0; i < gfxFontEncHashSize; ++i) { - code = hashTab[h]; - if (code == -1 || - (code > 0 && encoding[code] && !strcmp(encoding[code], name))) - return code; - if (++h >= gfxFontEncHashSize) - h = 0; - } - return -1; -} - //------------------------------------------------------------------------ // GfxFont //------------------------------------------------------------------------ GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) { BuiltinFont *builtinFont; - char buf[256]; - Object obj1, obj2, obj3; - char *p1, *p2; + Object obj1, obj2, obj3, obj4; + int missingWidth; + char *name2, *p; int i; // get font tag and ID tag = new GString(tag1); id = id1; + // get font type + type = fontUnknownType; + 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; + obj1.free(); + is16 = gFalse; + // get base font name name = NULL; fontDict->lookup("BaseFont", &obj1); @@ -184,6 +98,50 @@ GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) { name = new GString(obj1.getName()); obj1.free(); + // 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); + } + } + // is it a built-in font? builtinFont = NULL; if (name) { @@ -195,89 +153,89 @@ GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) { } } - // get font type - type = fontUnknownType; - fontDict->lookup("Subtype", &obj1); - if (obj1.isName("Type1")) - type = fontType1; - else if (obj1.isName("Type3")) - type = fontType3; - else if (obj1.isName("TrueType")) - type = fontTrueType; - else if (obj1.isName("Type0")) - type = fontType0; - obj1.free(); - is16 = gFalse; + // assume Times-Roman by default (for substitution purposes) + flags = fontSerif; // get info from font descriptor - // for flags: assume Times-Roman (or TimesNewRoman), but - // explicitly check for Arial and CourierNew -- certain PDF - // generators apparently don't include FontDescriptors for Arial, - // TimesNewRoman, and CourierNew - flags = fontSerif; // assume Times-Roman by default - if (type == fontTrueType && !name->cmp("Arial")) - flags = 0; - else if (type == fontTrueType && !name->cmp("CourierNew")) - flags = fontFixedWidth; + embFontName = NULL; embFontID.num = -1; embFontID.gen = -1; - embFontName = NULL; - extFontFile = NULL; + missingWidth = 0; fontDict->lookup("FontDescriptor", &obj1); if (obj1.isDict()) { - // flags + // get flags obj1.dictLookup("Flags", &obj2); if (obj2.isInt()) flags = obj2.getInt(); obj2.free(); - // embedded Type 1 font file and font name + // get name + obj1.dictLookup("FontName", &obj2); + 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()) + embFontID = obj2.getRef(); + obj2.free(); + } + if (embFontID.num == -1 && type == fontTrueType) { + obj1.dictLookupNF("FontFile2", &obj2); + if (obj2.isRef()) + embFontID = obj2.getRef(); + obj2.free(); + } + if (embFontID.num == -1) { + obj1.dictLookupNF("FontFile3", &obj2); if (obj2.isRef()) { embFontID = obj2.getRef(); - - // get font name from the font file itself since font subsets - // sometimes use the 'AAAAAA+foo' name and sometimes use just 'foo' obj2.fetch(&obj3); if (obj3.isStream()) { - obj3.streamReset(); - for (i = 0; i < 64; ++i) { - obj3.streamGetLine(buf, sizeof(buf)); - if (!strncmp(buf, "/FontName", 9)) { - if ((p1 = strchr(buf+9, '/'))) { - ++p1; - for (p2 = p1; *p2 && !isspace(*p2); ++p2) ; - embFontName = new GString(p1, p2 - p1); - } - break; - } - } + obj3.streamGetDict()->lookup("Subtype", &obj4); + if (obj4.isName("Type1")) + type = fontType1; + else if (obj4.isName("Type1C")) + 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(); } obj3.free(); - obj2.free(); - - // couldn't find font name so just use the one in the PDF font - // descriptor - if (!embFontName) { - obj1.dictLookup("FontName", &obj2); - if (obj2.isName()) - embFontName = new GString(obj2.getName()); - } } obj2.free(); + } - // embedded TrueType font file - } else if (type == fontTrueType) { - obj1.dictLookupNF("FontFile2", &obj2); - if (obj2.isRef()) - embFontID = obj2.getRef(); - obj2.free(); + // look for MissingWidth + obj1.dictLookup("MissingWidth", &obj2); + if (obj2.isInt()) { + missingWidth = obj2.getInt(); } + 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(); + } + } + + // look for an external font file + extFontFile = NULL; + if (type == fontType1 && name) + findExtFontFile(); + // get font matrix fontMat[0] = fontMat[3] = 1; fontMat[1] = fontMat[2] = fontMat[4] = fontMat[5] = 0; @@ -293,27 +251,32 @@ GfxFont::GfxFont(char *tag1, Ref id1, Dict *fontDict) { // get encoding and character widths if (type == fontType0) { getType0EncAndWidths(fontDict); - } else if (builtinFont) { - makeEncoding(fontDict, builtinFont->encoding); - makeWidths(fontDict, builtinFont->encoding, builtinFont->widths); } else { - makeEncoding(fontDict, NULL); - makeWidths(fontDict, NULL, NULL); + getEncAndWidths(fontDict, builtinFont, missingWidth); } } GfxFont::~GfxFont() { delete tag; - if (name) + if (name) { delete name; - if (!is16 && encoding) + } + if (!is16 && encoding) { delete encoding; - if (embFontName) + } + if (embFontName) { delete embFontName; - if (extFontFile) + } + if (extFontFile) { delete extFontFile; - if (is16) + } + if (charProcs.isDict()) { + charProcs.free(); + } + if (is16) { gfree(widths16.exceps); + gfree(widths16.excepsV); + } } double GfxFont::getWidth(GString *s) { @@ -348,36 +311,160 @@ double GfxFont::getWidth16(int c) { return w; } -double GfxFont::getWidth16(GString *s) { - double w; - int c; - int i; +double GfxFont::getHeight16(int c) { + double h; + int a, b, m; - w = 0; - for (i = 0; i < s->getLength(); i += 2) { - c = (s->getChar(i) << 8) + s->getChar(i+1); - w += getWidth16(c); + 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; + } } - return w; + 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; + } + } + 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; + } + } + return vy; +} + +Object *GfxFont::getCharProc(int code, Object *proc) { + if (charProcs.isDict()) { + charProcs.dictLookup(encoding->getCharName(code), proc); + } else { + proc->initNull(); + } + return proc; } -void GfxFont::makeEncoding(Dict *fontDict, GfxFontEncoding *builtinEncoding) { - GfxFontEncoding *baseEnc; +void GfxFont::getEncAndWidths(Dict *fontDict, BuiltinFont *builtinFont, + int missingWidth) { Object obj1, obj2, obj3; - char *charName; + char *buf; + int len; + FontFile *fontFile; int code, i; - // start with empty encoding - encoding = new GfxFontEncoding(); - - // get encoding from font dict + // 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 + // 3. default: + // - builtin --> builtin encoding + // - TrueType --> MacRomanEncoding + // - others --> StandardEncoding + // and then add a list of differences from + // FontDict.Encoding.Differences. + + // check FontDict for base encoding + encoding = NULL; fontDict->lookup("Encoding", &obj1); - - // encoding specified by dictionary if (obj1.isDict()) { obj1.dictLookup("BaseEncoding", &obj2); - baseEnc = makeEncoding1(obj2, fontDict, builtinEncoding); + if (obj2.isName("MacRomanEncoding")) { + encoding = macRomanEncoding.copy(); + } else if (obj2.isName("WinAnsiEncoding")) { + encoding = winAnsiEncoding.copy(); + } else if (obj2.isName("StandardEncoding")) { + encoding = standardEncoding.copy(); + } obj2.free(); + } else if (obj1.isName("MacRomanEncoding")) { + encoding = macRomanEncoding.copy(); + } else if (obj1.isName("WinAnsiEncoding")) { + encoding = winAnsiEncoding.copy(); + } else if (obj1.isName("StandardEncoding")) { + encoding = standardEncoding.copy(); + } + obj1.free(); + + // check embedded or external font file for base encoding + if ((type == fontType1 || type == fontType1C) && + (extFontFile || embFontID.num >= 0)) { + if (extFontFile) + buf = readExtFontFile(&len); + else + buf = readEmbFontFile(&len); + if (buf) { + if (type == fontType1) + fontFile = new Type1FontFile(buf, len); + else + fontFile = new Type1CFontFile(buf, len); + if (fontFile->getName()) { + if (embFontName) + delete embFontName; + embFontName = new GString(fontFile->getName()); + } + if (!encoding) + encoding = fontFile->getEncoding(gTrue); + delete fontFile; + 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(); + } + + // merge differences into encoding + fontDict->lookup("Encoding", &obj1); + if (obj1.isDict()) { obj1.dictLookup("Differences", &obj2); if (obj2.isArray()) { code = 0; @@ -397,150 +484,99 @@ void GfxFont::makeEncoding(Dict *fontDict, GfxFontEncoding *builtinEncoding) { } } obj2.free(); - - // encoding specified by name or null - } else { - baseEnc = makeEncoding1(obj1, fontDict, builtinEncoding); } - - // free the font dict encoding obj1.free(); - // merge base encoding and differences; - for (code = 0; code < 256; ++code) { - if (!encoding->getCharName(code)) { - if ((charName = baseEnc->getCharName(code))) - encoding->addChar(code, copyString(charName)); - } - } + // get character widths + if (builtinFont) + makeWidths(fontDict, builtinFont->encoding, builtinFont->widths, + missingWidth); + else + makeWidths(fontDict, NULL, NULL, missingWidth); } -GfxFontEncoding *GfxFont::makeEncoding1(Object obj, Dict *fontDict, - GfxFontEncoding *builtinEncoding) { - GfxFontEncoding *enc; - GBool haveEncoding; - Object obj1, obj2; +void GfxFont::findExtFontFile() { char **path; FILE *f; - FileStream *str; - - // MacRoman, WinAnsi, or Standard encoding - if (obj.isName("MacRomanEncoding")) { - enc = &macRomanEncoding; - } else if (obj.isName("WinAnsiEncoding")) { - enc = &winAnsiEncoding; - } else if (obj.isName("StandardEncoding")) { - enc = &standardEncoding; - - // use the built-in font encoding if possible - } else if (builtinEncoding) { - enc = builtinEncoding; - - // check font type - } else { - - // Type 1 font: try to get encoding from font file - if (type == fontType1) { - - // default to using standard encoding - enc = &standardEncoding; - - // is there an external font file? - haveEncoding = gFalse; - if (name) { - 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) { - obj1.initNull(); - str = new FileStream(f, 0, -1, &obj1); - getType1Encoding(str); - delete str; - fclose(f); - haveEncoding = gTrue; - break; - } - delete extFontFile; - extFontFile = NULL; - } - } - - // is there an embedded font file? - // (this has to be checked after the external font because - // XOutputDev needs the encoding from the external font) - if (!haveEncoding && embFontID.num >= 0) { - obj1.initRef(embFontID.num, embFontID.gen); - obj1.fetch(&obj2); - if (obj2.isStream()) - getType1Encoding(obj2.getStream()); - obj2.free(); - obj1.free(); - } - - // TrueType font: use Mac encoding - } else if (type == fontTrueType) { - enc = &macRomanEncoding; - // not Type 1 or TrueType: just use the standard encoding - } else { - enc = &standardEncoding; + 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; } +} + +char *GfxFont::readExtFontFile(int *len) { + FILE *f; + char *buf; - return enc; + 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; } -void GfxFont::getType1Encoding(Stream *str) { - char buf[256]; - char *p; - GBool found; - int code, i; +char *GfxFont::readEmbFontFile(int *len) { + char *buf; + Object obj1, obj2; + Stream *str; + int c; + int size, i; - // look for encoding in font file - str->reset(); - found = gFalse; - for (i = 0; i < 100; ++i) { - if (!str->getLine(buf, sizeof(buf))) - break; - if (!strncmp(buf, "/Encoding StandardEncoding def", 30)) - break; - if (!strncmp(buf, "/Encoding 256 array", 19)) { - found = gTrue; - break; - } + 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(); - // found the encoding, grab it - if (found) { - for (i = 0; i < 300; ++i) { - if (!str->getLine(buf, sizeof(buf))) - break; - p = strtok(buf, " \t"); - if (p && !strcmp(p, "dup")) { - if ((p = strtok(NULL, " \t"))) { - code = atoi(p); - if ((p = strtok(NULL, " \t"))) { - if (p[0] == '/') - encoding->addChar(code, copyString(p+1)); - } - } - } + buf = NULL; + i = size = 0; + str->reset(); + while ((c = str->getChar()) != EOF) { + if (i == size) { + size += 4096; + buf = (char *)grealloc(buf, size); } - //~ look for getinterval/putinterval junk + buf[i++] = c; } + *len = i; + str->close(); + + obj2.free(); + obj1.free(); + + return buf; } -void GfxFont::makeWidths(Dict *fontDict, GfxFontEncoding *builtinEncoding, - Gushort *builtinWidths) { +void GfxFont::makeWidths(Dict *fontDict, FontEncoding *builtinEncoding, + Gushort *builtinWidths, int missingWidth) { Object obj1, obj2; int firstChar, lastChar; int code, code2; @@ -549,9 +585,10 @@ void GfxFont::makeWidths(Dict *fontDict, GfxFontEncoding *builtinEncoding, int index; double mult; - // initialize all widths to zero - for (code = 0; code < 256; ++code) - widths[code] = 0; + // initialize all widths + for (code = 0; code < 256; ++code) { + widths[code] = missingWidth * 0.001; + } // use widths from built-in font if (builtinEncoding) { @@ -585,7 +622,7 @@ void GfxFont::makeWidths(Dict *fontDict, GfxFontEncoding *builtinEncoding, } else { // couldn't find widths -- use defaults -#if 0 +#if 0 //~ //~ certain PDF generators apparently don't include widths //~ for Arial and TimesNewRoman -- and this error message //~ is a nuisance @@ -614,21 +651,26 @@ void GfxFont::makeWidths(Dict *fontDict, GfxFontEncoding *builtinEncoding, } void GfxFont::getType0EncAndWidths(Dict *fontDict) { - Object obj1, obj2, obj3, obj4, obj5, obj6; + Object obj1, obj2, obj3, obj4, obj5, obj6, obj7, obj8; int excepsSize; int i, j, k, n; + widths16.exceps = NULL; + widths16.excepsV = NULL; + + // get the CIDFont fontDict->lookup("DescendantFonts", &obj1); if (!obj1.isArray() || obj1.arrayGetLength() != 1) { error(-1, "Bad DescendantFonts entry for Type 0 font"); goto err1; } obj1.arrayGet(0, &obj2); - if (!obj2.isDict("Font")) { + if (!obj2.isDict()) { error(-1, "Bad descendant font of Type 0 font"); goto err2; } + // get font info obj2.dictLookup("CIDSystemInfo", &obj3); if (!obj3.isDict()) { error(-1, "Bad CIDSystemInfo in Type 0 font descendant"); @@ -645,6 +687,24 @@ void GfxFont::getType0EncAndWidths(Dict *fontDict) { #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", @@ -659,6 +719,7 @@ void GfxFont::getType0EncAndWidths(Dict *fontDict) { obj4.free(); obj3.free(); + // get default char width obj2.dictLookup("DW", &obj3); if (obj3.isInt()) widths16.defWidth = obj3.getInt() * 0.001; @@ -666,6 +727,25 @@ void GfxFont::getType0EncAndWidths(Dict *fontDict) { widths16.defWidth = 1.0; obj3.free(); + // 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; + } + obj4.free(); + obj3.arrayGet(1, &obj4); + if (obj4.isInt()) { + widths16.defHeight = obj4.getInt() * 0.001; + } + obj4.free(); + } + obj3.free(); + + // get char width exceptions widths16.exceps = NULL; widths16.numExceps = 0; obj2.dictLookup("W", &obj3); @@ -734,9 +814,93 @@ void GfxFont::getType0EncAndWidths(Dict *fontDict) { } obj3.free(); + // get char metric exceptions for vertical font + widths16.excepsV = NULL; + widths16.numExcepsV = 0; + obj2.dictLookup("W2", &obj3); + if (obj3.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()) { + 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; + 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)); + } + 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()) { + 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; + } + i += 2; + } else { + error(-1, "Bad widths array in Type 0 font"); + obj5.free(); + obj4.free(); + break; + } + obj5.free(); + obj4.free(); + } + widths16.numExcepsV = k; + if (k > 0) { + qsort(widths16.excepsV, k, sizeof(GfxFontWidthExcepV), &cmpWidthExcepV); + } + } + obj3.free(); + obj2.free(); obj1.free(); + // get encoding (CMap) fontDict->lookup("Encoding", &obj1); if (!obj1.isName()) { error(-1, "Bad encoding for Type 0 font"); @@ -744,16 +908,44 @@ void GfxFont::getType0EncAndWidths(Dict *fontDict) { } #if JAPANESE_SUPPORT if (enc16.charSet == font16AdobeJapan12) { - for (i = 0; gfxFontEnc16Tab[i].name; ++i) { - if (!strcmp(obj1.getName(), gfxFontEnc16Tab[i].name)) + for (i = 0; gfxJapan12Tab[i].name; ++i) { + if (!strcmp(obj1.getName(), gfxJapan12Tab[i].name)) break; } - if (!gfxFontEnc16Tab[i].name) { + if (!gfxJapan12Tab[i].name) { error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font", obj1.getName()); goto err1; } - enc16.enc = gfxFontEnc16Tab[i].enc; + enc16.enc = gfxJapan12Tab[i].enc; + } +#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; + } +#endif +#if CHINESE_CNS_SUPPORT + if (enc16.charSet == font16AdobeCNS13) { + for (i = 0; gfxCNS13Tab[i].name; ++i) { + if (!strcmp(obj1.getName(), gfxCNS13Tab[i].name)) + break; + } + if (!gfxCNS13Tab[i].name) { + error(-1, "Unknown encoding '%s' for Adobe-CNS1-3 font", + obj1.getName()); + goto err1; + } + enc16.enc = gfxCNS13Tab[i].enc; } #endif obj1.free(); @@ -769,14 +961,19 @@ void GfxFont::getType0EncAndWidths(Dict *fontDict) { obj2.free(); err1: obj1.free(); - makeEncoding(fontDict, NULL); - makeWidths(fontDict, NULL, NULL); + //~ fix this --> add 16-bit font support to FontFile + encoding = new FontEncoding(); + makeWidths(fontDict, NULL, NULL, 0); } static int CDECL cmpWidthExcep(const void *w1, const void *w2) { return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first; } +static int CDECL cmpWidthExcepV(const void *w1, const void *w2) { + return ((GfxFontWidthExcepV *)w1)->first - ((GfxFontWidthExcepV *)w2)->first; +} + //------------------------------------------------------------------------ // GfxFontDict //------------------------------------------------------------------------ @@ -790,7 +987,7 @@ GfxFontDict::GfxFontDict(Dict *fontDict) { for (i = 0; i < numFonts; ++i) { fontDict->getValNF(i, &obj1); obj1.fetch(&obj2); - if (obj1.isRef() && obj2.isDict("Font")) { + if (obj1.isRef() && obj2.isDict()) { fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(), obj2.getDict()); } else {