tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

aflatin.c (160239B)


      1 /****************************************************************************
      2 *
      3 * aflatin.c
      4 *
      5 *   Auto-fitter hinting routines for latin writing system (body).
      6 *
      7 * Copyright (C) 2003-2025 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 <freetype/ftadvanc.h>
     20 #include <freetype/internal/ftdebug.h>
     21 
     22 #include "afglobal.h"
     23 #include "aflatin.h"
     24 #include "aferrors.h"
     25 #include "afadjust.h"
     26 
     27 
     28  /**************************************************************************
     29   *
     30   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     31   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     32   * messages during execution.
     33   */
     34 #undef  FT_COMPONENT
     35 #define FT_COMPONENT  aflatin
     36 
     37 
     38  /* needed for computation of round vs. flat segments */
     39 #define FLAT_THRESHOLD( x )  ( x / 14 )
     40 
     41 
     42  /*************************************************************************/
     43  /*************************************************************************/
     44  /*****                                                               *****/
     45  /*****            L A T I N   G L O B A L   M E T R I C S            *****/
     46  /*****                                                               *****/
     47  /*************************************************************************/
     48  /*************************************************************************/
     49 
     50 
     51  /* Find segments and links, compute all stem widths, and initialize */
     52  /* standard width and height for the glyph with given charcode.     */
     53 
     54  FT_LOCAL_DEF( void )
     55  af_latin_metrics_init_widths( AF_LatinMetrics  metrics,
     56                                FT_Face          face )
     57  {
     58    /* scan the array of segments in each direction */
     59    AF_GlyphHintsRec  hints[1];
     60 
     61 
     62    FT_TRACE5(( "\n" ));
     63    FT_TRACE5(( "latin standard widths computation (style `%s')\n",
     64                af_style_names[metrics->root.style_class->style] ));
     65    FT_TRACE5(( "=====================================================\n" ));
     66    FT_TRACE5(( "\n" ));
     67 
     68    af_glyph_hints_init( hints, face->memory );
     69 
     70    metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
     71    metrics->axis[AF_DIMENSION_VERT].width_count = 0;
     72 
     73    {
     74      FT_Error            error;
     75      FT_ULong            glyph_index;
     76      int                 dim;
     77      AF_LatinMetricsRec  dummy[1];
     78      AF_Scaler           scaler = &dummy->root.scaler;
     79 
     80      AF_StyleClass   style_class  = metrics->root.style_class;
     81      AF_ScriptClass  script_class = af_script_classes[style_class->script];
     82 
     83      /* If HarfBuzz is not available, we need a pointer to a single */
     84      /* unsigned long value.                                        */
     85      FT_ULong  shaper_buf_;
     86      void*     shaper_buf = &shaper_buf_;
     87 
     88      const char*  p;
     89 
     90 #ifdef FT_DEBUG_LEVEL_TRACE
     91      FT_ULong  ch = 0;
     92 #endif
     93 
     94 
     95      p = script_class->standard_charstring;
     96 
     97      if ( ft_hb_enabled ( metrics->root.globals ) )
     98        shaper_buf = af_shaper_buf_create( metrics->root.globals );
     99 
    100      /*
    101       * We check a list of standard characters to catch features like
    102       * `c2sc' (small caps from caps) that don't contain lowercase letters
    103       * by definition, or other features that mainly operate on numerals.
    104       * The first match wins.
    105       */
    106 
    107      glyph_index = 0;
    108      while ( *p )
    109      {
    110        unsigned int  num_idx;
    111 
    112 #ifdef FT_DEBUG_LEVEL_TRACE
    113        const char*  p_old;
    114 #endif
    115 
    116 
    117        while ( *p == ' ' )
    118          p++;
    119 
    120 #ifdef FT_DEBUG_LEVEL_TRACE
    121        p_old = p;
    122        GET_UTF8_CHAR( ch, p_old );
    123 #endif
    124 
    125        /* reject input that maps to more than a single glyph */
    126        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
    127        if ( num_idx > 1 )
    128          continue;
    129 
    130        /* otherwise exit loop if we have a result */
    131        glyph_index = af_shaper_get_elem( &metrics->root,
    132                                          shaper_buf,
    133                                          0,
    134                                          NULL,
    135                                          NULL );
    136        if ( glyph_index )
    137          break;
    138      }
    139 
    140      af_shaper_buf_destroy( metrics->root.globals, shaper_buf );
    141 
    142      if ( !glyph_index )
    143      {
    144        FT_TRACE5(( "standard character missing;"
    145                    " using fallback stem widths\n" ));
    146        goto Exit;
    147      }
    148 
    149      FT_TRACE5(( "standard character: U+%04lX (glyph index %lu)\n",
    150                  ch, glyph_index ));
    151 
    152      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
    153      if ( error || face->glyph->outline.n_points <= 0 )
    154        goto Exit;
    155 
    156      FT_ZERO( dummy );
    157 
    158      dummy->units_per_em = metrics->units_per_em;
    159 
    160      scaler->x_scale = 0x10000L;
    161      scaler->y_scale = 0x10000L;
    162      scaler->x_delta = 0;
    163      scaler->y_delta = 0;
    164 
    165      scaler->face        = face;
    166      scaler->render_mode = FT_RENDER_MODE_NORMAL;
    167      scaler->flags       = 0;
    168 
    169      af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy );
    170 
    171      error = af_glyph_hints_reload( hints, &face->glyph->outline );
    172      if ( error )
    173        goto Exit;
    174 
    175      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    176      {
    177        AF_LatinAxis  axis    = &metrics->axis[dim];
    178        AF_AxisHints  axhints = &hints->axis[dim];
    179        AF_Segment    seg, limit, link;
    180        FT_UInt       num_widths = 0;
    181 
    182 
    183        error = af_latin_hints_compute_segments( hints,
    184                                                 (AF_Dimension)dim );
    185        if ( error )
    186          goto Exit;
    187 
    188        /*
    189         * We assume that the glyphs selected for the stem width
    190         * computation are `featureless' enough so that the linking
    191         * algorithm works fine without adjustments of its scoring
    192         * function.
    193         */
    194        af_latin_hints_link_segments( hints,
    195                                      0,
    196                                      NULL,
    197                                      (AF_Dimension)dim );
    198 
    199        seg   = axhints->segments;
    200        limit = FT_OFFSET( seg, axhints->num_segments );
    201 
    202        for ( ; seg < limit; seg++ )
    203        {
    204          link = seg->link;
    205 
    206          /* we only consider stem segments there! */
    207          if ( link && link->link == seg && link > seg )
    208          {
    209            FT_Pos  dist;
    210 
    211 
    212            dist = seg->pos - link->pos;
    213            if ( dist < 0 )
    214              dist = -dist;
    215 
    216            if ( num_widths < AF_LATIN_MAX_WIDTHS )
    217              axis->widths[num_widths++].org = dist;
    218          }
    219        }
    220 
    221        /* this also replaces multiple almost identical stem widths */
    222        /* with a single one (the value 100 is heuristic)           */
    223        af_sort_and_quantize_widths( &num_widths, axis->widths,
    224                                     dummy->units_per_em / 100 );
    225        axis->width_count = num_widths;
    226      }
    227 
    228    Exit:
    229      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    230      {
    231        AF_LatinAxis  axis = &metrics->axis[dim];
    232        FT_Pos        stdw;
    233 
    234 
    235        stdw = ( axis->width_count > 0 ) ? axis->widths[0].org
    236                                         : AF_LATIN_CONSTANT( metrics, 50 );
    237 
    238        /* let's try 20% of the smallest width */
    239        axis->edge_distance_threshold = stdw / 5;
    240        axis->standard_width          = stdw;
    241        axis->extra_light             = 0;
    242 
    243 #ifdef FT_DEBUG_LEVEL_TRACE
    244        {
    245          FT_UInt  i;
    246 
    247 
    248          FT_TRACE5(( "%s widths:\n",
    249                      dim == AF_DIMENSION_VERT ? "horizontal"
    250                                               : "vertical" ));
    251 
    252          FT_TRACE5(( "  %ld (standard)", axis->standard_width ));
    253          for ( i = 1; i < axis->width_count; i++ )
    254            FT_TRACE5(( " %ld", axis->widths[i].org ));
    255 
    256          FT_TRACE5(( "\n" ));
    257        }
    258 #endif
    259      }
    260    }
    261 
    262    FT_TRACE5(( "\n" ));
    263 
    264    af_glyph_hints_done( hints );
    265  }
    266 
    267 
    268  static void
    269  af_latin_sort_blue( FT_UInt        count,
    270                      AF_LatinBlue*  table )
    271  {
    272    FT_UInt       i, j;
    273    AF_LatinBlue  swap;
    274 
    275 
    276    /* we sort from bottom to top */
    277    for ( i = 1; i < count; i++ )
    278    {
    279      for ( j = i; j > 0; j-- )
    280      {
    281        FT_Pos  a, b;
    282 
    283 
    284        if ( table[j - 1]->flags & ( AF_LATIN_BLUE_TOP     |
    285                                     AF_LATIN_BLUE_SUB_TOP ) )
    286          a = table[j - 1]->ref.org;
    287        else
    288          a = table[j - 1]->shoot.org;
    289 
    290        if ( table[j]->flags & ( AF_LATIN_BLUE_TOP     |
    291                                 AF_LATIN_BLUE_SUB_TOP ) )
    292          b = table[j]->ref.org;
    293        else
    294          b = table[j]->shoot.org;
    295 
    296        if ( b >= a )
    297          break;
    298 
    299        swap         = table[j];
    300        table[j]     = table[j - 1];
    301        table[j - 1] = swap;
    302      }
    303    }
    304  }
    305 
    306 
    307  /* Find all blue zones.  Flat segments give the reference points, */
    308  /* round segments the overshoot positions.                        */
    309 
    310  static int
    311  af_latin_metrics_init_blues( AF_LatinMetrics  metrics,
    312                               FT_Face          face )
    313  {
    314    FT_Pos        flats [AF_BLUE_STRING_MAX_LEN];
    315    FT_Pos        rounds[AF_BLUE_STRING_MAX_LEN];
    316 
    317    FT_UInt       num_flats;
    318    FT_UInt       num_rounds;
    319 
    320    AF_LatinBlue  blue;
    321    FT_Error      error;
    322    AF_LatinAxis  axis = &metrics->axis[AF_DIMENSION_VERT];
    323    FT_Outline    outline;
    324 
    325    AF_StyleClass  sc = metrics->root.style_class;
    326 
    327    AF_Blue_Stringset         bss = sc->blue_stringset;
    328    const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
    329 
    330    FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
    331 
    332    /* If HarfBuzz is not available, we need a pointer to a single */
    333    /* unsigned long value.                                        */
    334    FT_ULong  shaper_buf_;
    335    void*     shaper_buf = &shaper_buf_;
    336 
    337 
    338    /* we walk over the blue character strings as specified in the */
    339    /* style's entry in the `af_blue_stringset' array              */
    340 
    341    FT_TRACE5(( "latin blue zones computation\n" ));
    342    FT_TRACE5(( "============================\n" ));
    343    FT_TRACE5(( "\n" ));
    344 
    345    if ( ft_hb_enabled ( metrics->root.globals ) )
    346      shaper_buf = af_shaper_buf_create( metrics->root.globals );
    347 
    348    for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
    349    {
    350      const char*  p = &af_blue_strings[bs->string];
    351      FT_Pos*      blue_ref;
    352      FT_Pos*      blue_shoot;
    353      FT_Pos       ascender;
    354      FT_Pos       descender;
    355 
    356 
    357 #ifdef FT_DEBUG_LEVEL_TRACE
    358      {
    359        FT_Bool  have_flag = 0;
    360 
    361 
    362        FT_TRACE5(( "blue zone %u", axis->blue_count ));
    363 
    364        if ( bs->properties )
    365        {
    366          FT_TRACE5(( " (" ));
    367 
    368          if ( AF_LATIN_IS_TOP_BLUE( bs ) )
    369          {
    370            FT_TRACE5(( "top" ));
    371            have_flag = 1;
    372          }
    373          else if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) )
    374          {
    375            FT_TRACE5(( "sub top" ));
    376            have_flag = 1;
    377          }
    378 
    379          if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
    380          {
    381            if ( have_flag )
    382              FT_TRACE5(( ", " ));
    383            FT_TRACE5(( "neutral" ));
    384            have_flag = 1;
    385          }
    386 
    387          if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) )
    388          {
    389            if ( have_flag )
    390              FT_TRACE5(( ", " ));
    391            FT_TRACE5(( "small top" ));
    392            have_flag = 1;
    393          }
    394 
    395          if ( AF_LATIN_IS_LONG_BLUE( bs ) )
    396          {
    397            if ( have_flag )
    398              FT_TRACE5(( ", " ));
    399            FT_TRACE5(( "long" ));
    400          }
    401 
    402          if ( AF_LATIN_IS_CAPITAL_BOTTOM_BLUE( bs ) )
    403          {
    404            if ( have_flag )
    405              FT_TRACE5(( ", " ));
    406            FT_TRACE5(( "capital bottom" ));
    407          }
    408 
    409          if ( AF_LATIN_IS_SMALL_BOTTOM_BLUE( bs ) )
    410          {
    411            if ( have_flag )
    412              FT_TRACE5(( ", " ));
    413            FT_TRACE5(( "small bottom" ));
    414          }
    415 
    416          FT_TRACE5(( ")" ));
    417        }
    418 
    419        FT_TRACE5(( ":\n" ));
    420      }
    421 #endif /* FT_DEBUG_LEVEL_TRACE */
    422 
    423      num_flats  = 0;
    424      num_rounds = 0;
    425      ascender   = 0;
    426      descender  = 0;
    427 
    428      while ( *p )
    429      {
    430        FT_ULong    glyph_index;
    431        FT_Long     y_offset;
    432        FT_Int      best_point, best_contour_first, best_contour_last;
    433        FT_Vector*  points;
    434 
    435        FT_Pos   best_y_extremum;                      /* same as points.y */
    436        FT_Bool  best_round = 0;
    437 
    438        unsigned int  i, num_idx;
    439 
    440 #ifdef FT_DEBUG_LEVEL_TRACE
    441        const char*  p_old;
    442        FT_ULong     ch;
    443 #endif
    444 
    445 
    446        while ( *p == ' ' )
    447          p++;
    448 
    449 #ifdef FT_DEBUG_LEVEL_TRACE
    450        p_old = p;
    451        GET_UTF8_CHAR( ch, p_old );
    452 #endif
    453 
    454        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
    455 
    456        if ( !num_idx )
    457        {
    458          FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
    459          continue;
    460        }
    461 
    462        if ( AF_LATIN_IS_TOP_BLUE( bs ) )
    463          best_y_extremum = FT_LONG_MIN;
    464        else
    465          best_y_extremum = FT_LONG_MAX;
    466 
    467        /* iterate over all glyph elements of the character cluster */
    468        /* and get the data of the `biggest' one                    */
    469        for ( i = 0; i < num_idx; i++ )
    470        {
    471          FT_Pos   best_y;
    472          FT_Bool  round = 0;
    473 
    474 
    475          /* load the character in the face -- skip unknown or empty ones */
    476          glyph_index = af_shaper_get_elem( &metrics->root,
    477                                            shaper_buf,
    478                                            i,
    479                                            NULL,
    480                                            &y_offset );
    481          if ( glyph_index == 0 )
    482          {
    483            FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
    484            continue;
    485          }
    486 
    487          error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
    488          outline = face->glyph->outline;
    489          /* reject glyphs that don't produce any rendering */
    490          if ( error || outline.n_points <= 2 )
    491          {
    492 #ifdef FT_DEBUG_LEVEL_TRACE
    493            if ( num_idx == 1 )
    494              FT_TRACE5(( "  U+%04lX contains no (usable) outlines\n", ch ));
    495            else
    496              FT_TRACE5(( "  component %u of cluster starting with U+%04lX"
    497                          " contains no (usable) outlines\n", i, ch ));
    498 #endif
    499            continue;
    500          }
    501 
    502          /* now compute min or max point indices and coordinates */
    503          points             = outline.points;
    504          best_point         = -1;
    505          best_contour_first = -1;
    506          best_contour_last  = -1;
    507          best_y             = 0;  /* make compiler happy */
    508 
    509          {
    510            FT_Int  nn;
    511            FT_Int  pp, first, last;
    512 
    513 
    514            last = -1;
    515            for ( nn = 0; nn < outline.n_contours; nn++ )
    516            {
    517              first = last + 1;
    518              last  = outline.contours[nn];
    519 
    520              /* Avoid single-point contours since they are never      */
    521              /* rasterized.  In some fonts, they correspond to mark   */
    522              /* attachment points that are way outside of the glyph's */
    523              /* real outline.                                         */
    524              if ( last <= first )
    525                continue;
    526 
    527              if ( AF_LATIN_IS_TOP_BLUE( bs )     ||
    528                   AF_LATIN_IS_SUB_TOP_BLUE( bs ) )
    529              {
    530                for ( pp = first; pp <= last; pp++ )
    531                {
    532                  if ( best_point < 0 || points[pp].y > best_y )
    533                  {
    534                    best_point = pp;
    535                    best_y     = points[pp].y;
    536                    ascender   = FT_MAX( ascender, best_y + y_offset );
    537                  }
    538                  else
    539                    descender = FT_MIN( descender, points[pp].y + y_offset );
    540                }
    541              }
    542              else
    543              {
    544                for ( pp = first; pp <= last; pp++ )
    545                {
    546                  if ( best_point < 0 || points[pp].y < best_y )
    547                  {
    548                    best_point = pp;
    549                    best_y     = points[pp].y;
    550                    descender  = FT_MIN( descender, best_y + y_offset );
    551                  }
    552                  else
    553                    ascender = FT_MAX( ascender, points[pp].y + y_offset );
    554                }
    555              }
    556 
    557              if ( best_point > best_contour_last )
    558              {
    559                best_contour_first = first;
    560                best_contour_last  = last;
    561              }
    562            }
    563          }
    564 
    565          /* now check whether the point belongs to a straight or round   */
    566          /* segment; we first need to find in which contour the extremum */
    567          /* lies, then inspect its previous and next points              */
    568          if ( best_point >= 0 )
    569          {
    570            FT_Pos  best_x = points[best_point].x;
    571            FT_Int  prev, next;
    572            FT_Int  best_segment_first, best_segment_last;
    573            FT_Int  best_on_point_first, best_on_point_last;
    574            FT_Pos  dist;
    575 
    576 
    577            best_segment_first = best_point;
    578            best_segment_last  = best_point;
    579 
    580            if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON )
    581            {
    582              best_on_point_first = best_point;
    583              best_on_point_last  = best_point;
    584            }
    585            else
    586            {
    587              best_on_point_first = -1;
    588              best_on_point_last  = -1;
    589            }
    590 
    591            /* look for the previous and next points on the contour  */
    592            /* that are not on the same Y coordinate, then threshold */
    593            /* the `closeness'...                                    */
    594            prev = best_point;
    595            next = prev;
    596 
    597            do
    598            {
    599              if ( prev > best_contour_first )
    600                prev--;
    601              else
    602                prev = best_contour_last;
    603 
    604              dist = FT_ABS( points[prev].y - best_y );
    605              /* accept a small distance or a small angle (both values are */
    606              /* heuristic; value 20 corresponds to approx. 2.9 degrees)   */
    607              if ( dist > 5 )
    608                if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist )
    609                  break;
    610 
    611              best_segment_first = prev;
    612 
    613              if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON )
    614              {
    615                best_on_point_first = prev;
    616                if ( best_on_point_last < 0 )
    617                  best_on_point_last = prev;
    618              }
    619 
    620            } while ( prev != best_point );
    621 
    622            do
    623            {
    624              if ( next < best_contour_last )
    625                next++;
    626              else
    627                next = best_contour_first;
    628 
    629              dist = FT_ABS( points[next].y - best_y );
    630              if ( dist > 5 )
    631                if ( FT_ABS( points[next].x - best_x ) <= 20 * dist )
    632                  break;
    633 
    634              best_segment_last = next;
    635 
    636              if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON )
    637              {
    638                best_on_point_last = next;
    639                if ( best_on_point_first < 0 )
    640                  best_on_point_first = next;
    641              }
    642 
    643            } while ( next != best_point );
    644 
    645            if ( AF_LATIN_IS_LONG_BLUE( bs ) )
    646            {
    647              /* If this flag is set, we have an additional constraint to  */
    648              /* get the blue zone distance: Find a segment of the topmost */
    649              /* (or bottommost) contour that is longer than a heuristic   */
    650              /* threshold.  This ensures that small bumps in the outline  */
    651              /* are ignored (for example, the `vertical serifs' found in  */
    652              /* many Hebrew glyph designs).                               */
    653 
    654              /* If this segment is long enough, we are done.  Otherwise,  */
    655              /* search the segment next to the extremum that is long      */
    656              /* enough, has the same direction, and a not too large       */
    657              /* vertical distance from the extremum.  Note that the       */
    658              /* algorithm doesn't check whether the found segment is      */
    659              /* actually the one (vertically) nearest to the extremum.    */
    660 
    661              /* heuristic threshold value */
    662              FT_Pos  length_threshold = metrics->units_per_em / 25;
    663 
    664 
    665              dist = FT_ABS( points[best_segment_last].x -
    666                               points[best_segment_first].x );
    667 
    668              if ( dist < length_threshold                       &&
    669                   best_segment_last - best_segment_first + 2 <=
    670                     best_contour_last - best_contour_first      )
    671              {
    672                /* heuristic threshold value */
    673                FT_Pos  height_threshold = metrics->units_per_em / 4;
    674 
    675                FT_Int   first;
    676                FT_Int   last;
    677                FT_Bool  hit;
    678 
    679                /* we intentionally declare these two variables        */
    680                /* outside of the loop since various compilers emit    */
    681                /* incorrect warning messages otherwise, talking about */
    682                /* `possibly uninitialized variables'                  */
    683                FT_Int  p_first = 0;            /* make compiler happy */
    684                FT_Int  p_last  = 0;
    685 
    686                FT_Bool  left2right;
    687 
    688 
    689                /* compute direction */
    690                prev = best_point;
    691 
    692                do
    693                {
    694                  if ( prev > best_contour_first )
    695                    prev--;
    696                  else
    697                    prev = best_contour_last;
    698 
    699                  if ( points[prev].x != best_x )
    700                    break;
    701 
    702                } while ( prev != best_point );
    703 
    704                /* skip glyph for the degenerate case */
    705                if ( prev == best_point )
    706                  continue;
    707 
    708                left2right = FT_BOOL( points[prev].x < points[best_point].x );
    709 
    710                first = best_segment_last;
    711                last  = first;
    712                hit   = 0;
    713 
    714                do
    715                {
    716                  FT_Bool  l2r;
    717                  FT_Pos   d;
    718 
    719 
    720                  if ( !hit )
    721                  {
    722                    /* no hit; adjust first point */
    723                    first = last;
    724 
    725                    /* also adjust first and last on point */
    726                    if ( FT_CURVE_TAG( outline.tags[first] ) ==
    727                           FT_CURVE_TAG_ON )
    728                    {
    729                      p_first = first;
    730                      p_last  = first;
    731                    }
    732                    else
    733                    {
    734                      p_first = -1;
    735                      p_last  = -1;
    736                    }
    737 
    738                    hit = 1;
    739                  }
    740 
    741                  if ( last < best_contour_last )
    742                    last++;
    743                  else
    744                    last = best_contour_first;
    745 
    746                  if ( FT_ABS( best_y - points[first].y ) > height_threshold )
    747                  {
    748                    /* vertical distance too large */
    749                    hit = 0;
    750                    continue;
    751                  }
    752 
    753                  /* same test as above */
    754                  dist = FT_ABS( points[last].y - points[first].y );
    755                  if ( dist > 5 )
    756                    if ( FT_ABS( points[last].x - points[first].x ) <=
    757                           20 * dist )
    758                    {
    759                      hit = 0;
    760                      continue;
    761                    }
    762 
    763                  if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON )
    764                  {
    765                    p_last = last;
    766                    if ( p_first < 0 )
    767                      p_first = last;
    768                  }
    769 
    770                  l2r = FT_BOOL( points[first].x < points[last].x );
    771                  d   = FT_ABS( points[last].x - points[first].x );
    772 
    773                  if ( l2r == left2right     &&
    774                       d >= length_threshold )
    775                  {
    776                    /* all constraints are met; update segment after */
    777                    /* finding its end                               */
    778                    do
    779                    {
    780                      if ( last < best_contour_last )
    781                        last++;
    782                      else
    783                        last = best_contour_first;
    784 
    785                      d = FT_ABS( points[last].y - points[first].y );
    786                      if ( d > 5 )
    787                        if ( FT_ABS( points[next].x - points[first].x ) <=
    788                               20 * dist )
    789                        {
    790                          if ( last > best_contour_first )
    791                            last--;
    792                          else
    793                            last = best_contour_last;
    794                          break;
    795                        }
    796 
    797                      p_last = last;
    798 
    799                      if ( FT_CURVE_TAG( outline.tags[last] ) ==
    800                             FT_CURVE_TAG_ON )
    801                      {
    802                        p_last = last;
    803                        if ( p_first < 0 )
    804                          p_first = last;
    805                      }
    806 
    807                    } while ( last != best_segment_first );
    808 
    809                    best_y = points[first].y;
    810 
    811                    best_segment_first = first;
    812                    best_segment_last  = last;
    813 
    814                    best_on_point_first = p_first;
    815                    best_on_point_last  = p_last;
    816 
    817                    break;
    818                  }
    819 
    820                } while ( last != best_segment_first );
    821              }
    822            }
    823 
    824            /* for computing blue zones, we add the y offset as returned */
    825            /* by the currently used OpenType feature -- for example,    */
    826            /* superscript glyphs might be identical to subscript glyphs */
    827            /* with a vertical shift                                     */
    828            best_y += y_offset;
    829 
    830 #ifdef FT_DEBUG_LEVEL_TRACE
    831            if ( num_idx == 1 )
    832              FT_TRACE5(( "  U+%04lX: best_y = %5ld", ch, best_y ));
    833            else
    834              FT_TRACE5(( "  component %u of cluster starting with U+%04lX:"
    835                          " best_y = %5ld", i, ch, best_y ));
    836 #endif
    837 
    838            /* now set the `round' flag depending on the segment's kind: */
    839            /*                                                           */
    840            /* - if the horizontal distance between the first and last   */
    841            /*   `on' point is larger than a heuristic threshold         */
    842            /*   we have a flat segment                                  */
    843            /* - if either the first or the last point of the segment is */
    844            /*   an `off' point, the segment is round, otherwise it is   */
    845            /*   flat                                                    */
    846            if ( best_on_point_first >= 0                               &&
    847                 best_on_point_last >= 0                                &&
    848                 ( FT_ABS( points[best_on_point_last].x -
    849                           points[best_on_point_first].x ) ) >
    850                   flat_threshold                                       )
    851              round = 0;
    852            else
    853              round = FT_BOOL(
    854                        FT_CURVE_TAG( outline.tags[best_segment_first] ) !=
    855                          FT_CURVE_TAG_ON                                   ||
    856                        FT_CURVE_TAG( outline.tags[best_segment_last]  ) !=
    857                          FT_CURVE_TAG_ON                                   );
    858 
    859            if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
    860            {
    861              /* only use flat segments for a neutral blue zone */
    862              FT_TRACE5(( " (round, skipped)\n" ));
    863              continue;
    864            }
    865 
    866            FT_TRACE5(( " (%s)\n", round ? "round" : "flat" ));
    867          }
    868 
    869          if ( AF_LATIN_IS_TOP_BLUE( bs ) )
    870          {
    871            if ( best_y > best_y_extremum )
    872            {
    873              best_y_extremum = best_y;
    874              best_round      = round;
    875            }
    876          }
    877          else
    878          {
    879            if ( best_y < best_y_extremum )
    880            {
    881              best_y_extremum = best_y;
    882              best_round      = round;
    883            }
    884          }
    885 
    886        } /* end for loop */
    887 
    888        if ( !( best_y_extremum == FT_LONG_MIN ||
    889                best_y_extremum == FT_LONG_MAX ) )
    890        {
    891          if ( best_round )
    892            rounds[num_rounds++] = best_y_extremum;
    893          else
    894            flats[num_flats++]   = best_y_extremum;
    895        }
    896 
    897      } /* end while loop */
    898 
    899      if ( num_flats == 0 && num_rounds == 0 )
    900      {
    901        /*
    902         * we couldn't find a single glyph to compute this blue zone,
    903         * we will simply ignore it then
    904         */
    905        FT_TRACE5(( "  empty\n" ));
    906        continue;
    907      }
    908 
    909      /* we have computed the contents of the `rounds' and `flats' tables, */
    910      /* now determine the reference and overshoot position of the blue -- */
    911      /* we simply take the median value after a simple sort               */
    912      af_sort_pos( num_rounds, rounds );
    913      af_sort_pos( num_flats,  flats );
    914 
    915      blue       = &axis->blues[axis->blue_count];
    916      blue_ref   = &blue->ref.org;
    917      blue_shoot = &blue->shoot.org;
    918 
    919      axis->blue_count++;
    920 
    921      if ( num_flats == 0 )
    922      {
    923        *blue_ref   =
    924        *blue_shoot = rounds[num_rounds / 2];
    925      }
    926      else if ( num_rounds == 0 )
    927      {
    928        *blue_ref   =
    929        *blue_shoot = flats[num_flats / 2];
    930      }
    931      else
    932      {
    933        *blue_ref   = flats [num_flats  / 2];
    934        *blue_shoot = rounds[num_rounds / 2];
    935      }
    936 
    937      /* there are sometimes problems: if the overshoot position of top     */
    938      /* zones is under its reference position, or the opposite for bottom  */
    939      /* zones.  We must thus check everything there and correct the errors */
    940      if ( *blue_shoot != *blue_ref )
    941      {
    942        FT_Pos   ref      = *blue_ref;
    943        FT_Pos   shoot    = *blue_shoot;
    944        FT_Bool  over_ref = FT_BOOL( shoot > ref );
    945 
    946 
    947        if ( ( AF_LATIN_IS_TOP_BLUE( bs )    ||
    948               AF_LATIN_IS_SUB_TOP_BLUE( bs) ) ^ over_ref )
    949        {
    950          *blue_ref   =
    951          *blue_shoot = ( shoot + ref ) / 2;
    952 
    953          FT_TRACE5(( "  [overshoot smaller than reference,"
    954                      " taking mean value]\n" ));
    955        }
    956      }
    957 
    958      blue->ascender  = ascender;
    959      blue->descender = descender;
    960 
    961      blue->flags = 0;
    962      if ( AF_LATIN_IS_TOP_BLUE( bs ) )
    963        blue->flags |= AF_LATIN_BLUE_TOP;
    964      if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) )
    965        blue->flags |= AF_LATIN_BLUE_SUB_TOP;
    966      if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
    967        blue->flags |= AF_LATIN_BLUE_NEUTRAL;
    968      if ( AF_LATIN_IS_CAPITAL_BOTTOM_BLUE( bs ) )
    969        blue->flags |= AF_LATIN_BLUE_BOTTOM;
    970      if ( AF_LATIN_IS_SMALL_BOTTOM_BLUE( bs ) )
    971        blue->flags |= AF_LATIN_BLUE_BOTTOM_SMALL;
    972 
    973      /*
    974       * The following flag is used later to adjust the y and x scales
    975       * in order to optimize the pixel grid alignment of the top of small
    976       * letters.
    977       */
    978      if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) )
    979        blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
    980 
    981      FT_TRACE5(( "    -> reference = %ld\n", *blue_ref ));
    982      FT_TRACE5(( "       overshoot = %ld\n", *blue_shoot ));
    983 
    984    } /* end for loop */
    985 
    986    af_shaper_buf_destroy( metrics->root.globals, shaper_buf );
    987 
    988    if ( axis->blue_count )
    989    {
    990      /* we finally check whether blue zones are ordered;            */
    991      /* `ref' and `shoot' values of two blue zones must not overlap */
    992 
    993      FT_UInt       i;
    994      AF_LatinBlue  blue_sorted[AF_BLUE_STRINGSET_MAX_LEN];
    995 
    996 
    997      for ( i = 0; i < axis->blue_count; i++ )
    998        blue_sorted[i] = &axis->blues[i];
    999 
   1000      /* sort bottoms of blue zones... */
   1001      af_latin_sort_blue( axis->blue_count, blue_sorted );
   1002 
   1003      /* ...and adjust top values if necessary */
   1004      for ( i = 0; i < axis->blue_count - 1; i++ )
   1005      {
   1006        FT_Pos*  a;
   1007        FT_Pos*  b;
   1008 
   1009 #ifdef FT_DEBUG_LEVEL_TRACE
   1010        FT_Bool  a_is_top = 0;
   1011 #endif
   1012 
   1013 
   1014        if ( blue_sorted[i]->flags & ( AF_LATIN_BLUE_TOP     |
   1015                                       AF_LATIN_BLUE_SUB_TOP ) )
   1016        {
   1017          a = &blue_sorted[i]->shoot.org;
   1018 #ifdef FT_DEBUG_LEVEL_TRACE
   1019          a_is_top = 1;
   1020 #endif
   1021        }
   1022        else
   1023          a = &blue_sorted[i]->ref.org;
   1024 
   1025        if ( blue_sorted[i + 1]->flags & ( AF_LATIN_BLUE_TOP     |
   1026                                           AF_LATIN_BLUE_SUB_TOP ) )
   1027          b = &blue_sorted[i + 1]->shoot.org;
   1028        else
   1029          b = &blue_sorted[i + 1]->ref.org;
   1030 
   1031        if ( *a > *b )
   1032        {
   1033          *a = *b;
   1034          FT_TRACE5(( "blue zone overlap:"
   1035                      " adjusting %s %td to %ld\n",
   1036                      a_is_top ? "overshoot" : "reference",
   1037                      blue_sorted[i] - axis->blues,
   1038                      *a ));
   1039        }
   1040      }
   1041 
   1042      FT_TRACE5(( "\n" ));
   1043 
   1044      return 0;
   1045    }
   1046    else
   1047    {
   1048      /* disable hinting for the current style if there are no blue zones */
   1049 
   1050      AF_FaceGlobals  globals = metrics->root.globals;
   1051      FT_UShort*      gstyles = globals->glyph_styles;
   1052 
   1053      FT_UInt  i;
   1054 
   1055 
   1056      FT_TRACE5(( "no blue zones found:"
   1057                  " hinting disabled for this style\n" ));
   1058 
   1059      for ( i = 0; i < globals->glyph_count; i++ )
   1060      {
   1061        if ( ( gstyles[i] & AF_STYLE_MASK ) == sc->style )
   1062          gstyles[i] = AF_STYLE_NONE_DFLT;
   1063      }
   1064 
   1065      FT_TRACE5(( "\n" ));
   1066 
   1067      return 1;
   1068    }
   1069  }
   1070 
   1071 
   1072  /* Check whether all ASCII digits have the same advance width. */
   1073 
   1074  FT_LOCAL_DEF( void )
   1075  af_latin_metrics_check_digits( AF_LatinMetrics  metrics,
   1076                                 FT_Face          face )
   1077  {
   1078    FT_Bool  started = 0, same_width = 1;
   1079    FT_Long  advance = 0, old_advance = 0;
   1080 
   1081    /* If HarfBuzz is not available, we need a pointer to a single */
   1082    /* unsigned long value.                                        */
   1083    FT_ULong  shaper_buf_;
   1084    void*     shaper_buf = &shaper_buf_;
   1085 
   1086    /* in all supported charmaps, digits have character codes 0x30-0x39 */
   1087    const char   digits[] = "0 1 2 3 4 5 6 7 8 9";
   1088    const char*  p;
   1089 
   1090    FT_UNUSED( face );
   1091 
   1092 
   1093    p = digits;
   1094 
   1095    if ( ft_hb_enabled ( metrics->root.globals ) )
   1096      shaper_buf = af_shaper_buf_create( metrics->root.globals );
   1097 
   1098    while ( *p )
   1099    {
   1100      FT_ULong      glyph_index;
   1101      unsigned int  num_idx;
   1102 
   1103 
   1104      /* reject input that maps to more than a single glyph */
   1105      p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
   1106      if ( num_idx > 1 )
   1107        continue;
   1108 
   1109      glyph_index = af_shaper_get_elem( &metrics->root,
   1110                                        shaper_buf,
   1111                                        0,
   1112                                        &advance,
   1113                                        NULL );
   1114      if ( !glyph_index )
   1115        continue;
   1116 
   1117      if ( started )
   1118      {
   1119        if ( advance != old_advance )
   1120        {
   1121          same_width = 0;
   1122          break;
   1123        }
   1124      }
   1125      else
   1126      {
   1127        old_advance = advance;
   1128        started     = 1;
   1129      }
   1130    }
   1131 
   1132    af_shaper_buf_destroy( metrics->root.globals, shaper_buf );
   1133 
   1134    metrics->root.digits_have_same_width = same_width;
   1135  }
   1136 
   1137 
   1138  /* Initialize global metrics. */
   1139 
   1140  FT_LOCAL_DEF( FT_Error )
   1141  af_latin_metrics_init( AF_StyleMetrics  metrics_,   /* AF_LatinMetrics */
   1142                         FT_Face          face )
   1143  {
   1144    AF_LatinMetrics  metrics = (AF_LatinMetrics)metrics_;
   1145 
   1146    FT_Error  error = FT_Err_Ok;
   1147 
   1148    FT_CharMap  oldmap = face->charmap;
   1149 
   1150 
   1151    metrics->units_per_em = face->units_per_EM;
   1152 
   1153    if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
   1154    {
   1155      af_latin_metrics_init_widths( metrics, face );
   1156      if ( af_latin_metrics_init_blues( metrics, face ) )
   1157      {
   1158        /* use internal error code to indicate missing blue zones */
   1159        error = -1;
   1160        goto Exit;
   1161      }
   1162      af_latin_metrics_check_digits( metrics, face );
   1163    }
   1164 
   1165    af_reverse_character_map_new( &metrics->root.reverse_charmap,
   1166                                  &metrics->root );
   1167 
   1168  Exit:
   1169    face->charmap = oldmap;
   1170    return error;
   1171  }
   1172 
   1173 
   1174  /* Adjust scaling value, then scale and shift widths   */
   1175  /* and blue zones (if applicable) for given dimension. */
   1176 
   1177  static void
   1178  af_latin_metrics_scale_dim( AF_LatinMetrics  metrics,
   1179                              AF_Scaler        scaler,
   1180                              AF_Dimension     dim )
   1181  {
   1182    FT_Fixed      scale;
   1183    FT_Pos        delta;
   1184    AF_LatinAxis  axis;
   1185    FT_UInt       nn;
   1186 
   1187 
   1188    if ( dim == AF_DIMENSION_HORZ )
   1189    {
   1190      scale = scaler->x_scale;
   1191      delta = scaler->x_delta;
   1192    }
   1193    else
   1194    {
   1195      scale = scaler->y_scale;
   1196      delta = scaler->y_delta;
   1197    }
   1198 
   1199    axis = &metrics->axis[dim];
   1200 
   1201    if ( axis->org_scale == scale && axis->org_delta == delta )
   1202      return;
   1203 
   1204    axis->org_scale = scale;
   1205    axis->org_delta = delta;
   1206 
   1207    /*
   1208     * correct X and Y scale to optimize the alignment of the top of small
   1209     * letters to the pixel grid
   1210     */
   1211    {
   1212      AF_LatinAxis  Axis = &metrics->axis[AF_DIMENSION_VERT];
   1213      AF_LatinBlue  blue = NULL;
   1214 
   1215 
   1216      for ( nn = 0; nn < Axis->blue_count; nn++ )
   1217      {
   1218        if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
   1219        {
   1220          blue = &Axis->blues[nn];
   1221          break;
   1222        }
   1223      }
   1224 
   1225      if ( blue )
   1226      {
   1227        FT_Pos   scaled;
   1228        FT_Pos   threshold;
   1229        FT_Pos   fitted;
   1230        FT_UInt  limit;
   1231        FT_UInt  ppem;
   1232 
   1233 
   1234        scaled    = FT_MulFix( blue->shoot.org, scale );
   1235        ppem      = metrics->root.scaler.face->size->metrics.x_ppem;
   1236        limit     = metrics->root.globals->increase_x_height;
   1237        threshold = 40;
   1238 
   1239        /* if the `increase-x-height' property is active, */
   1240        /* we round up much more often                    */
   1241        if ( limit                                 &&
   1242             ppem <= limit                         &&
   1243             ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN )
   1244          threshold = 52;
   1245 
   1246        fitted = ( scaled + threshold ) & ~63;
   1247 
   1248        if ( scaled != fitted )
   1249        {
   1250 #if 0
   1251          if ( dim == AF_DIMENSION_HORZ )
   1252          {
   1253            if ( fitted < scaled )
   1254              scale -= scale / 50;  /* scale *= 0.98 */
   1255          }
   1256          else
   1257 #endif
   1258          if ( dim == AF_DIMENSION_VERT )
   1259          {
   1260            FT_Pos    max_height;
   1261            FT_Pos    dist;
   1262            FT_Fixed  new_scale;
   1263 
   1264 
   1265            new_scale = FT_MulDiv( scale, fitted, scaled );
   1266 
   1267            /* the scaling should not change the result by more than two pixels */
   1268            max_height = metrics->units_per_em;
   1269 
   1270            for ( nn = 0; nn < Axis->blue_count; nn++ )
   1271            {
   1272              max_height = FT_MAX( max_height, Axis->blues[nn].ascender );
   1273              max_height = FT_MAX( max_height, -Axis->blues[nn].descender );
   1274            }
   1275 
   1276            dist = FT_MulFix( max_height, new_scale - scale );
   1277 
   1278            if ( -128 < dist && dist < 128 )
   1279            {
   1280              FT_TRACE5(( "af_latin_metrics_scale_dim:"
   1281                          " x height alignment (style `%s'):\n",
   1282                          af_style_names[metrics->root.style_class->style] ));
   1283              FT_TRACE5(( "                           "
   1284                          " vertical scaling changed"
   1285                          " from %.5f to %.5f (by %ld%%)\n",
   1286                          (double)scale / 65536,
   1287                          (double)new_scale / 65536,
   1288                          ( fitted - scaled ) * 100 / scaled ));
   1289              FT_TRACE5(( "\n" ));
   1290 
   1291              scale = new_scale;
   1292            }
   1293 #ifdef FT_DEBUG_LEVEL_TRACE
   1294            else
   1295            {
   1296              FT_TRACE5(( "af_latin_metrics_scale_dim:"
   1297                          " x height alignment (style `%s'):\n",
   1298                          af_style_names[metrics->root.style_class->style] ));
   1299              FT_TRACE5(( "                           "
   1300                          " excessive vertical scaling abandoned\n" ));
   1301              FT_TRACE5(( "\n" ));
   1302            }
   1303 #endif
   1304          }
   1305        }
   1306      }
   1307    }
   1308 
   1309    axis->scale = scale;
   1310    axis->delta = delta;
   1311 
   1312    if ( dim == AF_DIMENSION_HORZ )
   1313    {
   1314      metrics->root.scaler.x_scale = scale;
   1315      metrics->root.scaler.x_delta = delta;
   1316    }
   1317    else
   1318    {
   1319      metrics->root.scaler.y_scale = scale;
   1320      metrics->root.scaler.y_delta = delta;
   1321    }
   1322 
   1323    FT_TRACE5(( "%s widths (style `%s')\n",
   1324                dim == AF_DIMENSION_HORZ ? "horizontal" : "vertical",
   1325                af_style_names[metrics->root.style_class->style] ));
   1326 
   1327    /* scale the widths */
   1328    for ( nn = 0; nn < axis->width_count; nn++ )
   1329    {
   1330      AF_Width  width = axis->widths + nn;
   1331 
   1332 
   1333      width->cur = FT_MulFix( width->org, scale );
   1334      width->fit = width->cur;
   1335 
   1336      FT_TRACE5(( "  %ld scaled to %.2f\n",
   1337                  width->org,
   1338                  (double)width->cur / 64 ));
   1339    }
   1340 
   1341    FT_TRACE5(( "\n" ));
   1342 
   1343    /* an extra-light axis corresponds to a standard width that is */
   1344    /* smaller than 5/8 pixels                                     */
   1345    axis->extra_light =
   1346      FT_BOOL( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
   1347 
   1348 #ifdef FT_DEBUG_LEVEL_TRACE
   1349    if ( axis->extra_light )
   1350    {
   1351      FT_TRACE5(( "`%s' style is extra light (at current resolution)\n",
   1352                  af_style_names[metrics->root.style_class->style] ));
   1353      FT_TRACE5(( "\n" ));
   1354    }
   1355 #endif
   1356 
   1357    if ( dim == AF_DIMENSION_VERT )
   1358    {
   1359 #ifdef FT_DEBUG_LEVEL_TRACE
   1360      if ( axis->blue_count )
   1361        FT_TRACE5(( "blue zones (style `%s')\n",
   1362                    af_style_names[metrics->root.style_class->style] ));
   1363 #endif
   1364 
   1365      /* scale the blue zones */
   1366      for ( nn = 0; nn < axis->blue_count; nn++ )
   1367      {
   1368        AF_LatinBlue  blue = &axis->blues[nn];
   1369        FT_Pos        dist;
   1370 
   1371 
   1372        blue->ref.cur   = FT_MulFix( blue->ref.org, scale ) + delta;
   1373        blue->ref.fit   = blue->ref.cur;
   1374        blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
   1375        blue->shoot.fit = blue->shoot.cur;
   1376        blue->flags    &= ~AF_LATIN_BLUE_ACTIVE;
   1377 
   1378        /* a blue zone is only active if it is less than 3/4 pixels tall */
   1379        dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
   1380        if ( dist <= 48 && dist >= -48 )
   1381        {
   1382 #if 0
   1383          FT_Pos  delta1;
   1384 #endif
   1385          FT_Pos  delta2;
   1386 
   1387 
   1388          /* use discrete values for blue zone widths */
   1389 
   1390 #if 0
   1391 
   1392          /* generic, original code */
   1393          delta1 = blue->shoot.org - blue->ref.org;
   1394          delta2 = delta1;
   1395          if ( delta1 < 0 )
   1396            delta2 = -delta2;
   1397 
   1398          delta2 = FT_MulFix( delta2, scale );
   1399 
   1400          if ( delta2 < 32 )
   1401            delta2 = 0;
   1402          else if ( delta2 < 64 )
   1403            delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
   1404          else
   1405            delta2 = FT_PIX_ROUND( delta2 );
   1406 
   1407          if ( delta1 < 0 )
   1408            delta2 = -delta2;
   1409 
   1410          blue->ref.fit   = FT_PIX_ROUND( blue->ref.cur );
   1411          blue->shoot.fit = blue->ref.fit + delta2;
   1412 
   1413 #else
   1414 
   1415          /* simplified version due to abs(dist) <= 48 */
   1416          delta2 = dist;
   1417          if ( dist < 0 )
   1418            delta2 = -delta2;
   1419 
   1420          if ( delta2 < 32 )
   1421            delta2 = 0;
   1422          else if ( delta2 < 48 )
   1423            delta2 = 32;
   1424          else
   1425            delta2 = 64;
   1426 
   1427          if ( dist < 0 )
   1428            delta2 = -delta2;
   1429 
   1430          blue->ref.fit   = FT_PIX_ROUND( blue->ref.cur );
   1431          blue->shoot.fit = blue->ref.fit - delta2;
   1432 
   1433 #endif
   1434 
   1435          blue->flags |= AF_LATIN_BLUE_ACTIVE;
   1436        }
   1437      }
   1438 
   1439      /* use sub-top blue zone only if it doesn't overlap with */
   1440      /* another (non-sup-top) blue zone; otherwise, the       */
   1441      /* effect would be similar to a neutral blue zone, which */
   1442      /* is not desired here                                   */
   1443      for ( nn = 0; nn < axis->blue_count; nn++ )
   1444      {
   1445        AF_LatinBlue  blue = &axis->blues[nn];
   1446        FT_UInt       i;
   1447 
   1448 
   1449        if ( !( blue->flags & AF_LATIN_BLUE_SUB_TOP ) )
   1450          continue;
   1451        if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
   1452          continue;
   1453 
   1454        for ( i = 0; i < axis->blue_count; i++ )
   1455        {
   1456          AF_LatinBlue  b = &axis->blues[i];
   1457 
   1458 
   1459          if ( b->flags & AF_LATIN_BLUE_SUB_TOP )
   1460            continue;
   1461          if ( !( b->flags & AF_LATIN_BLUE_ACTIVE ) )
   1462            continue;
   1463 
   1464          if ( b->ref.fit <= blue->shoot.fit &&
   1465               b->shoot.fit >= blue->ref.fit )
   1466          {
   1467            blue->flags &= ~AF_LATIN_BLUE_ACTIVE;
   1468            break;
   1469          }
   1470        }
   1471      }
   1472 
   1473 #ifdef FT_DEBUG_LEVEL_TRACE
   1474      for ( nn = 0; nn < axis->blue_count; nn++ )
   1475      {
   1476        AF_LatinBlue  blue = &axis->blues[nn];
   1477 
   1478 
   1479        FT_TRACE5(( "  reference %u: %ld scaled to %.2f%s\n",
   1480                    nn,
   1481                    blue->ref.org,
   1482                    (double)blue->ref.fit / 64,
   1483                    ( blue->flags & AF_LATIN_BLUE_ACTIVE ) ? ""
   1484                                                           : " (inactive)" ));
   1485        FT_TRACE5(( "  overshoot %u: %ld scaled to %.2f%s\n",
   1486                    nn,
   1487                    blue->shoot.org,
   1488                    (double)blue->shoot.fit / 64,
   1489                    ( blue->flags & AF_LATIN_BLUE_ACTIVE ) ? ""
   1490                                                           : " (inactive)" ));
   1491      }
   1492 #endif
   1493    }
   1494  }
   1495 
   1496 
   1497  FT_CALLBACK_DEF( void )
   1498  af_latin_metrics_done( AF_StyleMetrics  metrics_ )
   1499  {
   1500    AF_LatinMetrics  metrics = (AF_LatinMetrics)metrics_;
   1501 
   1502 
   1503    af_reverse_character_map_done( metrics->root.reverse_charmap,
   1504                                   metrics->root.globals->face->memory );
   1505  }
   1506 
   1507 
   1508  /* Scale global values in both directions. */
   1509 
   1510  FT_LOCAL_DEF( void )
   1511  af_latin_metrics_scale( AF_StyleMetrics  metrics_,   /* AF_LatinMetrics */
   1512                          AF_Scaler        scaler )
   1513  {
   1514    AF_LatinMetrics  metrics = (AF_LatinMetrics)metrics_;
   1515 
   1516 
   1517    metrics->root.scaler.render_mode = scaler->render_mode;
   1518    metrics->root.scaler.face        = scaler->face;
   1519    metrics->root.scaler.flags       = scaler->flags;
   1520 
   1521    af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
   1522    af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
   1523  }
   1524 
   1525 
   1526  /* Extract standard_width from writing system/script specific */
   1527  /* metrics class.                                             */
   1528 
   1529  FT_CALLBACK_DEF( void )
   1530  af_latin_get_standard_widths( AF_StyleMetrics  metrics_, /* AF_LatinMetrics */
   1531                                FT_Pos*          stdHW,
   1532                                FT_Pos*          stdVW )
   1533  {
   1534    AF_LatinMetrics  metrics = (AF_LatinMetrics)metrics_;
   1535 
   1536 
   1537    if ( stdHW )
   1538      *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
   1539 
   1540    if ( stdVW )
   1541      *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
   1542  }
   1543 
   1544 
   1545  /*************************************************************************/
   1546  /*************************************************************************/
   1547  /*****                                                               *****/
   1548  /*****           L A T I N   G L Y P H   A N A L Y S I S             *****/
   1549  /*****                                                               *****/
   1550  /*************************************************************************/
   1551  /*************************************************************************/
   1552 
   1553 
   1554  /* Walk over all contours and compute its segments. */
   1555 
   1556  FT_LOCAL_DEF( FT_Error )
   1557  af_latin_hints_compute_segments( AF_GlyphHints  hints,
   1558                                   AF_Dimension   dim )
   1559  {
   1560    AF_LatinMetrics  metrics       = (AF_LatinMetrics)hints->metrics;
   1561    AF_AxisHints     axis          = &hints->axis[dim];
   1562    FT_Memory        memory        = hints->memory;
   1563    FT_Error         error         = FT_Err_Ok;
   1564    AF_Segment       segment       = NULL;
   1565    AF_SegmentRec    seg0;
   1566    AF_Point*        contour       = hints->contours;
   1567    AF_Point*        contour_limit = contour + hints->num_contours;
   1568    AF_Direction     major_dir, segment_dir;
   1569 
   1570    FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
   1571 
   1572 
   1573    FT_ZERO( &seg0 );
   1574    seg0.score = 32000;
   1575    seg0.flags = AF_EDGE_NORMAL;
   1576 
   1577    major_dir   = (AF_Direction)FT_ABS( axis->major_dir );
   1578    segment_dir = major_dir;
   1579 
   1580    axis->num_segments = 0;
   1581 
   1582    /* set up (u,v) in each point */
   1583    if ( dim == AF_DIMENSION_HORZ )
   1584    {
   1585      AF_Point  point = hints->points;
   1586      AF_Point  limit = point + hints->num_points;
   1587 
   1588 
   1589      for ( ; point < limit; point++ )
   1590      {
   1591        point->u = point->fx;
   1592        point->v = point->fy;
   1593      }
   1594    }
   1595    else
   1596    {
   1597      AF_Point  point = hints->points;
   1598      AF_Point  limit = point + hints->num_points;
   1599 
   1600 
   1601      for ( ; point < limit; point++ )
   1602      {
   1603        point->u = point->fy;
   1604        point->v = point->fx;
   1605      }
   1606    }
   1607 
   1608    /* do each contour separately */
   1609    for ( ; contour < contour_limit; contour++ )
   1610    {
   1611      AF_Point  point   = contour[0];
   1612      AF_Point  last    = point->prev;
   1613      int       on_edge = 0;
   1614 
   1615      /* we call values measured along a segment (point->v)    */
   1616      /* `coordinates', and values orthogonal to it (point->u) */
   1617      /* `positions'                                           */
   1618      FT_Pos     min_pos      =  32000;
   1619      FT_Pos     max_pos      = -32000;
   1620      FT_Pos     min_coord    =  32000;
   1621      FT_Pos     max_coord    = -32000;
   1622      FT_UShort  min_flags    =  AF_FLAG_NONE;
   1623      FT_UShort  max_flags    =  AF_FLAG_NONE;
   1624      FT_Pos     min_on_coord =  32000;
   1625      FT_Pos     max_on_coord = -32000;
   1626 
   1627      FT_Bool  passed;
   1628 
   1629      AF_Segment  prev_segment = NULL;
   1630 
   1631      FT_Pos     prev_min_pos      = min_pos;
   1632      FT_Pos     prev_max_pos      = max_pos;
   1633      FT_Pos     prev_min_coord    = min_coord;
   1634      FT_Pos     prev_max_coord    = max_coord;
   1635      FT_UShort  prev_min_flags    = min_flags;
   1636      FT_UShort  prev_max_flags    = max_flags;
   1637      FT_Pos     prev_min_on_coord = min_on_coord;
   1638      FT_Pos     prev_max_on_coord = max_on_coord;
   1639 
   1640 
   1641      if ( !( point->flags & AF_FLAG_IGNORE )    &&
   1642           FT_ABS( last->out_dir )  == major_dir &&
   1643           FT_ABS( point->out_dir ) == major_dir )
   1644      {
   1645        /* we are already on an edge, try to locate its start */
   1646        last = point;
   1647 
   1648        for (;;)
   1649        {
   1650          point = point->prev;
   1651          if ( FT_ABS( point->out_dir ) != major_dir )
   1652          {
   1653            point = point->next;
   1654            break;
   1655          }
   1656          if ( point == last )
   1657            break;
   1658        }
   1659      }
   1660 
   1661      last   = point;
   1662      passed = 0;
   1663 
   1664      for (;;)
   1665      {
   1666        FT_Pos  u, v;
   1667 
   1668 
   1669        if ( on_edge )
   1670        {
   1671          /* get minimum and maximum position */
   1672          u = point->u;
   1673          if ( u < min_pos )
   1674            min_pos = u;
   1675          if ( u > max_pos )
   1676            max_pos = u;
   1677 
   1678          /* get minimum and maximum coordinate together with flags */
   1679          v = point->v;
   1680          if ( v < min_coord )
   1681          {
   1682            min_coord = v;
   1683            min_flags = point->flags;
   1684          }
   1685          if ( v > max_coord )
   1686          {
   1687            max_coord = v;
   1688            max_flags = point->flags;
   1689          }
   1690 
   1691          /* get minimum and maximum coordinate of `on' points */
   1692          if ( !( point->flags & AF_FLAG_CONTROL ) )
   1693          {
   1694            v = point->v;
   1695            if ( v < min_on_coord )
   1696              min_on_coord = v;
   1697            if ( v > max_on_coord )
   1698              max_on_coord = v;
   1699          }
   1700 
   1701          if ( point->flags & AF_FLAG_IGNORE ||
   1702               point->out_dir != segment_dir ||
   1703               point == last )
   1704          {
   1705            /* check whether the new segment's start point is identical to */
   1706            /* the previous segment's end point; for example, this might   */
   1707            /* happen for spikes                                           */
   1708 
   1709            if ( point->flags & AF_FLAG_IGNORE        ||
   1710                 !prev_segment                        ||
   1711                 segment->first != prev_segment->last )
   1712            {
   1713              /* points are different: we are just leaving an edge, thus */
   1714              /* record a new segment                                    */
   1715 
   1716              segment->last  = point;
   1717              segment->pos   = (FT_Short)( ( min_pos + max_pos ) >> 1 );
   1718              segment->delta = (FT_Short)( ( max_pos - min_pos ) >> 1 );
   1719 
   1720              /* a segment is round if either its first or last point */
   1721              /* is a control point, and the length of the on points  */
   1722              /* inbetween doesn't exceed a heuristic limit           */
   1723              if ( ( min_flags | max_flags ) & AF_FLAG_CONTROL      &&
   1724                   ( max_on_coord - min_on_coord ) < flat_threshold )
   1725                segment->flags |= AF_EDGE_ROUND;
   1726 
   1727              segment->min_coord = (FT_Short)min_coord;
   1728              segment->max_coord = (FT_Short)max_coord;
   1729              segment->height    = segment->max_coord - segment->min_coord;
   1730 
   1731              prev_segment      = segment;
   1732              prev_min_pos      = min_pos;
   1733              prev_max_pos      = max_pos;
   1734              prev_min_coord    = min_coord;
   1735              prev_max_coord    = max_coord;
   1736              prev_min_flags    = min_flags;
   1737              prev_max_flags    = max_flags;
   1738              prev_min_on_coord = min_on_coord;
   1739              prev_max_on_coord = max_on_coord;
   1740            }
   1741            else
   1742            {
   1743              /* points are the same: we don't create a new segment but */
   1744              /* merge the current segment with the previous one        */
   1745 
   1746              if ( prev_segment->last->in_dir == point->in_dir )
   1747              {
   1748                /* we have identical directions (this can happen for       */
   1749                /* degenerate outlines that move zig-zag along the main    */
   1750                /* axis without changing the coordinate value of the other */
   1751                /* axis, and where the segments have just been merged):    */
   1752                /* unify segments                                          */
   1753 
   1754                /* update constraints */
   1755 
   1756                if ( prev_min_pos < min_pos )
   1757                  min_pos = prev_min_pos;
   1758                if ( prev_max_pos > max_pos )
   1759                  max_pos = prev_max_pos;
   1760 
   1761                if ( prev_min_coord < min_coord )
   1762                {
   1763                  min_coord = prev_min_coord;
   1764                  min_flags = prev_min_flags;
   1765                }
   1766                if ( prev_max_coord > max_coord )
   1767                {
   1768                  max_coord = prev_max_coord;
   1769                  max_flags = prev_max_flags;
   1770                }
   1771 
   1772                if ( prev_min_on_coord < min_on_coord )
   1773                  min_on_coord = prev_min_on_coord;
   1774                if ( prev_max_on_coord > max_on_coord )
   1775                  max_on_coord = prev_max_on_coord;
   1776 
   1777                prev_segment->last  = point;
   1778                prev_segment->pos   = (FT_Short)( ( min_pos +
   1779                                                    max_pos ) >> 1 );
   1780                prev_segment->delta = (FT_Short)( ( max_pos -
   1781                                                    min_pos ) >> 1 );
   1782 
   1783                if ( ( min_flags | max_flags ) & AF_FLAG_CONTROL      &&
   1784                     ( max_on_coord - min_on_coord ) < flat_threshold )
   1785                  prev_segment->flags |= AF_EDGE_ROUND;
   1786                else
   1787                  prev_segment->flags &= ~AF_EDGE_ROUND;
   1788 
   1789                prev_segment->min_coord = (FT_Short)min_coord;
   1790                prev_segment->max_coord = (FT_Short)max_coord;
   1791                prev_segment->height    = prev_segment->max_coord -
   1792                                          prev_segment->min_coord;
   1793              }
   1794              else
   1795              {
   1796                /* we have different directions; use the properties of the */
   1797                /* longer segment and discard the other one                */
   1798 
   1799                if ( FT_ABS( prev_max_coord - prev_min_coord ) >
   1800                     FT_ABS( max_coord - min_coord ) )
   1801                {
   1802                  /* discard current segment */
   1803 
   1804                  if ( min_pos < prev_min_pos )
   1805                    prev_min_pos = min_pos;
   1806                  if ( max_pos > prev_max_pos )
   1807                    prev_max_pos = max_pos;
   1808 
   1809                  prev_segment->last  = point;
   1810                  prev_segment->pos   = (FT_Short)( ( prev_min_pos +
   1811                                                      prev_max_pos ) >> 1 );
   1812                  prev_segment->delta = (FT_Short)( ( prev_max_pos -
   1813                                                      prev_min_pos ) >> 1 );
   1814                }
   1815                else
   1816                {
   1817                  /* discard previous segment */
   1818 
   1819                  if ( prev_min_pos < min_pos )
   1820                    min_pos = prev_min_pos;
   1821                  if ( prev_max_pos > max_pos )
   1822                    max_pos = prev_max_pos;
   1823 
   1824                  segment->last  = point;
   1825                  segment->pos   = (FT_Short)( ( min_pos + max_pos ) >> 1 );
   1826                  segment->delta = (FT_Short)( ( max_pos - min_pos ) >> 1 );
   1827 
   1828                  if ( ( min_flags | max_flags ) & AF_FLAG_CONTROL      &&
   1829                       ( max_on_coord - min_on_coord ) < flat_threshold )
   1830                    segment->flags |= AF_EDGE_ROUND;
   1831 
   1832                  segment->min_coord = (FT_Short)min_coord;
   1833                  segment->max_coord = (FT_Short)max_coord;
   1834                  segment->height    = segment->max_coord -
   1835                                       segment->min_coord;
   1836 
   1837                  *prev_segment = *segment;
   1838 
   1839                  prev_min_pos      = min_pos;
   1840                  prev_max_pos      = max_pos;
   1841                  prev_min_coord    = min_coord;
   1842                  prev_max_coord    = max_coord;
   1843                  prev_min_flags    = min_flags;
   1844                  prev_max_flags    = max_flags;
   1845                  prev_min_on_coord = min_on_coord;
   1846                  prev_max_on_coord = max_on_coord;
   1847                }
   1848              }
   1849 
   1850              axis->num_segments--;
   1851            }
   1852 
   1853            on_edge = 0;
   1854            segment = NULL;
   1855 
   1856            /* fall through */
   1857          }
   1858        }
   1859 
   1860        /* now exit if we are at the start/end point */
   1861        if ( point == last )
   1862        {
   1863          if ( passed )
   1864            break;
   1865          passed = 1;
   1866        }
   1867 
   1868        /* if we are not on an edge, check whether the major direction */
   1869        /* coincides with the current point's `out' direction, or      */
   1870        /* whether we have a single-point contour                      */
   1871        if ( !( point->flags & AF_FLAG_IGNORE )        &&
   1872             !on_edge                                  &&
   1873             ( FT_ABS( point->out_dir ) == major_dir ||
   1874               point == point->prev                  ) )
   1875        {
   1876          /*
   1877           * For efficiency, we restrict the number of segments to 1000,
   1878           * which is a heuristic value: it is very unlikely that a glyph
   1879           * with so many segments can be hinted in a sensible way.
   1880           * Reasons:
   1881           *
   1882           * - The glyph has really 1000 segments; this implies that it has
   1883           *   at least 2000 outline points.  Assuming 'normal' fonts that
   1884           *   have superfluous points optimized away, viewing such a glyph
   1885           *   only makes sense at large magnifications where hinting
   1886           *   isn't applied anyway.
   1887           *
   1888           * - We have a broken glyph.  Hinting doesn't make sense in this
   1889           *   case either.
   1890           */
   1891          if ( axis->num_segments > 1000 )
   1892          {
   1893            FT_TRACE0(( "af_latin_hints_compute_segments:"
   1894                        " more than 1000 segments in this glyph;\n" ));
   1895            FT_TRACE0(( "                                "
   1896                        " hinting is suppressed\n" ));
   1897            axis->num_segments = 0;
   1898            return FT_Err_Ok;
   1899          }
   1900 
   1901          /* this is the start of a new segment! */
   1902          segment_dir = (AF_Direction)point->out_dir;
   1903 
   1904          error = af_axis_hints_new_segment( axis, memory, &segment );
   1905          if ( error )
   1906            goto Exit;
   1907 
   1908          /* clear all segment fields */
   1909          segment[0] = seg0;
   1910 
   1911          segment->dir   = (FT_Char)segment_dir;
   1912          segment->first = point;
   1913          segment->last  = point;
   1914 
   1915          /* `af_axis_hints_new_segment' reallocates memory,    */
   1916          /* thus we have to refresh the `prev_segment' pointer */
   1917          if ( prev_segment )
   1918            prev_segment = segment - 1;
   1919 
   1920          min_pos   = max_pos   = point->u;
   1921          min_coord = max_coord = point->v;
   1922          min_flags = max_flags = point->flags;
   1923 
   1924          if ( point->flags & AF_FLAG_CONTROL )
   1925          {
   1926            min_on_coord =  32000;
   1927            max_on_coord = -32000;
   1928          }
   1929          else
   1930            min_on_coord = max_on_coord = point->v;
   1931 
   1932          on_edge = 1;
   1933 
   1934          if ( point == point->prev )
   1935          {
   1936            /* we have a one-point segment: this is a one-point */
   1937            /* contour with `in' and `out' direction set to     */
   1938            /* AF_DIR_NONE                                      */
   1939            segment->pos = (FT_Short)min_pos;
   1940 
   1941            if (point->flags & AF_FLAG_CONTROL)
   1942              segment->flags |= AF_EDGE_ROUND;
   1943 
   1944            segment->min_coord = (FT_Short)point->v;
   1945            segment->max_coord = (FT_Short)point->v;
   1946            segment->height = 0;
   1947 
   1948            on_edge = 0;
   1949            segment = NULL;
   1950          }
   1951        }
   1952 
   1953        point = point->next;
   1954      }
   1955 
   1956    } /* contours */
   1957 
   1958 
   1959    /* now slightly increase the height of segments if this makes */
   1960    /* sense -- this is used to better detect and ignore serifs   */
   1961    {
   1962      AF_Segment  segments     = axis->segments;
   1963      AF_Segment  segments_end = FT_OFFSET( segments, axis->num_segments );
   1964 
   1965 
   1966      for ( segment = segments; segment < segments_end; segment++ )
   1967      {
   1968        AF_Point  first   = segment->first;
   1969        AF_Point  last    = segment->last;
   1970        FT_Pos    first_v = first->v;
   1971        FT_Pos    last_v  = last->v;
   1972 
   1973 
   1974        if ( first_v < last_v )
   1975        {
   1976          AF_Point  p;
   1977 
   1978 
   1979          p = first->prev;
   1980          if ( p->v < first_v )
   1981            segment->height = (FT_Short)( segment->height +
   1982                                          ( ( first_v - p->v ) >> 1 ) );
   1983 
   1984          p = last->next;
   1985          if ( p->v > last_v )
   1986            segment->height = (FT_Short)( segment->height +
   1987                                          ( ( p->v - last_v ) >> 1 ) );
   1988        }
   1989        else
   1990        {
   1991          AF_Point  p;
   1992 
   1993 
   1994          p = first->prev;
   1995          if ( p->v > first_v )
   1996            segment->height = (FT_Short)( segment->height +
   1997                                          ( ( p->v - first_v ) >> 1 ) );
   1998 
   1999          p = last->next;
   2000          if ( p->v < last_v )
   2001            segment->height = (FT_Short)( segment->height +
   2002                                          ( ( last_v - p->v ) >> 1 ) );
   2003        }
   2004      }
   2005    }
   2006 
   2007  Exit:
   2008    return error;
   2009  }
   2010 
   2011 
   2012  /* Link segments to form stems and serifs.  If `width_count' and      */
   2013  /* `widths' are non-zero, use them to fine-tune the scoring function. */
   2014 
   2015  FT_LOCAL_DEF( void )
   2016  af_latin_hints_link_segments( AF_GlyphHints  hints,
   2017                                FT_UInt        width_count,
   2018                                AF_WidthRec*   widths,
   2019                                AF_Dimension   dim )
   2020  {
   2021    AF_AxisHints  axis          = &hints->axis[dim];
   2022    AF_Segment    segments      = axis->segments;
   2023    AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
   2024    FT_Pos        len_threshold, len_score, dist_score, max_width;
   2025    AF_Segment    seg1, seg2;
   2026 
   2027 
   2028    if ( width_count )
   2029      max_width = widths[width_count - 1].org;
   2030    else
   2031      max_width = 0;
   2032 
   2033    /* a heuristic value to set up a minimum value for overlapping */
   2034    len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
   2035    if ( len_threshold == 0 )
   2036      len_threshold = 1;
   2037 
   2038    /* a heuristic value to weight lengths */
   2039    len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
   2040 
   2041    /* a heuristic value to weight distances (no call to    */
   2042    /* AF_LATIN_CONSTANT needed, since we work on multiples */
   2043    /* of the stem width)                                   */
   2044    dist_score = 3000;
   2045 
   2046    /* now compare each segment to the others */
   2047    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
   2048    {
   2049      if ( seg1->dir != axis->major_dir )
   2050        continue;
   2051 
   2052      /* search for stems having opposite directions, */
   2053      /* with seg1 to the `left' of seg2              */
   2054      for ( seg2 = segments; seg2 < segment_limit; seg2++ )
   2055      {
   2056        FT_Pos  pos1 = seg1->pos;
   2057        FT_Pos  pos2 = seg2->pos;
   2058 
   2059 
   2060        if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 )
   2061        {
   2062          /* compute distance between the two segments */
   2063          FT_Pos  min = seg1->min_coord;
   2064          FT_Pos  max = seg1->max_coord;
   2065          FT_Pos  len;
   2066 
   2067 
   2068          if ( min < seg2->min_coord )
   2069            min = seg2->min_coord;
   2070 
   2071          if ( max > seg2->max_coord )
   2072            max = seg2->max_coord;
   2073 
   2074          /* compute maximum coordinate difference of the two segments */
   2075          /* (that is, how much they overlap)                          */
   2076          len = max - min;
   2077          if ( len >= len_threshold )
   2078          {
   2079            /*
   2080             * The score is the sum of two demerits indicating the
   2081             * `badness' of a fit, measured along the segments' main axis
   2082             * and orthogonal to it, respectively.
   2083             *
   2084             * - The less overlapping along the main axis, the worse it
   2085             *   is, causing a larger demerit.
   2086             *
   2087             * - The nearer the orthogonal distance to a stem width, the
   2088             *   better it is, causing a smaller demerit.  For simplicity,
   2089             *   however, we only increase the demerit for values that
   2090             *   exceed the largest stem width.
   2091             */
   2092 
   2093            FT_Pos  dist = pos2 - pos1;
   2094 
   2095            FT_Pos  dist_demerit, score;
   2096 
   2097 
   2098            if ( max_width )
   2099            {
   2100              /* distance demerits are based on multiples of `max_width'; */
   2101              /* we scale by 1024 for getting more precision              */
   2102              FT_Pos  delta = ( dist << 10 ) / max_width - ( 1 << 10 );
   2103 
   2104 
   2105              if ( delta > 10000 )
   2106                dist_demerit = 32000;
   2107              else if ( delta > 0 )
   2108                dist_demerit = delta * delta / dist_score;
   2109              else
   2110                dist_demerit = 0;
   2111            }
   2112            else
   2113              dist_demerit = dist; /* default if no widths available */
   2114 
   2115            score = dist_demerit + len_score / len;
   2116 
   2117            /* and we search for the smallest score */
   2118            if ( score < seg1->score )
   2119            {
   2120              seg1->score = score;
   2121              seg1->link  = seg2;
   2122            }
   2123 
   2124            if ( score < seg2->score )
   2125            {
   2126              seg2->score = score;
   2127              seg2->link  = seg1;
   2128            }
   2129          }
   2130        }
   2131      }
   2132    }
   2133 
   2134    /* now compute the `serif' segments, cf. explanations in `afhints.h' */
   2135    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
   2136    {
   2137      seg2 = seg1->link;
   2138 
   2139      if ( seg2 )
   2140      {
   2141        if ( seg2->link != seg1 )
   2142        {
   2143          seg1->link  = NULL;
   2144          seg1->serif = seg2->link;
   2145        }
   2146      }
   2147    }
   2148  }
   2149 
   2150 
   2151  /* Link segments to edges, using feature analysis for selection. */
   2152 
   2153  FT_LOCAL_DEF( FT_Error )
   2154  af_latin_hints_compute_edges( AF_GlyphHints  hints,
   2155                                AF_Dimension   dim )
   2156  {
   2157    AF_AxisHints  axis   = &hints->axis[dim];
   2158    FT_Error      error  = FT_Err_Ok;
   2159    FT_Memory     memory = hints->memory;
   2160    AF_LatinAxis  laxis  = &((AF_LatinMetrics)hints->metrics)->axis[dim];
   2161 
   2162    AF_StyleClass   style_class  = hints->metrics->style_class;
   2163    AF_ScriptClass  script_class = af_script_classes[style_class->script];
   2164 
   2165    FT_Bool  top_to_bottom_hinting = 0;
   2166 
   2167    AF_Segment    segments      = axis->segments;
   2168    AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
   2169    AF_Segment    seg;
   2170 
   2171 #if 0
   2172    AF_Direction  up_dir;
   2173 #endif
   2174    FT_Fixed      scale;
   2175    FT_Pos        edge_distance_threshold;
   2176    FT_Pos        segment_length_threshold;
   2177    FT_Pos        segment_width_threshold;
   2178 
   2179 
   2180    axis->num_edges = 0;
   2181 
   2182    scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
   2183                                         : hints->y_scale;
   2184 
   2185 #if 0
   2186    up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
   2187                                          : AF_DIR_RIGHT;
   2188 #endif
   2189 
   2190    if ( dim == AF_DIMENSION_VERT )
   2191      top_to_bottom_hinting = script_class->top_to_bottom_hinting;
   2192 
   2193    /*
   2194     * We ignore all segments that are less than 1 pixel in length
   2195     * to avoid many problems with serif fonts.  We compute the
   2196     * corresponding threshold in font units.
   2197     */
   2198    if ( dim == AF_DIMENSION_HORZ )
   2199      segment_length_threshold = FT_DivFix( 64, hints->y_scale );
   2200    else
   2201      segment_length_threshold = 0;
   2202 
   2203    /*
   2204     * Similarly, we ignore segments that have a width delta
   2205     * larger than 0.5px (i.e., a width larger than 1px).
   2206     */
   2207    segment_width_threshold = FT_DivFix( 32, scale );
   2208 
   2209    /**********************************************************************
   2210     *
   2211     * We begin by generating a sorted table of edges for the current
   2212     * direction.  To do so, we simply scan each segment and try to find
   2213     * an edge in our table that corresponds to its position.
   2214     *
   2215     * If no edge is found, we create and insert a new edge in the
   2216     * sorted table.  Otherwise, we simply add the segment to the edge's
   2217     * list which gets processed in the second step to compute the
   2218     * edge's properties.
   2219     *
   2220     * Note that the table of edges is sorted along the segment/edge
   2221     * position.
   2222     *
   2223     */
   2224 
   2225    /* assure that edge distance threshold is at most 0.25px */
   2226    edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
   2227                                         scale );
   2228    if ( edge_distance_threshold > 64 / 4 )
   2229      edge_distance_threshold = 64 / 4;
   2230 
   2231    edge_distance_threshold = FT_DivFix( edge_distance_threshold,
   2232                                         scale );
   2233 
   2234    for ( seg = segments; seg < segment_limit; seg++ )
   2235    {
   2236      AF_Edge  found = NULL;
   2237      FT_UInt  ee;
   2238 
   2239 
   2240      /* ignore too short segments, too wide ones, and, in this loop, */
   2241      /* one-point segments without a direction                       */
   2242      if ( seg->height < segment_length_threshold ||
   2243           seg->delta > segment_width_threshold   ||
   2244           seg->dir == AF_DIR_NONE                )
   2245        continue;
   2246 
   2247      /* A special case for serif edges: If they are smaller than */
   2248      /* 1.5 pixels we ignore them.                               */
   2249      if ( seg->serif                                     &&
   2250           2 * seg->height < 3 * segment_length_threshold )
   2251        continue;
   2252 
   2253      /* look for an edge corresponding to the segment */
   2254      for ( ee = 0; ee < axis->num_edges; ee++ )
   2255      {
   2256        AF_Edge  edge = axis->edges + ee;
   2257        FT_Pos   dist;
   2258 
   2259 
   2260        dist = seg->pos - edge->fpos;
   2261        if ( dist < 0 )
   2262          dist = -dist;
   2263 
   2264        if ( dist < edge_distance_threshold && edge->dir == seg->dir )
   2265        {
   2266          found = edge;
   2267          break;
   2268        }
   2269      }
   2270 
   2271      if ( !found )
   2272      {
   2273        AF_Edge  edge;
   2274 
   2275 
   2276        /* insert a new edge in the list and */
   2277        /* sort according to the position    */
   2278        error = af_axis_hints_new_edge( axis, seg->pos,
   2279                                        (AF_Direction)seg->dir,
   2280                                        top_to_bottom_hinting,
   2281                                        memory, &edge );
   2282        if ( error )
   2283          goto Exit;
   2284 
   2285        /* add the segment to the new edge's list */
   2286        FT_ZERO( edge );
   2287 
   2288        edge->first    = seg;
   2289        edge->last     = seg;
   2290        edge->dir      = seg->dir;
   2291        edge->fpos     = seg->pos;
   2292        edge->opos     = FT_MulFix( seg->pos, scale );
   2293        edge->pos      = edge->opos;
   2294        seg->edge_next = seg;
   2295      }
   2296      else
   2297      {
   2298        /* if an edge was found, simply add the segment to the edge's */
   2299        /* list                                                       */
   2300        seg->edge_next         = found->first;
   2301        found->last->edge_next = seg;
   2302        found->last            = seg;
   2303      }
   2304    }
   2305 
   2306    /* we loop again over all segments to catch one-point segments   */
   2307    /* without a direction: if possible, link them to existing edges */
   2308    for ( seg = segments; seg < segment_limit; seg++ )
   2309    {
   2310      AF_Edge  found = NULL;
   2311      FT_UInt  ee;
   2312 
   2313 
   2314      if ( seg->dir != AF_DIR_NONE )
   2315        continue;
   2316 
   2317      /* look for an edge corresponding to the segment */
   2318      for ( ee = 0; ee < axis->num_edges; ee++ )
   2319      {
   2320        AF_Edge  edge = axis->edges + ee;
   2321        FT_Pos   dist;
   2322 
   2323 
   2324        dist = seg->pos - edge->fpos;
   2325        if ( dist < 0 )
   2326          dist = -dist;
   2327 
   2328        if ( dist < edge_distance_threshold )
   2329        {
   2330          found = edge;
   2331          break;
   2332        }
   2333      }
   2334 
   2335      /* one-point segments without a match are ignored */
   2336      if ( found )
   2337      {
   2338        seg->edge_next         = found->first;
   2339        found->last->edge_next = seg;
   2340        found->last            = seg;
   2341      }
   2342    }
   2343 
   2344 
   2345    /*******************************************************************
   2346     *
   2347     * Good, we now compute each edge's properties according to the
   2348     * segments found on its position.  Basically, these are
   2349     *
   2350     * - the edge's main direction
   2351     * - stem edge, serif edge or both (which defaults to stem then)
   2352     * - rounded edge, straight or both (which defaults to straight)
   2353     * - link for edge
   2354     *
   2355     */
   2356 
   2357    /* first of all, set the `edge' field in each segment -- this is */
   2358    /* required in order to compute edge links                       */
   2359 
   2360    /*
   2361     * Note that removing this loop and setting the `edge' field of each
   2362     * segment directly in the code above slows down execution speed for
   2363     * some reasons on platforms like the Sun.
   2364     */
   2365    {
   2366      AF_Edge  edges      = axis->edges;
   2367      AF_Edge  edge_limit = FT_OFFSET( edges, axis->num_edges );
   2368      AF_Edge  edge;
   2369 
   2370 
   2371      for ( edge = edges; edge < edge_limit; edge++ )
   2372      {
   2373        seg = edge->first;
   2374        if ( seg )
   2375          do
   2376          {
   2377            seg->edge = edge;
   2378            seg       = seg->edge_next;
   2379 
   2380          } while ( seg != edge->first );
   2381      }
   2382 
   2383      /* now compute each edge properties */
   2384      for ( edge = edges; edge < edge_limit; edge++ )
   2385      {
   2386        FT_Int  is_round    = 0;  /* does it contain round segments?    */
   2387        FT_Int  is_straight = 0;  /* does it contain straight segments? */
   2388 #if 0
   2389        FT_Pos  ups         = 0;  /* number of upwards segments         */
   2390        FT_Pos  downs       = 0;  /* number of downwards segments       */
   2391 #endif
   2392 
   2393 
   2394        seg = edge->first;
   2395 
   2396        do
   2397        {
   2398          FT_Bool  is_serif;
   2399 
   2400 
   2401          /* check for roundness of segment */
   2402          if ( seg->flags & AF_EDGE_ROUND )
   2403            is_round++;
   2404          else
   2405            is_straight++;
   2406 
   2407 #if 0
   2408          /* check for segment direction */
   2409          if ( seg->dir == up_dir )
   2410            ups   += seg->max_coord - seg->min_coord;
   2411          else
   2412            downs += seg->max_coord - seg->min_coord;
   2413 #endif
   2414 
   2415          /* check for links -- if seg->serif is set, then seg->link must */
   2416          /* be ignored                                                   */
   2417          is_serif = FT_BOOL( seg->serif               &&
   2418                              seg->serif->edge         &&
   2419                              seg->serif->edge != edge );
   2420 
   2421          if ( ( seg->link && seg->link->edge ) || is_serif )
   2422          {
   2423            AF_Edge     edge2;
   2424            AF_Segment  seg2;
   2425 
   2426 
   2427            edge2 = edge->link;
   2428            seg2  = seg->link;
   2429 
   2430            if ( is_serif )
   2431            {
   2432              seg2  = seg->serif;
   2433              edge2 = edge->serif;
   2434            }
   2435 
   2436            if ( edge2 )
   2437            {
   2438              FT_Pos  edge_delta;
   2439              FT_Pos  seg_delta;
   2440 
   2441 
   2442              edge_delta = edge->fpos - edge2->fpos;
   2443              if ( edge_delta < 0 )
   2444                edge_delta = -edge_delta;
   2445 
   2446              seg_delta = seg->pos - seg2->pos;
   2447              if ( seg_delta < 0 )
   2448                seg_delta = -seg_delta;
   2449 
   2450              if ( seg_delta < edge_delta )
   2451                edge2 = seg2->edge;
   2452            }
   2453            else
   2454              edge2 = seg2->edge;
   2455 
   2456            if ( is_serif )
   2457            {
   2458              edge->serif   = edge2;
   2459              edge2->flags |= AF_EDGE_SERIF;
   2460            }
   2461            else
   2462              edge->link  = edge2;
   2463          }
   2464 
   2465          seg = seg->edge_next;
   2466 
   2467        } while ( seg != edge->first );
   2468 
   2469        /* set the round/straight flags */
   2470        edge->flags = AF_EDGE_NORMAL;
   2471 
   2472        if ( is_round > 0 && is_round >= is_straight )
   2473          edge->flags |= AF_EDGE_ROUND;
   2474 
   2475 #if 0
   2476        /* set the edge's main direction */
   2477        edge->dir = AF_DIR_NONE;
   2478 
   2479        if ( ups > downs )
   2480          edge->dir = (FT_Char)up_dir;
   2481 
   2482        else if ( ups < downs )
   2483          edge->dir = (FT_Char)-up_dir;
   2484 
   2485        else if ( ups == downs )
   2486          edge->dir = 0;  /* both up and down! */
   2487 #endif
   2488 
   2489        /* get rid of serifs if link is set                 */
   2490        /* XXX: This gets rid of many unpleasant artefacts! */
   2491        /*      Example: the `c' in cour.pfa at size 13     */
   2492 
   2493        if ( edge->serif && edge->link )
   2494          edge->serif = NULL;
   2495      }
   2496    }
   2497 
   2498  Exit:
   2499    return error;
   2500  }
   2501 
   2502 
   2503  /* Detect segments and edges for given dimension. */
   2504 
   2505  FT_LOCAL_DEF( FT_Error )
   2506  af_latin_hints_detect_features( AF_GlyphHints  hints,
   2507                                  FT_UInt        width_count,
   2508                                  AF_WidthRec*   widths,
   2509                                  AF_Dimension   dim )
   2510  {
   2511    FT_Error  error;
   2512 
   2513 
   2514    error = af_latin_hints_compute_segments( hints, dim );
   2515    if ( !error )
   2516    {
   2517      af_latin_hints_link_segments( hints, width_count, widths, dim );
   2518 
   2519      error = af_latin_hints_compute_edges( hints, dim );
   2520    }
   2521 
   2522    return error;
   2523  }
   2524 
   2525 
   2526  /* Compute all edges which lie within blue zones. */
   2527 
   2528  static void
   2529  af_latin_hints_compute_blue_edges( AF_GlyphHints    hints,
   2530                                     AF_LatinMetrics  metrics )
   2531  {
   2532    AF_AxisHints  axis       = &hints->axis[AF_DIMENSION_VERT];
   2533    AF_Edge       edge       = axis->edges;
   2534    AF_Edge       edge_limit = FT_OFFSET( edge, axis->num_edges );
   2535    AF_LatinAxis  latin      = &metrics->axis[AF_DIMENSION_VERT];
   2536    FT_Fixed      scale      = latin->scale;
   2537 
   2538 
   2539    /* compute which blue zones are active, i.e. have their scaled */
   2540    /* size < 3/4 pixels                                           */
   2541 
   2542    /* for each horizontal edge search the blue zone which is closest */
   2543    for ( ; edge < edge_limit; edge++ )
   2544    {
   2545      FT_UInt   bb;
   2546      AF_Width  best_blue            = NULL;
   2547      FT_Bool   best_blue_is_neutral = 0;
   2548      FT_Pos    best_dist;                 /* initial threshold */
   2549 
   2550 
   2551      if ( edge->flags & AF_EDGE_NO_BLUE )
   2552        continue;
   2553 
   2554      /* compute the initial threshold as a fraction of the EM size */
   2555      /* (the value 40 is heuristic)                                */
   2556      best_dist = FT_MulFix( metrics->units_per_em / 40, scale );
   2557 
   2558      /* assure a minimum distance of 0.5px */
   2559      if ( best_dist > 64 / 2 )
   2560        best_dist = 64 / 2;
   2561 
   2562      for ( bb = 0; bb < latin->blue_count; bb++ )
   2563      {
   2564        AF_LatinBlue  blue = latin->blues + bb;
   2565        FT_Bool       is_top_blue, is_neutral_blue, is_major_dir;
   2566 
   2567 
   2568        /* skip inactive blue zones (i.e., those that are too large) */
   2569        if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
   2570          continue;
   2571 
   2572        /* if it is a top zone, check for right edges (against the major */
   2573        /* direction); if it is a bottom zone, check for left edges (in  */
   2574        /* the major direction) -- this assumes the TrueType convention  */
   2575        /* for the orientation of contours                               */
   2576        is_top_blue =
   2577          (FT_Byte)( ( blue->flags & ( AF_LATIN_BLUE_TOP     |
   2578                                       AF_LATIN_BLUE_SUB_TOP ) ) != 0 );
   2579        is_neutral_blue =
   2580          (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0);
   2581        is_major_dir =
   2582          FT_BOOL( edge->dir == axis->major_dir );
   2583 
   2584        /* neutral blue zones are handled for both directions */
   2585        if ( is_top_blue ^ is_major_dir || is_neutral_blue )
   2586        {
   2587          FT_Pos  dist;
   2588 
   2589 
   2590          /* first of all, compare it to the reference position */
   2591          dist = edge->fpos - blue->ref.org;
   2592          if ( dist < 0 )
   2593            dist = -dist;
   2594 
   2595          dist = FT_MulFix( dist, scale );
   2596          if ( dist < best_dist )
   2597          {
   2598            best_dist            = dist;
   2599            best_blue            = &blue->ref;
   2600            best_blue_is_neutral = is_neutral_blue;
   2601          }
   2602 
   2603          /* now compare it to the overshoot position and check whether */
   2604          /* the edge is rounded, and whether the edge is over the      */
   2605          /* reference position of a top zone, or under the reference   */
   2606          /* position of a bottom zone (provided we don't have a        */
   2607          /* neutral blue zone)                                         */
   2608          if ( edge->flags & AF_EDGE_ROUND &&
   2609               dist != 0                   &&
   2610               !is_neutral_blue            )
   2611          {
   2612            FT_Bool  is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
   2613 
   2614 
   2615            if ( is_top_blue ^ is_under_ref )
   2616            {
   2617              dist = edge->fpos - blue->shoot.org;
   2618              if ( dist < 0 )
   2619                dist = -dist;
   2620 
   2621              dist = FT_MulFix( dist, scale );
   2622              if ( dist < best_dist )
   2623              {
   2624                best_dist            = dist;
   2625                best_blue            = &blue->shoot;
   2626                best_blue_is_neutral = is_neutral_blue;
   2627              }
   2628            }
   2629          }
   2630        }
   2631      }
   2632 
   2633      if ( best_blue )
   2634      {
   2635        edge->blue_edge = best_blue;
   2636        if ( best_blue_is_neutral )
   2637          edge->flags |= AF_EDGE_NEUTRAL;
   2638      }
   2639    }
   2640  }
   2641 
   2642 
   2643  /* Initialize hinting engine. */
   2644 
   2645  static FT_Error
   2646  af_latin_hints_init( AF_GlyphHints    hints,
   2647                       AF_StyleMetrics  metrics_ )   /* AF_LatinMetrics */
   2648  {
   2649    AF_LatinMetrics  metrics = (AF_LatinMetrics)metrics_;
   2650 
   2651    FT_Render_Mode  mode;
   2652    FT_UInt32       scaler_flags, other_flags;
   2653    FT_Face         face = metrics->root.scaler.face;
   2654 
   2655 
   2656    af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics );
   2657 
   2658    /*
   2659     * correct x_scale and y_scale if needed, since they may have
   2660     * been modified by `af_latin_metrics_scale_dim' above
   2661     */
   2662    hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
   2663    hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
   2664    hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
   2665    hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
   2666 
   2667    /* compute flags depending on render mode, etc. */
   2668    mode = metrics->root.scaler.render_mode;
   2669 
   2670    scaler_flags = hints->scaler_flags;
   2671    other_flags  = 0;
   2672 
   2673    /*
   2674     * We snap the width of vertical stems for the monochrome and
   2675     * horizontal LCD rendering targets only.
   2676     */
   2677    if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
   2678      other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
   2679 
   2680    /*
   2681     * We snap the width of horizontal stems for the monochrome and
   2682     * vertical LCD rendering targets only.
   2683     */
   2684    if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
   2685      other_flags |= AF_LATIN_HINTS_VERT_SNAP;
   2686 
   2687    /*
   2688     * We adjust stems to full pixels unless in `light' or `lcd' mode.
   2689     */
   2690    if ( mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD )
   2691      other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
   2692 
   2693    if ( mode == FT_RENDER_MODE_MONO )
   2694      other_flags |= AF_LATIN_HINTS_MONO;
   2695 
   2696    /*
   2697     * In `light' or `lcd' mode we disable horizontal hinting completely.
   2698     * We also do it if the face is italic.
   2699     *
   2700     * However, if warping is enabled (which only works in `light' hinting
   2701     * mode), advance widths get adjusted, too.
   2702     */
   2703    if ( mode == FT_RENDER_MODE_LIGHT || mode == FT_RENDER_MODE_LCD ||
   2704         ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0          )
   2705      scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
   2706 
   2707    hints->scaler_flags = scaler_flags;
   2708    hints->other_flags  = other_flags;
   2709 
   2710    return FT_Err_Ok;
   2711  }
   2712 
   2713 
   2714  /*************************************************************************/
   2715  /*************************************************************************/
   2716  /*****                                                               *****/
   2717  /*****        L A T I N   G L Y P H   G R I D - F I T T I N G        *****/
   2718  /*****                                                               *****/
   2719  /*************************************************************************/
   2720  /*************************************************************************/
   2721 
   2722  /* Snap a given width in scaled coordinates to one of the */
   2723  /* current standard widths.                               */
   2724 
   2725  static FT_Pos
   2726  af_latin_snap_width( AF_Width  widths,
   2727                       FT_UInt   count,
   2728                       FT_Pos    width )
   2729  {
   2730    FT_UInt  n;
   2731    FT_Pos   best      = 64 + 32 + 2;
   2732    FT_Pos   reference = width;
   2733    FT_Pos   scaled;
   2734 
   2735 
   2736    for ( n = 0; n < count; n++ )
   2737    {
   2738      FT_Pos  w;
   2739      FT_Pos  dist;
   2740 
   2741 
   2742      w = widths[n].cur;
   2743      dist = width - w;
   2744      if ( dist < 0 )
   2745        dist = -dist;
   2746      if ( dist < best )
   2747      {
   2748        best      = dist;
   2749        reference = w;
   2750      }
   2751    }
   2752 
   2753    scaled = FT_PIX_ROUND( reference );
   2754 
   2755    if ( width >= reference )
   2756    {
   2757      if ( width < scaled + 48 )
   2758        width = reference;
   2759    }
   2760    else
   2761    {
   2762      if ( width > scaled - 48 )
   2763        width = reference;
   2764    }
   2765 
   2766    return width;
   2767  }
   2768 
   2769 
   2770 #undef  FT_COMPONENT
   2771 #define FT_COMPONENT  afadjust
   2772 
   2773 
   2774  static void
   2775  af_move_contour_vertically( AF_Point  contour,
   2776                              FT_Int    movement )
   2777  {
   2778    AF_Point  point       = contour;
   2779    AF_Point  first_point = point;
   2780 
   2781 
   2782    if ( point )
   2783    {
   2784      do
   2785      {
   2786        point->y += movement;
   2787        point     = point->next;
   2788 
   2789      } while ( point != first_point );
   2790    }
   2791  }
   2792 
   2793 
   2794  /* Move all contours higher than `limit` by `delta`. */
   2795  static void
   2796  af_move_contours_up( AF_GlyphHints  hints,
   2797                       FT_Pos         limit,
   2798                       FT_Pos         delta )
   2799  {
   2800    FT_Int  contour;
   2801 
   2802 
   2803    for ( contour = 0; contour < hints->num_contours; contour++ )
   2804    {
   2805      FT_Pos  min_y = hints->contour_y_minima[contour];
   2806      FT_Pos  max_y = hints->contour_y_maxima[contour];
   2807 
   2808 
   2809      if ( min_y < max_y &&
   2810           min_y > limit )
   2811        af_move_contour_vertically( hints->contours[contour],
   2812                                    delta );
   2813    }
   2814  }
   2815 
   2816 
   2817  static void
   2818  af_move_contours_down( AF_GlyphHints  hints,
   2819                         FT_Pos         limit,
   2820                         FT_Pos         delta )
   2821  {
   2822    FT_Int  contour;
   2823 
   2824 
   2825    for ( contour = 0; contour < hints->num_contours; contour++ )
   2826    {
   2827      FT_Pos  min_y = hints->contour_y_minima[contour];
   2828      FT_Pos  max_y = hints->contour_y_maxima[contour];
   2829 
   2830 
   2831      if ( min_y < max_y &&
   2832           max_y < limit )
   2833        af_move_contour_vertically( hints->contours[contour],
   2834                                    -delta );
   2835    }
   2836  }
   2837 
   2838 
   2839  /* Compute vertical extrema of all contours and store them in the */
   2840  /* `contour_y_minima` and `contour_y_maxima` arrays of `hints`.   */
   2841  static void
   2842  af_compute_vertical_extrema( AF_GlyphHints  hints )
   2843  {
   2844    FT_Int  contour;
   2845 
   2846 
   2847    for ( contour = 0; contour < hints->num_contours; contour++ )
   2848    {
   2849      FT_Pos  min_y = FT_LONG_MAX;
   2850      FT_Pos  max_y = FT_LONG_MIN;
   2851 
   2852      AF_Point  first_point = hints->contours[contour];
   2853      AF_Point  point       = first_point;
   2854 
   2855 
   2856      if ( !first_point || first_point->next->next == first_point )
   2857        goto End_loop;
   2858 
   2859      do
   2860      {
   2861        if ( point->y < min_y )
   2862          min_y = point->y;
   2863        if ( point->y > max_y )
   2864          max_y = point->y;
   2865 
   2866        point = point->next;
   2867 
   2868      } while ( point != first_point );
   2869 
   2870    End_loop:
   2871      hints->contour_y_minima[contour] = min_y;
   2872      hints->contour_y_maxima[contour] = max_y;
   2873    }
   2874  }
   2875 
   2876 
   2877  static FT_Int
   2878  af_find_highest_contour( AF_GlyphHints  hints )
   2879  {
   2880    FT_Int  highest_contour = 0;
   2881    FT_Pos  highest_min_y   = FT_LONG_MAX;
   2882    FT_Pos  highest_max_y   = FT_LONG_MIN;
   2883 
   2884    FT_Int  contour;
   2885 
   2886 
   2887    /* At this point we have one 'lower' (usually the base glyph)   */
   2888    /* and one 'upper' object (usually the diacritic glyph).  If    */
   2889    /* there are more contours, they must be enclosed within either */
   2890    /* 'lower' or 'upper'.  To find this enclosing 'upper' contour  */
   2891    /* it is thus sufficient to search for the contour with the     */
   2892    /* highest y maximum value.                                     */
   2893    for ( contour = 0; contour < hints->num_contours; contour++ )
   2894    {
   2895      FT_Pos  current_min_y = hints->contour_y_minima[contour];
   2896      FT_Pos  current_max_y = hints->contour_y_maxima[contour];
   2897 
   2898 
   2899      /* If we have two contours with the same maximum value, take */
   2900      /* the one that has a smaller height.                        */
   2901      if ( current_max_y > highest_max_y      ||
   2902           ( current_max_y == highest_max_y &&
   2903             current_min_y > highest_min_y  ) )
   2904      {
   2905        highest_min_y   = current_min_y;
   2906        highest_max_y   = current_max_y;
   2907        highest_contour = contour;
   2908      }
   2909    }
   2910 
   2911    return highest_contour;
   2912  }
   2913 
   2914 
   2915  static FT_Int
   2916  af_find_second_highest_contour( AF_GlyphHints  hints )
   2917  {
   2918    FT_Int  highest_contour;
   2919    FT_Pos  highest_min_y;
   2920 
   2921    FT_Int  second_highest_contour = 0;
   2922    FT_Pos  second_highest_max_y   = FT_LONG_MIN;
   2923 
   2924    FT_Int  contour;
   2925 
   2926 
   2927    if ( hints->num_contours < 3 )
   2928      return 0;
   2929 
   2930    highest_contour = af_find_highest_contour( hints );
   2931    highest_min_y   = hints->contour_y_minima[highest_contour];
   2932 
   2933    /* Search the contour with the largest vertical maximum that has a */
   2934    /* vertical minimum lower than the vertical minimum of the topmost */
   2935    /* contour.                                                        */
   2936    for ( contour = 0; contour < hints->num_contours; contour++ )
   2937    {
   2938      FT_Pos  current_min_y;
   2939      FT_Pos  current_max_y;
   2940 
   2941 
   2942      if ( contour == highest_contour )
   2943        continue;
   2944 
   2945      current_min_y = hints->contour_y_minima[contour];
   2946      current_max_y = hints->contour_y_maxima[contour];
   2947 
   2948      if ( current_max_y > second_highest_max_y &&
   2949           current_min_y < highest_min_y        )
   2950      {
   2951        second_highest_max_y   = current_max_y;
   2952        second_highest_contour = contour;
   2953      }
   2954    }
   2955 
   2956    return second_highest_contour;
   2957  }
   2958 
   2959 
   2960  static FT_Int
   2961  af_find_lowest_contour( AF_GlyphHints  hints )
   2962  {
   2963    FT_Int  lowest_contour = 0;
   2964    FT_Pos  lowest_min_y   = FT_LONG_MAX;
   2965    FT_Pos  lowest_max_y   = FT_LONG_MIN;
   2966 
   2967    FT_Int  contour;
   2968 
   2969 
   2970    for ( contour = 0; contour < hints->num_contours; contour++ )
   2971    {
   2972      FT_Pos  current_min_y = hints->contour_y_minima[contour];
   2973      FT_Pos  current_max_y = hints->contour_y_maxima[contour];
   2974 
   2975 
   2976      if ( current_min_y < lowest_min_y      ||
   2977           ( current_min_y == lowest_min_y &&
   2978             current_max_y < lowest_max_y  ) )
   2979      {
   2980        lowest_min_y   = current_min_y;
   2981        lowest_max_y   = current_max_y;
   2982        lowest_contour = contour;
   2983      }
   2984    }
   2985 
   2986    return lowest_contour;
   2987  }
   2988 
   2989 
   2990  static FT_Int
   2991  af_find_second_lowest_contour( AF_GlyphHints  hints )
   2992  {
   2993    FT_Int  lowest_contour;
   2994    FT_Pos  lowest_max_y;
   2995 
   2996    FT_Int  second_lowest_contour = 0;
   2997    FT_Pos  second_lowest_min_y   = FT_LONG_MAX;
   2998 
   2999    FT_Int  contour;
   3000 
   3001 
   3002    if ( hints->num_contours < 3 )
   3003      return 0;
   3004 
   3005    lowest_contour = af_find_lowest_contour( hints );
   3006    lowest_max_y   = hints->contour_y_maxima[lowest_contour];
   3007 
   3008    for ( contour = 0; contour < hints->num_contours; contour++ )
   3009    {
   3010      FT_Pos  current_min_y;
   3011      FT_Pos  current_max_y;
   3012 
   3013 
   3014      if ( contour == lowest_contour )
   3015        continue;
   3016 
   3017      current_min_y = hints->contour_y_minima[contour];
   3018      current_max_y = hints->contour_y_maxima[contour];
   3019 
   3020      if ( current_min_y < second_lowest_min_y &&
   3021           current_max_y > lowest_max_y        )
   3022      {
   3023        second_lowest_min_y   = current_min_y;
   3024        second_lowest_contour = contour;
   3025      }
   3026    }
   3027 
   3028    return second_lowest_contour;
   3029  }
   3030 
   3031 
   3032  /* While aligning edges to blue zones, make the auto-hinter */
   3033  /* ignore the ones that are higher than `pos`.              */
   3034  static void
   3035  af_prevent_top_blue_alignment( AF_GlyphHints  hints,
   3036                                 FT_Pos         pos )
   3037  {
   3038    AF_AxisHints  axis = &hints->axis[AF_DIMENSION_VERT];
   3039 
   3040    AF_Edge  edges      = axis->edges;
   3041    AF_Edge  edge_limit = FT_OFFSET( edges, axis->num_edges );
   3042    AF_Edge  edge;
   3043 
   3044 
   3045    for ( edge = edges; edge < edge_limit; edge++ )
   3046      if ( edge->pos > pos )
   3047        edge->flags |= AF_EDGE_NO_BLUE;
   3048  }
   3049 
   3050 
   3051  static void
   3052  af_prevent_bottom_blue_alignment( AF_GlyphHints  hints,
   3053                                    FT_Pos         pos )
   3054  {
   3055    AF_AxisHints  axis = &hints->axis[AF_DIMENSION_VERT];
   3056 
   3057    AF_Edge  edges      = axis->edges;
   3058    AF_Edge  edge_limit = FT_OFFSET( edges, axis->num_edges );
   3059    AF_Edge  edge;
   3060 
   3061 
   3062    for ( edge = edges; edge < edge_limit; edge++ )
   3063      if ( edge->pos < pos )
   3064        edge->flags |= AF_EDGE_NO_BLUE;
   3065  }
   3066 
   3067 
   3068  static void
   3069  af_latin_get_base_glyph_blues( AF_GlyphHints  hints,
   3070                                 FT_Bool        is_capital,
   3071                                 AF_LatinBlue*  top,
   3072                                 AF_LatinBlue*  bottom )
   3073  {
   3074    AF_LatinMetrics  metrics = (AF_LatinMetrics)hints->metrics;
   3075    AF_LatinAxis     axis    = &metrics->axis[AF_DIMENSION_VERT];
   3076 
   3077    FT_UInt  top_flag;
   3078    FT_UInt  bottom_flag;
   3079 
   3080    FT_UInt  i;
   3081 
   3082 
   3083    top_flag  = is_capital ? AF_LATIN_BLUE_TOP
   3084                           : AF_LATIN_BLUE_ADJUSTMENT;
   3085    top_flag |= AF_LATIN_BLUE_ACTIVE;
   3086 
   3087    for ( i = 0; i < axis->blue_count; i++ )
   3088      if ( ( axis->blues[i].flags & top_flag ) == top_flag )
   3089        break;
   3090    if ( i < axis->blue_count )
   3091      *top = &axis->blues[i];
   3092 
   3093    bottom_flag  = is_capital ? AF_LATIN_BLUE_BOTTOM
   3094                              : AF_LATIN_BLUE_BOTTOM_SMALL;
   3095    bottom_flag |= AF_LATIN_BLUE_ACTIVE;
   3096 
   3097    for ( i = 0; i < axis->blue_count; i++ )
   3098      if ( ( axis->blues[i].flags & bottom_flag ) == bottom_flag )
   3099        break;
   3100    if ( i < axis->blue_count )
   3101      *bottom = &axis->blues[i];
   3102  }
   3103 
   3104 
   3105  /* Make the auto-hinter ignore top blue zones while aligning edges. */
   3106  /* This affects everything that is higher than a vertical position  */
   3107  /* based on the lowercase or uppercase top and bottom blue zones    */
   3108  /* (depending on `is_capital`).                                     */
   3109  static void
   3110  af_latin_ignore_top( AF_GlyphHints  hints,
   3111                       AF_LatinBlue   top_blue,
   3112                       AF_LatinBlue   bottom_blue )
   3113  {
   3114    FT_Pos  base_glyph_height;
   3115    FT_Pos  limit;
   3116 
   3117 
   3118    /* Ignore blue zones that are higher than a heuristic threshold     */
   3119    /* (value 7 corresponds to approx. 14%, which should be sufficient  */
   3120    /* to exceed the height of uppercase serifs.  We also add a quarter */
   3121    /* of a pixel as a safety measure.                                  */
   3122    base_glyph_height = top_blue->shoot.cur - bottom_blue->shoot.cur;
   3123    limit             = top_blue->shoot.cur + base_glyph_height / 7 + 16;
   3124 
   3125    af_prevent_top_blue_alignment( hints, limit );
   3126  }
   3127 
   3128 
   3129  static void
   3130  af_latin_ignore_bottom( AF_GlyphHints  hints,
   3131                          AF_LatinBlue   top_blue,
   3132                          AF_LatinBlue   bottom_blue )
   3133  {
   3134    FT_Pos  base_glyph_height;
   3135    FT_Pos  limit;
   3136 
   3137 
   3138    base_glyph_height = top_blue->shoot.cur - bottom_blue->shoot.cur;
   3139    limit             = bottom_blue->shoot.cur - base_glyph_height / 7 - 16;
   3140 
   3141    af_prevent_bottom_blue_alignment( hints, limit );
   3142  }
   3143 
   3144 
   3145  static void
   3146  af_touch_contour( AF_GlyphHints  hints,
   3147                    FT_Int         contour )
   3148  {
   3149    AF_Point  first_point = hints->contours[contour];
   3150    AF_Point  p           = first_point;
   3151 
   3152 
   3153    do
   3154    {
   3155      p = p->next;
   3156 
   3157      p->flags |= AF_FLAG_IGNORE;
   3158      if ( !( p->flags & AF_FLAG_CONTROL ) )
   3159        p->flags |= AF_FLAG_TOUCH_Y;
   3160 
   3161    } while ( p != first_point );
   3162  }
   3163 
   3164 
   3165  static void
   3166  af_touch_top_contours( AF_GlyphHints  hints,
   3167                         FT_Int         limit_contour )
   3168  {
   3169    FT_Pos  limit = hints->contour_y_minima[limit_contour];
   3170 
   3171    FT_Int  contour;
   3172 
   3173 
   3174    for ( contour = 0; contour < hints->num_contours; contour++ )
   3175    {
   3176      FT_Pos  min_y = hints->contour_y_minima[contour];
   3177      FT_Pos  max_y = hints->contour_y_maxima[contour];
   3178 
   3179 
   3180      if ( min_y < max_y  &&
   3181           min_y >= limit )
   3182        af_touch_contour( hints, contour );
   3183    }
   3184  }
   3185 
   3186 
   3187  static void
   3188  af_touch_bottom_contours( AF_GlyphHints  hints,
   3189                            FT_Int         limit_contour )
   3190  {
   3191    FT_Pos  limit = hints->contour_y_minima[limit_contour];
   3192 
   3193    FT_Int  contour;
   3194 
   3195 
   3196    for ( contour = 0; contour < hints->num_contours; contour++ )
   3197    {
   3198      FT_Pos  min_y = hints->contour_y_minima[contour];
   3199      FT_Pos  max_y = hints->contour_y_maxima[contour];
   3200 
   3201 
   3202      if ( min_y < max_y  &&
   3203           max_y <= limit )
   3204        af_touch_contour( hints, contour );
   3205    }
   3206  }
   3207 
   3208 
   3209  /* Stretch tilde vertically, if necessary, and return the height */
   3210  /* difference between the original and the stretched outline.    */
   3211  static FT_Pos
   3212  af_latin_stretch_top_tilde( AF_GlyphHints  hints,
   3213                              FT_Int         tilde_contour )
   3214  {
   3215    AF_Point  p           = hints->contours[tilde_contour];
   3216    AF_Point  first_point = p;
   3217 
   3218    FT_Pos  min_y = hints->contour_y_minima[tilde_contour];
   3219    FT_Pos  max_y = hints->contour_y_maxima[tilde_contour];
   3220 
   3221    FT_Pos   min_measurement   = FT_LONG_MAX;
   3222    FT_Bool  measurement_taken = FALSE;
   3223 
   3224    FT_Pos  height;
   3225    FT_Pos  extremum_threshold;
   3226    FT_Pos  target_height;
   3227 
   3228 
   3229    if ( min_y == max_y )
   3230      return 0;
   3231 
   3232    FT_TRACE4(( "af_latin_stretch_top_tilde: min y: %ld, max y: %ld\n",
   3233                min_y, max_y ));
   3234 
   3235    height             = SUB_LONG( max_y, min_y );
   3236    extremum_threshold = height / 8;    /* Value 8 is heuristic. */
   3237 
   3238    /* Find points that are local vertical round extrema, and which   */
   3239    /* do not coincide with the vertical extreme values (i.e., we     */
   3240    /* search for the 'other' wiggles in the tilde), then measure the */
   3241    /* distance to the vertical extreme values.  Try to find the one  */
   3242    /* with the smallest distance.                                    */
   3243    /*                                                                */
   3244    /* The algorithm only works for tilde shapes that don't deviate   */
   3245    /* from the standard shape too much.  In particular, the wiggles  */
   3246    /* must be round extrema.                                         */
   3247    do
   3248    {
   3249      p = p->next;
   3250 
   3251      if ( !( p->flags & AF_FLAG_CONTROL )          &&
   3252           p->prev->y == p->y && p->next->y == p->y &&
   3253           p->y != min_y && p->y != max_y           &&
   3254           p->prev->flags & AF_FLAG_CONTROL         &&
   3255           p->next->flags & AF_FLAG_CONTROL         )
   3256      {
   3257        /* This point could be a candidate.  Find the next and previous */
   3258        /* on-curve points, and make sure they are both either above or */
   3259        /* below the point, then make the measurement.                  */
   3260        AF_Point  prev_on = p->prev;
   3261        AF_Point  next_on = p->next;
   3262 
   3263        FT_Pos  measurement;
   3264 
   3265 
   3266        while ( prev_on->flags & AF_FLAG_CONTROL )
   3267          prev_on = prev_on->prev;
   3268        while ( next_on->flags & AF_FLAG_CONTROL )
   3269          next_on = next_on->next;
   3270 
   3271        if ( next_on->y > p->y && prev_on->y > p->y )
   3272          measurement = p->y - min_y;
   3273        else if ( next_on->y < p->y && prev_on->y < p->y )
   3274          measurement = max_y - p->y;
   3275        else
   3276          continue;
   3277 
   3278        /* Ignore hits that are too near to a vertical extremum. */
   3279        if ( measurement < extremum_threshold )
   3280          continue;
   3281 
   3282        if ( !measurement_taken || measurement < min_measurement )
   3283        {
   3284          measurement_taken = TRUE;
   3285          min_measurement   = measurement;
   3286        }
   3287      }
   3288 
   3289    } while ( p != first_point );
   3290 
   3291    if ( !measurement_taken )
   3292      min_measurement = 0;
   3293 
   3294    FT_TRACE4(( "af_latin_stretch_top_tilde: min measurement %ld\n",
   3295                min_measurement ));
   3296 
   3297    /* To preserve the stretched shape we prevent that the tilde */
   3298    /* gets auto-hinted; we do this for all contours equal or    */
   3299    /* above the vertical minimum of `tilde_contour`.            */
   3300    af_touch_top_contours( hints, tilde_contour );
   3301 
   3302    /* XXX This is an important element of the algorithm; */
   3303    /*     we need a description.                         */
   3304    target_height = min_measurement + 64;
   3305    if ( height >= target_height )
   3306      return 0;
   3307 
   3308    /* Do the scaling. */
   3309    p = first_point;
   3310    do
   3311    {
   3312      p     = p->next;
   3313      /* We adjust the height of the diacritic only, which means    */
   3314      /* we are never dealing with large numbers and can thus avoid */
   3315      /* `FT_MulFix`.                                               */
   3316      p->y  = ( ( p->y - min_y ) * target_height / height ) + min_y;
   3317 
   3318    } while ( p != first_point );
   3319 
   3320    return target_height - height;
   3321  }
   3322 
   3323 
   3324  static FT_Pos
   3325  af_latin_stretch_bottom_tilde( AF_GlyphHints  hints,
   3326                                 FT_Int         tilde_contour )
   3327  {
   3328    AF_Point  p           = hints->contours[tilde_contour];
   3329    AF_Point  first_point = p;
   3330 
   3331    FT_Pos  min_y = hints->contour_y_minima[tilde_contour];
   3332    FT_Pos  max_y = hints->contour_y_maxima[tilde_contour];
   3333 
   3334    FT_Pos   min_measurement   = FT_LONG_MAX;
   3335    FT_Bool  measurement_taken = FALSE;
   3336 
   3337    FT_Pos  height;
   3338    FT_Pos  extremum_threshold;
   3339    FT_Pos  target_height;
   3340 
   3341 
   3342    if ( min_y == max_y )
   3343      return 0;
   3344 
   3345    FT_TRACE4(( "af_latin_stretch_bottom_tilde: min y: %ld, max y: %ld\n",
   3346                min_y, max_y ));
   3347 
   3348    height             = SUB_LONG( max_y, min_y );
   3349    extremum_threshold = height / 8;
   3350 
   3351    do
   3352    {
   3353      p = p->next;
   3354 
   3355      if ( !( p->flags & AF_FLAG_CONTROL )          &&
   3356           p->prev->y == p->y && p->next->y == p->y &&
   3357           p->y != min_y && p->y != max_y           &&
   3358           p->prev->flags & AF_FLAG_CONTROL         &&
   3359           p->next->flags & AF_FLAG_CONTROL         )
   3360      {
   3361        AF_Point  prev_on = p->prev;
   3362        AF_Point  next_on = p->next;
   3363 
   3364        FT_Pos  measurement;
   3365 
   3366 
   3367        while ( prev_on->flags & AF_FLAG_CONTROL )
   3368          prev_on = prev_on->prev;
   3369        while ( next_on->flags & AF_FLAG_CONTROL )
   3370          next_on = next_on->next;
   3371 
   3372        if ( next_on->y > p->y && prev_on->y > p->y )
   3373          measurement = p->y - min_y;
   3374        else if ( next_on->y < p->y && prev_on->y < p->y )
   3375          measurement = max_y - p->y;
   3376        else
   3377          continue;
   3378 
   3379        if ( measurement < extremum_threshold )
   3380          continue;
   3381 
   3382        if ( !measurement_taken || measurement < min_measurement )
   3383        {
   3384          measurement_taken = TRUE;
   3385          min_measurement   = measurement;
   3386        }
   3387      }
   3388 
   3389    } while ( p != first_point );
   3390 
   3391    if ( !measurement_taken )
   3392      min_measurement = 0;
   3393 
   3394    FT_TRACE4(( "af_latin_stretch_bottom_tilde: min measurement %ld\n",
   3395                min_measurement ));
   3396 
   3397    af_touch_bottom_contours( hints, tilde_contour );
   3398 
   3399    target_height = min_measurement + 64;
   3400    if ( height >= target_height )
   3401      return 0;
   3402 
   3403    p = first_point;
   3404    do
   3405    {
   3406      p     = p->next;
   3407      p->y  = ( ( p->y - max_y ) * target_height / height ) + max_y;
   3408 
   3409    } while ( p != first_point );
   3410 
   3411    return target_height - height;
   3412  }
   3413 
   3414 
   3415  /*
   3416    As part of `af_latin_stretch_top_tilde`, normally all points in the
   3417    tilde are marked as touched, so the existing grid fitting will leave the
   3418    tilde misaligned with the grid.
   3419 
   3420    This function moves the tilde contour down to be grid-fitted.  We assume
   3421    that if moving the tilde down would cause it to touch or overlap another
   3422    countour, the vertical adjustment step will fix it.
   3423 
   3424    Because the vertical adjustment step comes after all other grid-fitting
   3425    steps, the top edge of the contour under the tilde is usually aligned
   3426    with a horizontal grid line.  The vertical gap enforced by the vertical
   3427    adjustment is exactly one pixel, so if the top edge of the contour below
   3428    the tilde is on a grid line, the resulting tilde contour will also be
   3429    grid-aligned.
   3430 
   3431    But in cases where the gap is already big enough so that the vertical
   3432    adjustment does nothing, this function ensures that even without the
   3433    intervention of the vertical adjustment step, the tilde will be
   3434    grid-aligned.
   3435 
   3436    Return the vertical alignment amount.
   3437  */
   3438  static FT_Pos
   3439  af_latin_align_top_tilde( AF_GlyphHints  hints,
   3440                            FT_Int         tilde_contour )
   3441  {
   3442    AF_Point  p           = hints->contours[tilde_contour];
   3443    AF_Point  first_point = p;
   3444 
   3445    FT_Pos  min_y = p->y;
   3446    FT_Pos  max_y = p->y;
   3447 
   3448    FT_Pos  min_y_rounded;
   3449    FT_Pos  delta;
   3450    FT_Pos  height;
   3451 
   3452 
   3453    /* Find vertical extrema of the (now stretched) tilde contour. */
   3454    do
   3455    {
   3456      p = p->next;
   3457      if ( p->y < min_y )
   3458        min_y = p->y;
   3459      if ( p->y > max_y )
   3460        max_y = p->y;
   3461 
   3462    } while ( p != first_point );
   3463 
   3464    /* Align bottom of the tilde to the grid. */
   3465    min_y_rounded = FT_PIX_ROUND( min_y );
   3466    delta         = min_y_rounded - min_y;
   3467    height        = max_y - min_y;
   3468 
   3469    /* If the tilde is less than 3 pixels tall, snap the center of it */
   3470    /* to the grid instead of the bottom to improve readability.      */
   3471    if ( height < 64 * 3 )
   3472      delta += ( FT_PIX_ROUND( height ) - height ) / 2;
   3473 
   3474    af_move_contour_vertically( first_point, delta );
   3475 
   3476    return delta;
   3477  }
   3478 
   3479 
   3480  static FT_Pos
   3481  af_latin_align_bottom_tilde( AF_GlyphHints  hints,
   3482                               FT_Int         tilde_contour )
   3483  {
   3484    AF_Point  p           = hints->contours[tilde_contour];
   3485    AF_Point  first_point = p;
   3486 
   3487    FT_Pos  min_y = p->y;
   3488    FT_Pos  max_y = p->y;
   3489 
   3490    FT_Pos  max_y_rounded;
   3491    FT_Pos  delta;
   3492    FT_Pos  height;
   3493 
   3494 
   3495    do
   3496    {
   3497      p = p->next;
   3498      if ( p->y < min_y )
   3499        min_y = p->y;
   3500      if ( p->y > max_y )
   3501        max_y = p->y;
   3502 
   3503    } while ( p != first_point );
   3504 
   3505    max_y_rounded = FT_PIX_ROUND( max_y );
   3506    delta         = max_y_rounded - max_y;
   3507    height        = max_y - min_y;
   3508 
   3509    if ( height < 64 * 3 )
   3510      delta -= ( FT_PIX_ROUND( height ) - height ) / 2;
   3511 
   3512    af_move_contour_vertically( first_point, delta );
   3513 
   3514    return delta;
   3515  }
   3516 
   3517 
   3518  /* Return 1 if the given contour overlaps horizontally with the bounding */
   3519  /* box of all other contours combined.  This is a helper for function    */
   3520  /* `af_glyph_hints_apply_vertical_separation_adjustments`.               */
   3521  static FT_Bool
   3522  af_check_contour_horizontal_overlap( AF_GlyphHints  hints,
   3523                                       FT_Int         contour_index )
   3524  {
   3525    FT_Pos  contour_max_x = FT_LONG_MIN;
   3526    FT_Pos  contour_min_x = FT_LONG_MAX;
   3527    FT_Pos  others_max_x  = FT_LONG_MIN;
   3528    FT_Pos  others_min_x  = FT_LONG_MAX;
   3529 
   3530    FT_Int  contour;
   3531 
   3532    FT_Bool  horizontal_overlap;
   3533 
   3534 
   3535    for ( contour = 0; contour < hints->num_contours; contour++ )
   3536    {
   3537      AF_Point  first_point = hints->contours[contour];
   3538      AF_Point  p           = first_point;
   3539 
   3540 
   3541      /* Ignore dimensionless contours (i.e., contours with only one or */
   3542      /* two points).                                                   */
   3543      if ( first_point->next->next == first_point )
   3544        continue;
   3545 
   3546      do
   3547      {
   3548        p = p->next;
   3549 
   3550        if ( contour == contour_index )
   3551        {
   3552          if ( p->x < contour_min_x )
   3553            contour_min_x = p->x;
   3554          if ( p->x > contour_max_x )
   3555            contour_max_x = p->x;
   3556        }
   3557        else
   3558        {
   3559          if ( p->x < others_min_x )
   3560            others_min_x = p->x;
   3561          if ( p->x > others_max_x )
   3562            others_max_x = p->x;
   3563        }
   3564      } while ( p != first_point );
   3565    }
   3566 
   3567    horizontal_overlap =
   3568      ( others_min_x <= contour_max_x && contour_max_x <= others_max_x ) ||
   3569      ( others_min_x <= contour_min_x && contour_min_x <= others_max_x ) ||
   3570      ( contour_max_x >= others_max_x && contour_min_x <= others_min_x );
   3571 
   3572    return horizontal_overlap;
   3573  }
   3574 
   3575 
   3576  static void
   3577  af_glyph_hints_apply_vertical_separation_adjustments(
   3578    AF_GlyphHints  hints,
   3579    AF_Dimension   dim,
   3580    FT_UInt        glyph_index,
   3581    FT_Pos         accent_height_limit,
   3582    FT_Hash        reverse_charmap )
   3583  {
   3584    FT_Bool  adjust_top       = FALSE;
   3585    FT_Bool  adjust_below_top = FALSE;
   3586 
   3587    FT_Bool  adjust_bottom       = FALSE;
   3588    FT_Bool  adjust_above_bottom = FALSE;
   3589 
   3590    size_t*    val;
   3591    FT_UInt32  adj_type = AF_ADJUST_NONE;
   3592 
   3593 
   3594    FT_TRACE4(( "Entering"
   3595                " af_glyph_hints_apply_vertical_separation_adjustments\n" ));
   3596 
   3597    if ( dim != AF_DIMENSION_VERT )
   3598      return;
   3599 
   3600    val = ft_hash_num_lookup( (FT_Int)glyph_index, reverse_charmap );
   3601    if ( val )
   3602    {
   3603      FT_UInt  codepoint = *val;
   3604 
   3605 
   3606      adj_type = af_adjustment_database_lookup( codepoint );
   3607 
   3608      if ( adj_type )
   3609      {
   3610        adjust_top       = !!( adj_type & AF_ADJUST_UP );
   3611        adjust_below_top = !!( adj_type & AF_ADJUST_UP2 );
   3612 
   3613        adjust_bottom       = !!( adj_type & AF_ADJUST_DOWN );
   3614        adjust_above_bottom = !!( adj_type & AF_ADJUST_DOWN2 );
   3615      }
   3616    }
   3617 
   3618    if ( ( ( adjust_top || adjust_bottom ) &&
   3619           hints->num_contours >= 2        )             ||
   3620         ( ( adjust_below_top || adjust_above_bottom ) &&
   3621           hints->num_contours >= 3                    ) )
   3622    {
   3623      /* Recompute vertical extrema, this time acting on already */
   3624      /* auto-hinted outlines.                                   */
   3625      af_compute_vertical_extrema( hints );
   3626    }
   3627 
   3628    if ( ( adjust_top && hints->num_contours >= 2 )       ||
   3629         ( adjust_below_top && hints->num_contours >= 3 ) )
   3630    {
   3631      FT_Int  high_contour;
   3632      FT_Pos  high_min_y;
   3633      FT_Pos  high_max_y;
   3634      FT_Pos  high_height;
   3635 
   3636      FT_Int  tilde_contour;
   3637      FT_Pos  tilde_min_y;
   3638      FT_Pos  tilde_max_y;
   3639      FT_Pos  tilde_height;
   3640 
   3641      FT_Int   contour;
   3642      FT_Bool  horizontal_overlap;
   3643 
   3644      FT_Pos  min_distance         = 64;
   3645      FT_Pos  adjustment_amount;
   3646      FT_Pos  calculated_amount;
   3647      FT_Pos  centering_adjustment = 0;
   3648      FT_Pos  pos;
   3649 
   3650      FT_Bool  is_top_tilde       = !!( adj_type & AF_ADJUST_TILDE_TOP );
   3651      FT_Bool  is_below_top_tilde = !!( adj_type & AF_ADJUST_TILDE_TOP2 );
   3652 
   3653 
   3654      FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
   3655                  "  Applying vertical adjustment: %s\n",
   3656                  adjust_top ? "AF_ADJUST_TOP" : "AF_ADJUST_TOP2" ));
   3657 
   3658      high_contour = adjust_below_top
   3659                       ? af_find_second_highest_contour( hints )
   3660                       : af_find_highest_contour( hints );
   3661 
   3662      /* Check for a horizontal overlap between the high contour and the */
   3663      /* rest.  If there is no overlap, do not adjust.                   */
   3664      horizontal_overlap =
   3665        af_check_contour_horizontal_overlap( hints, high_contour );
   3666      if ( !horizontal_overlap )
   3667      {
   3668        FT_TRACE4(( "    High contour does not horizontally overlap"
   3669                    " with other contours.\n"
   3670                    "    Skipping adjustment.\n" ));
   3671        return;
   3672      }
   3673 
   3674      high_min_y  = hints->contour_y_minima[high_contour];
   3675      high_max_y  = hints->contour_y_maxima[high_contour];
   3676      high_height = high_max_y - high_min_y;
   3677 
   3678      if ( high_height > accent_height_limit )
   3679      {
   3680        FT_TRACE4(( "    High contour height (%.2f) exceeds accent height"
   3681                    " limit (%.2f).\n"
   3682                    "    Skipping adjustment.\n",
   3683                    (double)high_height / 64,
   3684                    (double)accent_height_limit / 64 ));
   3685        return;
   3686      }
   3687 
   3688      /* If the difference between the vertical minimum of the high   */
   3689      /* contour and the vertical maximum of another contour is less  */
   3690      /* than a pixel, shift up the high contour to make the distance */
   3691      /* one pixel.                                                   */
   3692      for ( contour = 0; contour < hints->num_contours; contour++ )
   3693      {
   3694        FT_Pos  min_y;
   3695        FT_Pos  max_y;
   3696        FT_Pos  distance;
   3697 
   3698 
   3699        if ( contour == high_contour )
   3700          continue;
   3701 
   3702        min_y = hints->contour_y_minima[contour];
   3703        max_y = hints->contour_y_maxima[contour];
   3704 
   3705        /* We also check that the y minimum of the 'other' contour */
   3706        /* is below the high contour to avoid potential false hits */
   3707        /* with contours enclosed in the high one.                 */
   3708        distance = high_min_y - max_y;
   3709        if ( distance < 64           &&
   3710             distance < min_distance &&
   3711             min_y < high_min_y      )
   3712          min_distance = distance;
   3713      }
   3714 
   3715      adjustment_amount = 64 - min_distance;
   3716 
   3717      if ( is_top_tilde || is_below_top_tilde )
   3718      {
   3719        tilde_contour = adjust_top
   3720                          ? high_contour
   3721                          : ( is_below_top_tilde
   3722                                ? high_contour
   3723                                : af_find_highest_contour( hints ) );
   3724 
   3725        tilde_min_y  = hints->contour_y_minima[tilde_contour];
   3726        tilde_max_y  = hints->contour_y_maxima[tilde_contour];
   3727        tilde_height = tilde_max_y - tilde_min_y;
   3728 
   3729        /* The vertical separation adjustment potentially undoes a */
   3730        /* tilde center alignment.  If it would grid-align a tilde */
   3731        /* less than 3 pixels in height, shift additionally to     */
   3732        /* re-center the tilde.                                    */
   3733 
   3734        pos = high_min_y + adjustment_amount;
   3735        if ( adjust_below_top && is_top_tilde )
   3736          pos += high_height;
   3737 
   3738        if ( pos % 64 == 0 && tilde_height < 3 * 64 )
   3739        {
   3740          centering_adjustment = ( FT_PIX_ROUND( tilde_height ) -
   3741                                   tilde_height ) / 2;
   3742 
   3743          FT_TRACE4(( "    Additional tilde centering adjustment: %ld\n",
   3744                      centering_adjustment ));
   3745        }
   3746      }
   3747 
   3748      if ( ( adjust_top && is_top_tilde )             ||
   3749           ( adjust_below_top && is_below_top_tilde ) )
   3750        calculated_amount = adjustment_amount + centering_adjustment;
   3751      else
   3752        calculated_amount = adjustment_amount;
   3753 
   3754      /* allow a delta of 2/64px to handle rounding differences */
   3755      FT_TRACE4(( "    Calculated adjustment amount: %ld%s\n",
   3756                  calculated_amount,
   3757                  ( calculated_amount < -2                               ||
   3758                    ( adjustment_amount > 66 && calculated_amount > 66 ) )
   3759                      ? " (out of range [-2;66], not adjusting)" : "" ));
   3760 
   3761      if ( calculated_amount != 0                                 &&
   3762           calculated_amount >= -2                                &&
   3763           ( calculated_amount <= 66 || adjustment_amount <= 66 ) )
   3764      {
   3765        /* Value 8 is heuristic. */
   3766        FT_Pos  height_delta = high_height / 8;
   3767        FT_Pos  min_y_limit  = high_min_y - height_delta;
   3768 
   3769 
   3770        FT_TRACE4(( "    Pushing high contour %ld units up\n",
   3771                    calculated_amount ));
   3772 
   3773        /* While we use only a single contour (the 'high' one) for    */
   3774        /* computing `adjustment_amount`, we apply it to all contours */
   3775        /* that are (approximately) in the same vertical range or     */
   3776        /* higher.  This covers, for example, the inner contour of    */
   3777        /* the Czech ring accent or the second acute accent in the    */
   3778        /* Hungarian double acute accent.                             */
   3779        af_move_contours_up( hints, min_y_limit, adjustment_amount );
   3780 
   3781        if ( adjust_below_top && is_top_tilde )
   3782        {
   3783          FT_TRACE4(( "    Pushing top tilde %ld units up\n",
   3784                      centering_adjustment ));
   3785 
   3786          af_move_contours_up( hints,
   3787                               min_y_limit + high_height,
   3788                               centering_adjustment );
   3789        }
   3790      }
   3791    }
   3792 
   3793    if ( ( adjust_bottom && hints->num_contours >= 2 )       ||
   3794         ( adjust_above_bottom && hints->num_contours >= 3 ) )
   3795    {
   3796      FT_Int  low_contour;
   3797      FT_Pos  low_min_y;
   3798      FT_Pos  low_max_y;
   3799      FT_Pos  low_height;
   3800 
   3801      FT_Int  tilde_contour;
   3802      FT_Pos  tilde_min_y;
   3803      FT_Pos  tilde_max_y;
   3804      FT_Pos  tilde_height;
   3805 
   3806      FT_Int   contour;
   3807      FT_Bool  horizontal_overlap;
   3808 
   3809      FT_Pos  min_distance         = 64;
   3810      FT_Pos  adjustment_amount;
   3811      FT_Pos  calculated_amount;
   3812      FT_Pos  centering_adjustment = 0;
   3813      FT_Pos  pos;
   3814 
   3815      FT_Bool  is_bottom_tilde =
   3816                 !!( adj_type & AF_ADJUST_TILDE_BOTTOM );
   3817      FT_Bool  is_above_bottom_tilde =
   3818                 !!( adj_type & AF_ADJUST_TILDE_BOTTOM2 );
   3819 
   3820 
   3821      FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
   3822                  "  Applying vertical adjustment: %s\n",
   3823                  adjust_bottom ? "AF_ADJUST_DOWN": "AF_ADJUST_DOWN2" ));
   3824 
   3825      low_contour = adjust_above_bottom
   3826                      ? af_find_second_lowest_contour( hints )
   3827                      : af_find_lowest_contour( hints );
   3828 
   3829      horizontal_overlap =
   3830        af_check_contour_horizontal_overlap( hints, low_contour );
   3831      if ( !horizontal_overlap )
   3832      {
   3833        FT_TRACE4(( "    Low contour does not horizontally overlap"
   3834                    " with other contours.\n"
   3835                    "    Skipping adjustment.\n" ));
   3836        return;
   3837      }
   3838 
   3839      low_min_y  = hints->contour_y_minima[low_contour];
   3840      low_max_y  = hints->contour_y_maxima[low_contour];
   3841      low_height = low_max_y - low_min_y;
   3842 
   3843      if ( low_height > accent_height_limit )
   3844      {
   3845        FT_TRACE4(( "    Low contour height (%.2f) exceeds accent height"
   3846                    " limit (%.2f).\n"
   3847                    "    Skipping adjustment.\n",
   3848                    (double)low_height / 64,
   3849                    (double)accent_height_limit / 64 ));
   3850        return;
   3851      }
   3852 
   3853      for ( contour = 0; contour < hints->num_contours; contour++ )
   3854      {
   3855        FT_Pos  min_y;
   3856        FT_Pos  max_y;
   3857        FT_Pos  distance;
   3858 
   3859 
   3860        if ( contour == low_contour )
   3861          continue;
   3862 
   3863        min_y = hints->contour_y_minima[contour];
   3864        max_y = hints->contour_y_maxima[contour];
   3865 
   3866        distance = min_y - low_max_y;
   3867        if ( distance < 64           &&
   3868             distance < min_distance &&
   3869             max_y > low_max_y       )
   3870          min_distance = distance;
   3871      }
   3872 
   3873      adjustment_amount = 64 - min_distance;
   3874 
   3875      if ( is_bottom_tilde || is_above_bottom_tilde )
   3876      {
   3877        tilde_contour = adjust_bottom
   3878                          ? low_contour
   3879                          : ( is_above_bottom_tilde
   3880                                ? low_contour
   3881                                : af_find_lowest_contour( hints ) );
   3882 
   3883        tilde_min_y  = hints->contour_y_minima[tilde_contour];
   3884        tilde_max_y  = hints->contour_y_maxima[tilde_contour];
   3885        tilde_height = tilde_max_y - tilde_min_y;
   3886 
   3887        pos = low_max_y - adjustment_amount;
   3888        if ( adjust_above_bottom && is_bottom_tilde )
   3889          pos -= low_height;
   3890 
   3891        if ( pos % 64 == 0 && tilde_height < 3 * 64 )
   3892        {
   3893          centering_adjustment = ( FT_PIX_ROUND( tilde_height ) -
   3894                                   tilde_height ) / 2;
   3895 
   3896          FT_TRACE4(( "    Additional tilde centering adjustment: %ld\n",
   3897                      centering_adjustment ));
   3898        }
   3899      }
   3900 
   3901      if ( ( adjust_bottom && is_bottom_tilde )             ||
   3902           ( adjust_above_bottom && is_above_bottom_tilde ) )
   3903        calculated_amount = adjustment_amount + centering_adjustment;
   3904      else
   3905        calculated_amount = adjustment_amount;
   3906 
   3907      FT_TRACE4(( "    Calculated adjustment amount: %ld%s\n",
   3908                  calculated_amount,
   3909                  ( calculated_amount < -2                               ||
   3910                    ( adjustment_amount > 66 && calculated_amount > 66 ) )
   3911                      ? " (out of range [-2;66], not adjusting)" : "" ));
   3912 
   3913      if ( calculated_amount != 0                                 &&
   3914           calculated_amount >= -2                                &&
   3915           ( calculated_amount <= 66 || adjustment_amount <= 66 ) )
   3916      {
   3917        FT_Pos  height_delta = low_height / 8;
   3918        FT_Pos  max_y_limit  = low_max_y + height_delta;
   3919 
   3920 
   3921        FT_TRACE4(( "    Pushing low contour %ld units down\n",
   3922                    calculated_amount ));
   3923 
   3924        af_move_contours_down( hints, max_y_limit, adjustment_amount );
   3925 
   3926        if ( adjust_above_bottom && is_bottom_tilde )
   3927        {
   3928          FT_TRACE4(( "    Pushing bottom tilde %ld units down\n",
   3929                      centering_adjustment ));
   3930 
   3931          af_move_contours_down( hints,
   3932                                 max_y_limit - low_height,
   3933                                 centering_adjustment );
   3934        }
   3935      }
   3936    }
   3937 
   3938 #ifdef FT_DEBUG_LEVEL_TRACE
   3939    if ( !( ( ( adjust_top || adjust_bottom ) &&
   3940              hints->num_contours >= 2        )             ||
   3941            ( ( adjust_below_top || adjust_above_bottom ) &&
   3942              hints->num_contours >= 3                    ) ) )
   3943      FT_TRACE4(( "af_glyph_hints_apply_vertical_separation_adjustments:\n"
   3944                  "  No vertical adjustment applied\n" ));
   3945 #endif
   3946 
   3947    FT_TRACE4(( "Exiting"
   3948                " af_glyph_hints_apply_vertical_separation_adjustments\n" ));
   3949  }
   3950 
   3951 
   3952 #undef  FT_COMPONENT
   3953 #define FT_COMPONENT  aflatin
   3954 
   3955 
   3956  /* Compute the snapped width of a given stem, ignoring very thin ones. */
   3957  /* There is a lot of voodoo in this function; changing the hard-coded  */
   3958  /* parameters influence the whole hinting process.                     */
   3959 
   3960  static FT_Pos
   3961  af_latin_compute_stem_width( AF_GlyphHints  hints,
   3962                               AF_Dimension   dim,
   3963                               FT_Pos         width,
   3964                               FT_Pos         base_delta,
   3965                               FT_UInt        base_flags,
   3966                               FT_UInt        stem_flags )
   3967  {
   3968    AF_LatinMetrics  metrics  = (AF_LatinMetrics)hints->metrics;
   3969    AF_LatinAxis     axis     = &metrics->axis[dim];
   3970    FT_Pos           dist     = width;
   3971    FT_Int           sign     = 0;
   3972    FT_Int           vertical = ( dim == AF_DIMENSION_VERT );
   3973 
   3974 
   3975    if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
   3976         axis->extra_light                       )
   3977      return width;
   3978 
   3979    if ( dist < 0 )
   3980    {
   3981      dist = -width;
   3982      sign = 1;
   3983    }
   3984 
   3985    if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
   3986         ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
   3987    {
   3988      /* smooth hinting process: very lightly quantize the stem width */
   3989 
   3990      /* leave the widths of serifs alone */
   3991      if ( ( stem_flags & AF_EDGE_SERIF ) &&
   3992           vertical                       &&
   3993           ( dist < 3 * 64 )              )
   3994        goto Done_Width;
   3995 
   3996      else if ( base_flags & AF_EDGE_ROUND )
   3997      {
   3998        if ( dist < 80 )
   3999          dist = 64;
   4000      }
   4001      else if ( dist < 56 )
   4002        dist = 56;
   4003 
   4004      if ( axis->width_count > 0 )
   4005      {
   4006        FT_Pos  delta;
   4007 
   4008 
   4009        /* compare to standard width */
   4010        delta = dist - axis->widths[0].cur;
   4011 
   4012        if ( delta < 0 )
   4013          delta = -delta;
   4014 
   4015        if ( delta < 40 )
   4016        {
   4017          dist = axis->widths[0].cur;
   4018          if ( dist < 48 )
   4019            dist = 48;
   4020 
   4021          goto Done_Width;
   4022        }
   4023 
   4024        if ( dist < 3 * 64 )
   4025        {
   4026          delta  = dist & 63;
   4027          dist  &= -64;
   4028 
   4029          if ( delta < 10 )
   4030            dist += delta;
   4031 
   4032          else if ( delta < 32 )
   4033            dist += 10;
   4034 
   4035          else if ( delta < 54 )
   4036            dist += 54;
   4037 
   4038          else
   4039            dist += delta;
   4040        }
   4041        else
   4042        {
   4043          /* A stem's end position depends on two values: the start        */
   4044          /* position and the stem length.  The former gets usually        */
   4045          /* rounded to the grid, while the latter gets rounded also if it */
   4046          /* exceeds a certain length (see below in this function).  This  */
   4047          /* `double rounding' can lead to a great difference to the       */
   4048          /* original, unhinted position; this normally doesn't matter for */
   4049          /* large PPEM values, but for small sizes it can easily make     */
   4050          /* outlines collide.  For this reason, we adjust the stem length */
   4051          /* by a small amount depending on the PPEM value in case the     */
   4052          /* former and latter rounding both point into the same           */
   4053          /* direction.                                                    */
   4054 
   4055          FT_Pos  bdelta = 0;
   4056 
   4057 
   4058          if ( ( ( width > 0 ) && ( base_delta > 0 ) ) ||
   4059               ( ( width < 0 ) && ( base_delta < 0 ) ) )
   4060          {
   4061            FT_UInt  ppem = metrics->root.scaler.face->size->metrics.x_ppem;
   4062 
   4063 
   4064            if ( ppem < 10 )
   4065              bdelta = base_delta;
   4066            else if ( ppem < 30 )
   4067              bdelta = ( base_delta * (FT_Pos)( 30 - ppem ) ) / 20;
   4068 
   4069            if ( bdelta < 0 )
   4070              bdelta = -bdelta;
   4071          }
   4072 
   4073          dist = ( dist - bdelta + 32 ) & ~63;
   4074        }
   4075      }
   4076    }
   4077    else
   4078    {
   4079      /* strong hinting process: snap the stem width to integer pixels */
   4080 
   4081      FT_Pos  org_dist = dist;
   4082 
   4083 
   4084      dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
   4085 
   4086      if ( vertical )
   4087      {
   4088        /* in the case of vertical hinting, always round */
   4089        /* the stem heights to integer pixels            */
   4090 
   4091        if ( dist >= 64 )
   4092          dist = ( dist + 16 ) & ~63;
   4093        else
   4094          dist = 64;
   4095      }
   4096      else
   4097      {
   4098        if ( AF_LATIN_HINTS_DO_MONO( hints ) )
   4099        {
   4100          /* monochrome horizontal hinting: snap widths to integer pixels */
   4101          /* with a different threshold                                   */
   4102 
   4103          if ( dist < 64 )
   4104            dist = 64;
   4105          else
   4106            dist = ( dist + 32 ) & ~63;
   4107        }
   4108        else
   4109        {
   4110          /* for horizontal anti-aliased hinting, we adopt a more subtle */
   4111          /* approach: we strengthen small stems, round stems whose size */
   4112          /* is between 1 and 2 pixels to an integer, otherwise nothing  */
   4113 
   4114          if ( dist < 48 )
   4115            dist = ( dist + 64 ) >> 1;
   4116 
   4117          else if ( dist < 128 )
   4118          {
   4119            /* We only round to an integer width if the corresponding */
   4120            /* distortion is less than 1/4 pixel.  Otherwise this     */
   4121            /* makes everything worse since the diagonals, which are  */
   4122            /* not hinted, appear a lot bolder or thinner than the    */
   4123            /* vertical stems.                                        */
   4124 
   4125            FT_Pos  delta;
   4126 
   4127 
   4128            dist = ( dist + 22 ) & ~63;
   4129            delta = dist - org_dist;
   4130            if ( delta < 0 )
   4131              delta = -delta;
   4132 
   4133            if ( delta >= 16 )
   4134            {
   4135              dist = org_dist;
   4136              if ( dist < 48 )
   4137                dist = ( dist + 64 ) >> 1;
   4138            }
   4139          }
   4140          else
   4141            /* round otherwise to prevent color fringes in LCD mode */
   4142            dist = ( dist + 32 ) & ~63;
   4143        }
   4144      }
   4145    }
   4146 
   4147  Done_Width:
   4148    if ( sign )
   4149      dist = -dist;
   4150 
   4151    return dist;
   4152  }
   4153 
   4154 
   4155  /* Align one stem edge relative to the previous stem edge. */
   4156 
   4157  static void
   4158  af_latin_align_linked_edge( AF_GlyphHints  hints,
   4159                              AF_Dimension   dim,
   4160                              AF_Edge        base_edge,
   4161                              AF_Edge        stem_edge )
   4162  {
   4163    FT_Pos  dist, base_delta;
   4164    FT_Pos  fitted_width;
   4165 
   4166 
   4167    dist       = stem_edge->opos - base_edge->opos;
   4168    base_delta = base_edge->pos - base_edge->opos;
   4169 
   4170    fitted_width = af_latin_compute_stem_width( hints, dim,
   4171                                                dist, base_delta,
   4172                                                base_edge->flags,
   4173                                                stem_edge->flags );
   4174 
   4175 
   4176    stem_edge->pos = base_edge->pos + fitted_width;
   4177 
   4178    FT_TRACE5(( "  LINK: edge %td (opos=%.2f) linked to %.2f,"
   4179                " dist was %.2f, now %.2f\n",
   4180                stem_edge - hints->axis[dim].edges,
   4181                (double)stem_edge->opos / 64, (double)stem_edge->pos / 64,
   4182                (double)dist / 64, (double)fitted_width / 64 ));
   4183  }
   4184 
   4185 
   4186  /* Shift the coordinates of the `serif' edge by the same amount */
   4187  /* as the corresponding `base' edge has been moved already.     */
   4188 
   4189  static void
   4190  af_latin_align_serif_edge( AF_GlyphHints  hints,
   4191                             AF_Edge        base,
   4192                             AF_Edge        serif )
   4193  {
   4194    FT_UNUSED( hints );
   4195 
   4196    serif->pos = base->pos + ( serif->opos - base->opos );
   4197  }
   4198 
   4199 
   4200  /*************************************************************************/
   4201  /*************************************************************************/
   4202  /*************************************************************************/
   4203  /****                                                                 ****/
   4204  /****                    E D G E   H I N T I N G                      ****/
   4205  /****                                                                 ****/
   4206  /*************************************************************************/
   4207  /*************************************************************************/
   4208  /*************************************************************************/
   4209 
   4210 
   4211  /* The main grid-fitting routine. */
   4212 
   4213  static void
   4214  af_latin_hint_edges( AF_GlyphHints  hints,
   4215                       AF_Dimension   dim )
   4216  {
   4217    AF_AxisHints  axis = &hints->axis[dim];
   4218 
   4219    AF_Edge     edges      = axis->edges;
   4220    AF_Edge     edge_limit = FT_OFFSET( edges, axis->num_edges );
   4221    AF_Edge     edge;
   4222    FT_PtrDist  n_edges;
   4223 
   4224    AF_Edge  anchor             = NULL;
   4225    FT_Bool  has_non_stem_edges = 0;
   4226 
   4227    AF_StyleClass   style_class  = hints->metrics->style_class;
   4228    AF_ScriptClass  script_class = af_script_classes[style_class->script];
   4229 
   4230    FT_Bool  top_to_bottom_hinting = 0;
   4231 
   4232 #ifdef FT_DEBUG_LEVEL_TRACE
   4233    FT_UInt  num_actions = 0;
   4234 #endif
   4235 
   4236 
   4237    FT_TRACE5(( "latin %s edge hinting (style `%s')\n",
   4238                dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
   4239                af_style_names[hints->metrics->style_class->style] ));
   4240 
   4241    if ( dim == AF_DIMENSION_VERT )
   4242      top_to_bottom_hinting = script_class->top_to_bottom_hinting;
   4243 
   4244    /* we begin by aligning all stems relative to the blue zone */
   4245    /* if needed -- that's only for horizontal edges            */
   4246 
   4247    if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
   4248    {
   4249      for ( edge = edges; edge < edge_limit; edge++ )
   4250      {
   4251        AF_Width  blue;
   4252        AF_Edge   edge1, edge2; /* these edges form the stem to check */
   4253 
   4254 
   4255        if ( edge->flags & AF_EDGE_DONE )
   4256          continue;
   4257 
   4258        edge1 = NULL;
   4259        edge2 = edge->link;
   4260 
   4261        /*
   4262         * If a stem contains both a neutral and a non-neutral blue zone,
   4263         * skip the neutral one.  Otherwise, outlines with different
   4264         * directions might be incorrectly aligned at the same vertical
   4265         * position.
   4266         *
   4267         * If we have two neutral blue zones, skip one of them.
   4268         *
   4269         */
   4270        if ( edge->blue_edge && edge2 && edge2->blue_edge )
   4271        {
   4272          FT_Byte  neutral  = edge->flags  & AF_EDGE_NEUTRAL;
   4273          FT_Byte  neutral2 = edge2->flags & AF_EDGE_NEUTRAL;
   4274 
   4275 
   4276          if ( neutral2 )
   4277          {
   4278            edge2->blue_edge = NULL;
   4279            edge2->flags    &= ~AF_EDGE_NEUTRAL;
   4280          }
   4281          else if ( neutral )
   4282          {
   4283            edge->blue_edge = NULL;
   4284            edge->flags    &= ~AF_EDGE_NEUTRAL;
   4285          }
   4286        }
   4287 
   4288        blue = edge->blue_edge;
   4289        if ( blue )
   4290          edge1 = edge;
   4291 
   4292        /* flip edges if the other edge is aligned to a blue zone */
   4293        else if ( edge2 && edge2->blue_edge )
   4294        {
   4295          blue  = edge2->blue_edge;
   4296          edge1 = edge2;
   4297          edge2 = edge;
   4298        }
   4299 
   4300        if ( !edge1 )
   4301          continue;
   4302 
   4303 #ifdef FT_DEBUG_LEVEL_TRACE
   4304        if ( !anchor )
   4305          FT_TRACE5(( "  BLUE_ANCHOR: edge %td (opos=%.2f) snapped to %.2f,"
   4306                      " was %.2f (anchor=edge %td)\n",
   4307                      edge1 - edges,
   4308                      (double)edge1->opos / 64, (double)blue->fit / 64,
   4309                      (double)edge1->pos / 64, edge - edges ));
   4310        else
   4311          FT_TRACE5(( "  BLUE: edge %td (opos=%.2f) snapped to %.2f,"
   4312                      " was %.2f\n",
   4313                      edge1 - edges,
   4314                      (double)edge1->opos / 64, (double)blue->fit / 64,
   4315                      (double)edge1->pos / 64 ));
   4316 
   4317        num_actions++;
   4318 #endif
   4319 
   4320        edge1->pos    = blue->fit;
   4321        edge1->flags |= AF_EDGE_DONE;
   4322 
   4323        if ( edge2 && !edge2->blue_edge )
   4324        {
   4325          af_latin_align_linked_edge( hints, dim, edge1, edge2 );
   4326          edge2->flags |= AF_EDGE_DONE;
   4327 
   4328 #ifdef FT_DEBUG_LEVEL_TRACE
   4329          num_actions++;
   4330 #endif
   4331        }
   4332 
   4333        if ( !anchor )
   4334          anchor = edge;
   4335      }
   4336    }
   4337 
   4338    /* now we align all other stem edges, trying to maintain the */
   4339    /* relative order of stems in the glyph                      */
   4340    for ( edge = edges; edge < edge_limit; edge++ )
   4341    {
   4342      AF_Edge  edge2;
   4343 
   4344 
   4345      if ( edge->flags & AF_EDGE_DONE )
   4346        continue;
   4347 
   4348      /* skip all non-stem edges */
   4349      edge2 = edge->link;
   4350      if ( !edge2 )
   4351      {
   4352        has_non_stem_edges = TRUE;
   4353        continue;
   4354      }
   4355 
   4356      /* now align the stem */
   4357 
   4358      /* this should not happen, but it's better to be safe */
   4359      if ( edge2->blue_edge )
   4360      {
   4361        FT_TRACE5(( "  ASSERTION FAILED for edge %td\n", edge2 - edges ));
   4362 
   4363        af_latin_align_linked_edge( hints, dim, edge2, edge );
   4364        edge->flags |= AF_EDGE_DONE;
   4365 
   4366 #ifdef FT_DEBUG_LEVEL_TRACE
   4367        num_actions++;
   4368 #endif
   4369        continue;
   4370      }
   4371 
   4372      if ( !anchor )
   4373      {
   4374        /* if we reach this if clause, no stem has been aligned yet */
   4375 
   4376        FT_Pos  org_len, org_center, cur_len;
   4377        FT_Pos  cur_pos1, error1, error2, u_off, d_off;
   4378 
   4379 
   4380        org_len = edge2->opos - edge->opos;
   4381        cur_len = af_latin_compute_stem_width( hints, dim,
   4382                                               org_len, 0,
   4383                                               edge->flags,
   4384                                               edge2->flags );
   4385 
   4386        /* some voodoo to specially round edges for small stem widths; */
   4387        /* the idea is to align the center of a stem, then shifting    */
   4388        /* the stem edges to suitable positions                        */
   4389        if ( cur_len <= 64 )
   4390        {
   4391          /* width <= 1px */
   4392          u_off = 32;
   4393          d_off = 32;
   4394        }
   4395        else
   4396        {
   4397          /* 1px < width < 1.5px */
   4398          u_off = 38;
   4399          d_off = 26;
   4400        }
   4401 
   4402        if ( cur_len < 96 )
   4403        {
   4404          org_center = edge->opos + ( org_len >> 1 );
   4405          cur_pos1   = FT_PIX_ROUND( org_center );
   4406 
   4407          error1 = org_center - ( cur_pos1 - u_off );
   4408          if ( error1 < 0 )
   4409            error1 = -error1;
   4410 
   4411          error2 = org_center - ( cur_pos1 + d_off );
   4412          if ( error2 < 0 )
   4413            error2 = -error2;
   4414 
   4415          if ( error1 < error2 )
   4416            cur_pos1 -= u_off;
   4417          else
   4418            cur_pos1 += d_off;
   4419 
   4420          edge->pos  = cur_pos1 - cur_len / 2;
   4421          edge2->pos = edge->pos + cur_len;
   4422        }
   4423        else
   4424          edge->pos = FT_PIX_ROUND( edge->opos );
   4425 
   4426        anchor       = edge;
   4427        edge->flags |= AF_EDGE_DONE;
   4428 
   4429        FT_TRACE5(( "  ANCHOR: edge %td (opos=%.2f) and %td (opos=%.2f)"
   4430                    " snapped to %.2f and %.2f\n",
   4431                    edge - edges, (double)edge->opos / 64,
   4432                    edge2 - edges, (double)edge2->opos / 64,
   4433                    (double)edge->pos / 64, (double)edge2->pos / 64 ));
   4434 
   4435        af_latin_align_linked_edge( hints, dim, edge, edge2 );
   4436 
   4437 #ifdef FT_DEBUG_LEVEL_TRACE
   4438        num_actions += 2;
   4439 #endif
   4440      }
   4441      else
   4442      {
   4443        FT_Pos  org_pos, org_len, org_center, cur_len;
   4444        FT_Pos  cur_pos1, cur_pos2, delta1, delta2;
   4445 
   4446 
   4447        org_pos    = anchor->pos + ( edge->opos - anchor->opos );
   4448        org_len    = edge2->opos - edge->opos;
   4449        org_center = org_pos + ( org_len >> 1 );
   4450 
   4451        cur_len = af_latin_compute_stem_width( hints, dim,
   4452                                               org_len, 0,
   4453                                               edge->flags,
   4454                                               edge2->flags );
   4455 
   4456        if ( edge2->flags & AF_EDGE_DONE )
   4457        {
   4458          FT_TRACE5(( "  ADJUST: edge %td (pos=%.2f) moved to %.2f\n",
   4459                      edge - edges, (double)edge->pos / 64,
   4460                      (double)( edge2->pos - cur_len ) / 64 ));
   4461 
   4462          edge->pos = edge2->pos - cur_len;
   4463        }
   4464 
   4465        else if ( cur_len < 96 )
   4466        {
   4467          FT_Pos  u_off, d_off;
   4468 
   4469 
   4470          cur_pos1 = FT_PIX_ROUND( org_center );
   4471 
   4472          if ( cur_len <= 64 )
   4473          {
   4474            u_off = 32;
   4475            d_off = 32;
   4476          }
   4477          else
   4478          {
   4479            u_off = 38;
   4480            d_off = 26;
   4481          }
   4482 
   4483          delta1 = org_center - ( cur_pos1 - u_off );
   4484          if ( delta1 < 0 )
   4485            delta1 = -delta1;
   4486 
   4487          delta2 = org_center - ( cur_pos1 + d_off );
   4488          if ( delta2 < 0 )
   4489            delta2 = -delta2;
   4490 
   4491          if ( delta1 < delta2 )
   4492            cur_pos1 -= u_off;
   4493          else
   4494            cur_pos1 += d_off;
   4495 
   4496          edge->pos  = cur_pos1 - cur_len / 2;
   4497          edge2->pos = cur_pos1 + cur_len / 2;
   4498 
   4499          FT_TRACE5(( "  STEM: edge %td (opos=%.2f) linked to %td (opos=%.2f)"
   4500                      " snapped to %.2f and %.2f\n",
   4501                      edge - edges, (double)edge->opos / 64,
   4502                      edge2 - edges, (double)edge2->opos / 64,
   4503                      (double)edge->pos / 64, (double)edge2->pos / 64 ));
   4504        }
   4505 
   4506        else
   4507        {
   4508          org_pos    = anchor->pos + ( edge->opos - anchor->opos );
   4509          org_len    = edge2->opos - edge->opos;
   4510          org_center = org_pos + ( org_len >> 1 );
   4511 
   4512          cur_len    = af_latin_compute_stem_width( hints, dim,
   4513                                                    org_len, 0,
   4514                                                    edge->flags,
   4515                                                    edge2->flags );
   4516 
   4517          cur_pos1 = FT_PIX_ROUND( org_pos );
   4518          delta1   = cur_pos1 + ( cur_len >> 1 ) - org_center;
   4519          if ( delta1 < 0 )
   4520            delta1 = -delta1;
   4521 
   4522          cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
   4523          delta2   = cur_pos2 + ( cur_len >> 1 ) - org_center;
   4524          if ( delta2 < 0 )
   4525            delta2 = -delta2;
   4526 
   4527          edge->pos  = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
   4528          edge2->pos = edge->pos + cur_len;
   4529 
   4530          FT_TRACE5(( "  STEM: edge %td (opos=%.2f) linked to %td (opos=%.2f)"
   4531                      " snapped to %.2f and %.2f\n",
   4532                      edge - edges, (double)edge->opos / 64,
   4533                      edge2 - edges, (double)edge2->opos / 64,
   4534                      (double)edge->pos / 64, (double)edge2->pos / 64 ));
   4535        }
   4536 
   4537 #ifdef FT_DEBUG_LEVEL_TRACE
   4538        num_actions++;
   4539 #endif
   4540 
   4541        edge->flags  |= AF_EDGE_DONE;
   4542        edge2->flags |= AF_EDGE_DONE;
   4543 
   4544        if ( edge > edges                                             &&
   4545             ( top_to_bottom_hinting ? ( edge->pos > edge[-1].pos )
   4546                                     : ( edge->pos < edge[-1].pos ) ) )
   4547        {
   4548          /* don't move if stem would (almost) disappear otherwise; */
   4549          /* the ad-hoc value 16 corresponds to 1/4px               */
   4550          if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 )
   4551          {
   4552 #ifdef FT_DEBUG_LEVEL_TRACE
   4553            FT_TRACE5(( "  BOUND: edge %td (pos=%.2f) moved to %.2f\n",
   4554                        edge - edges,
   4555                        (double)edge->pos / 64,
   4556                        (double)edge[-1].pos / 64 ));
   4557 
   4558            num_actions++;
   4559 #endif
   4560 
   4561            edge->pos = edge[-1].pos;
   4562          }
   4563        }
   4564      }
   4565    }
   4566 
   4567    /* make sure that lowercase m's maintain their symmetry */
   4568 
   4569    /* In general, lowercase m's have six vertical edges if they are sans */
   4570    /* serif, or twelve if they are with serifs.  This implementation is  */
   4571    /* based on that assumption, and seems to work very well with most    */
   4572    /* faces.  However, if for a certain face this assumption is not      */
   4573    /* true, the m is just rendered like before.  In addition, any stem   */
   4574    /* correction will only be applied to symmetrical glyphs (even if the */
   4575    /* glyph is not an m), so the potential for unwanted distortion is    */
   4576    /* relatively low.                                                    */
   4577 
   4578    /* We don't handle horizontal edges since we can't easily assure that */
   4579    /* the third (lowest) stem aligns with the base line; it might end up */
   4580    /* one pixel higher or lower.                                         */
   4581 
   4582    n_edges = edge_limit - edges;
   4583    if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
   4584    {
   4585      AF_Edge  edge1, edge2, edge3;
   4586      FT_Pos   dist1, dist2, span, delta;
   4587 
   4588 
   4589      if ( n_edges == 6 )
   4590      {
   4591        edge1 = edges;
   4592        edge2 = edges + 2;
   4593        edge3 = edges + 4;
   4594      }
   4595      else
   4596      {
   4597        edge1 = edges + 1;
   4598        edge2 = edges + 5;
   4599        edge3 = edges + 9;
   4600      }
   4601 
   4602      dist1 = edge2->opos - edge1->opos;
   4603      dist2 = edge3->opos - edge2->opos;
   4604 
   4605      span = dist1 - dist2;
   4606      if ( span < 0 )
   4607        span = -span;
   4608 
   4609      if ( span < 8 )
   4610      {
   4611        delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
   4612        edge3->pos -= delta;
   4613        if ( edge3->link )
   4614          edge3->link->pos -= delta;
   4615 
   4616        /* move the serifs along with the stem */
   4617        if ( n_edges == 12 )
   4618        {
   4619          ( edges + 8 )->pos -= delta;
   4620          ( edges + 11 )->pos -= delta;
   4621        }
   4622 
   4623        edge3->flags |= AF_EDGE_DONE;
   4624        if ( edge3->link )
   4625          edge3->link->flags |= AF_EDGE_DONE;
   4626      }
   4627    }
   4628 
   4629    if ( has_non_stem_edges || !anchor )
   4630    {
   4631      /*
   4632       * now hint the remaining edges (serifs and single) in order
   4633       * to complete our processing
   4634       */
   4635      for ( edge = edges; edge < edge_limit; edge++ )
   4636      {
   4637        FT_Pos  delta;
   4638 
   4639 
   4640        if ( edge->flags & AF_EDGE_DONE )
   4641          continue;
   4642 
   4643        delta = 1000;
   4644 
   4645        if ( edge->serif )
   4646        {
   4647          AF_Edge  e, top, bottom;
   4648          FT_Pos   min_pos, max_pos;
   4649 
   4650 
   4651          /* Check whether we have a real serif -- if there are  */
   4652          /* other edges with overlapping (or enclosed) segments */
   4653          /* between the primary and serif edge, we have not.    */
   4654          /*                                                     */
   4655          /* Such a situation might happen if an accent is very  */
   4656          /* near to its base glyph (for example, Vietnamese     */
   4657          /* uppercase letters with two accents in `arial.ttf`), */
   4658          /* and the segment detection algorithm classifies the  */
   4659          /* top of the accent incorrectly as a serif.           */
   4660          delta = edge->serif->opos - edge->opos;
   4661          if ( delta < 0 )
   4662          {
   4663            delta = -delta;
   4664 
   4665            top    = edge;
   4666            bottom = edge->serif;
   4667          }
   4668          else
   4669          {
   4670            top    = edge->serif;
   4671            bottom = edge;
   4672          }
   4673 
   4674          if ( delta < 64 + 32 )
   4675          {
   4676            /* take care of outline orientation while computing extrema */
   4677            min_pos = FT_MIN( FT_MIN( FT_MIN( top->first->first->v,
   4678                                              top->first->last->v ),
   4679                                      FT_MIN( top->last->first->v,
   4680                                              top->last->last->v ) ),
   4681                              FT_MIN( FT_MIN( bottom->first->first->v,
   4682                                              bottom->first->last->v ),
   4683                                      FT_MIN( bottom->last->first->v,
   4684                                              bottom->last->last->v ) ) );
   4685            max_pos = FT_MAX( FT_MAX( FT_MAX( top->first->first->v,
   4686                                              top->first->last->v ),
   4687                                      FT_MAX( top->last->first->v,
   4688                                              top->last->last->v ) ),
   4689                              FT_MAX( FT_MAX( bottom->first->first->v,
   4690                                              bottom->first->last->v ),
   4691                                      FT_MAX( bottom->last->first->v,
   4692                                              bottom->last->last->v ) ) );
   4693 
   4694            for ( e = bottom + 1; e < top; e++ )
   4695            {
   4696              FT_Pos  e_min = FT_MIN( FT_MIN( e->first->first->v,
   4697                                              e->first->last->v ),
   4698                                      FT_MIN( e->last->first->v,
   4699                                              e->last->last->v ) );
   4700              FT_Pos  e_max = FT_MAX( FT_MAX( e->first->first->v,
   4701                                              e->first->last->v ),
   4702                                      FT_MAX( e->last->first->v,
   4703                                              e->last->last->v ) );
   4704 
   4705              if ( !( ( e_min < min_pos && e_max < min_pos ) ||
   4706                      ( e_min > max_pos && e_max > max_pos ) ) )
   4707              {
   4708                delta = 1000;  /* not a real serif */
   4709                break;
   4710              }
   4711            }
   4712 
   4713            if ( delta == 1000 )
   4714              continue;
   4715          }
   4716        }
   4717 
   4718        if ( delta < 64 + 16 )
   4719        {
   4720          af_latin_align_serif_edge( hints, edge->serif, edge );
   4721          FT_TRACE5(( "  SERIF: edge %td (opos=%.2f) serif to %td (opos=%.2f)"
   4722                      " aligned to %.2f\n",
   4723                      edge - edges, (double)edge->opos / 64,
   4724                      edge->serif - edges, (double)edge->serif->opos / 64,
   4725                      (double)edge->pos / 64 ));
   4726        }
   4727        else if ( !anchor )
   4728        {
   4729          edge->pos = FT_PIX_ROUND( edge->opos );
   4730          anchor    = edge;
   4731          FT_TRACE5(( "  SERIF_ANCHOR: edge %td (opos=%.2f)"
   4732                      " snapped to %.2f\n",
   4733                      edge - edges,
   4734                      (double)edge->opos / 64, (double)edge->pos / 64 ));
   4735        }
   4736        else
   4737        {
   4738          AF_Edge  before, after;
   4739 
   4740 
   4741          for ( before = edge - 1; before >= edges; before-- )
   4742            if ( before->flags & AF_EDGE_DONE )
   4743              break;
   4744 
   4745          for ( after = edge + 1; after < edge_limit; after++ )
   4746            if ( after->flags & AF_EDGE_DONE )
   4747              break;
   4748 
   4749          if ( before >= edges && before < edge   &&
   4750               after < edge_limit && after > edge )
   4751          {
   4752            if ( after->opos == before->opos )
   4753              edge->pos = before->pos;
   4754            else
   4755              edge->pos = before->pos +
   4756                          FT_MulDiv( edge->opos - before->opos,
   4757                                     after->pos - before->pos,
   4758                                     after->opos - before->opos );
   4759 
   4760            FT_TRACE5(( "  SERIF_LINK1: edge %td (opos=%.2f) snapped to %.2f"
   4761                        " from %td (opos=%.2f)\n",
   4762                        edge - edges, (double)edge->opos / 64,
   4763                        (double)edge->pos / 64,
   4764                        before - edges, (double)before->opos / 64 ));
   4765          }
   4766          else
   4767          {
   4768            edge->pos = anchor->pos +
   4769                        ( ( edge->opos - anchor->opos + 16 ) & ~31 );
   4770            FT_TRACE5(( "  SERIF_LINK2: edge %td (opos=%.2f)"
   4771                        " snapped to %.2f\n",
   4772                        edge - edges,
   4773                        (double)edge->opos / 64, (double)edge->pos / 64 ));
   4774          }
   4775        }
   4776 
   4777 #ifdef FT_DEBUG_LEVEL_TRACE
   4778        num_actions++;
   4779 #endif
   4780        edge->flags |= AF_EDGE_DONE;
   4781 
   4782        if ( edge > edges                                             &&
   4783             ( top_to_bottom_hinting ? ( edge->pos > edge[-1].pos )
   4784                                     : ( edge->pos < edge[-1].pos ) ) )
   4785        {
   4786          /* don't move if stem would (almost) disappear otherwise; */
   4787          /* the ad-hoc value 16 corresponds to 1/4px               */
   4788          if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 )
   4789          {
   4790 #ifdef FT_DEBUG_LEVEL_TRACE
   4791            FT_TRACE5(( "  BOUND: edge %td (pos=%.2f) moved to %.2f\n",
   4792                        edge - edges,
   4793                        (double)edge->pos / 64,
   4794                        (double)edge[-1].pos / 64 ));
   4795 
   4796            num_actions++;
   4797 #endif
   4798            edge->pos = edge[-1].pos;
   4799          }
   4800        }
   4801 
   4802        if ( edge + 1 < edge_limit                                   &&
   4803             edge[1].flags & AF_EDGE_DONE                            &&
   4804             ( top_to_bottom_hinting ? ( edge->pos < edge[1].pos )
   4805                                     : ( edge->pos > edge[1].pos ) ) )
   4806        {
   4807          /* don't move if stem would (almost) disappear otherwise; */
   4808          /* the ad-hoc value 16 corresponds to 1/4px               */
   4809          if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 )
   4810          {
   4811 #ifdef FT_DEBUG_LEVEL_TRACE
   4812            FT_TRACE5(( "  BOUND: edge %td (pos=%.2f) moved to %.2f\n",
   4813                        edge - edges,
   4814                        (double)edge->pos / 64,
   4815                        (double)edge[1].pos / 64 ));
   4816 
   4817            num_actions++;
   4818 #endif
   4819 
   4820            edge->pos = edge[1].pos;
   4821          }
   4822        }
   4823      }
   4824    }
   4825 
   4826 #ifdef FT_DEBUG_LEVEL_TRACE
   4827    if ( !num_actions )
   4828      FT_TRACE5(( "  (none)\n" ));
   4829    FT_TRACE5(( "\n" ));
   4830 #endif
   4831  }
   4832 
   4833 
   4834  /* Apply the complete hinting algorithm to a latin glyph. */
   4835 
   4836  static FT_Error
   4837  af_latin_hints_apply( FT_UInt          glyph_index,
   4838                        AF_GlyphHints    hints,
   4839                        FT_Outline*      outline,
   4840                        AF_StyleMetrics  metrics_ )    /* AF_LatinMetrics */
   4841  {
   4842    AF_LatinMetrics  metrics = (AF_LatinMetrics)metrics_;
   4843 
   4844    FT_Error  error;
   4845    int       dim;
   4846 
   4847    AF_LatinAxis  axis;
   4848 
   4849    FT_Pos  accent_height_limit = 0;
   4850 
   4851 
   4852    error = af_glyph_hints_reload( hints, outline );
   4853    if ( error )
   4854      goto Exit;
   4855 
   4856    /* analyze glyph outline */
   4857    if ( AF_HINTS_DO_HORIZONTAL( hints ) )
   4858    {
   4859      axis  = &metrics->axis[AF_DIMENSION_HORZ];
   4860      error = af_latin_hints_detect_features( hints,
   4861                                              axis->width_count,
   4862                                              axis->widths,
   4863                                              AF_DIMENSION_HORZ );
   4864      if ( error )
   4865        goto Exit;
   4866    }
   4867 
   4868    if ( AF_HINTS_DO_VERTICAL( hints ) )
   4869    {
   4870      size_t*  val;
   4871 
   4872      FT_Int  top_tilde_contour    = 0;
   4873      FT_Int  bottom_tilde_contour = 0;
   4874 
   4875      FT_Int  below_top_tilde_contour    = 0;
   4876      FT_Int  above_bottom_tilde_contour = 0;
   4877 
   4878      AF_LatinBlue  capital_top_blue    = NULL;
   4879      AF_LatinBlue  capital_bottom_blue = NULL;
   4880 
   4881      AF_LatinBlue  small_top_blue    = NULL;
   4882      AF_LatinBlue  small_bottom_blue = NULL;
   4883 
   4884      FT_Bool  have_flags = FALSE;
   4885 
   4886      FT_Bool  is_top_tilde    = FALSE;
   4887      FT_Bool  is_bottom_tilde = FALSE;
   4888 
   4889      FT_Bool  is_below_top_tilde    = FALSE;
   4890      FT_Bool  is_above_bottom_tilde = FALSE;
   4891 
   4892      FT_Bool  ignore_capital_top    = FALSE;
   4893      FT_Bool  ignore_capital_bottom = FALSE;
   4894 
   4895      FT_Bool  ignore_small_top    = FALSE;
   4896      FT_Bool  ignore_small_bottom = FALSE;
   4897 
   4898      FT_Bool  do_height_check = TRUE;
   4899 
   4900      FT_Pos  limit;
   4901      FT_Pos  y_offset;
   4902 
   4903 
   4904      val = ft_hash_num_lookup( (FT_Int)glyph_index,
   4905                                metrics->root.reverse_charmap );
   4906      if ( val )
   4907      {
   4908        FT_UInt    codepoint = *val;
   4909        FT_UInt32  adj_type  = af_adjustment_database_lookup( codepoint );
   4910 
   4911 
   4912        if ( adj_type )
   4913        {
   4914          have_flags = !!adj_type;
   4915 
   4916          is_top_tilde    = !!( adj_type & AF_ADJUST_TILDE_TOP );
   4917          is_bottom_tilde = !!( adj_type & AF_ADJUST_TILDE_BOTTOM );
   4918 
   4919          is_below_top_tilde    = !!( adj_type & AF_ADJUST_TILDE_TOP2 );
   4920          is_above_bottom_tilde = !!( adj_type & AF_ADJUST_TILDE_BOTTOM2 );
   4921 
   4922          ignore_capital_top    = !!( adj_type & AF_IGNORE_CAPITAL_TOP );
   4923          ignore_capital_bottom = !!( adj_type & AF_IGNORE_CAPITAL_BOTTOM );
   4924 
   4925          ignore_small_top    = !!( adj_type & AF_IGNORE_SMALL_TOP );
   4926          ignore_small_bottom = !!( adj_type & AF_IGNORE_SMALL_BOTTOM );
   4927 
   4928          do_height_check = !( adj_type & AF_ADJUST_NO_HEIGHT_CHECK );
   4929        }
   4930      }
   4931 
   4932      if ( is_top_tilde || is_bottom_tilde             ||
   4933           is_below_top_tilde || is_above_bottom_tilde )
   4934        af_compute_vertical_extrema( hints );
   4935 
   4936      /* Process inner tilde glyphs first. */
   4937      if ( is_below_top_tilde )
   4938      {
   4939        below_top_tilde_contour = af_find_second_highest_contour( hints );
   4940 
   4941        y_offset = af_latin_stretch_top_tilde(
   4942                     hints, below_top_tilde_contour );
   4943        y_offset += af_latin_align_top_tilde(
   4944                      hints, below_top_tilde_contour );
   4945 
   4946        limit = hints->contour_y_minima[below_top_tilde_contour];
   4947        af_move_contours_up( hints, limit, y_offset );
   4948      }
   4949      if ( is_above_bottom_tilde )
   4950      {
   4951        above_bottom_tilde_contour = af_find_second_lowest_contour( hints );
   4952 
   4953        y_offset = af_latin_stretch_bottom_tilde(
   4954                     hints, above_bottom_tilde_contour );
   4955        y_offset -= af_latin_align_bottom_tilde(
   4956                      hints, above_bottom_tilde_contour );
   4957 
   4958        limit = hints->contour_y_maxima[above_bottom_tilde_contour];
   4959        af_move_contours_down( hints, limit, y_offset );
   4960      }
   4961 
   4962      if ( is_top_tilde )
   4963      {
   4964        top_tilde_contour = af_find_highest_contour( hints );
   4965 
   4966        (void)af_latin_stretch_top_tilde( hints, top_tilde_contour );
   4967        (void)af_latin_align_top_tilde( hints, top_tilde_contour );
   4968      }
   4969      if ( is_bottom_tilde )
   4970      {
   4971        bottom_tilde_contour = af_find_lowest_contour( hints );
   4972 
   4973        (void)af_latin_stretch_bottom_tilde( hints, bottom_tilde_contour );
   4974        (void)af_latin_align_bottom_tilde( hints, bottom_tilde_contour );
   4975      }
   4976 
   4977      axis  = &metrics->axis[AF_DIMENSION_VERT];
   4978      error = af_latin_hints_detect_features( hints,
   4979                                              axis->width_count,
   4980                                              axis->widths,
   4981                                              AF_DIMENSION_VERT );
   4982 
   4983      if ( have_flags )
   4984      {
   4985        af_latin_get_base_glyph_blues( hints,
   4986                                       TRUE,
   4987                                       &capital_top_blue,
   4988                                       &capital_bottom_blue );
   4989        af_latin_get_base_glyph_blues( hints,
   4990                                       FALSE,
   4991                                       &small_top_blue,
   4992                                       &small_bottom_blue );
   4993 
   4994        if ( do_height_check )
   4995        {
   4996          /* Set a heuristic limit for the accent height so that    */
   4997          /* `af_glyph_hints_apply_vertical_separation_adjustments` */
   4998          /* can correctly ignore the case where an accent is       */
   4999          /* unexpectedly not the highest (or lowest) contour.      */
   5000 
   5001          /* Either 2/3 of the lowercase blue zone height... */
   5002          if ( small_top_blue && small_bottom_blue )
   5003            accent_height_limit = 2 * ( small_top_blue->shoot.cur -
   5004                                        small_bottom_blue->shoot.cur ) / 3;
   5005          /* or 1/2 of the uppercase blue zone height... */
   5006          else if ( capital_top_blue && capital_bottom_blue )
   5007            accent_height_limit = ( capital_top_blue->shoot.cur -
   5008                                    capital_bottom_blue->shoot.cur ) / 2;
   5009          /* or half of the standard PostScript ascender value (8/10) */
   5010          /* of the EM value, scaled.                                 */
   5011          else
   5012            accent_height_limit = FT_MulFix( metrics->units_per_em * 4 / 10,
   5013                                             metrics->root.scaler.y_scale );
   5014        }
   5015      }
   5016 
   5017      if ( capital_top_blue && capital_bottom_blue )
   5018      {
   5019        if ( ignore_capital_top )
   5020          af_latin_ignore_top( hints,
   5021                               capital_top_blue, capital_bottom_blue );
   5022        if ( ignore_capital_bottom )
   5023          af_latin_ignore_bottom( hints,
   5024                                  capital_top_blue, capital_bottom_blue );
   5025      }
   5026      if ( small_top_blue && small_bottom_blue )
   5027      {
   5028        if ( ignore_small_top )
   5029          af_latin_ignore_top( hints,
   5030                               small_top_blue, small_bottom_blue );
   5031        if ( ignore_small_bottom )
   5032          af_latin_ignore_bottom( hints,
   5033                                  small_top_blue, small_bottom_blue );
   5034      }
   5035 
   5036      if ( error )
   5037        goto Exit;
   5038 
   5039      /* apply blue zones to base characters only */
   5040      if ( !( metrics->root.globals->glyph_styles[glyph_index] & AF_NONBASE ) )
   5041        af_latin_hints_compute_blue_edges( hints, metrics );
   5042    }
   5043 
   5044    /* grid-fit the outline */
   5045    for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
   5046    {
   5047      if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
   5048           ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) )   )
   5049      {
   5050        af_latin_hint_edges( hints, (AF_Dimension)dim );
   5051        af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
   5052        af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
   5053        af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
   5054        af_glyph_hints_apply_vertical_separation_adjustments(
   5055          hints,
   5056          (AF_Dimension)dim,
   5057          glyph_index,
   5058          accent_height_limit,
   5059          metrics->root.reverse_charmap );
   5060      }
   5061    }
   5062 
   5063    af_glyph_hints_save( hints, outline );
   5064 
   5065  Exit:
   5066    return error;
   5067  }
   5068 
   5069 
   5070  /*************************************************************************/
   5071  /*************************************************************************/
   5072  /*****                                                               *****/
   5073  /*****              L A T I N   S C R I P T   C L A S S              *****/
   5074  /*****                                                               *****/
   5075  /*************************************************************************/
   5076  /*************************************************************************/
   5077 
   5078 
   5079  AF_DEFINE_WRITING_SYSTEM_CLASS(
   5080    af_latin_writing_system_class,
   5081 
   5082    AF_WRITING_SYSTEM_LATIN,
   5083 
   5084    sizeof ( AF_LatinMetricsRec ),
   5085 
   5086    (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init,        /* style_metrics_init    */
   5087    (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale,       /* style_metrics_scale   */
   5088    (AF_WritingSystem_DoneMetricsFunc) af_latin_metrics_done,        /* style_metrics_done    */
   5089    (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */
   5090 
   5091    (AF_WritingSystem_InitHintsFunc)   af_latin_hints_init,          /* style_hints_init      */
   5092    (AF_WritingSystem_ApplyHintsFunc)  af_latin_hints_apply          /* style_hints_apply     */
   5093  )
   5094 
   5095 
   5096 /* END */