1 //========================================================================
2 //
3 // SplashFTFont.cc
4 //
5 // Copyright 2003-2013 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
12
13 #ifdef USE_GCC_PRAGMAS
14 #pragma implementation
15 #endif
16
17 #include <ft2build.h>
18 #include FT_OUTLINE_H
19 #include FT_SIZES_H
20 #include FT_GLYPH_H
21 #include "gmem.h"
22 #include "SplashMath.h"
23 #include "SplashGlyphBitmap.h"
24 #include "SplashPath.h"
25 #include "SplashFontEngine.h"
26 #include "SplashFTFontEngine.h"
27 #include "SplashFTFontFile.h"
28 #include "SplashFTFont.h"
29
30 //------------------------------------------------------------------------
31
32 static int glyphPathMoveTo(const FT_Vector *pt, void *path);
33 static int glyphPathLineTo(const FT_Vector *pt, void *path);
34 static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
35 void *path);
36 static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
37 const FT_Vector *pt, void *path);
38
39 //------------------------------------------------------------------------
40 // SplashFTFont
41 //------------------------------------------------------------------------
42
SplashFTFont(SplashFTFontFile * fontFileA,SplashCoord * matA,SplashCoord * textMatA)43 SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
44 SplashCoord *textMatA):
45 SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)
46 {
47 FT_Face face;
48 int size, div;
49 int x, y;
50 #if USE_FIXEDPOINT
51 SplashCoord scale;
52 #endif
53
54 face = fontFileA->face;
55 if (FT_New_Size(face, &sizeObj)) {
56 return;
57 }
58 face->size = sizeObj;
59 size = splashRound(splashDist(0, 0, mat[2], mat[3]));
60 if (size < 1) {
61 size = 1;
62 }
63 if (FT_Set_Pixel_Sizes(face, 0, size)) {
64 return;
65 }
66 // if the textMat values are too small, FreeType's fixed point
67 // arithmetic doesn't work so well
68 textScale = splashDist(0, 0, textMat[2], textMat[3]) / size;
69
70 div = face->bbox.xMax > 20000 ? 65536 : 1;
71
72 #if USE_FIXEDPOINT
73 scale = (SplashCoord)1 / (SplashCoord)face->units_per_EM;
74
75 // transform the four corners of the font bounding box -- the min
76 // and max values form the bounding box of the transformed font
77 x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) +
78 mat[2] * (scale * (face->bbox.yMin / div)));
79 xMin = xMax = x;
80 y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) +
81 mat[3] * (scale * (face->bbox.yMin / div)));
82 yMin = yMax = y;
83 x = (int)(mat[0] * (scale * (face->bbox.xMin / div)) +
84 mat[2] * (scale * (face->bbox.yMax / div)));
85 if (x < xMin) {
86 xMin = x;
87 } else if (x > xMax) {
88 xMax = x;
89 }
90 y = (int)(mat[1] * (scale * (face->bbox.xMin / div)) +
91 mat[3] * (scale * (face->bbox.yMax / div)));
92 if (y < yMin) {
93 yMin = y;
94 } else if (y > yMax) {
95 yMax = y;
96 }
97 x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) +
98 mat[2] * (scale * (face->bbox.yMin / div)));
99 if (x < xMin) {
100 xMin = x;
101 } else if (x > xMax) {
102 xMax = x;
103 }
104 y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) +
105 mat[3] * (scale * (face->bbox.yMin / div)));
106 if (y < yMin) {
107 yMin = y;
108 } else if (y > yMax) {
109 yMax = y;
110 }
111 x = (int)(mat[0] * (scale * (face->bbox.xMax / div)) +
112 mat[2] * (scale * (face->bbox.yMax / div)));
113 if (x < xMin) {
114 xMin = x;
115 } else if (x > xMax) {
116 xMax = x;
117 }
118 y = (int)(mat[1] * (scale * (face->bbox.xMax / div)) +
119 mat[3] * (scale * (face->bbox.yMax / div)));
120 if (y < yMin) {
121 yMin = y;
122 } else if (y > yMax) {
123 yMax = y;
124 }
125 #else // USE_FIXEDPOINT
126 // transform the four corners of the font bounding box -- the min
127 // and max values form the bounding box of the transformed font
128 x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
129 (div * face->units_per_EM));
130 xMin = xMax = x;
131 y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
132 (div * face->units_per_EM));
133 yMin = yMax = y;
134 x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
135 (div * face->units_per_EM));
136 if (x < xMin) {
137 xMin = x;
138 } else if (x > xMax) {
139 xMax = x;
140 }
141 y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
142 (div * face->units_per_EM));
143 if (y < yMin) {
144 yMin = y;
145 } else if (y > yMax) {
146 yMax = y;
147 }
148 x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
149 (div * face->units_per_EM));
150 if (x < xMin) {
151 xMin = x;
152 } else if (x > xMax) {
153 xMax = x;
154 }
155 y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
156 (div * face->units_per_EM));
157 if (y < yMin) {
158 yMin = y;
159 } else if (y > yMax) {
160 yMax = y;
161 }
162 x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
163 (div * face->units_per_EM));
164 if (x < xMin) {
165 xMin = x;
166 } else if (x > xMax) {
167 xMax = x;
168 }
169 y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
170 (div * face->units_per_EM));
171 if (y < yMin) {
172 yMin = y;
173 } else if (y > yMax) {
174 yMax = y;
175 }
176 #endif // USE_FIXEDPOINT
177 // This is a kludge: some buggy PDF generators embed fonts with
178 // zero bounding boxes.
179 if (xMax == xMin) {
180 xMin = 0;
181 xMax = size;
182 }
183 if (yMax == yMin) {
184 yMin = 0;
185 yMax = (int)((SplashCoord)1.2 * size);
186 }
187
188 // compute the transform matrix
189 #if USE_FIXEDPOINT
190 matrix.xx = (FT_Fixed)((mat[0] / size).get16Dot16());
191 matrix.yx = (FT_Fixed)((mat[1] / size).get16Dot16());
192 matrix.xy = (FT_Fixed)((mat[2] / size).get16Dot16());
193 matrix.yy = (FT_Fixed)((mat[3] / size).get16Dot16());
194 textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)).get16Dot16());
195 textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)).get16Dot16());
196 textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)).get16Dot16());
197 textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)).get16Dot16());
198 #else
199 matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
200 matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
201 matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
202 matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
203 textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536);
204 textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536);
205 textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536);
206 textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536);
207 #endif
208 }
209
~SplashFTFont()210 SplashFTFont::~SplashFTFont() {
211 }
212
getGlyph(int c,int xFrac,int yFrac,SplashGlyphBitmap * bitmap)213 GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
214 SplashGlyphBitmap *bitmap) {
215 return SplashFont::getGlyph(c, xFrac, 0, bitmap);
216 }
217
makeGlyph(int c,int xFrac,int yFrac,SplashGlyphBitmap * bitmap)218 GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
219 SplashGlyphBitmap *bitmap) {
220 SplashFTFontFile *ff;
221 FT_Vector offset;
222 FT_GlyphSlot slot;
223 FT_UInt gid;
224 FT_Int32 flags;
225 int rowSize;
226 Guchar *p, *q;
227 int i;
228
229 ff = (SplashFTFontFile *)fontFile;
230
231 ff->face->size = sizeObj;
232 offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64);
233 offset.y = 0;
234 FT_Set_Transform(ff->face, &matrix, &offset);
235 slot = ff->face->glyph;
236
237 if (ff->codeToGID && c < ff->codeToGIDLen) {
238 gid = (FT_UInt)ff->codeToGID[c];
239 } else {
240 gid = (FT_UInt)c;
241 }
242 if (ff->trueType && gid < 0) {
243 // skip the TrueType notdef glyph
244 return gFalse;
245 }
246
247 // Set up the load flags:
248 // * disable bitmaps because they look ugly when scaled, rotated,
249 // etc.
250 // * disable autohinting because it can fail badly with font subsets
251 // that use invalid glyph names (the FreeType autohinter depends
252 // on the glyph name to figure out how to autohint the glyph)
253 // * but enable light autohinting for Type 1 fonts because regular
254 // hinting looks pretty bad, and the invalid glyph name issue
255 // seems to be very rare (Type 1 fonts are mostly used for
256 // substitution, in which case the full font is being used, which
257 // means we have the glyph names)
258 flags = FT_LOAD_NO_BITMAP;
259 if (ff->engine->flags & splashFTNoHinting) {
260 flags |= FT_LOAD_NO_HINTING;
261 } else if (ff->useLightHinting) {
262 flags |= FT_LOAD_TARGET_LIGHT;
263 } else {
264 flags |= FT_LOAD_NO_AUTOHINT;
265 }
266 if (FT_Load_Glyph(ff->face, gid, flags)) {
267 return gFalse;
268 }
269 if (FT_Render_Glyph(slot, aa ? FT_RENDER_MODE_NORMAL
270 : FT_RENDER_MODE_MONO)) {
271 return gFalse;
272 }
273 if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) {
274 // this can happen if (a) the glyph is really tiny or (b) the
275 // metrics in the TrueType file are broken
276 return gFalse;
277 }
278
279 bitmap->x = -slot->bitmap_left;
280 bitmap->y = slot->bitmap_top;
281 bitmap->w = slot->bitmap.width;
282 bitmap->h = slot->bitmap.rows;
283 bitmap->aa = aa;
284 if (aa) {
285 rowSize = bitmap->w;
286 } else {
287 rowSize = (bitmap->w + 7) >> 3;
288 }
289 bitmap->data = (Guchar *)gmallocn(bitmap->h, rowSize);
290 bitmap->freeData = gTrue;
291 for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
292 i < bitmap->h;
293 ++i, p += rowSize, q += slot->bitmap.pitch) {
294 memcpy(p, q, rowSize);
295 }
296
297 return gTrue;
298 }
299
300 struct SplashFTFontPath {
301 SplashPath *path;
302 SplashCoord textScale;
303 GBool needClose;
304 };
305
getGlyphPath(int c)306 SplashPath *SplashFTFont::getGlyphPath(int c) {
307 static FT_Outline_Funcs outlineFuncs = {
308 #if FREETYPE_MINOR <= 1
309 (int (*)(FT_Vector *, void *))&glyphPathMoveTo,
310 (int (*)(FT_Vector *, void *))&glyphPathLineTo,
311 (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo,
312 (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo,
313 #else
314 &glyphPathMoveTo,
315 &glyphPathLineTo,
316 &glyphPathConicTo,
317 &glyphPathCubicTo,
318 #endif
319 0, 0
320 };
321 SplashFTFontFile *ff;
322 SplashFTFontPath path;
323 FT_GlyphSlot slot;
324 FT_UInt gid;
325 FT_Glyph glyph;
326
327 ff = (SplashFTFontFile *)fontFile;
328 ff->face->size = sizeObj;
329 FT_Set_Transform(ff->face, &textMatrix, NULL);
330 slot = ff->face->glyph;
331 if (ff->codeToGID && c < ff->codeToGIDLen) {
332 gid = ff->codeToGID[c];
333 } else {
334 gid = (FT_UInt)c;
335 }
336 if (ff->trueType && gid < 0) {
337 // skip the TrueType notdef glyph
338 return NULL;
339 }
340 if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) {
341 return NULL;
342 }
343 if (FT_Get_Glyph(slot, &glyph)) {
344 return NULL;
345 }
346 path.path = new SplashPath();
347 path.textScale = textScale;
348 path.needClose = gFalse;
349 FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
350 &outlineFuncs, &path);
351 if (path.needClose) {
352 path.path->close();
353 }
354 FT_Done_Glyph(glyph);
355 return path.path;
356 }
357
glyphPathMoveTo(const FT_Vector * pt,void * path)358 static int glyphPathMoveTo(const FT_Vector *pt, void *path) {
359 SplashFTFontPath *p = (SplashFTFontPath *)path;
360
361 if (p->needClose) {
362 p->path->close();
363 p->needClose = gFalse;
364 }
365 p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0,
366 (SplashCoord)pt->y * p->textScale / 64.0);
367 return 0;
368 }
369
glyphPathLineTo(const FT_Vector * pt,void * path)370 static int glyphPathLineTo(const FT_Vector *pt, void *path) {
371 SplashFTFontPath *p = (SplashFTFontPath *)path;
372
373 p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0,
374 (SplashCoord)pt->y * p->textScale / 64.0);
375 p->needClose = gTrue;
376 return 0;
377 }
378
glyphPathConicTo(const FT_Vector * ctrl,const FT_Vector * pt,void * path)379 static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
380 void *path) {
381 SplashFTFontPath *p = (SplashFTFontPath *)path;
382 SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
383
384 if (!p->path->getCurPt(&x0, &y0)) {
385 return 0;
386 }
387 xc = (SplashCoord)ctrl->x * p->textScale / 64.0;
388 yc = (SplashCoord)ctrl->y * p->textScale / 64.0;
389 x3 = (SplashCoord)pt->x * p->textScale / 64.0;
390 y3 = (SplashCoord)pt->y * p->textScale / 64.0;
391
392 // A second-order Bezier curve is defined by two endpoints, p0 and
393 // p3, and one control point, pc:
394 //
395 // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
396 //
397 // A third-order Bezier curve is defined by the same two endpoints,
398 // p0 and p3, and two control points, p1 and p2:
399 //
400 // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
401 //
402 // Applying some algebra, we can convert a second-order curve to a
403 // third-order curve:
404 //
405 // p1 = (1/3) * (p0 + 2pc)
406 // p2 = (1/3) * (2pc + p3)
407
408 x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc);
409 y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc);
410 x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3);
411 y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3);
412
413 p->path->curveTo(x1, y1, x2, y2, x3, y3);
414 p->needClose = gTrue;
415 return 0;
416 }
417
glyphPathCubicTo(const FT_Vector * ctrl1,const FT_Vector * ctrl2,const FT_Vector * pt,void * path)418 static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
419 const FT_Vector *pt, void *path) {
420 SplashFTFontPath *p = (SplashFTFontPath *)path;
421
422 p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0,
423 (SplashCoord)ctrl1->y * p->textScale / 64.0,
424 (SplashCoord)ctrl2->x * p->textScale / 64.0,
425 (SplashCoord)ctrl2->y * p->textScale / 64.0,
426 (SplashCoord)pt->x * p->textScale / 64.0,
427 (SplashCoord)pt->y * p->textScale / 64.0);
428 p->needClose = gTrue;
429 return 0;
430 }
431
432 #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
433