tor-browser

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

afcjk.c (66588B)


      1 /****************************************************************************
      2 *
      3 * afcjk.c
      4 *
      5 *   Auto-fitter hinting routines for CJK writing system (body).
      6 *
      7 * Copyright (C) 2006-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   * The algorithm is based on akito's autohint patch, archived at
     20   *
     21   * https://web.archive.org/web/20051219160454/http://www.kde.gr.jp:80/~akito/patch/freetype2/2.1.7/
     22   *
     23   */
     24 
     25 #include <freetype/ftadvanc.h>
     26 #include <freetype/internal/ftdebug.h>
     27 
     28 #include "afglobal.h"
     29 #include "aflatin.h"
     30 #include "afcjk.h"
     31 
     32 
     33 #ifdef AF_CONFIG_OPTION_CJK
     34 
     35 #undef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT
     36 
     37 #include "aferrors.h"
     38 
     39 
     40  /**************************************************************************
     41   *
     42   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
     43   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
     44   * messages during execution.
     45   */
     46 #undef  FT_COMPONENT
     47 #define FT_COMPONENT  afcjk
     48 
     49 
     50  /*************************************************************************/
     51  /*************************************************************************/
     52  /*****                                                               *****/
     53  /*****              C J K   G L O B A L   M E T R I C S              *****/
     54  /*****                                                               *****/
     55  /*************************************************************************/
     56  /*************************************************************************/
     57 
     58 
     59  /* Basically the Latin version with AF_CJKMetrics */
     60  /* to replace AF_LatinMetrics.                    */
     61 
     62  FT_LOCAL_DEF( void )
     63  af_cjk_metrics_init_widths( AF_CJKMetrics  metrics,
     64                              FT_Face        face )
     65  {
     66    /* scan the array of segments in each direction */
     67    AF_GlyphHintsRec  hints[1];
     68 
     69 
     70    FT_TRACE5(( "\n" ));
     71    FT_TRACE5(( "cjk standard widths computation (style `%s')\n",
     72                af_style_names[metrics->root.style_class->style] ));
     73    FT_TRACE5(( "===================================================\n" ));
     74    FT_TRACE5(( "\n" ));
     75 
     76    af_glyph_hints_init( hints, face->memory );
     77 
     78    metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
     79    metrics->axis[AF_DIMENSION_VERT].width_count = 0;
     80 
     81    {
     82      FT_Error          error;
     83      FT_ULong          glyph_index;
     84      int               dim;
     85      AF_CJKMetricsRec  dummy[1];
     86      AF_Scaler         scaler = &dummy->root.scaler;
     87 
     88      AF_StyleClass   style_class  = metrics->root.style_class;
     89      AF_ScriptClass  script_class = af_script_classes[style_class->script];
     90 
     91      /* If HarfBuzz is not available, we need a pointer to a single */
     92      /* unsigned long value.                                        */
     93      FT_ULong  shaper_buf_;
     94      void*     shaper_buf = &shaper_buf_;
     95 
     96      const char*  p;
     97 
     98 #ifdef FT_DEBUG_LEVEL_TRACE
     99      FT_ULong  ch = 0;
    100 #endif
    101 
    102      p = script_class->standard_charstring;
    103 
    104      if ( ft_hb_enabled( metrics->root.globals ) )
    105        shaper_buf = af_shaper_buf_create( metrics->root.globals );
    106 
    107      /* We check a list of standard characters.  The first match wins. */
    108 
    109      glyph_index = 0;
    110      while ( *p )
    111      {
    112        unsigned int  num_idx;
    113 
    114 #ifdef FT_DEBUG_LEVEL_TRACE
    115        const char*  p_old;
    116 #endif
    117 
    118 
    119        while ( *p == ' ' )
    120          p++;
    121 
    122 #ifdef FT_DEBUG_LEVEL_TRACE
    123        p_old = p;
    124        GET_UTF8_CHAR( ch, p_old );
    125 #endif
    126 
    127        /* reject input that maps to more than a single glyph */
    128        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
    129        if ( num_idx > 1 )
    130          continue;
    131 
    132        /* otherwise exit loop if we have a result */
    133        glyph_index = af_shaper_get_elem( &metrics->root,
    134                                          shaper_buf,
    135                                          0,
    136                                          NULL,
    137                                          NULL );
    138        if ( glyph_index )
    139          break;
    140      }
    141 
    142      af_shaper_buf_destroy( metrics->root.globals, shaper_buf );
    143 
    144      if ( !glyph_index )
    145        goto Exit;
    146 
    147      if ( !glyph_index )
    148        goto Exit;
    149 
    150      FT_TRACE5(( "standard character: U+%04lX (glyph index %lu)\n",
    151                  ch, glyph_index ));
    152 
    153      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
    154      if ( error || face->glyph->outline.n_points <= 0 )
    155        goto Exit;
    156 
    157      FT_ZERO( dummy );
    158 
    159      dummy->units_per_em = metrics->units_per_em;
    160 
    161      scaler->x_scale = 0x10000L;
    162      scaler->y_scale = 0x10000L;
    163      scaler->x_delta = 0;
    164      scaler->y_delta = 0;
    165 
    166      scaler->face        = face;
    167      scaler->render_mode = FT_RENDER_MODE_NORMAL;
    168      scaler->flags       = 0;
    169 
    170      af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy );
    171 
    172      error = af_glyph_hints_reload( hints, &face->glyph->outline );
    173      if ( error )
    174        goto Exit;
    175 
    176      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    177      {
    178        AF_CJKAxis    axis    = &metrics->axis[dim];
    179        AF_AxisHints  axhints = &hints->axis[dim];
    180        AF_Segment    seg, limit, link;
    181        FT_UInt       num_widths = 0;
    182 
    183 
    184        error = af_latin_hints_compute_segments( hints,
    185                                                 (AF_Dimension)dim );
    186        if ( error )
    187          goto Exit;
    188 
    189        /*
    190         * We assume that the glyphs selected for the stem width
    191         * computation are `featureless' enough so that the linking
    192         * algorithm works fine without adjustments of its scoring
    193         * function.
    194         */
    195        af_latin_hints_link_segments( hints,
    196                                      0,
    197                                      NULL,
    198                                      (AF_Dimension)dim );
    199 
    200        seg   = axhints->segments;
    201        limit = seg + axhints->num_segments;
    202 
    203        for ( ; seg < limit; seg++ )
    204        {
    205          link = seg->link;
    206 
    207          /* we only consider stem segments there! */
    208          if ( link && link->link == seg && link > seg )
    209          {
    210            FT_Pos  dist;
    211 
    212 
    213            dist = seg->pos - link->pos;
    214            if ( dist < 0 )
    215              dist = -dist;
    216 
    217            if ( num_widths < AF_CJK_MAX_WIDTHS )
    218              axis->widths[num_widths++].org = dist;
    219          }
    220        }
    221 
    222        /* this also replaces multiple almost identical stem widths */
    223        /* with a single one (the value 100 is heuristic)           */
    224        af_sort_and_quantize_widths( &num_widths, axis->widths,
    225                                     dummy->units_per_em / 100 );
    226        axis->width_count = num_widths;
    227      }
    228 
    229    Exit:
    230      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
    231      {
    232        AF_CJKAxis  axis = &metrics->axis[dim];
    233        FT_Pos      stdw;
    234 
    235 
    236        stdw = ( axis->width_count > 0 ) ? axis->widths[0].org
    237                                         : AF_LATIN_CONSTANT( metrics, 50 );
    238 
    239        /* let's try 20% of the smallest width */
    240        axis->edge_distance_threshold = stdw / 5;
    241        axis->standard_width          = stdw;
    242        axis->extra_light             = 0;
    243 
    244 #ifdef FT_DEBUG_LEVEL_TRACE
    245        {
    246          FT_UInt  i;
    247 
    248 
    249          FT_TRACE5(( "%s widths:\n",
    250                      dim == AF_DIMENSION_VERT ? "horizontal"
    251                                               : "vertical" ));
    252 
    253          FT_TRACE5(( "  %ld (standard)", axis->standard_width ));
    254          for ( i = 1; i < axis->width_count; i++ )
    255            FT_TRACE5(( " %ld", axis->widths[i].org ));
    256 
    257          FT_TRACE5(( "\n" ));
    258        }
    259 #endif
    260      }
    261    }
    262 
    263    FT_TRACE5(( "\n" ));
    264 
    265    af_glyph_hints_done( hints );
    266  }
    267 
    268 
    269  /* Find all blue zones. */
    270 
    271  static void
    272  af_cjk_metrics_init_blues( AF_CJKMetrics  metrics,
    273                             FT_Face        face )
    274  {
    275    FT_Pos      fills[AF_BLUE_STRING_MAX_LEN];
    276    FT_Pos      flats[AF_BLUE_STRING_MAX_LEN];
    277 
    278    FT_UInt     num_fills;
    279    FT_UInt     num_flats;
    280 
    281    FT_Bool     fill;
    282 
    283    AF_CJKBlue  blue;
    284    FT_Error    error;
    285    AF_CJKAxis  axis;
    286    FT_Outline  outline;
    287 
    288    AF_StyleClass  sc = metrics->root.style_class;
    289 
    290    AF_Blue_Stringset         bss = sc->blue_stringset;
    291    const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
    292 
    293    /* If HarfBuzz is not available, we need a pointer to a single */
    294    /* unsigned long value.                                        */
    295    FT_ULong  shaper_buf_;
    296    void*     shaper_buf = &shaper_buf_;
    297 
    298 
    299    /* we walk over the blue character strings as specified in the   */
    300    /* style's entry in the `af_blue_stringset' array, computing its */
    301    /* extremum points (depending on the string properties)          */
    302 
    303    FT_TRACE5(( "cjk blue zones computation\n" ));
    304    FT_TRACE5(( "==========================\n" ));
    305    FT_TRACE5(( "\n" ));
    306 
    307    if ( ft_hb_enabled( metrics->root.globals ) )
    308      shaper_buf = af_shaper_buf_create( metrics->root.globals );
    309 
    310    for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
    311    {
    312      const char*  p = &af_blue_strings[bs->string];
    313      FT_Pos*      blue_ref;
    314      FT_Pos*      blue_shoot;
    315 
    316 
    317      if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
    318        axis = &metrics->axis[AF_DIMENSION_HORZ];
    319      else
    320        axis = &metrics->axis[AF_DIMENSION_VERT];
    321 
    322 #ifdef FT_DEBUG_LEVEL_TRACE
    323      {
    324        FT_String*  cjk_blue_name[4] =
    325        {
    326          (FT_String*)"bottom",    /* --   , --  */
    327          (FT_String*)"top",       /* --   , TOP */
    328          (FT_String*)"left",      /* HORIZ, --  */
    329          (FT_String*)"right"      /* HORIZ, TOP */
    330        };
    331 
    332 
    333        FT_TRACE5(( "blue zone %u (%s):\n",
    334                    axis->blue_count,
    335                    cjk_blue_name[AF_CJK_IS_HORIZ_BLUE( bs ) |
    336                                  AF_CJK_IS_TOP_BLUE( bs )   ] ));
    337      }
    338 #endif /* FT_DEBUG_LEVEL_TRACE */
    339 
    340      num_fills = 0;
    341      num_flats = 0;
    342 
    343      fill = 1;  /* start with characters that define fill values */
    344      FT_TRACE5(( "  [overshoot values]\n" ));
    345 
    346      while ( *p )
    347      {
    348        FT_ULong    glyph_index;
    349        FT_Pos      best_pos;       /* same as points.y or points.x, resp. */
    350        FT_Int      best_point;
    351        FT_Vector*  points;
    352 
    353        unsigned int  num_idx;
    354 
    355 #ifdef FT_DEBUG_LEVEL_TRACE
    356        const char*  p_old;
    357        FT_ULong     ch;
    358 #endif
    359 
    360 
    361        while ( *p == ' ' )
    362          p++;
    363 
    364 #ifdef FT_DEBUG_LEVEL_TRACE
    365        p_old = p;
    366        GET_UTF8_CHAR( ch, p_old );
    367 #endif
    368 
    369        /* switch to characters that define flat values */
    370        if ( *p == '|' )
    371        {
    372          fill = 0;
    373          FT_TRACE5(( "  [reference values]\n" ));
    374          p++;
    375          continue;
    376        }
    377 
    378        /* reject input that maps to more than a single glyph */
    379        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
    380        if ( num_idx > 1 )
    381          continue;
    382 
    383        /* load the character in the face -- skip unknown or empty ones */
    384        glyph_index = af_shaper_get_elem( &metrics->root,
    385                                          shaper_buf,
    386                                          0,
    387                                          NULL,
    388                                          NULL );
    389        if ( glyph_index == 0 )
    390        {
    391          FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
    392          continue;
    393        }
    394 
    395        error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
    396        outline = face->glyph->outline;
    397        if ( error || outline.n_points <= 2 )
    398        {
    399          FT_TRACE5(( "  U+%04lX contains no (usable) outlines\n", ch ));
    400          continue;
    401        }
    402 
    403        /* now compute min or max point indices and coordinates */
    404        points     = outline.points;
    405        best_point = -1;
    406        best_pos   = 0;  /* make compiler happy */
    407 
    408        {
    409          FT_Int  nn;
    410          FT_Int  pp, first, last;
    411 
    412 
    413          last = -1;
    414          for ( nn = 0; nn < outline.n_contours; nn++ )
    415          {
    416            first = last + 1;
    417            last  = outline.contours[nn];
    418 
    419            /* Avoid single-point contours since they are never rasterized. */
    420            /* In some fonts, they correspond to mark attachment points     */
    421            /* which are way outside of the glyph's real outline.           */
    422            if ( last <= first )
    423              continue;
    424 
    425            if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
    426            {
    427              if ( AF_CJK_IS_RIGHT_BLUE( bs ) )
    428              {
    429                for ( pp = first; pp <= last; pp++ )
    430                  if ( best_point < 0 || points[pp].x > best_pos )
    431                  {
    432                    best_point = pp;
    433                    best_pos   = points[pp].x;
    434                  }
    435              }
    436              else
    437              {
    438                for ( pp = first; pp <= last; pp++ )
    439                  if ( best_point < 0 || points[pp].x < best_pos )
    440                  {
    441                    best_point = pp;
    442                    best_pos   = points[pp].x;
    443                  }
    444              }
    445            }
    446            else
    447            {
    448              if ( AF_CJK_IS_TOP_BLUE( bs ) )
    449              {
    450                for ( pp = first; pp <= last; pp++ )
    451                  if ( best_point < 0 || points[pp].y > best_pos )
    452                  {
    453                    best_point = pp;
    454                    best_pos   = points[pp].y;
    455                  }
    456              }
    457              else
    458              {
    459                for ( pp = first; pp <= last; pp++ )
    460                  if ( best_point < 0 || points[pp].y < best_pos )
    461                  {
    462                    best_point = pp;
    463                    best_pos   = points[pp].y;
    464                  }
    465              }
    466            }
    467          }
    468 
    469          FT_TRACE5(( "  U+%04lX: best_pos = %5ld\n", ch, best_pos ));
    470        }
    471 
    472        if ( fill )
    473          fills[num_fills++] = best_pos;
    474        else
    475          flats[num_flats++] = best_pos;
    476 
    477      } /* end while loop */
    478 
    479      if ( num_flats == 0 && num_fills == 0 )
    480      {
    481        /*
    482         * we couldn't find a single glyph to compute this blue zone,
    483         * we will simply ignore it then
    484         */
    485        FT_TRACE5(( "  empty\n" ));
    486        continue;
    487      }
    488 
    489      /* we have computed the contents of the `fill' and `flats' tables,   */
    490      /* now determine the reference and overshoot position of the blue -- */
    491      /* we simply take the median value after a simple sort               */
    492      af_sort_pos( num_fills, fills );
    493      af_sort_pos( num_flats, flats );
    494 
    495      blue       = &axis->blues[axis->blue_count];
    496      blue_ref   = &blue->ref.org;
    497      blue_shoot = &blue->shoot.org;
    498 
    499      axis->blue_count++;
    500 
    501      if ( num_flats == 0 )
    502      {
    503        *blue_ref   =
    504        *blue_shoot = fills[num_fills / 2];
    505      }
    506      else if ( num_fills == 0 )
    507      {
    508        *blue_ref   =
    509        *blue_shoot = flats[num_flats / 2];
    510      }
    511      else
    512      {
    513        *blue_ref   = fills[num_fills / 2];
    514        *blue_shoot = flats[num_flats / 2];
    515      }
    516 
    517      /* make sure blue_ref >= blue_shoot for top/right or */
    518      /* vice versa for bottom/left                        */
    519      if ( *blue_shoot != *blue_ref )
    520      {
    521        FT_Pos   ref       = *blue_ref;
    522        FT_Pos   shoot     = *blue_shoot;
    523        FT_Bool  under_ref = FT_BOOL( shoot < ref );
    524 
    525 
    526        /* AF_CJK_IS_TOP_BLUE covers `right' and `top' */
    527        if ( AF_CJK_IS_TOP_BLUE( bs ) ^ under_ref )
    528        {
    529          *blue_ref   =
    530          *blue_shoot = ( shoot + ref ) / 2;
    531 
    532          FT_TRACE5(( "  [reference smaller than overshoot,"
    533                      " taking mean value]\n" ));
    534        }
    535      }
    536 
    537      blue->flags = 0;
    538      if ( AF_CJK_IS_TOP_BLUE( bs ) )
    539        blue->flags |= AF_CJK_BLUE_TOP;
    540 
    541      FT_TRACE5(( "    -> reference = %ld\n", *blue_ref ));
    542      FT_TRACE5(( "       overshoot = %ld\n", *blue_shoot ));
    543 
    544    } /* end for loop */
    545 
    546    af_shaper_buf_destroy( metrics->root.globals, shaper_buf );
    547 
    548    FT_TRACE5(( "\n" ));
    549 
    550    return;
    551  }
    552 
    553 
    554  /* Basically the Latin version with type AF_CJKMetrics for metrics. */
    555 
    556  FT_LOCAL_DEF( void )
    557  af_cjk_metrics_check_digits( AF_CJKMetrics  metrics,
    558                               FT_Face        face )
    559  {
    560    FT_Bool  started = 0, same_width = 1;
    561    FT_Long  advance = 0, old_advance = 0;
    562 
    563    /* If HarfBuzz is not available, we need a pointer to a single */
    564    /* unsigned long value.                                        */
    565    FT_ULong  shaper_buf_;
    566    void*     shaper_buf = &shaper_buf_;
    567 
    568    /* in all supported charmaps, digits have character codes 0x30-0x39 */
    569    const char   digits[] = "0 1 2 3 4 5 6 7 8 9";
    570    const char*  p;
    571 
    572    FT_UNUSED( face );
    573 
    574 
    575    p = digits;
    576 
    577    if ( ft_hb_enabled( metrics->root.globals ) )
    578      shaper_buf = af_shaper_buf_create( metrics->root.globals );
    579 
    580    while ( *p )
    581    {
    582      FT_ULong      glyph_index;
    583      unsigned int  num_idx;
    584 
    585 
    586      /* reject input that maps to more than a single glyph */
    587      p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
    588      if ( num_idx > 1 )
    589        continue;
    590 
    591      glyph_index = af_shaper_get_elem( &metrics->root,
    592                                        shaper_buf,
    593                                        0,
    594                                        &advance,
    595                                        NULL );
    596      if ( !glyph_index )
    597        continue;
    598 
    599      if ( started )
    600      {
    601        if ( advance != old_advance )
    602        {
    603          same_width = 0;
    604          break;
    605        }
    606      }
    607      else
    608      {
    609        old_advance = advance;
    610        started     = 1;
    611      }
    612    }
    613 
    614    af_shaper_buf_destroy( metrics->root.globals, shaper_buf );
    615 
    616    metrics->root.digits_have_same_width = same_width;
    617  }
    618 
    619 
    620  /* Initialize global metrics. */
    621 
    622  FT_LOCAL_DEF( FT_Error )
    623  af_cjk_metrics_init( AF_StyleMetrics  metrics_,  /* AF_CJKMetrics */
    624                       FT_Face          face )
    625  {
    626    AF_CJKMetrics  metrics = (AF_CJKMetrics)metrics_;
    627    FT_CharMap     oldmap  = face->charmap;
    628 
    629 
    630    metrics->units_per_em = face->units_per_EM;
    631 
    632    if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
    633    {
    634      af_cjk_metrics_init_widths( metrics, face );
    635      af_cjk_metrics_init_blues( metrics, face );
    636      af_cjk_metrics_check_digits( metrics, face );
    637    }
    638 
    639    face->charmap = oldmap;
    640    return FT_Err_Ok;
    641  }
    642 
    643 
    644  /* Adjust scaling value, then scale and shift widths   */
    645  /* and blue zones (if applicable) for given dimension. */
    646 
    647  static void
    648  af_cjk_metrics_scale_dim( AF_CJKMetrics  metrics,
    649                            AF_Scaler      scaler,
    650                            AF_Dimension   dim )
    651  {
    652    FT_Fixed    scale;
    653    FT_Pos      delta;
    654    AF_CJKAxis  axis;
    655    FT_UInt     nn;
    656 
    657 
    658    if ( dim == AF_DIMENSION_HORZ )
    659    {
    660      scale = scaler->x_scale;
    661      delta = scaler->x_delta;
    662    }
    663    else
    664    {
    665      scale = scaler->y_scale;
    666      delta = scaler->y_delta;
    667    }
    668 
    669    axis = &metrics->axis[dim];
    670 
    671    if ( axis->org_scale == scale && axis->org_delta == delta )
    672      return;
    673 
    674    axis->org_scale = scale;
    675    axis->org_delta = delta;
    676 
    677    axis->scale = scale;
    678    axis->delta = delta;
    679 
    680    /* scale the blue zones */
    681    for ( nn = 0; nn < axis->blue_count; nn++ )
    682    {
    683      AF_CJKBlue  blue = &axis->blues[nn];
    684      FT_Pos      dist;
    685 
    686 
    687      blue->ref.cur   = FT_MulFix( blue->ref.org, scale ) + delta;
    688      blue->ref.fit   = blue->ref.cur;
    689      blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
    690      blue->shoot.fit = blue->shoot.cur;
    691      blue->flags    &= ~AF_CJK_BLUE_ACTIVE;
    692 
    693      /* a blue zone is only active if it is less than 3/4 pixels tall */
    694      dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
    695      if ( dist <= 48 && dist >= -48 )
    696      {
    697        FT_Pos  delta1, delta2;
    698 
    699 
    700        blue->ref.fit = FT_PIX_ROUND( blue->ref.cur );
    701 
    702        /* shoot is under shoot for cjk */
    703        delta1 = FT_DivFix( blue->ref.fit, scale ) - blue->shoot.org;
    704        delta2 = delta1;
    705        if ( delta1 < 0 )
    706          delta2 = -delta2;
    707 
    708        delta2 = FT_MulFix( delta2, scale );
    709 
    710        FT_TRACE5(( "delta: %ld", delta1 ));
    711        if ( delta2 < 32 )
    712          delta2 = 0;
    713 #if 0
    714        else if ( delta2 < 64 )
    715          delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
    716 #endif
    717        else
    718          delta2 = FT_PIX_ROUND( delta2 );
    719        FT_TRACE5(( "/%ld\n", delta2 ));
    720 
    721        if ( delta1 < 0 )
    722          delta2 = -delta2;
    723 
    724        blue->shoot.fit = blue->ref.fit - delta2;
    725 
    726        FT_TRACE5(( ">> active cjk blue zone %c%u[%ld/%ld]:\n",
    727                    ( dim == AF_DIMENSION_HORZ ) ? 'H' : 'V',
    728                    nn, blue->ref.org, blue->shoot.org ));
    729        FT_TRACE5(( "     ref:   cur=%.2f fit=%.2f\n",
    730                    (double)blue->ref.cur / 64,
    731                    (double)blue->ref.fit / 64 ));
    732        FT_TRACE5(( "     shoot: cur=%.2f fit=%.2f\n",
    733                    (double)blue->shoot.cur / 64,
    734                    (double)blue->shoot.fit / 64 ));
    735 
    736        blue->flags |= AF_CJK_BLUE_ACTIVE;
    737      }
    738    }
    739  }
    740 
    741 
    742  /* Scale global values in both directions. */
    743 
    744  FT_LOCAL_DEF( void )
    745  af_cjk_metrics_scale( AF_StyleMetrics  metrics_,   /* AF_CJKMetrics */
    746                        AF_Scaler        scaler )
    747  {
    748    AF_CJKMetrics  metrics = (AF_CJKMetrics)metrics_;
    749 
    750 
    751    /* we copy the whole structure since the x and y scaling values */
    752    /* are not modified, contrary to e.g. the `latin' auto-hinter   */
    753    metrics->root.scaler = *scaler;
    754 
    755    af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
    756    af_cjk_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
    757  }
    758 
    759 
    760  /* Extract standard_width from writing system/script specific */
    761  /* metrics class.                                             */
    762 
    763  FT_CALLBACK_DEF( void )
    764  af_cjk_get_standard_widths( AF_StyleMetrics  metrics_,  /* AF_CJKMetrics */
    765                              FT_Pos*          stdHW,
    766                              FT_Pos*          stdVW )
    767  {
    768    AF_CJKMetrics  metrics = (AF_CJKMetrics)metrics_;
    769 
    770 
    771    if ( stdHW )
    772      *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
    773 
    774    if ( stdVW )
    775      *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
    776  }
    777 
    778 
    779  /*************************************************************************/
    780  /*************************************************************************/
    781  /*****                                                               *****/
    782  /*****              C J K   G L Y P H   A N A L Y S I S              *****/
    783  /*****                                                               *****/
    784  /*************************************************************************/
    785  /*************************************************************************/
    786 
    787 
    788  /* Walk over all contours and compute its segments. */
    789 
    790  static FT_Error
    791  af_cjk_hints_compute_segments( AF_GlyphHints  hints,
    792                                 AF_Dimension   dim )
    793  {
    794    AF_AxisHints  axis          = &hints->axis[dim];
    795    AF_Segment    segments      = axis->segments;
    796    AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
    797    FT_Error      error;
    798    AF_Segment    seg;
    799 
    800 
    801    error = af_latin_hints_compute_segments( hints, dim );
    802    if ( error )
    803      return error;
    804 
    805    /* a segment is round if it doesn't have successive */
    806    /* on-curve points.                                 */
    807    for ( seg = segments; seg < segment_limit; seg++ )
    808    {
    809      AF_Point  pt   = seg->first;
    810      AF_Point  last = seg->last;
    811      FT_UInt   f0   = pt->flags & AF_FLAG_CONTROL;
    812      FT_UInt   f1;
    813 
    814 
    815      seg->flags &= ~AF_EDGE_ROUND;
    816 
    817      for ( ; pt != last; f0 = f1 )
    818      {
    819        pt = pt->next;
    820        f1 = pt->flags & AF_FLAG_CONTROL;
    821 
    822        if ( !f0 && !f1 )
    823          break;
    824 
    825        if ( pt == last )
    826          seg->flags |= AF_EDGE_ROUND;
    827      }
    828    }
    829 
    830    return FT_Err_Ok;
    831  }
    832 
    833 
    834  static void
    835  af_cjk_hints_link_segments( AF_GlyphHints  hints,
    836                              AF_Dimension   dim )
    837  {
    838    AF_AxisHints  axis          = &hints->axis[dim];
    839    AF_Segment    segments      = axis->segments;
    840    AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
    841    AF_Direction  major_dir     = axis->major_dir;
    842    AF_Segment    seg1, seg2;
    843    FT_Pos        len_threshold;
    844    FT_Pos        dist_threshold;
    845 
    846 
    847    len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
    848 
    849    dist_threshold = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
    850                                                  : hints->y_scale;
    851    dist_threshold = FT_DivFix( 64 * 3, dist_threshold );
    852 
    853    /* now compare each segment to the others */
    854    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
    855    {
    856      if ( seg1->dir != major_dir )
    857        continue;
    858 
    859      for ( seg2 = segments; seg2 < segment_limit; seg2++ )
    860        if ( seg2 != seg1 && seg1->dir + seg2->dir == 0 )
    861        {
    862          FT_Pos  dist = seg2->pos - seg1->pos;
    863 
    864 
    865          if ( dist < 0 )
    866            continue;
    867 
    868          {
    869            FT_Pos  min = seg1->min_coord;
    870            FT_Pos  max = seg1->max_coord;
    871            FT_Pos  len;
    872 
    873 
    874            if ( min < seg2->min_coord )
    875              min = seg2->min_coord;
    876 
    877            if ( max > seg2->max_coord )
    878              max = seg2->max_coord;
    879 
    880            len = max - min;
    881            if ( len >= len_threshold )
    882            {
    883              if ( dist * 8 < seg1->score * 9                        &&
    884                   ( dist * 8 < seg1->score * 7 || seg1->len < len ) )
    885              {
    886                seg1->score = dist;
    887                seg1->len   = len;
    888                seg1->link  = seg2;
    889              }
    890 
    891              if ( dist * 8 < seg2->score * 9                        &&
    892                   ( dist * 8 < seg2->score * 7 || seg2->len < len ) )
    893              {
    894                seg2->score = dist;
    895                seg2->len   = len;
    896                seg2->link  = seg1;
    897              }
    898            }
    899          }
    900        }
    901    }
    902 
    903    /*
    904     * now compute the `serif' segments
    905     *
    906     * In Hanzi, some strokes are wider on one or both of the ends.
    907     * We either identify the stems on the ends as serifs or remove
    908     * the linkage, depending on the length of the stems.
    909     *
    910     */
    911 
    912    {
    913      AF_Segment  link1, link2;
    914 
    915 
    916      for ( seg1 = segments; seg1 < segment_limit; seg1++ )
    917      {
    918        link1 = seg1->link;
    919        if ( !link1 || link1->link != seg1 || link1->pos <= seg1->pos )
    920          continue;
    921 
    922        if ( seg1->score >= dist_threshold )
    923          continue;
    924 
    925        for ( seg2 = segments; seg2 < segment_limit; seg2++ )
    926        {
    927          if ( seg2->pos > seg1->pos || seg1 == seg2 )
    928            continue;
    929 
    930          link2 = seg2->link;
    931          if ( !link2 || link2->link != seg2 || link2->pos < link1->pos )
    932            continue;
    933 
    934          if ( seg1->pos == seg2->pos && link1->pos == link2->pos )
    935            continue;
    936 
    937          if ( seg2->score <= seg1->score || seg1->score * 4 <= seg2->score )
    938            continue;
    939 
    940          /* seg2 < seg1 < link1 < link2 */
    941 
    942          if ( seg1->len >= seg2->len * 3 )
    943          {
    944            AF_Segment  seg;
    945 
    946 
    947            for ( seg = segments; seg < segment_limit; seg++ )
    948            {
    949              AF_Segment  link = seg->link;
    950 
    951 
    952              if ( link == seg2 )
    953              {
    954                seg->link  = NULL;
    955                seg->serif = link1;
    956              }
    957              else if ( link == link2 )
    958              {
    959                seg->link  = NULL;
    960                seg->serif = seg1;
    961              }
    962            }
    963          }
    964          else
    965          {
    966            seg1->link = link1->link = NULL;
    967 
    968            break;
    969          }
    970        }
    971      }
    972    }
    973 
    974    for ( seg1 = segments; seg1 < segment_limit; seg1++ )
    975    {
    976      seg2 = seg1->link;
    977 
    978      if ( seg2 )
    979      {
    980        if ( seg2->link != seg1 )
    981        {
    982          seg1->link = NULL;
    983 
    984          if ( seg2->score < dist_threshold || seg1->score < seg2->score * 4 )
    985            seg1->serif = seg2->link;
    986        }
    987      }
    988    }
    989  }
    990 
    991 
    992  static FT_Error
    993  af_cjk_hints_compute_edges( AF_GlyphHints  hints,
    994                              AF_Dimension   dim )
    995  {
    996    AF_AxisHints  axis   = &hints->axis[dim];
    997    FT_Error      error  = FT_Err_Ok;
    998    FT_Memory     memory = hints->memory;
    999    AF_CJKAxis    laxis  = &((AF_CJKMetrics)hints->metrics)->axis[dim];
   1000 
   1001    AF_Segment    segments      = axis->segments;
   1002    AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
   1003    AF_Segment    seg;
   1004 
   1005    FT_Fixed      scale;
   1006    FT_Pos        edge_distance_threshold;
   1007 
   1008 
   1009    axis->num_edges = 0;
   1010 
   1011    scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
   1012                                         : hints->y_scale;
   1013 
   1014    /**********************************************************************
   1015     *
   1016     * We begin by generating a sorted table of edges for the current
   1017     * direction.  To do so, we simply scan each segment and try to find
   1018     * an edge in our table that corresponds to its position.
   1019     *
   1020     * If no edge is found, we create and insert a new edge in the
   1021     * sorted table.  Otherwise, we simply add the segment to the edge's
   1022     * list which is then processed in the second step to compute the
   1023     * edge's properties.
   1024     *
   1025     * Note that the edges table is sorted along the segment/edge
   1026     * position.
   1027     *
   1028     */
   1029 
   1030    edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
   1031                                         scale );
   1032    if ( edge_distance_threshold > 64 / 4 )
   1033      edge_distance_threshold = FT_DivFix( 64 / 4, scale );
   1034    else
   1035      edge_distance_threshold = laxis->edge_distance_threshold;
   1036 
   1037    for ( seg = segments; seg < segment_limit; seg++ )
   1038    {
   1039      AF_Edge  found = NULL;
   1040      FT_Pos   best  = 0xFFFFU;
   1041      FT_UInt  ee;
   1042 
   1043 
   1044      /* look for an edge corresponding to the segment */
   1045      for ( ee = 0; ee < axis->num_edges; ee++ )
   1046      {
   1047        AF_Edge  edge = axis->edges + ee;
   1048        FT_Pos   dist;
   1049 
   1050 
   1051        if ( edge->dir != seg->dir )
   1052          continue;
   1053 
   1054        dist = seg->pos - edge->fpos;
   1055        if ( dist < 0 )
   1056          dist = -dist;
   1057 
   1058        if ( dist < edge_distance_threshold && dist < best )
   1059        {
   1060          AF_Segment  link = seg->link;
   1061 
   1062 
   1063          /* check whether all linked segments of the candidate edge */
   1064          /* can make a single edge.                                 */
   1065          if ( link )
   1066          {
   1067            AF_Segment  seg1  = edge->first;
   1068            FT_Pos      dist2 = 0;
   1069 
   1070 
   1071            do
   1072            {
   1073              AF_Segment  link1 = seg1->link;
   1074 
   1075 
   1076              if ( link1 )
   1077              {
   1078                dist2 = AF_SEGMENT_DIST( link, link1 );
   1079                if ( dist2 >= edge_distance_threshold )
   1080                  break;
   1081              }
   1082 
   1083            } while ( ( seg1 = seg1->edge_next ) != edge->first );
   1084 
   1085            if ( dist2 >= edge_distance_threshold )
   1086              continue;
   1087          }
   1088 
   1089          best  = dist;
   1090          found = edge;
   1091        }
   1092      }
   1093 
   1094      if ( !found )
   1095      {
   1096        AF_Edge  edge;
   1097 
   1098 
   1099        /* insert a new edge in the list and */
   1100        /* sort according to the position    */
   1101        error = af_axis_hints_new_edge( axis, seg->pos,
   1102                                        (AF_Direction)seg->dir, 0,
   1103                                        memory, &edge );
   1104        if ( error )
   1105          goto Exit;
   1106 
   1107        /* add the segment to the new edge's list */
   1108        FT_ZERO( edge );
   1109 
   1110        edge->first    = seg;
   1111        edge->last     = seg;
   1112        edge->dir      = seg->dir;
   1113        edge->fpos     = seg->pos;
   1114        edge->opos     = FT_MulFix( seg->pos, scale );
   1115        edge->pos      = edge->opos;
   1116        seg->edge_next = seg;
   1117      }
   1118      else
   1119      {
   1120        /* if an edge was found, simply add the segment to the edge's */
   1121        /* list                                                       */
   1122        seg->edge_next         = found->first;
   1123        found->last->edge_next = seg;
   1124        found->last            = seg;
   1125      }
   1126    }
   1127 
   1128    /*******************************************************************
   1129     *
   1130     * Good, we now compute each edge's properties according to the
   1131     * segments found on its position.  Basically, these are
   1132     *
   1133     * - the edge's main direction
   1134     * - stem edge, serif edge or both (which defaults to stem then)
   1135     * - rounded edge, straight or both (which defaults to straight)
   1136     * - link for edge
   1137     *
   1138     */
   1139 
   1140    /* first of all, set the `edge' field in each segment -- this is */
   1141    /* required in order to compute edge links                       */
   1142 
   1143    /*
   1144     * Note that removing this loop and setting the `edge' field of each
   1145     * segment directly in the code above slows down execution speed for
   1146     * some reasons on platforms like the Sun.
   1147     */
   1148    {
   1149      AF_Edge  edges      = axis->edges;
   1150      AF_Edge  edge_limit = FT_OFFSET( edges, axis->num_edges );
   1151      AF_Edge  edge;
   1152 
   1153 
   1154      for ( edge = edges; edge < edge_limit; edge++ )
   1155      {
   1156        seg = edge->first;
   1157        if ( seg )
   1158          do
   1159          {
   1160            seg->edge = edge;
   1161            seg       = seg->edge_next;
   1162 
   1163          } while ( seg != edge->first );
   1164      }
   1165 
   1166      /* now compute each edge properties */
   1167      for ( edge = edges; edge < edge_limit; edge++ )
   1168      {
   1169        FT_Int  is_round    = 0;  /* does it contain round segments?    */
   1170        FT_Int  is_straight = 0;  /* does it contain straight segments? */
   1171 
   1172 
   1173        seg = edge->first;
   1174        if ( !seg )
   1175          goto Skip_Loop;
   1176 
   1177        do
   1178        {
   1179          FT_Bool  is_serif;
   1180 
   1181 
   1182          /* check for roundness of segment */
   1183          if ( seg->flags & AF_EDGE_ROUND )
   1184            is_round++;
   1185          else
   1186            is_straight++;
   1187 
   1188          /* check for links -- if seg->serif is set, then seg->link must */
   1189          /* be ignored                                                   */
   1190          is_serif = FT_BOOL( seg->serif && seg->serif->edge != edge );
   1191 
   1192          if ( seg->link || is_serif )
   1193          {
   1194            AF_Edge     edge2;
   1195            AF_Segment  seg2;
   1196 
   1197 
   1198            edge2 = edge->link;
   1199            seg2  = seg->link;
   1200 
   1201            if ( is_serif )
   1202            {
   1203              seg2  = seg->serif;
   1204              edge2 = edge->serif;
   1205            }
   1206 
   1207            if ( edge2 )
   1208            {
   1209              FT_Pos  edge_delta;
   1210              FT_Pos  seg_delta;
   1211 
   1212 
   1213              edge_delta = edge->fpos - edge2->fpos;
   1214              if ( edge_delta < 0 )
   1215                edge_delta = -edge_delta;
   1216 
   1217              seg_delta = AF_SEGMENT_DIST( seg, seg2 );
   1218 
   1219              if ( seg_delta < edge_delta )
   1220                edge2 = seg2->edge;
   1221            }
   1222            else
   1223              edge2 = seg2->edge;
   1224 
   1225            if ( is_serif )
   1226            {
   1227              edge->serif   = edge2;
   1228              edge2->flags |= AF_EDGE_SERIF;
   1229            }
   1230            else
   1231              edge->link = edge2;
   1232          }
   1233 
   1234          seg = seg->edge_next;
   1235 
   1236        } while ( seg != edge->first );
   1237 
   1238      Skip_Loop:
   1239        /* set the round/straight flags */
   1240        edge->flags = AF_EDGE_NORMAL;
   1241 
   1242        if ( is_round > 0 && is_round >= is_straight )
   1243          edge->flags |= AF_EDGE_ROUND;
   1244 
   1245        /* get rid of serifs if link is set                 */
   1246        /* XXX: This gets rid of many unpleasant artefacts! */
   1247        /*      Example: the `c' in cour.pfa at size 13     */
   1248 
   1249        if ( edge->serif && edge->link )
   1250          edge->serif = NULL;
   1251      }
   1252    }
   1253 
   1254  Exit:
   1255    return error;
   1256  }
   1257 
   1258 
   1259  /* Detect segments and edges for given dimension. */
   1260 
   1261  static FT_Error
   1262  af_cjk_hints_detect_features( AF_GlyphHints  hints,
   1263                                AF_Dimension   dim )
   1264  {
   1265    FT_Error  error;
   1266 
   1267 
   1268    error = af_cjk_hints_compute_segments( hints, dim );
   1269    if ( !error )
   1270    {
   1271      af_cjk_hints_link_segments( hints, dim );
   1272 
   1273      error = af_cjk_hints_compute_edges( hints, dim );
   1274    }
   1275    return error;
   1276  }
   1277 
   1278 
   1279  /* Compute all edges which lie within blue zones. */
   1280 
   1281  static void
   1282  af_cjk_hints_compute_blue_edges( AF_GlyphHints  hints,
   1283                                   AF_CJKMetrics  metrics,
   1284                                   AF_Dimension   dim )
   1285  {
   1286    AF_AxisHints  axis       = &hints->axis[dim];
   1287    AF_Edge       edge       = axis->edges;
   1288    AF_Edge       edge_limit = FT_OFFSET( edge, axis->num_edges );
   1289    AF_CJKAxis    cjk        = &metrics->axis[dim];
   1290    FT_Fixed      scale      = cjk->scale;
   1291    FT_Pos        best_dist0;  /* initial threshold */
   1292 
   1293 
   1294    /* compute the initial threshold as a fraction of the EM size */
   1295    best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale );
   1296 
   1297    if ( best_dist0 > 64 / 2 ) /* maximum 1/2 pixel */
   1298      best_dist0 = 64 / 2;
   1299 
   1300    /* compute which blue zones are active, i.e. have their scaled */
   1301    /* size < 3/4 pixels                                           */
   1302 
   1303    /* If the distant between an edge and a blue zone is shorter than */
   1304    /* best_dist0, set the blue zone for the edge.  Then search for   */
   1305    /* the blue zone with the smallest best_dist to the edge.         */
   1306 
   1307    for ( ; edge < edge_limit; edge++ )
   1308    {
   1309      FT_UInt   bb;
   1310      AF_Width  best_blue = NULL;
   1311      FT_Pos    best_dist = best_dist0;
   1312 
   1313 
   1314      for ( bb = 0; bb < cjk->blue_count; bb++ )
   1315      {
   1316        AF_CJKBlue  blue = cjk->blues + bb;
   1317        FT_Bool     is_top_right_blue, is_major_dir;
   1318 
   1319 
   1320        /* skip inactive blue zones (i.e., those that are too small) */
   1321        if ( !( blue->flags & AF_CJK_BLUE_ACTIVE ) )
   1322          continue;
   1323 
   1324        /* if it is a top zone, check for right edges -- if it is a bottom */
   1325        /* zone, check for left edges                                      */
   1326        /*                                                                 */
   1327        /* of course, that's for TrueType                                  */
   1328        is_top_right_blue =
   1329          (FT_Byte)( ( blue->flags & AF_CJK_BLUE_TOP ) != 0 );
   1330        is_major_dir =
   1331          FT_BOOL( edge->dir == axis->major_dir );
   1332 
   1333        /* if it is a top zone, the edge must be against the major    */
   1334        /* direction; if it is a bottom zone, it must be in the major */
   1335        /* direction                                                  */
   1336        if ( is_top_right_blue ^ is_major_dir )
   1337        {
   1338          FT_Pos    dist;
   1339          AF_Width  compare;
   1340 
   1341 
   1342          /* Compare the edge to the closest blue zone type */
   1343          if ( FT_ABS( edge->fpos - blue->ref.org ) >
   1344               FT_ABS( edge->fpos - blue->shoot.org ) )
   1345            compare = &blue->shoot;
   1346          else
   1347            compare = &blue->ref;
   1348 
   1349          dist = edge->fpos - compare->org;
   1350          if ( dist < 0 )
   1351            dist = -dist;
   1352 
   1353          dist = FT_MulFix( dist, scale );
   1354          if ( dist < best_dist )
   1355          {
   1356            best_dist = dist;
   1357            best_blue = compare;
   1358          }
   1359        }
   1360      }
   1361 
   1362      if ( best_blue )
   1363        edge->blue_edge = best_blue;
   1364    }
   1365  }
   1366 
   1367 
   1368  /* Initialize hinting engine. */
   1369 
   1370  FT_LOCAL_DEF( FT_Error )
   1371  af_cjk_hints_init( AF_GlyphHints    hints,
   1372                     AF_StyleMetrics  metrics_ )   /* AF_CJKMetrics */
   1373  {
   1374    AF_CJKMetrics   metrics = (AF_CJKMetrics)metrics_;
   1375    FT_Render_Mode  mode;
   1376    FT_UInt32       scaler_flags, other_flags;
   1377 
   1378 
   1379    af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics );
   1380 
   1381    /*
   1382     * correct x_scale and y_scale when needed, since they may have
   1383     * been modified af_cjk_scale_dim above
   1384     */
   1385    hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
   1386    hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
   1387    hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
   1388    hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
   1389 
   1390    /* compute flags depending on render mode, etc. */
   1391    mode = metrics->root.scaler.render_mode;
   1392 
   1393    scaler_flags = hints->scaler_flags;
   1394    other_flags  = 0;
   1395 
   1396    /*
   1397     * We snap the width of vertical stems for the monochrome and
   1398     * horizontal LCD rendering targets only.
   1399     */
   1400    if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
   1401      other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
   1402 
   1403    /*
   1404     * We snap the width of horizontal stems for the monochrome and
   1405     * vertical LCD rendering targets only.
   1406     */
   1407    if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
   1408      other_flags |= AF_LATIN_HINTS_VERT_SNAP;
   1409 
   1410    /*
   1411     * We adjust stems to full pixels unless in `light' or `lcd' mode.
   1412     */
   1413    if ( mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD )
   1414      other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
   1415 
   1416    if ( mode == FT_RENDER_MODE_MONO )
   1417      other_flags |= AF_LATIN_HINTS_MONO;
   1418 
   1419    scaler_flags |= AF_SCALER_FLAG_NO_ADVANCE;
   1420 
   1421    hints->scaler_flags = scaler_flags;
   1422    hints->other_flags  = other_flags;
   1423 
   1424    return FT_Err_Ok;
   1425  }
   1426 
   1427 
   1428  /*************************************************************************/
   1429  /*************************************************************************/
   1430  /*****                                                               *****/
   1431  /*****          C J K   G L Y P H   G R I D - F I T T I N G          *****/
   1432  /*****                                                               *****/
   1433  /*************************************************************************/
   1434  /*************************************************************************/
   1435 
   1436  /* Snap a given width in scaled coordinates to one of the */
   1437  /* current standard widths.                               */
   1438 
   1439  static FT_Pos
   1440  af_cjk_snap_width( AF_Width  widths,
   1441                     FT_UInt   count,
   1442                     FT_Pos    width )
   1443  {
   1444    FT_UInt  n;
   1445    FT_Pos   best      = 64 + 32 + 2;
   1446    FT_Pos   reference = width;
   1447    FT_Pos   scaled;
   1448 
   1449 
   1450    for ( n = 0; n < count; n++ )
   1451    {
   1452      FT_Pos  w;
   1453      FT_Pos  dist;
   1454 
   1455 
   1456      w = widths[n].cur;
   1457      dist = width - w;
   1458      if ( dist < 0 )
   1459        dist = -dist;
   1460      if ( dist < best )
   1461      {
   1462        best      = dist;
   1463        reference = w;
   1464      }
   1465    }
   1466 
   1467    scaled = FT_PIX_ROUND( reference );
   1468 
   1469    if ( width >= reference )
   1470    {
   1471      if ( width < scaled + 48 )
   1472        width = reference;
   1473    }
   1474    else
   1475    {
   1476      if ( width > scaled - 48 )
   1477        width = reference;
   1478    }
   1479 
   1480    return width;
   1481  }
   1482 
   1483 
   1484  /* Compute the snapped width of a given stem.                          */
   1485  /* There is a lot of voodoo in this function; changing the hard-coded  */
   1486  /* parameters influence the whole hinting process.                     */
   1487 
   1488  static FT_Pos
   1489  af_cjk_compute_stem_width( AF_GlyphHints  hints,
   1490                             AF_Dimension   dim,
   1491                             FT_Pos         width,
   1492                             FT_UInt        base_flags,
   1493                             FT_UInt        stem_flags )
   1494  {
   1495    AF_CJKMetrics  metrics  = (AF_CJKMetrics)hints->metrics;
   1496    AF_CJKAxis     axis     = &metrics->axis[dim];
   1497    FT_Pos         dist     = width;
   1498    FT_Int         sign     = 0;
   1499    FT_Bool        vertical = FT_BOOL( dim == AF_DIMENSION_VERT );
   1500 
   1501    FT_UNUSED( base_flags );
   1502    FT_UNUSED( stem_flags );
   1503 
   1504 
   1505    if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
   1506      return width;
   1507 
   1508    if ( dist < 0 )
   1509    {
   1510      dist = -width;
   1511      sign = 1;
   1512    }
   1513 
   1514    if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
   1515         ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
   1516    {
   1517      /* smooth hinting process: very lightly quantize the stem width */
   1518 
   1519      if ( axis->width_count > 0 )
   1520      {
   1521        if ( FT_ABS( dist - axis->widths[0].cur ) < 40 )
   1522        {
   1523          dist = axis->widths[0].cur;
   1524          if ( dist < 48 )
   1525            dist = 48;
   1526 
   1527          goto Done_Width;
   1528        }
   1529      }
   1530 
   1531      if ( dist < 54 )
   1532        dist += ( 54 - dist ) / 2;
   1533      else if ( dist < 3 * 64 )
   1534      {
   1535        FT_Pos  delta;
   1536 
   1537 
   1538        delta  = dist & 63;
   1539        dist  &= -64;
   1540 
   1541        if ( delta < 10 )
   1542          dist += delta;
   1543        else if ( delta < 22 )
   1544          dist += 10;
   1545        else if ( delta < 42 )
   1546          dist += delta;
   1547        else if ( delta < 54 )
   1548          dist += 54;
   1549        else
   1550          dist += delta;
   1551      }
   1552    }
   1553    else
   1554    {
   1555      /* strong hinting process: snap the stem width to integer pixels */
   1556 
   1557      dist = af_cjk_snap_width( axis->widths, axis->width_count, dist );
   1558 
   1559      if ( vertical )
   1560      {
   1561        /* in the case of vertical hinting, always round */
   1562        /* the stem heights to integer pixels            */
   1563 
   1564        if ( dist >= 64 )
   1565          dist = ( dist + 16 ) & ~63;
   1566        else
   1567          dist = 64;
   1568      }
   1569      else
   1570      {
   1571        if ( AF_LATIN_HINTS_DO_MONO( hints ) )
   1572        {
   1573          /* monochrome horizontal hinting: snap widths to integer pixels */
   1574          /* with a different threshold                                   */
   1575 
   1576          if ( dist < 64 )
   1577            dist = 64;
   1578          else
   1579            dist = ( dist + 32 ) & ~63;
   1580        }
   1581        else
   1582        {
   1583          /* for horizontal anti-aliased hinting, we adopt a more subtle */
   1584          /* approach: we strengthen small stems, round stems whose size */
   1585          /* is between 1 and 2 pixels to an integer, otherwise nothing  */
   1586 
   1587          if ( dist < 48 )
   1588            dist = ( dist + 64 ) >> 1;
   1589 
   1590          else if ( dist < 128 )
   1591            dist = ( dist + 22 ) & ~63;
   1592          else
   1593            /* round otherwise to prevent color fringes in LCD mode */
   1594            dist = ( dist + 32 ) & ~63;
   1595        }
   1596      }
   1597    }
   1598 
   1599  Done_Width:
   1600    if ( sign )
   1601      dist = -dist;
   1602 
   1603    return dist;
   1604  }
   1605 
   1606 
   1607  /* Align one stem edge relative to the previous stem edge. */
   1608 
   1609  static void
   1610  af_cjk_align_linked_edge( AF_GlyphHints  hints,
   1611                            AF_Dimension   dim,
   1612                            AF_Edge        base_edge,
   1613                            AF_Edge        stem_edge )
   1614  {
   1615    FT_Pos  dist = stem_edge->opos - base_edge->opos;
   1616 
   1617    FT_Pos  fitted_width = af_cjk_compute_stem_width( hints, dim, dist,
   1618                                                      base_edge->flags,
   1619                                                      stem_edge->flags );
   1620 
   1621 
   1622    stem_edge->pos = base_edge->pos + fitted_width;
   1623 
   1624    FT_TRACE5(( "  CJKLINK: edge %td @%d (opos=%.2f) linked to %.2f,"
   1625                " dist was %.2f, now %.2f\n",
   1626                stem_edge - hints->axis[dim].edges, stem_edge->fpos,
   1627                (double)stem_edge->opos / 64,
   1628                (double)stem_edge->pos / 64,
   1629                (double)dist / 64,
   1630                (double)fitted_width / 64 ));
   1631  }
   1632 
   1633 
   1634  /* Shift the coordinates of the `serif' edge by the same amount */
   1635  /* as the corresponding `base' edge has been moved already.     */
   1636 
   1637  static void
   1638  af_cjk_align_serif_edge( AF_GlyphHints  hints,
   1639                           AF_Edge        base,
   1640                           AF_Edge        serif )
   1641  {
   1642    FT_UNUSED( hints );
   1643 
   1644    serif->pos = base->pos + ( serif->opos - base->opos );
   1645  }
   1646 
   1647 
   1648  /*************************************************************************/
   1649  /*************************************************************************/
   1650  /*************************************************************************/
   1651  /****                                                                 ****/
   1652  /****                    E D G E   H I N T I N G                      ****/
   1653  /****                                                                 ****/
   1654  /*************************************************************************/
   1655  /*************************************************************************/
   1656  /*************************************************************************/
   1657 
   1658 
   1659 #define AF_LIGHT_MODE_MAX_HORZ_GAP    9
   1660 #define AF_LIGHT_MODE_MAX_VERT_GAP   15
   1661 #define AF_LIGHT_MODE_MAX_DELTA_ABS  14
   1662 
   1663 
   1664  static FT_Pos
   1665  af_hint_normal_stem( AF_GlyphHints  hints,
   1666                       AF_Edge        edge,
   1667                       AF_Edge        edge2,
   1668                       FT_Pos         anchor,
   1669                       AF_Dimension   dim )
   1670  {
   1671    FT_Pos  org_len, cur_len, org_center;
   1672    FT_Pos  cur_pos1, cur_pos2;
   1673    FT_Pos  d_off1, u_off1, d_off2, u_off2, delta;
   1674    FT_Pos  offset;
   1675    FT_Pos  threshold = 64;
   1676 
   1677 
   1678    if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
   1679    {
   1680      if ( ( edge->flags  & AF_EDGE_ROUND ) &&
   1681           ( edge2->flags & AF_EDGE_ROUND ) )
   1682      {
   1683        if ( dim == AF_DIMENSION_VERT )
   1684          threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP;
   1685        else
   1686          threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP;
   1687      }
   1688      else
   1689      {
   1690        if ( dim == AF_DIMENSION_VERT )
   1691          threshold = 64 - AF_LIGHT_MODE_MAX_HORZ_GAP / 3;
   1692        else
   1693          threshold = 64 - AF_LIGHT_MODE_MAX_VERT_GAP / 3;
   1694      }
   1695    }
   1696 
   1697    org_len    = edge2->opos - edge->opos;
   1698    cur_len    = af_cjk_compute_stem_width( hints, dim, org_len,
   1699                                            edge->flags,
   1700                                            edge2->flags );
   1701 
   1702    org_center = ( edge->opos + edge2->opos ) / 2 + anchor;
   1703    cur_pos1   = org_center - cur_len / 2;
   1704    cur_pos2   = cur_pos1 + cur_len;
   1705    d_off1     = cur_pos1 - FT_PIX_FLOOR( cur_pos1 );
   1706    d_off2     = cur_pos2 - FT_PIX_FLOOR( cur_pos2 );
   1707    u_off1     = 64 - d_off1;
   1708    u_off2     = 64 - d_off2;
   1709    delta      = 0;
   1710 
   1711 
   1712    if ( d_off1 == 0 || d_off2 == 0 )
   1713      goto Exit;
   1714 
   1715    if ( cur_len <= threshold )
   1716    {
   1717      if ( d_off2 < cur_len )
   1718      {
   1719        if ( u_off1 <= d_off2 )
   1720          delta =  u_off1;
   1721        else
   1722          delta = -d_off2;
   1723      }
   1724 
   1725      goto Exit;
   1726    }
   1727 
   1728    if ( threshold < 64 )
   1729    {
   1730      if ( d_off1 >= threshold || u_off1 >= threshold ||
   1731           d_off2 >= threshold || u_off2 >= threshold )
   1732        goto Exit;
   1733    }
   1734 
   1735    offset = cur_len & 63;
   1736 
   1737    if ( offset < 32 )
   1738    {
   1739      if ( u_off1 <= offset || d_off2 <= offset )
   1740        goto Exit;
   1741    }
   1742    else
   1743      offset = 64 - threshold;
   1744 
   1745    d_off1 = threshold - u_off1;
   1746    u_off1 = u_off1    - offset;
   1747    u_off2 = threshold - d_off2;
   1748    d_off2 = d_off2    - offset;
   1749 
   1750    if ( d_off1 <= u_off1 )
   1751      u_off1 = -d_off1;
   1752 
   1753    if ( d_off2 <= u_off2 )
   1754      u_off2 = -d_off2;
   1755 
   1756    if ( FT_ABS( u_off1 ) <= FT_ABS( u_off2 ) )
   1757      delta = u_off1;
   1758    else
   1759      delta = u_off2;
   1760 
   1761  Exit:
   1762 
   1763 #if 1
   1764    if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) )
   1765    {
   1766      if ( delta > AF_LIGHT_MODE_MAX_DELTA_ABS )
   1767        delta = AF_LIGHT_MODE_MAX_DELTA_ABS;
   1768      else if ( delta < -AF_LIGHT_MODE_MAX_DELTA_ABS )
   1769        delta = -AF_LIGHT_MODE_MAX_DELTA_ABS;
   1770    }
   1771 #endif
   1772 
   1773    cur_pos1 += delta;
   1774 
   1775    if ( edge->opos < edge2->opos )
   1776    {
   1777      edge->pos  = cur_pos1;
   1778      edge2->pos = cur_pos1 + cur_len;
   1779    }
   1780    else
   1781    {
   1782      edge->pos  = cur_pos1 + cur_len;
   1783      edge2->pos = cur_pos1;
   1784    }
   1785 
   1786    return delta;
   1787  }
   1788 
   1789 
   1790  /* The main grid-fitting routine. */
   1791 
   1792  static void
   1793  af_cjk_hint_edges( AF_GlyphHints  hints,
   1794                     AF_Dimension   dim )
   1795  {
   1796    AF_AxisHints  axis       = &hints->axis[dim];
   1797    AF_Edge       edges      = axis->edges;
   1798    AF_Edge       edge_limit = FT_OFFSET( edges, axis->num_edges );
   1799    FT_PtrDist    n_edges;
   1800    AF_Edge       edge;
   1801    AF_Edge       anchor   = NULL;
   1802    FT_Pos        delta    = 0;
   1803    FT_Int        skipped  = 0;
   1804    FT_Bool       has_last_stem = FALSE;
   1805    FT_Pos        last_stem_pos = 0;
   1806 
   1807 #ifdef FT_DEBUG_LEVEL_TRACE
   1808    FT_UInt       num_actions = 0;
   1809 #endif
   1810 
   1811 
   1812    FT_TRACE5(( "cjk %s edge hinting (style `%s')\n",
   1813                dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
   1814                af_style_names[hints->metrics->style_class->style] ));
   1815 
   1816    /* we begin by aligning all stems relative to the blue zone */
   1817 
   1818    if ( AF_HINTS_DO_BLUES( hints ) )
   1819    {
   1820      for ( edge = edges; edge < edge_limit; edge++ )
   1821      {
   1822        AF_Width  blue;
   1823        AF_Edge   edge1, edge2;
   1824 
   1825 
   1826        if ( edge->flags & AF_EDGE_DONE )
   1827          continue;
   1828 
   1829        blue  = edge->blue_edge;
   1830        edge1 = NULL;
   1831        edge2 = edge->link;
   1832 
   1833        if ( blue )
   1834        {
   1835          edge1 = edge;
   1836        }
   1837        else if ( edge2 && edge2->blue_edge )
   1838        {
   1839          blue  = edge2->blue_edge;
   1840          edge1 = edge2;
   1841          edge2 = edge;
   1842        }
   1843 
   1844        if ( !edge1 )
   1845          continue;
   1846 
   1847 #ifdef FT_DEBUG_LEVEL_TRACE
   1848        FT_TRACE5(( "  CJKBLUE: edge %td @%d (opos=%.2f) snapped to %.2f,"
   1849                    " was %.2f\n",
   1850                    edge1 - edges, edge1->fpos, (double)edge1->opos / 64,
   1851                    (double)blue->fit / 64, (double)edge1->pos / 64 ));
   1852 
   1853        num_actions++;
   1854 #endif
   1855 
   1856        edge1->pos    = blue->fit;
   1857        edge1->flags |= AF_EDGE_DONE;
   1858 
   1859        if ( edge2 && !edge2->blue_edge )
   1860        {
   1861          af_cjk_align_linked_edge( hints, dim, edge1, edge2 );
   1862          edge2->flags |= AF_EDGE_DONE;
   1863 
   1864 #ifdef FT_DEBUG_LEVEL_TRACE
   1865          num_actions++;
   1866 #endif
   1867        }
   1868 
   1869        if ( !anchor )
   1870          anchor = edge;
   1871      }
   1872    }
   1873 
   1874    /* now we align all stem edges. */
   1875    for ( edge = edges; edge < edge_limit; edge++ )
   1876    {
   1877      AF_Edge  edge2;
   1878 
   1879 
   1880      if ( edge->flags & AF_EDGE_DONE )
   1881        continue;
   1882 
   1883      /* skip all non-stem edges */
   1884      edge2 = edge->link;
   1885      if ( !edge2 )
   1886      {
   1887        skipped++;
   1888        continue;
   1889      }
   1890 
   1891      /* Some CJK characters have so many stems that
   1892       * the hinter is likely to merge two adjacent ones.
   1893       * To solve this problem, if either edge of a stem
   1894       * is too close to the previous one, we avoid
   1895       * aligning the two edges, but rather interpolate
   1896       * their locations at the end of this function in
   1897       * order to preserve the space between the stems.
   1898       */
   1899      if ( has_last_stem                       &&
   1900           ( edge->pos  < last_stem_pos + 64 ||
   1901             edge2->pos < last_stem_pos + 64 ) )
   1902      {
   1903        skipped++;
   1904        continue;
   1905      }
   1906 
   1907      /* now align the stem */
   1908 
   1909      /* this should not happen, but it's better to be safe */
   1910      if ( edge2->blue_edge )
   1911      {
   1912        FT_TRACE5(( "ASSERTION FAILED for edge %td\n", edge2 - edges ));
   1913 
   1914        af_cjk_align_linked_edge( hints, dim, edge2, edge );
   1915        edge->flags |= AF_EDGE_DONE;
   1916 
   1917 #ifdef FT_DEBUG_LEVEL_TRACE
   1918        num_actions++;
   1919 #endif
   1920 
   1921        continue;
   1922      }
   1923 
   1924      if ( edge2 < edge )
   1925      {
   1926        af_cjk_align_linked_edge( hints, dim, edge2, edge );
   1927        edge->flags |= AF_EDGE_DONE;
   1928 
   1929 #ifdef FT_DEBUG_LEVEL_TRACE
   1930        num_actions++;
   1931 #endif
   1932 
   1933        /* We rarely reaches here it seems;
   1934         * usually the two edges belonging
   1935         * to one stem are marked as DONE together
   1936         */
   1937        has_last_stem = TRUE;
   1938        last_stem_pos = edge->pos;
   1939        continue;
   1940      }
   1941 
   1942      if ( dim != AF_DIMENSION_VERT && !anchor )
   1943      {
   1944 
   1945 #if 0
   1946        if ( fixedpitch )
   1947        {
   1948          AF_Edge     left  = edge;
   1949          AF_Edge     right = edge_limit - 1;
   1950          AF_EdgeRec  left1, left2, right1, right2;
   1951          FT_Pos      target, center1, center2;
   1952          FT_Pos      delta1, delta2, d1, d2;
   1953 
   1954 
   1955          while ( right > left && !right->link )
   1956            right--;
   1957 
   1958          left1  = *left;
   1959          left2  = *left->link;
   1960          right1 = *right->link;
   1961          right2 = *right;
   1962 
   1963          delta  = ( ( ( hinter->pp2.x + 32 ) & -64 ) - hinter->pp2.x ) / 2;
   1964          target = left->opos + ( right->opos - left->opos ) / 2 + delta - 16;
   1965 
   1966          delta1  = delta;
   1967          delta1 += af_hint_normal_stem( hints, left, left->link,
   1968                                         delta1, 0 );
   1969 
   1970          if ( left->link != right )
   1971            af_hint_normal_stem( hints, right->link, right, delta1, 0 );
   1972 
   1973          center1 = left->pos + ( right->pos - left->pos ) / 2;
   1974 
   1975          if ( center1 >= target )
   1976            delta2 = delta - 32;
   1977          else
   1978            delta2 = delta + 32;
   1979 
   1980          delta2 += af_hint_normal_stem( hints, &left1, &left2, delta2, 0 );
   1981 
   1982          if ( delta1 != delta2 )
   1983          {
   1984            if ( left->link != right )
   1985              af_hint_normal_stem( hints, &right1, &right2, delta2, 0 );
   1986 
   1987            center2 = left1.pos + ( right2.pos - left1.pos ) / 2;
   1988 
   1989            d1 = center1 - target;
   1990            d2 = center2 - target;
   1991 
   1992            if ( FT_ABS( d2 ) < FT_ABS( d1 ) )
   1993            {
   1994              left->pos       = left1.pos;
   1995              left->link->pos = left2.pos;
   1996 
   1997              if ( left->link != right )
   1998              {
   1999                right->link->pos = right1.pos;
   2000                right->pos       = right2.pos;
   2001              }
   2002 
   2003              delta1 = delta2;
   2004            }
   2005          }
   2006 
   2007          delta               = delta1;
   2008          right->link->flags |= AF_EDGE_DONE;
   2009          right->flags       |= AF_EDGE_DONE;
   2010        }
   2011        else
   2012 
   2013 #endif /* 0 */
   2014 
   2015          delta = af_hint_normal_stem( hints, edge, edge2, 0,
   2016                                       AF_DIMENSION_HORZ );
   2017      }
   2018      else
   2019        af_hint_normal_stem( hints, edge, edge2, delta, dim );
   2020 
   2021 #if 0
   2022      printf( "stem (%d,%d) adjusted (%.1f,%.1f)\n",
   2023               edge - edges, edge2 - edges,
   2024               (double)( edge->pos - edge->opos ) / 64,
   2025               (double)( edge2->pos - edge2->opos ) / 64 );
   2026 #endif
   2027 
   2028      anchor = edge;
   2029      edge->flags  |= AF_EDGE_DONE;
   2030      edge2->flags |= AF_EDGE_DONE;
   2031      has_last_stem = TRUE;
   2032      last_stem_pos = edge2->pos;
   2033    }
   2034 
   2035    /* make sure that lowercase m's maintain their symmetry */
   2036 
   2037    /* In general, lowercase m's have six vertical edges if they are sans */
   2038    /* serif, or twelve if they are with serifs.  This implementation is  */
   2039    /* based on that assumption, and seems to work very well with most    */
   2040    /* faces.  However, if for a certain face this assumption is not      */
   2041    /* true, the m is just rendered like before.  In addition, any stem   */
   2042    /* correction will only be applied to symmetrical glyphs (even if the */
   2043    /* glyph is not an m), so the potential for unwanted distortion is    */
   2044    /* relatively low.                                                    */
   2045 
   2046    /* We don't handle horizontal edges since we can't easily assure that */
   2047    /* the third (lowest) stem aligns with the base line; it might end up */
   2048    /* one pixel higher or lower.                                         */
   2049 
   2050    n_edges = edge_limit - edges;
   2051    if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
   2052    {
   2053      AF_Edge  edge1, edge2, edge3;
   2054      FT_Pos   dist1, dist2, span;
   2055 
   2056 
   2057      if ( n_edges == 6 )
   2058      {
   2059        edge1 = edges;
   2060        edge2 = edges + 2;
   2061        edge3 = edges + 4;
   2062      }
   2063      else
   2064      {
   2065        edge1 = edges + 1;
   2066        edge2 = edges + 5;
   2067        edge3 = edges + 9;
   2068      }
   2069 
   2070      dist1 = edge2->opos - edge1->opos;
   2071      dist2 = edge3->opos - edge2->opos;
   2072 
   2073      span = dist1 - dist2;
   2074      if ( span < 0 )
   2075        span = -span;
   2076 
   2077      if ( edge1->link == edge1 + 1 &&
   2078           edge2->link == edge2 + 1 &&
   2079           edge3->link == edge3 + 1 && span < 8 )
   2080      {
   2081        delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
   2082        edge3->pos -= delta;
   2083        if ( edge3->link )
   2084          edge3->link->pos -= delta;
   2085 
   2086        /* move the serifs along with the stem */
   2087        if ( n_edges == 12 )
   2088        {
   2089          ( edges + 8 )->pos -= delta;
   2090          ( edges + 11 )->pos -= delta;
   2091        }
   2092 
   2093        edge3->flags |= AF_EDGE_DONE;
   2094        if ( edge3->link )
   2095          edge3->link->flags |= AF_EDGE_DONE;
   2096      }
   2097    }
   2098 
   2099    if ( !skipped )
   2100      goto Exit;
   2101 
   2102    /*
   2103     * now hint the remaining edges (serifs and single) in order
   2104     * to complete our processing
   2105     */
   2106    for ( edge = edges; edge < edge_limit; edge++ )
   2107    {
   2108      if ( edge->flags & AF_EDGE_DONE )
   2109        continue;
   2110 
   2111      if ( edge->serif )
   2112      {
   2113        af_cjk_align_serif_edge( hints, edge->serif, edge );
   2114        edge->flags |= AF_EDGE_DONE;
   2115        skipped--;
   2116      }
   2117    }
   2118 
   2119    if ( !skipped )
   2120      goto Exit;
   2121 
   2122    for ( edge = edges; edge < edge_limit; edge++ )
   2123    {
   2124      AF_Edge  before, after;
   2125 
   2126 
   2127      if ( edge->flags & AF_EDGE_DONE )
   2128        continue;
   2129 
   2130      before = after = edge;
   2131 
   2132      while ( --before >= edges )
   2133        if ( before->flags & AF_EDGE_DONE )
   2134          break;
   2135 
   2136      while ( ++after < edge_limit )
   2137        if ( after->flags & AF_EDGE_DONE )
   2138          break;
   2139 
   2140      if ( before >= edges || after < edge_limit )
   2141      {
   2142        if ( before < edges )
   2143          af_cjk_align_serif_edge( hints, after, edge );
   2144        else if ( after >= edge_limit )
   2145          af_cjk_align_serif_edge( hints, before, edge );
   2146        else
   2147        {
   2148          if ( after->fpos == before->fpos )
   2149            edge->pos = before->pos;
   2150          else
   2151            edge->pos = before->pos +
   2152                        FT_MulDiv( edge->fpos - before->fpos,
   2153                                   after->pos - before->pos,
   2154                                   after->fpos - before->fpos );
   2155        }
   2156      }
   2157    }
   2158 
   2159  Exit:
   2160 
   2161 #ifdef FT_DEBUG_LEVEL_TRACE
   2162    if ( !num_actions )
   2163      FT_TRACE5(( "  (none)\n" ));
   2164    FT_TRACE5(( "\n" ));
   2165 #endif
   2166 
   2167    return;
   2168  }
   2169 
   2170 
   2171  static void
   2172  af_cjk_align_edge_points( AF_GlyphHints  hints,
   2173                            AF_Dimension   dim )
   2174  {
   2175    AF_AxisHints  axis       = &hints->axis[dim];
   2176    AF_Edge       edges      = axis->edges;
   2177    AF_Edge       edge_limit = FT_OFFSET( edges, axis->num_edges );
   2178    AF_Edge       edge;
   2179    FT_Bool       snapping;
   2180 
   2181 
   2182    snapping = FT_BOOL( ( dim == AF_DIMENSION_HORZ             &&
   2183                          AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) )  ||
   2184                        ( dim == AF_DIMENSION_VERT             &&
   2185                          AF_LATIN_HINTS_DO_VERT_SNAP( hints ) )  );
   2186 
   2187    for ( edge = edges; edge < edge_limit; edge++ )
   2188    {
   2189      /* move the points of each segment     */
   2190      /* in each edge to the edge's position */
   2191      AF_Segment  seg = edge->first;
   2192 
   2193 
   2194      if ( snapping )
   2195      {
   2196        do
   2197        {
   2198          AF_Point  point = seg->first;
   2199 
   2200 
   2201          for (;;)
   2202          {
   2203            if ( dim == AF_DIMENSION_HORZ )
   2204            {
   2205              point->x      = edge->pos;
   2206              point->flags |= AF_FLAG_TOUCH_X;
   2207            }
   2208            else
   2209            {
   2210              point->y      = edge->pos;
   2211              point->flags |= AF_FLAG_TOUCH_Y;
   2212            }
   2213 
   2214            if ( point == seg->last )
   2215              break;
   2216 
   2217            point = point->next;
   2218          }
   2219 
   2220          seg = seg->edge_next;
   2221 
   2222        } while ( seg != edge->first );
   2223      }
   2224      else
   2225      {
   2226        FT_Pos  delta = edge->pos - edge->opos;
   2227 
   2228 
   2229        do
   2230        {
   2231          AF_Point  point = seg->first;
   2232 
   2233 
   2234          for (;;)
   2235          {
   2236            if ( dim == AF_DIMENSION_HORZ )
   2237            {
   2238              point->x     += delta;
   2239              point->flags |= AF_FLAG_TOUCH_X;
   2240            }
   2241            else
   2242            {
   2243              point->y     += delta;
   2244              point->flags |= AF_FLAG_TOUCH_Y;
   2245            }
   2246 
   2247            if ( point == seg->last )
   2248              break;
   2249 
   2250            point = point->next;
   2251          }
   2252 
   2253          seg = seg->edge_next;
   2254 
   2255        } while ( seg != edge->first );
   2256      }
   2257    }
   2258  }
   2259 
   2260 
   2261  /* Apply the complete hinting algorithm to a CJK glyph. */
   2262 
   2263  FT_LOCAL_DEF( FT_Error )
   2264  af_cjk_hints_apply( FT_UInt          glyph_index,
   2265                      AF_GlyphHints    hints,
   2266                      FT_Outline*      outline,
   2267                      AF_StyleMetrics  metrics_ )   /* AF_CJKMetrics */
   2268  {
   2269    AF_CJKMetrics  metrics = (AF_CJKMetrics)metrics_;
   2270 
   2271    FT_Error  error;
   2272    int       dim;
   2273 
   2274    FT_UNUSED( metrics );
   2275    FT_UNUSED( glyph_index );
   2276 
   2277 
   2278    error = af_glyph_hints_reload( hints, outline );
   2279    if ( error )
   2280      goto Exit;
   2281 
   2282    /* analyze glyph outline */
   2283    if ( AF_HINTS_DO_HORIZONTAL( hints ) )
   2284    {
   2285      error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ );
   2286      if ( error )
   2287        goto Exit;
   2288 
   2289      af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_HORZ );
   2290    }
   2291 
   2292    if ( AF_HINTS_DO_VERTICAL( hints ) )
   2293    {
   2294      error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT );
   2295      if ( error )
   2296        goto Exit;
   2297 
   2298      af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_VERT );
   2299    }
   2300 
   2301    /* grid-fit the outline */
   2302    for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
   2303    {
   2304      if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
   2305           ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) )   )
   2306      {
   2307        af_cjk_hint_edges( hints, (AF_Dimension)dim );
   2308        af_cjk_align_edge_points( hints, (AF_Dimension)dim );
   2309        af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
   2310        af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
   2311      }
   2312    }
   2313 
   2314    af_glyph_hints_save( hints, outline );
   2315 
   2316  Exit:
   2317    return error;
   2318  }
   2319 
   2320 
   2321  /*************************************************************************/
   2322  /*************************************************************************/
   2323  /*****                                                               *****/
   2324  /*****                C J K   S C R I P T   C L A S S                *****/
   2325  /*****                                                               *****/
   2326  /*************************************************************************/
   2327  /*************************************************************************/
   2328 
   2329 
   2330  AF_DEFINE_WRITING_SYSTEM_CLASS(
   2331    af_cjk_writing_system_class,
   2332 
   2333    AF_WRITING_SYSTEM_CJK,
   2334 
   2335    sizeof ( AF_CJKMetricsRec ),
   2336 
   2337    (AF_WritingSystem_InitMetricsFunc) af_cjk_metrics_init,        /* style_metrics_init    */
   2338    (AF_WritingSystem_ScaleMetricsFunc)af_cjk_metrics_scale,       /* style_metrics_scale   */
   2339    (AF_WritingSystem_DoneMetricsFunc) NULL,                       /* style_metrics_done    */
   2340    (AF_WritingSystem_GetStdWidthsFunc)af_cjk_get_standard_widths, /* style_metrics_getstdw */
   2341 
   2342    (AF_WritingSystem_InitHintsFunc)   af_cjk_hints_init,          /* style_hints_init      */
   2343    (AF_WritingSystem_ApplyHintsFunc)  af_cjk_hints_apply          /* style_hints_apply     */
   2344  )
   2345 
   2346 
   2347 #else /* !AF_CONFIG_OPTION_CJK */
   2348 
   2349 
   2350  AF_DEFINE_WRITING_SYSTEM_CLASS(
   2351    af_cjk_writing_system_class,
   2352 
   2353    AF_WRITING_SYSTEM_CJK,
   2354 
   2355    sizeof ( AF_CJKMetricsRec ),
   2356 
   2357    (AF_WritingSystem_InitMetricsFunc) NULL, /* style_metrics_init    */
   2358    (AF_WritingSystem_ScaleMetricsFunc)NULL, /* style_metrics_scale   */
   2359    (AF_WritingSystem_DoneMetricsFunc) NULL, /* style_metrics_done    */
   2360    (AF_WritingSystem_GetStdWidthsFunc)NULL, /* style_metrics_getstdw */
   2361 
   2362    (AF_WritingSystem_InitHintsFunc)   NULL, /* style_hints_init      */
   2363    (AF_WritingSystem_ApplyHintsFunc)  NULL  /* style_hints_apply     */
   2364  )
   2365 
   2366 
   2367 #endif /* !AF_CONFIG_OPTION_CJK */
   2368 
   2369 
   2370 /* END */