]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | //======================================================================== |
2 | // | |
3 | // SplashFTFont.cc | |
4 | // | |
5 | //======================================================================== | |
6 | ||
7 | #include <config.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_INTERNAL_OBJECTS_H // needed for FT_New_Size decl | |
18 | #include "gmem.h" | |
19 | #include "SplashMath.h" | |
20 | #include "SplashGlyphBitmap.h" | |
21 | #include "SplashPath.h" | |
22 | #include "SplashFTFontEngine.h" | |
23 | #include "SplashFTFontFile.h" | |
24 | #include "SplashFTFont.h" | |
25 | ||
26 | //------------------------------------------------------------------------ | |
27 | ||
28 | static int glyphPathMoveTo(FT_Vector *pt, void *path); | |
29 | static int glyphPathLineTo(FT_Vector *pt, void *path); | |
30 | static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path); | |
31 | static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2, | |
32 | FT_Vector *pt, void *path); | |
33 | ||
34 | //------------------------------------------------------------------------ | |
35 | // SplashFTFont | |
36 | //------------------------------------------------------------------------ | |
37 | ||
38 | SplashFTFont::SplashFTFont(SplashFTFontFile *fontFileA, SplashCoord *matA): | |
39 | SplashFont(fontFileA, matA, fontFileA->engine->aa) | |
40 | { | |
41 | FT_Face face; | |
42 | SplashCoord size, div; | |
43 | int x, y; | |
44 | ||
45 | face = fontFileA->face; | |
46 | if (FT_New_Size(face, &sizeObj)) { | |
47 | return; | |
48 | } | |
49 | face->size = sizeObj; | |
50 | size = splashSqrt(mat[2]*mat[2] + mat[3]*mat[3]); | |
51 | if (FT_Set_Pixel_Sizes(face, 0, (int)size)) { | |
52 | return; | |
53 | } | |
54 | ||
55 | div = face->bbox.xMax > 20000 ? 65536 : 1; | |
56 | ||
57 | // transform the four corners of the font bounding box -- the min | |
58 | // and max values form the bounding box of the transformed font | |
59 | x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMin) / | |
60 | (div * face->units_per_EM)); | |
61 | xMin = xMax = x; | |
62 | y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMin) / | |
63 | (div * face->units_per_EM)); | |
64 | yMin = yMax = y; | |
65 | x = (int)((mat[0] * face->bbox.xMin + mat[2] * face->bbox.yMax) / | |
66 | (div * face->units_per_EM)); | |
67 | if (x < xMin) { | |
68 | xMin = x; | |
69 | } else if (x > xMax) { | |
70 | xMax = x; | |
71 | } | |
72 | y = (int)((mat[1] * face->bbox.xMin + mat[3] * face->bbox.yMax) / | |
73 | (div * face->units_per_EM)); | |
74 | if (y < yMin) { | |
75 | yMin = y; | |
76 | } else if (y > yMax) { | |
77 | yMax = y; | |
78 | } | |
79 | x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMin) / | |
80 | (div * face->units_per_EM)); | |
81 | if (x < xMin) { | |
82 | xMin = x; | |
83 | } else if (x > xMax) { | |
84 | xMax = x; | |
85 | } | |
86 | y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMin) / | |
87 | (div * face->units_per_EM)); | |
88 | if (y < yMin) { | |
89 | yMin = y; | |
90 | } else if (y > yMax) { | |
91 | yMax = y; | |
92 | } | |
93 | x = (int)((mat[0] * face->bbox.xMax + mat[2] * face->bbox.yMax) / | |
94 | (div * face->units_per_EM)); | |
95 | if (x < xMin) { | |
96 | xMin = x; | |
97 | } else if (x > xMax) { | |
98 | xMax = x; | |
99 | } | |
100 | y = (int)((mat[1] * face->bbox.xMax + mat[3] * face->bbox.yMax) / | |
101 | (div * face->units_per_EM)); | |
102 | if (y < yMin) { | |
103 | yMin = y; | |
104 | } else if (y > yMax) { | |
105 | yMax = y; | |
106 | } | |
107 | // This is a kludge: some buggy PDF generators embed fonts with | |
108 | // zero bounding boxes. | |
109 | if (xMax == xMin) { | |
110 | xMin = 0; | |
111 | xMax = (int)size; | |
112 | } | |
113 | if (yMax == yMin) { | |
114 | yMin = 0; | |
115 | yMax = (int)((SplashCoord)1.2 * size); | |
116 | } | |
117 | ||
118 | // compute the transform matrix | |
119 | #if USE_FIXEDPOINT | |
120 | matrix.xx = (FT_Fixed)((mat[0] / size).getRaw()); | |
121 | matrix.yx = (FT_Fixed)((mat[1] / size).getRaw()); | |
122 | matrix.xy = (FT_Fixed)((mat[2] / size).getRaw()); | |
123 | matrix.yy = (FT_Fixed)((mat[3] / size).getRaw()); | |
124 | #else | |
125 | matrix.xx = (FT_Fixed)((mat[0] / size) * 65536); | |
126 | matrix.yx = (FT_Fixed)((mat[1] / size) * 65536); | |
127 | matrix.xy = (FT_Fixed)((mat[2] / size) * 65536); | |
128 | matrix.yy = (FT_Fixed)((mat[3] / size) * 65536); | |
129 | #endif | |
130 | } | |
131 | ||
132 | SplashFTFont::~SplashFTFont() { | |
133 | } | |
134 | ||
135 | GBool SplashFTFont::getGlyph(int c, int xFrac, int yFrac, | |
136 | SplashGlyphBitmap *bitmap) { | |
137 | return SplashFont::getGlyph(c, xFrac, 0, bitmap); | |
138 | } | |
139 | ||
140 | GBool SplashFTFont::makeGlyph(int c, int xFrac, int yFrac, | |
141 | SplashGlyphBitmap *bitmap) { | |
142 | SplashFTFontFile *ff; | |
143 | FT_Vector offset; | |
144 | FT_GlyphSlot slot; | |
145 | FT_UInt gid; | |
146 | int rowSize; | |
147 | Guchar *p, *q; | |
148 | int i; | |
149 | ||
150 | ff = (SplashFTFontFile *)fontFile; | |
151 | ||
152 | ff->face->size = sizeObj; | |
153 | offset.x = (FT_Pos)(int)((SplashCoord)xFrac * splashFontFractionMul * 64); | |
154 | offset.y = 0; | |
155 | FT_Set_Transform(ff->face, &matrix, &offset); | |
156 | slot = ff->face->glyph; | |
157 | ||
158 | if (ff->codeToGID && c < ff->codeToGIDLen) { | |
159 | gid = (FT_UInt)ff->codeToGID[c]; | |
160 | } else { | |
161 | gid = (FT_UInt)c; | |
162 | } | |
163 | ||
164 | // if we have the FT2 bytecode interpreter, autohinting won't be used | |
165 | #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER | |
166 | if (FT_Load_Glyph(ff->face, gid, | |
167 | aa ? FT_LOAD_NO_BITMAP : FT_LOAD_DEFAULT)) { | |
168 | return gFalse; | |
169 | } | |
170 | #else | |
171 | // FT2's autohinting doesn't always work very well (especially with | |
172 | // font subsets), so turn it off if anti-aliasing is enabled; if | |
173 | // anti-aliasing is disabled, this seems to be a tossup - some fonts | |
174 | // look better with hinting, some without, so leave hinting on | |
175 | if (FT_Load_Glyph(ff->face, gid, | |
176 | aa ? FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP | |
177 | : FT_LOAD_DEFAULT)) { | |
178 | return gFalse; | |
179 | } | |
180 | #endif | |
181 | if (FT_Render_Glyph(slot, aa ? ft_render_mode_normal | |
182 | : ft_render_mode_mono)) { | |
183 | return gFalse; | |
184 | } | |
185 | ||
186 | bitmap->x = -slot->bitmap_left; | |
187 | bitmap->y = slot->bitmap_top; | |
188 | bitmap->w = slot->bitmap.width; | |
189 | bitmap->h = slot->bitmap.rows; | |
190 | bitmap->aa = aa; | |
191 | if (aa) { | |
192 | rowSize = bitmap->w; | |
193 | } else { | |
194 | rowSize = (bitmap->w + 7) >> 3; | |
195 | } | |
196 | bitmap->data = (Guchar *)gmalloc(rowSize * bitmap->h); | |
197 | bitmap->freeData = gTrue; | |
198 | for (i = 0, p = bitmap->data, q = slot->bitmap.buffer; | |
199 | i < bitmap->h; | |
200 | ++i, p += rowSize, q += slot->bitmap.pitch) { | |
201 | memcpy(p, q, rowSize); | |
202 | } | |
203 | ||
204 | return gTrue; | |
205 | } | |
206 | ||
207 | struct SplashFTFontPath { | |
208 | SplashPath *path; | |
209 | GBool needClose; | |
210 | }; | |
211 | ||
212 | SplashPath *SplashFTFont::getGlyphPath(int c) { | |
213 | static FT_Outline_Funcs outlineFuncs = { | |
214 | &glyphPathMoveTo, | |
215 | &glyphPathLineTo, | |
216 | &glyphPathConicTo, | |
217 | &glyphPathCubicTo, | |
218 | 0, 0 | |
219 | }; | |
220 | SplashFTFontFile *ff; | |
221 | SplashFTFontPath path; | |
222 | FT_GlyphSlot slot; | |
223 | FT_UInt gid; | |
224 | FT_Glyph glyph; | |
225 | ||
226 | ff = (SplashFTFontFile *)fontFile; | |
227 | ff->face->size = sizeObj; | |
228 | FT_Set_Transform(ff->face, &matrix, NULL); | |
229 | slot = ff->face->glyph; | |
230 | if (ff->codeToGID && c < ff->codeToGIDLen) { | |
231 | gid = ff->codeToGID[c]; | |
232 | } else { | |
233 | gid = (FT_UInt)c; | |
234 | } | |
235 | if (FT_Load_Glyph(ff->face, gid, FT_LOAD_NO_BITMAP)) { | |
236 | return NULL; | |
237 | } | |
238 | if (FT_Get_Glyph(slot, &glyph)) { | |
239 | return NULL; | |
240 | } | |
241 | path.path = new SplashPath(); | |
242 | path.needClose = gFalse; | |
243 | FT_Outline_Decompose(&((FT_OutlineGlyph)glyph)->outline, | |
244 | &outlineFuncs, &path); | |
245 | if (path.needClose) { | |
246 | path.path->close(); | |
247 | } | |
248 | FT_Done_Glyph(glyph); | |
249 | return path.path; | |
250 | } | |
251 | ||
252 | static int glyphPathMoveTo(FT_Vector *pt, void *path) { | |
253 | SplashFTFontPath *p = (SplashFTFontPath *)path; | |
254 | ||
255 | if (p->needClose) { | |
256 | p->path->close(); | |
257 | p->needClose = gFalse; | |
258 | } | |
259 | p->path->moveTo(pt->x / 64.0, -pt->y / 64.0); | |
260 | return 0; | |
261 | } | |
262 | ||
263 | static int glyphPathLineTo(FT_Vector *pt, void *path) { | |
264 | SplashFTFontPath *p = (SplashFTFontPath *)path; | |
265 | ||
266 | p->path->lineTo(pt->x / 64.0, -pt->y / 64.0); | |
267 | p->needClose = gTrue; | |
268 | return 0; | |
269 | } | |
270 | ||
271 | static int glyphPathConicTo(FT_Vector *ctrl, FT_Vector *pt, void *path) { | |
272 | SplashFTFontPath *p = (SplashFTFontPath *)path; | |
273 | SplashCoord x0, y0, x1, y1, x2, y2, x3, y3, xc, yc; | |
274 | ||
275 | if (!p->path->getCurPt(&x0, &y0)) { | |
276 | return 0; | |
277 | } | |
278 | xc = ctrl->x / 64.0; | |
279 | yc = -ctrl->y / 64.0; | |
280 | x3 = pt->x / 64.0; | |
281 | y3 = -pt->y / 64.0; | |
282 | ||
283 | // A second-order Bezier curve is defined by two endpoints, p0 and | |
284 | // p3, and one control point, pc: | |
285 | // | |
286 | // p(t) = (1-t)^2*p0 + t*(1-t)*pc + t^2*p3 | |
287 | // | |
288 | // A third-order Bezier curve is defined by the same two endpoints, | |
289 | // p0 and p3, and two control points, p1 and p2: | |
290 | // | |
291 | // p(t) = (1-t)^3*p0 + 3t*(1-t)^2*p1 + 3t^2*(1-t)*p2 + t^3*p3 | |
292 | // | |
293 | // Applying some algebra, we can convert a second-order curve to a | |
294 | // third-order curve: | |
295 | // | |
296 | // p1 = (1/3) * (p0 + 2pc) | |
297 | // p2 = (1/3) * (2pc + p3) | |
298 | ||
299 | x1 = (SplashCoord)(1.0 / 3.0) * (x0 + (SplashCoord)2 * xc); | |
300 | y1 = (SplashCoord)(1.0 / 3.0) * (y0 + (SplashCoord)2 * yc); | |
301 | x2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * xc + x3); | |
302 | y2 = (SplashCoord)(1.0 / 3.0) * ((SplashCoord)2 * yc + y3); | |
303 | ||
304 | p->path->curveTo(x1, y1, x2, y2, x3, y3); | |
305 | p->needClose = gTrue; | |
306 | return 0; | |
307 | } | |
308 | ||
309 | static int glyphPathCubicTo(FT_Vector *ctrl1, FT_Vector *ctrl2, | |
310 | FT_Vector *pt, void *path) { | |
311 | SplashFTFontPath *p = (SplashFTFontPath *)path; | |
312 | ||
313 | p->path->curveTo(ctrl1->x / 64.0, -ctrl1->y / 64.0, | |
314 | ctrl2->x / 64.0, -ctrl2->y / 64.0, | |
315 | pt->x / 64.0, -pt->y / 64.0); | |
316 | p->needClose = gTrue; | |
317 | return 0; | |
318 | } | |
319 | ||
320 | #endif // HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H |