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
46static int glyphPathMoveTo(const FT_Vector *pt, void *path);
47static int glyphPathLineTo(const FT_Vector *pt, void *path);
48static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt, void *path);
49static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2, const FT_Vector *pt, void *path);
50
51//------------------------------------------------------------------------
52// SplashFTFont
53//------------------------------------------------------------------------
54
55SplashFTFont::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
150SplashFTFont::~SplashFTFont() { }
151
152bool 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
157static 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
187bool 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
266double 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
302struct SplashFTFontPath
303{
304 SplashPath *path;
305 SplashCoord textScale;
306 bool needClose;
307};
308
309SplashPath *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
365static 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
377static 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
386static 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
425static 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

source code of poppler/splash/SplashFTFont.cc