|
1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* afloader.c */ |
|
4 /* */ |
|
5 /* Auto-fitter glyph loading routines (body). */ |
|
6 /* */ |
|
7 /* Copyright 2003-2009, 2011 by */ |
|
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ |
|
9 /* */ |
|
10 /* This file is part of the FreeType project, and may only be used, */ |
|
11 /* modified, and distributed under the terms of the FreeType project */ |
|
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ |
|
13 /* this file you indicate that you have read the license and */ |
|
14 /* understand and accept it fully. */ |
|
15 /* */ |
|
16 /***************************************************************************/ |
|
17 |
|
18 |
|
19 #include "afloader.h" |
|
20 #include "afhints.h" |
|
21 #include "afglobal.h" |
|
22 #include "aferrors.h" |
|
23 |
|
24 |
|
25 /* Initialize glyph loader. */ |
|
26 |
|
27 FT_LOCAL_DEF( FT_Error ) |
|
28 af_loader_init( AF_Loader loader, |
|
29 FT_Memory memory ) |
|
30 { |
|
31 FT_ZERO( loader ); |
|
32 |
|
33 af_glyph_hints_init( &loader->hints, memory ); |
|
34 #ifdef FT_DEBUG_AUTOFIT |
|
35 _af_debug_hints = &loader->hints; |
|
36 #endif |
|
37 return FT_GlyphLoader_New( memory, &loader->gloader ); |
|
38 } |
|
39 |
|
40 |
|
41 /* Reset glyph loader and compute globals if necessary. */ |
|
42 |
|
43 FT_LOCAL_DEF( FT_Error ) |
|
44 af_loader_reset( AF_Loader loader, |
|
45 FT_Face face ) |
|
46 { |
|
47 FT_Error error = AF_Err_Ok; |
|
48 |
|
49 |
|
50 loader->face = face; |
|
51 loader->globals = (AF_FaceGlobals)face->autohint.data; |
|
52 |
|
53 FT_GlyphLoader_Rewind( loader->gloader ); |
|
54 |
|
55 if ( loader->globals == NULL ) |
|
56 { |
|
57 error = af_face_globals_new( face, &loader->globals ); |
|
58 if ( !error ) |
|
59 { |
|
60 face->autohint.data = |
|
61 (FT_Pointer)loader->globals; |
|
62 face->autohint.finalizer = |
|
63 (FT_Generic_Finalizer)af_face_globals_free; |
|
64 } |
|
65 } |
|
66 |
|
67 return error; |
|
68 } |
|
69 |
|
70 |
|
71 /* Finalize glyph loader. */ |
|
72 |
|
73 FT_LOCAL_DEF( void ) |
|
74 af_loader_done( AF_Loader loader ) |
|
75 { |
|
76 af_glyph_hints_done( &loader->hints ); |
|
77 |
|
78 loader->face = NULL; |
|
79 loader->globals = NULL; |
|
80 |
|
81 #ifdef FT_DEBUG_AUTOFIT |
|
82 _af_debug_hints = NULL; |
|
83 #endif |
|
84 FT_GlyphLoader_Done( loader->gloader ); |
|
85 loader->gloader = NULL; |
|
86 } |
|
87 |
|
88 |
|
89 /* Load a single glyph component. This routine calls itself */ |
|
90 /* recursively, if necessary, and does the main work of */ |
|
91 /* `af_loader_load_glyph.' */ |
|
92 |
|
93 static FT_Error |
|
94 af_loader_load_g( AF_Loader loader, |
|
95 AF_Scaler scaler, |
|
96 FT_UInt glyph_index, |
|
97 FT_Int32 load_flags, |
|
98 FT_UInt depth ) |
|
99 { |
|
100 FT_Error error; |
|
101 FT_Face face = loader->face; |
|
102 FT_GlyphLoader gloader = loader->gloader; |
|
103 AF_ScriptMetrics metrics = loader->metrics; |
|
104 AF_GlyphHints hints = &loader->hints; |
|
105 FT_GlyphSlot slot = face->glyph; |
|
106 FT_Slot_Internal internal = slot->internal; |
|
107 |
|
108 |
|
109 error = FT_Load_Glyph( face, glyph_index, load_flags ); |
|
110 if ( error ) |
|
111 goto Exit; |
|
112 |
|
113 loader->transformed = internal->glyph_transformed; |
|
114 if ( loader->transformed ) |
|
115 { |
|
116 FT_Matrix inverse; |
|
117 |
|
118 |
|
119 loader->trans_matrix = internal->glyph_matrix; |
|
120 loader->trans_delta = internal->glyph_delta; |
|
121 |
|
122 inverse = loader->trans_matrix; |
|
123 FT_Matrix_Invert( &inverse ); |
|
124 FT_Vector_Transform( &loader->trans_delta, &inverse ); |
|
125 } |
|
126 |
|
127 /* set linear metrics */ |
|
128 slot->linearHoriAdvance = slot->metrics.horiAdvance; |
|
129 slot->linearVertAdvance = slot->metrics.vertAdvance; |
|
130 |
|
131 switch ( slot->format ) |
|
132 { |
|
133 case FT_GLYPH_FORMAT_OUTLINE: |
|
134 /* translate the loaded glyph when an internal transform is needed */ |
|
135 if ( loader->transformed ) |
|
136 FT_Outline_Translate( &slot->outline, |
|
137 loader->trans_delta.x, |
|
138 loader->trans_delta.y ); |
|
139 |
|
140 /* copy the outline points in the loader's current */ |
|
141 /* extra points which is used to keep original glyph coordinates */ |
|
142 error = FT_GLYPHLOADER_CHECK_POINTS( gloader, |
|
143 slot->outline.n_points + 4, |
|
144 slot->outline.n_contours ); |
|
145 if ( error ) |
|
146 goto Exit; |
|
147 |
|
148 FT_ARRAY_COPY( gloader->current.outline.points, |
|
149 slot->outline.points, |
|
150 slot->outline.n_points ); |
|
151 |
|
152 FT_ARRAY_COPY( gloader->current.outline.contours, |
|
153 slot->outline.contours, |
|
154 slot->outline.n_contours ); |
|
155 |
|
156 FT_ARRAY_COPY( gloader->current.outline.tags, |
|
157 slot->outline.tags, |
|
158 slot->outline.n_points ); |
|
159 |
|
160 gloader->current.outline.n_points = slot->outline.n_points; |
|
161 gloader->current.outline.n_contours = slot->outline.n_contours; |
|
162 |
|
163 /* compute original horizontal phantom points (and ignore */ |
|
164 /* vertical ones) */ |
|
165 loader->pp1.x = hints->x_delta; |
|
166 loader->pp1.y = hints->y_delta; |
|
167 loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, |
|
168 hints->x_scale ) + hints->x_delta; |
|
169 loader->pp2.y = hints->y_delta; |
|
170 |
|
171 /* be sure to check for spacing glyphs */ |
|
172 if ( slot->outline.n_points == 0 ) |
|
173 goto Hint_Metrics; |
|
174 |
|
175 /* now load the slot image into the auto-outline and run the */ |
|
176 /* automatic hinting process */ |
|
177 if ( metrics->clazz->script_hints_apply ) |
|
178 metrics->clazz->script_hints_apply( hints, |
|
179 &gloader->current.outline, |
|
180 metrics ); |
|
181 |
|
182 /* we now need to adjust the metrics according to the change in */ |
|
183 /* width/positioning that occurred during the hinting process */ |
|
184 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) |
|
185 { |
|
186 FT_Pos old_rsb, old_lsb, new_lsb; |
|
187 FT_Pos pp1x_uh, pp2x_uh; |
|
188 AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; |
|
189 AF_Edge edge1 = axis->edges; /* leftmost edge */ |
|
190 AF_Edge edge2 = edge1 + |
|
191 axis->num_edges - 1; /* rightmost edge */ |
|
192 |
|
193 |
|
194 if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) |
|
195 { |
|
196 old_rsb = loader->pp2.x - edge2->opos; |
|
197 old_lsb = edge1->opos; |
|
198 new_lsb = edge1->pos; |
|
199 |
|
200 /* remember unhinted values to later account */ |
|
201 /* for rounding errors */ |
|
202 |
|
203 pp1x_uh = new_lsb - old_lsb; |
|
204 pp2x_uh = edge2->pos + old_rsb; |
|
205 |
|
206 /* prefer too much space over too little space */ |
|
207 /* for very small sizes */ |
|
208 |
|
209 if ( old_lsb < 24 ) |
|
210 pp1x_uh -= 8; |
|
211 |
|
212 if ( old_rsb < 24 ) |
|
213 pp2x_uh += 8; |
|
214 |
|
215 loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); |
|
216 loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); |
|
217 |
|
218 if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) |
|
219 loader->pp1.x -= 64; |
|
220 |
|
221 if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) |
|
222 loader->pp2.x += 64; |
|
223 |
|
224 slot->lsb_delta = loader->pp1.x - pp1x_uh; |
|
225 slot->rsb_delta = loader->pp2.x - pp2x_uh; |
|
226 } |
|
227 else |
|
228 { |
|
229 FT_Pos pp1x = loader->pp1.x; |
|
230 FT_Pos pp2x = loader->pp2.x; |
|
231 |
|
232 |
|
233 loader->pp1.x = FT_PIX_ROUND( pp1x ); |
|
234 loader->pp2.x = FT_PIX_ROUND( pp2x ); |
|
235 |
|
236 slot->lsb_delta = loader->pp1.x - pp1x; |
|
237 slot->rsb_delta = loader->pp2.x - pp2x; |
|
238 } |
|
239 } |
|
240 else |
|
241 { |
|
242 FT_Pos pp1x = loader->pp1.x; |
|
243 FT_Pos pp2x = loader->pp2.x; |
|
244 |
|
245 |
|
246 loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); |
|
247 loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); |
|
248 |
|
249 slot->lsb_delta = loader->pp1.x - pp1x; |
|
250 slot->rsb_delta = loader->pp2.x - pp2x; |
|
251 } |
|
252 |
|
253 /* good, we simply add the glyph to our loader's base */ |
|
254 FT_GlyphLoader_Add( gloader ); |
|
255 break; |
|
256 |
|
257 case FT_GLYPH_FORMAT_COMPOSITE: |
|
258 { |
|
259 FT_UInt nn, num_subglyphs = slot->num_subglyphs; |
|
260 FT_UInt num_base_subgs, start_point; |
|
261 FT_SubGlyph subglyph; |
|
262 |
|
263 |
|
264 start_point = gloader->base.outline.n_points; |
|
265 |
|
266 /* first of all, copy the subglyph descriptors in the glyph loader */ |
|
267 error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs ); |
|
268 if ( error ) |
|
269 goto Exit; |
|
270 |
|
271 FT_ARRAY_COPY( gloader->current.subglyphs, |
|
272 slot->subglyphs, |
|
273 num_subglyphs ); |
|
274 |
|
275 gloader->current.num_subglyphs = num_subglyphs; |
|
276 num_base_subgs = gloader->base.num_subglyphs; |
|
277 |
|
278 /* now read each subglyph independently */ |
|
279 for ( nn = 0; nn < num_subglyphs; nn++ ) |
|
280 { |
|
281 FT_Vector pp1, pp2; |
|
282 FT_Pos x, y; |
|
283 FT_UInt num_points, num_new_points, num_base_points; |
|
284 |
|
285 |
|
286 /* gloader.current.subglyphs can change during glyph loading due */ |
|
287 /* to re-allocation -- we must recompute the current subglyph on */ |
|
288 /* each iteration */ |
|
289 subglyph = gloader->base.subglyphs + num_base_subgs + nn; |
|
290 |
|
291 pp1 = loader->pp1; |
|
292 pp2 = loader->pp2; |
|
293 |
|
294 num_base_points = gloader->base.outline.n_points; |
|
295 |
|
296 error = af_loader_load_g( loader, scaler, subglyph->index, |
|
297 load_flags, depth + 1 ); |
|
298 if ( error ) |
|
299 goto Exit; |
|
300 |
|
301 /* recompute subglyph pointer */ |
|
302 subglyph = gloader->base.subglyphs + num_base_subgs + nn; |
|
303 |
|
304 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS ) |
|
305 { |
|
306 pp1 = loader->pp1; |
|
307 pp2 = loader->pp2; |
|
308 } |
|
309 else |
|
310 { |
|
311 loader->pp1 = pp1; |
|
312 loader->pp2 = pp2; |
|
313 } |
|
314 |
|
315 num_points = gloader->base.outline.n_points; |
|
316 num_new_points = num_points - num_base_points; |
|
317 |
|
318 /* now perform the transformation required for this subglyph */ |
|
319 |
|
320 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE | |
|
321 FT_SUBGLYPH_FLAG_XY_SCALE | |
|
322 FT_SUBGLYPH_FLAG_2X2 ) ) |
|
323 { |
|
324 FT_Vector* cur = gloader->base.outline.points + |
|
325 num_base_points; |
|
326 FT_Vector* limit = cur + num_new_points; |
|
327 |
|
328 |
|
329 for ( ; cur < limit; cur++ ) |
|
330 FT_Vector_Transform( cur, &subglyph->transform ); |
|
331 } |
|
332 |
|
333 /* apply offset */ |
|
334 |
|
335 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) ) |
|
336 { |
|
337 FT_Int k = subglyph->arg1; |
|
338 FT_UInt l = subglyph->arg2; |
|
339 FT_Vector* p1; |
|
340 FT_Vector* p2; |
|
341 |
|
342 |
|
343 if ( start_point + k >= num_base_points || |
|
344 l >= (FT_UInt)num_new_points ) |
|
345 { |
|
346 error = AF_Err_Invalid_Composite; |
|
347 goto Exit; |
|
348 } |
|
349 |
|
350 l += num_base_points; |
|
351 |
|
352 /* for now, only use the current point coordinates; */ |
|
353 /* we may consider another approach in the near future */ |
|
354 p1 = gloader->base.outline.points + start_point + k; |
|
355 p2 = gloader->base.outline.points + start_point + l; |
|
356 |
|
357 x = p1->x - p2->x; |
|
358 y = p1->y - p2->y; |
|
359 } |
|
360 else |
|
361 { |
|
362 x = FT_MulFix( subglyph->arg1, hints->x_scale ) + hints->x_delta; |
|
363 y = FT_MulFix( subglyph->arg2, hints->y_scale ) + hints->y_delta; |
|
364 |
|
365 x = FT_PIX_ROUND( x ); |
|
366 y = FT_PIX_ROUND( y ); |
|
367 } |
|
368 |
|
369 { |
|
370 FT_Outline dummy = gloader->base.outline; |
|
371 |
|
372 |
|
373 dummy.points += num_base_points; |
|
374 dummy.n_points = (short)num_new_points; |
|
375 |
|
376 FT_Outline_Translate( &dummy, x, y ); |
|
377 } |
|
378 } |
|
379 } |
|
380 break; |
|
381 |
|
382 default: |
|
383 /* we don't support other formats (yet?) */ |
|
384 error = AF_Err_Unimplemented_Feature; |
|
385 } |
|
386 |
|
387 Hint_Metrics: |
|
388 if ( depth == 0 ) |
|
389 { |
|
390 FT_BBox bbox; |
|
391 FT_Vector vvector; |
|
392 |
|
393 |
|
394 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; |
|
395 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; |
|
396 vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); |
|
397 vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); |
|
398 |
|
399 /* transform the hinted outline if needed */ |
|
400 if ( loader->transformed ) |
|
401 { |
|
402 FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); |
|
403 FT_Vector_Transform( &vvector, &loader->trans_matrix ); |
|
404 } |
|
405 #if 1 |
|
406 /* we must translate our final outline by -pp1.x and compute */ |
|
407 /* the new metrics */ |
|
408 if ( loader->pp1.x ) |
|
409 FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); |
|
410 #endif |
|
411 FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); |
|
412 |
|
413 bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); |
|
414 bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); |
|
415 bbox.xMax = FT_PIX_CEIL( bbox.xMax ); |
|
416 bbox.yMax = FT_PIX_CEIL( bbox.yMax ); |
|
417 |
|
418 slot->metrics.width = bbox.xMax - bbox.xMin; |
|
419 slot->metrics.height = bbox.yMax - bbox.yMin; |
|
420 slot->metrics.horiBearingX = bbox.xMin; |
|
421 slot->metrics.horiBearingY = bbox.yMax; |
|
422 |
|
423 slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); |
|
424 slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); |
|
425 |
|
426 /* for mono-width fonts (like Andale, Courier, etc.) we need */ |
|
427 /* to keep the original rounded advance width; ditto for */ |
|
428 /* digits if all have the same advance width */ |
|
429 #if 0 |
|
430 if ( !FT_IS_FIXED_WIDTH( slot->face ) ) |
|
431 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; |
|
432 else |
|
433 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, |
|
434 x_scale ); |
|
435 #else |
|
436 if ( FT_IS_FIXED_WIDTH( slot->face ) || |
|
437 ( af_face_globals_is_digit( loader->globals, glyph_index ) && |
|
438 metrics->digits_have_same_width ) ) |
|
439 { |
|
440 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, |
|
441 metrics->scaler.x_scale ); |
|
442 |
|
443 /* Set delta values to 0. Otherwise code that uses them is */ |
|
444 /* going to ruin the fixed advance width. */ |
|
445 slot->lsb_delta = 0; |
|
446 slot->rsb_delta = 0; |
|
447 } |
|
448 else |
|
449 { |
|
450 /* non-spacing glyphs must stay as-is */ |
|
451 if ( slot->metrics.horiAdvance ) |
|
452 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; |
|
453 } |
|
454 #endif |
|
455 |
|
456 slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, |
|
457 metrics->scaler.y_scale ); |
|
458 |
|
459 slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); |
|
460 slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); |
|
461 |
|
462 /* now copy outline into glyph slot */ |
|
463 FT_GlyphLoader_Rewind( internal->loader ); |
|
464 error = FT_GlyphLoader_CopyPoints( internal->loader, gloader ); |
|
465 if ( error ) |
|
466 goto Exit; |
|
467 |
|
468 slot->outline = internal->loader->base.outline; |
|
469 slot->format = FT_GLYPH_FORMAT_OUTLINE; |
|
470 } |
|
471 |
|
472 Exit: |
|
473 return error; |
|
474 } |
|
475 |
|
476 |
|
477 /* Load a glyph. */ |
|
478 |
|
479 FT_LOCAL_DEF( FT_Error ) |
|
480 af_loader_load_glyph( AF_Loader loader, |
|
481 FT_Face face, |
|
482 FT_UInt gindex, |
|
483 FT_UInt32 load_flags ) |
|
484 { |
|
485 FT_Error error; |
|
486 FT_Size size = face->size; |
|
487 AF_ScalerRec scaler; |
|
488 |
|
489 |
|
490 if ( !size ) |
|
491 return AF_Err_Invalid_Argument; |
|
492 |
|
493 FT_ZERO( &scaler ); |
|
494 |
|
495 scaler.face = face; |
|
496 scaler.x_scale = size->metrics.x_scale; |
|
497 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ |
|
498 scaler.y_scale = size->metrics.y_scale; |
|
499 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ |
|
500 |
|
501 scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); |
|
502 scaler.flags = 0; /* XXX: fix this */ |
|
503 |
|
504 error = af_loader_reset( loader, face ); |
|
505 if ( !error ) |
|
506 { |
|
507 AF_ScriptMetrics metrics; |
|
508 FT_UInt options = 0; |
|
509 |
|
510 |
|
511 #ifdef FT_OPTION_AUTOFIT2 |
|
512 /* XXX: undocumented hook to activate the latin2 hinter */ |
|
513 if ( load_flags & ( 1UL << 20 ) ) |
|
514 options = 2; |
|
515 #endif |
|
516 |
|
517 error = af_face_globals_get_metrics( loader->globals, gindex, |
|
518 options, &metrics ); |
|
519 if ( !error ) |
|
520 { |
|
521 loader->metrics = metrics; |
|
522 |
|
523 if ( metrics->clazz->script_metrics_scale ) |
|
524 metrics->clazz->script_metrics_scale( metrics, &scaler ); |
|
525 else |
|
526 metrics->scaler = scaler; |
|
527 |
|
528 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; |
|
529 load_flags &= ~FT_LOAD_RENDER; |
|
530 |
|
531 if ( metrics->clazz->script_hints_init ) |
|
532 { |
|
533 error = metrics->clazz->script_hints_init( &loader->hints, |
|
534 metrics ); |
|
535 if ( error ) |
|
536 goto Exit; |
|
537 } |
|
538 |
|
539 error = af_loader_load_g( loader, &scaler, gindex, load_flags, 0 ); |
|
540 } |
|
541 } |
|
542 Exit: |
|
543 return error; |
|
544 } |
|
545 |
|
546 |
|
547 /* END */ |