Oops, a bug fixed.

diff --git a/src/autofit/afcjk.c b/src/autofit/afcjk.c
index c15d343..2be157a 100644
--- a/src/autofit/afcjk.c
+++ b/src/autofit/afcjk.c
@@ -45,8 +45,456 @@
   /*************************************************************************/
   /*************************************************************************/
 
+  /* Basically the Latin version with AF_CJKMetrics to replace AF_LatinMetrics */
+  FT_LOCAL_DEF( void )
+  af_cjk_metrics_init_widths( AF_CJKMetrics    metrics,
+                              FT_Face          face,
+                              FT_ULong         charcode )
+  {
+    /* scan the array of segments in each direction */
+    AF_GlyphHintsRec  hints[1];
+
+
+    af_glyph_hints_init( hints, face->memory );
+
+    metrics->axis[AF_DIMENSION_HORZ].width_count = 0;
+    metrics->axis[AF_DIMENSION_VERT].width_count = 0;
+
+    {
+      FT_Error             error;
+      FT_UInt              glyph_index;
+      int                  dim;
+      AF_CJKMetricsRec     dummy[1];
+      AF_Scaler            scaler = &dummy->root.scaler;
+
+
+      glyph_index = FT_Get_Char_Index( face, charcode );
+      if ( glyph_index == 0 )
+        goto Exit;
+
+      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+      if ( error || face->glyph->outline.n_points <= 0 )
+        goto Exit;
+
+      FT_ZERO( dummy );
+
+      dummy->units_per_em = metrics->units_per_em;
+      scaler->x_scale     = scaler->y_scale = 0x10000L;
+      scaler->x_delta     = scaler->y_delta = 0;
+      scaler->face        = face;
+      scaler->render_mode = FT_RENDER_MODE_NORMAL;
+      scaler->flags       = 0;
+
+      af_glyph_hints_rescale( hints, (AF_ScriptMetrics)dummy );
+
+      error = af_glyph_hints_reload( hints, &face->glyph->outline );
+      if ( error )
+        goto Exit;
+
+      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+      {
+        AF_CJKAxis    axis    = &metrics->axis[dim];
+        AF_AxisHints  axhints = &hints->axis[dim];
+        AF_Segment    seg, limit, link;
+        FT_UInt       num_widths = 0;
+
+
+        error = af_latin_hints_compute_segments( hints,
+                                                 (AF_Dimension)dim );
+        if ( error )
+          goto Exit;
+
+        af_latin_hints_link_segments( hints,
+                                      (AF_Dimension)dim );
+
+        seg   = axhints->segments;
+        limit = seg + axhints->num_segments;
+
+        for ( ; seg < limit; seg++ )
+        {
+          link = seg->link;
+
+          /* we only consider stem segments there! */
+          if ( link && link->link == seg && link > seg )
+          {
+            FT_Pos  dist;
+
+
+            dist = seg->pos - link->pos;
+            if ( dist < 0 )
+              dist = -dist;
+
+            if ( num_widths < AF_CJK_MAX_WIDTHS )
+              axis->widths[ num_widths++ ].org = dist;
+          }
+        }
+
+        af_sort_widths( num_widths, axis->widths );
+        axis->width_count = num_widths;
+      }
+
+  Exit:
+      for ( dim = 0; dim < AF_DIMENSION_MAX; dim++ )
+      {
+        AF_CJKAxis    axis = &metrics->axis[dim];
+        FT_Pos        stdw;
+
+
+        stdw = ( axis->width_count > 0 )
+                 ? axis->widths[0].org
+                 : AF_LATIN_CONSTANT( metrics, 50 );
+
+        /* let's try 20% of the smallest width */
+        axis->edge_distance_threshold = stdw / 5;
+        axis->standard_width          = stdw;
+        axis->extra_light             = 0;
+      }
+    }
+
+    af_glyph_hints_done( hints );
+  }
+
+
+
+#define AF_CJK_MAX_TEST_CHARACTERS  32
+
+
+  /* Every blue zone has 2 types of fill and unfill, 
+   * Which means fill the entire square or not.
+   * */ 
+  enum
+  {
+    AF_CJK_BLUE_TYPE_FILL,
+    AF_CJK_BLUE_TYPE_UNFILL,
+    AF_CJK_BLUE_TYPE_MAX
+  };
+
+  /* Put some common and representative CJK characters here. */ 
+  static const FT_ULong af_cjk_blue_chars[AF_CJK_BLUE_MAX]
+                                         [AF_CJK_BLUE_TYPE_MAX]
+                                         [AF_CJK_MAX_TEST_CHARACTERS] =
+  {
+    {
+      {
+	0X4ED6, 0X4EEC, 0X4F60, 0X4F86, 0X5011, 0X5230, 0X548C, 0X5730,
+	0X5BF9, 0X5C0D, 0X5C31, 0X5E2D, 0X6211, 0X65F6, 0X6642, 0X6703,
+	0X6765, 0X70BA, 0X80FD, 0X8230, 0X8AAA, 0X8BF4, 0X8FD9, 0X9019,
+	0X9F4A /* top fill */
+      },
+      {
+	0X519B, 0X540C, 0X5DF2, 0X613F, 0X65E2, 0X661F, 0X662F, 0X666F,
+	0X6C11, 0X7167, 0X73B0, 0X73FE, 0X7406, 0X7528, 0X7F6E, 0X8981,
+	0X8ECD, 0X90A3, 0X914D, 0X91CC, 0X958B, 0X96F7, 0X9732, 0X9762,
+	0X987E /* top unfill */
+      }
+    },
+    {
+      {
+	0X4E2A, 0X4E3A, 0X4EBA, 0X4ED6, 0X4EE5, 0X4EEC, 0X4F60, 0X4F86,
+	0X500B, 0X5011, 0X5230, 0X548C, 0X5927, 0X5BF9, 0X5C0D, 0X5C31,
+	0X6211, 0X65F6, 0X6642, 0X6709, 0X6765, 0X70BA, 0X8981, 0X8AAA,
+	0X8BF4 /* bottom fill */
+      },
+      {
+	0X4E3B, 0X4E9B, 0X56E0, 0X5B83, 0X60F3, 0X610F, 0X7406, 0X751F,
+	0X7576, 0X770B, 0X7740, 0X7F6E, 0X8005, 0X81EA, 0X8457, 0X88E1,
+	0X8FC7, 0X8FD8, 0X8FDB, 0X9032, 0X904E, 0X9053, 0X9084, 0X91CC,
+	0X9762 /* bottom unfill */
+      }
+    },
+    {
+      {
+	0X4E9B, 0X4EEC, 0X4F60, 0X4F86, 0X5011, 0X5230, 0X548C, 0X5730,
+	0X5979, 0X5C06, 0X5C07, 0X5C31, 0X5E74, 0X5F97, 0X60C5, 0X6700,
+	0X6837, 0X6A23, 0X7406, 0X80FD, 0X8AAA, 0X8BF4, 0X8FD9, 0X9019,
+	0X90A /* left fill */
+      },
+      {
+	0X5373, 0X5417, 0X5427, 0X542C, 0X5462, 0X54C1, 0X54CD, 0X55CE,
+	0X5E08, 0X5E2B, 0X6536, 0X65AD, 0X65B7, 0X660E, 0X773C, 0X9593,
+	0X95F4, 0X9645, 0X9648, 0X9650, 0X9664, 0X9673, 0X968F, 0X969B,
+	0X96A8 /* left unfill */
+      }
+    },
+    {
+      {
+	0X4E8B, 0X524D, 0X5B78, 0X5C06, 0X5C07, 0X60C5, 0X60F3, 0X6216,
+	0X653F, 0X65AF, 0X65B0, 0X6837, 0X6A23, 0X6C11, 0X6C92, 0X6CA1,
+	0X7136, 0X7279, 0X73B0, 0X73FE, 0X7403, 0X7B2C, 0X7D93, 0X8C01,
+	0X8D77 /* right fill */
+      },
+      {
+	0X4F8B, 0X5225, 0X522B, 0X5236, 0X52A8, 0X52D5, 0X5417, 0X55CE,
+	0X589E, 0X6307, 0X660E, 0X671D, 0X671F, 0X6784, 0X7269, 0X786E,
+	0X79CD, 0X8ABF, 0X8C03, 0X8CBB, 0X8D39, 0X90A3, 0X90FD, 0X9593,
+	0X95F4 /* right unfill */
+      }
+    }
+  };
+
+
+  /* Calculate blue zones for all the CJK_BLUE_XXX's */
+  static void
+  af_cjk_metrics_init_blues( AF_CJKMetrics  metrics,
+                             FT_Face        face,
+                       const FT_ULong       blue_chars[AF_CJK_BLUE_MAX]
+                                              [AF_CJK_BLUE_TYPE_MAX]
+                                              [AF_CJK_MAX_TEST_CHARACTERS] )
+  {
+    FT_Pos        fills[AF_CJK_MAX_TEST_CHARACTERS];
+    FT_Pos        flats [AF_CJK_MAX_TEST_CHARACTERS];
+    FT_Int        num_fills;
+    FT_Int        num_flats;
+    FT_Int        bb;
+    AF_CJKBlue    blue;
+    FT_Error      error;
+    AF_CJKAxis    axis;
+    FT_GlyphSlot  glyph = face->glyph;
+
+    /* we compute the blues simply by loading each character from the    */
+    /* 'blue_chars[blues]' string, then compute its extreme  */
+    /* points (depending blue zone type etc.)                */
+
+    FT_TRACE5(( "cjk blue zones computation\n" ));
+    FT_TRACE5(( "------------------------------------------------\n" ));
+
+    for ( bb = 0; bb < AF_CJK_BLUE_MAX; bb++ )
+    {
+      FT_Int     fill_type;
+      FT_Pos*    blue_ref;
+      FT_Pos*    blue_shoot;
+
+
+      num_fills  = 0;
+      num_flats  = 0;
+      for ( fill_type = 0 ; fill_type < AF_CJK_BLUE_TYPE_MAX; fill_type++)
+      {
+	const FT_ULong*  p     = blue_chars[bb][fill_type];
+	const FT_ULong*  limit = p + AF_CJK_MAX_TEST_CHARACTERS;
+	FT_Bool     fill = ( fill_type == AF_CJK_BLUE_TYPE_FILL );
+
+
+	FT_TRACE5(( "cjk blue %3d/%d: ", bb, fill_type ));
+
+
+	for ( ; p < limit && *p; p++ )
+	{
+	  FT_UInt     glyph_index;
+	  FT_Pos      best_pos; /* same as points.y */
+	  FT_Int      best_point, best_first, best_last;
+	  FT_Vector*  points;
+
+	  FT_TRACE5(( "0X%lX", *p ));
+
+	  /* load the character in the face -- skip unknown or empty ones */
+	  glyph_index = FT_Get_Char_Index( face, *p );
+	  if ( glyph_index == 0 )
+	    continue;
+
+	  error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+	  if ( error || glyph->outline.n_points <= 0 )
+	    continue;
+
+	  /* now compute min or max point indices and coordinates */
+	  points      = glyph->outline.points;
+	  best_point  = -1;
+	  best_pos    = 0;  /* make compiler happy */
+	  best_first  = 0;  /* ditto */
+	  best_last   = 0;  /* ditto */
+
+	  {
+	    FT_Int  nn;
+	    FT_Int  first = 0;
+	    FT_Int  last  = -1;
+
+
+	    for ( nn = 0; nn < glyph->outline.n_contours; first = last+1, nn++ )
+	    {
+	      FT_Int  old_best_point = best_point;
+	      FT_Int  pp;
+
+
+	      last = glyph->outline.contours[nn];
+
+	      /* Avoid single-point contours since they are never rasterized. */
+	      /* In some fonts, they correspond to mark attachment points     */
+	      /* which are way outside of the glyph's real outline.           */
+	      if ( last <= first )
+		  continue;
+
+	      switch (bb)
+              {
+                case AF_CJK_BLUE_TOP:
+                  for ( pp = first; pp <= last; pp++ )
+                    if ( best_point < 0 || points[pp].y > best_pos )
+                    {
+                      best_point = pp;
+                      best_pos     = points[pp].y;
+                    }
+                  break;
+                case AF_CJK_BLUE_BOTTOM:
+                  for ( pp = first; pp <= last; pp++ )
+                    if ( best_point < 0 || points[pp].y < best_pos )
+                    {
+                      best_point = pp;
+                      best_pos     = points[pp].y;
+                    }
+                  break;
+                case AF_CJK_BLUE_LEFT:
+                  for ( pp = first; pp <= last; pp++ )
+                    if ( best_point < 0 || points[pp].x < best_pos )
+                    {
+                      best_point = pp;
+                      best_pos     = points[pp].x;
+                    }
+                  break;
+                case AF_CJK_BLUE_RIGHT:
+                  for ( pp = first; pp <= last; pp++ )
+                    if ( best_point < 0 || points[pp].x > best_pos )
+                    {
+                      best_point = pp;
+                      best_pos     = points[pp].x;
+                    }
+                  break;
+                default:
+                  ;
+            }
+
+	      if ( best_point != old_best_point )
+	      {
+		best_first = first;
+		best_last  = last;
+	      }
+	    }
+	    FT_TRACE5(( "%5ld, ", best_pos ));
+	  }
+
+	  if (fill)
+	    fills[num_fills++]   = best_pos;
+	  else
+	    flats[num_flats++]   = best_pos;
+	}
+
+	FT_TRACE5(( "\n" ));
+      }
+
+      if ( num_flats == 0 && num_fills == 0)
+      {
+	/*
+	 *  we couldn't find a single glyph to compute this blue zone,
+	 *  we will simply ignore it then
+	 */
+	FT_TRACE5(( "empty\n" ));
+	continue;
+      }
+
+      /* we have computed the contents of the `fill' and `flats' tables, */
+      /* now determine the reference position of the blue --  */
+      /* we simply take the median value after a simple sort  */
+      af_sort_pos( num_flats,  flats );
+      af_sort_pos( num_fills,  fills );
+
+      if ( AF_CJK_BLUE_TOP == bb || AF_CJK_BLUE_BOTTOM == bb )
+	  axis = &metrics->axis[AF_DIMENSION_VERT];
+      else
+	  axis = &metrics->axis[AF_DIMENSION_HORZ];
+
+      blue       = & axis->blues[axis->blue_count];
+      blue_ref   = & blue->ref.org;
+      blue_shoot   = & blue->shoot.org;
+
+      axis->blue_count++;
+      if ( num_flats == 0 )
+      {
+	*blue_ref    =
+	*blue_shoot  = fills[num_fills/2];
+      }
+      else if ( num_fills == 0 )
+      {
+	*blue_ref    =
+	*blue_shoot  = flats[num_flats/2];
+      }
+      else
+      {
+	*blue_ref    = fills[num_fills / 2];
+	*blue_shoot  = flats[num_flats / 2];
+      }
+
+      /* make sure blue_ref >= blue_shoot for top/right or
+       * vis vesa for bottom/left. */
+      if ( *blue_shoot != *blue_ref )
+      {
+	FT_Pos   ref      = *blue_ref;
+	FT_Pos   shoot    = *blue_shoot;
+	FT_Bool  under_ref = FT_BOOL( shoot < ref );
+
+
+	if ( (AF_CJK_BLUE_TOP == bb || AF_CJK_BLUE_RIGHT == bb ) ^
+	    under_ref )
+	  *blue_shoot = *blue_ref = ( shoot + ref ) / 2;
+      }
+
+      blue->flags = 0;
+      if ( AF_CJK_BLUE_TOP == bb )
+	blue->flags |= AF_CJK_BLUE_IS_TOP;
+      else if ( AF_CJK_BLUE_RIGHT == bb )
+	blue->flags |= AF_CJK_BLUE_IS_RIGHT;
+
+      FT_TRACE5(( "-- cjk ref = %ld shoot = %ld\n", *blue_ref, *blue_shoot ));
+    }
+    return;
+  }
+
+
+  /* Basically the Latin version with type AF_CJKMetrics for metrics. */
+  FT_LOCAL_DEF( void )
+  af_cjk_metrics_check_digits( AF_CJKMetrics    metrics,
+                               FT_Face          face )
+  {
+    FT_UInt   i;
+    FT_Bool   started = 0, same_width = 1;
+    FT_Fixed  advance, old_advance = 0;
+
+
+    /* check whether all ASCII digits have the same advance width; */
+    /* digit `0' is 0x30 in all supported charmaps                 */
+    for ( i = 0x30; i <= 0x39; i++ )
+    {
+      FT_UInt  glyph_index;
+
+
+      glyph_index = FT_Get_Char_Index( face, i );
+      if ( glyph_index == 0 )
+        continue;
+
+      if ( FT_Get_Advance( face, glyph_index,
+                           FT_LOAD_NO_SCALE         |
+                           FT_LOAD_NO_HINTING       |
+                           FT_LOAD_IGNORE_TRANSFORM,
+                           &advance ) )
+        continue;
+
+      if ( started )
+      {
+        if ( advance != old_advance )
+        {
+          same_width = 0;
+          break;
+        }
+      }
+      else
+      {
+        old_advance = advance;
+        started     = 1;
+      }
+    }
+
+    metrics->root.digits_have_same_width = same_width;
+  }
+
+
   FT_LOCAL_DEF( FT_Error )
