Coverage Report

Created: 2026-07-16 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xpdf-4.06/splash/SplashFTFont.cc
Line
Count
Source
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
8.77k
  SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)
43
8.77k
{
44
8.77k
  FT_Face face;
45
8.77k
  int size, div;
46
8.77k
  int x, y;
47
#if USE_FIXEDPOINT
48
  SplashCoord scale;
49
#endif
50
51
8.77k
  face = fontFileA->face;
52
8.77k
  if (FT_New_Size(face, &sizeObj)) {
53
0
    return;
54
0
  }
55
8.77k
  face->size = sizeObj;
56
8.77k
  size = splashRound(splashDist(0, 0, mat[2], mat[3]));
57
8.77k
  if (size < 1) {
58
8.77k
    size = 1;
59
8.77k
  }
60
8.77k
  if (FT_Set_Pixel_Sizes(face, 0, size)) {
61
122
    return;
62
122
  }
63
  // if the textMat values are too small, FreeType's fixed point
64
  // arithmetic doesn't work so well
65
8.64k
  textScale = splashDist(0, 0, textMat[2], textMat[3]) / size;
66
  // avoid problems with singular (or close-to-singular) matrices
67
8.64k
  if (textScale < 0.00001) {
68
3.54k
    textScale = 0.00001;
69
3.54k
  }
70
71
8.64k
  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
8.64k
  x = (int)((mat[0] * (SplashCoord)face->bbox.xMin
130
8.64k
       + mat[2] * (SplashCoord)face->bbox.yMin) /
131
8.64k
      (div * face->units_per_EM));
132
8.64k
  xMin = xMax = x;
133
8.64k
  y = (int)((mat[1] * (SplashCoord)face->bbox.xMin
134
8.64k
       + mat[3] * (SplashCoord)face->bbox.yMin) /
135
8.64k
      (div * face->units_per_EM));
136
8.64k
  yMin = yMax = y;
137
8.64k
  x = (int)((mat[0] * (SplashCoord)face->bbox.xMin
138
8.64k
       + mat[2] * (SplashCoord)face->bbox.yMax) /
139
8.64k
      (div * face->units_per_EM));
140
8.64k
  if (x < xMin) {
141
0
    xMin = x;
142
8.64k
  } else if (x > xMax) {
143
0
    xMax = x;
144
0
  }
145
8.64k
  y = (int)((mat[1] * (SplashCoord)face->bbox.xMin
146
8.64k
       + mat[3] * (SplashCoord)face->bbox.yMax) /
147
8.64k
      (div * face->units_per_EM));
148
8.64k
  if (y < yMin) {
149
581
    yMin = y;
150
8.06k
  } else if (y > yMax) {
151
59
    yMax = y;
152
59
  }
153
8.64k
  x = (int)((mat[0] * (SplashCoord)face->bbox.xMax
154
8.64k
       + mat[2] * (SplashCoord)face->bbox.yMin) /
155
8.64k
      (div * face->units_per_EM));
156
8.64k
  if (x < xMin) {
157
605
    xMin = x;
158
8.04k
  } else if (x > xMax) {
159
235
    xMax = x;
160
235
  }
161
8.64k
  y = (int)((mat[1] * (SplashCoord)face->bbox.xMax
162
8.64k
       + mat[3] * (SplashCoord)face->bbox.yMin) /
163
8.64k
      (div * face->units_per_EM));
164
8.64k
  if (y < yMin) {
165
0
    yMin = y;
166
8.64k
  } else if (y > yMax) {
167
0
    yMax = y;
168
0
  }
169
8.64k
  x = (int)((mat[0] * (SplashCoord)face->bbox.xMax
170
8.64k
       + mat[2] * (SplashCoord)face->bbox.yMax) /
171
8.64k
      (div * face->units_per_EM));
172
8.64k
  if (x < xMin) {
173
0
    xMin = x;
174
8.64k
  } else if (x > xMax) {
175
0
    xMax = x;
176
0
  }
177
8.64k
  y = (int)((mat[1] * (SplashCoord)face->bbox.xMax
178
8.64k
       + mat[3] * (SplashCoord)face->bbox.yMax) /
179
8.64k
      (div * face->units_per_EM));
180
8.64k
  if (y < yMin) {
181
0
    yMin = y;
182
8.64k
  } else if (y > yMax) {
183
0
    yMax = y;
184
0
  }
185
8.64k
#endif // USE_FIXEDPOINT
186
  // This is a kludge: some buggy PDF generators embed fonts with
187
  // zero bounding boxes.
188
8.64k
  if (xMax == xMin) {
189
7.80k
    xMin = 0;
190
7.80k
    xMax = size;
191
7.80k
  }
192
8.64k
  if (yMax == yMin) {
193
8.00k
    yMin = 0;
194
8.00k
    yMax = (int)((SplashCoord)1.2 * size);
195
8.00k
  }
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
8.64k
  matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
209
8.64k
  matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
210
8.64k
  matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
211
8.64k
  matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
212
8.64k
  textMatrix.xx = (FT_Fixed)((textMat[0] / (textScale * size)) * 65536);
213
8.64k
  textMatrix.yx = (FT_Fixed)((textMat[1] / (textScale * size)) * 65536);
214
8.64k
  textMatrix.xy = (FT_Fixed)((textMat[2] / (textScale * size)) * 65536);
215
8.64k
  textMatrix.yy = (FT_Fixed)((textMat[3] / (textScale * size)) * 65536);
216
8.64k
#endif
217
8.64k
}
218
219
8.77k
SplashFTFont::~SplashFTFont() {
220
8.77k
}
221
222
GBool SplashFTFont::getGlyph(Guint c, int xFrac, int yFrac,
223
581k
           SplashGlyphBitmap *bitmap) {
224
581k
  return SplashFont::getGlyph(c, xFrac, 0, bitmap);
225
581k
}
226
227
GBool SplashFTFont::makeGlyph(Guint c, int xFrac, int yFrac,
228
569k
            SplashGlyphBitmap *bitmap) {
229
569k
  SplashFTFontFile *ff;
230
569k
  FT_Vector offset;
231
569k
  FT_GlyphSlot slot;
232
569k
  int gid;
233
569k
  FT_Int32 flags;
234
569k
  int rowSize;
235
569k
  Guchar *p, *q;
236
569k
  int i;
237
238
569k
  ff = (SplashFTFontFile *)fontFile;
239
240
569k
  ff->face->size = sizeObj;
241
569k
  offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64);
