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( hints, 154 &gloader->base.outline, 155 metrics ); 156 } 157 158 /* we now need to adjust the metrics according to the change in */ 159 /* width/positioning that occurred during the hinting process */ 160 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) 161 { 162 FT_Pos old_rsb, old_lsb, new_lsb; 163 FT_Pos pp1x_uh, pp2x_uh; 164 AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; 165 AF_Edge edge1 = axis->edges; /* leftmost edge */ 166 AF_Edge edge2 = edge1 + 167 axis->num_edges - 1; /* rightmost edge */ 168 169 170 if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) 171 { 172 old_rsb = loader->pp2.x - edge2->opos; 173 old_lsb = edge1->opos; 174 new_lsb = edge1->pos; 175 176 /* remember unhinted values to later account */ 177 /* for rounding errors */ 178 179 pp1x_uh = new_lsb - old_lsb; 180 pp2x_uh = edge2->pos + old_rsb; 181 182 /* prefer too much space over too little space */ 183 /* for very small sizes */ 184 185 if ( old_lsb < 24 ) 186 pp1x_uh -= 8; 187 188 if ( old_rsb < 24 ) 189 pp2x_uh += 8; 190 191 loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); 192 loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); 193 194 if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) 195 loader->pp1.x -= 64; 196 197 if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) 198 loader->pp2.x += 64; 199 200 slot->lsb_delta = loader->pp1.x - pp1x_uh; 201 slot->rsb_delta = loader->pp2.x - pp2x_uh; 202 } 203 else 204 { 205 FT_Pos pp1x = loader->pp1.x; 206 FT_Pos pp2x = loader->pp2.x; 207 208 209 loader->pp1.x = FT_PIX_ROUND( pp1x ); 210 loader->pp2.x = FT_PIX_ROUND( pp2x ); 211 212 slot->lsb_delta = loader->pp1.x - pp1x; 213 slot->rsb_delta = loader->pp2.x - pp2x; 214 } 215 } 216 else 217 { 218 FT_Pos pp1x = loader->pp1.x; 219 FT_Pos pp2x = loader->pp2.x; 220 221 222 loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); 223 loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); 224 225 slot->lsb_delta = loader->pp1.x - pp1x; 226 slot->rsb_delta = loader->pp2.x - pp2x; 227 } 228 229 break; 230 231 default: 232 /* we don't support other formats (yet?) */ 233 error = FT_THROW( Unimplemented_Feature ); 234 } 235 236 Hint_Metrics: 237 { 238 FT_BBox bbox; 239 FT_Vector vvector; 240 241 242 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; 243 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; 244 vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); 245 vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); 246 247 /* transform the hinted outline if needed */ 248 if ( loader->transformed ) 249 { 250 FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); 251 FT_Vector_Transform( &vvector, &loader->trans_matrix ); 252 } 253 #if 1 254 /* we must translate our final outline by -pp1.x and compute */ 255 /* the new metrics */ 256 if ( loader->pp1.x ) 257 FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); 258 #endif 259 FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); 260 261 bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); 262 bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); 263 bbox.xMax = FT_PIX_CEIL( bbox.xMax ); 264 bbox.yMax = FT_PIX_CEIL( bbox.yMax ); 265 266 slot->metrics.width = bbox.xMax - bbox.xMin; 267 slot->metrics.height = bbox.yMax - bbox.yMin; 268 slot->metrics.horiBearingX = bbox.xMin; 269 slot->metrics.horiBearingY = bbox.yMax; 270 271 slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); 272 slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); 273 274 /* for mono-width fonts (like Andale, Courier, etc.) we need */ 275 /* to keep the original rounded advance width; ditto for */ 276 /* digits if all have the same advance width */ 277 #if 0 278 if ( !FT_IS_FIXED_WIDTH( slot->face ) ) 279 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 280 else 281 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 282 x_scale ); 283 #else 284 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT && 285 ( FT_IS_FIXED_WIDTH( slot->face ) || 286 ( af_face_globals_is_digit( loader->globals, glyph_index ) && 287 metrics->digits_have_same_width ) ) ) 288 { 289 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 290 metrics->scaler.x_scale ); 291 292 /* Set delta values to 0. Otherwise code that uses them is */ 293 /* going to ruin the fixed advance width. */ 294 slot->lsb_delta = 0; 295 slot->rsb_delta = 0; 296 } 297 else 298 { 299 /* non-spacing glyphs must stay as-is */ 300 if ( slot->metrics.horiAdvance ) 301 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 302 } 303 #endif 304 305 slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, 306 metrics->scaler.y_scale ); 307 308 slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); 309 slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); 310 311 #if 0 312 /* reassign all outline fields except flags to protect them */ 313 slot->outline.n_contours = internal->loader->base.outline.n_contours; 314 slot->outline.n_points = internal->loader->base.outline.n_points; 315 slot->outline.points = internal->loader->base.outline.points; 316 slot->outline.tags = internal->loader->base.outline.tags; 317 slot->outline.contours = internal->loader->base.outline.contours; 318 #endif 319 320 slot->format = FT_GLYPH_FORMAT_OUTLINE; 321 } 322 323 Exit: 324 return error; 325 } 326 327 328 /* Load a glyph. */ 329 330 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)331 af_loader_load_glyph( AF_Loader loader, 332 AF_Module module, 333 FT_Face face, 334 FT_UInt gindex, 335 FT_Int32 load_flags ) 336 { 337 FT_Error error; 338 FT_Size size = face->size; 339 AF_ScalerRec scaler; 340 341 342 if ( !size ) 343 return FT_THROW( Invalid_Size_Handle ); 344 345 FT_ZERO( &scaler ); 346 347 scaler.face = face; 348 scaler.x_scale = size->metrics.x_scale; 349 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ 350 scaler.y_scale = size->metrics.y_scale; 351 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ 352 353 scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); 354 scaler.flags = 0; /* XXX: fix this */ 355 356 error = af_loader_reset( loader, module, face ); 357 if ( !error ) 358 { 359 AF_StyleMetrics metrics; 360 FT_UInt options = AF_STYLE_NONE_DFLT; 361 362 363 #ifdef FT_OPTION_AUTOFIT2 364 /* XXX: undocumented hook to activate the latin2 writing system */ 365 if ( load_flags & ( 1UL << 20 ) ) 366 options = AF_STYLE_LTN2_DFLT; 367 #endif 368 369 error = af_face_globals_get_metrics( loader->globals, gindex, 370 options, &metrics ); 371 if ( !error ) 372 { 373 #ifdef FT_CONFIG_OPTION_PIC 374 AF_FaceGlobals globals = loader->globals; 375 #endif 376 AF_StyleClass style_class = metrics->style_class; 377 AF_WritingSystemClass writing_system_class = 378 AF_WRITING_SYSTEM_CLASSES_GET[style_class->writing_system]; 379 380 381 loader->metrics = metrics; 382 383 if ( writing_system_class->style_metrics_scale ) 384 writing_system_class->style_metrics_scale( metrics, &scaler ); 385 else 386 metrics->scaler = scaler; 387 388 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; 389 load_flags &= ~FT_LOAD_RENDER; 390 391 if ( writing_system_class->style_hints_init ) 392 { 393 error = writing_system_class->style_hints_init( loader->hints, 394 metrics ); 395 if ( error ) 396 goto Exit; 397 } 398 399 error = af_loader_load_g( loader, &scaler, gindex, load_flags ); 400 } 401 } 402 Exit: 403 return error; 404 } 405 406 407 /* END */ 408