1 //========================================================================
2 //
3 // SplashFTFont.cc
4 //
5 //========================================================================
6 
7 #include <aconf.h>
8 
9 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
10 
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14 
15 #include <ft2build.h>
16 #include FT_OUTLINE_H
17 #include FT_SIZES_H
18 #include FT_GLYPH_H
19 #include "gmem.h"
20 #include "SplashMath.h"
21 #include "SplashGlyphBitmap.h"
22 #include "SplashPath.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 
SplashFTFont(SplashFTFontFile * fontFileA,SplashCoord * matA,SplashCoord * textMatA)40 SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA,
41 			   SplashCoord *textMatA):
42   SplashFont(fontFileA, matA, textMatA, fontFileA->engine->aa)
43 {
44   FT_Face face;
45   SplashCoord size, div;
46   int x, y;
47 
48   face = fontFileA->face;
49 
50   if (FT_New_Size(face, &sizeObj)) {
51     return;
52   }
53   face->size = sizeObj;
54   size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]);
55   if (FT_Set_Pixel_Sizes(face, 0, (int)size)) {
56     return;
57   }
58 
59   this->ascender = face->ascender;
60   this->descender = face->descender;
61 
62   // if the textMat values are too small, FreeType's fixed point
63   // arithmetic doesn't work so well
64   textScale = splashSqrt(textMat[2]*textMat[2] + textMat[3]*textMat[3]) / size;
65 
66   div = face->bbox.xMax > 20000 ? 65536 : 1;
67 
68   // transform the four corners of the font bounding box -- the min
69   // and max values form the bounding box of the transformed font
70   x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) /
71 	    (div * face->units_per_EM));
72   xMin = xMax = x;
73   y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) /
74 	    (div * face->units_per_EM));
75   yMin = yMax = y;
76   x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) /
77 	    (div * face->units_per_EM));
78   if (x < xMin) {
79     xMin = x;
80   } else if (x > xMax) {
81     xMax = x;
82   }
83   y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) /
84 	    (div * face->units_per_EM));
85   if (y < yMin) {
86     yMin = y;
87   } else if (y > yMax) {
88     yMax = y;
89   }
90   x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) /
91 	    (div * face->units_per_EM));
92   if (x < xMin) {
93     xMin = x;
94   } else if (x > xMax) {
95     xMax = x;
96   }
97   y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) /
98 	    (div * face->units_per_EM));
99   if (y < yMin) {
100     yMin = y;
101   } else if (y > yMax) {
102     yMax = y;
103   }
104   x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) /
105 	    (div * face->units_per_EM));
106   if (x < xMin) {
107     xMin = x;
108   } else if (x > xMax) {
109     xMax = x;
110   }
111   y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) /
112 	    (div * face->units_per_EM));
113   if (y < yMin) {
114     yMin = y;
115   } else if (y > yMax) {
116     yMax = y;
117   }
118   // This is a kludge: some buggy PDF generators embed fonts with
119   // zero bounding boxes.
120   if (xMax == xMin) {
121     xMin = 0;
122     xMax = (int)size;
123   }
124   if (yMax == yMin) {
125     yMin = 0;
126     yMax = (int)((SplashCoord)1.2 * size);
127   }
128 
129   // compute the transform matrix
130 #if USE_FIXEDPOINT
131   matrix.xx = (FT_Fixed)((mat[0] / size).getRaw());
132   matrix.yx = (FT_Fixed)((mat[1] / size).getRaw());
133   matrix.xy = (FT_Fixed)((mat[2] / size).getRaw());
134   matrix.yy = (FT_Fixed)((mat[3] / size).getRaw());
135   textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)).getRaw());
136   textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)).getRaw());
137   textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)).getRaw());
138   textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)).getRaw());
139 #else
140   matrix.xx = (FT_Fixed)((mat[0] / size) * 65536);
141   matrix.yx = (FT_Fixed)((mat[1] / size) * 65536);
142   matrix.xy = (FT_Fixed)((mat[2] / size) * 65536);
143   matrix.yy = (FT_Fixed)((mat[3] / size) * 65536);
144   textMatrix.xx = (FT_Fixed)((textMat[0] / (size * textScale)) * 65536);
145   textMatrix.yx = (FT_Fixed)((textMat[1] / (size * textScale)) * 65536);
146   textMatrix.xy = (FT_Fixed)((textMat[2] / (size * textScale)) * 65536);
147   textMatrix.yy = (FT_Fixed)((textMat[3] / (size * textScale)) * 65536);
148 #endif
149 }
150 
~SplashFTFont()151 SplashFTFont::~SplashFTFont() {
152 }
153 
getGlyph(int c,int xFrac,int yFrac,SplashGlyphBitmap * bitmap)154 GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac,
155 			     SplashGlyphBitmap *bitmap) {
156   return SplashFont::getGlyph(c, xFrac, 0, bitmap);
157 }
158 
makeGlyph(int c,int xFrac,int yFrac,SplashGlyphBitmap * bitmap)159 GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac,
160 			      SplashGlyphBitmap *bitmap) {
161   SplashFTFontFile *ff;
162   FT_Vector offset;
163   FT_GlyphSlot slot;
164   FT_UInt gid;
165   int rowSize;
166   Guchar *p, *q;
167   int i;
168 
169   ff = (SplashFTFontFile *)fontFile;
170 
171   ff->face->size = sizeObj;
172   offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64);
173   offset.y = 0;
174   FT_Set_Transform(ff->face, &matrix, &offset);
175   slot = ff->face->glyph;
176 
177   if (ff->codeToGID && c < ff->codeToGIDLen) {
178     gid = (FT_UInt)ff->codeToGID[c];
179   } else {
180     gid = (FT_UInt)c;
181   }
182   if (ff->trueType && gid == 0) {
183     // skip the TrueType notdef glyph
184     return gFalse;
185   }
186 
187   // if we have the FT2 bytecode interpreter, autohinting won't be used
188 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
189   if (FT_Load_Glyph(ff->face, gid,
190 		    aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) {
191     return gFalse;
192   }
193 #else
194   // FT2's autohinting doesn't always work very well (especially with
195   // font subsets), so turn it off if anti-aliasing is enabled; if
196   // anti-aliasing is disabled, this seems to be a tossup - some fonts
197   // look better with hinting, some without, so leave hinting on
198   if (FT_Load_Glyph(ff->face, gid,
199 		    aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP
200                        : FT_LOAD_DEFAULT)) {
201     return gFalse;
202   }
203 #endif
204   if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal
205 		               : ft_render_mode_mono)) {
206     return gFalse;
207   }
208 
209   bitmap->x = -slot->bitmap_left;
210   bitmap->y = slot->bitmap_top;
211   bitmap->w = slot->bitmap.width;
212   bitmap->h = slot->bitmap.rows;
213   bitmap->aa = aa;
214   if (aa) {
215     rowSize = bitmap->w;
216   } else {
217     rowSize = (bitmap->w + 7) >> 3;
218   }
219   bitmap->data = (Guchar *)gmalloc(rowSize * bitmap->h);
220   bitmap->freeData = gTrue;
221   for (i = 0, p = bitmap->data, q = slot->bitmap.buffer;
222        i < bitmap->h;
223        ++i, p += rowSize, q += slot->bitmap.pitch) {
224     memcpy(p, q, rowSize);
225   }
226 
227   return gTrue;
228 }
229 
230 struct SplashFTFontPath {
231   SplashPath *path;
232   SplashCoord textScale;
233   GBool needClose;
234 };
235 
getNumChars()236 int SplashFTFont::getNumChars()
237 {
238   SplashFTFontFile* ff = (SplashFTFontFile *)fontFile;
239   return ff->face->num_glyphs;
240 }
241 
getGlyphPath(int c)242 SplashPath *SplashFTFont::getGlyphPath(int c) {
243   static FT_Outline_Funcs outlineFuncs = {
244 #if FREETYPE_MINOR <= 1
245     (int (*)(FT_Vector *, void *))&glyphPathMoveTo,
246     (int (*)(FT_Vector *, void *))&glyphPathLineTo,
247     (int (*)(FT_Vector *, FT_Vector *, void *))&glyphPathConicTo,
248     (int (*)(FT_Vector *, FT_Vector *, FT_Vector *, void *))&glyphPathCubicTo,
249 #else
250     &glyphPathMoveTo,
251     &glyphPathLineTo,
252     &glyphPathConicTo,
253     &glyphPathCubicTo,
254 #endif
255     0, 0
256   };
257   SplashFTFontFile *ff;
258   SplashFTFontPath path;
259   FT_GlyphSlot slot;
260   FT_UInt gid;
261   FT_Glyph glyph;
262 
263   this->last_advance = -1;
264 
265   ff = (SplashFTFontFile *)fontFile;
266   ff->face->size = sizeObj;
267   FT_Set_Transform(ff->face, &textMatrix, NULL);
268   slot = ff->face->glyph;
269   if (ff->codeToGID && c < ff->codeToGIDLen) {
270     gid = ff->codeToGID[c];
271   } else {
272     gid = (FT_UInt)c;
273   }
274   if (ff->trueType && gid == 0) {
275     // skip the TrueType notdef glyph
276     return NULL;
277   }
278   int error = 0;
279   if ((error=FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP))) {
280     if ((error=FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP|FT_LOAD_NO_HINTING))) {
281       fprintf(stderr, "Truetype wasn't able to load glyph %d, error %d\n", gid, error);
282       return NULL;
283     }
284   }
285   if (FT_Get_Glyph(slot, &glyph)) {
286     return NULL;
287   }
288   this->last_advance = glyph->advance.x/65536.0;
289 
290   path.path = new SplashPath();
291   path.textScale = textScale;
292   path.needClose = gFalse;
293   error = FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline,
294 		       &outlineFuncs, &path);
295   if(error) {
296       fprintf(stderr, "Truetype wasn't able to read glyph %d, error %d\n", gid, error);
297   }
298   if (path.needClose) {
299     path.path->close();
300   }
301   FT_Done_Glyph(glyph);
302   return path.path;
303 }
304 
glyphPathMoveTo(const FT_Vector * pt,void * path)305 static int glyphPathMoveTo(const FT_Vector *pt, void *path) {
306   SplashFTFontPath *p = (SplashFTFontPath *)path;
307 
308   if (p->needClose) {
309     p->path->close();
310     p->needClose = gFalse;
311   }
312   p->path->moveTo((SplashCoord)pt->x * p->textScale / 64.0,
313 		  (SplashCoord)pt->y * p->textScale / 64.0);
314   return 0;
315 }
316 
glyphPathLineTo(const FT_Vector * pt,void * path)317 static int glyphPathLineTo(const FT_Vector *pt, void *path) {
318   SplashFTFontPath *p = (SplashFTFontPath *)path;
319 
320   p->path->lineTo((SplashCoord)pt->x * p->textScale / 64.0,
321 		  (SplashCoord)pt->y * p->textScale / 64.0);
322   p->needClose = gTrue;
323   return 0;
324 }
325 
glyphPathConicTo(const FT_Vector * ctrl,const FT_Vector * pt,void * path)326 static int glyphPathConicTo(const FT_Vector *ctrl, const FT_Vector *pt,
327 			    void *path) {
328   SplashFTFontPath *p = (SplashFTFontPath *)path;
329   SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc;
330 
331   if (!p->path->getCurPt(&x0, &y0)) {
332     return 0;
333   }
334   xc = (SplashCoord)ctrl->x * p->textScale / 64.0;
335   yc = (SplashCoord)ctrl->y * p->textScale / 64.0;
336   x3 = (SplashCoord)pt->x * p->textScale / 64.0;
337   y3 = (SplashCoord)pt->y * p->textScale / 64.0;
338 
339   // A second-order Bezier curve is defined by two endpoints, p0 and
340   // p3, and one control point, pc:
341   //
342   //     p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3
343   //
344   // A third-order Bezier curve is defined by the same two endpoints,
345   // p0 and p3, and two control points, p1 and p2:
346   //
347   //     p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3
348   //
349   // Applying some algebra, we can convert a second-order curve to a
350   // third-order curve:
351   //
352   //     p1 = (1/3) * (p0 + 2pc)
353   //     p2 = (1/3) * (2pc + p3)
354 
355   x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc);
356   y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc);
357   x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3);
358   y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3);
359 
360   p->path->curveTo(x1, y1, x2, y2, x3, y3);
361   p->needClose = gTrue;
362   return 0;
363 }
364 
glyphPathCubicTo(const FT_Vector * ctrl1,const FT_Vector * ctrl2,const FT_Vector * pt,void * path)365 static int glyphPathCubicTo(const FT_Vector *ctrl1, const FT_Vector *ctrl2,
366 			    const FT_Vector *pt, void *path) {
367   SplashFTFontPath *p = (SplashFTFontPath *)path;
368 
369   p->path->curveTo((SplashCoord)ctrl1->x * p->textScale / 64.0,
370 		   (SplashCoord)ctrl1->y * p->textScale / 64.0,
371 		   (SplashCoord)ctrl2->x * p->textScale / 64.0,
372 		   (SplashCoord)ctrl2->y * p->textScale / 64.0,
373 		   (SplashCoord)pt->x * p->textScale / 64.0,
374 		   (SplashCoord)pt->y * p->textScale / 64.0);
375   p->needClose = gTrue;
376   return 0;
377 }
378 
379 #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
380