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