//
// T1Font.cc
//
+// Copyright 2001-2003 Glyph & Cog, LLC
+//
//========================================================================
-#ifdef __GNUC__
-#pragma implementation
-#endif
+#include <aconf.h>
#if HAVE_T1LIB_H
+#ifdef USE_GCC_PRAGMAS
+#pragma implementation
+#endif
+
#include <math.h>
#include <string.h>
#include <X11/Xlib.h>
#include "gmem.h"
-#include "FontEncoding.h"
+#include "GfxState.h"
#include "T1Font.h"
//------------------------------------------------------------------------
-T1FontEngine::T1FontEngine(Display *display, Visual *visual, int depth,
- Colormap colormap, GBool aa, GBool aaHigh):
- SFontEngine(display, visual, depth, colormap)
+int T1FontEngine::t1libInitCount = 0;
+
+//------------------------------------------------------------------------
+
+T1FontEngine::T1FontEngine(Display *displayA, Visual *visualA, int depthA,
+ Colormap colormapA, GBool aaA, GBool aaHighA):
+ SFontEngine(displayA, visualA, depthA, colormapA)
{
static unsigned long grayVals[17] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
};
ok = gFalse;
- T1_SetBitmapPad(8);
- if (!T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |
- T1_NO_AFM)) {
- return;
- }
- this->aa = aa;
- this->aaHigh = aaHigh;
- if (aa) {
- T1_AASetBitsPerPixel(8);
- if (aaHigh) {
- T1_AASetLevel(T1_AA_HIGH);
- T1_AAHSetGrayValues(grayVals);
+ aa = aaA;
+ aaHigh = aaHighA;
+ //~ for multithreading: need a mutex here
+ if (t1libInitCount == 0) {
+ T1_SetBitmapPad(8);
+ if (!T1_InitLib(NO_LOGFILE | IGNORE_CONFIGFILE | IGNORE_FONTDATABASE |
+ T1_NO_AFM)) {
+ return;
+ }
+ if (aa) {
+ T1_AASetBitsPerPixel(8);
+ if (aaHigh) {
+ T1_AASetLevel(T1_AA_HIGH);
+ T1_AAHSetGrayValues(grayVals);
+ } else {
+ T1_AASetLevel(T1_AA_LOW);
+ T1_AASetGrayValues(0, 1, 2, 3, 4);
+ }
} else {
- T1_AASetLevel(T1_AA_LOW);
- T1_AASetGrayValues(0, 1, 2, 3, 4);
+ T1_AANSetGrayValues(0, 1);
}
- } else {
- T1_AANSetGrayValues(0, 1);
}
+ ++t1libInitCount;
ok = gTrue;
}
T1FontEngine::~T1FontEngine() {
- T1_CloseLib();
+ //~ for multithreading: need a mutex here
+ if (--t1libInitCount == 0) {
+ T1_CloseLib();
+ }
}
//------------------------------------------------------------------------
-T1FontFile::T1FontFile(T1FontEngine *engine, char *fontFileName,
- FontEncoding *fontEnc) {
+T1FontFile::T1FontFile(T1FontEngine *engineA, char *fontFileName,
+ char **fontEnc, double *bboxA) {
int encStrSize;
char *encPtr;
int i;
ok = gFalse;
- this->engine = engine;
+ engine = engineA;
+ enc = NULL;
+ encStr = NULL;
+ for (i = 0; i < 4; ++i) {
+ bbox[i] = bboxA[i];
+ }
// load the font file
if ((id = T1_AddFont(fontFileName)) < 0) {
// reencode it
encStrSize = 0;
- for (i = 0; i < 256 && i < fontEnc->getSize(); ++i) {
- if (fontEnc->getCharName(i)) {
- encStrSize += strlen(fontEnc->getCharName(i)) + 1;
+ for (i = 0; i < 256; ++i) {
+ if (fontEnc[i]) {
+ encStrSize += strlen(fontEnc[i]) + 1;
}
}
enc = (char **)gmalloc(257 * sizeof(char *));
encStr = (char *)gmalloc(encStrSize * sizeof(char));
encPtr = encStr;
- for (i = 0; i < 256 && i < fontEnc->getSize(); ++i) {
- if (fontEnc->getCharName(i)) {
- strcpy(encPtr, fontEnc->getCharName(i));
+ for (i = 0; i < 256; ++i) {
+ if (fontEnc[i]) {
+ strcpy(encPtr, fontEnc[i]);
enc[i] = encPtr;
encPtr += strlen(encPtr) + 1;
} else {
enc[i] = ".notdef";
}
}
- for (; i < 256; ++i) {
- enc[i] = ".notdef";
- }
enc[256] = "custom";
T1_ReencodeFont(id, enc);
T1FontFile::~T1FontFile() {
gfree(enc);
gfree(encStr);
- T1_DeleteFont(id);
+ if (id >= 0) {
+ T1_DeleteFont(id);
+ }
}
//------------------------------------------------------------------------
-T1Font::T1Font(T1FontFile *fontFile, double *m) {
+T1Font::T1Font(T1FontFile *fontFileA, double *m) {
T1FontEngine *engine;
T1_TMATRIX matrix;
BBox bbox;
+ double bbx0, bby0, bbx1, bby1;
int x, y, xMin, xMax, yMin, yMax;
int i;
ok = gFalse;
+ fontFile = fontFileA;
engine = fontFile->engine;
- this->fontFile = fontFile;
id = T1_CopyFont(fontFile->id);
// transform the four corners of the font bounding box -- the min
// and max values form the bounding box of the transformed font
+ bbx0 = fontFile->bbox[0];
+ bby0 = fontFile->bbox[1];
+ bbx1 = fontFile->bbox[2];
+ bby1 = fontFile->bbox[3];
+ // some fonts in PDF files have bboxes which are just plain wrong,
+ // so we check the font file's bbox too
bbox = T1_GetFontBBox(id);
- x = (int)((m[0] * bbox.llx + m[2] * bbox.lly) * 0.001);
+ if (0.001 * bbox.llx < bbx0) {
+ bbx0 = 0.001 * bbox.llx;
+ }
+ if (0.001 * bbox.lly < bby0) {
+ bby0 = 0.001 * bbox.lly;
+ }
+ if (0.001 * bbox.urx > bbx1) {
+ bbx1 = 0.001 * bbox.urx;
+ }
+ if (0.001 * bbox.ury > bby1) {
+ bby1 = 0.001 * bbox.ury;
+ }
+ // some fonts are completely broken, so we fake it (with values
+ // large enough that most glyphs should fit)
+ if (bbx0 == 0 && bby0 == 0 && bbx1 == 0 && bby1 == 0) {
+ bbx0 = bby0 = -0.5;
+ bbx1 = bby1 = 1.5;
+ }
+ x = (int)(m[0] * bbx0 + m[2] * bby0);
xMin = xMax = x;
- y = (int)((m[1] * bbox.llx + m[3] * bbox.lly) * 0.001);
+ y = (int)(m[1] * bbx0 + m[3] * bby0);
yMin = yMax = y;
- x = (int)((m[0] * bbox.llx + m[2] * bbox.ury) * 0.001);
+ x = (int)(m[0] * bbx0 + m[2] * bby1);
if (x < xMin) {
xMin = x;
} else if (x > xMax) {
xMax = x;
}
- y = (int)((m[1] * bbox.llx + m[3] * bbox.ury) * 0.001);
+ y = (int)(m[1] * bbx0 + m[3] * bby1);
if (y < yMin) {
yMin = y;
} else if (y > yMax) {
yMax = y;
}
- x = (int)((m[0] * bbox.urx + m[2] * bbox.lly) * 0.001);
+ x = (int)(m[0] * bbx1 + m[2] * bby0);
if (x < xMin) {
xMin = x;
} else if (x > xMax) {
xMax = x;
}
- y = (int)((m[1] * bbox.urx + m[3] * bbox.lly) * 0.001);
+ y = (int)(m[1] * bbx1 + m[3] * bby0);
if (y < yMin) {
yMin = y;
} else if (y > yMax) {
yMax = y;
}
- x = (int)((m[0] * bbox.urx + m[2] * bbox.ury) * 0.001);
+ x = (int)(m[0] * bbx1 + m[2] * bby1);
if (x < xMin) {
xMin = x;
} else if (x > xMax) {
xMax = x;
}
- y = (int)((m[1] * bbox.urx + m[3] * bbox.ury) * 0.001);
+ y = (int)(m[1] * bbx1 + m[3] * bby1);
if (y < yMin) {
yMin = y;
} else if (y > yMax) {
yMax = y;
}
-#if 1 //~
- //~ This is a kludge: some buggy PDF generators embed fonts with
- //~ zero bounding boxes.
+ // This is a kludge: some buggy PDF generators embed fonts with
+ // zero bounding boxes.
if (xMax == xMin) {
xMin = 0;
xMax = (int)size;
yMin = 0;
yMax = (int)(1.2 * size);
}
-#endif
+ // Another kludge: an unusually large xMin or yMin coordinate is
+ // probably wrong.
+ if (xMin > 0) {
+ xMin = 0;
+ }
+ if (yMin > 0) {
+ yMin = 0;
+ }
+ // Another kludge: t1lib doesn't correctly handle fonts with
+ // real (non-integer) bounding box coordinates.
+ if (xMax - xMin > 5000) {
+ xMin = 0;
+ xMax = (int)size;
+ }
+ if (yMax - yMin > 5000) {
+ yMin = 0;
+ yMax = (int)(1.2 * size);
+ }
// this should be (max - min + 1), but we add some padding to
// deal with rounding errors
glyphW = xMax - xMin + 3;
}
GBool T1Font::drawChar(Drawable d, int w, int h, GC gc,
- int x, int y, int r, int g, int b, Gushort c) {
+ int x, int y, int r, int g, int b,
+ CharCode c, Unicode u) {
T1FontEngine *engine;
XColor xcolor;
int bgR, bgG, bgB;
Gulong colors[17];
- Guchar *p;
+ Guchar *bitmap, *p;
+ GBool tempBitmap;
+ XImage *img;
int xOffset, yOffset, x0, y0, x1, y1, gw, gh, w0, h0;
int xx, yy, xx1;
Guchar pix, mPix;
engine = fontFile->engine;
// generate the glyph pixmap
- if (!(p = getGlyphPixmap(c, &xOffset, &yOffset, &gw, &gh))) {
+ if (!(bitmap = getGlyphPixmap(c, &xOffset, &yOffset, &gw, &gh,
+ &tempBitmap))) {
return gFalse;
}
w0 = w - x0;
}
if (w0 < 0) {
- return gTrue;
+ goto done;
}
if (y0 < 0) {
y1 = -y0;
h0 = h - y0;
}
if (h0 < 0) {
- return gTrue;
+ goto done;
+ }
+
+ // getGlyphPixmap may have returned a larger-than-cache-entry
+ // bitmap, in which case we need to allocate a temporary XImage here
+ if (tempBitmap) {
+ if (!(img = XCreateImage(engine->display, engine->visual, engine->depth,
+ ZPixmap, 0, NULL, gw, gh, 8, 0))) {
+ goto done;
+ }
+ img->data = (char *)gmalloc(gh * img->bytes_per_line);
+ } else {
+ img = image;
}
// read the X image
XGetSubImage(engine->display, d, x0, y0, w0, h0, (1 << engine->depth) - 1,
- ZPixmap, image, x1, y1);
+ ZPixmap, img, x1, y1);
if (engine->aa) {
// compute the colors
- xcolor.pixel = XGetPixel(image, x1, y1);
+ xcolor.pixel = XGetPixel(img, x1 + w0/2, y1 + h0/2);
XQueryColor(engine->display, engine->colormap, &xcolor);
bgR = xcolor.red;
bgG = xcolor.green;
}
// stuff the glyph pixmap into the X image
+ p = bitmap;
for (yy = 0; yy < gh; ++yy) {
for (xx = 0; xx < gw; ++xx) {
pix = *p++;
if (pix > mPix) {
pix = mPix;
}
- XPutPixel(image, xx, yy, colors[pix]);
+ XPutPixel(img, xx, yy, colors[pix]);
}
}
}
colors[1] = engine->findColor(r, g, b);
// stuff the glyph bitmap into the X image
+ p = bitmap;
for (yy = 0; yy < gh; ++yy) {
for (xx = 0; xx < gw; xx += 8) {
pix = *p++;
for (xx1 = xx; xx1 < xx + 8 && xx1 < gw; ++xx1) {
if (pix & 0x01) {
- XPutPixel(image, xx1, yy, colors[1]);
+ XPutPixel(img, xx1, yy, colors[1]);
}
pix >>= 1;
}
}
// draw the X image
- XPutImage(engine->display, d, gc, image, x1, y1, x0, y0, w0, h0);
+ XPutImage(engine->display, d, gc, img, x1, y1, x0, y0, w0, h0);
+ if (tempBitmap) {
+ gfree(img->data);
+ img->data = NULL;
+ XDestroyImage(img);
+ }
+ done:
+ if (tempBitmap) {
+ gfree(bitmap);
+ }
return gTrue;
}
-Guchar *T1Font::getGlyphPixmap(Gushort c, int *x, int *y, int *w, int *h) {
+Guchar *T1Font::getGlyphPixmap(CharCode c, int *x, int *y, int *w, int *h,
+ GBool *tempBitmap) {
T1FontEngine *engine;
GLYPH *glyph;
int gSize;
}
}
cacheTags[i+j].mru = 0x8000;
+ *tempBitmap = gFalse;
return cache + (i+j) * glyphSize;
}
}
if (!glyph) {
return NULL;
}
+
+ // copy the glyph into the cache or a temporary bitmap
*x = -glyph->metrics.leftSideBearing;
*y = glyph->metrics.ascent;
*w = glyph->metrics.rightSideBearing - glyph->metrics.leftSideBearing;
*h = glyph->metrics.ascent - glyph->metrics.descent;
- if (*w > glyphW || *h > glyphH) {
-#if 1 //~
- fprintf(stderr, "Weird t1lib glyph size: %d > %d or %d > %d\n",
- *w, glyphW, *h, glyphH);
-#endif
- return NULL;
+ if (engine->aa) {
+ gSize = *w * *h;
+ } else {
+ gSize = ((*w + 7) >> 3) * *h;
}
-
- // store glyph pixmap in cache
- ret = NULL;
- for (j = 0; j < cacheAssoc; ++j) {
- if ((cacheTags[i+j].mru & 0x7fff) == cacheAssoc - 1) {
- cacheTags[i+j].mru = 0x8000;
- cacheTags[i+j].code = c;
- cacheTags[i+j].x = *x;
- cacheTags[i+j].y = *y;
- cacheTags[i+j].w = *w;
- cacheTags[i+j].h = *h;
- if (engine->aa) {
- gSize = *w * *h;
- } else {
- gSize = ((*w + 7) >> 3) * *h;
- }
- ret = cache + (i+j) * glyphSize;
- if (glyph->bits) {
- memcpy(ret, glyph->bits, gSize);
+ if (*w > glyphW || *h > glyphH) {
+ // the glyph doesn't fit in the bounding box -- return a
+ // temporary, uncached bitmap (this shouldn't happen but some
+ // fonts have incorrect bboxes)
+ ret = (Guchar *)gmalloc(gSize);
+ *tempBitmap = gTrue;
+ } else {
+ // store glyph pixmap in cache
+ ret = NULL; // make gcc happy
+ for (j = 0; j < cacheAssoc; ++j) {
+ if ((cacheTags[i+j].mru & 0x7fff) == cacheAssoc - 1) {
+ cacheTags[i+j].mru = 0x8000;
+ cacheTags[i+j].code = c;
+ cacheTags[i+j].x = *x;
+ cacheTags[i+j].y = *y;
+ cacheTags[i+j].w = *w;
+ cacheTags[i+j].h = *h;
+ ret = cache + (i+j) * glyphSize;
} else {
- memset(ret, 0, gSize);
+ ++cacheTags[i+j].mru;
}
- } else {
- ++cacheTags[i+j].mru;
}
+ *tempBitmap = gFalse;
+ }
+ if (glyph->bits) {
+ memcpy(ret, glyph->bits, gSize);
+ } else {
+ memset(ret, 0, gSize);
}
return ret;
}
+GBool T1Font::getCharPath(CharCode c, Unicode u, GfxState *state) {
+ T1_OUTLINE *outline;
+ T1_PATHSEGMENT *seg;
+ T1_BEZIERSEGMENT *bez;
+ double x, y, x1, y1;
+
+ outline = T1_GetCharOutline(id, c, size, NULL);
+ x = 0;
+ y = 0;
+ for (seg = outline; seg; seg = seg->link) {
+ switch (seg->type) {
+ case T1_PATHTYPE_MOVE:
+ x += seg->dest.x / 65536.0;
+ y += seg->dest.y / 65536.0;
+ state->moveTo(x, y);
+ break;
+ case T1_PATHTYPE_LINE:
+ x += seg->dest.x / 65536.0;
+ y += seg->dest.y / 65536.0;
+ state->lineTo(x, y);
+ break;
+ case T1_PATHTYPE_BEZIER:
+ bez = (T1_BEZIERSEGMENT *)seg;
+ x1 = x + bez->dest.x / 65536.0;
+ y1 = y + bez->dest.y / 65536.0;
+ state->curveTo(x + bez->B.x / 65536.0, y + bez->B.y / 65536.0,
+ x + bez->C.x / 65536.0, y + bez->C.y / 65536.0,
+ x1, y1);
+ x = x1;
+ y = y1;
+ break;
+ }
+ }
+ T1_FreeOutline(outline);
+ return gTrue;
+}
+
#endif // HAVE_T1LIB_H