shithub: freetype+ttf2subf

Download patch

ref: c1eb4459651daddef94027a50328daabc4d3c1c7
parent: 5c4a23a4e97f2deff9afc198f487d857eba7c299
author: Werner Lemberg <[email protected]>
date: Sun Aug 25 15:47:26 EDT 2013

[autofit] Make `cjk' module use blue stringsets.

* src/autofit/afcjk.c (AF_CJK_MAX_TEST_CHARACTERS): Removed.
(af_cjk_hani_blue_chars): Removed.
(AF_CJK_BLUE_TYPE_*): Removed.
(af_cjk_metrics_init_blues): Replace AF_CJK_MAX_TEST_CHARACTERS with
AF_BLUE_STRING_MAX_LEN.
Change loops to use offsets (in file `afblue.h') into the new arrays
`af_blue_stringsets' and `af_blue_strings' (in file `afblue.c').
Instead of three dimensions (as used in the old blue string array)
we now use properties to do the same, saving one loop nesting level.

* src/autofit/afcjk.h: Remove old enumeration values superseded by
the new data in `afblue.h'.
(AF_CJK_IS_TOP_BLUE, AF_CJK_IS_HORIZ_BLUE, AF_CJK_IS_FILLED_BLUE,
AF_CJK_IS_RIGHT_BLUE): New macros, to be used in
`af_cjk_metrics_init_blues'.
(AF_CJK_BLUE_IS_RIGHT): Remove this now redundant enum value.
(AF_CJK_BLUE_IS_TOP): Renamed to...
(AF_CJK_BLUE_TOP): This.
(AF_CJK_MAX_BLUES): Remove.
(AF_CJKAxisRec): Updated.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,30 @@
 2013-08-25  Werner Lemberg  <[email protected]>
 
+	[autofit] Make `cjk' module use blue stringsets.
+
+	* src/autofit/afcjk.c (AF_CJK_MAX_TEST_CHARACTERS): Removed.
+	(af_cjk_hani_blue_chars): Removed.
+	(AF_CJK_BLUE_TYPE_*): Removed.
+	(af_cjk_metrics_init_blues): Replace AF_CJK_MAX_TEST_CHARACTERS with
+	AF_BLUE_STRING_MAX_LEN.
+	Change loops to use offsets (in file `afblue.h') into the new arrays
+	`af_blue_stringsets' and `af_blue_strings' (in file `afblue.c'). 
+	Instead of three dimensions (as used in the old blue string array)
+	we now use properties to do the same, saving one loop nesting level.
+
+	* src/autofit/afcjk.h: Remove old enumeration values superseded by
+	the new data in `afblue.h'.
+	(AF_CJK_IS_TOP_BLUE, AF_CJK_IS_HORIZ_BLUE, AF_CJK_IS_FILLED_BLUE,
+	AF_CJK_IS_RIGHT_BLUE): New macros, to be used in
+	`af_cjk_metrics_init_blues'.
+	(AF_CJK_BLUE_IS_RIGHT): Remove this now redundant enum value.
+	(AF_CJK_BLUE_IS_TOP): Renamed to...
+	(AF_CJK_BLUE_TOP): This.
+	(AF_CJK_MAX_BLUES): Remove.
+	(AF_CJKAxisRec): Updated.
+
+2013-08-25  Werner Lemberg  <[email protected]>
+
 	[autofit] Typo.
 
 	* src/autofit/afblue.hin, src/autofit/afblue.c (GET_UTF8_CHAR): Use
--- a/src/autofit/afcjk.c
+++ b/src/autofit/afcjk.c
@@ -207,252 +207,172 @@
   }
 
 
-#define AF_CJK_MAX_TEST_CHARACTERS  32
+  /* Find all blue zones. */
 
