1 | //======================================================================== |
2 | // |
3 | // SplashFTFont.cc |
4 | // |
5 | //======================================================================== |
6 | |
7 | //======================================================================== |
8 | // |
9 | // Modified under the Poppler project - http://poppler.freedesktop.org |
10 | // |
11 | // All changes made under the Poppler project to this file are licensed |
12 | // under GPL version 2 or later |
13 | // |
14 | // Copyright (C) 2005, 2007-2011, 2014, 2018, 2020 Albert Astals Cid <aacid@kde.org> |
15 | // Copyright (C) 2006 Kristian Høgsberg <krh@bitplanet.net> |
16 | // Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com> |
17 | // Copyright (C) 2010 Suzuki Toshiya <mpsuzuki@hiroshima-u.ac.jp> |
18 | // Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com> |
19 | // Copyright (C) 2012 Thomas Freitag <Thomas.Freitag@alfa.de> |
20 | // Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com> |
21 | // Copyright (C) 2018 Oliver Sander <oliver.sander@tu-dresden.de> |
22 | // |
23 | // To see a description of the changes please see the Changelog file that |
24 | // came with your tarball or type make ChangeLog if you are building from git |
25 | // |
26 | //======================================================================== |
27 | |
28 | #include <config.h> |
29 | |
30 | #include <ft2build.h> |
31 | #include FT_OUTLINE_H |
32 | #include FT_SIZES_H |
33 | #include FT_GLYPH_H |
34 | #include "goo/gmem.h" |
35 | #include "SplashMath.h" |
36 | #include "SplashGlyphBitmap.h" |
37 | #include "SplashPath.h" |
38 | #include "SplashFTFontEngine.h" |
39 | #include "SplashFTFontFile.h" |
40 | #include "SplashFTFont.h" |
41 | |
42 | #include "goo/GooLikely.h" |
43 | |
44 | //------------------------------------------------------------------------ |
45 | |
46 | static int glyphPathMoveTo(const FT_Vector *pt, void *path); |
47 | static int glyphPathLineTo(const FT_Vector *pt, void *path); |
48 | static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path); |
49 | static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path); |
50 | |
51 | //------------------------------------------------------------------------ |
52 | // SplashFTFont |
53 | //------------------------------------------------------------------------ |
54 | |
55 | SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA, const SplashCoord *textMatA) |
56 | : SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa), textScale(0), enableFreeTypeHinting(fontFileA->engine->enableFreeTypeHinting), enableSlightHinting(fontFileA->engine->enableSlightHinting), isOk(false) |
57 | { |
58 | FT_Face face; |
59 | int div; |
60 | int x, y; |
61 | |
62 | face = fontFileA->face; |
63 | if (FT_New_Size(face, size: &sizeObj)) { |
64 | return; |
65 | } |
66 | face->size = sizeObj; |
67 | size = splashRound(x: splashDist(x0: 0, y0: 0, x1: mat[2], y1: mat[3])); |
68 | if (size < 1) { |
69 | size = 1; |
70 | } |
71 | if (FT_Set_Pixel_Sizes(face, pixel_width: 0, pixel_height: size)) { |
72 | return; |
73 | } |
74 | // if the textMat values are too small, FreeType's fixed point |
75 | // arithmetic doesn't work so well |
76 | textScale = splashDist(x0: 0, y0: 0, x1: textMat[2], y1: textMat[3]) / size; |
77 | |
78 | if (unlikely(textScale == 0 || face->units_per_EM == 0)) { |
79 | return; |
80 | } |
81 | |
82 | div = face->bbox.xMax > 20000 ? 65536 : 1; |
83 | |
84 | // transform the four corners of the font bounding box -- the min |
85 | // and max values form the bounding box of the transformed font |
86 | x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) / (div * face->units_per_EM)); |
87 | xMin = xMax = x; |
88 | y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) / (div * face->units_per_EM)); |
89 | yMin = yMax = y; |
90 | x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) / (div * face->units_per_EM)); |
91 | if (x < xMin) { |
92 | xMin = x; |
93 | } else if (x > xMax) { |
94 | xMax = x; |
95 | } |
96 | y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) / (div * face->units_per_EM)); |
97 | if (y < yMin) { |
98 | yMin = y; |
99 | } else if (y > yMax) { |
100 | yMax = y; |
101 | } |
102 | x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) / (div * face->units_per_EM)); |
103 | if (x < xMin) { |
104 | xMin = x; |
105 | } else if (x > xMax) { |
106 | xMax = x; |
107 | } |
108 | y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) / (div * face->units_per_EM)); |
109 | if (y < yMin) { |
110 | yMin = y; |
111 | } else if (y > yMax) { |
112 | yMax = y; |
113 | } |
114 | x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) / (div * face->units_per_EM)); |
115 | if (x < xMin) { |
116 | xMin = x; |
117 | } else if (x > xMax) { |
118 | xMax = x; |
119 | } |
120 | y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) / (div * face->units_per_EM)); |
121 | if (y < yMin) { |
122 | yMin = y; |
123 | } else if (y > yMax) { |
124 | yMax = y; |
125 | } |
126 | // This is a kludge: some buggy PDF generators embed fonts with |
127 | // zero bounding boxes. |
128 | if (xMax == xMin) { |
129 | xMin = 0; |
130 | xMax = size; |
131 | } |
132 | if (yMax == yMin) { |
133 | yMin = 0; |
134 | yMax = (int)((SplashCoord)1.2 * size); |
135 | } |
136 | |
137 | // compute the transform matrix |
138 | matrix.xx = (FT_Fixed)((mat[0] / size) * 65536); |
139 | matrix.yx = (FT_Fixed)((mat[1] / size) * 65536); |
140 | matrix.xy = (FT_Fixed)((mat[2] / size) * 65536); |
141 | matrix.yy = (FT_Fixed)((mat[3] / size) * 65536); |
142 | textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536); |
143 | textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536); |
144 | textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536); |
145 | textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536); |
146 | |
147 | isOk = true; |
148 | } |
149 | |
150 | SplashFTFont::~SplashFTFont() { } |
151 | |
152 | bool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) |
153 | { |
154 | return SplashFont::getGlyph(c, xFrac, yFrac: 0, bitmap, x0, y0, clip, clipRes); |
155 | } |
156 | |
157 | static FT_Int32 getFTLoadFlags(bool type1, bool trueType, bool aa, bool enableFreeTypeHinting, bool enableSlightHinting) |
158 | { |
159 | int ret = FT_LOAD_DEFAULT; |
160 | if (aa) { |
161 | ret |= FT_LOAD_NO_BITMAP; |
162 | } |
163 | |
164 | if (enableFreeTypeHinting) { |
165 | if (enableSlightHinting) { |
166 | ret |= FT_LOAD_TARGET_LIGHT; |
167 | } else { |
168 | if (trueType) { |
169 | // FT2's autohinting doesn't always work very well (especially with |
170 | // font subsets), so turn it off if anti-aliasing is enabled; if |
171 | // anti-aliasing is disabled, this seems to be a tossup - some fonts |
172 | // look better with hinting, some without, so leave hinting on |
173 | if (aa) { |
174 | ret |= FT_LOAD_NO_AUTOHINT; |
175 | } |
176 | } else if (type1) { |
177 | // Type 1 fonts seem to look better with 'light' hinting mode |
178 | ret |= FT_LOAD_TARGET_LIGHT; |
179 | } |
180 | } |
181 | } else { |
182 | ret |= FT_LOAD_NO_HINTING; |
183 | } |
184 | return ret; |
185 | } |
186 | |
187 | bool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, SplashGlyphBitmap *bitmap, int x0, int y0, SplashClip *clip, SplashClipResult *clipRes) |
188 | { |
189 | SplashFTFontFile *ff; |
190 | FT_Vector offset; |
191 | FT_GlyphSlot slot; |
192 | FT_UInt gid; |
193 | int rowSize; |
194 | unsigned char *p, *q; |
195 | int i; |
196 | |
197 | if (unlikely(!isOk)) { |
198 | return false; |
199 | } |
200 | |
201 | ff = (SplashFTFontFile *)fontFile; |
202 | |
203 | ff->face->size = sizeObj; |
204 | offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64); |
205 | offset.y = 0; |
206 | FT_Set_Transform(face: ff->face, matrix: &matrix, delta: &offset); |
207 | slot = ff->face->glyph; |
208 | |
209 | if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { |
210 | gid = (FT_UInt)ff->codeToGID[c]; |
211 | } else { |
212 | gid = (FT_UInt)c; |
213 | } |
214 | |
215 | if (FT_Load_Glyph(face: ff->face, glyph_index: gid, load_flags: getFTLoadFlags(type1: ff->type1, trueType: ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { |
216 | return false; |
217 | } |
218 | |
219 | // prelimirary values based on FT_Outline_Get_CBox |
220 | // we add two pixels to each side to be in the safe side |
221 | FT_BBox cbox; |
222 | FT_Outline_Get_CBox(outline: &ff->face->glyph->outline, acbox: &cbox); |
223 | bitmap->x = -(cbox.xMin / 64) + 2; |
224 | bitmap->y = (cbox.yMax / 64) + 2; |
225 | bitmap->w = ((cbox.xMax - cbox.xMin) / 64) + 4; |
226 | bitmap->h = ((cbox.yMax - cbox.yMin) / 64) + 4; |
227 | |
228 | *clipRes = clip->testRect(rectXMin: x0 - bitmap->x, rectYMin: y0 - bitmap->y, rectXMax: x0 - bitmap->x + bitmap->w, rectYMax: y0 - bitmap->y + bitmap->h); |
229 | if (*clipRes == splashClipAllOutside) { |
230 | bitmap->freeData = false; |
231 | return true; |
232 | } |
233 | |
234 | if (FT_Render_Glyph(slot, render_mode: aa ? ft_render_mode_normal : ft_render_mode_mono)) { |
235 | return false; |
236 | } |
237 | |
238 | if (slot->bitmap.width == 0 || slot->bitmap.rows == 0) { |
239 | // this can happen if (a) the glyph is really tiny or (b) the |
240 | // metrics in the TrueType file are broken |
241 | return false; |
242 | } |
243 | |
244 | bitmap->x = -slot->bitmap_left; |
245 | bitmap->y = slot->bitmap_top; |
246 | bitmap->w = slot->bitmap.width; |
247 | bitmap->h = slot->bitmap.rows; |
248 | bitmap->aa = aa; |
249 | if (aa) { |
250 | rowSize = bitmap->w; |
251 | } else { |
252 | rowSize = (bitmap->w + 7) >> 3; |
253 | } |
254 | bitmap->data = (unsigned char *)gmallocn_checkoverflow(count: rowSize, size: bitmap->h); |
255 | if (!bitmap->data) { |
256 | return false; |
257 | } |
258 | bitmap->freeData = true; |
259 | for (i = 0, p = bitmap->data, q = slot->bitmap.buffer; i < bitmap->h; ++i, p += rowSize, q += slot->bitmap.pitch) { |
260 | memcpy(dest: p, src: q, n: rowSize); |
261 | } |
262 | |
263 | return true; |
264 | } |
265 | |
266 | double SplashFTFont::getGlyphAdvance(int c) |
267 | { |
268 | SplashFTFontFile *ff; |
269 | FT_Vector offset; |
270 | FT_UInt gid; |
271 | FT_Matrix identityMatrix; |
272 | |
273 | ff = (SplashFTFontFile *)fontFile; |
274 | |
275 | // init the matrix |
276 | identityMatrix.xx = 65536; // 1 in 16.16 format |
277 | identityMatrix.xy = 0; |
278 | identityMatrix.yx = 0; |
279 | identityMatrix.yy = 65536; // 1 in 16.16 format |
280 | |
281 | // init the offset |
282 | offset.x = 0; |
283 | offset.y = 0; |
284 | |
285 | ff->face->size = sizeObj; |
286 | FT_Set_Transform(face: ff->face, matrix: &identityMatrix, delta: &offset); |
287 | |
288 | if (ff->codeToGID && c < ff->codeToGIDLen) { |
289 | gid = (FT_UInt)ff->codeToGID[c]; |
290 | } else { |
291 | gid = (FT_UInt)c; |
292 | } |
293 | |
294 | if (FT_Load_Glyph(face: ff->face, glyph_index: gid, load_flags: getFTLoadFlags(type1: ff->type1, trueType: ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { |
295 | return -1; |
296 | } |
297 | |
298 | // 64.0 is 1 in 26.6 format |
299 | return ff->face->glyph->metrics.horiAdvance / 64.0 / size; |
300 | } |
301 | |
302 | struct SplashFTFontPath |
303 | { |
304 | SplashPath *path; |
305 | SplashCoord textScale; |
306 | bool needClose; |
307 | }; |
308 | |
309 | SplashPath *SplashFTFont::getGlyphPath(int c) |
310 | { |
311 | static const FT_Outline_Funcs outlineFuncs = { |
312 | #if FREETYPE_MINOR <= 1 |
313 | (int (*)(FT_Vector *, void *)) & glyphPathMoveTo, |
314 | (int (*)(FT_Vector *, void *)) & glyphPathLineTo, |
315 | (int (*)(FT_Vector *, FT_Vector *, void *)) & glyphPathConicTo, |
316 | (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *)) & glyphPathCubicTo, |
317 | #else |
318 | .move_to: &glyphPathMoveTo, |
319 | .line_to: &glyphPathLineTo, |
320 | .conic_to: &glyphPathConicTo, |
321 | .cubic_to: &glyphPathCubicTo, |
322 | #endif |
323 | .shift: 0, |
324 | .delta: 0 |
325 | }; |
326 | SplashFTFontFile *ff; |
327 | SplashFTFontPath path; |
328 | FT_GlyphSlot slot; |
329 | FT_UInt gid; |
330 | FT_Glyph glyph; |
331 | |
332 | if (unlikely(textScale == 0)) { |
333 | return nullptr; |
334 | } |
335 | |
336 | ff = (SplashFTFontFile *)fontFile; |
337 | ff->face->size = sizeObj; |
338 | FT_Set_Transform(face: ff->face, matrix: &textMatrix, delta: nullptr); |
339 | slot = ff->face->glyph; |
340 | if (ff->codeToGID && c < ff->codeToGIDLen && c >= 0) { |
341 | gid = ff->codeToGID[c]; |
342 | } else { |
343 | gid = (FT_UInt)c; |
344 | } |
345 | if (FT_Load_Glyph(face: ff->face, glyph_index: gid, load_flags: getFTLoadFlags(type1: ff->type1, trueType: ff->trueType, aa, enableFreeTypeHinting, enableSlightHinting))) { |
346 | return nullptr; |
347 | } |
348 | if (FT_Get_Glyph(slot, aglyph: &glyph)) { |
349 | return nullptr; |
350 | } |
351 | if (FT_Outline_Check(outline: &((FT_OutlineGlyph)glyph)->outline)) { |
352 | return nullptr; |
353 | } |
354 | path.path = new SplashPath(); |
355 | path.textScale = textScale; |
356 | path.needClose = false; |
357 | FT_Outline_Decompose(outline: &((FT_OutlineGlyph)glyph)->outline, func_interface: &outlineFuncs, user: &path); |
358 | if (path.needClose) { |
359 | path.path->close(); |
360 | } |
361 | FT_Done_Glyph(glyph); |
362 | return path.path; |
363 | } |
364 | |
365 | static int glyphPathMoveTo(const FT_Vector *pt, void *path) |
366 | { |
367 | SplashFTFontPath *p = (SplashFTFontPath *)path; |
368 | |
369 | if (p->needClose) { |
370 | p->path->close(); |
371 | p->needClose = false; |
372 | } |
373 | p->path->moveTo(x: (SplashCoord)pt->x * p->textScale / 64.0, y: (SplashCoord)pt->y * p->textScale / 64.0); |
374 | return 0; |
375 | } |
376 | |
377 | static int glyphPathLineTo(const FT_Vector *pt, void *path) |
378 | { |
379 | SplashFTFontPath *p = (SplashFTFontPath *)path; |
380 | |
381 | p->path->lineTo(x: (SplashCoord)pt->x * p->textScale / 64.0, y: (SplashCoord)pt->y * p->textScale / 64.0); |
382 | p->needClose = true; |
383 | return 0; |
384 | } |
385 | |
386 | static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path) |
387 | { |
388 | SplashFTFontPath *p = (SplashFTFontPath *)path; |
389 | SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; |
390 | |
391 | if (!p->path->getCurPt(x: &x0, y: &y0)) { |
392 | return 0; |
393 | } |
394 | xc = (SplashCoord)ctrl->x * p->textScale / 64.0; |
395 | yc = (SplashCoord)ctrl->y * p->textScale / 64.0; |
396 | x3 = (SplashCoord)pt->x * p->textScale / 64.0; |
397 | y3 = (SplashCoord)pt->y * p->textScale / 64.0; |
398 | |
399 | // A second-order Bezier curve is defined by two endpoints, p0 and |
400 | // p3, and one control point, pc: |
401 | // |
402 | // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3 |
403 | // |
404 | // A third-order Bezier curve is defined by the same two endpoints, |
405 | // p0 and p3, and two control points, p1 and p2: |
406 | // |
407 | // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3 |
408 | // |
409 | // Applying some algebra, we can convert a second-order curve to a |
410 | // third-order curve: |
411 | // |
412 | // p1 = (1/3) * (p0 + 2pc) |
413 | // p2 = (1/3) * (2pc + p3) |
414 | |
415 | x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc); |
416 | y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc); |
417 | x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3); |
418 | y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3); |
419 | |
420 | p->path->curveTo(x1, y1, x2, y2, x3, y3); |
421 | p->needClose = true; |
422 | return 0; |
423 | } |
424 | |
425 | static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path) |
426 | { |
427 | SplashFTFontPath *p = (SplashFTFontPath *)path; |
428 | |
429 | p->path->curveTo(x1: (SplashCoord)ctrl1->x * p->textScale / 64.0, y1: (SplashCoord)ctrl1->y * p->textScale / 64.0, x2: (SplashCoord)ctrl2->x * p->textScale / 64.0, y2: (SplashCoord)ctrl2->y * p->textScale / 64.0, |
430 | x3: (SplashCoord)pt->x * p->textScale / 64.0, y3: (SplashCoord)pt->y * p->textScale / 64.0); |
431 | p->needClose = true; |
432 | return 0; |
433 | } |
434 | |