//======================================================================== // // FTFont.cc // // Copyright 2001-2003 Glyph & Cog, LLC // //======================================================================== #include #if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) #ifdef USE_GCC_PRAGMAS #pragma implementation #endif #include #include #include "gmem.h" #include "freetype/ftoutln.h" #include "freetype/internal/ftobjs.h" #if 1 //~ cff cid->gid map #include "freetype/internal/cfftypes.h" #include "freetype/internal/tttypes.h" #endif #include "GlobalParams.h" #include "GfxState.h" #include "FTFont.h" //------------------------------------------------------------------------ FTFontEngine::FTFontEngine(Display *displayA, Visual *visualA, int depthA, Colormap colormapA, GBool aaA): SFontEngine(displayA, visualA, depthA, colormapA) { ok = gFalse; if (FT_Init_FreeType(&lib)) { return; } aa = aaA; ok = gTrue; } FTFontEngine::~FTFontEngine() { FT_Done_FreeType(lib); } //------------------------------------------------------------------------ FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName, char **fontEnc, GBool pdfFontHasEncoding, GBool pdfFontIsSymbolic) { char *name; int unicodeCmap, macRomanCmap, msSymbolCmap; int i, j; ok = gFalse; engine = engineA; codeMap = NULL; cidToGID = NULL; cidToGIDLen = 0; if (FT_New_Face(engine->lib, fontFileName, 0, &face)) { return; } if (!strcmp(face->driver->root.clazz->module_name, "type1") || !strcmp(face->driver->root.clazz->module_name, "cff")) { mode = ftFontModeCodeMapDirect; codeMap = (Guint *)gmalloc(256 * sizeof(Guint)); for (i = 0; i < 256; ++i) { codeMap[i] = 0; if ((name = fontEnc[i])) { codeMap[i] = FT_Get_Name_Index(face, name); } } } else { // To match up with the Adobe-defined behaviour, we choose a cmap // like this: // 1. If the PDF font has an encoding: // 1a. If the TrueType font has a Microsoft Unicode cmap, use it, // and use the Unicode indexes, not the char codes. // 1b. If the PDF font is symbolic and the TrueType font has a // Microsoft Symbol cmap, use it, and use (0xf000 + char code). // 1c. If the TrueType font has a Macintosh Roman cmap, use it, // and reverse map the char names through MacRomanEncoding to // get char codes. // 2. If the PDF font does not have an encoding: // 2a. If the TrueType font has a Macintosh Roman cmap, use it, // and use char codes directly. // 2b. If the TrueType font has a Microsoft Symbol cmap, use it, // and use (0xf000 + char code). // 3. If none of these rules apply, use the first cmap and hope for // the best (this shouldn't happen). unicodeCmap = macRomanCmap = msSymbolCmap = 0xffff; for (i = 0; i < face->num_charmaps; ++i) { if ((face->charmaps[i]->platform_id == 3 && face->charmaps[i]->encoding_id == 1) || face->charmaps[i]->platform_id == 0) { unicodeCmap = i; } else if (face->charmaps[i]->platform_id == 1 && face->charmaps[i]->encoding_id == 0) { macRomanCmap = i; } else if (face->charmaps[i]->platform_id == 3 && face->charmaps[i]->encoding_id == 0) { msSymbolCmap = i; } } i = 0; mode = ftFontModeCharCode; charMapOffset = 0; if (pdfFontHasEncoding) { if (unicodeCmap != 0xffff) { i = unicodeCmap; mode = ftFontModeUnicode; } else if (pdfFontIsSymbolic && msSymbolCmap != 0xffff) { i = msSymbolCmap; mode = ftFontModeCharCodeOffset; charMapOffset = 0xf000; } else if (macRomanCmap != 0xffff) { i = macRomanCmap; mode = ftFontModeCodeMap; codeMap = (Guint *)gmalloc(256 * sizeof(Guint)); for (j = 0; j < 256; ++j) { if (fontEnc[j]) { codeMap[j] = globalParams->getMacRomanCharCode(fontEnc[j]); } else { codeMap[j] = 0; } } } } else { if (macRomanCmap != 0xffff) { i = macRomanCmap; mode = ftFontModeCharCode; } else if (msSymbolCmap != 0xffff) { i = msSymbolCmap; mode = ftFontModeCharCodeOffset; charMapOffset = 0xf000; } } if (FT_Set_Charmap(face, face->charmaps[i])) { return; } } ok = gTrue; } FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName, Gushort *cidToGIDA, int cidToGIDLenA, GBool embedded) { int i; ok = gFalse; engine = engineA; codeMap = NULL; cidToGID = NULL; cidToGIDLen = 0; if (FT_New_Face(engine->lib, fontFileName, 0, &face)) { return; } cidToGIDLen = cidToGIDLenA; cidToGID = (Gushort *)gmalloc(cidToGIDLen * sizeof(Gushort)); memcpy(cidToGID, cidToGIDA, cidToGIDLen * sizeof(Gushort)); if (!strcmp(face->driver->root.clazz->module_name, "t1cid")) { mode = ftFontModeCID; } else if (!strcmp(face->driver->root.clazz->module_name, "cff")) { mode = ftFontModeCFFCharset; } else if (embedded) { mode = ftFontModeCIDToGIDMap; } else { mode = ftFontModeUnicode; for (i = 0; i < face->num_charmaps; ++i) { if ((face->charmaps[i]->platform_id == 3 && face->charmaps[i]->encoding_id == 1) || face->charmaps[i]->platform_id == 0) { break; } } if (i == face->num_charmaps) { i = 0; } FT_Set_Charmap(face, face->charmaps[i]); } ok = gTrue; } FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName, GBool embedded) { int i; ok = gFalse; engine = engineA; codeMap = NULL; cidToGID = NULL; cidToGIDLen = 0; if (FT_New_Face(engine->lib, fontFileName, 0, &face)) { return; } if (!strcmp(face->driver->root.clazz->module_name, "t1cid")) { mode = ftFontModeCID; } else if (embedded) { mode = ftFontModeCFFCharset; } else { mode = ftFontModeUnicode; for (i = 0; i < face->num_charmaps; ++i) { if ((face->charmaps[i]->platform_id == 3 && face->charmaps[i]->encoding_id == 1) || face->charmaps[i]->platform_id == 0) { break; } } if (i == face->num_charmaps) { i = 0; } FT_Set_Charmap(face, face->charmaps[i]); } ok = gTrue; } FTFontFile::~FTFontFile() { if (face) { FT_Done_Face(face); } if (codeMap) { gfree(codeMap); } if (cidToGID) { gfree(cidToGID); } } //------------------------------------------------------------------------ FTFont::FTFont(FTFontFile *fontFileA, double *m) { FTFontEngine *engine; FT_Face face; double size, div; int x, xMin, xMax; int y, yMin, yMax; int i; ok = gFalse; fontFile = fontFileA; engine = fontFile->engine; face = fontFile->face; if (FT_New_Size(face, &sizeObj)) { return; } face->size = sizeObj; size = sqrt(m[2]*m[2] + m[3]*m[3]); if (FT_Set_Pixel_Sizes(face, 0, (int)size)) { return; } div = face->bbox.xMax > 20000 ? 65536 : 1; // transform the four corners of the font bounding box -- the min // and max values form the bounding box of the transformed font x = (int)((m[0] * face->bbox.xMin + m[2] * face->bbox.yMin) / (div * face->units_per_EM)); xMin = xMax = x; y = (int)((m[1] * face->bbox.xMin + m[3] * face->bbox.yMin) / (div * face->units_per_EM)); yMin = yMax = y; x = (int)((m[0] * face->bbox.xMin + m[2] * face->bbox.yMax) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((m[1] * face->bbox.xMin + m[3] * face->bbox.yMax) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((m[0] * face->bbox.xMax + m[2] * face->bbox.yMin) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((m[1] * face->bbox.xMax + m[3] * face->bbox.yMin) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((m[0] * face->bbox.xMax + m[2] * face->bbox.yMax) / (div * face->units_per_EM)); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((m[1] * face->bbox.xMax + m[3] * face->bbox.yMax) / (div * face->units_per_EM)); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } // This is a kludge: some buggy PDF generators embed fonts with // zero bounding boxes. if (xMax == xMin) { xMin = 0; xMax = (int)size; } if (yMax == yMin) { yMin = 0; yMax = (int)(1.2 * size); } // this should be (max - min + 1), but we add some padding to // deal with rounding errors, bogus bboxes, etc. glyphW = xMax - xMin + 3; glyphW += glyphW >> 1; glyphH = yMax - yMin + 3; glyphH += glyphH >> 1; if (engine->aa) { glyphSize = glyphW * glyphH; } else { glyphSize = ((glyphW + 7) >> 3) * glyphH; } // set up the glyph pixmap cache cacheAssoc = 8; if (glyphSize <= 256) { cacheSets = 8; } else if (glyphSize <= 512) { cacheSets = 4; } else if (glyphSize <= 1024) { cacheSets = 2; } else { cacheSets = 1; } cache = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize); cacheTags = (FTFontCacheTag *)gmalloc(cacheSets * cacheAssoc * sizeof(FTFontCacheTag)); for (i = 0; i < cacheSets * cacheAssoc; ++i) { cacheTags[i].mru = i & (cacheAssoc - 1); } // create the XImage if (!(image = XCreateImage(engine->display, engine->visual, engine->depth, ZPixmap, 0, NULL, glyphW, glyphH, 8, 0))) { return; } image->data = (char *)gmalloc(glyphH * image->bytes_per_line); // compute the transform matrix matrix.xx = (FT_Fixed)((m[0] / size) * 65536); matrix.yx = (FT_Fixed)((m[1] / size) * 65536); matrix.xy = (FT_Fixed)((m[2] / size) * 65536); matrix.yy = (FT_Fixed)((m[3] / size) * 65536); ok = gTrue; } FTFont::~FTFont() { gfree(cacheTags); gfree(cache); gfree(image->data); image->data = NULL; XDestroyImage(image); } GBool FTFont::drawChar(Drawable d, int w, int h, GC gc, int x, int y, int r, int g, int b, CharCode c, Unicode u) { FTFontEngine *engine; XColor xcolor; int bgR, bgG, bgB; Gulong colors[5]; Guchar *bitmap, *p; GBool tempBitmap; XImage *img; int pix; int xOffset, yOffset, x0, y0, x1, y1, gw, gh, w0, h0; int xx, yy, xx1; engine = fontFile->engine; // no Unicode index for this char - don't draw anything if (fontFile->mode == ftFontModeUnicode && u == 0) { return gFalse; } // generate the glyph pixmap if (!(bitmap = getGlyphPixmap(c, u, &xOffset, &yOffset, &gw, &gh, &tempBitmap))) { return gFalse; } // compute: (x0,y0) = position in destination drawable // (x1,y1) = position in glyph image // (w0,h0) = size of image transfer x0 = x - xOffset; y0 = y - yOffset; x1 = 0; y1 = 0; w0 = gw; h0 = gh; if (x0 < 0) { x1 = -x0; w0 += x0; x0 = 0; } if (x0 + w0 > w) { w0 = w - x0; } if (w0 < 0) { goto done; } if (y0 < 0) { y1 = -y0; h0 += y0; y0 = 0; } if (y0 + h0 > h) { h0 = h - y0; } if (h0 < 0) { 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, img, x1, y1); if (engine->aa) { // compute the colors xcolor.pixel = XGetPixel(img, x1 + w0/2, y1 + h0/2); XQueryColor(engine->display, engine->colormap, &xcolor); bgR = xcolor.red; bgG = xcolor.green; bgB = xcolor.blue; colors[1] = engine->findColor((r + 3*bgR) / 4, (g + 3*bgG) / 4, (b + 3*bgB) / 4); colors[2] = engine->findColor((r + bgR) / 2, (g + bgG) / 2, (b + bgB) / 2); colors[3] = engine->findColor((3*r + bgR) / 4, (3*g + bgG) / 4, (3*b + bgB) / 4); colors[4] = engine->findColor(r, g, b); // stuff the glyph pixmap into the X image p = bitmap; for (yy = 0; yy < gh; ++yy) { for (xx = 0; xx < gw; ++xx) { pix = *p++ & 0xff; // this is a heuristic which seems to produce decent // results -- the linear mapping would be: // pix = (pix * 5) / 256; pix = ((pix + 10) * 5) / 256; if (pix > 4) { pix = 4; } if (pix > 0) { XPutPixel(img, xx, yy, colors[pix]); } } } } else { // one color 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 & 0x80) { XPutPixel(img, xx1, yy, colors[1]); } pix <<= 1; } } } } // draw the X image 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 *FTFont::getGlyphPixmap(CharCode c, Unicode u, int *x, int *y, int *w, int *h, GBool *tempBitmap) { FT_GlyphSlot slot; FT_UInt idx; int rowSize; int i, j, k; Guchar *ret, *p, *q; // check the cache i = (c & (cacheSets - 1)) * cacheAssoc; for (j = 0; j < cacheAssoc; ++j) { if ((cacheTags[i+j].mru & 0x8000) && cacheTags[i+j].code == c) { *x = cacheTags[i+j].x; *y = cacheTags[i+j].y; *w = cacheTags[i+j].w; *h = cacheTags[i+j].h; for (k = 0; k < cacheAssoc; ++k) { if (k != j && (cacheTags[i+k].mru & 0x7fff) < (cacheTags[i+j].mru & 0x7fff)) { ++cacheTags[i+k].mru; } } cacheTags[i+j].mru = 0x8000; *tempBitmap = gFalse; return cache + (i+j) * glyphSize; } } // generate the glyph pixmap or bitmap fontFile->face->size = sizeObj; FT_Set_Transform(fontFile->face, &matrix, NULL); slot = fontFile->face->glyph; idx = getGlyphIndex(c, u); // if we have the FT2 bytecode interpreter, autohinting won't be used #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER if (FT_Load_Glyph(fontFile->face, idx, FT_LOAD_DEFAULT)) { return gFalse; } #else // FT2's autohinting doesn't always work very well (especially with // font subsets), so turn it off if anti-aliasing is enabled; if // anti-aliasing is disabled, this seems to be a tossup - some fonts // look better with hinting, some without, so leave hinting on if (FT_Load_Glyph(fontFile->face, idx, fontFile->engine->aa ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT)) { return gFalse; } #endif if (FT_Render_Glyph(slot, fontFile->engine->aa ? ft_render_mode_normal : ft_render_mode_mono)) { return gFalse; } // copy the glyph into the cache or a temporary bitmap *x = -slot->bitmap_left; *y = slot->bitmap_top; *w = slot->bitmap.width; *h = slot->bitmap.rows; if (fontFile->engine->aa) { rowSize = *w; } else { rowSize = (*w + 7) >> 3; } 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(*h * rowSize); *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 { ++cacheTags[i+j].mru; } } *tempBitmap = gFalse; } for (k = 0, p = ret, q = slot->bitmap.buffer; k < slot->bitmap.rows; ++k, p += rowSize, q += slot->bitmap.pitch) { memcpy(p, q, rowSize); } return ret; } GBool FTFont::getCharPath(CharCode c, Unicode u, GfxState *state) { static FT_Outline_Funcs outlineFuncs = { &charPathMoveTo, &charPathLineTo, &charPathConicTo, &charPathCubicTo, 0, 0 }; FT_GlyphSlot slot; FT_UInt idx; FT_Glyph glyph; fontFile->face->size = sizeObj; FT_Set_Transform(fontFile->face, &matrix, NULL); slot = fontFile->face->glyph; idx = getGlyphIndex(c, u); #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER if (FT_Load_Glyph(fontFile->face, idx, FT_LOAD_DEFAULT)) { return gFalse; } #else // FT2's autohinting doesn't always work very well (especially with // font subsets), so turn it off if anti-aliasing is enabled; if // anti-aliasing is disabled, this seems to be a tossup - some fonts // look better with hinting, some without, so leave hinting on if (FT_Load_Glyph(fontFile->face, idx, fontFile->engine->aa ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT)) { return gFalse; } #endif if (FT_Get_Glyph(slot, &glyph)) { return gFalse; } FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline, &outlineFuncs, state); return gTrue; } int FTFont::charPathMoveTo(FT_Vector *pt, void *state) { ((GfxState *)state)->moveTo(pt->x / 64.0, -pt->y / 64.0); return 0; } int FTFont::charPathLineTo(FT_Vector *pt, void *state) { ((GfxState *)state)->lineTo(pt->x / 64.0, -pt->y / 64.0); return 0; } int FTFont::charPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *state) { double x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; x0 = ((GfxState *)state)->getCurX(); y0 = ((GfxState *)state)->getCurY(); xc = ctrl->x / 64.0; yc = -ctrl->y / 64.0; x3 = pt->x / 64.0; y3 = -pt->y / 64.0; // A second-order Bezier curve is defined by two endpoints, p0 and // p3, and one control point, pc: // // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3 // // A third-order Bezier curve is defined by the same two endpoints, // p0 and p3, and two control points, p1 and p2: // // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3 // // Applying some algebra, we can convert a second-order curve to a // third-order curve: // // p1 = (1/3) * (p0 + 2pc) // p2 = (1/3) * (2pc + p3) x1 = (1.0 / 3.0) * (x0 + 2 * xc); y1 = (1.0 / 3.0) * (y0 + 2 * yc); x2 = (1.0 / 3.0) * (2 * xc + x3); y2 = (1.0 / 3.0) * (2 * yc + y3); ((GfxState *)state)->curveTo(x1, y1, x2, y2, x3, y3); return 0; } int FTFont::charPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2, FT_Vector *pt, void *state) { ((GfxState *)state)->curveTo(ctrl1->x / 64.0, -ctrl1->y / 64.0, ctrl2->x / 64.0, -ctrl2->y / 64.0, pt->x / 64.0, -pt->y / 64.0); return 0; } FT_UInt FTFont::getGlyphIndex(CharCode c, Unicode u) { FT_UInt idx; int j; idx = 0; // make gcc happy switch (fontFile->mode) { case ftFontModeUnicode: idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)u); break; case ftFontModeCharCode: idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)c); break; case ftFontModeCharCodeOffset: if ((idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)c)) == 0) { idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)(c + fontFile->charMapOffset)); } break; case ftFontModeCodeMap: if (c <= 0xff) { idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)fontFile->codeMap[c]); } else { idx = 0; } break; case ftFontModeCodeMapDirect: if (c <= 0xff) { idx = (FT_UInt)fontFile->codeMap[c]; } else { idx = 0; } break; case ftFontModeCIDToGIDMap: if (fontFile->cidToGIDLen) { if ((int)c < fontFile->cidToGIDLen) { idx = (FT_UInt)fontFile->cidToGID[c]; } else { idx = (FT_UInt)0; } } else { idx = (FT_UInt)c; } break; case ftFontModeCFFCharset: #if 1 //~ cff cid->gid map { #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 0 CFF_Font *cff = (CFF_Font *)((TT_Face)fontFile->face)->extra.data; #else CFF_Font cff = (CFF_Font)((TT_Face)fontFile->face)->extra.data; #endif idx = 0; for (j = 0; j < (int)cff->num_glyphs; ++j) { if (cff->charset.sids[j] == c) { idx = j; break; } } } #endif break; case ftFontModeCID: idx = c; break; } return idx; } #endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)