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

//========================================================================
//
// 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)