You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
783 lines
20 KiB
783 lines
20 KiB
//======================================================================== |
|
// |
|
// FTFont.cc |
|
// |
|
// Copyright 2001-2003 Glyph & Cog, LLC |
|
// |
|
//======================================================================== |
|
|
|
#include <aconf.h> |
|
|
|
#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H) |
|
|
|
#ifdef USE_GCC_PRAGMAS |
|
#pragma implementation |
|
#endif |
|
|
|
#include <math.h> |
|
#include <string.h> |
|
#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)
|
|
|