1 /****************************************************************************
   2  *
   3  * afloader.c
   4  *
   5  *   Auto-fitter glyph loading routines (body).
   6  *
   7  * Copyright (C) 2003-2020 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 #include FT_INTERNAL_CALC_H
  26 
  27 
  28   /* Initialize glyph loader. */
  29 
  30   FT_LOCAL_DEF( void )
  31   af_loader_init( AF_Loader      loader,
  32                   AF_GlyphHints  hints )
  33   {
  34     FT_ZERO( loader );
  35 
  36     loader->hints = hints;
  37   }
  38 
  39 
  40   /* Reset glyph loader and compute globals if necessary. */
  41 
  42   FT_LOCAL_DEF( FT_Error )
  43   af_loader_reset( AF_Loader  loader,
  44                    AF_Module  module,
  45                    FT_Face    face )
  46   {
  47     FT_Error  error = FT_Err_Ok;
  48 
  49 
  50     loader->face    = face;
  51     loader->globals = (AF_FaceGlobals)face->autohint.data;
  52 
  53     if ( !loader->globals )
  54     {
  55       error = af_face_globals_new( face, &loader->globals, module );
  56       if ( !error )
  57       {
  58         face->autohint.data =
  59           (FT_Pointer)loader->globals;
  60         face->autohint.finalizer =
  61           (FT_Generic_Finalizer)af_face_globals_free;
  62       }
  63     }
  64 
  65     return error;
  66   }
  67 
  68 
  69   /* Finalize glyph loader. */
  70 
  71   FT_LOCAL_DEF( void )
  72   af_loader_done( AF_Loader  loader )
  73   {
  74     loader->face    = NULL;
  75     loader->globals = NULL;
  76     loader->hints   = NULL;
  77   }
  78 
  79 
  80 #define af_intToFixed( i ) \
  81           ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
  82 #define af_fixedToInt( x ) \
  83           ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
  84 #define af_floatToFixed( f ) \
  85           ( (FT_Fixed)( (f) * 65536.0 + 0.5 ) )
  86 
  87 
  88   static FT_Error
  89   af_loader_embolden_glyph_in_slot( AF_Loader        loader,
  90                                     FT_Face          face,
  91                                     AF_StyleMetrics  style_metrics )
  92   {
  93     FT_Error  error = FT_Err_Ok;
  94 
  95     FT_GlyphSlot           slot    = face->glyph;
  96     AF_FaceGlobals         globals = loader->globals;
  97     AF_WritingSystemClass  writing_system_class;
  98 
  99     FT_Size_Metrics*  size_metrics = &face->size->internal->autohint_metrics;
 100 
 101     FT_Pos  stdVW = 0;
 102     FT_Pos  stdHW = 0;
 103 
 104     FT_Bool  size_changed = size_metrics->x_ppem !=
 105                               globals->stem_darkening_for_ppem;
 106 
 107     FT_Fixed  em_size  = af_intToFixed( face->units_per_EM );
 108     FT_Fixed  em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size );
 109 
 110     FT_Matrix  scale_down_matrix = { 0x10000L, 0, 0, 0x10000L };
 111 
 112 
 113     /* Skip stem darkening for broken fonts. */
 114     if ( !face->units_per_EM )
 115     {
 116       error = FT_ERR( Corrupted_Font_Header );
 117       goto Exit;
 118     }
 119 
 120     /*
 121      * We depend on the writing system (script analyzers) to supply
 122      * standard widths for the script of the glyph we are looking at.  If
 123      * it can't deliver, stem darkening is disabled.
 124      */
 125     writing_system_class =
 126       af_writing_system_classes[style_metrics->style_class->writing_system];
 127 
 128     if ( writing_system_class->style_metrics_getstdw )
 129       writing_system_class->style_metrics_getstdw( style_metrics,
 130                                                    &stdHW,
 131                                                    &stdVW );
 132     else
 133     {
 134       error = FT_ERR( Unimplemented_Feature );
 135       goto Exit;
 136     }
 137 
 138     if ( size_changed                                               ||
 139          ( stdVW > 0 && stdVW != globals->standard_vertical_width ) )
 140     {
 141       FT_Fixed  darken_by_font_units_x, darken_x;
 142 
 143 
 144       darken_by_font_units_x =
 145         af_intToFixed( af_loader_compute_darkening( loader,
 146                                                     face,
 147                                                     stdVW ) );
 148       darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x,
 149                                        size_metrics->x_scale ),
 150                             em_ratio );
 151 
 152       globals->standard_vertical_width = stdVW;
 153       globals->stem_darkening_for_ppem = size_metrics->x_ppem;
 154       globals->darken_x                = af_fixedToInt( darken_x );
 155     }
 156 
 157     if ( size_changed                                                 ||
 158          ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) )
 159     {
 160       FT_Fixed  darken_by_font_units_y, darken_y;
 161 
 162 
 163       darken_by_font_units_y =
 164         af_intToFixed( af_loader_compute_darkening( loader,
 165                                                     face,
 166                                                     stdHW ) );
 167       darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y,
 168                                        size_metrics->y_scale ),
 169                             em_ratio );
 170 
 171       globals->standard_horizontal_width = stdHW;
 172       globals->stem_darkening_for_ppem   = size_metrics->x_ppem;
 173       globals->darken_y                  = af_fixedToInt( darken_y );
 174 
 175       /*
 176        * Scale outlines down on the Y-axis to keep them inside their blue
 177        * zones.  The stronger the emboldening, the stronger the downscaling
 178        * (plus heuristical padding to prevent outlines still falling out
 179        * their zones due to rounding).
 180        *
 181        * Reason: `FT_Outline_Embolden' works by shifting the rightmost
 182        * points of stems farther to the right, and topmost points farther
 183        * up.  This positions points on the Y-axis outside their
 184        * pre-computed blue zones and leads to distortion when applying the
 185        * hints in the code further below.  Code outside this emboldening
 186        * block doesn't know we are presenting it with modified outlines the
 187        * analyzer didn't see!
 188        *
 189        * An unfortunate side effect of downscaling is that the emboldening
 190        * effect is slightly decreased.  The loss becomes more pronounced
 191        * versus the CFF driver at smaller sizes, e.g., at 9ppem and below.
 192        */
 193       globals->scale_down_factor =
 194         FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ),
 195                    em_size );
 196     }
 197 
 198     FT_Outline_EmboldenXY( &slot->outline,
 199                            globals->darken_x,
 200                            globals->darken_y );
 201 
 202     scale_down_matrix.yy = globals->scale_down_factor;
 203     FT_Outline_Transform( &slot->outline, &scale_down_matrix );
 204 
 205   Exit:
 206     return error;
 207   }
 208 
 209 
 210   /* Load the glyph at index into the current slot of a face and hint it. */
 211 
 212   FT_LOCAL_DEF( FT_Error )
 213   af_loader_load_glyph( AF_Loader  loader,
 214                         AF_Module  module,
 215                         FT_Face    face,
 216                         FT_UInt    glyph_index,
 217                         FT_Int32   load_flags )
 218   {
 219     FT_Error  error;
 220 
 221     FT_Size           size          = face->size;
 222     FT_Size_Internal  size_internal = size->internal;
 223     FT_GlyphSlot      slot          = face->glyph;
 224     FT_Slot_Internal  slot_internal = slot->internal;
 225     FT_GlyphLoader    gloader       = slot_internal->loader;
 226 
 227     AF_GlyphHints          hints         = loader->hints;
 228     AF_ScalerRec           scaler;
 229     AF_StyleMetrics        style_metrics;
 230     FT_UInt                style_options = AF_STYLE_NONE_DFLT;
 231     AF_StyleClass          style_class;
 232     AF_WritingSystemClass  writing_system_class;
 233 
 234 
 235     if ( !size )
 236       return FT_THROW( Invalid_Size_Handle );
 237 
 238     FT_ZERO( &scaler );
 239 
 240     if ( !size_internal->autohint_metrics.x_scale                          ||
 241          size_internal->autohint_mode != FT_LOAD_TARGET_MODE( load_flags ) )
 242     {
 243       /* switching between hinting modes usually means different scaling */
 244       /* values; this later on enforces recomputation of everything      */
 245       /* related to the current size                                     */
 246 
 247       size_internal->autohint_mode    = FT_LOAD_TARGET_MODE( load_flags );
 248       size_internal->autohint_metrics = size->metrics;
 249 
 250 #ifdef AF_CONFIG_OPTION_TT_SIZE_METRICS
 251       {
 252         FT_Size_Metrics*  size_metrics = &size_internal->autohint_metrics;
 253 
 254 
 255         /* set metrics to integer values and adjust scaling accordingly; */
 256         /* this is the same setup as with TrueType fonts, cf. function   */
 257         /* `tt_size_reset' in file `ttobjs.c'                            */
 258         size_metrics->ascender  = FT_PIX_ROUND(
 259                                     FT_MulFix( face->ascender,
 260                                                size_metrics->y_scale ) );
 261         size_metrics->descender = FT_PIX_ROUND(
 262                                     FT_MulFix( face->descender,
 263                                                size_metrics->y_scale ) );
 264         size_metrics->height    = FT_PIX_ROUND(
 265                                     FT_MulFix( face->height,
 266                                                size_metrics->y_scale ) );
 267 
 268         size_metrics->x_scale     = FT_DivFix( size_metrics->x_ppem << 6,
 269                                                face->units_per_EM );
 270         size_metrics->y_scale     = FT_DivFix( size_metrics->y_ppem << 6,
 271                                                face->units_per_EM );
 272         size_metrics->max_advance = FT_PIX_ROUND(
 273                                       FT_MulFix( face->max_advance_width,
 274                                                  size_metrics->x_scale ) );
 275       }
 276 #endif /* AF_CONFIG_OPTION_TT_SIZE_METRICS */
 277     }
 278 
 279     /*
 280      * TODO: This code currently doesn't support fractional advance widths,
 281      * i.e., placing hinted glyphs at anything other than integer
 282      * x-positions.  This is only relevant for the warper code, which
 283      * scales and shifts glyphs to optimize blackness of stems (hinting on
 284      * the x-axis by nature places things on pixel integers, hinting on the
 285      * y-axis only, i.e., LIGHT mode, doesn't touch the x-axis).  The delta
 286      * values of the scaler would need to be adjusted.
 287      */
 288     scaler.face    = face;
 289     scaler.x_scale = size_internal->autohint_metrics.x_scale;
 290     scaler.x_delta = 0;
 291     scaler.y_scale = size_internal->autohint_metrics.y_scale;
 292     scaler.y_delta = 0;
 293 
 294     scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags );
 295     scaler.flags       = 0;
 296 
 297     /* note that the fallback style can't be changed anymore */
 298     /* after the first call of `af_loader_load_glyph'        */
 299     error = af_loader_reset( loader, module, face );
 300     if ( error )
 301       goto Exit;
 302 
 303 #ifdef FT_OPTION_AUTOFIT2
 304     /* XXX: undocumented hook to activate the latin2 writing system. */
 305     if ( load_flags & ( 1UL << 20 ) )
 306       style_options = AF_STYLE_LTN2_DFLT;
 307 #endif
 308 
 309     /*
 310      * Glyphs (really code points) are assigned to scripts.  Script
 311      * analysis is done lazily: For each glyph that passes through here,
 312      * the corresponding script analyzer is called, but returns immediately
 313      * if it has been run already.
 314      */
 315     error = af_face_globals_get_metrics( loader->globals, glyph_index,
 316                                          style_options, &style_metrics );
 317     if ( error )
 318       goto Exit;
 319 
 320     style_class          = style_metrics->style_class;
 321     writing_system_class =
 322       af_writing_system_classes[style_class->writing_system];
 323 
 324     loader->metrics = style_metrics;
 325 
 326     if ( writing_system_class->style_metrics_scale )
 327       writing_system_class->style_metrics_scale( style_metrics, &scaler );
 328     else
 329       style_metrics->scaler = scaler;
 330 
 331     if ( writing_system_class->style_hints_init )
 332     {
 333       error = writing_system_class->style_hints_init( hints,
 334                                                       style_metrics );
 335       if ( error )
 336         goto Exit;
 337     }
 338 
 339     /*
 340      * Do the main work of `af_loader_load_glyph'.  Note that we never have
 341      * to deal with composite glyphs as those get loaded into
 342      * FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function.
 343      * In the rare cases where FT_LOAD_NO_RECURSE is set, it implies
 344      * FT_LOAD_NO_SCALE and as such the auto-hinter is never called.
 345      */
 346     load_flags |=  FT_LOAD_NO_SCALE         |
 347                    FT_LOAD_IGNORE_TRANSFORM |
 348                    FT_LOAD_LINEAR_DESIGN;
 349     load_flags &= ~FT_LOAD_RENDER;
 350 
 351     error = FT_Load_Glyph( face, glyph_index, load_flags );
 352     if ( error )
 353       goto Exit;
 354 
 355     /*
 356      * Apply stem darkening (emboldening) here before hints are applied to
 357      * the outline.  Glyphs are scaled down proportionally to the
 358      * emboldening so that curve points don't fall outside their
 359      * precomputed blue zones.
 360      *
 361      * Any emboldening done by the font driver (e.g., the CFF driver)
 362      * doesn't reach here because the autohinter loads the unprocessed
 363      * glyphs in font units for analysis (functions `af_*_metrics_init_*')
 364      * and then above to prepare it for the rasterizers by itself,
 365      * independently of the font driver.  So emboldening must be done here,
 366      * within the autohinter.
 367      *
 368      * All glyphs to be autohinted pass through here one by one.  The
 369      * standard widths can therefore change from one glyph to the next,
 370      * depending on what script a glyph is assigned to (each script has its
 371      * own set of standard widths and other metrics).  The darkening amount
 372      * must therefore be recomputed for each size and
 373      * `standard_{vertical,horizontal}_width' change.
 374      *
 375      * Ignore errors and carry on without emboldening.
 376      *
 377      */
 378 
 379     /* stem darkening only works well in `light' mode */
 380     if ( scaler.render_mode == FT_RENDER_MODE_LIGHT    &&
 381          ( !face->internal->no_stem_darkening        ||
 382            ( face->internal->no_stem_darkening < 0 &&
 383              !module->no_stem_darkening            ) ) )
 384       af_loader_embolden_glyph_in_slot( loader, face, style_metrics );
 385 
 386     loader->transformed = slot_internal->glyph_transformed;
 387     if ( loader->transformed )
 388     {
 389       FT_Matrix  inverse;
 390 
 391 
 392       loader->trans_matrix = slot_internal->glyph_matrix;
 393       loader->trans_delta  = slot_internal->glyph_delta;
 394 
 395       inverse = loader->trans_matrix;
 396       if ( !FT_Matrix_Invert( &inverse ) )
 397         FT_Vector_Transform( &loader->trans_delta, &inverse );
 398     }
 399 
 400     switch ( slot->format )
 401     {
 402     case FT_GLYPH_FORMAT_OUTLINE:
 403       /* translate the loaded glyph when an internal transform is needed */
 404       if ( loader->transformed )
 405         FT_Outline_Translate( &slot->outline,
 406                               loader->trans_delta.x,
 407                               loader->trans_delta.y );
 408 
 409       /* compute original horizontal phantom points */
 410       /* (and ignore vertical ones)                 */
 411       loader->pp1.x = hints->x_delta;
 412       loader->pp1.y = hints->y_delta;
 413       loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance,
 414                                  hints->x_scale ) + hints->x_delta;
 415       loader->pp2.y = hints->y_delta;
 416 
 417       /* be sure to check for spacing glyphs */
 418       if ( slot->outline.n_points == 0 )
 419         goto Hint_Metrics;
 420 
 421       /* now load the slot image into the auto-outline */
 422       /* and run the automatic hinting process         */
 423       if ( writing_system_class->style_hints_apply )
 424       {
 425         error = writing_system_class->style_hints_apply(
 426                   glyph_index,
 427                   hints,
 428                   &gloader->base.outline,
 429                   style_metrics );
 430         if ( error )
 431           goto Exit;
 432       }
 433 
 434       /* we now need to adjust the metrics according to the change in */
 435       /* width/positioning that occurred during the hinting process   */
 436       if ( scaler.render_mode != FT_RENDER_MODE_LIGHT )
 437       {
 438         AF_AxisHints  axis  = &hints->axis[AF_DIMENSION_HORZ];
 439 
 440 
 441         if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
 442         {
 443           AF_Edge  edge1 = axis->edges;         /* leftmost edge  */
 444           AF_Edge  edge2 = edge1 +
 445                            axis->num_edges - 1; /* rightmost edge */
 446 
 447           FT_Pos  old_rsb = loader->pp2.x - edge2->opos;
 448           /* loader->pp1.x is always zero at this point of time */
 449           FT_Pos  old_lsb = edge1->opos;     /* - loader->pp1.x */
 450           FT_Pos  new_lsb = edge1->pos;
 451 
 452           /* remember unhinted values to later account */
 453           /* for rounding errors                       */
 454           FT_Pos  pp1x_uh = new_lsb    - old_lsb;
 455           FT_Pos  pp2x_uh = edge2->pos + old_rsb;
 456 
 457 
 458           /* prefer too much space over too little space */
 459           /* for very small sizes                        */
 460 
 461           if ( old_lsb < 24 )
 462             pp1x_uh -= 8;
 463 
 464           if ( old_rsb < 24 )
 465             pp2x_uh += 8;
 466 
 467           loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
 468           loader->pp2.x = FT_PIX_ROUND( pp2x_uh );
 469 
 470           if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
 471             loader->pp1.x -= 64;
 472 
 473           if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
 474             loader->pp2.x += 64;
 475 
 476           slot->lsb_delta = loader->pp1.x - pp1x_uh;
 477           slot->rsb_delta = loader->pp2.x - pp2x_uh;
 478         }
 479         else
 480         {
 481           FT_Pos  pp1x = loader->pp1.x;
 482           FT_Pos  pp2x = loader->pp2.x;
 483 
 484 
 485           loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
 486           loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
 487 
 488           slot->lsb_delta = loader->pp1.x - pp1x;
 489           slot->rsb_delta = loader->pp2.x - pp2x;
 490         }
 491       }
 492       /* `light' mode uses integer advance widths */
 493       /* but sets `lsb_delta' and `rsb_delta'     */
 494       else
 495       {
 496         FT_Pos  pp1x = loader->pp1.x;
 497         FT_Pos  pp2x = loader->pp2.x;
 498 
 499 
 500         loader->pp1.x = FT_PIX_ROUND( pp1x );
 501         loader->pp2.x = FT_PIX_ROUND( pp2x );
 502 
 503         slot->lsb_delta = loader->pp1.x - pp1x;
 504         slot->rsb_delta = loader->pp2.x - pp2x;
 505       }
 506 
 507       break;
 508 
 509     default:
 510       /* we don't support other formats (yet?) */
 511       error = FT_THROW( Unimplemented_Feature );
 512     }
 513 
 514   Hint_Metrics:
 515     {
 516       FT_BBox    bbox;
 517       FT_Vector  vvector;
 518 
 519 
 520       vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
 521       vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
 522       vvector.x = FT_MulFix( vvector.x, style_metrics->scaler.x_scale );
 523       vvector.y = FT_MulFix( vvector.y, style_metrics->scaler.y_scale );
 524 
 525       /* transform the hinted outline if needed */
 526       if ( loader->transformed )
 527       {
 528         FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
 529         FT_Vector_Transform( &vvector, &loader->trans_matrix );
 530       }
 531 
 532       /* we must translate our final outline by -pp1.x and compute */
 533       /* the new metrics                                           */
 534       if ( loader->pp1.x )
 535         FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
 536 
 537       FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
 538 
 539       bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
 540       bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
 541       bbox.xMax = FT_PIX_CEIL(  bbox.xMax );
 542       bbox.yMax = FT_PIX_CEIL(  bbox.yMax );
 543 
 544       slot->metrics.width        = bbox.xMax - bbox.xMin;
 545       slot->metrics.height       = bbox.yMax - bbox.yMin;
 546       slot->metrics.horiBearingX = bbox.xMin;
 547       slot->metrics.horiBearingY = bbox.yMax;
 548 
 549       slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
 550       slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );
 551 
 552       /* for mono-width fonts (like Andale, Courier, etc.) we need */
 553       /* to keep the original rounded advance width; ditto for     */
 554       /* digits if all have the same advance width                 */
 555       if ( scaler.render_mode != FT_RENDER_MODE_LIGHT                       &&
 556            ( FT_IS_FIXED_WIDTH( slot->face )                              ||
 557              ( af_face_globals_is_digit( loader->globals, glyph_index ) &&
 558                style_metrics->digits_have_same_width                    ) ) )
 559       {
 560         slot->metrics.horiAdvance =
 561           FT_MulFix( slot->metrics.horiAdvance,
 562                      style_metrics->scaler.x_scale );
 563 
 564         /* Set delta values to 0.  Otherwise code that uses them is */
 565         /* going to ruin the fixed advance width.                   */
 566         slot->lsb_delta = 0;
 567         slot->rsb_delta = 0;
 568       }
 569       else
 570       {
 571         /* non-spacing glyphs must stay as-is */
 572         if ( slot->metrics.horiAdvance )
 573           slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
 574       }
 575 
 576       slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
 577                                              style_metrics->scaler.y_scale );
 578 
 579       slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
 580       slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
 581 
 582       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
 583     }
 584 
 585   Exit:
 586     return error;
 587   }
 588 
 589 
 590   /*
 591    * Compute amount of font units the face should be emboldened by, in
 592    * analogy to the CFF driver's `cf2_computeDarkening' function.  See there
 593    * for details of the algorithm.
 594    *
 595    * XXX: Currently a crude adaption of the original algorithm.  Do better?
 596    */
 597   FT_LOCAL_DEF( FT_Int32 )
 598   af_loader_compute_darkening( AF_Loader  loader,
 599                                FT_Face    face,
 600                                FT_Pos     standard_width )
 601   {
 602     AF_Module  module = loader->globals->module;
 603 
 604     FT_UShort  units_per_EM;
 605     FT_Fixed   ppem, em_ratio;
 606     FT_Fixed   stem_width, stem_width_per_1000, scaled_stem, darken_amount;
 607     FT_Int     log_base_2;
 608     FT_Int     x1, y1, x2, y2, x3, y3, x4, y4;
 609 
 610 
 611     ppem         = FT_MAX( af_intToFixed( 4 ),
 612                            af_intToFixed( face->size->metrics.x_ppem ) );
 613     units_per_EM = face->units_per_EM;
 614 
 615     em_ratio = FT_DivFix( af_intToFixed( 1000 ),
 616                           af_intToFixed ( units_per_EM ) );
 617     if ( em_ratio < af_floatToFixed( .01 ) )
 618     {
 619       /* If something goes wrong, don't embolden. */
 620       return 0;
 621     }
 622 
 623     x1 = module->darken_params[0];
 624     y1 = module->darken_params[1];
 625     x2 = module->darken_params[2];
 626     y2 = module->darken_params[3];
 627     x3 = module->darken_params[4];
 628     y3 = module->darken_params[5];
 629     x4 = module->darken_params[6];
 630     y4 = module->darken_params[7];
 631 
 632     if ( standard_width <= 0 )
 633     {
 634       stem_width          = af_intToFixed( 75 ); /* taken from cf2font.c */
 635       stem_width_per_1000 = stem_width;
 636     }
 637     else
 638     {
 639       stem_width          = af_intToFixed( standard_width );
 640       stem_width_per_1000 = FT_MulFix( stem_width, em_ratio );
 641     }
 642 
 643     log_base_2 = FT_MSB( (FT_UInt32)stem_width_per_1000 ) +
 644                  FT_MSB( (FT_UInt32)ppem );
 645 
 646     if ( log_base_2 >= 46 )
 647     {
 648       /* possible overflow */
 649       scaled_stem = af_intToFixed( x4 );
 650     }
 651     else
 652       scaled_stem = FT_MulFix( stem_width_per_1000, ppem );
 653 
 654     /* now apply the darkening parameters */
 655     if ( scaled_stem < af_intToFixed( x1 ) )
 656       darken_amount = FT_DivFix( af_intToFixed( y1 ), ppem );
 657 
 658     else if ( scaled_stem < af_intToFixed( x2 ) )
 659     {
 660       FT_Int  xdelta = x2 - x1;
 661       FT_Int  ydelta = y2 - y1;
 662       FT_Int  x      = stem_width_per_1000 -
 663                        FT_DivFix( af_intToFixed( x1 ), ppem );
 664 
 665 
 666       if ( !xdelta )
 667         goto Try_x3;
 668 
 669       darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
 670                       FT_DivFix( af_intToFixed( y1 ), ppem );
 671     }
 672 
 673     else if ( scaled_stem < af_intToFixed( x3 ) )
 674     {
 675     Try_x3:
 676       {
 677         FT_Int  xdelta = x3 - x2;
 678         FT_Int  ydelta = y3 - y2;
 679         FT_Int  x      = stem_width_per_1000 -
 680                          FT_DivFix( af_intToFixed( x2 ), ppem );
 681 
 682 
 683         if ( !xdelta )
 684           goto Try_x4;
 685 
 686         darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
 687                         FT_DivFix( af_intToFixed( y2 ), ppem );
 688       }
 689     }
 690 
 691     else if ( scaled_stem < af_intToFixed( x4 ) )
 692     {
 693     Try_x4:
 694       {
 695         FT_Int  xdelta = x4 - x3;
 696         FT_Int  ydelta = y4 - y3;
 697         FT_Int  x      = stem_width_per_1000 -
 698                          FT_DivFix( af_intToFixed( x3 ), ppem );
 699 
 700 
 701         if ( !xdelta )
 702           goto Use_y4;
 703 
 704         darken_amount = FT_MulDiv( x, ydelta, xdelta ) +
 705                         FT_DivFix( af_intToFixed( y3 ), ppem );
 706       }
 707     }
 708 
 709     else
 710     {
 711     Use_y4:
 712       darken_amount = FT_DivFix( af_intToFixed( y4 ), ppem );
 713     }
 714 
 715     /* Convert darken_amount from per 1000 em to true character space. */
 716     return af_fixedToInt( FT_DivFix( darken_amount, em_ratio ) );
 717   }
 718 
 719 
 720 /* END */