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