home *** CD-ROM | disk | FTP | other *** search
- //========================================================================
- //
- // GfxFont.cc
- //
- // Copyright 1996 Derek B. Noonburg
- //
- //========================================================================
-
- #ifdef __GNUC__
- #pragma implementation
- #endif
-
- #include <stdlib.h>
- #include <stddef.h>
- #include <stdio.h>
- #include <string.h>
- #include <ctype.h>
- #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;
- //printf("GfxFont(%s,%s,%d)\n",tag1,name?name->getCString():"??",type);
-
- // 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) {
- //printf("type0 encoding\n");
- getType0EncAndWidths(fontDict);
- } else if (builtinFont) {
- //printf("builtin encoding\n");
- makeEncoding(fontDict, builtinFont->encoding);
- makeWidths(fontDict, builtinFont->encoding, builtinFont->widths);
- } else {
- //printf("unknown encoding\n");
- 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()) {
- //printf("encoding found\n");
- 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 {
- //printf("makeencoding1\n");
- 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;
- myFILE *f;
- FileStream *str;
-
- //printf("makeencoding1(%s)\n",obj.isName()?obj.getName():"??");
- // MacRoman, WinAnsi, or Standard encoding
- if (obj.isName("MacRomanEncoding")) {
- enc = &macRomanEncoding;
- //printf("macRomanEncoding\n");
- } else if (obj.isName("WinAnsiEncoding")) {
- enc = &winAnsiEncoding;
- //printf("winANSIEncoding\n");
- } else if (obj.isName("StandardEncoding")) {
- enc = &standardEncoding;
- //printf("standardEncoding\n");
-
- // use the built-in font encoding if possible
- } else if (builtinEncoding) {
- enc = builtinEncoding;
- //printf("buitinEncoding\n");
-
- // check font type
- } else {
-
- // Type 1 font: try to get encoding from font file
- if (type == fontType1) {
-
- // default to using standard encoding
- enc = &standardEncoding;
- //printf("Type1Encoding\n");
-
- // is there an external font file?
- haveEncoding = gFalse;
- if (name) {
- for (path = fontPath; *path; ++path) {
- extFontFile = appendToPath(new GString(*path), name->getCString());
- f = myfopen(extFontFile->getCString(), "rb");
- if (!f) {
- extFontFile->append(".pfb");
- f = myfopen(extFontFile->getCString(), "rb");
- }
- if (!f) {
- extFontFile->del(extFontFile->getLength() - 4, 4);
- extFontFile->append(".pfa");
- f = myfopen(extFontFile->getCString(), "rb");
- }
- if (f) {
- obj1.initNull();
- str = new FileStream(f, 0, -1, &obj1);
- getType1Encoding(str);
- delete str;
- myfclose(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) {
- //printf("embeded font\n");
- 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;
- //printf("TTEncoding\n");
-
- // not Type 1 or TrueType: just use the standard encoding
- } else {
- enc = &standardEncoding;
- //printf("defaultEncoding\n");
- }
- }
-
- 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;
- }
-