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