-  af_cjk_metrics_init( AF_LatinMetrics  metrics,
+  af_cjk_metrics_init( AF_CJKMetrics    metrics,
                        FT_Face          face )
   {
     FT_CharMap  oldmap = face->charmap;
@@ -54,15 +502,13 @@
 
     metrics->units_per_em = face->units_per_EM;
 
-    /* TODO are there blues? */
-
     if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
       face->charmap = NULL;
     else
     {
-      /* latin's version would suffice */
-      af_latin_metrics_init_widths( metrics, face, 0x7530 );
-      af_latin_metrics_check_digits( metrics, face );
+      af_cjk_metrics_init_widths( metrics, face, 0x7530 );
+      af_cjk_metrics_init_blues( metrics, face, af_cjk_blue_chars );
+      af_cjk_metrics_check_digits( metrics, face );
     }
 
     FT_Set_Charmap( face, oldmap );
@@ -72,30 +518,98 @@
 
 
   static void
-  af_cjk_metrics_scale_dim( AF_LatinMetrics  metrics,
+  af_cjk_metrics_scale_dim( AF_CJKMetrics    metrics,
                             AF_Scaler        scaler,
                             AF_Dimension     dim )
   {
-    AF_LatinAxis  axis;
+    FT_Fixed    scale;
+    FT_Pos      delta;
+    AF_CJKAxis  axis;
+    FT_UInt     nn;
 
 
     axis = &metrics->axis[dim];
 
     if ( dim == AF_DIMENSION_HORZ )
     {
-      axis->scale = scaler->x_scale;
-      axis->delta = scaler->x_delta;
+      scale = scaler->x_scale;
+      delta = scaler->x_delta;
     }
     else
     {
-      axis->scale = scaler->y_scale;
-      axis->delta = scaler->y_delta;
+      scale = scaler->y_scale;
+      delta = scaler->y_delta;
+    }
+
+    if ( axis->org_scale == scale && axis->org_delta == delta )
+      return;
+
+    axis->org_scale = scale;
+    axis->org_delta = delta;
+
+    axis->scale = scale;
+    axis->delta = delta;
+
+    /* scale the blue zones */
+    for ( nn = 0; nn < axis->blue_count; nn++ )
+    {
+      AF_CJKBlue    blue = &axis->blues[nn];
+      FT_Pos        dist;
+
+
+      blue->ref.cur   = FT_MulFix( blue->ref.org, scale ) + delta;
+      blue->ref.fit   = blue->ref.cur;
+      blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta;
+      blue->shoot.fit = blue->shoot.cur;
+      blue->flags    &= ~AF_CJK_BLUE_ACTIVE;
+
+      /* a blue zone is only active if it is less than 3/4 pixels tall */
+      dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale );
+      if ( dist <= 48 && dist >= -48 )
+      {
+        FT_Pos  delta1, delta2;
+
+        blue->ref.fit   = FT_PIX_ROUND( blue->ref.cur );
+
+        /* shoot is under shoot for cjk */
+        delta1 = FT_DivFix(blue->ref.fit, scale) - blue->shoot.org;
+        delta2 = delta1;
+        if ( delta1 < 0 )
+          delta2 = -delta2;
+
+        delta2 = FT_MulFix( delta2, scale );
+
+        FT_TRACE5(( "delta: %d", delta1 ));
+        if ( delta2 < 32 )
+          delta2 = 0;
+        /*
+        else if ( delta2 < 64 )
+          delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 );
+        */
+        else
+          delta2 = FT_PIX_ROUND( delta2 );
+        FT_TRACE5(( "/%d\n", delta2 ));
+
+        if ( delta1 < 0 )
+          delta2 = -delta2;
+
+        blue->shoot.fit = blue->ref.fit - delta2;
+
+        FT_TRACE5(( ">> active cjk blue zone %c%d[%ld/%ld]: "
+                     "ref: cur=%.2f fit=%.2f shoot: cur=%.2f fit=%.2f\n",
+                       ( dim == AF_DIMENSION_HORZ ) ? 'H':'V', 
+                       nn, blue->ref.org, blue->shoot.org,
+                       blue->ref.cur/64.0, blue->ref.fit/64.0,
+                       blue->shoot.cur/64.0, blue->shoot.fit/64.0 ));
+
+        blue->flags |= AF_CJK_BLUE_ACTIVE;
+      }
     }
   }
 
 
   FT_LOCAL_DEF( void )
-  af_cjk_metrics_scale( AF_LatinMetrics  metrics,
+  af_cjk_metrics_scale( AF_CJKMetrics    metrics,
                         AF_Scaler        scaler )
   {
     metrics->root.scaler = *scaler;
@@ -329,7 +843,7 @@
     AF_AxisHints  axis   = &hints->axis[dim];
     FT_Error      error  = AF_Err_Ok;
     FT_Memory     memory = hints->memory;
-    AF_LatinAxis  laxis  = &((AF_LatinMetrics)hints->metrics)->axis[dim];
+    AF_CJKAxis    laxis  = &((AF_CJKMetrics)hints->metrics)->axis[dim];
 
     AF_Segment    segments      = axis->segments;
     AF_Segment    segment_limit = segments + axis->num_segments;
@@ -601,9 +1115,94 @@
   }
 
 
+  FT_LOCAL_DEF( void )
+  af_cjk_hints_compute_blue_edges( AF_GlyphHints    hints,
+                                   AF_CJKMetrics    metrics,
+                                   AF_Dimension     dim )
+  {
+    AF_AxisHints  axis       = &hints->axis[ dim ];
+    AF_Edge       edge       = axis->edges;
+    AF_Edge       edge_limit = edge + axis->num_edges;
+    AF_CJKAxis    cjk        = &metrics->axis[ dim ];
+    FT_Fixed      scale      = cjk->scale;
+    FT_Pos        best_dist0;  /* initial threshold */
+
+
+    /* compute the initial threshold as a fraction of the EM size */
+    best_dist0 = FT_MulFix( metrics->units_per_em / 40, scale );
+
+    if ( best_dist0 > 64 / 2 ) /* Maximum 1/2 pixel */
+      best_dist0 = 64 / 2;
+
+    /* compute which blue zones are active, i.e. have their scaled */
+    /* size < 3/4 pixels                                           */
+
+    /* If the distant between an edge and a blue zone is shorter than
+     * best_dist0, set the blue zone for the edge. Then search for 
+     * the blue zone with the smallest best_dist to the edge. */ 
+    for ( ; edge < edge_limit; edge++ )
+    {
+      FT_UInt   bb;
+      AF_Width  best_blue = NULL;
+      FT_Pos    best_dist = best_dist0;
+
+
+      for ( bb = 0; bb < cjk->blue_count; bb++ )
+      {
+        AF_CJKBlue    blue = cjk->blues + bb;
+        FT_Bool       is_top_right_blue, is_major_dir;
+
+        /* skip inactive blue zones (i.e., those that are too small) */
+        if ( !( blue->flags & AF_CJK_BLUE_ACTIVE ) )
+          continue;
+
+        /* if it is a top zone, check for right edges -- if it is a bottom */
+        /* zone, check for left edges                                      */
+        /*                                                                 */
+        /* of course, that's for TrueType                                  */
+        is_top_right_blue  = FT_BOOL( 
+                ( ( blue->flags & AF_CJK_BLUE_IS_TOP ) != 0 ) ||
+                ( ( blue->flags & AF_CJK_BLUE_IS_RIGHT ) != 0 ) );
+        is_major_dir = FT_BOOL( edge->dir == axis->major_dir );
+
+        /* if it is a top zone, the edge must be against the major    */
+        /* direction; if it is a bottom zone, it must be in the major */
+        /* direction                                                  */
+        if ( is_top_right_blue ^ is_major_dir )
+        {
+          FT_Pos  dist;
+          AF_Width   compare;
+
+
+          /* Compare the edge to the closest blue zone type */
+          if ( FT_ABS( edge->fpos - blue->ref.org ) > 
+	      FT_ABS( edge->fpos - blue->shoot.org ) )
+            compare = &blue->shoot;
+          else
+            compare = &blue->ref;
+
+          dist = edge->fpos - compare->org;
+          if (dist < 0)
+            dist = -dist;
+
+          dist = FT_MulFix( dist, scale );
+          if ( dist < best_dist )
+          {
+            best_dist = dist;
+            best_blue = compare;
+          }
+        }
+      }
+
+      if ( best_blue )
+        edge->blue_edge = best_blue;
+    }
+  }
+
+
   FT_LOCAL_DEF( FT_Error )
   af_cjk_hints_init( AF_GlyphHints    hints,
-                     AF_LatinMetrics  metrics )
+                     AF_CJKMetrics    metrics )
   {
     FT_Render_Mode  mode;
     FT_UInt32       scaler_flags, other_flags;
@@ -728,8 +1327,8 @@
                              AF_Edge_Flags  base_flags,
                              AF_Edge_Flags  stem_flags )
   {
-    AF_LatinMetrics  metrics  = (AF_LatinMetrics) hints->metrics;
-    AF_LatinAxis     axis     = & metrics->axis[dim];
+    AF_CJKMetrics    metrics  = (AF_CJKMetrics) hints->metrics;
+    AF_CJKAxis       axis     = & metrics->axis[dim];
     FT_Pos           dist     = width;
     FT_Int           sign     = 0;
     FT_Int           vertical = ( dim == AF_DIMENSION_VERT );
@@ -1029,6 +1628,58 @@
     FT_Pos        last_stem_pos = 0;
 
 
+    /* we begin by aligning all stems relative to the blue zone */
+    FT_TRACE5(( "==== cjk hinting %s edges =====\n",
+          dim == AF_DIMENSION_HORZ ? "vertical" : "horizontal" ));
+
+    if ( AF_HINTS_DO_BLUES( hints ) )
+    {
+      for ( edge = edges; edge < edge_limit; edge++ )
+      {
+        AF_Width  blue;
+        AF_Edge   edge1, edge2;
+
+
+        if ( edge->flags & AF_EDGE_DONE )
+          continue;
+
+        blue  = edge->blue_edge;
+        edge1 = NULL;
+        edge2 = edge->link;
+
+        if ( blue )
+        {
+          edge1 = edge;
+        }
+        else if ( edge2 && edge2->blue_edge )
+        {
+          blue  = edge2->blue_edge;
+          edge1 = edge2;
+          edge2 = edge;
+        }
+
+        if ( !edge1 )
+          continue;
+
+        FT_TRACE5(( "CJKBLUE: edge %d @%d (opos=%.2f) snapped to (%.2f), "
+                 "was (%.2f)\n",
+                 edge1-edges, edge1->fpos, edge1->opos / 64.0, blue->fit / 64.0,
+                 edge1->pos / 64.0 ));
+
+        edge1->pos    = blue->fit;
+        edge1->flags |= AF_EDGE_DONE;
+
+        if ( edge2 && !edge2->blue_edge )
+        {
+          af_cjk_align_linked_edge( hints, dim, edge1, edge2 );
+          edge2->flags |= AF_EDGE_DONE;
+        }
+
+        if ( !anchor )
+          anchor = edge;
+      }
+    }
+
     /* now we align all stem edges. */
     for ( edge = edges; edge < edge_limit; edge++ )
     {
@@ -1063,6 +1714,15 @@
       }
 
       /* now align the stem */
+      /* this should not happen, but it's better to be safe */
+      if ( edge2->blue_edge )
+      {
+        FT_TRACE5(( "ASSERTION FAILED for edge %d\n", edge2-edges ));
+
+        af_cjk_align_linked_edge( hints, dim, edge2, edge );
+        edge->flags |= AF_EDGE_DONE;
+        continue;
+      }
 
       if ( edge2 < edge )
       {
@@ -1389,7 +2049,7 @@
   FT_LOCAL_DEF( FT_Error )
   af_cjk_hints_apply( AF_GlyphHints    hints,
                       FT_Outline*      outline,
-                      AF_LatinMetrics  metrics )
+                      AF_CJKMetrics    metrics )
   {
     FT_Error  error;
     int       dim;
@@ -1407,6 +2067,8 @@
       error = af_cjk_hints_detect_features( hints, AF_DIMENSION_HORZ );
       if ( error )
         goto Exit;
+
+      af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_HORZ );
     }
 
     if ( AF_HINTS_DO_VERTICAL( hints ) )
@@ -1414,6 +2076,8 @@
       error = af_cjk_hints_detect_features( hints, AF_DIMENSION_VERT );
       if ( error )
         goto Exit;
+
+      af_cjk_hints_compute_blue_edges( hints, metrics, AF_DIMENSION_VERT );
     }
 
     /* grid-fit the outline */
@@ -1512,7 +2176,7 @@
     AF_SCRIPT_CJK,
     af_cjk_uniranges,
 
-    sizeof( AF_LatinMetricsRec ),
+    sizeof( AF_CJKMetricsRec ),
 
     (AF_Script_InitMetricsFunc) af_cjk_metrics_init,
     (AF_Script_ScaleMetricsFunc)af_cjk_metrics_scale,
@@ -1534,7 +2198,7 @@
     AF_SCRIPT_CJK,
     af_cjk_uniranges,
 
-    sizeof( AF_LatinMetricsRec ),
+    sizeof( AF_CJKMetricsRec ),
 
     (AF_Script_InitMetricsFunc) NULL,
     (AF_Script_ScaleMetricsFunc)NULL,
diff --git a/src/autofit/afcjk.h b/src/autofit/afcjk.h
index 0b20d4a..20b7f80 100644
--- a/src/autofit/afcjk.h
+++ b/src/autofit/afcjk.h
@@ -20,6 +20,7 @@
 #define __AFCJK_H__
 
 #include "afhints.h"
+#include "aflatin.h"
 
 
 FT_BEGIN_HEADER
@@ -29,23 +30,92 @@ FT_BEGIN_HEADER
 
   AF_DECLARE_SCRIPT_CLASS(af_cjk_script_class)
 
+  /* CJK (global) metrics management */
+
+  /* 
+   * CJK glyphs tend to fill the square. So we have both verticle and
+   * horizontal blue zones. But some glyphs have flat bounding stroke that
+   * leave some space between neighbour glyphs.
+   */
+  enum
+  {
+    AF_CJK_BLUE_TOP,
+    AF_CJK_BLUE_BOTTOM,
+    AF_CJK_BLUE_LEFT,
+    AF_CJK_BLUE_RIGHT,
+
+    AF_CJK_BLUE_MAX
+  };
+
+
+#define AF_CJK_MAX_WIDTHS  16
+#define AF_CJK_MAX_BLUES   AF_CJK_BLUE_MAX
+
+
+  enum
+  {
+    AF_CJK_BLUE_ACTIVE     = 1 << 0,
+    AF_CJK_BLUE_IS_TOP     = 1 << 1,
+    AF_CJK_BLUE_IS_RIGHT   = 1 << 2,
+    AF_CJK_BLUE_ADJUSTMENT = 1 << 3,  /* used for scale adjustment */
+                                        /* optimization              */
+    AF_CJK_BLUE_FLAG_MAX
+  };
+
+  typedef struct  AF_CJKBlueRec_
+  {
+    AF_WidthRec  ref;
+    AF_WidthRec  shoot; /* undershoot */
+    FT_UInt      flags;
+
+  } AF_CJKBlueRec, *AF_CJKBlue;
+
+  typedef struct  AF_CJKAxisRec_
+  {
+    FT_Fixed         scale;
+    FT_Pos           delta;
+
+    FT_UInt          width_count;
+    AF_WidthRec      widths[AF_CJK_MAX_WIDTHS];
+    FT_Pos           edge_distance_threshold;
+    FT_Pos           standard_width;
+    FT_Bool          extra_light;
+
+    /* used for horizontal metrics too for CJK */
+    FT_Bool          control_overshoot;
+    FT_UInt          blue_count;
+    AF_CJKBlueRec    blues[AF_CJK_BLUE_MAX];
+
+    FT_Fixed         org_scale;
+    FT_Pos           org_delta;
+
+  } AF_CJKAxisRec, *AF_CJKAxis;
+
+
+  typedef struct  AF_CJKMetricsRec_
+  {
+    AF_ScriptMetricsRec  root;
+    FT_UInt              units_per_em;
+    AF_CJKAxisRec        axis[AF_DIMENSION_MAX];
+
+  } AF_CJKMetricsRec, *AF_CJKMetrics;
 
   FT_LOCAL( FT_Error )
-  af_cjk_metrics_init( AF_LatinMetrics  metrics,
+  af_cjk_metrics_init( AF_CJKMetrics    metrics,
                        FT_Face          face );
 
   FT_LOCAL( void )
-  af_cjk_metrics_scale( AF_LatinMetrics  metrics,
+  af_cjk_metrics_scale( AF_CJKMetrics    metrics,
                         AF_Scaler        scaler );
 
   FT_LOCAL( FT_Error )
   af_cjk_hints_init( AF_GlyphHints    hints,
-                     AF_LatinMetrics  metrics );
+                     AF_CJKMetrics    metrics );
 
   FT_LOCAL( FT_Error )
   af_cjk_hints_apply( AF_GlyphHints    hints,
                       FT_Outline*      outline,
-                      AF_LatinMetrics  metrics );
+                      AF_CJKMetrics    metrics );
 
 /* */
 
diff --git a/src/autofit/afindic.c b/src/autofit/afindic.c
index 057d851..7c2f4dd 100644
--- a/src/autofit/afindic.c
+++ b/src/autofit/afindic.c
@@ -33,16 +33,35 @@
 
 
   static FT_Error
-  af_indic_metrics_init( AF_LatinMetrics  metrics,
+  af_indic_metrics_init( AF_CJKMetrics    metrics,
                          FT_Face          face )
   {
-    /* use CJK routines */
-    return af_cjk_metrics_init( metrics, face );
+    /* skip blue zone init in CJK routines */
+    FT_CharMap  oldmap = face->charmap;
+
+
+    metrics->units_per_em = face->units_per_EM;
+
+    if ( FT_Select_Charmap( face, FT_ENCODING_UNICODE ) )
+      face->charmap = NULL;
+    else
+    {
+      af_cjk_metrics_init_widths( metrics, face, 0x7530 );
+#if 0
+      /* either need indic specific blue_chars[] or just skip blue zones. */
+      af_cjk_metrics_init_blues( metrics, face, af_cjk_blue_chars );
+#endif
+      af_cjk_metrics_check_digits( metrics, face );
+    }
+
+    FT_Set_Charmap( face, oldmap );
+
+    return AF_Err_Ok;
   }
 
 
   static void
-  af_indic_metrics_scale( AF_LatinMetrics  metrics,
+  af_indic_metrics_scale( AF_CJKMetrics    metrics,
                           AF_Scaler        scaler )
   {
     /* use CJK routines */
@@ -52,7 +71,7 @@
 
   static FT_Error
   af_indic_hints_init( AF_GlyphHints    hints,
-                       AF_LatinMetrics  metrics )
+                       AF_CJKMetrics    metrics )
   {
     /* use CJK routines */
     return af_cjk_hints_init( hints, metrics );
@@ -62,7 +81,7 @@
   static FT_Error
   af_indic_hints_apply( AF_GlyphHints    hints,
                         FT_Outline*      outline,
-                        AF_LatinMetrics  metrics)
+                        AF_CJKMetrics    metrics)
   {
     /* use CJK routines */
     return af_cjk_hints_apply( hints, outline, metrics );
@@ -98,7 +117,7 @@
     AF_SCRIPT_INDIC,
     af_indic_uniranges,
 
-    sizeof( AF_LatinMetricsRec ),
+    sizeof( AF_CJKMetricsRec ),
 
     (AF_Script_InitMetricsFunc) af_indic_metrics_init,
     (AF_Script_ScaleMetricsFunc)af_indic_metrics_scale,
@@ -120,7 +139,7 @@
     AF_SCRIPT_INDIC,
     af_indic_uniranges,
 
-    sizeof( AF_LatinMetricsRec ),
+    sizeof( AF_CJKMetricsRec ),
 
     (AF_Script_InitMetricsFunc) NULL,
     (AF_Script_ScaleMetricsFunc)NULL,
_______________________________________________
Freetype-devel mailing list
Freetype-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/freetype-devel

Reply via email to