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