"Fossies" - the Fresh Open Source Software Archive

Member "SDL2_ttf-2.20.2/external/freetype/src/autofit/afcjk.c" (25 May 2022, 66119 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 "afcjk.c" see the Fossies "Dox" file reference documentation.

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