filters

FTFont.cpp
1//========================================================================
2//
3// FTFont.cpp
4//
5// Copyright 2001-2002 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <aconf.h>
10
11#if FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)
12
13#ifdef USE_GCC_PRAGMAS
14#pragma implementation
15#endif
16
17#include <math.h>
18#include <string.h>
19#include "gmem.h"
20#include "freetype/ftoutln.h"
21#include "freetype/internal/ftobjs.h"
22#if 1 //~ cff cid->gid map
23#include "freetype/internal/cfftypes.h"
24#include "freetype/internal/tttypes.h"
25#endif
26#include "GlobalParams.h"
27#include "GfxState.h"
28#include "FTFont.h"
29
30//------------------------------------------------------------------------
31
32FTFontEngine::FTFontEngine(Display *displayA, Visual *visualA, int depthA,
33 Colormap colormapA, GBool aaA):
34 SFontEngine(displayA, visualA, depthA, colormapA) {
35
36 ok = gFalse;
37 if (FT_Init_FreeType(&lib)) {
38 return;
39 }
40 aa = aaA;
41 ok = gTrue;
42}
43
44FTFontEngine::~FTFontEngine() {
45 FT_Done_FreeType(lib);
46}
47
48//------------------------------------------------------------------------
49
50FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName,
51 char **fontEnc, GBool pdfFontHasEncoding) {
52 char *name;
53 int unicodeCmap, macRomanCmap, msSymbolCmap;
54 int i, j;
55
56 ok = gFalse;
57 engine = engineA;
58 codeMap = NULL;
59 if (FT_New_Face(engine->lib, fontFileName, 0, &face)) {
60 return;
61 }
62
63 if (!strcmp(face->driver->root.clazz->module_name, "type1") ||
64 !strcmp(face->driver->root.clazz->module_name, "cff")) {
65
66 mode = ftFontModeCodeMapDirect;
67 codeMap = (Guint *)gmalloc(256 * sizeof(Guint));
68 for (i = 0; i < 256; ++i) {
69 codeMap[i] = 0;
70 if ((name = fontEnc[i])) {
71 codeMap[i] = FT_Get_Name_Index(face, name);
72 }
73 }
74
75 } else {
76
77 // To match up with the Adobe-defined behaviour, we choose a cmap
78 // like this:
79 // 1. If the PDF font has an encoding:
80 // 1a. If the TrueType font has a Microsoft Unicode cmap, use it,
81 // and use the Unicode indexes, not the char codes.
82 // 1b. If the TrueType font has a Macintosh Roman cmap, use it,
83 // and reverse map the char names through MacRomanEncoding to
84 // get char codes.
85 // 2. If the PDF font does not have an encoding:
86 // 2a. If the TrueType font has a Macintosh Roman cmap, use it,
87 // and use char codes directly.
88 // 2b. If the TrueType font has a Microsoft Symbol cmap, use it,
89 // and use (0xf000 + char code).
90 // 3. If none of these rules apply, use the first cmap and hope for
91 // the best (this shouldn't happen).
92 unicodeCmap = macRomanCmap = msSymbolCmap = 0xffff;
93 for (i = 0; i < face->num_charmaps; ++i) {
94 if (face->charmaps[i]->platform_id == 3 &&
95 face->charmaps[i]->encoding_id == 1) {
96 unicodeCmap = i;
97 } else if (face->charmaps[i]->platform_id == 1 &&
98 face->charmaps[i]->encoding_id == 0) {
99 macRomanCmap = i;
100 } else if (face->charmaps[i]->platform_id == 3 &&
101 face->charmaps[i]->encoding_id == 0) {
102 msSymbolCmap = i;
103 }
104 }
105 i = 0;
106 mode = ftFontModeCharCode;
107 charMapOffset = 0;
108 if (pdfFontHasEncoding) {
109 if (unicodeCmap != 0xffff) {
110 i = unicodeCmap;
111 mode = ftFontModeUnicode;
112 } else if (macRomanCmap != 0xffff) {
113 i = macRomanCmap;
114 mode = ftFontModeCodeMap;
115 codeMap = (Guint *)gmalloc(256 * sizeof(Guint));
116 for (j = 0; j < 256; ++j) {
117 if (fontEnc[j]) {
118 codeMap[j] = globalParams->getMacRomanCharCode(fontEnc[j]);
119 } else {
120 codeMap[j] = 0;
121 }
122 }
123 }
124 } else {
125 if (macRomanCmap != 0xffff) {
126 i = macRomanCmap;
127 mode = ftFontModeCharCode;
128 } else if (msSymbolCmap != 0xffff) {
129 i = msSymbolCmap;
130 mode = ftFontModeCharCodeOffset;
131 charMapOffset = 0xf000;
132 }
133 }
134 if (FT_Set_Charmap(face, face->charmaps[i])) {
135 return;
136 }
137 }
138
139 ok = gTrue;
140}
141
142FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName,
143 Gushort *cidToGIDA, int cidToGIDLenA) {
144 ok = gFalse;
145 engine = engineA;
146 codeMap = NULL;
147 if (FT_New_Face(engine->lib, fontFileName, 0, &face)) {
148 return;
149 }
150 cidToGID = cidToGIDA;
151 cidToGIDLen = cidToGIDLenA;
152 mode = ftFontModeCIDToGIDMap;
153 ok = gTrue;
154}
155
156FTFontFile::FTFontFile(FTFontEngine *engineA, char *fontFileName) {
157 ok = gFalse;
158 engine = engineA;
159 codeMap = NULL;
160 if (FT_New_Face(engine->lib, fontFileName, 0, &face)) {
161 return;
162 }
163 cidToGID = NULL;
164 cidToGIDLen = 0;
165 mode = ftFontModeCFFCharset;
166 ok = gTrue;
167}
168
169FTFontFile::~FTFontFile() {
170 if (face) {
171 FT_Done_Face(face);
172 }
173 if (codeMap) {
174 gfree(codeMap);
175 }
176}
177
178//------------------------------------------------------------------------
179
180FTFont::FTFont(FTFontFile *fontFileA, double *m) {
181 FTFontEngine *engine;
182 FT_Face face;
183 double size, div;
184 int x, xMin, xMax;
185 int y, yMin, yMax;
186 int i;
187
188 ok = gFalse;
189 fontFile = fontFileA;
190 engine = fontFile->engine;
191 face = fontFile->face;
192 if (FT_New_Size(face, &sizeObj)) {
193 return;
194 }
195 face->size = sizeObj;
196 size = sqrt(m[2]*m[2] + m[3]*m[3]);
197 if (FT_Set_Pixel_Sizes(face, 0, (int)size)) {
198 return;
199 }
200
201 div = face->bbox.xMax > 20000 ? 65536 : 1;
202
203 // transform the four corners of the font bounding box -- the min
204 // and max values form the bounding box of the transformed font
205 x = (int)((m[0] * face->bbox.xMin + m[2] * face->bbox.yMin) /
206 (div * face->units_per_EM));
207 xMin = xMax = x;
208 y = (int)((m[1] * face->bbox.xMin + m[3] * face->bbox.yMin) /
209 (div * face->units_per_EM));
210 yMin = yMax = y;
211 x = (int)((m[0] * face->bbox.xMin + m[2] * face->bbox.yMax) /
212 (div * face->units_per_EM));
213 if (x < xMin) {
214 xMin = x;
215 } else if (x > xMax) {
216 xMax = x;
217 }
218 y = (int)((m[1] * face->bbox.xMin + m[3] * face->bbox.yMax) /
219 (div * face->units_per_EM));
220 if (y < yMin) {
221 yMin = y;
222 } else if (y > yMax) {
223 yMax = y;
224 }
225 x = (int)((m[0] * face->bbox.xMax + m[2] * face->bbox.yMin) /
226 (div * face->units_per_EM));
227 if (x < xMin) {
228 xMin = x;
229 } else if (x > xMax) {
230 xMax = x;
231 }
232 y = (int)((m[1] * face->bbox.xMax + m[3] * face->bbox.yMin) /
233 (div * face->units_per_EM));
234 if (y < yMin) {
235 yMin = y;
236 } else if (y > yMax) {
237 yMax = y;
238 }
239 x = (int)((m[0] * face->bbox.xMax + m[2] * face->bbox.yMax) /
240 (div * face->units_per_EM));
241 if (x < xMin) {
242 xMin = x;
243 } else if (x > xMax) {
244 xMax = x;
245 }
246 y = (int)((m[1] * face->bbox.xMax + m[3] * face->bbox.yMax) /
247 (div * face->units_per_EM));
248 if (y < yMin) {
249 yMin = y;
250 } else if (y > yMax) {
251 yMax = y;
252 }
253 // This is a kludge: some buggy PDF generators embed fonts with
254 // zero bounding boxes.
255 if (xMax == xMin) {
256 xMin = 0;
257 xMax = (int)size;
258 }
259 if (yMax == yMin) {
260 yMin = 0;
261 yMax = (int)(1.2 * size);
262 }
263 // this should be (max - min + 1), but we add some padding to
264 // deal with rounding errors, bogus bboxes, etc.
265 glyphW = xMax - xMin + 3;
266 glyphW += glyphW >> 1;
267 glyphH = yMax - yMin + 3;
268 glyphH += glyphH >> 1;
269 if (engine->aa) {
270 glyphSize = glyphW * glyphH;
271 } else {
272 glyphSize = ((glyphW + 7) >> 3) * glyphH;
273 }
274
275 // set up the glyph pixmap cache
276 cacheAssoc = 8;
277 if (glyphSize <= 256) {
278 cacheSets = 8;
279 } else if (glyphSize <= 512) {
280 cacheSets = 4;
281 } else if (glyphSize <= 1024) {
282 cacheSets = 2;
283 } else {
284 cacheSets = 1;
285 }
286 cache = (Guchar *)gmalloc(cacheSets * cacheAssoc * glyphSize);
287 cacheTags = (FTFontCacheTag *)gmalloc(cacheSets * cacheAssoc *
288 sizeof(FTFontCacheTag));
289 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
290 cacheTags[i].mru = i & (cacheAssoc - 1);
291 }
292
293 // create the XImage
294 if (!(image = XCreateImage(engine->display, engine->visual, engine->depth,
295 ZPixmap, 0, NULL, glyphW, glyphH, 8, 0))) {
296 return;
297 }
298 image->data = (char *)gmalloc(glyphH * image->bytes_per_line);
299
300 // compute the transform matrix
301 matrix.xx = (FT_Fixed)((m[0] / size) * 65536);
302 matrix.yx = (FT_Fixed)((m[1] / size) * 65536);
303 matrix.xy = (FT_Fixed)((m[2] / size) * 65536);
304 matrix.yy = (FT_Fixed)((m[3] / size) * 65536);
305
306 ok = gTrue;
307}
308
309FTFont::~FTFont() {
310 gfree(cacheTags);
311 gfree(cache);
312 gfree(image->data);
313 image->data = NULL;
314 XDestroyImage(image);
315}
316
317GBool FTFont::drawChar(Drawable d, int w, int h, GC gc,
318 int x, int y, int r, int g, int b,
319 CharCode c, Unicode u) {
320 FTFontEngine *engine;
321 XColor xcolor;
322 int bgR, bgG, bgB;
323 Gulong colors[5];
324 Guchar *p;
325 int pix;
326 int xOffset, yOffset, x0, y0, x1, y1, gw, gh, w0, h0;
327 int xx, yy, xx1;
328
329 engine = fontFile->engine;
330
331 // no Unicode index for this char - don't draw anything
332 if (fontFile->mode == ftFontModeUnicode && u == 0) {
333 return gFalse;
334 }
335
336 // generate the glyph pixmap
337 if (!(p = getGlyphPixmap(c, u, &xOffset, &yOffset, &gw, &gh))) {
338 return gFalse;
339 }
340
341 // compute: (x0,y0) = position in destination drawable
342 // (x1,y1) = position in glyph image
343 // (w0,h0) = size of image transfer
344 x0 = x - xOffset;
345 y0 = y - yOffset;
346 x1 = 0;
347 y1 = 0;
348 w0 = gw;
349 h0 = gh;
350 if (x0 < 0) {
351 x1 = -x0;
352 w0 += x0;
353 x0 = 0;
354 }
355 if (x0 + w0 > w) {
356 w0 = w - x0;
357 }
358 if (w0 < 0) {
359 return gTrue;
360 }
361 if (y0 < 0) {
362 y1 = -y0;
363 h0 += y0;
364 y0 = 0;
365 }
366 if (y0 + h0 > h) {
367 h0 = h - y0;
368 }
369 if (h0 < 0) {
370 return gTrue;
371 }
372
373 // read the X image
374 XGetSubImage(engine->display, d, x0, y0, w0, h0, (1 << engine->depth) - 1,
375 ZPixmap, image, x1, y1);
376
377 if (engine->aa) {
378
379 // compute the colors
380 xcolor.pixel = XGetPixel(image, x1 + w0/2, y1 + h0/2);
381 XQueryColor(engine->display, engine->colormap, &xcolor);
382 bgR = xcolor.red;
383 bgG = xcolor.green;
384 bgB = xcolor.blue;
385 colors[1] = engine->findColor((r + 3*bgR) / 4,
386 (g + 3*bgG) / 4,
387 (b + 3*bgB) / 4);
388 colors[2] = engine->findColor((r + bgR) / 2,
389 (g + bgG) / 2,
390 (b + bgB) / 2);
391 colors[3] = engine->findColor((3*r + bgR) / 4,
392 (3*g + bgG) / 4,
393 (3*b + bgB) / 4);
394 colors[4] = engine->findColor(r, g, b);
395
396 // stuff the glyph pixmap into the X image
397 for (yy = 0; yy < gh; ++yy) {
398 for (xx = 0; xx < gw; ++xx) {
399 pix = *p++ & 0xff;
400 // this is a heuristic which seems to produce decent
401 // results -- the linear mapping would be:
402 // pix = (pix * 5) / 256;
403 pix = ((pix + 10) * 5) / 256;
404 if (pix > 4) {
405 pix = 4;
406 }
407 if (pix > 0) {
408 XPutPixel(image, xx, yy, colors[pix]);
409 }
410 }
411 }
412
413 } else {
414
415 // one color
416 colors[1] = engine->findColor(r, g, b);
417
418 // stuff the glyph bitmap into the X image
419 for (yy = 0; yy < gh; ++yy) {
420 for (xx = 0; xx < gw; xx += 8) {
421 pix = *p++;
422 for (xx1 = xx; xx1 < xx + 8 && xx1 < gw; ++xx1) {
423 if (pix & 0x80) {
424 XPutPixel(image, xx1, yy, colors[1]);
425 }
426 pix <<= 1;
427 }
428 }
429 }
430
431 }
432
433 // draw the X image
434 XPutImage(engine->display, d, gc, image, x1, y1, x0, y0, w0, h0);
435
436 return gTrue;
437}
438
439Guchar *FTFont::getGlyphPixmap(CharCode c, Unicode u,
440 int *x, int *y, int *w, int *h) {
441 FT_GlyphSlot slot;
442 FT_UInt idx;
443 int rowSize;
444 int i, j, k;
445 Guchar *ret, *p, *q;
446
447 // check the cache
448 i = (c & (cacheSets - 1)) * cacheAssoc;
449 for (j = 0; j < cacheAssoc; ++j) {
450 if ((cacheTags[i+j].mru & 0x8000) && cacheTags[i+j].code == c) {
451 *x = cacheTags[i+j].x;
452 *y = cacheTags[i+j].y;
453 *w = cacheTags[i+j].w;
454 *h = cacheTags[i+j].h;
455 for (k = 0; k < cacheAssoc; ++k) {
456 if (k != j &&
457 (cacheTags[i+k].mru & 0x7fff) < (cacheTags[i+j].mru & 0x7fff)) {
458 ++cacheTags[i+k].mru;
459 }
460 }
461 cacheTags[i+j].mru = 0x8000;
462 return cache + (i+j) * glyphSize;
463 }
464 }
465
466 // generate the glyph pixmap or bitmap
467 fontFile->face->size = sizeObj;
468 FT_Set_Transform(fontFile->face, &matrix, NULL);
469 slot = fontFile->face->glyph;
470 idx = getGlyphIndex(c, u);
471 // if we have the FT2 bytecode interpreter, autohinting won't be used
472#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
473 if (FT_Load_Glyph(fontFile->face, idx, FT_LOAD_DEFAULT)) {
474 return gFalse;
475 }
476#else
477 // FT2's autohinting doesn't always work very well (especially with
478 // font subsets), so turn it off if anti-aliasing is enabled; if
479 // anti-aliasing is disabled, this seems to be a tossup - some fonts
480 // look better with hinting, some without, so leave hinting on
481 if (FT_Load_Glyph(fontFile->face, idx,
482 fontFile->engine->aa ? FT_LOAD_NO_HINTING
483 : FT_LOAD_DEFAULT)) {
484 return gFalse;
485 }
486#endif
487 if (FT_Render_Glyph(slot,
488 fontFile->engine->aa ? ft_render_mode_normal :
489 ft_render_mode_mono)) {
490 return gFalse;
491 }
492 *x = -slot->bitmap_left;
493 *y = slot->bitmap_top;
494 *w = slot->bitmap.width;
495 *h = slot->bitmap.rows;
496 if (*w > glyphW || *h > glyphH) {
497#if 1 //~ debug
498 fprintf(stderr, "Weird FreeType glyph size: %d > %d or %d > %d\n",
499 *w, glyphW, *h, glyphH);
500#endif
501 return NULL;
502 }
503
504 // store glyph pixmap in cache
505 ret = NULL;
506 for (j = 0; j < cacheAssoc; ++j) {
507 if ((cacheTags[i+j].mru & 0x7fff) == cacheAssoc - 1) {
508 cacheTags[i+j].mru = 0x8000;
509 cacheTags[i+j].code = c;
510 cacheTags[i+j].x = *x;
511 cacheTags[i+j].y = *y;
512 cacheTags[i+j].w = *w;
513 cacheTags[i+j].h = *h;
514 if (fontFile->engine->aa) {
515 rowSize = *w;
516 } else {
517 rowSize = (*w + 7) >> 3;
518 }
519 ret = cache + (i+j) * glyphSize;
520 for (k = 0, p = ret, q = slot->bitmap.buffer;
521 k < slot->bitmap.rows;
522 ++k, p += rowSize, q += slot->bitmap.pitch) {
523 memcpy(p, q, rowSize);
524 }
525 } else {
526 ++cacheTags[i+j].mru;
527 }
528 }
529 return ret;
530}
531
532GBool FTFont::getCharPath(CharCode c, Unicode u, GfxState *state) {
533 static FT_Outline_Funcs outlineFuncs = {
534 &charPathMoveTo,
535 &charPathLineTo,
536 &charPathConicTo,
537 &charPathCubicTo,
538 0, 0
539 };
540 FT_GlyphSlot slot;
541 FT_UInt idx;
542 FT_Glyph glyph;
543
544 fontFile->face->size = sizeObj;
545 FT_Set_Transform(fontFile->face, &matrix, NULL);
546 slot = fontFile->face->glyph;
547 idx = getGlyphIndex(c, u);
548#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
549 if (FT_Load_Glyph(fontFile->face, idx, FT_LOAD_DEFAULT)) {
550 return gFalse;
551 }
552#else
553 // FT2's autohinting doesn't always work very well (especially with
554 // font subsets), so turn it off if anti-aliasing is enabled; if
555 // anti-aliasing is disabled, this seems to be a tossup - some fonts
556 // look better with hinting, some without, so leave hinting on
557 if (FT_Load_Glyph(fontFile->face, idx,
558 fontFile->engine->aa ? FT_LOAD_NO_HINTING
559 : FT_LOAD_DEFAULT)) {
560 return gFalse;
561 }
562#endif
563 if (FT_Get_Glyph(slot, &glyph)) {
564 return gFalse;
565 }
566 FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
567 &outlineFuncs, state);
568 return gTrue;
569}
570
571int FTFont::charPathMoveTo(FT_Vector *pt, void *state) {
572 ((GfxState *)state)->moveTo(pt->x / 64.0, -pt->y / 64.0);
573 return 0;
574}
575
576int FTFont::charPathLineTo(FT_Vector *pt, void *state) {
577 ((GfxState *)state)->lineTo(pt->x / 64.0, -pt->y / 64.0);
578 return 0;
579}
580
581int FTFont::charPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *state) {
582 double x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
583
584 x0 = ((GfxState *)state)->getCurX();
585 y0 = ((GfxState *)state)->getCurY();
586 xc = ctrl->x / 64.0;
587 yc = -ctrl->y / 64.0;
588 x3 = pt->x / 64.0;
589 y3 = -pt->y / 64.0;
590
591 // A second-order Bezier curve is defined by two endpoints, p0 and
592 // p3, and one control point, pc:
593 //
594 // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
595 //
596 // A third-order Bezier curve is defined by the same two endpoints,
597 // p0 and p3, and two control points, p1 and p2:
598 //
599 // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
600 //
601 // Applying some algebra, we can convert a second-order curve to a
602 // third-order curve:
603 //
604 // p1 = (1/3) * (p0 + 2pc)
605 // p2 = (1/3) * (2pc + p3)
606
607 x1 = (1.0 / 3.0) * (x0 + 2 * xc);
608 y1 = (1.0 / 3.0) * (y0 + 2 * yc);
609 x2 = (1.0 / 3.0) * (2 * xc + x3);
610 y2 = (1.0 / 3.0) * (2 * yc + y3);
611
612 ((GfxState *)state)->curveTo(x1, y1, x2, y2, x3, y3);
613 return 0;
614}
615
616int FTFont::charPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2,
617 FT_Vector *pt, void *state) {
618 ((GfxState *)state)->curveTo(ctrl1->x / 64.0, -ctrl1->y / 64.0,
619 ctrl2->x / 64.0, -ctrl2->y / 64.0,
620 pt->x / 64.0, -pt->y / 64.0);
621 return 0;
622}
623
624FT_UInt FTFont::getGlyphIndex(CharCode c, Unicode u) {
625 FT_UInt idx;
626 int j;
627
628 idx = 0; // make gcc happy
629 switch (fontFile->mode) {
630 case ftFontModeUnicode:
631 idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)u);
632 break;
633 case ftFontModeCharCode:
634 idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)c);
635 break;
636 case ftFontModeCharCodeOffset:
637 idx = FT_Get_Char_Index(fontFile->face,
638 (FT_ULong)(c + fontFile->charMapOffset));
639 break;
640 case ftFontModeCodeMap:
641 if (c <= 0xff) {
642 idx = FT_Get_Char_Index(fontFile->face, (FT_ULong)fontFile->codeMap[c]);
643 } else {
644 idx = 0;
645 }
646 break;
647 case ftFontModeCodeMapDirect:
648 if (c <= 0xff) {
649 idx = (FT_UInt)fontFile->codeMap[c];
650 } else {
651 idx = 0;
652 }
653 break;
654 case ftFontModeCIDToGIDMap:
655 if (fontFile->cidToGIDLen) {
656 if ((int)c < fontFile->cidToGIDLen) {
657 idx = (FT_UInt)fontFile->cidToGID[c];
658 } else {
659 idx = (FT_UInt)0;
660 }
661 } else {
662 idx = (FT_UInt)c;
663 }
664 break;
665 case ftFontModeCFFCharset:
666#if 1 //~ cff cid->gid map
667#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 0
668 CFF_Font *cff = (CFF_Font *)((TT_Face)fontFile->face)->extra.data;
669#else
670 CFF_Font cff = (CFF_Font)((TT_Face)fontFile->face)->extra.data;
671#endif
672 idx = 0;
673 for (j = 0; j < (int)cff->num_glyphs; ++j) {
674 if (cff->charset.sids[j] == c) {
675 idx = j;
676 break;
677 }
678 }
679#endif
680 break;
681 }
682 return idx;
683}
684
685#endif // FREETYPE2 && (HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H)