home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 110
/
EnigmaAmiga110CD.iso
/
indispensabili
/
utility
/
apdf
/
xpdf-0.80
/
xpdf
/
gfxfont.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1999-06-23
|
20KB
|
835 lines
//========================================================================
//
// 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;
}