1 /***************************************************************************/ 2 /* */ 3 /* afloader.c */ 4 /* */ 5 /* Auto-fitter glyph loading routines (body). */ 6 /* */ 7 /* Copyright 2003-2009, 2011-2013 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 26 /* Initialize glyph loader. */ 27 28 FT_LOCAL_DEF( FT_Error ) 29 af_loader_init( AF_Module module ) 30 { 31 AF_Loader loader = module->loader; 32 FT_Memory memory = module->root.library->memory; 33 34 35 FT_ZERO( loader ); 36 37 af_glyph_hints_init( &loader->hints, memory ); 38 #ifdef FT_DEBUG_AUTOFIT 39 _af_debug_hints = &loader->hints; 40 #endif 41 return FT_GlyphLoader_New( memory, &loader->gloader ); 42 } 43 44 45 /* Reset glyph loader and compute globals if necessary. */ 46 47 FT_LOCAL_DEF( FT_Error ) 48 af_loader_reset( AF_Module module, 49 FT_Face face ) 50 { 51 FT_Error error = FT_Err_Ok; 52 AF_Loader loader = module->loader; 53 54 55 loader->face = face; 56 loader->globals = (AF_FaceGlobals)face->autohint.data; 57 58 FT_GlyphLoader_Rewind( loader->gloader ); 59 60 if ( loader->globals == NULL ) 61 { 62 error = af_face_globals_new( face, &loader->globals, module ); 63 if ( !error ) 64 { 65 face->autohint.data = 66 (FT_Pointer)loader->globals; 67 face->autohint.finalizer = 68 (FT_Generic_Finalizer)af_face_globals_free; 69 } 70 } 71 72 return error; 73 } 74 75 76 /* Finalize glyph loader. */ 77 78 FT_LOCAL_DEF( void ) 79 af_loader_done( AF_Module module ) 80 { 81 AF_Loader loader = module->loader; 82 83 84 af_glyph_hints_done( &loader->hints ); 85 86 loader->face = NULL; 87 loader->globals = NULL; 88 89 #ifdef FT_DEBUG_AUTOFIT 90 _af_debug_hints = NULL; 91 #endif 92 FT_GlyphLoader_Done( loader->gloader ); 93 loader->gloader = NULL; 94 } 95 96 97 /* Load a single glyph component. This routine calls itself */ 98 /* recursively, if necessary, and does the main work of */ 99 /* `af_loader_load_glyph.' */ 100 101 static FT_Error 102 af_loader_load_g( AF_Loader loader, 103 AF_Scaler scaler, 104 FT_UInt glyph_index, 105 FT_Int32 load_flags, 106 FT_UInt depth ) 107 { 108 FT_Error error; 109 FT_Face face = loader->face; 110 FT_GlyphLoader gloader = loader->gloader; 111 AF_ScriptMetrics metrics = loader->metrics; 112 AF_GlyphHints hints = &loader->hints; 113 FT_GlyphSlot slot = face->glyph; 114 FT_Slot_Internal internal = slot->internal; 115 FT_Int32 flags; 116 117 118 flags = load_flags | FT_LOAD_LINEAR_DESIGN; 119 error = FT_Load_Glyph( face, glyph_index, flags ); 120 if ( error ) 121 goto Exit; 122 123 loader->transformed = internal->glyph_transformed; 124 if ( loader->transformed ) 125 { 126 FT_Matrix inverse; 127 128 129 loader->trans_matrix = internal->glyph_matrix; 130 loader->trans_delta = internal->glyph_delta; 131 132 inverse = loader->trans_matrix; 133 FT_Matrix_Invert( &inverse ); 134 FT_Vector_Transform( &loader->trans_delta, &inverse ); 135 } 136 137 switch ( slot->format ) 138 { 139 case FT_GLYPH_FORMAT_OUTLINE: 140 /* translate the loaded glyph when an internal transform is needed */ 141 if ( loader->transformed ) 142 FT_Outline_Translate( &slot->outline, 143 loader->trans_delta.x, 144 loader->trans_delta.y ); 145 146 /* copy the outline points in the loader's current */ 147 /* extra points which are used to keep original glyph coordinates */ 148 error = FT_GLYPHLOADER_CHECK_POINTS( gloader, 149 slot->outline.n_points + 4, 150 slot->outline.n_contours ); 151 if ( error ) 152 goto Exit; 153 154 FT_ARRAY_COPY( gloader->current.outline.points, 155 slot->outline.points, 156 slot->outline.n_points ); 157 158 FT_ARRAY_COPY( gloader->current.outline.contours, 159 slot->outline.contours, 160 slot->outline.n_contours ); 161 162 FT_ARRAY_COPY( gloader->current.outline.tags, 163 slot->outline.tags, 164 slot->outline.n_points ); 165 166 gloader->current.outline.n_points = slot->outline.n_points; 167 gloader->current.outline.n_contours = slot->outline.n_contours; 168 169 /* compute original horizontal phantom points (and ignore */ 170 /* vertical ones) */ 171 loader->pp1.x = hints->x_delta; 172 loader->pp1.y = hints->y_delta; 173 loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, 174 hints->x_scale ) + hints->x_delta; 175 loader->pp2.y = hints->y_delta; 176 177 /* be sure to check for spacing glyphs */ 178 if ( slot->outline.n_points == 0 ) 179 goto Hint_Metrics; 180 181 /* now load the slot image into the auto-outline and run the */ 182 /* automatic hinting process */ 183 if ( metrics->clazz->script_hints_apply ) 184 metrics->clazz->script_hints_apply( hints, 185 &gloader->current.outline, 186 metrics ); 187 188 /* we now need to adjust the metrics according to the change in */ 189 /* width/positioning that occurred during the hinting process */ 190 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) 191 { 192 FT_Pos old_rsb, old_lsb, new_lsb; 193 FT_Pos pp1x_uh, pp2x_uh; 194 AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; 195 AF_Edge edge1 = axis->edges; /* leftmost edge */ 196 AF_Edge edge2 = edge1 + 197 axis->num_edges - 1; /* rightmost edge */ 198 199 200 if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) 201 { 202 old_rsb = loader->pp2.x - edge2->opos; 203 old_lsb = edge1->opos; 204 new_lsb = edge1->pos; 205 206 /* remember unhinted values to later account */ 207 /* for rounding errors */ 208 209 pp1x_uh = new_lsb - old_lsb; 210 pp2x_uh = edge2->pos + old_rsb; 211 212 /* prefer too much space over too little space */ 213 /* for very small sizes */ 214 215 if ( old_lsb < 24 ) 216 pp1x_uh -= 8; 217 218 if ( old_rsb < 24 ) 219 pp2x_uh += 8; 220 221 loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); 222 loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); 223 224 if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) 225 loader->pp1.x -= 64; 226 227 if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) 228 loader->pp2.x += 64; 229 230 slot->lsb_delta = loader->pp1.x - pp1x_uh; 231 slot->rsb_delta = loader->pp2.x - pp2x_uh; 232 } 233 else 234 { 235 FT_Pos pp1x = loader->pp1.x; 236 FT_Pos pp2x = loader->pp2.x; 237 238 239 loader->pp1.x = FT_PIX_ROUND( pp1x ); 240 loader->pp2.x = FT_PIX_ROUND( pp2x ); 241 242 slot->lsb_delta = loader->pp1.x - pp1x; 243 slot->rsb_delta = loader->pp2.x - pp2x; 244 } 245 } 246 else 247 { 248 FT_Pos pp1x = loader->pp1.x; 249 FT_Pos pp2x = loader->pp2.x; 250 251 252 loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); 253 loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); 254 255 slot->lsb_delta = loader->pp1.x - pp1x; 256 slot->rsb_delta = loader->pp2.x - pp2x; 257 } 258 259 /* good, we simply add the glyph to our loader's base */ 260 FT_GlyphLoader_Add( gloader ); 261 break; 262 263 case FT_GLYPH_FORMAT_COMPOSITE: 264 { 265 FT_UInt nn, num_subglyphs = slot->num_subglyphs; 266 FT_UInt num_base_subgs, start_point; 267 FT_SubGlyph subglyph; 268 269 270 start_point = gloader->base.outline.n_points; 271 272 /* first of all, copy the subglyph descriptors in the glyph loader */ 273 error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs ); 274 if ( error ) 275 goto Exit; 276 277 FT_ARRAY_COPY( gloader->current.subglyphs, 278 slot->subglyphs, 279 num_subglyphs ); 280 281 gloader->current.num_subglyphs = num_subglyphs; 282 num_base_subgs = gloader->base.num_subglyphs; 283 284 /* now read each subglyph independently */ 285 for ( nn = 0; nn < num_subglyphs; nn++ ) 286 { 287 FT_Vector pp1, pp2; 288 FT_Pos x, y; 289 FT_UInt num_points, num_new_points, num_base_points; 290 291 292 /* gloader.current.subglyphs can change during glyph loading due */ 293 /* to re-allocation -- we must recompute the current subglyph on */ 294 /* each iteration */ 295 subglyph = gloader->base.subglyphs + num_base_subgs + nn; 296 297 pp1 = loader->pp1; 298 pp2 = loader->pp2; 299 300 num_base_points = gloader->base.outline.n_points; 301 302 error = af_loader_load_g( loader, scaler, subglyph->index, 303 load_flags, depth + 1 ); 304 if ( error ) 305 goto Exit; 306 307 /* recompute subglyph pointer */ 308 subglyph = gloader->base.subglyphs + num_base_subgs + nn; 309 310 if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS ) 311 { 312 pp1 = loader->pp1; 313 pp2 = loader->pp2; 314 } 315 else 316 { 317 loader->pp1 = pp1; 318 loader->pp2 = pp2; 319 } 320 321 num_points = gloader->base.outline.n_points; 322 num_new_points = num_points - num_base_points; 323 324 /* now perform the transformation required for this subglyph */ 325 326 if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE | 327 FT_SUBGLYPH_FLAG_XY_SCALE | 328 FT_SUBGLYPH_FLAG_2X2 ) ) 329 { 330 FT_Vector* cur = gloader->base.outline.points + 331 num_base_points; 332 FT_Vector* limit = cur + num_new_points; 333 334 335 for ( ; cur < limit; cur++ ) 336 FT_Vector_Transform( cur, &subglyph->transform ); 337 } 338 339 /* apply offset */ 340 341 if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) ) 342 { 343 FT_Int k = subglyph->arg1; 344 FT_UInt l = subglyph->arg2; 345 FT_Vector* p1; 346 FT_Vector* p2; 347 348 349 if ( start_point + k >= num_base_points || 350 l >= (FT_UInt)num_new_points ) 351 { 352 error = FT_THROW( Invalid_Composite ); 353 goto Exit; 354 } 355 356 l += num_base_points; 357 358 /* for now, only use the current point coordinates; */ 359 /* we eventually may consider another approach */ 360 p1 = gloader->base.outline.points + start_point + k; 361 p2 = gloader->base.outline.points + start_point + l; 362 363 x = p1->x - p2->x; 364 y = p1->y - p2->y; 365 } 366 else 367 { 368 x = FT_MulFix( subglyph->arg1, hints->x_scale ) + hints->x_delta; 369 y = FT_MulFix( subglyph->arg2, hints->y_scale ) + hints->y_delta; 370 371 x = FT_PIX_ROUND( x ); 372 y = FT_PIX_ROUND( y ); 373 } 374 375 { 376 FT_Outline dummy = gloader->base.outline; 377 378 379 dummy.points += num_base_points; 380 dummy.n_points = (short)num_new_points; 381 382 FT_Outline_Translate( &dummy, x, y ); 383 } 384 } 385 } 386 break; 387 388 default: 389 /* we don't support other formats (yet?) */ 390 error = FT_THROW( Unimplemented_Feature ); 391 } 392 393 Hint_Metrics: 394 if ( depth == 0 ) 395 { 396 FT_BBox bbox; 397 FT_Vector vvector; 398 399 400 vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; 401 vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; 402 vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); 403 vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); 404 405 /* transform the hinted outline if needed */ 406 if ( loader->transformed ) 407 { 408 FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); 409 FT_Vector_Transform( &vvector, &loader->trans_matrix ); 410 } 411 #if 1 412 /* we must translate our final outline by -pp1.x and compute */ 413 /* the new metrics */ 414 if ( loader->pp1.x ) 415 FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); 416 #endif 417 FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); 418 419 bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); 420 bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); 421 bbox.xMax = FT_PIX_CEIL( bbox.xMax ); 422 bbox.yMax = FT_PIX_CEIL( bbox.yMax ); 423 424 slot->metrics.width = bbox.xMax - bbox.xMin; 425 slot->metrics.height = bbox.yMax - bbox.yMin; 426 slot->metrics.horiBearingX = bbox.xMin; 427 slot->metrics.horiBearingY = bbox.yMax; 428 429 slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); 430 slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); 431 432 /* for mono-width fonts (like Andale, Courier, etc.) we need */ 433 /* to keep the original rounded advance width; ditto for */ 434 /* digits if all have the same advance width */ 435 #if 0 436 if ( !FT_IS_FIXED_WIDTH( slot->face ) ) 437 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 438 else 439 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 440 x_scale ); 441 #else 442 if ( scaler->render_mode != FT_RENDER_MODE_LIGHT && 443 ( FT_IS_FIXED_WIDTH( slot->face ) || 444 ( af_face_globals_is_digit( loader->globals, glyph_index ) && 445 metrics->digits_have_same_width ) ) ) 446 { 447 slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, 448 metrics->scaler.x_scale ); 449 450 /* Set delta values to 0. Otherwise code that uses them is */ 451 /* going to ruin the fixed advance width. */ 452 slot->lsb_delta = 0; 453 slot->rsb_delta = 0; 454 } 455 else 456 { 457 /* non-spacing glyphs must stay as-is */ 458 if ( slot->metrics.horiAdvance ) 459 slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; 460 } 461 #endif 462 463 slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, 464 metrics->scaler.y_scale ); 465 466 slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); 467 slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); 468 469 /* now copy outline into glyph slot */ 470 FT_GlyphLoader_Rewind( internal->loader ); 471 error = FT_GlyphLoader_CopyPoints( internal->loader, gloader ); 472 if ( error ) 473 goto Exit; 474 475 /* reassign all outline fields except flags to protect them */ 476 slot->outline.n_contours = internal->loader->base.outline.n_contours; 477 slot->outline.n_points = internal->loader->base.outline.n_points; 478 slot->outline.points = internal->loader->base.outline.points; 479 slot->outline.tags = internal->loader->base.outline.tags; 480 slot->outline.contours = internal->loader->base.outline.contours; 481 482 slot->format = FT_GLYPH_FORMAT_OUTLINE; 483 } 484 485 Exit: 486 return error; 487 } 488 489 490 /* Load a glyph. */ 491 492 FT_LOCAL_DEF( FT_Error ) 493 af_loader_load_glyph( AF_Module module, 494 FT_Face face, 495 FT_UInt gindex, 496 FT_Int32 load_flags ) 497 { 498 FT_Error error; 499 FT_Size size = face->size; 500 AF_Loader loader = module->loader; 501 AF_ScalerRec scaler; 502 503 504 if ( !size ) 505 return FT_THROW( Invalid_Argument ); 506 507 FT_ZERO( &scaler ); 508 509 scaler.face = face; 510 scaler.x_scale = size->metrics.x_scale; 511 scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ 512 scaler.y_scale = size->metrics.y_scale; 513 scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ 514 515 scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); 516 scaler.flags = 0; /* XXX: fix this */ 517 518 error = af_loader_reset( module, face ); 519 if ( !error ) 520 { 521 AF_ScriptMetrics metrics; 522 FT_UInt options = 0; 523 524 525 #ifdef FT_OPTION_AUTOFIT2 526 /* XXX: undocumented hook to activate the latin2 hinter */ 527 if ( load_flags & ( 1UL << 20 ) ) 528 options = 2; 529 #endif 530 531 error = af_face_globals_get_metrics( loader->globals, gindex, 532 options, &metrics ); 533 if ( !error ) 534 { 535 loader->metrics = metrics; 536 537 if ( metrics->clazz->script_metrics_scale ) 538 metrics->clazz->script_metrics_scale( metrics, &scaler ); 539 else 540 metrics->scaler = scaler; 541 542 load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; 543 load_flags &= ~FT_LOAD_RENDER; 544 545 if ( metrics->clazz->script_hints_init ) 546 { 547 error = metrics->clazz->script_hints_init( &loader->hints, 548 metrics ); 549 if ( error ) 550 goto Exit; 551 } 552 553 error = af_loader_load_g( loader, &scaler, gindex, load_flags, 0 ); 554 } 555 } 556 Exit: 557 return error; 558 } 559 560 561 /* END */ 562