From d9f9a6449f377b4c933b75d57541b19c6d088994 Mon Sep 17 00:00:00 2001 From: Arturo Espinosa Date: Sat, 17 Apr 1999 02:59:58 +0000 Subject: Initial revision --- (limited to 'pdf/xpdf/GfxFont.cc') diff --git a/pdf/xpdf/GfxFont.cc b/pdf/xpdf/GfxFont.cc new file mode 100644 index 0000000..d1148cf --- /dev/null +++ b/pdf/xpdf/GfxFont.cc @@ -0,0 +1,821 @@ +//======================================================================== +// +// GfxFont.cc +// +// Copyright 1996 Derek B. Noonburg +// +//======================================================================== + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include +#include +#include +#include +#include +#include "GString.h" +#include "gmem.h" +#include "gfile.h" +#include "config.h" +#include "Object.h" +#include "Array.h" +#include "Dict.h" +#include "Error.h" +#include "Params.h" +#include "GfxFont.h" + +#include "FontInfo.h" +#if JAPANESE_SUPPORT +#include "CMapInfo.h" +#endif + +//------------------------------------------------------------------------ + +static int CDECL cmpWidthExcep(const void *w1, const void *w2); + +//------------------------------------------------------------------------ + +static Gushort *defCharWidths[12] = { + courierWidths, + courierObliqueWidths, + courierBoldWidths, + courierBoldObliqueWidths, + helveticaWidths, + helveticaObliqueWidths, + helveticaBoldWidths, + helveticaBoldObliqueWidths, + timesRomanWidths, + timesItalicWidths, + timesBoldWidths, + 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; + int i; + + // get font tag and ID + tag = new GString(tag1); + id = id1; + + // get base font name + name = NULL; + fontDict->lookup("BaseFont", &obj1); + if (obj1.isName()) + name = new GString(obj1.getName()); + obj1.free(); + + // is it a built-in font? + builtinFont = NULL; + if (name) { + for (i = 0; i < numBuiltinFonts; ++i) { + if (!strcmp(builtinFonts[i].name, name->getCString())) { + builtinFont = &builtinFonts[i]; + break; + } + } + } + + // 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; + + // 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; + embFontID.num = -1; + embFontID.gen = -1; + embFontName = NULL; + extFontFile = NULL; + fontDict->lookup("FontDescriptor", &obj1); + if (obj1.isDict()) { + + // flags + obj1.dictLookup("Flags", &obj2); + if (obj2.isInt()) + flags = obj2.getInt(); + obj2.free(); + + // embedded Type 1 font file and font name + if (type == fontType1) { + obj1.dictLookupNF("FontFile", &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.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(); + } + } + obj1.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(); + } + } + obj1.free(); + + // 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); + } +} + +GfxFont::~GfxFont() { + delete tag; + if (name) + delete name; + if (!is16 && encoding) + delete encoding; + if (embFontName) + delete embFontName; + if (extFontFile) + delete extFontFile; + if (is16) + gfree(widths16.exceps); +} + +double GfxFont::getWidth(GString *s) { + double w; + int i; + + w = 0; + for (i = 0; i < s->getLength(); ++i) + w += widths[s->getChar(i) & 0xff]; + return w; +} + +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; + } + } + return w; +} + +double GfxFont::getWidth16(GString *s) { + double w; + int c; + int i; + + w = 0; + for (i = 0; i < s->getLength(); i += 2) { + c = (s->getChar(i) << 8) + s->getChar(i+1); + w += getWidth16(c); + } + return w; +} + +void GfxFont::makeEncoding(Dict *fontDict, GfxFontEncoding *builtinEncoding) { + GfxFontEncoding *baseEnc; + Object obj1, obj2, obj3; + char *charName; + int code, i; + + // start with empty encoding + encoding = new GfxFontEncoding(); + + // get encoding from font dict + fontDict->lookup("Encoding", &obj1); + + // encoding specified by dictionary + if (obj1.isDict()) { + obj1.dictLookup("BaseEncoding", &obj2); + baseEnc = makeEncoding1(obj2, fontDict, builtinEncoding); + obj2.free(); + obj1.dictLookup("Differences", &obj2); + if (obj2.isArray()) { + 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())); + ++code; + } else { + error(-1, "Wrong type in font encoding resource differences (%s)", + obj3.getTypeName()); + } + obj3.free(); + } + } + 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)); + } + } +} + +GfxFontEncoding *GfxFont::makeEncoding1(Object obj, Dict *fontDict, + GfxFontEncoding *builtinEncoding) { + GfxFontEncoding *enc; + GBool haveEncoding; + Object obj1, obj2; + 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; + } + } + + return enc; +} + +void GfxFont::getType1Encoding(Stream *str) { + char buf[256]; + char *p; + GBool found; + int code, 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; + } + } + + // 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)); + } + } + } + } + //~ look for getinterval/putinterval junk + } +} + +void GfxFont::makeWidths(Dict *fontDict, GfxFontEncoding *builtinEncoding, + Gushort *builtinWidths) { + Object obj1, obj2; + int firstChar, lastChar; + int code, code2; + char *charName; + Gushort *defWidths; + int index; + double mult; + + // initialize all widths to zero + for (code = 0; code < 256; ++code) + widths[code] = 0; + + // use widths from built-in font + if (builtinEncoding) { + code2 = 0; // to make gcc happy + for (code = 0; code < 256; ++code) { + if ((charName = encoding->getCharName(code)) && + (code2 = builtinEncoding->getCharCode(charName)) >= 0) + widths[code] = builtinWidths[code2] * 0.001; + } + + // get widths from font dict + } 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(); + } + } 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; + } + } + obj1.free(); + } +} + +void GfxFont::getType0EncAndWidths(Dict *fontDict) { + Object obj1, obj2, obj3, obj4, obj5, obj6; + int excepsSize; + int i, j, k, n; + + 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")) { + error(-1, "Bad descendant font of Type 0 font"); + goto err2; + } + + obj2.dictLookup("CIDSystemInfo", &obj3); + if (!obj3.isDict()) { + error(-1, "Bad CIDSystemInfo in Type 0 font descendant"); + 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 { + error(-1, "Uknown Type 0 character set: %s-%s", + obj4.getString()->getCString(), obj5.getString()->getCString()); + goto err4; + } + } else { + error(-1, "Unknown Type 0 character set"); + goto err4; + } + obj5.free(); + obj4.free(); + obj3.free(); + + obj2.dictLookup("DW", &obj3); + if (obj3.isInt()) + widths16.defWidth = obj3.getInt() * 0.001; + else + widths16.defWidth = 1.0; + obj3.free(); + + widths16.exceps = NULL; + widths16.numExceps = 0; + obj2.dictLookup("W", &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); + if (!obj6.isNum()) { + 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; + 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)); + } + n = obj4.getInt(); + for (j = 0; j < obj5.arrayGetLength(); ++j) { + obj5.arrayGet(j, &obj6); + if (!obj6.isNum()) { + 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; + } + i += 2; + } else { + error(-1, "Bad widths array in Type 0 font"); + obj6.free(); + obj5.free(); + obj4.free(); + break; + } + obj5.free(); + obj4.free(); + } + widths16.numExceps = k; + if (k > 0) + qsort(widths16.exceps, k, sizeof(GfxFontWidthExcep), &cmpWidthExcep); + } + obj3.free(); + + obj2.free(); + obj1.free(); + + fontDict->lookup("Encoding", &obj1); + if (!obj1.isName()) { + error(-1, "Bad encoding for Type 0 font"); + goto err1; + } +#if JAPANESE_SUPPORT + if (enc16.charSet == font16AdobeJapan12) { + for (i = 0; gfxFontEnc16Tab[i].name; ++i) { + if (!strcmp(obj1.getName(), gfxFontEnc16Tab[i].name)) + break; + } + if (!gfxFontEnc16Tab[i].name) { + error(-1, "Unknown encoding '%s' for Adobe-Japan1-2 font", + obj1.getName()); + goto err1; + } + enc16.enc = gfxFontEnc16Tab[i].enc; + } +#endif + obj1.free(); + + return; + + err4: + obj5.free(); + obj4.free(); + err3: + obj3.free(); + err2: + obj2.free(); + err1: + obj1.free(); + makeEncoding(fontDict, NULL); + makeWidths(fontDict, NULL, NULL); +} + +static int CDECL cmpWidthExcep(const void *w1, const void *w2) { + return ((GfxFontWidthExcep *)w1)->first - ((GfxFontWidthExcep *)w2)->first; +} + +//------------------------------------------------------------------------ +// GfxFontDict +//------------------------------------------------------------------------ + +GfxFontDict::GfxFontDict(Dict *fontDict) { + int i; + Object obj1, obj2; + + numFonts = fontDict->getLength(); + fonts = (GfxFont **)gmalloc(numFonts * sizeof(GfxFont *)); + for (i = 0; i < numFonts; ++i) { + fontDict->getValNF(i, &obj1); + obj1.fetch(&obj2); + if (obj1.isRef() && obj2.isDict("Font")) { + fonts[i] = new GfxFont(fontDict->getKey(i), obj1.getRef(), + obj2.getDict()); + } else { + error(-1, "font resource is not a dictionary"); + fonts[i] = NULL; + } + obj1.free(); + obj2.free(); + } +} + +GfxFontDict::~GfxFontDict() { + int i; + + for (i = 0; i < numFonts; ++i) + delete fonts[i]; + gfree(fonts); +} + +GfxFont *GfxFontDict::lookup(char *tag) { + int i; + + for (i = 0; i < numFonts; ++i) { + if (fonts[i]->matches(tag)) + return fonts[i]; + } + return NULL; +} -- cgit v0.9.1