1 /***************************************************************************/ 2 /* */ 3 /* afloader.c */ 4 /* */ 5 /* Auto-fitter glyph loading routines (body). */ 6 /* */ 7 /* Copyright 2003-2015 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 27 /* Initialize glyph loader. */ 28 29 FT_LOCAL_DEF( void ) af_loader_init(AF_Loader loader,AF_GlyphHints hints)30 af_loader_init( AF_Loader loader, 31 AF_GlyphHints hints ) 32 { 33 FT_ZERO( loader ); 34 35 loader->hints = hints; 36 } 37 38 39 /* Reset glyph loader and compute globals if necessary. */ 40 41 FT_LOCAL_DEF( FT_Error ) af_loader_reset(AF_Loader loader,AF_Module module,FT_Face face)42 af_loader_reset( AF_Loader loader, 43 AF_Module module, 44 FT_Face face ) 45 { 46 FT_Error error = FT_Err_Ok; 47 48 49 loader->face = face; 50 loader->globals = (AF_FaceGlobals)face->autohint.data; 51 52 if ( loader->globals == NULL ) 53 { 54 error = af_face_globals_new( face, &loader->globals, module ); 55 if ( !error ) 56 { 57 face->autohint.data = 58 (FT_Pointer)loader->globals; 59 face->autohint.finalizer = 60 (FT_Generic_Finalizer)af_face_globals_free; 61 } 62 } 63 64 return error; 65 } 66 67 68 /* Finalize glyph loader. */ 69 70 FT_LOCAL_DEF( void ) af_loader_done(AF_Loader loader)71 af_loader_done( AF_Loader loader ) 72 { 73 loader->face = NULL; 74 loader->globals = NULL; 75 loader->hints = NULL; 76 } 77 78 79 /* Do the main work of `af_loader_load_glyph'. Note that we never */ 80 /* have to deal with composite glyphs as those get loaded into */ 81 /* FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. */ 82 /* In the rare cases where FT_LOAD_NO_RECURSE is set, it implies */ 83 /* FT_LOAD_NO_SCALE and as such the auto-hinter is never called. */ 84 85 static FT_Error af_loader_load_g(AF_Loader loader,AF_Scaler scaler,FT_UInt glyph_index,FT_Int32 load_flags)86 af_loader_load_g( AF_Loader loader, 87 AF_Scaler scaler, 88 FT_UInt glyph_index, 89 FT_Int32 load_flags ) 90 { 91 FT_Error error; 92 FT_Face face = loader->face; 93 AF_StyleMetrics metrics = loader->metrics; 94 AF_GlyphHints hints = loader->hints; 95 FT_GlyphSlot slot = face->glyph; 96 FT_Slot_Internal internal = slot->internal; 97 FT_GlyphLoader gloader = internal->loader; 98 FT_Int32 flags; 99 100 101 flags = load_flags | FT_LOAD_LINEAR_DESIGN; 102 error = FT_Load_Glyph( face, glyph_index, flags ); 103 if ( error ) 104 goto Exit; 105 106 loader->transformed = internal->glyph_transformed; 107 if ( loader->transformed ) 108 { 109 FT_Matrix inverse; 110 111 112 loader->trans_matrix = internal->glyph_matrix; 113 loader->trans_delta = internal->glyph_delta; 114 115 inverse = loader->trans_matrix; 116 if ( !FT_Matrix_Invert( &inverse ) ) 117 FT_Vector_Transform( &loader->trans_delta, &inverse ); 118 } 119 120 switch ( slot->format ) 121 { 122 case FT_GLYPH_FORMAT_OUTLINE: 123 /* translate the loaded glyph when an internal transform is needed */ 124 if ( loader->transformed ) 125 FT_Outline_Translate( &slot->outline, 126 loader->trans_delta.x, 127 loader->trans_delta.y ); 128 129 /* compute original horizontal phantom points (and ignore */ 130 /* vertical ones) */ 131 loader->pp1.x = hints->x_delta; 132 loader->pp1.y = hints->y_delta; 133 loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, 134 hints->x_scale ) + hints->x_delta; 135 loader->pp2.y = hints->y_delta; 136 137 /* be sure to check for spacing glyphs */ 138 if ( slot->outline.n_points == 0 ) 139 goto Hint_Metrics; 140 141 /* now load the slot image into the auto-outline and run the */ 142 /* automatic hinting process */ 143 { 144 #ifdef FT_CONFIG_OPTION_PIC 145 AF_FaceGlobals globals = loader->globals; 146 #endif 147 AF_StyleClass style_class = metrics->style_class; 148 AF_WritingSystemClass writing_system_class = 149 AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; 150 151 152 if ( writing_system_class->style_hints_apply ) 153 writing_system_class->style_hints_apply( glyph_index, 154 hints, 155 &gloader->base.outline, 156 metrics ); 157 } 158 159 /* we now need to adjust the metrics according to the change in */ 160 /* width/positioning that occurred during the hinting process */ 161 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) 162 { 163 FT_Pos old_rsb, old_lsb, new_lsb; 164 FT_Pos pp1x_uh, pp2x_uh; 165 AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; 166 AF_Edge edge1 = axis->edges; /* leftmost edge */ 167 AF_Edge edge2 = edge1 + 168 axis->num_edges - 1; /* rightmost edge */ 169 170 171 if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) 172 { 173 old_rsb = loader->pp2.x - edge2->opos; 174 old_lsb = edge1->opos; 175 new_lsb = edge1->pos; 176 177 /* remember unhinted values to later account */ 178 /* for rounding errors */ 179 180 pp1x_uh = new_lsb - old_lsb; 181 pp2x_uh = edge2->pos + old_rsb; 182 183 /* prefer too much space over too little space */ 184 /* for very small sizes */ 185 186 if ( old_lsb < 24 ) 187 pp1x_uh -= 8; 188 189 if ( old_rsb < 24 ) 190 pp2x_uh += 8; 191 192 loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); 193 loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); 194 195 if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) 196 loader->pp1.x -= 64; 197 198 if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) 199 loader->pp2.x += 64; 200 201 slot->lsb_delta = loader->pp1.x - pp1x_uh; 202 slot->rsb_delta = loader->pp2.x - pp2x_uh; 203 } 204 else 205 { 206 FT_Pos pp1x = loader->pp1.x; 207 FT_Pos pp2x = loader->pp2.x; 208 209 210 loader->pp1.x = FT_PIX_ROUND( pp1x ); 211 loader->pp2.x = FT_PIX_ROUND( pp2x ); 212 213 slot->lsb_delta = loader->pp1.x - pp1x; 214 slot->rsb_delta = loader->pp2.x - pp2x; 215 } 216 } 217 else 218 { 219 FT_Pos pp1x = loader->pp1.x; 220 FT_Pos pp2x = loader->pp2.x; 221 222 223 loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); 224 loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); 225 226 slot->lsb_delta = loader->pp1.x - pp1x; 227 slot->rsb_delta = loader->pp2.x - pp2x; 228 } 229 230 break; 231 232 default: 233 /* we don't support other formats (yet?) */ 234 error = FT_THROW( Unimplemented_Feature ); 235 } 236 237 Hint_Metrics: 238 { 239 FT_BBox bbox; 240 FT_Vector vvector; 241 242 243 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; 244 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; 245 vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); 246 vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); 247 248 /* transform the hinted outline if needed */ 249 if ( loader->transformed ) 250 { 251 FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); 252 FT_Vector_Transform( &vvector, &loader->trans_matrix ); 253 } 254 #if 1 255 /* we must translate our final outline by -pp1.x and compute */ 256 /* the new metrics */ 257 if ( loader->pp1.x ) 258 FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); 259 #endif 260 FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); 261 262 bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); 263 bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); 264 bbox.xMax = FT_PIX_CEIL( bbox.xMax ); 265 bbox.yMax = FT_PIX_CEIL( bbox.yMax ); 266 267 slot->metrics.width = bbox.xMax - bbox.xMin; 268 slot->metrics.height = bbox.yMax - bbox.yMin; 269 slot->metrics.horiBearingX = bbox.xMin; 270 slot->metrics.horiBearingY = bbox.yMax; 271 272 slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); 273 slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); 274 275 /* for mono-width fonts (like Andale, Courier, etc.) we need */ 276 /* to keep the original rounded advance width; ditto for */ 277 /* digits if all have the same advance width */ 278 #if 0 279 if ( !FT_IS_FIXED_WIDTH( slot->face ) ) 280 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 281 else 282 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 283 x_scale ); 284 #else 285 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT && 286 ( FT_IS_FIXED_WIDTH( slot->face ) || 287 ( af_face_globals_is_digit( loader->globals, glyph_index ) && 288 metrics->digits_have_same_width ) ) ) 289 { 290 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 291 metrics->scaler.x_scale ); 292 293 /* Set delta values to 0. Otherwise code that uses them is */ 294 /* going to ruin the fixed advance width. */ 295 slot->lsb_delta = 0; 296 slot->rsb_delta = 0; 297 } 298 else 299 { 300 /* non-spacing glyphs must stay as-is */ 301 if ( slot->metrics.horiAdvance ) 302 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 303 } 304 #endif 305 306 slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, 307 metrics->scaler.y_scale ); 308 309 slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); 310 slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); 311 312 #if 0 313 /* reassign all outline fields except flags to protect them */ 314 slot->outline.n_contours = internal->loader->base.outline.n_contours; 315 slot->outline.n_points = internal->loader->base.outline.n_points; 316 slot->outline.points = internal->loader->base.outline.points; 317 slot->outline.tags = internal->loader->base.outline.tags; 318 slot->outline.contours = internal->loader->base.outline.contours; 319 #endif 320 321 slot->format = FT_GLYPH_FORMAT_OUTLINE; 322 } 323 324 Exit: 325 return error; 326 } 327 328 329 /* Load a glyph. */ 330 331 FT_LOCAL_DEF( FT_Error ) af_loader_load_glyph(AF_Loader loader,AF_Module module,FT_Face face,FT_UInt gindex,FT_Int32 load_flags)332 af_loader_load_glyph( AF_Loader loader, 333 AF_Module module, 334 FT_Face face, 335 FT_UInt gindex, 336 FT_Int32 load_flags ) 337 { 338 FT_Error error; 339 FT_Size size = face->size; 340 AF_ScalerRec scaler; 341 342 343 if ( !size ) 344 return FT_THROW( Invalid_Size_Handle ); 345 346 FT_ZERO( &scaler ); 347 348 scaler.face = face; 349 scaler.x_scale = size->metrics.x_scale; 350 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ 351 scaler.y_scale = size->metrics.y_scale; 352 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ 353 354 scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); 355 scaler.flags = 0; /* XXX: fix this */ 356 357 error = af_loader_reset( loader, module, face ); 358 if ( !error ) 359 { 360 AF_StyleMetrics metrics; 361 FT_UInt options = AF_STYLE_NONE_DFLT; 362 363 364 #ifdef FT_OPTION_AUTOFIT2 365 /* XXX: undocumented hook to activate the latin2 writing system */ 366 if ( load_flags & ( 1UL << 20 ) ) 367 options = AF_STYLE_LTN2_DFLT; 368 #endif 369 370 error = af_face_globals_get_metrics( loader->globals, gindex, 371 options, &metrics ); 372 if ( !error ) 373 { 374 #ifdef FT_CONFIG_OPTION_PIC 375 AF_FaceGlobals globals = loader->globals; 376 #endif 377 AF_StyleClass style_class = metrics->style_class; 378 AF_WritingSystemClass writing_system_class = 379 AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; 380 381 382 loader->metrics = metrics; 383 384 if ( writing_system_class->style_metrics_scale ) 385 writing_system_class->style_metrics_scale( metrics, &scaler ); 386 else 387 metrics->scaler = scaler; 388 389 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; 390 load_flags &= ~FT_LOAD_RENDER; 391 392 if ( writing_system_class->style_hints_init ) 393 { 394 error = writing_system_class->style_hints_init( loader->hints, 395 metrics ); 396 if ( error ) 397 goto Exit; 398 } 399 400 error = af_loader_load_g( loader, &scaler, gindex, load_flags ); 401 } 402 } 403 Exit: 404 return error; 405 } 406 407 408 /* END */ 409