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