//======================================================================== // // TTFont.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 "gmem.h" #include "GlobalParams.h" #include "TTFont.h" //------------------------------------------------------------------------ TTFontEngine::TTFontEngine(Display *displayA, Visual *visualA, int depthA, Colormap colormapA, GBool aaA): SFontEngine(displayA, visualA, depthA, colormapA) { static TT_Byte ttPalette[5] = {0, 1, 2, 3, 4}; ok = gFalse; if (TT_Init_FreeType(&engine)) { return; } aa = aaA; if (aa) { if (TT_Set_Raster_Gray_Palette(engine, ttPalette)) { return; } } ok = gTrue; } TTFontEngine::~TTFontEngine() { TT_Done_FreeType(engine); } //------------------------------------------------------------------------ TTFontFile::TTFontFile(TTFontEngine *engineA, char *fontFileName, char **fontEnc, GBool pdfFontHasEncoding) { TT_Face_Properties props; TT_UShort unicodeCmap, macRomanCmap, msSymbolCmap; TT_UShort platform, encoding, i; int j; ok = gFalse; engine = engineA; codeMap = NULL; if (TT_Open_Face(engine->engine, fontFileName, &face)) { return; } if (TT_Get_Face_Properties(face, &props)) { return; } // 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 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 < props.num_CharMaps; ++i) { if (!TT_Get_CharMap_ID(face, i, &platform, &encoding)) { if ((platform == 3 && encoding == 1) || platform == 0) { unicodeCmap = i; } else if (platform == 1 && encoding == 0) { macRomanCmap = i; } else if (platform == 3 && encoding == 0) { msSymbolCmap = i; } } } i = 0; mode = ttFontModeCharCode; charMapOffset = 0; if (pdfFontHasEncoding) { if (unicodeCmap != 0xffff) { i = unicodeCmap; mode = ttFontModeUnicode; } else if (macRomanCmap != 0xffff) { i = macRomanCmap; mode = ttFontModeCodeMap; codeMap = (Guchar *)gmalloc(256 * sizeof(Guchar)); for (j = 0; j < 256; ++j) { if (fontEnc[j]) { codeMap[j] = (Guchar)globalParams->getMacRomanCharCode(fontEnc[j]); } else { codeMap[j] = 0; } } } } else { if (macRomanCmap != 0xffff) { i = macRomanCmap; mode = ttFontModeCharCode; } else if (msSymbolCmap != 0xffff) { i = msSymbolCmap; mode = ttFontModeCharCodeOffset; charMapOffset = 0xf000; } } TT_Get_CharMap(face, i, &charMap); ok = gTrue; } TTFontFile::TTFontFile(TTFontEngine *engineA, char *fontFileName, Gushort *cidToGIDA, int cidToGIDLenA) { ok = gFalse; engine = engineA; codeMap = NULL; cidToGID = cidToGIDA; cidToGIDLen = cidToGIDLenA; if (TT_Open_Face(engine->engine, fontFileName, &face)) { return; } mode = ttFontModeCIDToGIDMap; ok = gTrue; } TTFontFile::~TTFontFile() { TT_Close_Face(face); if (codeMap) { gfree(codeMap); } } //------------------------------------------------------------------------ TTFont::TTFont(TTFontFile *fontFileA, double *m) { TTFontEngine *engine; TT_Face_Properties props; TT_Instance_Metrics metrics; int x, xMin, xMax; int y, yMin, yMax; int i; ok = gFalse; fontFile = fontFileA; engine = fontFile->engine; if (TT_New_Instance(fontFile->face, &instance) || TT_Set_Instance_Resolutions(instance, 72, 72) || TT_Set_Instance_CharSize(instance, 1000 * 64) || TT_New_Glyph(fontFile->face, &glyph) || TT_Get_Face_Properties(fontFile->face, &props) || TT_Get_Instance_Metrics(instance, &metrics)) { return; } // 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] * props.header->xMin + m[2] * props.header->yMin) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); xMin = xMax = x; y = (int)((m[1] * props.header->xMin + m[3] * props.header->yMin) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); yMin = yMax = y; x = (int)((m[0] * props.header->xMin + m[2] * props.header->yMax) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((m[1] * props.header->xMin + m[3] * props.header->yMax) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((m[0] * props.header->xMax + m[2] * props.header->yMin) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((m[1] * props.header->xMax + m[3] * props.header->yMin) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } x = (int)((m[0] * props.header->xMax + m[2] * props.header->yMax) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); if (x < xMin) { xMin = x; } else if (x > xMax) { xMax = x; } y = (int)((m[1] * props.header->xMax + m[3] * props.header->yMax) * 0.001 * metrics.x_ppem / props.header->Units_Per_EM); if (y < yMin) { yMin = y; } else if (y > yMax) { yMax = y; } xOffset = -xMin; yOffset = -yMin; ras.width = xMax - xMin + 1; ras.rows = yMax - yMin + 1; // set up the Raster_Map structure if (engine->aa) { ras.width = (ras.width + 3) & ~3; ras.cols = ras.width; } else { ras.width = (ras.width + 7) & ~7; ras.cols = ras.width >> 3; } ras.flow = TT_Flow_Down; ras.size = ras.rows * ras.cols; ras.bitmap = gmalloc(ras.size); // set up the glyph pixmap cache cacheAssoc = 8; if (ras.size <= 256) { cacheSets = 8; } else if (ras.size <= 512) { cacheSets = 4; } else if (ras.size <= 1024) { cacheSets = 2; } else { cacheSets = 1; } cache = (Guchar *)gmalloc(cacheSets * cacheAssoc * ras.size); cacheTags = (TTFontCacheTag *)gmalloc(cacheSets * cacheAssoc * sizeof(TTFontCacheTag)); 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, ras.width, ras.rows, 8, 0))) { return; } image->data = (char *)gmalloc(ras.rows * image->bytes_per_line); // compute the transform matrix matrix.xx = (TT_Fixed)(m[0] * 65.536); matrix.yx = (TT_Fixed)(m[1] * 65.536); matrix.xy = (TT_Fixed)(m[2] * 65.536); matrix.yy = (TT_Fixed)(m[3] * 65.536); ok = gTrue; } TTFont::~TTFont() { gfree(cacheTags); gfree(cache); gfree(image->data); image->data = NULL; XDestroyImage(image); gfree(ras.bitmap); TT_Done_Glyph(glyph); TT_Done_Instance(instance); } GBool TTFont::drawChar(Drawable d, int w, int h, GC gc, int x, int y, int r, int g, int b, CharCode c, Unicode u) { TTFontEngine *engine; XColor xcolor; int bgR, bgG, bgB; Gulong colors[5]; TT_Byte *p; TT_Byte pix; int xx, yy, xx1; int x0, y0, x1, y1, w0, h0; engine = fontFile->engine; // compute: (x0,y0) = position in destination drawable // (x1,y1) = position in glyph image // (w0,h0) = size of image transfer x0 = x - xOffset; y0 = y - (ras.rows - yOffset); x1 = 0; y1 = 0; w0 = ras.width; h0 = ras.rows; if (x0 < 0) { x1 = -x0; w0 += x0; x0 = 0; } if (x0 + w0 > w) { w0 = w - x0; } if (w0 < 0) { return gTrue; } if (y0 < 0) { y1 = -y0; h0 += y0; y0 = 0; } if (y0 + h0 > h) { h0 = h - y0; } if (h0 < 0) { return gTrue; } // read the X image XGetSubImage(engine->display, d, x0, y0, w0, h0, (1 << engine->depth) - 1, ZPixmap, image, x1, y1); // generate the glyph pixmap if (!getGlyphPixmap(c, u)) { return gFalse; } if (engine->aa) { // compute the colors xcolor.pixel = XGetPixel(image, 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 = (TT_Byte *)ras.bitmap; for (yy = 0; yy < ras.rows; ++yy) { for (xx = 0; xx < ras.width; ++xx) { pix = *p++; if (pix > 0) { if (pix > 4) { pix = 4; } XPutPixel(image, xx, yy, colors[pix]); } } } } else { // one color colors[1] = engine->findColor(r, g, b); // stuff the glyph bitmap into the X image p = (TT_Byte *)ras.bitmap; for (yy = 0; yy < ras.rows; ++yy) { for (xx = 0; xx < ras.width; xx += 8) { pix = *p++; for (xx1 = xx; xx1 < xx + 8 && xx1 < ras.width; ++xx1) { if (pix & 0x80) { XPutPixel(image, xx1, yy, colors[1]); } pix <<= 1; } } } } // draw the X image XPutImage(engine->display, d, gc, image, x1, y1, x0, y0, w0, h0); return gTrue; } GBool TTFont::getGlyphPixmap(CharCode c, Unicode u) { TT_UShort idx; TT_Outline outline; int i, j, k; // 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) { memcpy(ras.bitmap, cache + (i+j) * ras.size, ras.size); 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; return gTrue; } } // generate the glyph pixmap or bitmap idx = 0; // make gcc happy switch (fontFile->mode) { case ttFontModeUnicode: idx = TT_Char_Index(fontFile->charMap, (TT_UShort)u); break; case ttFontModeCharCode: idx = TT_Char_Index(fontFile->charMap, (TT_UShort)c); break; case ttFontModeCharCodeOffset: idx = TT_Char_Index(fontFile->charMap, (TT_UShort)(c + fontFile->charMapOffset)); break; case ttFontModeCodeMap: if (c <= 0xff) { idx = TT_Char_Index(fontFile->charMap, (TT_UShort)(fontFile->codeMap[c] & 0xff)); } else { idx = 0; } break; case ttFontModeCIDToGIDMap: if (fontFile->cidToGIDLen) { if ((int)c < fontFile->cidToGIDLen) { idx = (TT_UShort)fontFile->cidToGID[c]; } else { idx = (TT_UShort)0; } } else { idx = (TT_UShort)c; } break; } if (TT_Load_Glyph(instance, glyph, idx, TTLOAD_DEFAULT) || TT_Get_Glyph_Outline(glyph, &outline)) { return gFalse; } TT_Transform_Outline(&outline, &matrix); memset(ras.bitmap, 0, ras.size); if (fontFile->engine->aa) { if (TT_Get_Glyph_Pixmap(glyph, &ras, xOffset * 64, yOffset * 64)) { return gFalse; } } else { if (TT_Get_Glyph_Bitmap(glyph, &ras, xOffset * 64, yOffset * 64)) { return gFalse; } } // store glyph pixmap in cache for (j = 0; j < cacheAssoc; ++j) { if ((cacheTags[i+j].mru & 0x7fff) == cacheAssoc - 1) { cacheTags[i+j].mru = 0x8000; cacheTags[i+j].code = c; memcpy(cache + (i+j) * ras.size, ras.bitmap, ras.size); } else { ++cacheTags[i+j].mru; } } return gTrue; } #endif // !FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)