-
-  /* Each blue zone has two types of fill and unfill, this is, */
-  /* filling the entire glyph square or not.                   */
-
-  enum
-  {
-    AF_CJK_BLUE_TYPE_FILL,
-    AF_CJK_BLUE_TYPE_UNFILL,
-    AF_CJK_BLUE_TYPE_MAX
-  };
-
-
-  /* Put some common and representative Han Ideographs characters here. */
-  static const FT_ULong af_cjk_hani_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 */
-      }
-    },
-#ifndef AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT
-      { {0x0000}, {0x0000} },
-      { {0x0000}, {0x0000} }
-#else
-    {
-      {
-        0x4E9B, 0x4EEC, 0x4F60, 0x4F86, 0x5011, 0x5230, 0x548C, 0x5730,
-        0x5979, 0x5C06, 0x5C07, 0x5C31, 0x5E74, 0x5F97, 0x60C5, 0x6700,
-        0x6837, 0x6A23, 0x7406, 0x80FD, 0x8AAA, 0x8BF4, 0x8FD9, 0x9019,
-        0x901A /* 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 */
-      }
-    }
-#endif /* AF_CONFIG_OPTION_CJK_BLUE_HANI_VERT */
-  };
-
-
-  /* Calculate blue zones for all the CJK_BLUE_XXX's. */
-
   static void
   af_cjk_metrics_init_blues( AF_CJKMetrics  metrics,
                              FT_Face        face )
   {
-    FT_Pos      fills[AF_CJK_MAX_TEST_CHARACTERS];
-    FT_Pos      flats[AF_CJK_MAX_TEST_CHARACTERS];
+    FT_Pos      fills[AF_BLUE_STRING_MAX_LEN];
+    FT_Pos      flats[AF_BLUE_STRING_MAX_LEN];
 
     FT_Int      num_fills;
     FT_Int      num_flats;
 
-    FT_Int      bb;
     AF_CJKBlue  blue;
     FT_Error    error;
     AF_CJKAxis  axis;
     FT_Outline  outline;
 
+    AF_Blue_Stringset         bss = metrics->root.script_class->blue_stringset;
+    const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
+
 #ifdef FT_DEBUG_LEVEL_TRACE
-    FT_String*  cjk_blue_name[AF_CJK_BLUE_MAX] = {
-      (FT_String*)"top",
-      (FT_String*)"bottom",
-      (FT_String*)"left",
-      (FT_String*)"right"
+    FT_String*  cjk_blue_name[4] =
+    {
+      (FT_String*)"bottom",    /* --   , --  */
+      (FT_String*)"top",       /* --   , TOP */
+      (FT_String*)"left",      /* HORIZ, --  */
+      (FT_String*)"right"      /* HORIZ, TOP */
     };
-    FT_String*  cjk_blue_type_name[AF_CJK_BLUE_TYPE_MAX] = {
-      (FT_String*)"filled",
-      (FT_String*)"unfilled"
+
+    FT_String*  cjk_blue_type_name[2] =
+    {
+      (FT_String*)"unfilled",  /* --   */
+      (FT_String*)"filled"     /* FILL */
     };
 #endif
 
 
-    /* We compute the blues simply by loading each character from the */
-    /* `blue_chars[blues]' string, then computing its extreme points  */
-    /* (depending blue zone type etc.).                               */
+    /* we walk over the blue character strings as specified in the    */
+    /* script's entry in the `af_blue_stringset' array, computing its */
+    /* extremum points (depending on the string properties)           */
 
     FT_TRACE5(( "cjk blue zones computation\n"
                 "==========================\n"
                 "\n" ));
 
-    for ( bb = 0; bb < AF_CJK_BLUE_MAX; bb++ )
+    for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
     {
-      FT_Int   fill_type;
-      FT_Pos*  blue_ref;
-      FT_Pos*  blue_shoot;
+      const char*  p = &af_blue_strings[bs->string];
+      FT_Pos*      blue_ref;
+      FT_Pos*      blue_shoot;
 
 
+      FT_TRACE5(( "blue zone %d:\n", axis->blue_count ));
+
       num_fills = 0;
       num_flats = 0;
 
-      for ( fill_type = 0; fill_type < AF_CJK_BLUE_TYPE_MAX; fill_type++ )
+      FT_TRACE5(( "  cjk blue %s/%s\n",
+                  cjk_blue_name[AF_CJK_IS_HORIZ_BLUE( bs ) |
+                                AF_CJK_IS_TOP_BLUE( bs )   ],
+                  cjk_blue_type_name[!!AF_CJK_IS_FILLED_BLUE( bs )] ));
+
+      while ( *p )
       {
-        const FT_ULong*  p     = af_cjk_hani_blue_chars[bb][fill_type];
-        const FT_ULong*  limit = p + AF_CJK_MAX_TEST_CHARACTERS;
-        FT_Bool          fill  = FT_BOOL(
-                                   fill_type == AF_CJK_BLUE_TYPE_FILL );
+        FT_ULong    ch;
+        FT_UInt     glyph_index;
+        FT_Pos      best_pos;       /* same as points.y or points.x, resp. */
+        FT_Int      best_point;
+        FT_Vector*  points;
 
 
-        FT_TRACE5(( "cjk blue %s/%s\n", cjk_blue_name[bb],
-                                        cjk_blue_type_name[fill_type] ));
+        GET_UTF8_CHAR( ch, p );
 
-        for ( ; p < limit && *p; p++ )
+        FT_TRACE5(( "    U+%lX... ", ch ));
+
+        /* load the character in the face -- skip unknown or empty ones */
+        glyph_index = FT_Get_Char_Index( face, ch );
+        if ( glyph_index == 0 )
         {
-          FT_UInt     glyph_index;
-          FT_Pos      best_pos; /* same as points.y */
-          FT_Int      best_point;
-          FT_Vector*  points;
+          FT_TRACE5(( "unavailable\n" ));
+          continue;
+        }
 
+        error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
+        outline = face->glyph->outline;
+        if ( error || outline.n_points <= 0 )
+        {
+          FT_TRACE5(( "no outline\n" ));
+          continue;
+        }
 
-          FT_TRACE5(( "  U+%lX... ", *p ));
+        /* now compute min or max point indices and coordinates */
+        points     = outline.points;
+        best_point = -1;
+        best_pos   = 0;  /* make compiler happy */
 
-          /* load the character in the face -- skip unknown or empty ones */
-          glyph_index = FT_Get_Char_Index( face, *p );
-          if ( glyph_index == 0 )
-          {
-            FT_TRACE5(( "unavailable\n" ));
-            continue;
-          }
+        {
+          FT_Int  nn;
+          FT_Int  first = 0;
+          FT_Int  last  = -1;
 
-          error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
-          outline = face->glyph->outline;
-          if ( error || outline.n_points <= 0 )
-          {
-            FT_TRACE5(( "no outline\n" ));
-            continue;
-          }
 
-          /* now compute min or max point indices and coordinates */
-          points     = outline.points;
-          best_point = -1;
-          best_pos   = 0;  /* make compiler happy */
-
+          for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
           {
-            FT_Int  nn;
-            FT_Int  first = 0;
-            FT_Int  last  = -1;
+            FT_Int  pp;
 
 
-            for ( nn = 0; nn < outline.n_contours; first = last + 1, nn++ )
-            {
-              FT_Int  pp;
+            last = 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;
 
-              last = 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 )
+            if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
+            {
+              if ( AF_CJK_IS_RIGHT_BLUE( bs ) )
               {
-              case AF_CJK_BLUE_TOP:
                 for ( pp = first; pp <= last; pp++ )
-                  if ( best_point < 0 || points[pp].y > best_pos )
+                  if ( best_point < 0 || points[pp].x > best_pos )
                   {
                     best_point = pp;
-                    best_pos   = points[pp].y;
+                    best_pos   = points[pp].x;
                   }
-                break;
-
-              case AF_CJK_BLUE_BOTTOM:
+              }
+              else
+              {
                 for ( pp = first; pp <= last; pp++ )
-                  if ( best_point < 0 || points[pp].y < best_pos )
+                  if ( best_point < 0 || points[pp].x < best_pos )
                   {
                     best_point = pp;
-                    best_pos   = points[pp].y;
+                    best_pos   = points[pp].x;
                   }
-                break;
-
-              case AF_CJK_BLUE_LEFT:
+              }
+            }
+            else
+            {
+              if ( AF_CJK_IS_TOP_BLUE( bs ) )
+              {
                 for ( pp = first; pp <= last; pp++ )
-                  if ( best_point < 0 || points[pp].x < best_pos )
+                  if ( best_point < 0 || points[pp].y > best_pos )
                   {
                     best_point = pp;
-                    best_pos   = points[pp].x;
+                    best_pos   = points[pp].y;
                   }
-                break;
-
-              case AF_CJK_BLUE_RIGHT:
+              }
+              else
+              {
                 for ( pp = first; pp <= last; pp++ )
-                  if ( best_point < 0 || points[pp].x > best_pos )
+                  if ( best_point < 0 || points[pp].y < best_pos )
                   {
                     best_point = pp;
-                    best_pos   = points[pp].x;
+                    best_pos   = points[pp].y;
                   }
-                break;
-
-              default:
-                ;
               }
             }
-
-            FT_TRACE5(( "best_pos = %5ld\n", best_pos ));
           }
 
-          if ( fill )
-            fills[num_fills++] = best_pos;
-          else
-            flats[num_flats++] = best_pos;
+          FT_TRACE5(( "best_pos = %5ld\n", best_pos ));
         }
+
+        if ( AF_CJK_IS_FILLED_BLUE( bs ) )
+          fills[num_fills++] = best_pos;
+        else
+          flats[num_flats++] = best_pos;
       }
 
       if ( num_flats == 0 && num_fills == 0 )
@@ -461,7 +381,7 @@
          *  we couldn't find a single glyph to compute this blue zone,
          *  we will simply ignore it then
          */
-        FT_TRACE5(( "  empty\n" ));
+        FT_TRACE5(( "    empty\n" ));
         continue;
       }
 
@@ -471,10 +391,10 @@
       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
+      if ( AF_CJK_IS_HORIZ_BLUE( bs ) )
         axis = &metrics->axis[AF_DIMENSION_HORZ];
+      else
+        axis = &metrics->axis[AF_DIMENSION_VERT];
 
       blue       = &axis->blues[axis->blue_count];
       blue_ref   = &blue->ref.org;
@@ -507,8 +427,8 @@
         FT_Bool  under_ref = FT_BOOL( shoot < ref );
 
 
-        if ( ( AF_CJK_BLUE_TOP == bb   ||
-               AF_CJK_BLUE_RIGHT == bb ) ^ under_ref )
+        /* AF_CJK_IS_TOP_BLUE covers `right' and `top' */
+        if ( AF_CJK_IS_TOP_BLUE( bs ) ^ under_ref )
         {
           *blue_ref   =
           *blue_shoot = ( shoot + ref ) / 2;
@@ -519,15 +439,12 @@
       }
 
       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;
+      if ( AF_CJK_IS_TOP_BLUE( bs ) )
+        blue->flags |= AF_CJK_BLUE_TOP;
 
-      FT_TRACE5(( "  cjk %s bluezone\n"
-                  "    -> reference = %ld\n"
+      FT_TRACE5(( "    -> reference = %ld\n"
                   "       overshoot = %ld\n",
-                  cjk_blue_name[bb], *blue_ref, *blue_shoot ));
+                  *blue_ref, *blue_shoot ));
     }
 
     FT_TRACE5(( "\n" ));
@@ -1269,10 +1186,8 @@
         /* 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 );
+        is_top_right_blue = FT_BOOL( blue->flags & AF_CJK_BLUE_TOP );
+        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 */
--- a/src/autofit/afcjk.h
+++ b/src/autofit/afcjk.h
@@ -36,35 +36,38 @@
   AF_DECLARE_SCRIPT_CLASS( af_hani_script_class )
 
 
-  /* CJK (global) metrics management */
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****              C J K   G L O B A L   M E T R I C S              *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
 
+
   /*
    *  CJK glyphs tend to fill the square.  So we have both vertical and
    *  horizontal blue zones.  But some glyphs have flat bounding strokes 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_IS_TOP_BLUE( b ) \
+          ( (b)->properties & AF_BLUE_PROPERTY_CJK_TOP )
+#define AF_CJK_IS_HORIZ_BLUE( b ) \
+          ( (b)->properties & AF_BLUE_PROPERTY_CJK_HORIZ )
+#define AF_CJK_IS_FILLED_BLUE( b ) \
+          ( (b)->properties & AF_BLUE_PROPERTY_CJK_FILL )
+#define AF_CJK_IS_RIGHT_BLUE  AF_CJK_IS_TOP_BLUE
 
-
 #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_ACTIVE     = 1 << 0,  /* set if zone height is <= 3/4px */
+    AF_CJK_BLUE_TOP        = 1 << 1,  /* result of AF_CJK_IS_TOP_BLUE   */
+    AF_CJK_BLUE_ADJUSTMENT = 1 << 2,  /* used for scale adjustment      */
+                                      /* optimization                   */
     AF_CJK_BLUE_FLAG_MAX
   };
 
@@ -83,16 +86,16 @@
     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;
+    FT_UInt        width_count;                   /* number of used widths */
+    AF_WidthRec    widths[AF_CJK_MAX_WIDTHS];     /* widths array          */
+    FT_Pos         edge_distance_threshold;     /* used for creating edges */
+    FT_Pos         standard_width;           /* the default stem thickness */
+    FT_Bool        extra_light;           /* is standard width very light? */
 
     /* used for horizontal metrics too for CJK */
     FT_Bool        control_overshoot;
     FT_UInt        blue_count;
-    AF_CJKBlueRec  blues[AF_CJK_BLUE_MAX];
+    AF_CJKBlueRec  blues[AF_BLUE_STRINGSET_MAX];
 
     FT_Fixed       org_scale;
     FT_Pos         org_delta;
--- a/src/autofit/aflatin.h
+++ b/src/autofit/aflatin.h
@@ -61,8 +61,6 @@
    */
 
 
-  /* Latin (global) metrics management */
-
 #define AF_LATIN_IS_TOP_BLUE( b ) \
           ( (b)->properties & AF_BLUE_PROPERTY_LATIN_TOP )
 #define AF_LATIN_IS_SMALL_TOP_BLUE( b ) \