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