blob: 811ec3b8e03c54daeb0f64f7d244043f3b68c3e0 [file] [log] [blame]
David Turner8ccb4552004-01-16 09:51:00 +00001#include "afmodule.h"
2#include "afhints.h"
3#include "afglobal.h"
4#include "aflatin.h"
5
6 static FT_Error
7 af_hinter_load( AF_Hinter hinter,
8 FT_UInt glyph_index,
9 FT_Int32 load_flags,
10 FT_UInt depth )
11 {
12 FT_Face face = hinter->face;
13 FT_GlyphSlot slot = face->glyph;
14 FT_Slot_Internal internal = slot->internal;
15 FT_Fixed x_scale = hinter->globals->x_scale;
16 FT_Fixed y_scale = hinter->globals->y_scale;
17 FT_Error error;
18 AF_Outline outline = hinter->glyph;
19 AF_Loader gloader = hinter->loader;
20
21
22 /* load the glyph */
23 error = FT_Load_Glyph( face, glyph_index, load_flags );
24 if ( error )
25 goto Exit;
26
27 /* Set `hinter->transformed' after loading with FT_LOAD_NO_RECURSE. */
28 hinter->transformed = internal->glyph_transformed;
29
30 if ( hinter->transformed )
31 {
32 FT_Matrix imatrix;
33
34
35 imatrix = internal->glyph_matrix;
36 hinter->trans_delta = internal->glyph_delta;
37 hinter->trans_matrix = imatrix;
38
39 FT_Matrix_Invert( &imatrix );
40 FT_Vector_Transform( &hinter->trans_delta, &imatrix );
41 }
42
43 /* set linear horizontal metrics */
44 slot->linearHoriAdvance = slot->metrics.horiAdvance;
45 slot->linearVertAdvance = slot->metrics.vertAdvance;
46
47 switch ( slot->format )
48 {
49 case FT_GLYPH_FORMAT_OUTLINE:
50
51 /* translate glyph outline if we need to */
52 if ( hinter->transformed )
53 {
54 FT_UInt n = slot->outline.n_points;
55 FT_Vector* point = slot->outline.points;
56
57
58 for ( ; n > 0; point++, n-- )
59 {
60 point->x += hinter->trans_delta.x;
61 point->y += hinter->trans_delta.y;
62 }
63 }
64
65 /* copy the outline points in the loader's current */
66 /* extra points which is used to keep original glyph coordinates */
67 error = af_loader_check_points( gloader, slot->outline.n_points + 2,
68 slot->outline.n_contours );
69 if ( error )
70 goto Exit;
71
72 FT_ARRAY_COPY( gloader->current.extra_points, slot->outline.points,
73 slot->outline.n_points );
74
75 FT_ARRAY_COPY( gloader->current.outline.contours, slot->outline.contours,
76 slot->outline.n_contours );
77
78 FT_ARRAY_COPY( gloader->current.outline.tags, slot->outline.tags,
79 slot->outline.n_points );
80
81 gloader->current.outline.n_points = slot->outline.n_points;
82 gloader->current.outline.n_contours = slot->outline.n_contours;
83
84 /* compute original phantom points */
85 hinter->pp1.x = 0;
86 hinter->pp1.y = 0;
87 hinter->pp2.x = FT_MulFix( slot->metrics.horiAdvance, x_scale );
88 hinter->pp2.y = 0;
89
90 /* be sure to check for spacing glyphs */
91 if ( slot->outline.n_points == 0 )
92 goto Hint_Metrics;
93
94 /* now load the slot image into the auto-outline and run the */
95 /* automatic hinting process */
96 error = af_outline_load( outline, x_scale, y_scale, face );
97 if ( error )
98 goto Exit;
99
100 /* perform feature detection */
101 af_outline_detect_features( outline );
102
103 if ( hinter->do_vert_hints )
104 {
105 af_outline_compute_blue_edges( outline, hinter->globals );
106 af_outline_scale_blue_edges( outline, hinter->globals );
107 }
108
109 /* perform alignment control */
110 af_hinter_hint_edges( hinter );
111 af_hinter_align( hinter );
112
113 /* now save the current outline into the loader's current table */
114 af_outline_save( outline, gloader );
115
116 /* we now need to hint the metrics according to the change in */
117 /* width/positioning that occured during the hinting process */
118 {
119 FT_Pos old_advance, old_rsb, old_lsb, new_lsb;
120 AF_Edge edge1 = outline->vert_edges; /* leftmost edge */
121 AF_Edge edge2 = edge1 +
122 outline->num_vedges - 1; /* rightmost edge */
123
124
125 old_advance = hinter->pp2.x;
126 old_rsb = old_advance - edge2->opos;
127 old_lsb = edge1->opos;
128 new_lsb = edge1->pos;
129
130 hinter->pp1.x = FT_PIX_ROUND( new_lsb - old_lsb );
131 hinter->pp2.x = FT_PIX_ROUND( edge2->pos + old_rsb );
132
133#if 0
134 /* try to fix certain bad advance computations */
135 if ( hinter->pp2.x + hinter->pp1.x == edge2->pos && old_rsb > 4 )
136 hinter->pp2.x += 64;
137#endif
138 }
139
140 /* good, we simply add the glyph to our loader's base */
141 af_loader_add( gloader );
142 break;
143
144 case FT_GLYPH_FORMAT_COMPOSITE:
145 {
146 FT_UInt nn, num_subglyphs = slot->num_subglyphs;
147 FT_UInt num_base_subgs, start_point;
148 FT_SubGlyph subglyph;
149
150
151 start_point = gloader->base.outline.n_points;
152
153 /* first of all, copy the subglyph descriptors in the glyph loader */
154 error = af_loader_check_subglyphs( gloader, num_subglyphs );
155 if ( error )
156 goto Exit;
157
158 FT_ARRAY_COPY( gloader->current.subglyphs, slot->subglyphs,
159 num_subglyphs );
160
161 gloader->current.num_subglyphs = num_subglyphs;
162 num_base_subgs = gloader->base.num_subglyphs;
163
164 /* now, read each subglyph independently */
165 for ( nn = 0; nn < num_subglyphs; nn++ )
166 {
167 FT_Vector pp1, pp2;
168 FT_Pos x, y;
169 FT_UInt num_points, num_new_points, num_base_points;
170
171
172 /* gloader.current.subglyphs can change during glyph loading due */
173 /* to re-allocation -- we must recompute the current subglyph on */
174 /* each iteration */
175 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
176
177 pp1 = hinter->pp1;
178 pp2 = hinter->pp2;
179
180 num_base_points = gloader->base.outline.n_points;
181
182 error = af_hinter_load( hinter, subglyph->index,
183 load_flags, depth + 1 );
184 if ( error )
185 goto Exit;
186
187 /* recompute subglyph pointer */
188 subglyph = gloader->base.subglyphs + num_base_subgs + nn;
189
190 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
191 {
192 pp1 = hinter->pp1;
193 pp2 = hinter->pp2;
194 }
195 else
196 {
197 hinter->pp1 = pp1;
198 hinter->pp2 = pp2;
199 }
200
201 num_points = gloader->base.outline.n_points;
202 num_new_points = num_points - num_base_points;
203
204 /* now perform the transform required for this subglyph */
205
206 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE |
207 FT_SUBGLYPH_FLAG_XY_SCALE |
208 FT_SUBGLYPH_FLAG_2X2 ) )
209 {
210 FT_Vector* cur = gloader->base.outline.points +
211 num_base_points;
212 FT_Vector* org = gloader->base.extra_points +
213 num_base_points;
214 FT_Vector* limit = cur + num_new_points;
215
216
217 for ( ; cur < limit; cur++, org++ )
218 {
219 FT_Vector_Transform( cur, &subglyph->transform );
220 FT_Vector_Transform( org, &subglyph->transform );
221 }
222 }
223
224 /* apply offset */
225
226 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
227 {
228 FT_Int k = subglyph->arg1;
229 FT_UInt l = subglyph->arg2;
230 FT_Vector* p1;
231 FT_Vector* p2;
232
233
234 if ( start_point + k >= num_base_points ||
235 l >= (FT_UInt)num_new_points )
236 {
237 error = AF_Err_Invalid_Composite;
238 goto Exit;
239 }
240
241 l += num_base_points;
242
243 /* for now, only use the current point coordinates; */
244 /* we may consider another approach in the near future */
245 p1 = gloader->base.outline.points + start_point + k;
246 p2 = gloader->base.outline.points + start_point + l;
247
248 x = p1->x - p2->x;
249 y = p1->y - p2->y;
250 }
251 else
252 {
253 x = FT_MulFix( subglyph->arg1, x_scale );
254 y = FT_MulFix( subglyph->arg2, y_scale );
255
256 x = FT_PIX_ROUND(x);
257 y = FT_PIX_ROUND(y);
258 }
259
260 {
261 FT_Outline dummy = gloader->base.outline;
262
263
264 dummy.points += num_base_points;
265 dummy.n_points = (short)num_new_points;
266
267 FT_Outline_Translate( &dummy, x, y );
268 }
269 }
270 }
271 break;
272
273 default:
274 /* we don't support other formats (yet?) */
275 error = AF_Err_Unimplemented_Feature;
276 }
277
278 Hint_Metrics:
279 if ( depth == 0 )
280 {
281 FT_BBox bbox;
282
283
284 /* transform the hinted outline if needed */
285 if ( hinter->transformed )
286 FT_Outline_Transform( &gloader->base.outline, &hinter->trans_matrix );
287
288 /* we must translate our final outline by -pp1.x and compute */
289 /* the new metrics */
290 if ( hinter->pp1.x )
291 FT_Outline_Translate( &gloader->base.outline, -hinter->pp1.x, 0 );
292
293 FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
294 bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
295 bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
296 bbox.xMax = FT_PIX_CEIL( bbox.xMax );
297 bbox.yMax = FT_PIX_CEIL( bbox.yMax );
298
299 slot->metrics.width = bbox.xMax - bbox.xMin;
300 slot->metrics.height = bbox.yMax - bbox.yMin;
301 slot->metrics.horiBearingX = bbox.xMin;
302 slot->metrics.horiBearingY = bbox.yMax;
303
304 /* for mono-width fonts (like Andale, Courier, etc.) we need */
305 /* to keep the original rounded advance width */
306 if ( !FT_IS_FIXED_WIDTH( slot->face ) )
307 slot->metrics.horiAdvance = hinter->pp2.x - hinter->pp1.x;
308 else
309 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
310 x_scale );
311
312 slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
313
314 /* now copy outline into glyph slot */
315 af_loader_rewind( slot->internal->loader );
316 error = af_loader_copy_points( slot->internal->loader, gloader );
317 if ( error )
318 goto Exit;
319
320 slot->outline = slot->internal->loader->base.outline;
321 slot->format = FT_GLYPH_FORMAT_OUTLINE;
322 }
323
324#ifdef DEBUG_HINTER
325 af_debug_hinter = hinter;
326#endif
327
328 Exit:
329 return error;
330 }
331
332
333 /* load and hint a given glyph */
334 FT_LOCAL_DEF( FT_Error )
335 af_hinter_load_glyph( AF_Hinter hinter,
336 FT_GlyphSlot slot,
337 FT_Size size,
338 FT_UInt glyph_index,
339 FT_Int32 load_flags )
340 {
341 FT_Face face = slot->face;
342 FT_Error error;
343 FT_Fixed x_scale = size->metrics.x_scale;
344 FT_Fixed y_scale = size->metrics.y_scale;
345 AF_Face_Globals face_globals = FACE_GLOBALS( face );
346 FT_Render_Mode hint_mode = FT_LOAD_TARGET_MODE( load_flags );
347
348
349 /* first of all, we need to check that we're using the correct face and */
350 /* global hints to load the glyph */
351 if ( hinter->face != face || hinter->globals != face_globals )
352 {
353 hinter->face = face;
354 if ( !face_globals )
355 {
356 error = af_hinter_new_face_globals( hinter, face, 0 );
357 if ( error )
358 goto Exit;
359
360 }
361 hinter->globals = FACE_GLOBALS( face );
362 face_globals = FACE_GLOBALS( face );
363
364 }
365
366#ifdef FT_CONFIG_CHESTER_BLUE_SCALE
367
368 /* try to optimize the y_scale so that the top of non-capital letters
369 * is aligned on a pixel boundary whenever possible
370 */
371 {
372 AF_Globals design = &face_globals->design;
373 FT_Pos shoot = design->blue_shoots[AF_BLUE_SMALL_TOP];
374
375
376 /* the value of 'shoot' will be -1000 if the font doesn't have */
377 /* small latin letters; we simply check the sign here... */
378 if ( shoot > 0 )
379 {
380 FT_Pos scaled = FT_MulFix( shoot, y_scale );
381 FT_Pos fitted = FT_PIX_ROUND( scaled );
382
383
384 if ( scaled != fitted )
385 {
386 /* adjust y_scale
387 */
388 y_scale = FT_MulDiv( y_scale, fitted, scaled );
389
390 /* adust x_scale
391 */
392 if ( fitted < scaled )
393 x_scale -= x_scale / 50; /* x_scale*0.98 with integers */
394 }
395 }
396 }
397
398#endif /* FT_CONFIG_CHESTER_BLUE_SCALE */
399
400 /* now, we must check the current character pixel size to see if we */
401 /* need to rescale the global metrics */
402 if ( face_globals->x_scale != x_scale ||
403 face_globals->y_scale != y_scale )
404 af_hinter_scale_globals( hinter, x_scale, y_scale );
405
406 af_loader_rewind( hinter->loader );
407
408 /* reset hinting flags according to load flags and current render target */
409 hinter->do_horz_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
410 hinter->do_vert_hints = FT_BOOL( !(load_flags & FT_LOAD_NO_AUTOHINT) );
411
412#ifdef DEBUG_HINTER
413 hinter->do_horz_hints = !af_debug_disable_vert; /* not a bug, the meaning */
414 hinter->do_vert_hints = !af_debug_disable_horz; /* of h/v is inverted! */
415#endif
416
417 /* we snap the width of vertical stems for the monochrome and */
418 /* horizontal LCD rendering targets only. Corresponds to X snapping. */
419 hinter->do_horz_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
420 hint_mode == FT_RENDER_MODE_LCD );
421
422 /* we snap the width of horizontal stems for the monochrome and */
423 /* vertical LCD rendering targets only. Corresponds to Y snapping. */
424 hinter->do_vert_snapping = FT_BOOL( hint_mode == FT_RENDER_MODE_MONO ||
425 hint_mode == FT_RENDER_MODE_LCD_V );
426
427 hinter->do_stem_adjust = FT_BOOL( hint_mode != FT_RENDER_MODE_LIGHT );
428
429 load_flags |= FT_LOAD_NO_SCALE
430 | FT_LOAD_IGNORE_TRANSFORM;
431 load_flags &= ~FT_LOAD_RENDER;
432
433 error = af_hinter_load( hinter, glyph_index, load_flags, 0 );
434
435 Exit:
436 return error;
437 }