1 | /***************************************************************************/ |
2 | /* */ |
3 | /* afloader.c */ |
4 | /* */ |
5 | /* Auto-fitter glyph loading routines (body). */ |
6 | /* */ |
7 | /* Copyright 2003-2018 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 "afglobal.h" |
20 | #include "afloader.h" |
21 | #include "afhints.h" |
22 | #include "aferrors.h" |
23 | #include "afmodule.h" |
24 | #include "afpic.h" |
25 | |
26 | #include FT_INTERNAL_CALC_H |
27 | |
28 | |
29 | /* Initialize glyph loader. */ |
30 | |
31 | FT_LOCAL_DEF( void ) |
32 | af_loader_init( AF_Loader loader, |
33 | AF_GlyphHints hints ) |
34 | { |
35 | FT_ZERO( loader ); |
36 | |
37 | loader->hints = hints; |
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 | AF_Module module, |
46 | FT_Face face ) |
47 | { |
48 | FT_Error error = FT_Err_Ok; |
49 | |
50 | |
51 | loader->face = face; |
52 | loader->globals = (AF_FaceGlobals)face->autohint.data; |
53 | |
54 | if ( !loader->globals ) |
55 | { |
56 | error = af_face_globals_new( face, &loader->globals, module ); |
57 | if ( !error ) |
58 | { |
59 | face->autohint.data = |
60 | (FT_Pointer)loader->globals; |
61 | face->autohint.finalizer = |
62 | (FT_Generic_Finalizer)af_face_globals_free; |
63 | } |
64 | } |
65 | |
66 | return error; |
67 | } |
68 | |
69 | |
70 | /* Finalize glyph loader. */ |
71 | |
72 | FT_LOCAL_DEF( void ) |
73 | af_loader_done( AF_Loader loader ) |
74 | { |
75 | loader->face = NULL; |
76 | loader->globals = NULL; |
77 | loader->hints = NULL; |
78 | } |
79 | |
80 | |
81 | #define af_intToFixed( i ) \ |
82 | ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) ) |
83 | #define af_fixedToInt( x ) \ |
84 | ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) ) |
85 | #define af_floatToFixed( f ) \ |
86 | ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) ) |
87 | |
88 | |
89 | static FT_Error |
90 | af_loader_embolden_glyph_in_slot( AF_Loader loader, |
91 | FT_Face face, |
92 | AF_StyleMetrics style_metrics ) |
93 | { |
94 | FT_Error error = FT_Err_Ok; |
95 | |
96 | FT_GlyphSlot slot = face->glyph; |
97 | AF_FaceGlobals globals = loader->globals; |
98 | AF_WritingSystemClass writing_system_class; |
99 | |
100 | FT_Size_Metrics* size_metrics = &face->size->internal->autohint_metrics; |
101 | |
102 | FT_Pos stdVW = 0; |
103 | FT_Pos stdHW = 0; |
104 | |
105 | FT_Bool size_changed = size_metrics->x_ppem != |
106 | globals->stem_darkening_for_ppem; |
107 | |
108 | FT_Fixed em_size = af_intToFixed( face->units_per_EM ); |
109 | FT_Fixed em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size ); |
110 | |
111 | FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L }; |
112 | |
113 | |
114 | /* Skip stem darkening for broken fonts. */ |
115 | if ( !face->units_per_EM ) |
116 | { |
117 | error = FT_ERR( Corrupted_Font_Header ); |
118 | goto Exit; |
119 | } |
120 | |
121 | /* |
122 | * We depend on the writing system (script analyzers) to supply |
123 | * standard widths for the script of the glyph we are looking at. If |
124 | * it can't deliver, stem darkening is disabled. |
125 | */ |
126 | writing_system_class = |
127 | AF_WRITING_SYSTEM_CLASSES_GET[style_metrics->style_class->writing_system]; |
128 | |
129 | if ( writing_system_class->style_metrics_getstdw ) |
130 | writing_system_class->style_metrics_getstdw( style_metrics, |
131 | &stdHW, |
132 | &stdVW ); |
133 | else |
134 | { |
135 | error = FT_ERR( Unimplemented_Feature ); |
136 | goto Exit; |
137 | } |
138 | |
139 | if ( size_changed || |
140 | ( stdVW > 0 && stdVW != globals->standard_vertical_width ) ) |
141 | { |
142 | FT_Fixed darken_by_font_units_x, darken_x; |
143 | |
144 | |
145 | darken_by_font_units_x = |
146 | af_intToFixed( af_loader_compute_darkening( loader, |
147 | face, |
148 | stdVW ) ); |
149 | darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x, |
150 | size_metrics->x_scale ), |
151 | em_ratio ); |
152 | |
153 | globals->standard_vertical_width = stdVW; |
154 | globals->stem_darkening_for_ppem = size_metrics->x_ppem; |
155 | globals->darken_x = af_fixedToInt( darken_x ); |
156 | } |
157 | |
158 | if ( size_changed || |
159 | ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) ) |
160 | { |
161 | FT_Fixed darken_by_font_units_y, darken_y; |
162 | |
163 | |
164 | darken_by_font_units_y = |
165 | af_intToFixed( af_loader_compute_darkening( loader, |
166 | face, |
167 | stdHW ) ); |
168 | darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y, |
169 | size_metrics->y_scale ), |
170 | em_ratio ); |
171 | |
172 | globals->standard_horizontal_width = stdHW; |
173 | globals->stem_darkening_for_ppem = size_metrics->x_ppem; |
174 | globals->darken_y = af_fixedToInt( darken_y ); |
175 | |
176 | /* |
177 | * Scale outlines down on the Y-axis to keep them inside their blue |
178 | * zones. The stronger the emboldening, the stronger the downscaling |
179 | * (plus heuristical padding to prevent outlines still falling out |
180 | * their zones due to rounding). |
181 | * |
182 | * Reason: `FT_Outline_Embolden' works by shifting the rightmost |
183 | * points of stems farther to the right, and topmost points farther |
184 | * up. This positions points on the Y-axis outside their |
185 | * pre-computed blue zones and leads to distortion when applying the |
186 | * hints in the code further below. Code outside this emboldening |
187 | * block doesn't know we are presenting it with modified outlines the |
188 | * analyzer didn't see! |
189 | * |
190 | * An unfortunate side effect of downscaling is that the emboldening |
191 | * effect is slightly decreased. The loss becomes more pronounced |
192 | * versus the CFF driver at smaller sizes, e.g., at 9ppem and below. |
193 | */ |
194 | globals->scale_down_factor = |
195 | FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ), |
196 | em_size ); |
197 | } |
198 | |
199 | FT_Outline_EmboldenXY( &slot->outline, |
200 | globals->darken_x, |
201 | globals->darken_y ); |
202 | |
203 | scale_down_matrix.yy = globals->scale_down_factor; |
204 | FT_Outline_Transform( &slot->outline, &scale_down_matrix ); |
205 | |
206 | Exit: |
207 | return error; |
208 | } |
209 | |
210 | |
211 | /* Load the glyph at index into the current slot of a face and hint it. */ |
212 | |
213 | FT_LOCAL_DEF( FT_Error ) |
214 | af_loader_load_glyph( AF_Loader loader, |
215 | AF_Module module, |
216 | FT_Face face, |
217 | FT_UInt glyph_index, |
218 | FT_Int32 load_flags ) |
219 | { |
220 | FT_Error error; |
221 | |
222 | FT_Size size = face->size; |
223 | FT_Size_Internal size_internal = size->internal; |
224 | FT_GlyphSlot slot = face->glyph; |
225 | FT_Slot_Internal slot_internal = slot->internal; |
226 | FT_GlyphLoader gloader = slot_internal->loader; |
227 | |
228 | AF_GlyphHints hints = loader->hints; |
229 | AF_ScalerRec scaler; |
230 | AF_StyleMetrics style_metrics; |
231 | FT_UInt style_options = AF_STYLE_NONE_DFLT; |
232 | AF_StyleClass style_class; |
233 | AF_WritingSystemClass writing_system_class; |
234 | |
235 | #ifdef FT_CONFIG_OPTION_PIC |
236 | AF_FaceGlobals globals = loader->globals; |
237 | #endif |
238 | |
239 | |
240 | if ( !size ) |
241 | return FT_THROW( Invalid_Size_Handle ); |
242 | |
243 | FT_ZERO( &scaler ); |
244 | |
245 | if ( !size_internal->autohint_metrics.x_scale || |
246 | size_internal->autohint_mode != FT_LOAD_TARGET_MODE( load_flags ) ) |
247 | { |
248 | /* switching between hinting modes usually means different scaling */ |
249 | /* values; this later on enforces recomputation of everything */ |
250 | /* related to the current size */ |
251 | |
252 | size_internal->autohint_mode = FT_LOAD_TARGET_MODE( load_flags ); |
253 | size_internal->autohint_metrics = size->metrics; |
254 | |
255 | #ifdef AF_CONFIG_OPTION_TT_SIZE_METRICS |
256 | { |
257 | FT_Size_Metrics* size_metrics = &size_internal->autohint_metrics; |
258 | |
259 | |
260 | /* set metrics to integer values and adjust scaling accordingly; */ |
261 | /* this is the same setup as with TrueType fonts, cf. function */ |
262 | /* `tt_size_reset' in file `ttobjs.c' */ |
263 | size_metrics->ascender = FT_PIX_ROUND( |
264 | FT_MulFix( face->ascender, |
265 | size_metrics->y_scale ) ); |
266 | size_metrics->descender = FT_PIX_ROUND( |
267 | FT_MulFix( face->descender, |
268 | size_metrics->y_scale ) ); |
269 | size_metrics->height = FT_PIX_ROUND( |
270 | FT_MulFix( face->height, |
271 | size_metrics->y_scale ) ); |
272 | |
273 | size_metrics->x_scale = FT_DivFix( size_metrics->x_ppem << 6, |
274 | face->units_per_EM ); |
275 | size_metrics->y_scale = FT_DivFix( size_metrics->y_ppem << 6, |
276 | face->units_per_EM ); |
277 | size_metrics->max_advance = FT_PIX_ROUND( |
278 | FT_MulFix( face->max_advance_width, |
279 | size_metrics->x_scale ) ); |
280 | } |
281 | #endif /* AF_CONFIG_OPTION_TT_SIZE_METRICS */ |
282 | } |
283 | |
284 | /* |
285 | * TODO: This code currently doesn't support fractional advance widths, |
286 | * i.e., placing hinted glyphs at anything other than integer |
287 | * x-positions. This is only relevant for the warper code, which |
288 | * scales and shifts glyphs to optimize blackness of stems (hinting on |
289 | * the x-axis by nature places things on pixel integers, hinting on the |
290 | * y-axis only, i.e., LIGHT mode, doesn't touch the x-axis). The delta |
291 | * values of the scaler would need to be adjusted. |
292 | */ |
293 | scaler.face = face; |
294 | scaler.x_scale = size_internal->autohint_metrics.x_scale; |
295 | scaler.x_delta = 0; |
296 | scaler.y_scale = size_internal->autohint_metrics.y_scale; |
297 | scaler.y_delta = 0; |
298 | |
299 | scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); |
300 | scaler.flags = 0; |
301 | |
302 | /* note that the fallback style can't be changed anymore */ |
303 | /* after the first call of `af_loader_load_glyph' */ |
304 | error = af_loader_reset( loader, module, face ); |
305 | if ( error ) |
306 | goto Exit; |
307 | |
308 | #ifdef FT_OPTION_AUTOFIT2 |
309 | /* XXX: undocumented hook to activate the latin2 writing system. */ |
310 | if ( load_flags & ( 1UL << 20 ) ) |
311 | style_options = AF_STYLE_LTN2_DFLT; |
312 | #endif |
313 | |
314 | /* |
315 | * Glyphs (really code points) are assigned to scripts. Script |
316 | * analysis is done lazily: For each glyph that passes through here, |
317 | * the corresponding script analyzer is called, but returns immediately |
318 | * if it has been run already. |
319 | */ |
320 | error = af_face_globals_get_metrics( loader->globals, glyph_index, |
321 | style_options, &style_metrics ); |
322 | if ( error ) |
323 | goto Exit; |
324 | |
325 | style_class = style_metrics->style_class; |
326 | writing_system_class = |
327 | AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; |
328 | |
329 | loader->metrics = style_metrics; |
330 | |
331 | if ( writing_system_class->style_metrics_scale ) |
332 | writing_system_class->style_metrics_scale( style_metrics, &scaler ); |
333 | else |
334 | style_metrics->scaler = scaler; |
335 | |
336 | if ( writing_system_class->style_hints_init ) |
337 | { |
338 | error = writing_system_class->style_hints_init( hints, |
339 | style_metrics ); |
340 | if ( error ) |
341 | goto Exit; |
342 | } |
343 | |
344 | /* |
345 | * Do the main work of `af_loader_load_glyph'. Note that we never have |
346 | * to deal with composite glyphs as those get loaded into |
347 | * FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. |
348 | * In the rare cases where FT_LOAD_NO_RECURSE is set, it implies |
349 | * FT_LOAD_NO_SCALE and as such the auto-hinter is never called. |
350 | */ |
351 | load_flags |= FT_LOAD_NO_SCALE | |
352 | FT_LOAD_IGNORE_TRANSFORM | |
353 | FT_LOAD_LINEAR_DESIGN; |
354 | load_flags &= ~FT_LOAD_RENDER; |
355 | |
356 | error = FT_Load_Glyph( face, glyph_index, load_flags ); |
357 | if ( error ) |
358 | goto Exit; |
359 | |
360 | /* |
361 | * Apply stem darkening (emboldening) here before hints are applied to |
362 | * the outline. Glyphs are scaled down proportionally to the |
363 | * emboldening so that curve points don't fall outside their |
364 | * precomputed blue zones. |
365 | * |
366 | * Any emboldening done by the font driver (e.g., the CFF driver) |
367 | * doesn't reach here because the autohinter loads the unprocessed |
368 | * glyphs in font units for analysis (functions `af_*_metrics_init_*') |
369 | * and then above to prepare it for the rasterizers by itself, |
370 | * independently of the font driver. So emboldening must be done here, |
371 | * within the autohinter. |
372 | * |
373 | * All glyphs to be autohinted pass through here one by one. The |
374 | * standard widths can therefore change from one glyph to the next, |
375 | * depending on what script a glyph is assigned to (each script has its |
376 | * own set of standard widths and other metrics). The darkening amount |
377 | * must therefore be recomputed for each size and |
378 | * `standard_{vertical,horizontal}_width' change. |
379 | * |
380 | * Ignore errors and carry on without emboldening. |
381 | * |
382 | */ |
383 | |
384 | /* stem darkening only works well in `light' mode */ |
385 | if ( scaler.render_mode == FT_RENDER_MODE_LIGHT && |
386 | ( !face->internal->no_stem_darkening || |
387 | ( face->internal->no_stem_darkening < 0 && |
388 | !module->no_stem_darkening ) ) ) |
389 | af_loader_embolden_glyph_in_slot( loader, face, style_metrics ); |
390 | |
391 | loader->transformed = slot_internal->glyph_transformed; |
392 | if ( loader->transformed ) |
393 | { |
394 | FT_Matrix inverse; |
395 | |
396 | |
397 | loader->trans_matrix = slot_internal->glyph_matrix; |
398 | loader->trans_delta = slot_internal->glyph_delta; |
399 | |
400 | inverse = loader->trans_matrix; |
401 | if ( !FT_Matrix_Invert( &inverse ) ) |
402 | FT_Vector_Transform( &loader->trans_delta, &inverse ); |
403 | } |
404 | |
405 | switch ( slot->format ) |
406 | { |
407 | case FT_GLYPH_FORMAT_OUTLINE: |
408 | /* translate the loaded glyph when an internal transform is needed */ |
409 | if ( loader->transformed ) |
410 | FT_Outline_Translate( &slot->outline, |
411 | loader->trans_delta.x, |
412 | loader->trans_delta.y ); |
413 | |
414 | /* compute original horizontal phantom points */ |
415 | /* (and ignore vertical ones) */ |
416 | loader->pp1.x = hints->x_delta; |
417 | loader->pp1.y = hints->y_delta; |
418 | loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, |
419 | hints->x_scale ) + hints->x_delta; |
420 | loader->pp2.y = hints->y_delta; |
421 | |
422 | /* be sure to check for spacing glyphs */ |
423 | if ( slot->outline.n_points == 0 ) |
424 | goto Hint_Metrics; |
425 | |
426 | /* now load the slot image into the auto-outline */ |
427 | /* and run the automatic hinting process */ |
428 | if ( writing_system_class->style_hints_apply ) |
429 | writing_system_class->style_hints_apply( glyph_index, |
430 | hints, |
431 | &gloader->base.outline, |
432 | style_metrics ); |
433 | |
434 | /* we now need to adjust the metrics according to the change in */ |
435 | /* width/positioning that occurred during the hinting process */ |
436 | if ( scaler.render_mode != FT_RENDER_MODE_LIGHT ) |
437 | { |
438 | FT_Pos old_rsb, old_lsb, new_lsb; |
439 | FT_Pos pp1x_uh, pp2x_uh; |
440 | |
441 | AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; |
442 | AF_Edge edge1 = axis->edges; /* leftmost edge */ |
443 | AF_Edge edge2 = edge1 + |
444 | axis->num_edges - 1; /* rightmost edge */ |
445 | |
446 | |
447 | if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) |
448 | { |
449 | old_rsb = loader->pp2.x - edge2->opos; |
450 | /* loader->pp1.x is always zero at this point of time */ |
451 | old_lsb = edge1->opos /* - loader->pp1.x */; |
452 | new_lsb = edge1->pos; |
453 | |
454 | /* remember unhinted values to later account */ |
455 | /* for rounding errors */ |
456 | pp1x_uh = new_lsb - old_lsb; |
457 | pp2x_uh = edge2->pos + old_rsb; |
458 | |
459 | /* prefer too much space over too little space */ |
460 | /* for very small sizes */ |
461 | |
462 | if ( old_lsb < 24 ) |
463 | pp1x_uh -= 8; |
464 | |
465 | if ( old_rsb < 24 ) |
466 | pp2x_uh += 8; |
467 | |
468 | loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); |
469 | loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); |
470 | |
471 | if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) |
472 | loader->pp1.x -= 64; |
473 | |
474 | if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) |
475 | loader->pp2.x += 64; |
476 | |
477 | slot->lsb_delta = loader->pp1.x - pp1x_uh; |
478 | slot->rsb_delta = loader->pp2.x - pp2x_uh; |
479 | } |
480 | else |
481 | { |
482 | FT_Pos pp1x = loader->pp1.x; |
483 | FT_Pos pp2x = loader->pp2.x; |
484 | |
485 | |
486 | loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); |
487 | loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); |
488 | |
489 | slot->lsb_delta = loader->pp1.x - pp1x; |
490 | slot->rsb_delta = loader->pp2.x - pp2x; |
491 | } |
492 | } |
493 | /* `light' mode uses integer advance widths */ |
494 | /* but sets `lsb_delta' and `rsb_delta' */ |
495 | else |
496 | { |
497 | FT_Pos pp1x = loader->pp1.x; |
498 | FT_Pos pp2x = loader->pp2.x; |
499 | |
500 | |
501 | loader->pp1.x = FT_PIX_ROUND( pp1x ); |
502 | loader->pp2.x = FT_PIX_ROUND( pp2x ); |
503 | |
504 | slot->lsb_delta = loader->pp1.x - pp1x; |
505 | slot->rsb_delta = loader->pp2.x - pp2x; |
506 | } |
507 | |
508 | break; |
509 | |
510 | default: |
511 | /* we don't support other formats (yet?) */ |
512 | error = FT_THROW( Unimplemented_Feature ); |
513 | } |
514 | |
515 | Hint_Metrics: |
516 | { |
517 | FT_BBox bbox; |
518 | FT_Vector vvector; |
519 | |
520 | |
521 | vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; |
522 | vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; |
523 | vvector.x = FT_MulFix( vvector.x, style_metrics->scaler.x_scale ); |
524 | vvector.y = FT_MulFix( vvector.y, style_metrics->scaler.y_scale ); |
525 | |
526 | /* transform the hinted outline if needed */ |
527 | if ( loader->transformed ) |
528 | { |
529 | FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); |
530 | FT_Vector_Transform( &vvector, &loader->trans_matrix ); |
531 | } |
532 | |
533 | /* we must translate our final outline by -pp1.x and compute */ |
534 | /* the new metrics */ |
535 | if ( loader->pp1.x ) |
536 | FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); |
537 | |
538 | FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); |
539 | |
540 | bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); |
541 | bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); |
542 | bbox.xMax = FT_PIX_CEIL( bbox.xMax ); |
543 | bbox.yMax = FT_PIX_CEIL( bbox.yMax ); |
544 | |
545 | slot->metrics.width = bbox.xMax - bbox.xMin; |
546 | slot->metrics.height = bbox.yMax - bbox.yMin; |
547 | slot->metrics.horiBearingX = bbox.xMin; |
548 | slot->metrics.horiBearingY = bbox.yMax; |
549 | |
550 | slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); |
551 | slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); |
552 | |
553 | /* for mono-width fonts (like Andale, Courier, etc.) we need */ |
554 | /* to keep the original rounded advance width; ditto for */ |
555 | /* digits if all have the same advance width */ |
556 | if ( scaler.render_mode != FT_RENDER_MODE_LIGHT && |
557 | ( FT_IS_FIXED_WIDTH( slot->face ) || |
558 | ( af_face_globals_is_digit( loader->globals, glyph_index ) && |
559 | style_metrics->digits_have_same_width ) ) ) |
560 | { |
561 | slot->metrics.horiAdvance = |
562 | FT_MulFix( slot->metrics.horiAdvance, |
563 | style_metrics->scaler.x_scale ); |
564 | |
565 | /* Set delta values to 0. Otherwise code that uses them is */ |
566 | /* going to ruin the fixed advance width. */ |
567 | slot->lsb_delta = 0; |
568 | slot->rsb_delta = 0; |
569 | } |
570 | else |
571 | { |
572 | /* non-spacing glyphs must stay as-is */ |
573 | if ( slot->metrics.horiAdvance ) |
574 | slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; |
575 | } |
576 | |
577 | slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, |
578 | style_metrics->scaler.y_scale ); |
579 | |
580 | slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); |
581 | slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); |
582 | |
583 | slot->format = FT_GLYPH_FORMAT_OUTLINE; |
584 | } |
585 | |
586 | Exit: |
587 | return error; |
588 | } |
589 | |
590 | |
591 | /* |
592 | * Compute amount of font units the face should be emboldened by, in |
593 | * analogy to the CFF driver's `cf2_computeDarkening' function. See there |
594 | * for details of the algorithm. |
595 | * |
596 | * XXX: Currently a crude adaption of the original algorithm. Do better? |
597 | */ |
598 | FT_LOCAL_DEF( FT_Int32 ) |
599 | af_loader_compute_darkening( AF_Loader loader, |
600 | FT_Face face, |
601 | FT_Pos standard_width ) |
602 | { |
603 | AF_Module module = loader->globals->module; |
604 | |
605 | FT_UShort units_per_EM; |
606 | FT_Fixed ppem, em_ratio; |
607 | FT_Fixed stem_width, stem_width_per_1000, scaled_stem, darken_amount; |
608 | FT_Int log_base_2; |
609 | FT_Int x1, y1, x2, y2, x3, y3, x4, y4; |
610 | |
611 | |
612 | ppem = FT_MAX( af_intToFixed( 4 ), |
613 | af_intToFixed( face->size->metrics.x_ppem ) ); |
614 | units_per_EM = face->units_per_EM; |
615 | |
616 | em_ratio = FT_DivFix( af_intToFixed( 1000 ), |
617 | af_intToFixed ( units_per_EM ) ); |
618 | if ( em_ratio < af_floatToFixed( .01 ) ) |
619 | { |
620 | /* If something goes wrong, don't embolden. */ |
621 | return 0; |
622 | } |
623 | |
624 | x1 = module->darken_params[0]; |
625 | y1 = module->darken_params[1]; |
626 | x2 = module->darken_params[2]; |
627 | y2 = module->darken_params[3]; |
628 | x3 = module->darken_params[4]; |
629 | y3 = module->darken_params[5]; |
630 | x4 = module->darken_params[6]; |
631 | y4 = module->darken_params[7]; |
632 | |
633 | if ( standard_width <= 0 ) |
634 | { |
635 | stem_width = af_intToFixed( 75 ); /* taken from cf2font.c */ |
636 | stem_width_per_1000 = stem_width; |
637 | } |
638 | else |
639 | { |
640 | stem_width = af_intToFixed( standard_width ); |
641 | stem_width_per_1000 = FT_MulFix( stem_width, em_ratio ); |
642 | } |
643 | |
644 | log_base_2 = FT_MSB( (FT_UInt32)stem_width_per_1000 ) + |
645 | FT_MSB( (FT_UInt32)ppem ); |
646 | |
647 | if ( log_base_2 >= 46 ) |
648 | { |
649 | /* possible overflow */ |
650 | scaled_stem = af_intToFixed( x4 ); |
651 | } |
652 | else |
653 | scaled_stem = FT_MulFix( stem_width_per_1000, ppem ); |
654 | |
655 | /* now apply the darkening parameters */ |
656 | if ( scaled_stem < af_intToFixed( x1 ) ) |
657 | darken_amount = FT_DivFix( af_intToFixed( y1 ), ppem ); |
658 | |
659 | else if ( scaled_stem < af_intToFixed( x2 ) ) |
660 | { |
661 | FT_Int xdelta = x2 - x1; |
662 | FT_Int ydelta = y2 - y1; |
663 | FT_Int x = stem_width_per_1000 - |
664 | FT_DivFix( af_intToFixed( x1 ), ppem ); |
665 | |
666 | |
667 | if ( !xdelta ) |
668 | goto Try_x3; |
669 | |
670 | darken_amount = FT_MulDiv( x, ydelta, xdelta ) + |
671 | FT_DivFix( af_intToFixed( y1 ), ppem ); |
672 | } |
673 | |
674 | else if ( scaled_stem < af_intToFixed( x3 ) ) |
675 | { |
676 | Try_x3: |
677 | { |
678 | FT_Int xdelta = x3 - x2; |
679 | FT_Int ydelta = y3 - y2; |
680 | FT_Int x = stem_width_per_1000 - |
681 | FT_DivFix( af_intToFixed( x2 ), ppem ); |
682 | |
683 | |
684 | if ( !xdelta ) |
685 | goto Try_x4; |
686 | |
687 | darken_amount = FT_MulDiv( x, ydelta, xdelta ) + |
688 | FT_DivFix( af_intToFixed( y2 ), ppem ); |
689 | } |
690 | } |
691 | |
692 | else if ( scaled_stem < af_intToFixed( x4 ) ) |
693 | { |
694 | Try_x4: |
695 | { |
696 | FT_Int xdelta = x4 - x3; |
697 | FT_Int ydelta = y4 - y3; |
698 | FT_Int x = stem_width_per_1000 - |
699 | FT_DivFix( af_intToFixed( x3 ), ppem ); |
700 | |
701 | |
702 | if ( !xdelta ) |
703 | goto Use_y4; |
704 | |
705 | darken_amount = FT_MulDiv( x, ydelta, xdelta ) + |
706 | FT_DivFix( af_intToFixed( y3 ), ppem ); |
707 | } |
708 | } |
709 | |
710 | else |
711 | { |
712 | Use_y4: |
713 | darken_amount = FT_DivFix( af_intToFixed( y4 ), ppem ); |
714 | } |
715 | |
716 | /* Convert darken_amount from per 1000 em to true character space. */ |
717 | return af_fixedToInt( FT_DivFix( darken_amount, em_ratio ) ); |
718 | } |
719 | |
720 | |
721 | /* END */ |
722 | |