"Fossies" - the Fresh Open Source Software Archive

Member "SDL2_ttf-2.20.2/external/freetype/src/autofit/aflatin.c" (25 May 2022, 113147 Bytes) of package /linux/misc/SDL2_ttf-2.20.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "aflatin.c" see the Fossies "Dox" file reference documentation.

    1 /****************************************************************************
    2  *
    3  * aflatin.c
    4  *
    5  *   Auto-fitter hinting routines for latin writing system (body).
    6  *
    7  * Copyright (C) 2003-2022 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 
   26 
   27   /**************************************************************************
   28    *
   29    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   30    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   31    * messages during execution.
   32    */
   33 #undef  FT_COMPONENT
   34 #define FT_COMPONENT  aflatin
   35 
   36 
   37   /* needed for computation of round vs. flat segments */
   38 #define FLAT_THRESHOLD( x )  ( x / 14 )
   39 
   40 
   41   /*************************************************************************/
   42   /*************************************************************************/
   43   /*****                                                               *****/
   44   /*****            L A T I N   G L O B A L   M E T R I C S            *****/
   45   /*****                                                               *****/
   46   /*************************************************************************/
   47   /*************************************************************************/
   48 
   49 
   50   /* Find segments and links, compute all stem widths, and initialize */
   51   /* standard width and height for the glyph with given charcode.     */
   52 
   53   FT_LOCAL_DEF( void )
   54   af_latin_metrics_init_widths( AF_LatinMetrics  metrics,
   55                                 FT_Face          face )
   56   {
   57     /* scan the array of segments in each direction */
   58     AF_GlyphHintsRec  hints[1];
   59 
   60 
   61     FT_TRACE5(( "\n" ));
   62     FT_TRACE5(( "latin standard widths computation (style `%s')\n",
   63                 af_style_names[metrics->root.style_class->style] ));
   64     FT_TRACE5(( "=====================================================\n" ));
   65     FT_TRACE5(( "\n" ));
   66 
   67     af_glyph_hints_init( hints, face->memory );
   68 
   69     metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
   70     metrics->axis[AF_DIMENSION_VERT].width_count = 0;
   71 
   72     {
   73       FT_Error            error;
   74       FT_ULong            glyph_index;
   75       int                 dim;
   76       AF_LatinMetricsRec  dummy[1];
   77       AF_Scaler           scaler = &dummy->root.scaler;
   78 
   79       AF_StyleClass   style_class  = metrics->root.style_class;
   80       AF_ScriptClass  script_class = af_script_classes[style_class->script];
   81 
   82       /* If HarfBuzz is not available, we need a pointer to a single */
   83       /* unsigned long value.                                        */
   84 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
   85       void*     shaper_buf;
   86 #else
   87       FT_ULong  shaper_buf_;
   88       void*     shaper_buf = &shaper_buf_;
   89 #endif
   90 
   91       const char*  p;
   92 
   93 #ifdef FT_DEBUG_LEVEL_TRACE
   94       FT_ULong  ch = 0;
   95 #endif
   96 
   97 
   98       p = script_class->standard_charstring;
   99 
  100 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
  101       shaper_buf = af_shaper_buf_create( face );
  102 #endif
  103       /*
  104        * We check a list of standard characters to catch features like
  105        * `c2sc' (small caps from caps) that don't contain lowercase letters
  106        * by definition, or other features that mainly operate on numerals.
  107        * The first match wins.
  108        */
  109 
  110       glyph_index = 0;
  111       while ( *p )
  112       {
  113         unsigned int  num_idx;
  114 
  115 #ifdef FT_DEBUG_LEVEL_TRACE
  116         const char*  p_old;
  117 #endif
  118 
  119 
  120         while ( *p == ' ' )
  121           p++;
  122 
  123 #ifdef FT_DEBUG_LEVEL_TRACE
  124         p_old = p;
  125         GET_UTF8_CHAR( ch, p_old );
  126 #endif
  127 
  128         /* reject input that maps to more than a single glyph */
  129         p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
  130         if ( num_idx > 1 )
  131           continue;
  132 
  133         /* otherwise exit loop if we have a result */
  134         glyph_index = af_shaper_get_elem( &metrics->root,
  135                                           shaper_buf,
  136                                           0,
  137                                           NULL,
  138                                           NULL );
  139         if ( glyph_index )
  140           break;
  141       }
  142 
  143       af_shaper_buf_destroy( face, shaper_buf );
  144 
  145       if ( !glyph_index )
  146       {
  147         FT_TRACE5(( "standard character missing;"
  148                     " using fallback stem widths\n" ));
  149         goto Exit;
  150       }
  151 
  152       FT_TRACE5(( "standard character: U+%04lX (glyph index %ld)\n",
  153                   ch, glyph_index ));
  154 
  155       error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
  156       if ( error || face->glyph->outline.n_points <= 0 )
  157         goto Exit;
  158 
  159       FT_ZERO( dummy );
  160 
  161       dummy->units_per_em = metrics->units_per_em;
  162 
  163       scaler->x_scale = 0x10000L;
  164       scaler->y_scale = 0x10000L;
  165       scaler->x_delta = 0;
  166       scaler->y_delta = 0;
  167 
  168       scaler->face        = face;
  169       scaler->render_mode = FT_RENDER_MODE_NORMAL;
  170       scaler->flags       = 0;
  171 
  172       af_glyph_hints_rescale( hints, (AF_StyleMetrics)dummy );
  173 
  174       error = af_glyph_hints_reload( hints, &face->glyph->outline );
  175       if ( error )
  176         goto Exit;
  177 
  178       for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
  179       {
  180         AF_LatinAxis  axis    = &metrics->axis[dim];
  181         AF_AxisHints  axhints = &hints->axis[dim];
  182         AF_Segment    seg, limit, link;
  183         FT_UInt       num_widths = 0;
  184 
  185 
  186         error = af_latin_hints_compute_segments( hints,
  187                                                  (AF_Dimension)dim );
  188         if ( error )
  189           goto Exit;
  190 
  191         /*
  192          * We assume that the glyphs selected for the stem width
  193          * computation are `featureless' enough so that the linking
  194          * algorithm works fine without adjustments of its scoring
  195          * function.
  196          */
  197         af_latin_hints_link_segments( hints,
  198                                       0,
  199                                       NULL,
  200                                       (AF_Dimension)dim );
  201 
  202         seg   = axhints->segments;
  203         limit = FT_OFFSET( seg, axhints->num_segments );
  204 
  205         for ( ; seg < limit; seg++ )
  206         {
  207           link = seg->link;
  208 
  209           /* we only consider stem segments there! */
  210           if ( link && link->link == seg && link > seg )
  211           {
  212             FT_Pos  dist;
  213 
  214 
  215             dist = seg->pos - link->pos;
  216             if ( dist < 0 )
  217               dist = -dist;
  218 
  219             if ( num_widths < AF_LATIN_MAX_WIDTHS )
  220               axis->widths[num_widths++].org = dist;
  221           }
  222         }
  223 
  224         /* this also replaces multiple almost identical stem widths */
  225         /* with a single one (the value 100 is heuristic)           */
  226         af_sort_and_quantize_widths( &num_widths, axis->widths,
  227                                      dummy->units_per_em / 100 );
  228         axis->width_count = num_widths;
  229       }
  230 
  231     Exit:
  232       for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
  233       {
  234         AF_LatinAxis  axis = &metrics->axis[dim];
  235         FT_Pos        stdw;
  236 
  237 
  238         stdw = ( axis->width_count > 0 ) ? axis->widths[0].org
  239                                          : AF_LATIN_CONSTANT( metrics, 50 );
  240 
  241         /* let's try 20% of the smallest width */
  242         axis->edge_distance_threshold = stdw / 5;
  243         axis->standard_width          = stdw;
  244         axis->extra_light             = 0;
  245 
  246 #ifdef FT_DEBUG_LEVEL_TRACE
  247         {
  248           FT_UInt  i;
  249 
  250 
  251           FT_TRACE5(( "%s widths:\n",
  252                       dim == AF_DIMENSION_VERT ? "horizontal"
  253                                                : "vertical" ));
  254 
  255           FT_TRACE5(( "  %ld (standard)", axis->standard_width ));
  256           for ( i = 1; i < axis->width_count; i++ )
  257             FT_TRACE5(( " %ld", axis->widths[i].org ));
  258 
  259           FT_TRACE5(( "\n" ));
  260         }
  261 #endif
  262       }
  263     }
  264 
  265     FT_TRACE5(( "\n" ));
  266 
  267     af_glyph_hints_done( hints );
  268   }
  269 
  270 
  271   static void
  272   af_latin_sort_blue( FT_UInt        count,
  273                       AF_LatinBlue*  table )
  274   {
  275     FT_UInt       i, j;
  276     AF_LatinBlue  swap;
  277 
  278 
  279     /* we sort from bottom to top */
  280     for ( i = 1; i < count; i++ )
  281     {
  282       for ( j = i; j > 0; j-- )
  283       {
  284         FT_Pos  a, b;
  285 
  286 
  287         if ( table[j - 1]->flags & ( AF_LATIN_BLUE_TOP     |
  288                                      AF_LATIN_BLUE_SUB_TOP ) )
  289           a = table[j - 1]->ref.org;
  290         else
  291           a = table[j - 1]->shoot.org;
  292 
  293         if ( table[j]->flags & ( AF_LATIN_BLUE_TOP     |
  294                                  AF_LATIN_BLUE_SUB_TOP ) )
  295           b = table[j]->ref.org;
  296         else
  297           b = table[j]->shoot.org;
  298 
  299         if ( b >= a )
  300           break;
  301 
  302         swap         = table[j];
  303         table[j]     = table[j - 1];
  304         table[j - 1] = swap;
  305       }
  306     }
  307   }
  308 
  309 
  310   /* Find all blue zones.  Flat segments give the reference points, */
  311   /* round segments the overshoot positions.                        */
  312 
  313   static int
  314   af_latin_metrics_init_blues( AF_LatinMetrics  metrics,
  315                                FT_Face          face )
  316   {
  317     FT_Pos        flats [AF_BLUE_STRING_MAX_LEN];
  318     FT_Pos        rounds[AF_BLUE_STRING_MAX_LEN];
  319 
  320     FT_UInt       num_flats;
  321     FT_UInt       num_rounds;
  322 
  323     AF_LatinBlue  blue;
  324     FT_Error      error;
  325     AF_LatinAxis  axis = &metrics->axis[AF_DIMENSION_VERT];
  326     FT_Outline    outline;
  327 
  328     AF_StyleClass  sc = metrics->root.style_class;
  329 
  330     AF_Blue_Stringset         bss = sc->blue_stringset;
  331     const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
  332 
  333     FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
  334 
  335     /* If HarfBuzz is not available, we need a pointer to a single */
  336     /* unsigned long value.                                        */
  337 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
  338     void*     shaper_buf;
  339 #else
  340     FT_ULong  shaper_buf_;
  341     void*     shaper_buf = &shaper_buf_;
  342 #endif
  343 
  344 
  345     /* we walk over the blue character strings as specified in the */
  346     /* style's entry in the `af_blue_stringset' array              */
  347 
  348     FT_TRACE5(( "latin blue zones computation\n" ));
  349     FT_TRACE5(( "============================\n" ));
  350     FT_TRACE5(( "\n" ));
  351 
  352 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
  353     shaper_buf = af_shaper_buf_create( face );
  354 #endif
  355 
  356     for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
  357     {
  358       const char*  p = &af_blue_strings[bs->string];
  359       FT_Pos*      blue_ref;
  360       FT_Pos*      blue_shoot;
  361       FT_Pos       ascender;
  362       FT_Pos       descender;
  363 
  364 
  365 #ifdef FT_DEBUG_LEVEL_TRACE
  366       {
  367         FT_Bool  have_flag = 0;
  368 
  369 
  370         FT_TRACE5(( "blue zone %d", axis->blue_count ));
  371 
  372         if ( bs->properties )
  373         {
  374           FT_TRACE5(( " (" ));
  375 
  376           if ( AF_LATIN_IS_TOP_BLUE( bs ) )
  377           {
  378             FT_TRACE5(( "top" ));
  379             have_flag = 1;
  380           }
  381           else if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) )
  382           {
  383             FT_TRACE5(( "sub top" ));
  384             have_flag = 1;
  385           }
  386 
  387           if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
  388           {
  389             if ( have_flag )
  390               FT_TRACE5(( ", " ));
  391             FT_TRACE5(( "neutral" ));
  392             have_flag = 1;
  393           }
  394 
  395           if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) )
  396           {
  397             if ( have_flag )
  398               FT_TRACE5(( ", " ));
  399             FT_TRACE5(( "small top" ));
  400             have_flag = 1;
  401           }
  402 
  403           if ( AF_LATIN_IS_LONG_BLUE( bs ) )
  404           {
  405             if ( have_flag )
  406               FT_TRACE5(( ", " ));
  407             FT_TRACE5(( "long" ));
  408           }
  409 
  410           FT_TRACE5(( ")" ));
  411         }
  412 
  413         FT_TRACE5(( ":\n" ));
  414       }
  415 #endif /* FT_DEBUG_LEVEL_TRACE */
  416 
  417       num_flats  = 0;
  418       num_rounds = 0;
  419       ascender   = 0;
  420       descender  = 0;
  421 
  422       while ( *p )
  423       {
  424         FT_ULong    glyph_index;
  425         FT_Long     y_offset;
  426         FT_Int      best_point, best_contour_first, best_contour_last;
  427         FT_Vector*  points;
  428 
  429         FT_Pos   best_y_extremum;                      /* same as points.y */
  430         FT_Bool  best_round = 0;
  431 
  432         unsigned int  i, num_idx;
  433 
  434 #ifdef FT_DEBUG_LEVEL_TRACE
  435         const char*  p_old;
  436         FT_ULong     ch;
  437 #endif
  438 
  439 
  440         while ( *p == ' ' )
  441           p++;
  442 
  443 #ifdef FT_DEBUG_LEVEL_TRACE
  444         p_old = p;
  445         GET_UTF8_CHAR( ch, p_old );
  446 #endif
  447 
  448         p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
  449 
  450         if ( !num_idx )
  451         {
  452           FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
  453           continue;
  454         }
  455 
  456         if ( AF_LATIN_IS_TOP_BLUE( bs ) )
  457           best_y_extremum = FT_INT_MIN;
  458         else
  459           best_y_extremum = FT_INT_MAX;
  460 
  461         /* iterate over all glyph elements of the character cluster */
  462         /* and get the data of the `biggest' one                    */
  463         for ( i = 0; i < num_idx; i++ )
  464         {
  465           FT_Pos   best_y;
  466           FT_Bool  round = 0;
  467 
  468 
  469           /* load the character in the face -- skip unknown or empty ones */
  470           glyph_index = af_shaper_get_elem( &metrics->root,
  471                                             shaper_buf,
  472                                             i,
  473                                             NULL,
  474                                             &y_offset );
  475           if ( glyph_index == 0 )
  476           {
  477             FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
  478             continue;
  479           }
  480 
  481           error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
  482           outline = face->glyph->outline;
  483           /* reject glyphs that don't produce any rendering */
  484           if ( error || outline.n_points <= 2 )
  485           {
  486 #ifdef FT_DEBUG_LEVEL_TRACE
  487             if ( num_idx == 1 )
  488               FT_TRACE5(( "  U+%04lX contains no (usable) outlines\n", ch ));
  489             else
  490               FT_TRACE5(( "  component %d of cluster starting with U+%04lX"
  491                           " contains no (usable) outlines\n", i, ch ));
  492 #endif
  493             continue;
  494           }
  495 
  496           /* now compute min or max point indices and coordinates */
  497           points             = outline.points;
  498           best_point         = -1;
  499           best_y             = 0;  /* make compiler happy */
  500           best_contour_first = 0;  /* ditto */
  501           best_contour_last  = 0;  /* ditto */
  502 
  503           {
  504             FT_Int  nn;
  505             FT_Int  first = 0;
  506             FT_Int  last  = -1;
  507 
  508 
  509             for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
  510             {
  511               FT_Int  old_best_point = best_point;
  512               FT_Int  pp;
  513 
  514 
  515               last = outline.contours[nn];
  516 
  517               /* Avoid single-point contours since they are never      */
  518               /* rasterized.  In some fonts, they correspond to mark   */
  519               /* attachment points that are way outside of the glyph's */
  520               /* real outline.                                         */
  521               if ( last <= first )
  522                 continue;
  523 
  524               if ( AF_LATIN_IS_TOP_BLUE( bs )     ||
  525                    AF_LATIN_IS_SUB_TOP_BLUE( bs ) )
  526               {
  527                 for ( pp = first; pp <= last; pp++ )
  528                 {
  529                   if ( best_point < 0 || points[pp].y > best_y )
  530                   {
  531                     best_point = pp;
  532                     best_y     = points[pp].y;
  533                     ascender   = FT_MAX( ascender, best_y + y_offset );
  534                   }
  535                   else
  536                     descender = FT_MIN( descender, points[pp].y + y_offset );
  537                 }
  538               }
  539               else
  540               {
  541                 for ( pp = first; pp <= last; pp++ )
  542                 {
  543                   if ( best_point < 0 || points[pp].y < best_y )
  544                   {
  545                     best_point = pp;
  546                     best_y     = points[pp].y;
  547                     descender  = FT_MIN( descender, best_y + y_offset );
  548                   }
  549                   else
  550                     ascender = FT_MAX( ascender, points[pp].y + y_offset );
  551                 }
  552               }
  553 
  554               if ( best_point != old_best_point )
  555               {
  556                 best_contour_first = first;
  557                 best_contour_last  = last;
  558               }
  559             }
  560           }
  561 
  562           /* now check whether the point belongs to a straight or round   */
  563           /* segment; we first need to find in which contour the extremum */
  564           /* lies, then inspect its previous and next points              */
  565           if ( best_point >= 0 )
  566           {
  567             FT_Pos  best_x = points[best_point].x;
  568             FT_Int  prev, next;
  569             FT_Int  best_segment_first, best_segment_last;
  570             FT_Int  best_on_point_first, best_on_point_last;
  571             FT_Pos  dist;
  572 
  573 
  574             best_segment_first = best_point;
  575             best_segment_last  = best_point;
  576 
  577             if ( FT_CURVE_TAG( outline.tags[best_point] ) == FT_CURVE_TAG_ON )
  578             {
  579               best_on_point_first = best_point;
  580               best_on_point_last  = best_point;
  581             }
  582             else
  583             {
  584               best_on_point_first = -1;
  585               best_on_point_last  = -1;
  586             }
  587 
  588             /* look for the previous and next points on the contour  */
  589             /* that are not on the same Y coordinate, then threshold */
  590             /* the `closeness'...                                    */
  591             prev = best_point;
  592             next = prev;
  593 
  594             do
  595             {
  596               if ( prev > best_contour_first )
  597                 prev--;
  598               else
  599                 prev = best_contour_last;
  600 
  601               dist = FT_ABS( points[prev].y - best_y );
  602               /* accept a small distance or a small angle (both values are */
  603               /* heuristic; value 20 corresponds to approx. 2.9 degrees)   */
  604               if ( dist > 5 )
  605                 if ( FT_ABS( points[prev].x - best_x ) <= 20 * dist )
  606                   break;
  607 
  608               best_segment_first = prev;
  609 
  610               if ( FT_CURVE_TAG( outline.tags[prev] ) == FT_CURVE_TAG_ON )
  611               {
  612                 best_on_point_first = prev;
  613                 if ( best_on_point_last < 0 )
  614                   best_on_point_last = prev;
  615               }
  616 
  617             } while ( prev != best_point );
  618 
  619             do
  620             {
  621               if ( next < best_contour_last )
  622                 next++;
  623               else
  624                 next = best_contour_first;
  625 
  626               dist = FT_ABS( points[next].y - best_y );
  627               if ( dist > 5 )
  628                 if ( FT_ABS( points[next].x - best_x ) <= 20 * dist )
  629                   break;
  630 
  631               best_segment_last = next;
  632 
  633               if ( FT_CURVE_TAG( outline.tags[next] ) == FT_CURVE_TAG_ON )
  634               {
  635                 best_on_point_last = next;
  636                 if ( best_on_point_first < 0 )
  637                   best_on_point_first = next;
  638               }
  639 
  640             } while ( next != best_point );
  641 
  642             if ( AF_LATIN_IS_LONG_BLUE( bs ) )
  643             {
  644               /* If this flag is set, we have an additional constraint to  */
  645               /* get the blue zone distance: Find a segment of the topmost */
  646               /* (or bottommost) contour that is longer than a heuristic   */
  647               /* threshold.  This ensures that small bumps in the outline  */
  648               /* are ignored (for example, the `vertical serifs' found in  */
  649               /* many Hebrew glyph designs).                               */
  650 
  651               /* If this segment is long enough, we are done.  Otherwise,  */
  652               /* search the segment next to the extremum that is long      */
  653               /* enough, has the same direction, and a not too large       */
  654               /* vertical distance from the extremum.  Note that the       */
  655               /* algorithm doesn't check whether the found segment is      */
  656               /* actually the one (vertically) nearest to the extremum.    */
  657 
  658               /* heuristic threshold value */
  659               FT_Pos  length_threshold = metrics->units_per_em / 25;
  660 
  661 
  662               dist = FT_ABS( points[best_segment_last].x -
  663                                points[best_segment_first].x );
  664 
  665               if ( dist < length_threshold                       &&
  666                    best_segment_last - best_segment_first + 2 <=
  667                      best_contour_last - best_contour_first      )
  668               {
  669                 /* heuristic threshold value */
  670                 FT_Pos  height_threshold = metrics->units_per_em / 4;
  671 
  672                 FT_Int   first;
  673                 FT_Int   last;
  674                 FT_Bool  hit;
  675 
  676                 /* we intentionally declare these two variables        */
  677                 /* outside of the loop since various compilers emit    */
  678                 /* incorrect warning messages otherwise, talking about */
  679                 /* `possibly uninitialized variables'                  */
  680                 FT_Int  p_first = 0;            /* make compiler happy */
  681                 FT_Int  p_last  = 0;
  682 
  683                 FT_Bool  left2right;
  684 
  685 
  686                 /* compute direction */
  687                 prev = best_point;
  688 
  689                 do
  690                 {
  691                   if ( prev > best_contour_first )
  692                     prev--;
  693                   else
  694                     prev = best_contour_last;
  695 
  696                   if ( points[prev].x != best_x )
  697                     break;
  698 
  699                 } while ( prev != best_point );
  700 
  701                 /* skip glyph for the degenerate case */
  702                 if ( prev == best_point )
  703                   continue;
  704 
  705                 left2right = FT_BOOL( points[prev].x < points[best_point].x );
  706 
  707                 first = best_segment_last;
  708                 last  = first;
  709                 hit   = 0;
  710 
  711                 do
  712                 {
  713                   FT_Bool  l2r;
  714                   FT_Pos   d;
  715 
  716 
  717                   if ( !hit )
  718                   {
  719                     /* no hit; adjust first point */
  720                     first = last;
  721 
  722                     /* also adjust first and last on point */
  723                     if ( FT_CURVE_TAG( outline.tags[first] ) ==
  724                            FT_CURVE_TAG_ON )
  725                     {
  726                       p_first = first;
  727                       p_last  = first;
  728                     }
  729                     else
  730                     {
  731                       p_first = -1;
  732                       p_last  = -1;
  733                     }
  734 
  735                     hit = 1;
  736                   }
  737 
  738                   if ( last < best_contour_last )
  739                     last++;
  740                   else
  741                     last = best_contour_first;
  742 
  743                   if ( FT_ABS( best_y - points[first].y ) > height_threshold )
  744                   {
  745                     /* vertical distance too large */
  746                     hit = 0;
  747                     continue;
  748                   }
  749 
  750                   /* same test as above */
  751                   dist = FT_ABS( points[last].y - points[first].y );
  752                   if ( dist > 5 )
  753                     if ( FT_ABS( points[last].x - points[first].x ) <=
  754                            20 * dist )
  755                     {
  756                       hit = 0;
  757                       continue;
  758                     }
  759 
  760                   if ( FT_CURVE_TAG( outline.tags[last] ) == FT_CURVE_TAG_ON )
  761                   {
  762                     p_last = last;
  763                     if ( p_first < 0 )
  764                       p_first = last;
  765                   }
  766 
  767                   l2r = FT_BOOL( points[first].x < points[last].x );
  768                   d   = FT_ABS( points[last].x - points[first].x );
  769 
  770                   if ( l2r == left2right     &&
  771                        d >= length_threshold )
  772                   {
  773                     /* all constraints are met; update segment after */
  774                     /* finding its end                               */
  775                     do
  776                     {
  777                       if ( last < best_contour_last )
  778                         last++;
  779                       else
  780                         last = best_contour_first;
  781 
  782                       d = FT_ABS( points[last].y - points[first].y );
  783                       if ( d > 5 )
  784                         if ( FT_ABS( points[next].x - points[first].x ) <=
  785                                20 * dist )
  786                         {
  787                           if ( last > best_contour_first )
  788                             last--;
  789                           else
  790                             last = best_contour_last;
  791                           break;
  792                         }
  793 
  794                       p_last = last;
  795 
  796                       if ( FT_CURVE_TAG( outline.tags[last] ) ==
  797                              FT_CURVE_TAG_ON )
  798                       {
  799                         p_last = last;
  800                         if ( p_first < 0 )
  801                           p_first = last;
  802                       }
  803 
  804                     } while ( last != best_segment_first );
  805 
  806                     best_y = points[first].y;
  807 
  808                     best_segment_first = first;
  809                     best_segment_last  = last;
  810 
  811                     best_on_point_first = p_first;
  812                     best_on_point_last  = p_last;
  813 
  814                     break;
  815                   }
  816 
  817                 } while ( last != best_segment_first );
  818               }
  819             }
  820 
  821             /* for computing blue zones, we add the y offset as returned */
  822             /* by the currently used OpenType feature -- for example,    */
  823             /* superscript glyphs might be identical to subscript glyphs */
  824             /* with a vertical shift                                     */
  825             best_y += y_offset;
  826 
  827 #ifdef FT_DEBUG_LEVEL_TRACE
  828             if ( num_idx == 1 )
  829               FT_TRACE5(( "  U+%04lX: best_y = %5ld", ch, best_y ));
  830             else
  831               FT_TRACE5(( "  component %d of cluster starting with U+%04lX:"
  832                           " best_y = %5ld", i, ch, best_y ));
  833 #endif
  834 
  835             /* now set the `round' flag depending on the segment's kind: */
  836             /*                                                           */
  837             /* - if the horizontal distance between the first and last   */
  838             /*   `on' point is larger than a heuristic threshold         */
  839             /*   we have a flat segment                                  */
  840             /* - if either the first or the last point of the segment is */
  841             /*   an `off' point, the segment is round, otherwise it is   */
  842             /*   flat                                                    */
  843             if ( best_on_point_first >= 0                               &&
  844                  best_on_point_last >= 0                                &&
  845                  ( FT_ABS( points[best_on_point_last].x -
  846                            points[best_on_point_first].x ) ) >
  847                    flat_threshold                                       )
  848               round = 0;
  849             else
  850               round = FT_BOOL(
  851                         FT_CURVE_TAG( outline.tags[best_segment_first] ) !=
  852                           FT_CURVE_TAG_ON                                   ||
  853                         FT_CURVE_TAG( outline.tags[best_segment_last]  ) !=
  854                           FT_CURVE_TAG_ON                                   );
  855 
  856             if ( round && AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
  857             {
  858               /* only use flat segments for a neutral blue zone */
  859               FT_TRACE5(( " (round, skipped)\n" ));
  860               continue;
  861             }
  862 
  863             FT_TRACE5(( " (%s)\n", round ? "round" : "flat" ));
  864           }
  865 
  866           if ( AF_LATIN_IS_TOP_BLUE( bs ) )
  867           {
  868             if ( best_y > best_y_extremum )
  869             {
  870               best_y_extremum = best_y;
  871               best_round      = round;
  872             }
  873           }
  874           else
  875           {
  876             if ( best_y < best_y_extremum )
  877             {
  878               best_y_extremum = best_y;
  879               best_round      = round;
  880             }
  881           }
  882 
  883         } /* end for loop */
  884 
  885         if ( !( best_y_extremum == FT_INT_MIN ||
  886                 best_y_extremum == FT_INT_MAX ) )
  887         {
  888           if ( best_round )
  889             rounds[num_rounds++] = best_y_extremum;
  890           else
  891             flats[num_flats++]   = best_y_extremum;
  892         }
  893 
  894       } /* end while loop */
  895 
  896       if ( num_flats == 0 && num_rounds == 0 )
  897       {
  898         /*
  899          * we couldn't find a single glyph to compute this blue zone,
  900          * we will simply ignore it then
  901          */
  902         FT_TRACE5(( "  empty\n" ));
  903         continue;
  904       }
  905 
  906       /* we have computed the contents of the `rounds' and `flats' tables, */
  907       /* now determine the reference and overshoot position of the blue -- */
  908       /* we simply take the median value after a simple sort               */
  909       af_sort_pos( num_rounds, rounds );
  910       af_sort_pos( num_flats,  flats );
  911 
  912       blue       = &axis->blues[axis->blue_count];
  913       blue_ref   = &blue->ref.org;
  914       blue_shoot = &blue->shoot.org;
  915 
  916       axis->blue_count++;
  917 
  918       if ( num_flats == 0 )
  919       {
  920         *blue_ref   =
  921         *blue_shoot = rounds[num_rounds / 2];
  922       }
  923       else if ( num_rounds == 0 )
  924       {
  925         *blue_ref   =
  926         *blue_shoot = flats[num_flats / 2];
  927       }
  928       else
  929       {
  930         *blue_ref   = flats [num_flats  / 2];
  931         *blue_shoot = rounds[num_rounds / 2];
  932       }
  933 
  934       /* there are sometimes problems: if the overshoot position of top     */
  935       /* zones is under its reference position, or the opposite for bottom  */
  936       /* zones.  We must thus check everything there and correct the errors */
  937       if ( *blue_shoot != *blue_ref )
  938       {
  939         FT_Pos   ref      = *blue_ref;
  940         FT_Pos   shoot    = *blue_shoot;
  941         FT_Bool  over_ref = FT_BOOL( shoot > ref );
  942 
  943 
  944         if ( ( AF_LATIN_IS_TOP_BLUE( bs )    ||
  945                AF_LATIN_IS_SUB_TOP_BLUE( bs) ) ^ over_ref )
  946         {
  947           *blue_ref   =
  948           *blue_shoot = ( shoot + ref ) / 2;
  949 
  950           FT_TRACE5(( "  [overshoot smaller than reference,"
  951                       " taking mean value]\n" ));
  952         }
  953       }
  954 
  955       blue->ascender  = ascender;
  956       blue->descender = descender;
  957 
  958       blue->flags = 0;
  959       if ( AF_LATIN_IS_TOP_BLUE( bs ) )
  960         blue->flags |= AF_LATIN_BLUE_TOP;
  961       if ( AF_LATIN_IS_SUB_TOP_BLUE( bs ) )
  962         blue->flags |= AF_LATIN_BLUE_SUB_TOP;
  963       if ( AF_LATIN_IS_NEUTRAL_BLUE( bs ) )
  964         blue->flags |= AF_LATIN_BLUE_NEUTRAL;
  965 
  966       /*
  967        * The following flag is used later to adjust the y and x scales
  968        * in order to optimize the pixel grid alignment of the top of small
  969        * letters.
  970        */
  971       if ( AF_LATIN_IS_X_HEIGHT_BLUE( bs ) )
  972         blue->flags |= AF_LATIN_BLUE_ADJUSTMENT;
  973 
  974       FT_TRACE5(( "    -> reference = %ld\n", *blue_ref ));
  975       FT_TRACE5(( "       overshoot = %ld\n", *blue_shoot ));
  976 
  977     } /* end for loop */
  978 
  979     af_shaper_buf_destroy( face, shaper_buf );
  980 
  981     if ( axis->blue_count )
  982     {
  983       /* we finally check whether blue zones are ordered;            */
  984       /* `ref' and `shoot' values of two blue zones must not overlap */
  985 
  986       FT_UInt       i;
  987       AF_LatinBlue  blue_sorted[AF_BLUE_STRINGSET_MAX_LEN + 2];
  988 
  989 
  990       for ( i = 0; i < axis->blue_count; i++ )
  991         blue_sorted[i] = &axis->blues[i];
  992 
  993       /* sort bottoms of blue zones... */
  994       af_latin_sort_blue( axis->blue_count, blue_sorted );
  995 
  996       /* ...and adjust top values if necessary */
  997       for ( i = 0; i < axis->blue_count - 1; i++ )
  998       {
  999         FT_Pos*  a;
 1000         FT_Pos*  b;
 1001 
 1002 #ifdef FT_DEBUG_LEVEL_TRACE
 1003         FT_Bool  a_is_top = 0;
 1004 #endif
 1005 
 1006 
 1007         if ( blue_sorted[i]->flags & ( AF_LATIN_BLUE_TOP     |
 1008                                        AF_LATIN_BLUE_SUB_TOP ) )
 1009         {
 1010           a = &blue_sorted[i]->shoot.org;
 1011 #ifdef FT_DEBUG_LEVEL_TRACE
 1012           a_is_top = 1;
 1013 #endif
 1014         }
 1015         else
 1016           a = &blue_sorted[i]->ref.org;
 1017 
 1018         if ( blue_sorted[i + 1]->flags & ( AF_LATIN_BLUE_TOP     |
 1019                                            AF_LATIN_BLUE_SUB_TOP ) )
 1020           b = &blue_sorted[i + 1]->shoot.org;
 1021         else
 1022           b = &blue_sorted[i + 1]->ref.org;
 1023 
 1024         if ( *a > *b )
 1025         {
 1026           *a = *b;
 1027           FT_TRACE5(( "blue zone overlap:"
 1028                       " adjusting %s %ld to %ld\n",
 1029                       a_is_top ? "overshoot" : "reference",
 1030                       blue_sorted[i] - axis->blues,
 1031                       *a ));
 1032         }
 1033       }
 1034 
 1035       FT_TRACE5(( "\n" ));
 1036 
 1037       return 0;
 1038     }
 1039     else
 1040     {
 1041       /* disable hinting for the current style if there are no blue zones */
 1042 
 1043       AF_FaceGlobals  globals = metrics->root.globals;
 1044       FT_UShort*      gstyles = globals->glyph_styles;
 1045 
 1046       FT_Long  i;
 1047 
 1048 
 1049       FT_TRACE5(( "no blue zones found:"
 1050                   " hinting disabled for this style\n" ));
 1051 
 1052       for ( i = 0; i < globals->glyph_count; i++ )
 1053       {
 1054         if ( ( gstyles[i] & AF_STYLE_MASK ) == sc->style )
 1055           gstyles[i] = AF_STYLE_NONE_DFLT;
 1056       }
 1057 
 1058       FT_TRACE5(( "\n" ));
 1059 
 1060       return 1;
 1061     }
 1062   }
 1063 
 1064 
 1065   /* Check whether all ASCII digits have the same advance width. */
 1066 
 1067   FT_LOCAL_DEF( void )
 1068   af_latin_metrics_check_digits( AF_LatinMetrics  metrics,
 1069                                  FT_Face          face )
 1070   {
 1071     FT_Bool   started = 0, same_width = 1;
 1072     FT_Fixed  advance = 0, old_advance = 0;
 1073 
 1074     /* If HarfBuzz is not available, we need a pointer to a single */
 1075     /* unsigned long value.                                        */
 1076 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
 1077     void*     shaper_buf;
 1078 #else
 1079     FT_ULong  shaper_buf_;
 1080     void*     shaper_buf = &shaper_buf_;
 1081 #endif
 1082 
 1083     /* in all supported charmaps, digits have character codes 0x30-0x39 */
 1084     const char   digits[] = "0 1 2 3 4 5 6 7 8 9";
 1085     const char*  p;
 1086 
 1087 
 1088     p = digits;
 1089 
 1090 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
 1091     shaper_buf = af_shaper_buf_create( face );
 1092 #endif
 1093 
 1094     while ( *p )
 1095     {
 1096       FT_ULong      glyph_index;
 1097       unsigned int  num_idx;
 1098 
 1099 
 1100       /* reject input that maps to more than a single glyph */
 1101       p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
 1102       if ( num_idx > 1 )
 1103         continue;
 1104 
 1105       glyph_index = af_shaper_get_elem( &metrics->root,
 1106                                         shaper_buf,
 1107                                         0,
 1108                                         &advance,
 1109                                         NULL );
 1110       if ( !glyph_index )
 1111         continue;
 1112 
 1113       if ( started )
 1114       {
 1115         if ( advance != old_advance )
 1116         {
 1117           same_width = 0;
 1118           break;
 1119         }
 1120       }
 1121       else
 1122       {
 1123         old_advance = advance;
 1124         started     = 1;
 1125       }
 1126     }
 1127 
 1128     af_shaper_buf_destroy( face, shaper_buf );
 1129 
 1130     metrics->root.digits_have_same_width = same_width;
 1131   }
 1132 
 1133 
 1134   /* Initialize global metrics. */
 1135 
 1136   FT_LOCAL_DEF( FT_Error )
 1137   af_latin_metrics_init( AF_LatinMetrics  metrics,
 1138                          FT_Face          face )
 1139   {
 1140     FT_Error  error = FT_Err_Ok;
 1141 
 1142     FT_CharMap  oldmap = face->charmap;
 1143 
 1144 
 1145     metrics->units_per_em = face->units_per_EM;
 1146 
 1147     if ( !FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
 1148     {
 1149       af_latin_metrics_init_widths( metrics, face );
 1150       if ( af_latin_metrics_init_blues( metrics, face ) )
 1151       {
 1152         /* use internal error code to indicate missing blue zones */
 1153         error = -1;
 1154         goto Exit;
 1155       }
 1156       af_latin_metrics_check_digits( metrics, face );
 1157     }
 1158 
 1159   Exit:
 1160     FT_Set_Charmap( face, oldmap );
 1161     return error;
 1162   }
 1163 
 1164 
 1165   /* Adjust scaling value, then scale and shift widths   */
 1166   /* and blue zones (if applicable) for given dimension. */
 1167 
 1168   static void
 1169   af_latin_metrics_scale_dim( AF_LatinMetrics  metrics,
 1170                               AF_Scaler        scaler,
 1171                               AF_Dimension     dim )
 1172   {
 1173     FT_Fixed      scale;
 1174     FT_Pos        delta;
 1175     AF_LatinAxis  axis;
 1176     FT_UInt       nn;
 1177 
 1178 
 1179     if ( dim == AF_DIMENSION_HORZ )
 1180     {
 1181       scale = scaler->x_scale;
 1182       delta = scaler->x_delta;
 1183     }
 1184     else
 1185     {
 1186       scale = scaler->y_scale;
 1187       delta = scaler->y_delta;
 1188     }
 1189 
 1190     axis = &metrics->axis[dim];
 1191 
 1192     if ( axis->org_scale == scale && axis->org_delta == delta )
 1193       return;
 1194 
 1195     axis->org_scale = scale;
 1196     axis->org_delta = delta;
 1197 
 1198     /*
 1199      * correct X and Y scale to optimize the alignment of the top of small
 1200      * letters to the pixel grid
 1201      */
 1202     {
 1203       AF_LatinAxis  Axis = &metrics->axis[AF_DIMENSION_VERT];
 1204       AF_LatinBlue  blue = NULL;
 1205 
 1206 
 1207       for ( nn = 0; nn < Axis->blue_count; nn++ )
 1208       {
 1209         if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT )
 1210         {
 1211           blue = &Axis->blues[nn];
 1212           break;
 1213         }
 1214       }
 1215 
 1216       if ( blue )
 1217       {
 1218         FT_Pos   scaled;
 1219         FT_Pos   threshold;
 1220         FT_Pos   fitted;
 1221         FT_UInt  limit;
 1222         FT_UInt  ppem;
 1223 
 1224 
 1225         scaled    = FT_MulFix( blue->shoot.org, scale );
 1226         ppem      = metrics->root.scaler.face->size->metrics.x_ppem;
 1227         limit     = metrics->root.globals->increase_x_height;
 1228         threshold = 40;
 1229 
 1230         /* if the `increase-x-height' property is active, */
 1231         /* we round up much more often                    */
 1232         if ( limit                                 &&
 1233              ppem <= limit                         &&
 1234              ppem >= AF_PROP_INCREASE_X_HEIGHT_MIN )
 1235           threshold = 52;
 1236 
 1237         fitted = ( scaled + threshold ) & ~63;
 1238 
 1239         if ( scaled != fitted )
 1240         {
 1241 #if 0
 1242           if ( dim == AF_DIMENSION_HORZ )
 1243           {
 1244             if ( fitted < scaled )
 1245               scale -= scale / 50;  /* scale *= 0.98 */
 1246           }
 1247           else
 1248 #endif
 1249           if ( dim == AF_DIMENSION_VERT )
 1250           {
 1251             FT_Pos    max_height;
 1252             FT_Pos    dist;
 1253             FT_Fixed  new_scale;
 1254 
 1255 
 1256             new_scale = FT_MulDiv( scale, fitted, scaled );
 1257 
 1258             /* the scaling should not change the result by more than two pixels */
 1259             max_height = metrics->units_per_em;
 1260 
 1261             for ( nn = 0; nn < Axis->blue_count; nn++ )
 1262             {
 1263               max_height = FT_MAX( max_height, Axis->blues[nn].ascender );
 1264               max_height = FT_MAX( max_height, -Axis->blues[nn].descender );
 1265             }
 1266 
 1267             dist  = FT_ABS( FT_MulFix( max_height, new_scale - scale ) );
 1268             dist &= ~127;
 1269 
 1270             if ( dist == 0 )
 1271             {
 1272               FT_TRACE5(( "af_latin_metrics_scale_dim:"
 1273                           " x height alignment (style `%s'):\n",
 1274                           af_style_names[metrics->root.style_class->style] ));
 1275               FT_TRACE5(( "                           "
 1276                           " vertical scaling changed"
 1277                           " from %.5f to %.5f (by %ld%%)\n",
 1278                           scale / 65536.0,
 1279                           new_scale / 65536.0,
 1280                           ( fitted - scaled ) * 100 / scaled ));
 1281               FT_TRACE5(( "\n" ));
 1282 
 1283               scale = new_scale;
 1284             }
 1285 #ifdef FT_DEBUG_LEVEL_TRACE
 1286             else
 1287             {
 1288               FT_TRACE5(( "af_latin_metrics_scale_dim:"
 1289                           " x height alignment (style `%s'):\n",
 1290                           af_style_names[metrics->root.style_class->style] ));
 1291               FT_TRACE5(( "                           "
 1292                           " excessive vertical scaling abandoned\n" ));
 1293               FT_TRACE5(( "\n" ));
 1294             }
 1295 #endif
 1296           }
 1297         }
 1298       }
 1299     }
 1300 
 1301     axis->scale = scale;
 1302     axis->delta = delta;
 1303 
 1304     if ( dim == AF_DIMENSION_HORZ )
 1305     {
 1306       metrics->root.scaler.x_scale = scale;
 1307       metrics->root.scaler.x_delta = delta;
 1308     }
 1309     else
 1310     {
 1311       metrics->root.scaler.y_scale = scale;
 1312       metrics->root.scaler.y_delta = delta;
 1313     }
 1314 
 1315     FT_TRACE5(( "%s widths (style `%s')\n",
 1316                 dim == AF_DIMENSION_HORZ ? "horizontal" : "vertical",
 1317                 af_style_names[metrics->root.style_class->style] ));
 1318 
 1319     /* scale the widths */
 1320     for ( nn = 0; nn < axis->width_count; nn++ )
 1321     {
 1322       AF_Width  width = axis->widths + nn;
 1323 
 1324 
 1325       width->cur = FT_MulFix( width->org, scale );
 1326       width->fit = width->cur;
 1327 
 1328       FT_TRACE5(( "  %ld scaled to %.2f\n",
 1329                   width->org,
 1330                   width->cur / 64.0 ));
 1331     }
 1332 
 1333     FT_TRACE5(( "\n" ));
 1334 
 1335     /* an extra-light axis corresponds to a standard width that is */
 1336     /* smaller than 5/8 pixels                                     */
 1337     axis->extra_light =
 1338       FT_BOOL( FT_MulFix( axis->standard_width, scale ) < 32 + 8 );
 1339 
 1340 #ifdef FT_DEBUG_LEVEL_TRACE
 1341     if ( axis->extra_light )
 1342     {
 1343       FT_TRACE5(( "`%s' style is extra light (at current resolution)\n",
 1344                   af_style_names[metrics->root.style_class->style] ));
 1345       FT_TRACE5(( "\n" ));
 1346     }
 1347 #endif
 1348 
 1349     if ( dim == AF_DIMENSION_VERT )
 1350     {
 1351 #ifdef FT_DEBUG_LEVEL_TRACE
 1352       if ( axis->blue_count )
 1353         FT_TRACE5(( "blue zones (style `%s')\n",
 1354                     af_style_names[metrics->root.style_class->style] ));
 1355 #endif
 1356 
 1357       /* scale the blue zones */
 1358       for ( nn = 0; nn < axis->blue_count; nn++ )
 1359       {
 1360         AF_LatinBlue  blue = &axis->blues[nn];
 1361         FT_Pos        dist;
 1362 
 1363 
 1364         blue->ref.cur   = FT_MulFix( blue->ref.org, scale ) + delta;
 1365         blue->ref.fit   = blue->ref.cur;
 1366         blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
 1367         blue->shoot.fit = blue->shoot.cur;
 1368         blue->flags    &= ~AF_LATIN_BLUE_ACTIVE;
 1369 
 1370         /* a blue zone is only active if it is less than 3/4 pixels tall */
 1371         dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
 1372         if ( dist <= 48 && dist >= -48 )
 1373         {
 1374 #if 0
 1375           FT_Pos  delta1;
 1376 #endif
 1377           FT_Pos  delta2;
 1378 
 1379 
 1380           /* use discrete values for blue zone widths */
 1381 
 1382 #if 0
 1383 
 1384           /* generic, original code */
 1385           delta1 = blue->shoot.org - blue->ref.org;
 1386           delta2 = delta1;
 1387           if ( delta1 < 0 )
 1388             delta2 = -delta2;
 1389 
 1390           delta2 = FT_MulFix( delta2, scale );
 1391 
 1392           if ( delta2 < 32 )
 1393             delta2 = 0;
 1394           else if ( delta2 < 64 )
 1395             delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
 1396           else
 1397             delta2 = FT_PIX_ROUND( delta2 );
 1398 
 1399           if ( delta1 < 0 )
 1400             delta2 = -delta2;
 1401 
 1402           blue->ref.fit   = FT_PIX_ROUND( blue->ref.cur );
 1403           blue->shoot.fit = blue->ref.fit + delta2;
 1404 
 1405 #else
 1406 
 1407           /* simplified version due to abs(dist) <= 48 */
 1408           delta2 = dist;
 1409           if ( dist < 0 )
 1410             delta2 = -delta2;
 1411 
 1412           if ( delta2 < 32 )
 1413             delta2 = 0;
 1414           else if ( delta2 < 48 )
 1415             delta2 = 32;
 1416           else
 1417             delta2 = 64;
 1418 
 1419           if ( dist < 0 )
 1420             delta2 = -delta2;
 1421 
 1422           blue->ref.fit   = FT_PIX_ROUND( blue->ref.cur );
 1423           blue->shoot.fit = blue->ref.fit - delta2;
 1424 
 1425 #endif
 1426 
 1427           blue->flags |= AF_LATIN_BLUE_ACTIVE;
 1428         }
 1429       }
 1430 
 1431       /* use sub-top blue zone only if it doesn't overlap with */
 1432       /* another (non-sup-top) blue zone; otherwise, the       */
 1433       /* effect would be similar to a neutral blue zone, which */
 1434       /* is not desired here                                   */
 1435       for ( nn = 0; nn < axis->blue_count; nn++ )
 1436       {
 1437         AF_LatinBlue  blue = &axis->blues[nn];
 1438         FT_UInt       i;
 1439 
 1440 
 1441         if ( !( blue->flags & AF_LATIN_BLUE_SUB_TOP ) )
 1442           continue;
 1443         if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
 1444           continue;
 1445 
 1446         for ( i = 0; i < axis->blue_count; i++ )
 1447         {
 1448           AF_LatinBlue  b = &axis->blues[i];
 1449 
 1450 
 1451           if ( b->flags & AF_LATIN_BLUE_SUB_TOP )
 1452             continue;
 1453           if ( !( b->flags & AF_LATIN_BLUE_ACTIVE ) )
 1454             continue;
 1455 
 1456           if ( b->ref.fit <= blue->shoot.fit &&
 1457                b->shoot.fit >= blue->ref.fit )
 1458           {
 1459             blue->flags &= ~AF_LATIN_BLUE_ACTIVE;
 1460             break;
 1461           }
 1462         }
 1463       }
 1464 
 1465 #ifdef FT_DEBUG_LEVEL_TRACE
 1466       for ( nn = 0; nn < axis->blue_count; nn++ )
 1467       {
 1468         AF_LatinBlue  blue = &axis->blues[nn];
 1469 
 1470 
 1471         FT_TRACE5(( "  reference %d: %ld scaled to %.2f%s\n",
 1472                     nn,
 1473                     blue->ref.org,
 1474                     blue->ref.fit / 64.0,
 1475                     ( blue->flags & AF_LATIN_BLUE_ACTIVE ) ? ""
 1476                                                            : " (inactive)" ));
 1477         FT_TRACE5(( "  overshoot %d: %ld scaled to %.2f%s\n",
 1478                     nn,
 1479                     blue->shoot.org,
 1480                     blue->shoot.fit / 64.0,
 1481                     ( blue->flags & AF_LATIN_BLUE_ACTIVE ) ? ""
 1482                                                            : " (inactive)" ));
 1483       }
 1484 #endif
 1485     }
 1486   }
 1487 
 1488 
 1489   /* Scale global values in both directions. */
 1490 
 1491   FT_LOCAL_DEF( void )
 1492   af_latin_metrics_scale( AF_LatinMetrics  metrics,
 1493                           AF_Scaler        scaler )
 1494   {
 1495     metrics->root.scaler.render_mode = scaler->render_mode;
 1496     metrics->root.scaler.face        = scaler->face;
 1497     metrics->root.scaler.flags       = scaler->flags;
 1498 
 1499     af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_HORZ );
 1500     af_latin_metrics_scale_dim( metrics, scaler, AF_DIMENSION_VERT );
 1501   }
 1502 
 1503 
 1504   /* Extract standard_width from writing system/script specific */
 1505   /* metrics class.                                             */
 1506 
 1507   FT_LOCAL_DEF( void )
 1508   af_latin_get_standard_widths( AF_LatinMetrics  metrics,
 1509                                 FT_Pos*          stdHW,
 1510                                 FT_Pos*          stdVW )
 1511   {
 1512     if ( stdHW )
 1513       *stdHW = metrics->axis[AF_DIMENSION_VERT].standard_width;
 1514 
 1515     if ( stdVW )
 1516       *stdVW = metrics->axis[AF_DIMENSION_HORZ].standard_width;
 1517   }
 1518 
 1519 
 1520   /*************************************************************************/
 1521   /*************************************************************************/
 1522   /*****                                                               *****/
 1523   /*****           L A T I N   G L Y P H   A N A L Y S I S             *****/
 1524   /*****                                                               *****/
 1525   /*************************************************************************/
 1526   /*************************************************************************/
 1527 
 1528 
 1529   /* Walk over all contours and compute its segments. */
 1530 
 1531   FT_LOCAL_DEF( FT_Error )
 1532   af_latin_hints_compute_segments( AF_GlyphHints  hints,
 1533                                    AF_Dimension   dim )
 1534   {
 1535     AF_LatinMetrics  metrics       = (AF_LatinMetrics)hints->metrics;
 1536     AF_AxisHints     axis          = &hints->axis[dim];
 1537     FT_Memory        memory        = hints->memory;
 1538     FT_Error         error         = FT_Err_Ok;
 1539     AF_Segment       segment       = NULL;
 1540     AF_SegmentRec    seg0;
 1541     AF_Point*        contour       = hints->contours;
 1542     AF_Point*        contour_limit = contour + hints->num_contours;
 1543     AF_Direction     major_dir, segment_dir;
 1544 
 1545     FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
 1546 
 1547 
 1548     FT_ZERO( &seg0 );
 1549     seg0.score = 32000;
 1550     seg0.flags = AF_EDGE_NORMAL;
 1551 
 1552     major_dir   = (AF_Direction)FT_ABS( axis->major_dir );
 1553     segment_dir = major_dir;
 1554 
 1555     axis->num_segments = 0;
 1556 
 1557     /* set up (u,v) in each point */
 1558     if ( dim == AF_DIMENSION_HORZ )
 1559     {
 1560       AF_Point  point = hints->points;
 1561       AF_Point  limit = point + hints->num_points;
 1562 
 1563 
 1564       for ( ; point < limit; point++ )
 1565       {
 1566         point->u = point->fx;
 1567         point->v = point->fy;
 1568       }
 1569     }
 1570     else
 1571     {
 1572       AF_Point  point = hints->points;
 1573       AF_Point  limit = point + hints->num_points;
 1574 
 1575 
 1576       for ( ; point < limit; point++ )
 1577       {
 1578         point->u = point->fy;
 1579         point->v = point->fx;
 1580       }
 1581     }
 1582 
 1583     /* do each contour separately */
 1584     for ( ; contour < contour_limit; contour++ )
 1585     {
 1586       AF_Point  point   = contour[0];
 1587       AF_Point  last    = point->prev;
 1588       int       on_edge = 0;
 1589 
 1590       /* we call values measured along a segment (point->v)    */
 1591       /* `coordinates', and values orthogonal to it (point->u) */
 1592       /* `positions'                                           */
 1593       FT_Pos     min_pos      =  32000;
 1594       FT_Pos     max_pos      = -32000;
 1595       FT_Pos     min_coord    =  32000;
 1596       FT_Pos     max_coord    = -32000;
 1597       FT_UShort  min_flags    =  AF_FLAG_NONE;
 1598       FT_UShort  max_flags    =  AF_FLAG_NONE;
 1599       FT_Pos     min_on_coord =  32000;
 1600       FT_Pos     max_on_coord = -32000;
 1601 
 1602       FT_Bool  passed;
 1603 
 1604       AF_Segment  prev_segment = NULL;
 1605 
 1606       FT_Pos     prev_min_pos      = min_pos;
 1607       FT_Pos     prev_max_pos      = max_pos;
 1608       FT_Pos     prev_min_coord    = min_coord;
 1609       FT_Pos     prev_max_coord    = max_coord;
 1610       FT_UShort  prev_min_flags    = min_flags;
 1611       FT_UShort  prev_max_flags    = max_flags;
 1612       FT_Pos     prev_min_on_coord = min_on_coord;
 1613       FT_Pos     prev_max_on_coord = max_on_coord;
 1614 
 1615 
 1616       if ( FT_ABS( last->out_dir )  == major_dir &&
 1617            FT_ABS( point->out_dir ) == major_dir )
 1618       {
 1619         /* we are already on an edge, try to locate its start */
 1620         last = point;
 1621 
 1622         for (;;)
 1623         {
 1624           point = point->prev;
 1625           if ( FT_ABS( point->out_dir ) != major_dir )
 1626           {
 1627             point = point->next;
 1628             break;
 1629           }
 1630           if ( point == last )
 1631             break;
 1632         }
 1633       }
 1634 
 1635       last   = point;
 1636       passed = 0;
 1637 
 1638       for (;;)
 1639       {
 1640         FT_Pos  u, v;
 1641 
 1642 
 1643         if ( on_edge )
 1644         {
 1645           /* get minimum and maximum position */
 1646           u = point->u;
 1647           if ( u < min_pos )
 1648             min_pos = u;
 1649           if ( u > max_pos )
 1650             max_pos = u;
 1651 
 1652           /* get minimum and maximum coordinate together with flags */
 1653           v = point->v;
 1654           if ( v < min_coord )
 1655           {
 1656             min_coord = v;
 1657             min_flags = point->flags;
 1658           }
 1659           if ( v > max_coord )
 1660           {
 1661             max_coord = v;
 1662             max_flags = point->flags;
 1663           }
 1664 
 1665           /* get minimum and maximum coordinate of `on' points */
 1666           if ( !( point->flags & AF_FLAG_CONTROL ) )
 1667           {
 1668             v = point->v;
 1669             if ( v < min_on_coord )
 1670               min_on_coord = v;
 1671             if ( v > max_on_coord )
 1672               max_on_coord = v;
 1673           }
 1674 
 1675           if ( point->out_dir != segment_dir || point == last )
 1676           {
 1677             /* check whether the new segment's start point is identical to */
 1678             /* the previous segment's end point; for example, this might   */
 1679             /* happen for spikes                                           */
 1680 
 1681             if ( !prev_segment || segment->first != prev_segment->last )
 1682             {
 1683               /* points are different: we are just leaving an edge, thus */
 1684               /* record a new segment                                    */
 1685 
 1686               segment->last  = point;
 1687               segment->pos   = (FT_Short)( ( min_pos + max_pos ) >> 1 );
 1688               segment->delta = (FT_Short)( ( max_pos - min_pos ) >> 1 );
 1689 
 1690               /* a segment is round if either its first or last point */
 1691               /* is a control point, and the length of the on points  */
 1692               /* inbetween doesn't exceed a heuristic limit           */
 1693               if ( ( min_flags | max_flags ) & AF_FLAG_CONTROL      &&
 1694                    ( max_on_coord - min_on_coord ) < flat_threshold )
 1695                 segment->flags |= AF_EDGE_ROUND;
 1696 
 1697               segment->min_coord = (FT_Short)min_coord;
 1698               segment->max_coord = (FT_Short)max_coord;
 1699               segment->height    = segment->max_coord - segment->min_coord;
 1700 
 1701               prev_segment      = segment;
 1702               prev_min_pos      = min_pos;
 1703               prev_max_pos      = max_pos;
 1704               prev_min_coord    = min_coord;
 1705               prev_max_coord    = max_coord;
 1706               prev_min_flags    = min_flags;
 1707               prev_max_flags    = max_flags;
 1708               prev_min_on_coord = min_on_coord;
 1709               prev_max_on_coord = max_on_coord;
 1710             }
 1711             else
 1712             {
 1713               /* points are the same: we don't create a new segment but */
 1714               /* merge the current segment with the previous one        */
 1715 
 1716               if ( prev_segment->last->in_dir == point->in_dir )
 1717               {
 1718                 /* we have identical directions (this can happen for       */
 1719                 /* degenerate outlines that move zig-zag along the main    */
 1720                 /* axis without changing the coordinate value of the other */
 1721                 /* axis, and where the segments have just been merged):    */
 1722                 /* unify segments                                          */
 1723 
 1724                 /* update constraints */
 1725 
 1726                 if ( prev_min_pos < min_pos )
 1727                   min_pos = prev_min_pos;
 1728                 if ( prev_max_pos > max_pos )
 1729                   max_pos = prev_max_pos;
 1730 
 1731                 if ( prev_min_coord < min_coord )
 1732                 {
 1733                   min_coord = prev_min_coord;
 1734                   min_flags = prev_min_flags;
 1735                 }
 1736                 if ( prev_max_coord > max_coord )
 1737                 {
 1738                   max_coord = prev_max_coord;
 1739                   max_flags = prev_max_flags;
 1740                 }
 1741 
 1742                 if ( prev_min_on_coord < min_on_coord )
 1743                   min_on_coord = prev_min_on_coord;
 1744                 if ( prev_max_on_coord > max_on_coord )
 1745                   max_on_coord = prev_max_on_coord;
 1746 
 1747                 prev_segment->last  = point;
 1748                 prev_segment->pos   = (FT_Short)( ( min_pos +
 1749                                                     max_pos ) >> 1 );
 1750                 prev_segment->delta = (FT_Short)( ( max_pos -
 1751                                                     min_pos ) >> 1 );
 1752 
 1753                 if ( ( min_flags | max_flags ) & AF_FLAG_CONTROL      &&
 1754                      ( max_on_coord - min_on_coord ) < flat_threshold )
 1755                   prev_segment->flags |= AF_EDGE_ROUND;
 1756                 else
 1757                   prev_segment->flags &= ~AF_EDGE_ROUND;
 1758 
 1759                 prev_segment->min_coord = (FT_Short)min_coord;
 1760                 prev_segment->max_coord = (FT_Short)max_coord;
 1761                 prev_segment->height    = prev_segment->max_coord -
 1762                                           prev_segment->min_coord;
 1763               }
 1764               else
 1765               {
 1766                 /* we have different directions; use the properties of the */
 1767                 /* longer segment and discard the other one                */
 1768 
 1769                 if ( FT_ABS( prev_max_coord - prev_min_coord ) >
 1770                      FT_ABS( max_coord - min_coord ) )
 1771                 {
 1772                   /* discard current segment */
 1773 
 1774                   if ( min_pos < prev_min_pos )
 1775                     prev_min_pos = min_pos;
 1776                   if ( max_pos > prev_max_pos )
 1777                     prev_max_pos = max_pos;
 1778 
 1779                   prev_segment->last  = point;
 1780                   prev_segment->pos   = (FT_Short)( ( prev_min_pos +
 1781                                                       prev_max_pos ) >> 1 );
 1782                   prev_segment->delta = (FT_Short)( ( prev_max_pos -
 1783                                                       prev_min_pos ) >> 1 );
 1784                 }
 1785                 else
 1786                 {
 1787                   /* discard previous segment */
 1788 
 1789                   if ( prev_min_pos < min_pos )
 1790                     min_pos = prev_min_pos;
 1791                   if ( prev_max_pos > max_pos )
 1792                     max_pos = prev_max_pos;
 1793 
 1794                   segment->last  = point;
 1795                   segment->pos   = (FT_Short)( ( min_pos + max_pos ) >> 1 );
 1796                   segment->delta = (FT_Short)( ( max_pos - min_pos ) >> 1 );
 1797 
 1798                   if ( ( min_flags | max_flags ) & AF_FLAG_CONTROL      &&
 1799                        ( max_on_coord - min_on_coord ) < flat_threshold )
 1800                     segment->flags |= AF_EDGE_ROUND;
 1801 
 1802                   segment->min_coord = (FT_Short)min_coord;
 1803                   segment->max_coord = (FT_Short)max_coord;
 1804                   segment->height    = segment->max_coord -
 1805                                        segment->min_coord;
 1806 
 1807                   *prev_segment = *segment;
 1808 
 1809                   prev_min_pos      = min_pos;
 1810                   prev_max_pos      = max_pos;
 1811                   prev_min_coord    = min_coord;
 1812                   prev_max_coord    = max_coord;
 1813                   prev_min_flags    = min_flags;
 1814                   prev_max_flags    = max_flags;
 1815                   prev_min_on_coord = min_on_coord;
 1816                   prev_max_on_coord = max_on_coord;
 1817                 }
 1818               }
 1819 
 1820               axis->num_segments--;
 1821             }
 1822 
 1823             on_edge = 0;
 1824             segment = NULL;
 1825 
 1826             /* fall through */
 1827           }
 1828         }
 1829 
 1830         /* now exit if we are at the start/end point */
 1831         if ( point == last )
 1832         {
 1833           if ( passed )
 1834             break;
 1835           passed = 1;
 1836         }
 1837 
 1838         /* if we are not on an edge, check whether the major direction */
 1839         /* coincides with the current point's `out' direction, or      */
 1840         /* whether we have a single-point contour                      */
 1841         if ( !on_edge                                  &&
 1842              ( FT_ABS( point->out_dir ) == major_dir ||
 1843                point == point->prev                  ) )
 1844         {
 1845           /*
 1846            * For efficiency, we restrict the number of segments to 1000,
 1847            * which is a heuristic value: it is very unlikely that a glyph
 1848            * with so many segments can be hinted in a sensible way.
 1849            * Reasons:
 1850            *
 1851            * - The glyph has really 1000 segments; this implies that it has
 1852            *   at least 2000 outline points.  Assuming 'normal' fonts that
 1853            *   have superfluous points optimized away, viewing such a glyph
 1854            *   only makes sense at large magnifications where hinting
 1855            *   isn't applied anyway.
 1856            *
 1857            * - We have a broken glyph.  Hinting doesn't make sense in this
 1858            *   case either.
 1859            */
 1860           if ( axis->num_segments > 1000 )
 1861           {
 1862             FT_TRACE0(( "af_latin_hints_compute_segments:"
 1863                         " more than 1000 segments in this glyph;\n" ));
 1864             FT_TRACE0(( "                                "
 1865                         " hinting is suppressed\n" ));
 1866             axis->num_segments = 0;
 1867             return FT_Err_Ok;
 1868           }
 1869 
 1870           /* this is the start of a new segment! */
 1871           segment_dir = (AF_Direction)point->out_dir;
 1872 
 1873           error = af_axis_hints_new_segment( axis, memory, &segment );
 1874           if ( error )
 1875             goto Exit;
 1876 
 1877           /* clear all segment fields */
 1878           segment[0] = seg0;
 1879 
 1880           segment->dir   = (FT_Char)segment_dir;
 1881           segment->first = point;
 1882           segment->last  = point;
 1883 
 1884           /* `af_axis_hints_new_segment' reallocates memory,    */
 1885           /* thus we have to refresh the `prev_segment' pointer */
 1886           if ( prev_segment )
 1887             prev_segment = segment - 1;
 1888 
 1889           min_pos   = max_pos   = point->u;
 1890           min_coord = max_coord = point->v;
 1891           min_flags = max_flags = point->flags;
 1892 
 1893           if ( point->flags & AF_FLAG_CONTROL )
 1894           {
 1895             min_on_coord =  32000;
 1896             max_on_coord = -32000;
 1897           }
 1898           else
 1899             min_on_coord = max_on_coord = point->v;
 1900 
 1901           on_edge = 1;
 1902 
 1903           if ( point == point->prev )
 1904           {
 1905             /* we have a one-point segment: this is a one-point */
 1906             /* contour with `in' and `out' direction set to     */
 1907             /* AF_DIR_NONE                                      */
 1908             segment->pos = (FT_Short)min_pos;
 1909 
 1910             if (point->flags & AF_FLAG_CONTROL)
 1911               segment->flags |= AF_EDGE_ROUND;
 1912 
 1913             segment->min_coord = (FT_Short)point->v;
 1914             segment->max_coord = (FT_Short)point->v;
 1915             segment->height = 0;
 1916 
 1917             on_edge = 0;
 1918             segment = NULL;
 1919           }
 1920         }
 1921 
 1922         point = point->next;
 1923       }
 1924 
 1925     } /* contours */
 1926 
 1927 
 1928     /* now slightly increase the height of segments if this makes */
 1929     /* sense -- this is used to better detect and ignore serifs   */
 1930     {
 1931       AF_Segment  segments     = axis->segments;
 1932       AF_Segment  segments_end = FT_OFFSET( segments, axis->num_segments );
 1933 
 1934 
 1935       for ( segment = segments; segment < segments_end; segment++ )
 1936       {
 1937         AF_Point  first   = segment->first;
 1938         AF_Point  last    = segment->last;
 1939         FT_Pos    first_v = first->v;
 1940         FT_Pos    last_v  = last->v;
 1941 
 1942 
 1943         if ( first_v < last_v )
 1944         {
 1945           AF_Point  p;
 1946 
 1947 
 1948           p = first->prev;
 1949           if ( p->v < first_v )
 1950             segment->height = (FT_Short)( segment->height +
 1951                                           ( ( first_v - p->v ) >> 1 ) );
 1952 
 1953           p = last->next;
 1954           if ( p->v > last_v )
 1955             segment->height = (FT_Short)( segment->height +
 1956                                           ( ( p->v - last_v ) >> 1 ) );
 1957         }
 1958         else
 1959         {
 1960           AF_Point  p;
 1961 
 1962 
 1963           p = first->prev;
 1964           if ( p->v > first_v )
 1965             segment->height = (FT_Short)( segment->height +
 1966                                           ( ( p->v - first_v ) >> 1 ) );
 1967 
 1968           p = last->next;
 1969           if ( p->v < last_v )
 1970             segment->height = (FT_Short)( segment->height +
 1971                                           ( ( last_v - p->v ) >> 1 ) );
 1972         }
 1973       }
 1974     }
 1975 
 1976   Exit:
 1977     return error;
 1978   }
 1979 
 1980 
 1981   /* Link segments to form stems and serifs.  If `width_count' and      */
 1982   /* `widths' are non-zero, use them to fine-tune the scoring function. */
 1983 
 1984   FT_LOCAL_DEF( void )
 1985   af_latin_hints_link_segments( AF_GlyphHints  hints,
 1986                                 FT_UInt        width_count,
 1987                                 AF_WidthRec*   widths,
 1988                                 AF_Dimension   dim )
 1989   {
 1990     AF_AxisHints  axis          = &hints->axis[dim];
 1991     AF_Segment    segments      = axis->segments;
 1992     AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
 1993     FT_Pos        len_threshold, len_score, dist_score, max_width;
 1994     AF_Segment    seg1, seg2;
 1995 
 1996 
 1997     if ( width_count )
 1998       max_width = widths[width_count - 1].org;
 1999     else
 2000       max_width = 0;
 2001 
 2002     /* a heuristic value to set up a minimum value for overlapping */
 2003     len_threshold = AF_LATIN_CONSTANT( hints->metrics, 8 );
 2004     if ( len_threshold == 0 )
 2005       len_threshold = 1;
 2006 
 2007     /* a heuristic value to weight lengths */
 2008     len_score = AF_LATIN_CONSTANT( hints->metrics, 6000 );
 2009 
 2010     /* a heuristic value to weight distances (no call to    */
 2011     /* AF_LATIN_CONSTANT needed, since we work on multiples */
 2012     /* of the stem width)                                   */
 2013     dist_score = 3000;
 2014 
 2015     /* now compare each segment to the others */
 2016     for ( seg1 = segments; seg1 < segment_limit; seg1++ )
 2017     {
 2018       if ( seg1->dir != axis->major_dir )
 2019         continue;
 2020 
 2021       /* search for stems having opposite directions, */
 2022       /* with seg1 to the `left' of seg2              */
 2023       for ( seg2 = segments; seg2 < segment_limit; seg2++ )
 2024       {
 2025         FT_Pos  pos1 = seg1->pos;
 2026         FT_Pos  pos2 = seg2->pos;
 2027 
 2028 
 2029         if ( seg1->dir + seg2->dir == 0 && pos2 > pos1 )
 2030         {
 2031           /* compute distance between the two segments */
 2032           FT_Pos  min = seg1->min_coord;
 2033           FT_Pos  max = seg1->max_coord;
 2034           FT_Pos  len;
 2035 
 2036 
 2037           if ( min < seg2->min_coord )
 2038             min = seg2->min_coord;
 2039 
 2040           if ( max > seg2->max_coord )
 2041             max = seg2->max_coord;
 2042 
 2043           /* compute maximum coordinate difference of the two segments */
 2044           /* (this is, how much they overlap)                          */
 2045           len = max - min;
 2046           if ( len >= len_threshold )
 2047           {
 2048             /*
 2049              * The score is the sum of two demerits indicating the
 2050              * `badness' of a fit, measured along the segments' main axis
 2051              * and orthogonal to it, respectively.
 2052              *
 2053              * - The less overlapping along the main axis, the worse it
 2054              *   is, causing a larger demerit.
 2055              *
 2056              * - The nearer the orthogonal distance to a stem width, the
 2057              *   better it is, causing a smaller demerit.  For simplicity,
 2058              *   however, we only increase the demerit for values that
 2059              *   exceed the largest stem width.
 2060              */
 2061 
 2062             FT_Pos  dist = pos2 - pos1;
 2063 
 2064             FT_Pos  dist_demerit, score;
 2065 
 2066 
 2067             if ( max_width )
 2068             {
 2069               /* distance demerits are based on multiples of `max_width'; */
 2070               /* we scale by 1024 for getting more precision              */
 2071               FT_Pos  delta = ( dist << 10 ) / max_width - ( 1 << 10 );
 2072 
 2073 
 2074               if ( delta > 10000 )
 2075                 dist_demerit = 32000;
 2076               else if ( delta > 0 )
 2077                 dist_demerit = delta * delta / dist_score;
 2078               else
 2079                 dist_demerit = 0;
 2080             }
 2081             else
 2082               dist_demerit = dist; /* default if no widths available */
 2083 
 2084             score = dist_demerit + len_score / len;
 2085 
 2086             /* and we search for the smallest score */
 2087             if ( score < seg1->score )
 2088             {
 2089               seg1->score = score;
 2090               seg1->link  = seg2;
 2091             }
 2092 
 2093             if ( score < seg2->score )
 2094             {
 2095               seg2->score = score;
 2096               seg2->link  = seg1;
 2097             }
 2098           }
 2099         }
 2100       }
 2101     }
 2102 
 2103     /* now compute the `serif' segments, cf. explanations in `afhints.h' */
 2104     for ( seg1 = segments; seg1 < segment_limit; seg1++ )
 2105     {
 2106       seg2 = seg1->link;
 2107 
 2108       if ( seg2 )
 2109       {
 2110         if ( seg2->link != seg1 )
 2111         {
 2112           seg1->link  = NULL;
 2113           seg1->serif = seg2->link;
 2114         }
 2115       }
 2116     }
 2117   }
 2118 
 2119 
 2120   /* Link segments to edges, using feature analysis for selection. */
 2121 
 2122   FT_LOCAL_DEF( FT_Error )
 2123   af_latin_hints_compute_edges( AF_GlyphHints  hints,
 2124                                 AF_Dimension   dim )
 2125   {
 2126     AF_AxisHints  axis   = &hints->axis[dim];
 2127     FT_Error      error  = FT_Err_Ok;
 2128     FT_Memory     memory = hints->memory;
 2129     AF_LatinAxis  laxis  = &((AF_LatinMetrics)hints->metrics)->axis[dim];
 2130 
 2131     AF_StyleClass   style_class  = hints->metrics->style_class;
 2132     AF_ScriptClass  script_class = af_script_classes[style_class->script];
 2133 
 2134     FT_Bool  top_to_bottom_hinting = 0;
 2135 
 2136     AF_Segment    segments      = axis->segments;
 2137     AF_Segment    segment_limit = FT_OFFSET( segments, axis->num_segments );
 2138     AF_Segment    seg;
 2139 
 2140 #if 0
 2141     AF_Direction  up_dir;
 2142 #endif
 2143     FT_Fixed      scale;
 2144     FT_Pos        edge_distance_threshold;
 2145     FT_Pos        segment_length_threshold;
 2146     FT_Pos        segment_width_threshold;
 2147 
 2148 
 2149     axis->num_edges = 0;
 2150 
 2151     scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale
 2152                                          : hints->y_scale;
 2153 
 2154 #if 0
 2155     up_dir = ( dim == AF_DIMENSION_HORZ ) ? AF_DIR_UP
 2156                                           : AF_DIR_RIGHT;
 2157 #endif
 2158 
 2159     if ( dim == AF_DIMENSION_VERT )
 2160       top_to_bottom_hinting = script_class->top_to_bottom_hinting;
 2161 
 2162     /*
 2163      * We ignore all segments that are less than 1 pixel in length
 2164      * to avoid many problems with serif fonts.  We compute the
 2165      * corresponding threshold in font units.
 2166      */
 2167     if ( dim == AF_DIMENSION_HORZ )
 2168       segment_length_threshold = FT_DivFix( 64, hints->y_scale );
 2169     else
 2170       segment_length_threshold = 0;
 2171 
 2172     /*
 2173      * Similarly, we ignore segments that have a width delta
 2174      * larger than 0.5px (i.e., a width larger than 1px).
 2175      */
 2176     segment_width_threshold = FT_DivFix( 32, scale );
 2177 
 2178     /**********************************************************************
 2179      *
 2180      * We begin by generating a sorted table of edges for the current
 2181      * direction.  To do so, we simply scan each segment and try to find
 2182      * an edge in our table that corresponds to its position.
 2183      *
 2184      * If no edge is found, we create and insert a new edge in the
 2185      * sorted table.  Otherwise, we simply add the segment to the edge's
 2186      * list which gets processed in the second step to compute the
 2187      * edge's properties.
 2188      *
 2189      * Note that the table of edges is sorted along the segment/edge
 2190      * position.
 2191      *
 2192      */
 2193 
 2194     /* assure that edge distance threshold is at most 0.25px */
 2195     edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold,
 2196                                          scale );
 2197     if ( edge_distance_threshold > 64 / 4 )
 2198       edge_distance_threshold = 64 / 4;
 2199 
 2200     edge_distance_threshold = FT_DivFix( edge_distance_threshold,
 2201                                          scale );
 2202 
 2203     for ( seg = segments; seg < segment_limit; seg++ )
 2204     {
 2205       AF_Edge  found = NULL;
 2206       FT_Int   ee;
 2207 
 2208 
 2209       /* ignore too short segments, too wide ones, and, in this loop, */
 2210       /* one-point segments without a direction                       */
 2211       if ( seg->height < segment_length_threshold ||
 2212            seg->delta > segment_width_threshold   ||
 2213            seg->dir == AF_DIR_NONE                )
 2214         continue;
 2215 
 2216       /* A special case for serif edges: If they are smaller than */
 2217       /* 1.5 pixels we ignore them.                               */
 2218       if ( seg->serif                                     &&
 2219            2 * seg->height < 3 * segment_length_threshold )
 2220         continue;
 2221 
 2222       /* look for an edge corresponding to the segment */
 2223       for ( ee = 0; ee < axis->num_edges; ee++ )
 2224       {
 2225         AF_Edge  edge = axis->edges + ee;
 2226         FT_Pos   dist;
 2227 
 2228 
 2229         dist = seg->pos - edge->fpos;
 2230         if ( dist < 0 )
 2231           dist = -dist;
 2232 
 2233         if ( dist < edge_distance_threshold && edge->dir == seg->dir )
 2234         {
 2235           found = edge;
 2236           break;
 2237         }
 2238       }
 2239 
 2240       if ( !found )
 2241       {
 2242         AF_Edge  edge;
 2243 
 2244 
 2245         /* insert a new edge in the list and */
 2246         /* sort according to the position    */
 2247         error = af_axis_hints_new_edge( axis, seg->pos,
 2248                                         (AF_Direction)seg->dir,
 2249                                         top_to_bottom_hinting,
 2250                                         memory, &edge );
 2251         if ( error )
 2252           goto Exit;
 2253 
 2254         /* add the segment to the new edge's list */
 2255         FT_ZERO( edge );
 2256 
 2257         edge->first    = seg;
 2258         edge->last     = seg;
 2259         edge->dir      = seg->dir;
 2260         edge->fpos     = seg->pos;
 2261         edge->opos     = FT_MulFix( seg->pos, scale );
 2262         edge->pos      = edge->opos;
 2263         seg->edge_next = seg;
 2264       }
 2265       else
 2266       {
 2267         /* if an edge was found, simply add the segment to the edge's */
 2268         /* list                                                       */
 2269         seg->edge_next         = found->first;
 2270         found->last->edge_next = seg;
 2271         found->last            = seg;
 2272       }
 2273     }
 2274 
 2275     /* we loop again over all segments to catch one-point segments   */
 2276     /* without a direction: if possible, link them to existing edges */
 2277     for ( seg = segments; seg < segment_limit; seg++ )
 2278     {
 2279       AF_Edge  found = NULL;
 2280       FT_Int   ee;
 2281 
 2282 
 2283       if ( seg->dir != AF_DIR_NONE )
 2284         continue;
 2285 
 2286       /* look for an edge corresponding to the segment */
 2287       for ( ee = 0; ee < axis->num_edges; ee++ )
 2288       {
 2289         AF_Edge  edge = axis->edges + ee;
 2290         FT_Pos   dist;
 2291 
 2292 
 2293         dist = seg->pos - edge->fpos;
 2294         if ( dist < 0 )
 2295           dist = -dist;
 2296 
 2297         if ( dist < edge_distance_threshold )
 2298         {
 2299           found = edge;
 2300           break;
 2301         }
 2302       }
 2303 
 2304       /* one-point segments without a match are ignored */
 2305       if ( found )
 2306       {
 2307         seg->edge_next         = found->first;
 2308         found->last->edge_next = seg;
 2309         found->last            = seg;
 2310       }
 2311     }
 2312 
 2313 
 2314     /*******************************************************************
 2315      *
 2316      * Good, we now compute each edge's properties according to the
 2317      * segments found on its position.  Basically, these are
 2318      *
 2319      * - the edge's main direction
 2320      * - stem edge, serif edge or both (which defaults to stem then)
 2321      * - rounded edge, straight or both (which defaults to straight)
 2322      * - link for edge
 2323      *
 2324      */
 2325 
 2326     /* first of all, set the `edge' field in each segment -- this is */
 2327     /* required in order to compute edge links                       */
 2328 
 2329     /*
 2330      * Note that removing this loop and setting the `edge' field of each
 2331      * segment directly in the code above slows down execution speed for
 2332      * some reasons on platforms like the Sun.
 2333      */
 2334     {
 2335       AF_Edge  edges      = axis->edges;
 2336       AF_Edge  edge_limit = FT_OFFSET( edges, axis->num_edges );
 2337       AF_Edge  edge;
 2338 
 2339 
 2340       for ( edge = edges; edge < edge_limit; edge++ )
 2341       {
 2342         seg = edge->first;
 2343         if ( seg )
 2344           do
 2345           {
 2346             seg->edge = edge;
 2347             seg       = seg->edge_next;
 2348 
 2349           } while ( seg != edge->first );
 2350       }
 2351 
 2352       /* now compute each edge properties */
 2353       for ( edge = edges; edge < edge_limit; edge++ )
 2354       {
 2355         FT_Int  is_round    = 0;  /* does it contain round segments?    */
 2356         FT_Int  is_straight = 0;  /* does it contain straight segments? */
 2357 #if 0
 2358         FT_Pos  ups         = 0;  /* number of upwards segments         */
 2359         FT_Pos  downs       = 0;  /* number of downwards segments       */
 2360 #endif
 2361 
 2362 
 2363         seg = edge->first;
 2364 
 2365         do
 2366         {
 2367           FT_Bool  is_serif;
 2368 
 2369 
 2370           /* check for roundness of segment */
 2371           if ( seg->flags & AF_EDGE_ROUND )
 2372             is_round++;
 2373           else
 2374             is_straight++;
 2375 
 2376 #if 0
 2377           /* check for segment direction */
 2378           if ( seg->dir == up_dir )
 2379             ups   += seg->max_coord - seg->min_coord;
 2380           else
 2381             downs += seg->max_coord - seg->min_coord;
 2382 #endif
 2383 
 2384           /* check for links -- if seg->serif is set, then seg->link must */
 2385           /* be ignored                                                   */
 2386           is_serif = FT_BOOL( seg->serif               &&
 2387                               seg->serif->edge         &&
 2388                               seg->serif->edge != edge );
 2389 
 2390           if ( ( seg->link && seg->link->edge ) || is_serif )
 2391           {
 2392             AF_Edge     edge2;
 2393             AF_Segment  seg2;
 2394 
 2395 
 2396             edge2 = edge->link;
 2397             seg2  = seg->link;
 2398 
 2399             if ( is_serif )
 2400             {
 2401               seg2  = seg->serif;
 2402               edge2 = edge->serif;
 2403             }
 2404 
 2405             if ( edge2 )
 2406             {
 2407               FT_Pos  edge_delta;
 2408               FT_Pos  seg_delta;
 2409 
 2410 
 2411               edge_delta = edge->fpos - edge2->fpos;
 2412               if ( edge_delta < 0 )
 2413                 edge_delta = -edge_delta;
 2414 
 2415               seg_delta = seg->pos - seg2->pos;
 2416               if ( seg_delta < 0 )
 2417                 seg_delta = -seg_delta;
 2418 
 2419               if ( seg_delta < edge_delta )
 2420                 edge2 = seg2->edge;
 2421             }
 2422             else
 2423               edge2 = seg2->edge;
 2424 
 2425             if ( is_serif )
 2426             {
 2427               edge->serif   = edge2;
 2428               edge2->flags |= AF_EDGE_SERIF;
 2429             }
 2430             else
 2431               edge->link  = edge2;
 2432           }
 2433 
 2434           seg = seg->edge_next;
 2435 
 2436         } while ( seg != edge->first );
 2437 
 2438         /* set the round/straight flags */
 2439         edge->flags = AF_EDGE_NORMAL;
 2440 
 2441         if ( is_round > 0 && is_round >= is_straight )
 2442           edge->flags |= AF_EDGE_ROUND;
 2443 
 2444 #if 0
 2445         /* set the edge's main direction */
 2446         edge->dir = AF_DIR_NONE;
 2447 
 2448         if ( ups > downs )
 2449           edge->dir = (FT_Char)up_dir;
 2450 
 2451         else if ( ups < downs )
 2452           edge->dir = (FT_Char)-up_dir;
 2453 
 2454         else if ( ups == downs )
 2455           edge->dir = 0;  /* both up and down! */
 2456 #endif
 2457 
 2458         /* get rid of serifs if link is set                 */
 2459         /* XXX: This gets rid of many unpleasant artefacts! */
 2460         /*      Example: the `c' in cour.pfa at size 13     */
 2461 
 2462         if ( edge->serif && edge->link )
 2463           edge->serif = NULL;
 2464       }
 2465     }
 2466 
 2467   Exit:
 2468     return error;
 2469   }
 2470 
 2471 
 2472   /* Detect segments and edges for given dimension. */
 2473 
 2474   FT_LOCAL_DEF( FT_Error )
 2475   af_latin_hints_detect_features( AF_GlyphHints  hints,
 2476                                   FT_UInt        width_count,
 2477                                   AF_WidthRec*   widths,
 2478                                   AF_Dimension   dim )
 2479   {
 2480     FT_Error  error;
 2481 
 2482 
 2483     error = af_latin_hints_compute_segments( hints, dim );
 2484     if ( !error )
 2485     {
 2486       af_latin_hints_link_segments( hints, width_count, widths, dim );
 2487 
 2488       error = af_latin_hints_compute_edges( hints, dim );
 2489     }
 2490 
 2491     return error;
 2492   }
 2493 
 2494 
 2495   /* Compute all edges which lie within blue zones. */
 2496 
 2497   static void
 2498   af_latin_hints_compute_blue_edges( AF_GlyphHints    hints,
 2499                                      AF_LatinMetrics  metrics )
 2500   {
 2501     AF_AxisHints  axis       = &hints->axis[AF_DIMENSION_VERT];
 2502     AF_Edge       edge       = axis->edges;
 2503     AF_Edge       edge_limit = FT_OFFSET( edge, axis->num_edges );
 2504     AF_LatinAxis  latin      = &metrics->axis[AF_DIMENSION_VERT];
 2505     FT_Fixed      scale      = latin->scale;
 2506 
 2507 
 2508     /* compute which blue zones are active, i.e. have their scaled */
 2509     /* size < 3/4 pixels                                           */
 2510 
 2511     /* for each horizontal edge search the blue zone which is closest */
 2512     for ( ; edge < edge_limit; edge++ )
 2513     {
 2514       FT_UInt   bb;
 2515       AF_Width  best_blue            = NULL;
 2516       FT_Bool   best_blue_is_neutral = 0;
 2517       FT_Pos    best_dist;                 /* initial threshold */
 2518 
 2519 
 2520       /* compute the initial threshold as a fraction of the EM size */
 2521       /* (the value 40 is heuristic)                                */
 2522       best_dist = FT_MulFix( metrics->units_per_em / 40, scale );
 2523 
 2524       /* assure a minimum distance of 0.5px */
 2525       if ( best_dist > 64 / 2 )
 2526         best_dist = 64 / 2;
 2527 
 2528       for ( bb = 0; bb < latin->blue_count; bb++ )
 2529       {
 2530         AF_LatinBlue  blue = latin->blues + bb;
 2531         FT_Bool       is_top_blue, is_neutral_blue, is_major_dir;
 2532 
 2533 
 2534         /* skip inactive blue zones (i.e., those that are too large) */
 2535         if ( !( blue->flags & AF_LATIN_BLUE_ACTIVE ) )
 2536           continue;
 2537 
 2538         /* if it is a top zone, check for right edges (against the major */
 2539         /* direction); if it is a bottom zone, check for left edges (in  */
 2540         /* the major direction) -- this assumes the TrueType convention  */
 2541         /* for the orientation of contours                               */
 2542         is_top_blue =
 2543           (FT_Byte)( ( blue->flags & ( AF_LATIN_BLUE_TOP     |
 2544                                        AF_LATIN_BLUE_SUB_TOP ) ) != 0 );
 2545         is_neutral_blue =
 2546           (FT_Byte)( ( blue->flags & AF_LATIN_BLUE_NEUTRAL ) != 0);
 2547         is_major_dir =
 2548           FT_BOOL( edge->dir == axis->major_dir );
 2549 
 2550         /* neutral blue zones are handled for both directions */
 2551         if ( is_top_blue ^ is_major_dir || is_neutral_blue )
 2552         {
 2553           FT_Pos  dist;
 2554 
 2555 
 2556           /* first of all, compare it to the reference position */
 2557           dist = edge->fpos - blue->ref.org;
 2558           if ( dist < 0 )
 2559             dist = -dist;
 2560 
 2561           dist = FT_MulFix( dist, scale );
 2562           if ( dist < best_dist )
 2563           {
 2564             best_dist            = dist;
 2565             best_blue            = &blue->ref;
 2566             best_blue_is_neutral = is_neutral_blue;
 2567           }
 2568 
 2569           /* now compare it to the overshoot position and check whether */
 2570           /* the edge is rounded, and whether the edge is over the      */
 2571           /* reference position of a top zone, or under the reference   */
 2572           /* position of a bottom zone (provided we don't have a        */
 2573           /* neutral blue zone)                                         */
 2574           if ( edge->flags & AF_EDGE_ROUND &&
 2575                dist != 0                   &&
 2576                !is_neutral_blue            )
 2577           {
 2578             FT_Bool  is_under_ref = FT_BOOL( edge->fpos < blue->ref.org );
 2579 
 2580 
 2581             if ( is_top_blue ^ is_under_ref )
 2582             {
 2583               dist = edge->fpos - blue->shoot.org;
 2584               if ( dist < 0 )
 2585                 dist = -dist;
 2586 
 2587               dist = FT_MulFix( dist, scale );
 2588               if ( dist < best_dist )
 2589               {
 2590                 best_dist            = dist;
 2591                 best_blue            = &blue->shoot;
 2592                 best_blue_is_neutral = is_neutral_blue;
 2593               }
 2594             }
 2595           }
 2596         }
 2597       }
 2598 
 2599       if ( best_blue )
 2600       {
 2601         edge->blue_edge = best_blue;
 2602         if ( best_blue_is_neutral )
 2603           edge->flags |= AF_EDGE_NEUTRAL;
 2604       }
 2605     }
 2606   }
 2607 
 2608 
 2609   /* Initalize hinting engine. */
 2610 
 2611   static FT_Error
 2612   af_latin_hints_init( AF_GlyphHints    hints,
 2613                        AF_LatinMetrics  metrics )
 2614   {
 2615     FT_Render_Mode  mode;
 2616     FT_UInt32       scaler_flags, other_flags;
 2617     FT_Face         face = metrics->root.scaler.face;
 2618 
 2619 
 2620     af_glyph_hints_rescale( hints, (AF_StyleMetrics)metrics );
 2621 
 2622     /*
 2623      * correct x_scale and y_scale if needed, since they may have
 2624      * been modified by `af_latin_metrics_scale_dim' above
 2625      */
 2626     hints->x_scale = metrics->axis[AF_DIMENSION_HORZ].scale;
 2627     hints->x_delta = metrics->axis[AF_DIMENSION_HORZ].delta;
 2628     hints->y_scale = metrics->axis[AF_DIMENSION_VERT].scale;
 2629     hints->y_delta = metrics->axis[AF_DIMENSION_VERT].delta;
 2630 
 2631     /* compute flags depending on render mode, etc. */
 2632     mode = metrics->root.scaler.render_mode;
 2633 
 2634     scaler_flags = hints->scaler_flags;
 2635     other_flags  = 0;
 2636 
 2637     /*
 2638      * We snap the width of vertical stems for the monochrome and
 2639      * horizontal LCD rendering targets only.
 2640      */
 2641     if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD )
 2642       other_flags |= AF_LATIN_HINTS_HORZ_SNAP;
 2643 
 2644     /*
 2645      * We snap the width of horizontal stems for the monochrome and
 2646      * vertical LCD rendering targets only.
 2647      */
 2648     if ( mode == FT_RENDER_MODE_MONO || mode == FT_RENDER_MODE_LCD_V )
 2649       other_flags |= AF_LATIN_HINTS_VERT_SNAP;
 2650 
 2651     /*
 2652      * We adjust stems to full pixels unless in `light' or `lcd' mode.
 2653      */
 2654     if ( mode != FT_RENDER_MODE_LIGHT && mode != FT_RENDER_MODE_LCD )
 2655       other_flags |= AF_LATIN_HINTS_STEM_ADJUST;
 2656 
 2657     if ( mode == FT_RENDER_MODE_MONO )
 2658       other_flags |= AF_LATIN_HINTS_MONO;
 2659 
 2660     /*
 2661      * In `light' or `lcd' mode we disable horizontal hinting completely.
 2662      * We also do it if the face is italic.
 2663      *
 2664      * However, if warping is enabled (which only works in `light' hinting
 2665      * mode), advance widths get adjusted, too.
 2666      */
 2667     if ( mode == FT_RENDER_MODE_LIGHT || mode == FT_RENDER_MODE_LCD ||
 2668          ( face->style_flags & FT_STYLE_FLAG_ITALIC ) != 0          )
 2669       scaler_flags |= AF_SCALER_FLAG_NO_HORIZONTAL;
 2670 
 2671     hints->scaler_flags = scaler_flags;
 2672     hints->other_flags  = other_flags;
 2673 
 2674     return FT_Err_Ok;
 2675   }
 2676 
 2677 
 2678   /*************************************************************************/
 2679   /*************************************************************************/
 2680   /*****                                                               *****/
 2681   /*****        L A T I N   G L Y P H   G R I D - F I T T I N G        *****/
 2682   /*****                                                               *****/
 2683   /*************************************************************************/
 2684   /*************************************************************************/
 2685 
 2686   /* Snap a given width in scaled coordinates to one of the */
 2687   /* current standard widths.                               */
 2688 
 2689   static FT_Pos
 2690   af_latin_snap_width( AF_Width  widths,
 2691                        FT_UInt   count,
 2692                        FT_Pos    width )
 2693   {
 2694     FT_UInt  n;
 2695     FT_Pos   best      = 64 + 32 + 2;
 2696     FT_Pos   reference = width;
 2697     FT_Pos   scaled;
 2698 
 2699 
 2700     for ( n = 0; n < count; n++ )
 2701     {
 2702       FT_Pos  w;
 2703       FT_Pos  dist;
 2704 
 2705 
 2706       w = widths[n].cur;
 2707       dist = width - w;
 2708       if ( dist < 0 )
 2709         dist = -dist;
 2710       if ( dist < best )
 2711       {
 2712         best      = dist;
 2713         reference = w;
 2714       }
 2715     }
 2716 
 2717     scaled = FT_PIX_ROUND( reference );
 2718 
 2719     if ( width >= reference )
 2720     {
 2721       if ( width < scaled + 48 )
 2722         width = reference;
 2723     }
 2724     else
 2725     {
 2726       if ( width > scaled - 48 )
 2727         width = reference;
 2728     }
 2729 
 2730     return width;
 2731   }
 2732 
 2733 
 2734   /* Compute the snapped width of a given stem, ignoring very thin ones. */
 2735   /* There is a lot of voodoo in this function; changing the hard-coded  */
 2736   /* parameters influence the whole hinting process.                     */
 2737 
 2738   static FT_Pos
 2739   af_latin_compute_stem_width( AF_GlyphHints  hints,
 2740                                AF_Dimension   dim,
 2741                                FT_Pos         width,
 2742                                FT_Pos         base_delta,
 2743                                FT_UInt        base_flags,
 2744                                FT_UInt        stem_flags )
 2745   {
 2746     AF_LatinMetrics  metrics  = (AF_LatinMetrics)hints->metrics;
 2747     AF_LatinAxis     axis     = &metrics->axis[dim];
 2748     FT_Pos           dist     = width;
 2749     FT_Int           sign     = 0;
 2750     FT_Int           vertical = ( dim == AF_DIMENSION_VERT );
 2751 
 2752 
 2753     if ( !AF_LATIN_HINTS_DO_STEM_ADJUST( hints ) ||
 2754          axis->extra_light                       )
 2755       return width;
 2756 
 2757     if ( dist < 0 )
 2758     {
 2759       dist = -width;
 2760       sign = 1;
 2761     }
 2762 
 2763     if ( (  vertical && !AF_LATIN_HINTS_DO_VERT_SNAP( hints ) ) ||
 2764          ( !vertical && !AF_LATIN_HINTS_DO_HORZ_SNAP( hints ) ) )
 2765     {
 2766       /* smooth hinting process: very lightly quantize the stem width */
 2767 
 2768       /* leave the widths of serifs alone */
 2769       if ( ( stem_flags & AF_EDGE_SERIF ) &&
 2770            vertical                       &&
 2771            ( dist < 3 * 64 )              )
 2772         goto Done_Width;
 2773 
 2774       else if ( base_flags & AF_EDGE_ROUND )
 2775       {
 2776         if ( dist < 80 )
 2777           dist = 64;
 2778       }
 2779       else if ( dist < 56 )
 2780         dist = 56;
 2781 
 2782       if ( axis->width_count > 0 )
 2783       {
 2784         FT_Pos  delta;
 2785 
 2786 
 2787         /* compare to standard width */
 2788         delta = dist - axis->widths[0].cur;
 2789 
 2790         if ( delta < 0 )
 2791           delta = -delta;
 2792 
 2793         if ( delta < 40 )
 2794         {
 2795           dist = axis->widths[0].cur;
 2796           if ( dist < 48 )
 2797             dist = 48;
 2798 
 2799           goto Done_Width;
 2800         }
 2801 
 2802         if ( dist < 3 * 64 )
 2803         {
 2804           delta  = dist & 63;
 2805           dist  &= -64;
 2806 
 2807           if ( delta < 10 )
 2808             dist += delta;
 2809 
 2810           else if ( delta < 32 )
 2811             dist += 10;
 2812 
 2813           else if ( delta < 54 )
 2814             dist += 54;
 2815 
 2816           else
 2817             dist += delta;
 2818         }
 2819         else
 2820         {
 2821           /* A stem's end position depends on two values: the start        */
 2822           /* position and the stem length.  The former gets usually        */
 2823           /* rounded to the grid, while the latter gets rounded also if it */
 2824           /* exceeds a certain length (see below in this function).  This  */
 2825           /* `double rounding' can lead to a great difference to the       */
 2826           /* original, unhinted position; this normally doesn't matter for */
 2827           /* large PPEM values, but for small sizes it can easily make     */
 2828           /* outlines collide.  For this reason, we adjust the stem length */
 2829           /* by a small amount depending on the PPEM value in case the     */
 2830           /* former and latter rounding both point into the same           */
 2831           /* direction.                                                    */
 2832 
 2833           FT_Pos  bdelta = 0;
 2834 
 2835 
 2836           if ( ( ( width > 0 ) && ( base_delta > 0 ) ) ||
 2837                ( ( width < 0 ) && ( base_delta < 0 ) ) )
 2838           {
 2839             FT_UInt  ppem = metrics->root.scaler.face->size->metrics.x_ppem;
 2840 
 2841 
 2842             if ( ppem < 10 )
 2843               bdelta = base_delta;
 2844             else if ( ppem < 30 )
 2845               bdelta = ( base_delta * (FT_Pos)( 30 - ppem ) ) / 20;
 2846 
 2847             if ( bdelta < 0 )
 2848               bdelta = -bdelta;
 2849           }
 2850 
 2851           dist = ( dist - bdelta + 32 ) & ~63;
 2852         }
 2853       }
 2854     }
 2855     else
 2856     {
 2857       /* strong hinting process: snap the stem width to integer pixels */
 2858 
 2859       FT_Pos  org_dist = dist;
 2860 
 2861 
 2862       dist = af_latin_snap_width( axis->widths, axis->width_count, dist );
 2863 
 2864       if ( vertical )
 2865       {
 2866         /* in the case of vertical hinting, always round */
 2867         /* the stem heights to integer pixels            */
 2868 
 2869         if ( dist >= 64 )
 2870           dist = ( dist + 16 ) & ~63;
 2871         else
 2872           dist = 64;
 2873       }
 2874       else
 2875       {
 2876         if ( AF_LATIN_HINTS_DO_MONO( hints ) )
 2877         {
 2878           /* monochrome horizontal hinting: snap widths to integer pixels */
 2879           /* with a different threshold                                   */
 2880 
 2881           if ( dist < 64 )
 2882             dist = 64;
 2883           else
 2884             dist = ( dist + 32 ) & ~63;
 2885         }
 2886         else
 2887         {
 2888           /* for horizontal anti-aliased hinting, we adopt a more subtle */
 2889           /* approach: we strengthen small stems, round stems whose size */
 2890           /* is between 1 and 2 pixels to an integer, otherwise nothing  */
 2891 
 2892           if ( dist < 48 )
 2893             dist = ( dist + 64 ) >> 1;
 2894 
 2895           else if ( dist < 128 )
 2896           {
 2897             /* We only round to an integer width if the corresponding */
 2898             /* distortion is less than 1/4 pixel.  Otherwise this     */
 2899             /* makes everything worse since the diagonals, which are  */
 2900             /* not hinted, appear a lot bolder or thinner than the    */
 2901             /* vertical stems.                                        */
 2902 
 2903             FT_Pos  delta;
 2904 
 2905 
 2906             dist = ( dist + 22 ) & ~63;
 2907             delta = dist - org_dist;
 2908             if ( delta < 0 )
 2909               delta = -delta;
 2910 
 2911             if ( delta >= 16 )
 2912             {
 2913               dist = org_dist;
 2914               if ( dist < 48 )
 2915                 dist = ( dist + 64 ) >> 1;
 2916             }
 2917           }
 2918           else
 2919             /* round otherwise to prevent color fringes in LCD mode */
 2920             dist = ( dist + 32 ) & ~63;
 2921         }
 2922       }
 2923     }
 2924 
 2925   Done_Width:
 2926     if ( sign )
 2927       dist = -dist;
 2928 
 2929     return dist;
 2930   }
 2931 
 2932 
 2933   /* Align one stem edge relative to the previous stem edge. */
 2934 
 2935   static void
 2936   af_latin_align_linked_edge( AF_GlyphHints  hints,
 2937                               AF_Dimension   dim,
 2938                               AF_Edge        base_edge,
 2939                               AF_Edge        stem_edge )
 2940   {
 2941     FT_Pos  dist, base_delta;
 2942     FT_Pos  fitted_width;
 2943 
 2944 
 2945     dist       = stem_edge->opos - base_edge->opos;
 2946     base_delta = base_edge->pos - base_edge->opos;
 2947 
 2948     fitted_width = af_latin_compute_stem_width( hints, dim,
 2949                                                 dist, base_delta,
 2950                                                 base_edge->flags,
 2951                                                 stem_edge->flags );
 2952 
 2953 
 2954     stem_edge->pos = base_edge->pos + fitted_width;
 2955 
 2956     FT_TRACE5(( "  LINK: edge %ld (opos=%.2f) linked to %.2f,"
 2957                 " dist was %.2f, now %.2f\n",
 2958                 stem_edge - hints->axis[dim].edges, stem_edge->opos / 64.0,
 2959                 stem_edge->pos / 64.0, dist / 64.0, fitted_width / 64.0 ));
 2960   }
 2961 
 2962 
 2963   /* Shift the coordinates of the `serif' edge by the same amount */
 2964   /* as the corresponding `base' edge has been moved already.     */
 2965 
 2966   static void
 2967   af_latin_align_serif_edge( AF_GlyphHints  hints,
 2968                              AF_Edge        base,
 2969                              AF_Edge        serif )
 2970   {
 2971     FT_UNUSED( hints );
 2972 
 2973     serif->pos = base->pos + ( serif->opos - base->opos );
 2974   }
 2975 
 2976 
 2977   /*************************************************************************/
 2978   /*************************************************************************/
 2979   /*************************************************************************/
 2980   /****                                                                 ****/
 2981   /****                    E D G E   H I N T I N G                      ****/
 2982   /****                                                                 ****/
 2983   /*************************************************************************/
 2984   /*************************************************************************/
 2985   /*************************************************************************/
 2986 
 2987 
 2988   /* The main grid-fitting routine. */
 2989 
 2990   static void
 2991   af_latin_hint_edges( AF_GlyphHints  hints,
 2992                        AF_Dimension   dim )
 2993   {
 2994     AF_AxisHints  axis       = &hints->axis[dim];
 2995     AF_Edge       edges      = axis->edges;
 2996     AF_Edge       edge_limit = FT_OFFSET( edges, axis->num_edges );
 2997     FT_PtrDist    n_edges;
 2998     AF_Edge       edge;
 2999     AF_Edge       anchor     = NULL;
 3000     FT_Int        has_serifs = 0;
 3001 
 3002     AF_StyleClass   style_class  = hints->metrics->style_class;
 3003     AF_ScriptClass  script_class = af_script_classes[style_class->script];
 3004 
 3005     FT_Bool  top_to_bottom_hinting = 0;
 3006 
 3007 #ifdef FT_DEBUG_LEVEL_TRACE
 3008     FT_UInt  num_actions = 0;
 3009 #endif
 3010 
 3011 
 3012     FT_TRACE5(( "latin %s edge hinting (style `%s')\n",
 3013                 dim == AF_DIMENSION_VERT ? "horizontal" : "vertical",
 3014                 af_style_names[hints->metrics->style_class->style] ));
 3015 
 3016     if ( dim == AF_DIMENSION_VERT )
 3017       top_to_bottom_hinting = script_class->top_to_bottom_hinting;
 3018 
 3019     /* we begin by aligning all stems relative to the blue zone */
 3020     /* if needed -- that's only for horizontal edges            */
 3021 
 3022     if ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_BLUES( hints ) )
 3023     {
 3024       for ( edge = edges; edge < edge_limit; edge++ )
 3025       {
 3026         AF_Width  blue;
 3027         AF_Edge   edge1, edge2; /* these edges form the stem to check */
 3028 
 3029 
 3030         if ( edge->flags & AF_EDGE_DONE )
 3031           continue;
 3032 
 3033         edge1 = NULL;
 3034         edge2 = edge->link;
 3035 
 3036         /*
 3037          * If a stem contains both a neutral and a non-neutral blue zone,
 3038          * skip the neutral one.  Otherwise, outlines with different
 3039          * directions might be incorrectly aligned at the same vertical
 3040          * position.
 3041          *
 3042          * If we have two neutral blue zones, skip one of them.
 3043          *
 3044          */
 3045         if ( edge->blue_edge && edge2 && edge2->blue_edge )
 3046         {
 3047           FT_Byte  neutral  = edge->flags  & AF_EDGE_NEUTRAL;
 3048           FT_Byte  neutral2 = edge2->flags & AF_EDGE_NEUTRAL;
 3049 
 3050 
 3051           if ( neutral2 )
 3052           {
 3053             edge2->blue_edge = NULL;
 3054             edge2->flags    &= ~AF_EDGE_NEUTRAL;
 3055           }
 3056           else if ( neutral )
 3057           {
 3058             edge->blue_edge = NULL;
 3059             edge->flags    &= ~AF_EDGE_NEUTRAL;
 3060           }
 3061         }
 3062 
 3063         blue = edge->blue_edge;
 3064         if ( blue )
 3065           edge1 = edge;
 3066 
 3067         /* flip edges if the other edge is aligned to a blue zone */
 3068         else if ( edge2 && edge2->blue_edge )
 3069         {
 3070           blue  = edge2->blue_edge;
 3071           edge1 = edge2;
 3072           edge2 = edge;
 3073         }
 3074 
 3075         if ( !edge1 )
 3076           continue;
 3077 
 3078 #ifdef FT_DEBUG_LEVEL_TRACE
 3079         if ( !anchor )
 3080           FT_TRACE5(( "  BLUE_ANCHOR: edge %ld (opos=%.2f) snapped to %.2f,"
 3081                       " was %.2f (anchor=edge %ld)\n",
 3082                       edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
 3083                       edge1->pos / 64.0, edge - edges ));
 3084         else
 3085           FT_TRACE5(( "  BLUE: edge %ld (opos=%.2f) snapped to %.2f,"
 3086                       " was %.2f\n",
 3087                       edge1 - edges, edge1->opos / 64.0, blue->fit / 64.0,
 3088                       edge1->pos / 64.0 ));
 3089 
 3090         num_actions++;
 3091 #endif
 3092 
 3093         edge1->pos    = blue->fit;
 3094         edge1->flags |= AF_EDGE_DONE;
 3095 
 3096         if ( edge2 && !edge2->blue_edge )
 3097         {
 3098           af_latin_align_linked_edge( hints, dim, edge1, edge2 );
 3099           edge2->flags |= AF_EDGE_DONE;
 3100 
 3101 #ifdef FT_DEBUG_LEVEL_TRACE
 3102           num_actions++;
 3103 #endif
 3104         }
 3105 
 3106         if ( !anchor )
 3107           anchor = edge;
 3108       }
 3109     }
 3110 
 3111     /* now we align all other stem edges, trying to maintain the */
 3112     /* relative order of stems in the glyph                      */
 3113     for ( edge = edges; edge < edge_limit; edge++ )
 3114     {
 3115       AF_Edge  edge2;
 3116 
 3117 
 3118       if ( edge->flags & AF_EDGE_DONE )
 3119         continue;
 3120 
 3121       /* skip all non-stem edges */
 3122       edge2 = edge->link;
 3123       if ( !edge2 )
 3124       {
 3125         has_serifs++;
 3126         continue;
 3127       }
 3128 
 3129       /* now align the stem */
 3130 
 3131       /* this should not happen, but it's better to be safe */
 3132       if ( edge2->blue_edge )
 3133       {
 3134         FT_TRACE5(( "  ASSERTION FAILED for edge %ld\n", edge2 - edges ));
 3135 
 3136         af_latin_align_linked_edge( hints, dim, edge2, edge );
 3137         edge->flags |= AF_EDGE_DONE;
 3138 
 3139 #ifdef FT_DEBUG_LEVEL_TRACE
 3140         num_actions++;
 3141 #endif
 3142         continue;
 3143       }
 3144 
 3145       if ( !anchor )
 3146       {
 3147         /* if we reach this if clause, no stem has been aligned yet */
 3148 
 3149         FT_Pos  org_len, org_center, cur_len;
 3150         FT_Pos  cur_pos1, error1, error2, u_off, d_off;
 3151 
 3152 
 3153         org_len = edge2->opos - edge->opos;
 3154         cur_len = af_latin_compute_stem_width( hints, dim,
 3155                                                org_len, 0,
 3156                                                edge->flags,
 3157                                                edge2->flags );
 3158 
 3159         /* some voodoo to specially round edges for small stem widths; */
 3160         /* the idea is to align the center of a stem, then shifting    */
 3161         /* the stem edges to suitable positions                        */
 3162         if ( cur_len <= 64 )
 3163         {
 3164           /* width <= 1px */
 3165           u_off = 32;
 3166           d_off = 32;
 3167         }
 3168         else
 3169         {
 3170           /* 1px < width < 1.5px */
 3171           u_off = 38;
 3172           d_off = 26;
 3173         }
 3174 
 3175         if ( cur_len < 96 )
 3176         {
 3177           org_center = edge->opos + ( org_len >> 1 );
 3178           cur_pos1   = FT_PIX_ROUND( org_center );
 3179 
 3180           error1 = org_center - ( cur_pos1 - u_off );
 3181           if ( error1 < 0 )
 3182             error1 = -error1;
 3183 
 3184           error2 = org_center - ( cur_pos1 + d_off );
 3185           if ( error2 < 0 )
 3186             error2 = -error2;
 3187 
 3188           if ( error1 < error2 )
 3189             cur_pos1 -= u_off;
 3190           else
 3191             cur_pos1 += d_off;
 3192 
 3193           edge->pos  = cur_pos1 - cur_len / 2;
 3194           edge2->pos = edge->pos + cur_len;
 3195         }
 3196         else
 3197           edge->pos = FT_PIX_ROUND( edge->opos );
 3198 
 3199         anchor       = edge;
 3200         edge->flags |= AF_EDGE_DONE;
 3201 
 3202         FT_TRACE5(( "  ANCHOR: edge %ld (opos=%.2f) and %ld (opos=%.2f)"
 3203                     " snapped to %.2f and %.2f\n",
 3204                     edge - edges, edge->opos / 64.0,
 3205                     edge2 - edges, edge2->opos / 64.0,
 3206                     edge->pos / 64.0, edge2->pos / 64.0 ));
 3207 
 3208         af_latin_align_linked_edge( hints, dim, edge, edge2 );
 3209 
 3210 #ifdef FT_DEBUG_LEVEL_TRACE
 3211         num_actions += 2;
 3212 #endif
 3213       }
 3214       else
 3215       {
 3216         FT_Pos  org_pos, org_len, org_center, cur_len;
 3217         FT_Pos  cur_pos1, cur_pos2, delta1, delta2;
 3218 
 3219 
 3220         org_pos    = anchor->pos + ( edge->opos - anchor->opos );
 3221         org_len    = edge2->opos - edge->opos;
 3222         org_center = org_pos + ( org_len >> 1 );
 3223 
 3224         cur_len = af_latin_compute_stem_width( hints, dim,
 3225                                                org_len, 0,
 3226                                                edge->flags,
 3227                                                edge2->flags );
 3228 
 3229         if ( edge2->flags & AF_EDGE_DONE )
 3230         {
 3231           FT_TRACE5(( "  ADJUST: edge %ld (pos=%.2f) moved to %.2f\n",
 3232                       edge - edges, edge->pos / 64.0,
 3233                       ( edge2->pos - cur_len ) / 64.0 ));
 3234 
 3235           edge->pos = edge2->pos - cur_len;
 3236         }
 3237 
 3238         else if ( cur_len < 96 )
 3239         {
 3240           FT_Pos  u_off, d_off;
 3241 
 3242 
 3243           cur_pos1 = FT_PIX_ROUND( org_center );
 3244 
 3245           if ( cur_len <= 64 )
 3246           {
 3247             u_off = 32;
 3248             d_off = 32;
 3249           }
 3250           else
 3251           {
 3252             u_off = 38;
 3253             d_off = 26;
 3254           }
 3255 
 3256           delta1 = org_center - ( cur_pos1 - u_off );
 3257           if ( delta1 < 0 )
 3258             delta1 = -delta1;
 3259 
 3260           delta2 = org_center - ( cur_pos1 + d_off );
 3261           if ( delta2 < 0 )
 3262             delta2 = -delta2;
 3263 
 3264           if ( delta1 < delta2 )
 3265             cur_pos1 -= u_off;
 3266           else
 3267             cur_pos1 += d_off;
 3268 
 3269           edge->pos  = cur_pos1 - cur_len / 2;
 3270           edge2->pos = cur_pos1 + cur_len / 2;
 3271 
 3272           FT_TRACE5(( "  STEM: edge %ld (opos=%.2f) linked to %ld (opos=%.2f)"
 3273                       " snapped to %.2f and %.2f\n",
 3274                       edge - edges, edge->opos / 64.0,
 3275                       edge2 - edges, edge2->opos / 64.0,
 3276                       edge->pos / 64.0, edge2->pos / 64.0 ));
 3277         }
 3278 
 3279         else
 3280         {
 3281           org_pos    = anchor->pos + ( edge->opos - anchor->opos );
 3282           org_len    = edge2->opos - edge->opos;
 3283           org_center = org_pos + ( org_len >> 1 );
 3284 
 3285           cur_len    = af_latin_compute_stem_width( hints, dim,
 3286                                                     org_len, 0,
 3287                                                     edge->flags,
 3288                                                     edge2->flags );
 3289 
 3290           cur_pos1 = FT_PIX_ROUND( org_pos );
 3291           delta1   = cur_pos1 + ( cur_len >> 1 ) - org_center;
 3292           if ( delta1 < 0 )
 3293             delta1 = -delta1;
 3294 
 3295           cur_pos2 = FT_PIX_ROUND( org_pos + org_len ) - cur_len;
 3296           delta2   = cur_pos2 + ( cur_len >> 1 ) - org_center;
 3297           if ( delta2 < 0 )
 3298             delta2 = -delta2;
 3299 
 3300           edge->pos  = ( delta1 < delta2 ) ? cur_pos1 : cur_pos2;
 3301           edge2->pos = edge->pos + cur_len;
 3302 
 3303           FT_TRACE5(( "  STEM: edge %ld (opos=%.2f) linked to %ld (opos=%.2f)"
 3304                       " snapped to %.2f and %.2f\n",
 3305                       edge - edges, edge->opos / 64.0,
 3306                       edge2 - edges, edge2->opos / 64.0,
 3307                       edge->pos / 64.0, edge2->pos / 64.0 ));
 3308         }
 3309 
 3310 #ifdef FT_DEBUG_LEVEL_TRACE
 3311         num_actions++;
 3312 #endif
 3313 
 3314         edge->flags  |= AF_EDGE_DONE;
 3315         edge2->flags |= AF_EDGE_DONE;
 3316 
 3317         if ( edge > edges                                             &&
 3318              ( top_to_bottom_hinting ? ( edge->pos > edge[-1].pos )
 3319                                      : ( edge->pos < edge[-1].pos ) ) )
 3320         {
 3321           /* don't move if stem would (almost) disappear otherwise; */
 3322           /* the ad-hoc value 16 corresponds to 1/4px               */
 3323           if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 )
 3324           {
 3325 #ifdef FT_DEBUG_LEVEL_TRACE
 3326             FT_TRACE5(( "  BOUND: edge %ld (pos=%.2f) moved to %.2f\n",
 3327                         edge - edges,
 3328                         edge->pos / 64.0,
 3329                         edge[-1].pos / 64.0 ));
 3330 
 3331             num_actions++;
 3332 #endif
 3333 
 3334             edge->pos = edge[-1].pos;
 3335           }
 3336         }
 3337       }
 3338     }
 3339 
 3340     /* make sure that lowercase m's maintain their symmetry */
 3341 
 3342     /* In general, lowercase m's have six vertical edges if they are sans */
 3343     /* serif, or twelve if they are with serifs.  This implementation is  */
 3344     /* based on that assumption, and seems to work very well with most    */
 3345     /* faces.  However, if for a certain face this assumption is not      */
 3346     /* true, the m is just rendered like before.  In addition, any stem   */
 3347     /* correction will only be applied to symmetrical glyphs (even if the */
 3348     /* glyph is not an m), so the potential for unwanted distortion is    */
 3349     /* relatively low.                                                    */
 3350 
 3351     /* We don't handle horizontal edges since we can't easily assure that */
 3352     /* the third (lowest) stem aligns with the base line; it might end up */
 3353     /* one pixel higher or lower.                                         */
 3354 
 3355     n_edges = edge_limit - edges;
 3356     if ( dim == AF_DIMENSION_HORZ && ( n_edges == 6 || n_edges == 12 ) )
 3357     {
 3358       AF_Edge  edge1, edge2, edge3;
 3359       FT_Pos   dist1, dist2, span, delta;
 3360 
 3361 
 3362       if ( n_edges == 6 )
 3363       {
 3364         edge1 = edges;
 3365         edge2 = edges + 2;
 3366         edge3 = edges + 4;
 3367       }
 3368       else
 3369       {
 3370         edge1 = edges + 1;
 3371         edge2 = edges + 5;
 3372         edge3 = edges + 9;
 3373       }
 3374 
 3375       dist1 = edge2->opos - edge1->opos;
 3376       dist2 = edge3->opos - edge2->opos;
 3377 
 3378       span = dist1 - dist2;
 3379       if ( span < 0 )
 3380         span = -span;
 3381 
 3382       if ( span < 8 )
 3383       {
 3384         delta = edge3->pos - ( 2 * edge2->pos - edge1->pos );
 3385         edge3->pos -= delta;
 3386         if ( edge3->link )
 3387           edge3->link->pos -= delta;
 3388 
 3389         /* move the serifs along with the stem */
 3390         if ( n_edges == 12 )
 3391         {
 3392           ( edges + 8 )->pos -= delta;
 3393           ( edges + 11 )->pos -= delta;
 3394         }
 3395 
 3396         edge3->flags |= AF_EDGE_DONE;
 3397         if ( edge3->link )
 3398           edge3->link->flags |= AF_EDGE_DONE;
 3399       }
 3400     }
 3401 
 3402     if ( has_serifs || !anchor )
 3403     {
 3404       /*
 3405        * now hint the remaining edges (serifs and single) in order
 3406        * to complete our processing
 3407        */
 3408       for ( edge = edges; edge < edge_limit; edge++ )
 3409       {
 3410         FT_Pos  delta;
 3411 
 3412 
 3413         if ( edge->flags & AF_EDGE_DONE )
 3414           continue;
 3415 
 3416         delta = 1000;
 3417 
 3418         if ( edge->serif )
 3419         {
 3420           delta = edge->serif->opos - edge->opos;
 3421           if ( delta < 0 )
 3422             delta = -delta;
 3423         }
 3424 
 3425         if ( delta < 64 + 16 )
 3426         {
 3427           af_latin_align_serif_edge( hints, edge->serif, edge );
 3428           FT_TRACE5(( "  SERIF: edge %ld (opos=%.2f) serif to %ld (opos=%.2f)"
 3429                       " aligned to %.2f\n",
 3430                       edge - edges, edge->opos / 64.0,
 3431                       edge->serif - edges, edge->serif->opos / 64.0,
 3432                       edge->pos / 64.0 ));
 3433         }
 3434         else if ( !anchor )
 3435         {
 3436           edge->pos = FT_PIX_ROUND( edge->opos );
 3437           anchor    = edge;
 3438           FT_TRACE5(( "  SERIF_ANCHOR: edge %ld (opos=%.2f)"
 3439                       " snapped to %.2f\n",
 3440                       edge-edges, edge->opos / 64.0, edge->pos / 64.0 ));
 3441         }
 3442         else
 3443         {
 3444           AF_Edge  before, after;
 3445 
 3446 
 3447           for ( before = edge - 1; before >= edges; before-- )
 3448             if ( before->flags & AF_EDGE_DONE )
 3449               break;
 3450 
 3451           for ( after = edge + 1; after < edge_limit; after++ )
 3452             if ( after->flags & AF_EDGE_DONE )
 3453               break;
 3454 
 3455           if ( before >= edges && before < edge   &&
 3456                after < edge_limit && after > edge )
 3457           {
 3458             if ( after->opos == before->opos )
 3459               edge->pos = before->pos;
 3460             else
 3461               edge->pos = before->pos +
 3462                           FT_MulDiv( edge->opos - before->opos,
 3463                                      after->pos - before->pos,
 3464                                      after->opos - before->opos );
 3465 
 3466             FT_TRACE5(( "  SERIF_LINK1: edge %ld (opos=%.2f) snapped to %.2f"
 3467                         " from %ld (opos=%.2f)\n",
 3468                         edge - edges, edge->opos / 64.0,
 3469                         edge->pos / 64.0,
 3470                         before - edges, before->opos / 64.0 ));
 3471           }
 3472           else
 3473           {
 3474             edge->pos = anchor->pos +
 3475                         ( ( edge->opos - anchor->opos + 16 ) & ~31 );
 3476             FT_TRACE5(( "  SERIF_LINK2: edge %ld (opos=%.2f)"
 3477                         " snapped to %.2f\n",
 3478                         edge - edges, edge->opos / 64.0, edge->pos / 64.0 ));
 3479           }
 3480         }
 3481 
 3482 #ifdef FT_DEBUG_LEVEL_TRACE
 3483         num_actions++;
 3484 #endif
 3485         edge->flags |= AF_EDGE_DONE;
 3486 
 3487         if ( edge > edges                                             &&
 3488              ( top_to_bottom_hinting ? ( edge->pos > edge[-1].pos )
 3489                                      : ( edge->pos < edge[-1].pos ) ) )
 3490         {
 3491           /* don't move if stem would (almost) disappear otherwise; */
 3492           /* the ad-hoc value 16 corresponds to 1/4px               */
 3493           if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 )
 3494           {
 3495 #ifdef FT_DEBUG_LEVEL_TRACE
 3496             FT_TRACE5(( "  BOUND: edge %ld (pos=%.2f) moved to %.2f\n",
 3497                         edge - edges,
 3498                         edge->pos / 64.0,
 3499                         edge[-1].pos / 64.0 ));
 3500 
 3501             num_actions++;
 3502 #endif
 3503             edge->pos = edge[-1].pos;
 3504           }
 3505         }
 3506 
 3507         if ( edge + 1 < edge_limit                                   &&
 3508              edge[1].flags & AF_EDGE_DONE                            &&
 3509              ( top_to_bottom_hinting ? ( edge->pos < edge[1].pos )
 3510                                      : ( edge->pos > edge[1].pos ) ) )
 3511         {
 3512           /* don't move if stem would (almost) disappear otherwise; */
 3513           /* the ad-hoc value 16 corresponds to 1/4px               */
 3514           if ( edge->link && FT_ABS( edge->link->pos - edge[-1].pos ) > 16 )
 3515           {
 3516 #ifdef FT_DEBUG_LEVEL_TRACE
 3517             FT_TRACE5(( "  BOUND: edge %ld (pos=%.2f) moved to %.2f\n",
 3518                         edge - edges,
 3519                         edge->pos / 64.0,
 3520                         edge[1].pos / 64.0 ));
 3521 
 3522             num_actions++;
 3523 #endif
 3524 
 3525             edge->pos = edge[1].pos;
 3526           }
 3527         }
 3528       }
 3529     }
 3530 
 3531 #ifdef FT_DEBUG_LEVEL_TRACE
 3532     if ( !num_actions )
 3533       FT_TRACE5(( "  (none)\n" ));
 3534     FT_TRACE5(( "\n" ));
 3535 #endif
 3536   }
 3537 
 3538 
 3539   /* Apply the complete hinting algorithm to a latin glyph. */
 3540 
 3541   static FT_Error
 3542   af_latin_hints_apply( FT_UInt          glyph_index,
 3543                         AF_GlyphHints    hints,
 3544                         FT_Outline*      outline,
 3545                         AF_LatinMetrics  metrics )
 3546   {
 3547     FT_Error  error;
 3548     int       dim;
 3549 
 3550     AF_LatinAxis  axis;
 3551 
 3552 
 3553     error = af_glyph_hints_reload( hints, outline );
 3554     if ( error )
 3555       goto Exit;
 3556 
 3557     /* analyze glyph outline */
 3558     if ( AF_HINTS_DO_HORIZONTAL( hints ) )
 3559     {
 3560       axis  = &metrics->axis[AF_DIMENSION_HORZ];
 3561       error = af_latin_hints_detect_features( hints,
 3562                                               axis->width_count,
 3563                                               axis->widths,
 3564                                               AF_DIMENSION_HORZ );
 3565       if ( error )
 3566         goto Exit;
 3567     }
 3568 
 3569     if ( AF_HINTS_DO_VERTICAL( hints ) )
 3570     {
 3571       axis  = &metrics->axis[AF_DIMENSION_VERT];
 3572       error = af_latin_hints_detect_features( hints,
 3573                                               axis->width_count,
 3574                                               axis->widths,
 3575                                               AF_DIMENSION_VERT );
 3576       if ( error )
 3577         goto Exit;
 3578 
 3579       /* apply blue zones to base characters only */
 3580       if ( !( metrics->root.globals->glyph_styles[glyph_index] & AF_NONBASE ) )
 3581         af_latin_hints_compute_blue_edges( hints, metrics );
 3582     }
 3583 
 3584     /* grid-fit the outline */
 3585     for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
 3586     {
 3587       if ( ( dim == AF_DIMENSION_HORZ && AF_HINTS_DO_HORIZONTAL( hints ) ) ||
 3588            ( dim == AF_DIMENSION_VERT && AF_HINTS_DO_VERTICAL( hints ) )   )
 3589       {
 3590         af_latin_hint_edges( hints, (AF_Dimension)dim );
 3591         af_glyph_hints_align_edge_points( hints, (AF_Dimension)dim );
 3592         af_glyph_hints_align_strong_points( hints, (AF_Dimension)dim );
 3593         af_glyph_hints_align_weak_points( hints, (AF_Dimension)dim );
 3594       }
 3595     }
 3596 
 3597     af_glyph_hints_save( hints, outline );
 3598 
 3599   Exit:
 3600     return error;
 3601   }
 3602 
 3603 
 3604   /*************************************************************************/
 3605   /*************************************************************************/
 3606   /*****                                                               *****/
 3607   /*****              L A T I N   S C R I P T   C L A S S              *****/
 3608   /*****                                                               *****/
 3609   /*************************************************************************/
 3610   /*************************************************************************/
 3611 
 3612 
 3613   AF_DEFINE_WRITING_SYSTEM_CLASS(
 3614     af_latin_writing_system_class,
 3615 
 3616     AF_WRITING_SYSTEM_LATIN,
 3617 
 3618     sizeof ( AF_LatinMetricsRec ),
 3619 
 3620     (AF_WritingSystem_InitMetricsFunc) af_latin_metrics_init,        /* style_metrics_init    */
 3621     (AF_WritingSystem_ScaleMetricsFunc)af_latin_metrics_scale,       /* style_metrics_scale   */
 3622     (AF_WritingSystem_DoneMetricsFunc) NULL,                         /* style_metrics_done    */
 3623     (AF_WritingSystem_GetStdWidthsFunc)af_latin_get_standard_widths, /* style_metrics_getstdw */
 3624 
 3625     (AF_WritingSystem_InitHintsFunc)   af_latin_hints_init,          /* style_hints_init      */
 3626     (AF_WritingSystem_ApplyHintsFunc)  af_latin_hints_apply          /* style_hints_apply     */
 3627   )
 3628 
 3629 
 3630 /* END */