242
569k
  offset.y = 0;
243
569k
  FT_Set_Transform(ff->face, &matrix, &offset);
244
569k
  slot = ff->face->glyph;
245
246
569k
  if (ff->codeToGID && c < (Guint)ff->codeToGIDLen) {
247
522k
    gid = ff->codeToGID[c];
248
522k
  } else {
249
46.4k
    gid = c;
250
46.4k
  }
251
569k
  if (ff->fontType == splashFontTrueType && gid < 0) {
252
    // skip the TrueType notdef glyph
253
30.4k
    return gFalse;
254
30.4k
  }
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
538k
  flags = FT_LOAD_NO_BITMAP | FT_LOAD_PEDANTIC;
271
538k
  if (ff->engine->flags & splashFTNoHinting) {
272
0
    flags |= FT_LOAD_NO_HINTING;
273
538k
  } else if (ff->fontType == splashFontType1) {
274
51.5k
    flags |= FT_LOAD_TARGET_LIGHT;
275
487k
  } else {
276
487k
    flags |= FT_LOAD_NO_AUTOHINT;
277
487k
  }
278
538k
  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
240k
    flags = FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING;
283
240k
    if (FT_Load_Glyph(ff->face, (FT_UInt)gid, flags)) {
284
199k
      return gFalse;
285
199k
    }
286
240k
  }
287
339k
  if (FT_Render_Glyph(slot, aa ? FT_RENDER_MODE_NORMAL
288
339k
                   : FT_RENDER_MODE_MONO)) {
289
85.4k
    return gFalse;
290
85.4k
  }
291
253k
  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
227k
    return gFalse;
295
227k
  }
296
297
26.6k
  bitmap->x = -slot->bitmap_left;
298
26.6k
  bitmap->y = slot->bitmap_top;
299
26.6k
  bitmap->w = slot->bitmap.width;
300
26.6k
  bitmap->h = slot->bitmap.rows;
301
26.6k
  bitmap->aa = aa;
302
26.6k
  if (aa) {
303
26.6k
    rowSize = bitmap->w;
304
26.6k
  } else {
305
0
    rowSize = (bitmap->w + 7) >> 3;
306
0
  }
307
26.6k
  bitmap->data = (Guchar *)gmallocn(bitmap->h, rowSize);
308
26.6k
  bitmap->freeData = gTrue;
309
26.6k
  for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
310
267k
       i < bitmap->h;
311
241k
       ++i, p += rowSize, q += slot->bitmap.pitch) {
312
241k
    memcpy(p, q, rowSize);
313
241k
  }
314
315
26.6k
  return gTrue;
