1 /***************************************************************************/
5 /* Auto-fitter glyph loading routines (body). */
7 /* Copyright 2003-2016 by */
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */
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. */
16 /***************************************************************************/
26 #include FT_INTERNAL_CALC_H
29 /* Initialize glyph loader. */
32 af_loader_init( AF_Loader loader
,
37 loader
->hints
= hints
;
41 /* Reset glyph loader and compute globals if necessary. */
43 FT_LOCAL_DEF( FT_Error
)
44 af_loader_reset( AF_Loader loader
,
48 FT_Error error
= FT_Err_Ok
;
52 loader
->globals
= (AF_FaceGlobals
)face
->autohint
.data
;
54 if ( loader
->globals
== NULL
)
56 error
= af_face_globals_new( face
, &loader
->globals
, module
);
60 (FT_Pointer
)loader
->globals
;
61 face
->autohint
.finalizer
=
62 (FT_Generic_Finalizer
)af_face_globals_free
;
70 /* Finalize glyph loader. */
73 af_loader_done( AF_Loader loader
)
76 loader
->globals
= NULL
;
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 ) )
89 /* Do the main work of `af_loader_load_glyph'. Note that we never */
90 /* have to deal with composite glyphs as those get loaded into */
91 /* FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. */
92 /* In the rare cases where FT_LOAD_NO_RECURSE is set, it implies */
93 /* FT_LOAD_NO_SCALE and as such the auto-hinter is never called. */
96 af_loader_load_g( AF_Loader loader
,
101 AF_Module module
= loader
->globals
->module
;
104 FT_Face face
= loader
->face
;
105 AF_StyleMetrics metrics
= loader
->metrics
;
106 AF_GlyphHints hints
= loader
->hints
;
107 FT_GlyphSlot slot
= face
->glyph
;
108 FT_Slot_Internal internal
= slot
->internal
;
109 FT_GlyphLoader gloader
= internal
->loader
;
113 flags
= load_flags
| FT_LOAD_LINEAR_DESIGN
;
114 error
= FT_Load_Glyph( face
, glyph_index
, flags
);
119 * Apply stem darkening (emboldening) here before hints are applied to
120 * the outline. Glyphs are scaled down proportionally to the
121 * emboldening so that curve points don't fall outside their precomputed
124 * Any emboldening done by the font driver (e.g., the CFF driver)
125 * doesn't reach here because the autohinter loads the unprocessed
126 * glyphs in font units for analysis (functions `af_*_metrics_init_*')
127 * and then above to prepare it for the rasterizers by itself,
128 * independently of the font driver. So emboldening must be done here,
129 * within the autohinter.
131 * All glyphs to be autohinted pass through here one by one. The
132 * standard widths can therefore change from one glyph to the next,
133 * depending on what script a glyph is assigned to (each script has its
134 * own set of standard widths and other metrics). The darkening amount
135 * must therefore be recomputed for each size and
136 * `standard_{vertical,horizontal}_width' change.
138 if ( !module
->no_stem_darkening
)
140 AF_FaceGlobals globals
= loader
->globals
;
141 AF_WritingSystemClass writing_system_class
;
146 FT_Bool size_changed
= face
->size
->metrics
.x_ppem
147 != globals
->stem_darkening_for_ppem
;
149 FT_Fixed em_size
= af_intToFixed( face
->units_per_EM
);
150 FT_Fixed em_ratio
= FT_DivFix( af_intToFixed( 1000 ), em_size
);
152 FT_Matrix scale_down_matrix
= { 0x10000L
, 0, 0, 0x10000L
};
155 /* Skip stem darkening for broken fonts. */
156 if ( !face
->units_per_EM
)
157 goto After_Emboldening
;
160 * We depend on the writing system (script analyzers) to supply
161 * standard widths for the script of the glyph we are looking at. If
162 * it can't deliver, stem darkening is effectively disabled.
164 writing_system_class
=
165 AF_WRITING_SYSTEM_CLASSES_GET
[metrics
->style_class
->writing_system
];
167 if ( writing_system_class
->style_metrics_getstdw
)
168 writing_system_class
->style_metrics_getstdw( metrics
,
172 goto After_Emboldening
;
176 ( stdVW
> 0 && stdVW
!= globals
->standard_vertical_width
) )
178 FT_Fixed darken_by_font_units_x
, darken_x
;
181 darken_by_font_units_x
=
182 af_intToFixed( af_loader_compute_darkening( loader
,
185 darken_x
= FT_DivFix( FT_MulFix( darken_by_font_units_x
,
186 face
->size
->metrics
.x_scale
),
189 globals
->standard_vertical_width
= stdVW
;
190 globals
->stem_darkening_for_ppem
= face
->size
->metrics
.x_ppem
;
191 globals
->darken_x
= af_fixedToInt( darken_x
);
195 ( stdHW
> 0 && stdHW
!= globals
->standard_horizontal_width
) )
197 FT_Fixed darken_by_font_units_y
, darken_y
;
200 darken_by_font_units_y
=
201 af_intToFixed( af_loader_compute_darkening( loader
,
204 darken_y
= FT_DivFix( FT_MulFix( darken_by_font_units_y
,
205 face
->size
->metrics
.y_scale
),
208 globals
->standard_horizontal_width
= stdHW
;
209 globals
->stem_darkening_for_ppem
= face
->size
->metrics
.x_ppem
;
210 globals
->darken_y
= af_fixedToInt( darken_y
);
213 * Scale outlines down on the Y-axis to keep them inside their blue
214 * zones. The stronger the emboldening, the stronger the
215 * downscaling (plus heuristical padding to prevent outlines still
216 * falling out their zones due to rounding).
218 * Reason: `FT_Outline_Embolden' works by shifting the rightmost
219 * points of stems farther to the right, and topmost points farther
220 * up. This positions points on the Y-axis outside their
221 * pre-computed blue zones and leads to distortion when applying the
222 * hints in the code further below. Code outside this emboldening
223 * block doesn't know we are presenting it with modified outlines
224 * the analyzer didn't see!
226 * An unfortunate side effect of downscaling is that the emboldening
227 * effect is slightly decreased. The loss becomes more pronounced
228 * versus the CFF driver at smaller sizes, e.g., at 9ppem and below.
230 globals
->scale_down_factor
=
231 FT_DivFix( em_size
- ( darken_by_font_units_y
+ af_intToFixed( 8 ) ),
235 FT_Outline_EmboldenXY( &slot
->outline
,
239 scale_down_matrix
.yy
= globals
->scale_down_factor
;
240 FT_Outline_Transform( &slot
->outline
, &scale_down_matrix
);
244 loader
->transformed
= internal
->glyph_transformed
;
245 if ( loader
->transformed
)
250 loader
->trans_matrix
= internal
->glyph_matrix
;
251 loader
->trans_delta
= internal
->glyph_delta
;
253 inverse
= loader
->trans_matrix
;
254 if ( !FT_Matrix_Invert( &inverse
) )
255 FT_Vector_Transform( &loader
->trans_delta
, &inverse
);
258 switch ( slot
->format
)
260 case FT_GLYPH_FORMAT_OUTLINE
:
261 /* translate the loaded glyph when an internal transform is needed */
262 if ( loader
->transformed
)
263 FT_Outline_Translate( &slot
->outline
,
264 loader
->trans_delta
.x
,
265 loader
->trans_delta
.y
);
267 /* compute original horizontal phantom points (and ignore */
269 loader
->pp1
.x
= hints
->x_delta
;
270 loader
->pp1
.y
= hints
->y_delta
;
271 loader
->pp2
.x
= FT_MulFix( slot
->metrics
.horiAdvance
,
272 hints
->x_scale
) + hints
->x_delta
;
273 loader
->pp2
.y
= hints
->y_delta
;
275 /* be sure to check for spacing glyphs */
276 if ( slot
->outline
.n_points
== 0 )
279 /* now load the slot image into the auto-outline and run the */
280 /* automatic hinting process */
282 #ifdef FT_CONFIG_OPTION_PIC
283 AF_FaceGlobals globals
= loader
->globals
;
285 AF_StyleClass style_class
= metrics
->style_class
;
286 AF_WritingSystemClass writing_system_class
=
287 AF_WRITING_SYSTEM_CLASSES_GET
[style_class
->writing_system
];
290 if ( writing_system_class
->style_hints_apply
)
291 writing_system_class
->style_hints_apply( glyph_index
,
293 &gloader
->base
.outline
,
297 /* we now need to adjust the metrics according to the change in */
298 /* width/positioning that occurred during the hinting process */
299 if ( scaler
->render_mode
!= FT_RENDER_MODE_LIGHT
)
301 FT_Pos old_rsb
, old_lsb
, new_lsb
;
302 FT_Pos pp1x_uh
, pp2x_uh
;
303 AF_AxisHints axis
= &hints
->axis
[AF_DIMENSION_HORZ
];
304 AF_Edge edge1
= axis
->edges
; /* leftmost edge */
305 AF_Edge edge2
= edge1
+
306 axis
->num_edges
- 1; /* rightmost edge */
309 if ( axis
->num_edges
> 1 && AF_HINTS_DO_ADVANCE( hints
) )
311 old_rsb
= loader
->pp2
.x
- edge2
->opos
;
312 old_lsb
= edge1
->opos
;
313 new_lsb
= edge1
->pos
;
315 /* remember unhinted values to later account */
316 /* for rounding errors */
318 pp1x_uh
= new_lsb
- old_lsb
;
319 pp2x_uh
= edge2
->pos
+ old_rsb
;
321 /* prefer too much space over too little space */
322 /* for very small sizes */
330 loader
->pp1
.x
= FT_PIX_ROUND( pp1x_uh
);
331 loader
->pp2
.x
= FT_PIX_ROUND( pp2x_uh
);
333 if ( loader
->pp1
.x
>= new_lsb
&& old_lsb
> 0 )
336 if ( loader
->pp2
.x
<= edge2
->pos
&& old_rsb
> 0 )
339 slot
->lsb_delta
= loader
->pp1
.x
- pp1x_uh
;
340 slot
->rsb_delta
= loader
->pp2
.x
- pp2x_uh
;
344 FT_Pos pp1x
= loader
->pp1
.x
;
345 FT_Pos pp2x
= loader
->pp2
.x
;
348 loader
->pp1
.x
= FT_PIX_ROUND( pp1x
);
349 loader
->pp2
.x
= FT_PIX_ROUND( pp2x
);
351 slot
->lsb_delta
= loader
->pp1
.x
- pp1x
;
352 slot
->rsb_delta
= loader
->pp2
.x
- pp2x
;
357 FT_Pos pp1x
= loader
->pp1
.x
;
358 FT_Pos pp2x
= loader
->pp2
.x
;
361 loader
->pp1
.x
= FT_PIX_ROUND( pp1x
+ hints
->xmin_delta
);
362 loader
->pp2
.x
= FT_PIX_ROUND( pp2x
+ hints
->xmax_delta
);
364 slot
->lsb_delta
= loader
->pp1
.x
- pp1x
;
365 slot
->rsb_delta
= loader
->pp2
.x
- pp2x
;
371 /* we don't support other formats (yet?) */
372 error
= FT_THROW( Unimplemented_Feature
);
381 vvector
.x
= slot
->metrics
.vertBearingX
- slot
->metrics
.horiBearingX
;
382 vvector
.y
= slot
->metrics
.vertBearingY
- slot
->metrics
.horiBearingY
;
383 vvector
.x
= FT_MulFix( vvector
.x
, metrics
->scaler
.x_scale
);
384 vvector
.y
= FT_MulFix( vvector
.y
, metrics
->scaler
.y_scale
);
386 /* transform the hinted outline if needed */
387 if ( loader
->transformed
)
389 FT_Outline_Transform( &gloader
->base
.outline
, &loader
->trans_matrix
);
390 FT_Vector_Transform( &vvector
, &loader
->trans_matrix
);
393 /* we must translate our final outline by -pp1.x and compute */
394 /* the new metrics */
396 FT_Outline_Translate( &gloader
->base
.outline
, -loader
->pp1
.x
, 0 );
398 FT_Outline_Get_CBox( &gloader
->base
.outline
, &bbox
);
400 bbox
.xMin
= FT_PIX_FLOOR( bbox
.xMin
);
401 bbox
.yMin
= FT_PIX_FLOOR( bbox
.yMin
);
402 bbox
.xMax
= FT_PIX_CEIL( bbox
.xMax
);
403 bbox
.yMax
= FT_PIX_CEIL( bbox
.yMax
);
405 slot
->metrics
.width
= bbox
.xMax
- bbox
.xMin
;
406 slot
->metrics
.height
= bbox
.yMax
- bbox
.yMin
;
407 slot
->metrics
.horiBearingX
= bbox
.xMin
;
408 slot
->metrics
.horiBearingY
= bbox
.yMax
;
410 slot
->metrics
.vertBearingX
= FT_PIX_FLOOR( bbox
.xMin
+ vvector
.x
);
411 slot
->metrics
.vertBearingY
= FT_PIX_FLOOR( bbox
.yMax
+ vvector
.y
);
413 /* for mono-width fonts (like Andale, Courier, etc.) we need */
414 /* to keep the original rounded advance width; ditto for */
415 /* digits if all have the same advance width */
417 if ( !FT_IS_FIXED_WIDTH( slot
->face
) )
418 slot
->metrics
.horiAdvance
= loader
->pp2
.x
- loader
->pp1
.x
;
420 slot
->metrics
.horiAdvance
= FT_MulFix( slot
->metrics
.horiAdvance
,
423 if ( scaler
->render_mode
!= FT_RENDER_MODE_LIGHT
&&
424 ( FT_IS_FIXED_WIDTH( slot
->face
) ||
425 ( af_face_globals_is_digit( loader
->globals
, glyph_index
) &&
426 metrics
->digits_have_same_width
) ) )
428 slot
->metrics
.horiAdvance
= FT_MulFix( slot
->metrics
.horiAdvance
,
429 metrics
->scaler
.x_scale
);
431 /* Set delta values to 0. Otherwise code that uses them is */
432 /* going to ruin the fixed advance width. */
438 /* non-spacing glyphs must stay as-is */
439 if ( slot
->metrics
.horiAdvance
)
440 slot
->metrics
.horiAdvance
= loader
->pp2
.x
- loader
->pp1
.x
;
444 slot
->metrics
.vertAdvance
= FT_MulFix( slot
->metrics
.vertAdvance
,
445 metrics
->scaler
.y_scale
);
447 slot
->metrics
.horiAdvance
= FT_PIX_ROUND( slot
->metrics
.horiAdvance
);
448 slot
->metrics
.vertAdvance
= FT_PIX_ROUND( slot
->metrics
.vertAdvance
);
451 /* reassign all outline fields except flags to protect them */
452 slot
->outline
.n_contours
= internal
->loader
->base
.outline
.n_contours
;
453 slot
->outline
.n_points
= internal
->loader
->base
.outline
.n_points
;
454 slot
->outline
.points
= internal
->loader
->base
.outline
.points
;
455 slot
->outline
.tags
= internal
->loader
->base
.outline
.tags
;
456 slot
->outline
.contours
= internal
->loader
->base
.outline
.contours
;
459 slot
->format
= FT_GLYPH_FORMAT_OUTLINE
;
469 FT_LOCAL_DEF( FT_Error
)
470 af_loader_load_glyph( AF_Loader loader
,
474 FT_Int32 load_flags
)
477 FT_Size size
= face
->size
;
482 return FT_THROW( Invalid_Size_Handle
);
487 scaler
.x_scale
= size
->metrics
.x_scale
;
488 scaler
.x_delta
= 0; /* XXX: TODO: add support for sub-pixel hinting */
489 scaler
.y_scale
= size
->metrics
.y_scale
;
490 scaler
.y_delta
= 0; /* XXX: TODO: add support for sub-pixel hinting */
492 scaler
.render_mode
= FT_LOAD_TARGET_MODE( load_flags
);
493 scaler
.flags
= 0; /* XXX: fix this */
495 error
= af_loader_reset( loader
, module
, face
);
498 AF_StyleMetrics metrics
;
499 FT_UInt options
= AF_STYLE_NONE_DFLT
;
502 #ifdef FT_OPTION_AUTOFIT2
503 /* XXX: undocumented hook to activate the latin2 writing system */
504 if ( load_flags
& ( 1UL << 20 ) )
505 options
= AF_STYLE_LTN2_DFLT
;
508 error
= af_face_globals_get_metrics( loader
->globals
, gindex
,
512 #ifdef FT_CONFIG_OPTION_PIC
513 AF_FaceGlobals globals
= loader
->globals
;
515 AF_StyleClass style_class
= metrics
->style_class
;
516 AF_WritingSystemClass writing_system_class
=
517 AF_WRITING_SYSTEM_CLASSES_GET
[style_class
->writing_system
];
520 loader
->metrics
= metrics
;
522 if ( writing_system_class
->style_metrics_scale
)
523 writing_system_class
->style_metrics_scale( metrics
, &scaler
);
525 metrics
->scaler
= scaler
;
527 load_flags
|= FT_LOAD_NO_SCALE
| FT_LOAD_IGNORE_TRANSFORM
;
528 load_flags
&= ~FT_LOAD_RENDER
;
530 if ( writing_system_class
->style_hints_init
)
532 error
= writing_system_class
->style_hints_init( loader
->hints
,
538 error
= af_loader_load_g( loader
, &scaler
, gindex
, load_flags
);
547 * Compute amount of font units the face should be emboldened by, in
548 * analogy to the CFF driver's `cf2_computeDarkening' function. See there
549 * for details of the algorithm.
551 * XXX: Currently a crude adaption of the original algorithm. Do better?
553 FT_LOCAL_DEF( FT_Int32
)
554 af_loader_compute_darkening( AF_Loader loader
,
556 FT_Pos standard_width
)
558 AF_Module module
= loader
->globals
->module
;
560 FT_UShort units_per_EM
;
561 FT_Fixed ppem
, em_ratio
;
562 FT_Fixed stem_width
, stem_width_per_1000
, scaled_stem
, darken_amount
;
564 FT_Int x1
, y1
, x2
, y2
, x3
, y3
, x4
, y4
;
567 ppem
= FT_MAX( af_intToFixed( 4 ),
568 af_intToFixed( face
->size
->metrics
.x_ppem
) );
569 units_per_EM
= face
->units_per_EM
;
571 em_ratio
= FT_DivFix( af_intToFixed( 1000 ),
572 af_intToFixed ( units_per_EM
) );
573 if ( em_ratio
< af_floatToFixed( .01 ) )
575 /* If something goes wrong, don't embolden. */
579 x1
= module
->darken_params
[0];
580 y1
= module
->darken_params
[1];
581 x2
= module
->darken_params
[2];
582 y2
= module
->darken_params
[3];
583 x3
= module
->darken_params
[4];
584 y3
= module
->darken_params
[5];
585 x4
= module
->darken_params
[6];
586 y4
= module
->darken_params
[7];
588 if ( standard_width
<= 0 )
590 stem_width
= af_intToFixed( 75 ); /* taken from cf2font.c */
591 stem_width_per_1000
= stem_width
;
595 stem_width
= af_intToFixed( standard_width
);
596 stem_width_per_1000
= FT_MulFix( stem_width
, em_ratio
);
599 log_base_2
= FT_MSB( (FT_UInt32
)stem_width_per_1000
) +
600 FT_MSB( (FT_UInt32
)ppem
);
602 if ( log_base_2
>= 46 )
604 /* possible overflow */
605 scaled_stem
= af_intToFixed( x4
);
608 scaled_stem
= FT_MulFix( stem_width_per_1000
, ppem
);
610 /* now apply the darkening parameters */
611 if ( scaled_stem
< af_intToFixed( x1
) )
612 darken_amount
= FT_DivFix( af_intToFixed( y1
), ppem
);
614 else if ( scaled_stem
< af_intToFixed( x2
) )
616 FT_Int xdelta
= x2
- x1
;
617 FT_Int ydelta
= y2
- y1
;
618 FT_Int x
= stem_width_per_1000
-
619 FT_DivFix( af_intToFixed( x1
), ppem
);
625 darken_amount
= FT_MulDiv( x
, ydelta
, xdelta
) +
626 FT_DivFix( af_intToFixed( y1
), ppem
);
629 else if ( scaled_stem
< af_intToFixed( x3
) )
633 FT_Int xdelta
= x3
- x2
;
634 FT_Int ydelta
= y3
- y2
;
635 FT_Int x
= stem_width_per_1000
-
636 FT_DivFix( af_intToFixed( x2
), ppem
);
642 darken_amount
= FT_MulDiv( x
, ydelta
, xdelta
) +
643 FT_DivFix( af_intToFixed( y2
), ppem
);
647 else if ( scaled_stem
< af_intToFixed( x4
) )
651 FT_Int xdelta
= x4
- x3
;
652 FT_Int ydelta
= y4
- y3
;
653 FT_Int x
= stem_width_per_1000
-
654 FT_DivFix( af_intToFixed( x3
), ppem
);
660 darken_amount
= FT_MulDiv( x
, ydelta
, xdelta
) +
661 FT_DivFix( af_intToFixed( y3
), ppem
);
668 darken_amount
= FT_DivFix( af_intToFixed( y4
), ppem
);
671 /* Convert darken_amount from per 1000 em to true character space. */
672 return af_fixedToInt( FT_DivFix( darken_amount
, em_ratio
) );