316
253k
}
317
318
struct SplashFTFontPath {
319
  SplashPath *path;
320
  SplashCoord textScale;
321
  GBool needClose;
322
};
323
324
1.32M
SplashPath *SplashFTFont::getGlyphPath(Guint c) {
325
1.32M
  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
1.32M
    &glyphPathMoveTo,
333
1.32M
    &glyphPathLineTo,
334
1.32M
    &glyphPathConicTo,
335
1.32M
    &glyphPathCubicTo,
336
1.32M
#endif
337
1.32M
    0, 0
338
1.32M
  };
339
1.32M
  SplashFTFontFile *ff;
340
1.32M
  SplashFTFontPath path;
341
1.32M
  FT_GlyphSlot slot;
342
1.32M
  int gid;
343
1.32M
  FT_Glyph glyph;
344
345
1.32M
  ff = (SplashFTFontFile *)fontFile;
346
1.32M
  ff->face->size = sizeObj;
347
1.32M
  FT_Set_Transform(ff->face, &textMatrix, NULL);
348
1.32M
  slot = ff->face->glyph;
349
1.32M
  if (ff->codeToGID && c < (Guint)ff->codeToGIDLen) {
350
1.31M
    gid = ff->codeToGID[c];
351
1.31M
  } else {
352
7.10k
    gid = c;
353
7.10k
  }
354
1.32M
  if (ff->fontType == splashFontTrueType && gid < 0) {
355
    // skip the TrueType notdef glyph
356
34.7k
    return NULL;
357
34.7k
  }
358
1.28M
  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
312k
    if (FT_Load_Glyph(ff->face, (FT_UInt)gid,
363
312k
          FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING)) {
364
310k
      return NULL;
365
310k
    }
366
312k
  }
367
975k
  if (FT_Get_Glyph(slot, &glyph)) {
368
1.05k
    return NULL;
369
1.05k
  }
370
974k
  path.path = new SplashPath();
371
974k
  path.textScale = textScale;
372
974k
  path.needClose = gFalse;
373
974k
  FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
374
974k
           &outlineFuncs, &path);
375
974k
  if (path.needClose) {
376
334k
    path.path->close();
377
334k
  }
378
974k
  FT_Done_Glyph(glyph);
379
974k
  return path.path;
380
975k
}
381
382
1.35M
static int glyphPathMoveTo(const FT_Vector *pt, void *path) {
383
1.35M
  SplashFTFontPath *p = (SplashFTFontPath *)path;
384
385
1.35M
  if (p->needClose) {
386
1.02M
    p->path->close();
387
1.02M
    p->needClose = gFalse;
388
1.02M
  }
389
1.35M
  p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0,
390
1.35M
      (SplashCoord)pt->y * p->textScale / 64.0);
391
1.35M
  return 0;
392
1.35M
}
393
394
4.18M
static int glyphPathLineTo(const FT_Vector *pt, void *path) {
395
4.18M
  SplashFTFontPath *p = (SplashFTFontPath *)path;
396
397
4.18M
  p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0,
398
4.18M
      (SplashCoord)pt->y * p->textScale / 64.0);
399
4.18M
  p->needClose = gTrue;
400
4.18M
  return 0;
401
4.18M
}
402
403
static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
404
484k
          void *path) {
405
484k
  SplashFTFontPath *p = (SplashFTFontPath *)path;
406
484k
  SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
407
408
484k
  if (!p->path->getCurPt(&x0, &y0)) {
409
0
    return 0;
410
0
  }
411
484k
  xc = (SplashCoord)ctrl->x * p->textScale / 64.0;
412
484k
  yc = (SplashCoord)ctrl->y * p->textScale / 64.0;
413
484k
  x3 = (SplashCoord)pt->x * p->textScale / 64.0;
414
484k
  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
484k
  x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc);
433
484k
  y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc);
434
484k
  x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3);
435
484k
  y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3);
436
437
484k
  p->path->curveTo(x1, y1, x2, y2, x3, y3);
438
484k
  p->needClose = gTrue;
439
484k
  return 0;
440
484k
}
441
442
static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
443
2.29k
          const FT_Vector *pt, void *path) {
444
2.29k
  SplashFTFontPath *p = (SplashFTFontPath *)path;
445
446
2.29k
  p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0,
447
2.29k
       (SplashCoord)ctrl1->y * p->textScale / 64.0,
448
2.29k
       (SplashCoord)ctrl2->x * p->textScale / 64.0,
449
2.29k
       (SplashCoord)ctrl2->y * p->textScale / 64.0,
450
2.29k
       (SplashCoord)pt->x * p->textScale / 64.0,
451
2.29k
       (SplashCoord)pt->y * p->textScale / 64.0);
452
2.29k
  p->needClose = gTrue;
453
2.29k
  return 0;
454
2.29k
}
455
456
#endif // HAVE_FREETYPE_H