(parse_otf_command): Fix previous change.
[m17n/m17n-lib.git] / src / m17n-flt.c
index eabc0be..b6fcad6 100644 (file)
@@ -1,5 +1,5 @@
 /* m17n-flt.c -- Font Layout Table sub-module.
 /* m17n-flt.c -- Font Layout Table sub-module.
-   Copyright (C) 2003, 2004, 2007
+   Copyright (C) 2003, 2004, 2007, 2008, 2009, 2010
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
     @brief FLT support for a window system.
 
     This section defines the m17n FLT API concerning character
     @brief FLT support for a window system.
 
     This section defines the m17n FLT API concerning character
-    layouting facility using FLT (Font Layout Table).  */
+    layouting facility using FLT (Font Layout Table).  The format of
+    FLT is described in @ref mdbFLT.  */
+
+/***ja
+    @addtogroup m17nFLT
+    @brief ¥¦¥£¥ó¥É¥¦¥·¥¹¥Æ¥à¤Î¤¿¤á¤Î FLT ¥µ¥Ý¡¼¥È.
+
+    ¤³¤Î¥»¥¯¥·¥ç¥ó¤Ç¤Ï¡¢FLT (Font Layout Table)
+    ¤òÍѤ¤¤¿Ê¸»ú¥ì¥¤¥¢¥¦¥Èµ¡Ç½¤Ë´Ø¤¹¤ë m17n FLT API ¤òÄêµÁ¤¹¤ë¡£
+    FLT ¤Î·Á¼°¤Ï @ref mdbFLT ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¡£  */
 
 /*=*/
 
 
 /*=*/
 
@@ -113,9 +122,9 @@ PREDEFIND-COMMAND ::=
 ;; consume nothing.
 
 OTF-COMMAND ::=
 ;; consume nothing.
 
 OTF-COMMAND ::=
-       'otf:''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
+       ':otf=''SCRIPT'[':'['LANGSYS'][':'[GSUB-FEATURES][':'GPOS-FEATURES]]]
 ;; Run the Open Type Layout Table on the current run.  Succeed always,
 ;; Run the Open Type Layout Table on the current run.  Succeed always,
-;; consume nothing.
+;; consume all glyphs in the current range.
 
 SCRIPT ::= OTF-TAG
 ;;     OTF's ScriptTag name (four letters) listed at:
 
 SCRIPT ::= OTF-TAG
 ;;     OTF's ScriptTag name (four letters) listed at:
@@ -235,7 +244,7 @@ MACRO-NAME ::= SYMBOL
 
 */
 
 
 */
 
-static int mdebug_flag = MDEBUG_FONT_FLT;
+static int mdebug_flag = MDEBUG_FLT;
 
 MSymbol Mfont, Mlayouter, Mcombining;
 
 
 MSymbol Mfont, Mlayouter, Mcombining;
 
@@ -246,18 +255,26 @@ static int flt_min_coverage, flt_max_coverage;
 
 enum GlyphInfoMask
 {
 
 enum GlyphInfoMask
 {
-  CombiningCodeMask = 0xFFFFFFF,
-  LeftPaddingMask = 1 << 28,
-  RightPaddingMask = 1 << 29
+  CategoryCodeMask = 0x7F,
+  CombiningCodeMask = 0xFFFFFF,
+  CombinedMask = 1 << 28,
+  LeftPaddingMask = 1 << 29,
+  RightPaddingMask = 1 << 30
 };
 
 #define SET_GLYPH_INFO(g, mask, ctx, info)                     \
   ((g)->internal = (((g)->internal & ~(mask)) | (info)),       \
    (ctx)->check_mask |= (mask))
 
 };
 
 #define SET_GLYPH_INFO(g, mask, ctx, info)                     \
   ((g)->internal = (((g)->internal & ~(mask)) | (info)),       \
    (ctx)->check_mask |= (mask))
 
+#define GET_CATEGORY_CODE(g) ((g)->internal & CategoryCodeMask)
+#define SET_CATEGORY_CODE(g, code)                                      \
+  ((g)->internal = (((g)->internal & ~(CombiningCodeMask | CombinedMask)) \
+                   | (code)))
+#define GET_COMBINED(g) ((g)->internal & CombinedMask)
 #define GET_COMBINING_CODE(g) ((g)->internal & CombiningCodeMask)
 #define GET_COMBINING_CODE(g) ((g)->internal & CombiningCodeMask)
-#define SET_COMBINING_CODE(g, ctx, code)       \
-  SET_GLYPH_INFO (g, CombiningCodeMask, ctx, code)
+#define SET_COMBINING_CODE(g, ctx, code)                       \
+  SET_GLYPH_INFO (g, CombiningCodeMask | CombinedMask, ctx,    \
+                 (code) | CombinedMask)
 #define GET_LEFT_PADDING(g) ((g)->internal & LeftPaddingMask)
 #define SET_LEFT_PADDING(g, ctx, flag) \
   SET_GLYPH_INFO (g, LeftPaddingMask, ctx, flag)
 #define GET_LEFT_PADDING(g) ((g)->internal & LeftPaddingMask)
 #define SET_LEFT_PADDING(g, ctx, flag) \
   SET_GLYPH_INFO (g, LeftPaddingMask, ctx, flag)
@@ -358,7 +375,7 @@ GREPLACE (MFLTGlyphString *src, int src_from, int src_to,
 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
 
 #define CMD_ID_TO_INDEX(id) (CMD_ID_OFFSET_INDEX - (id))
 #define INDEX_TO_CMD_ID(idx) (CMD_ID_OFFSET_INDEX - (idx))
 
-static MSymbol Mcond, Mrange, Mfont_facility;
+static MSymbol Mcond, Mrange, Mfont_facility, Mequal;
 
 #define GLYPH_CODE_P(code)     \
   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
 
 #define GLYPH_CODE_P(code)     \
   ((code) >= GLYPH_CODE_MIN && (code) <= GLYPH_CODE_MAX)
@@ -367,13 +384,10 @@ static MSymbol Mcond, Mrange, Mfont_facility;
 
 #define UPDATE_CLUSTER_RANGE(ctx, g)           \
   do {                                         \
 
 #define UPDATE_CLUSTER_RANGE(ctx, g)           \
   do {                                         \
-    if ((ctx)->cluster_begin_idx >= 0)         \
-      {                                                \
-       if (ctx->cluster_begin_pos > (g)->from) \
-         ctx->cluster_begin_pos = (g)->from;   \
-       if (ctx->cluster_end_pos < (g)->to)     \
-         ctx->cluster_end_pos = (g)->to;       \
-      }                                                \
+    if (ctx->cluster_begin_pos > (g)->from)    \
+      ctx->cluster_begin_pos = (g)->from;      \
+    if (ctx->cluster_end_pos < (g)->to)                \
+      ctx->cluster_end_pos = (g)->to;          \
   } while (0)
 
 enum FontLayoutCmdRuleSrcType
   } while (0)
 
 enum FontLayoutCmdRuleSrcType
@@ -402,8 +416,11 @@ typedef struct
     struct {
       int from, to;
     } range;
     struct {
       int from, to;
     } range;
-    int supported_glyph;
-    MFLTOtfSpec otf_spec;
+    struct {
+      int len;
+      MPlist *codes;
+      MFLTOtfSpec otf_spec;
+    } facility;
   } src;
 
   int n_cmds;
   } src;
 
   int n_cmds;
@@ -426,6 +443,7 @@ enum FontLayoutCmdType
     FontLayoutCmdTypeRule,
     FontLayoutCmdTypeCond,
     FontLayoutCmdTypeOTF,
     FontLayoutCmdTypeRule,
     FontLayoutCmdTypeCond,
     FontLayoutCmdTypeOTF,
+    FontLayoutCmdTypeOTFCategory,
     FontLayoutCmdTypeMAX
   };
 
     FontLayoutCmdTypeMAX
   };
 
@@ -439,9 +457,25 @@ typedef struct
   } body;
 } FontLayoutCmd;
 
   } body;
 } FontLayoutCmd;
 
+typedef struct
+{
+  int size;
+  unsigned int *tag;
+  char *code;
+} FeatureCodeTable;
+
+typedef struct
+{
+  MCharTable *table;
+  FeatureCodeTable feature_table;
+  /* Non-null if the table must be re-configured by OTF specs included
+     in the definition.  */
+  MPlist *definition;
+} FontLayoutCategory;
+
 typedef struct 
 {
 typedef struct 
 {
-  MCharTable *category;
+  FontLayoutCategory *category;
   int size, inc, used;
   FontLayoutCmd *cmds;
 } FontLayoutStage;
   int size, inc, used;
   FontLayoutCmd *cmds;
 } FontLayoutStage;
@@ -453,39 +487,83 @@ struct _MFLT
   MSymbol registry;
   MFLTOtfSpec otf;
   MDatabase *mdb;
   MSymbol registry;
   MFLTOtfSpec otf;
   MDatabase *mdb;
-  MCharTable *coverage;
+  FontLayoutCategory *coverage;
   MPlist *stages;
   MPlist *stages;
+  int need_config;
+  /* Font for which coverage or some of categories are configured.  */
+  MSymbol font_id;
 };
 
 /* Font layout table loader */
 
 static int parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec);
 
 };
 
 /* Font layout table loader */
 
 static int parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec);
 
+static void
+apply_otf_feature (MFLTFont *font, MFLTOtfSpec *spec,
+                  int from, int to, MCharTable *table, int category)
+{
+  unsigned char *buf;
+  int i;
+
+  if (! mflt_iterate_otf_feature)
+    return;
+  buf = alloca (to + 1 - from);
+  memset (buf, 0, to + 1 - from);
+  if (mflt_iterate_otf_feature (font, spec, from, to, buf) < 0)
+    return;
+  for (i = to - from; i >= 0; i--)
+    if (buf[i])
+      mchartable_set (table, from + i, (void *) category);
+}
+
+static unsigned int gen_otf_tag (char *p, int shift);
+
 /* Load a category table from PLIST.  PLIST has this form:
       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
 */
 
 /* Load a category table from PLIST.  PLIST has this form:
       PLIST ::= ( FROM-CODE TO-CODE ? CATEGORY-CHAR ) *
 */
 
-static MCharTable *
-load_category_table (MPlist *plist)
+static FontLayoutCategory *
+load_category_table (MPlist *plist, MFLTFont *font)
 {
 {
+  FontLayoutCategory *category;
   MCharTable *table;
   MCharTable *table;
+  MPlist *feature_table_head = NULL;
+  int feature_table_size = 0;
+  MPlist *p;
+  int need_otf = 0;
 
   table = mchartable (Minteger, (void *) 0);
 
   table = mchartable (Minteger, (void *) 0);
-
-  MPLIST_DO (plist, plist)
+  MPLIST_DO (p, plist)
     {
       MPlist *elt;
       int from, to, category_code;
 
     {
       MPlist *elt;
       int from, to, category_code;
 
-      if (! MPLIST_PLIST (plist))
-       MERROR (MERROR_FONT, NULL);
-      elt = MPLIST_PLIST (plist);
+      if (! MPLIST_PLIST_P (p))
+       MERROR_GOTO (MERROR_FLT, end);
+      elt = MPLIST_PLIST (p);
+      if (MPLIST_SYMBOL_P (elt))
+       {
+         MPlist *next;
+
+         if (! mflt_enable_new_feature)
+           {
+             M17N_OBJECT_UNREF (table);
+             return NULL;
+           }
+         next = MPLIST_NEXT (elt);
+         if (! MPLIST_INTEGER_P (next))
+           MERROR_GOTO (MERROR_FLT, end);
+         if (! feature_table_head)
+           feature_table_head = p;
+         feature_table_size++;
+         continue;
+       }
       if (! MPLIST_INTEGER_P (elt))
       if (! MPLIST_INTEGER_P (elt))
-       MERROR (MERROR_FONT, NULL);
+       MERROR_GOTO (MERROR_FLT, end);
       from = MPLIST_INTEGER (elt);
       elt = MPLIST_NEXT (elt);
       if (! MPLIST_INTEGER_P (elt))
       from = MPLIST_INTEGER (elt);
       elt = MPLIST_NEXT (elt);
       if (! MPLIST_INTEGER_P (elt))
-       MERROR (MERROR_FONT, NULL);
+       MERROR_GOTO (MERROR_FLT, end);
       to = MPLIST_INTEGER (elt);
       elt = MPLIST_NEXT (elt);
       if (MPLIST_TAIL_P (elt))
       to = MPLIST_INTEGER (elt);
       elt = MPLIST_NEXT (elt);
       if (MPLIST_TAIL_P (elt))
@@ -493,14 +571,38 @@ load_category_table (MPlist *plist)
          category_code = to;
          to = from;
        }
          category_code = to;
          to = from;
        }
+      else if (MPLIST_SYMBOL_P (elt))
+       {
+         if (! mflt_enable_new_feature)
+           {
+             M17N_OBJECT_UNREF (table);
+             return NULL;
+           }
+         if (font)
+           {
+             MFLTOtfSpec spec;
+             if (parse_otf_command (MPLIST_SYMBOL (elt), &spec) < 0)
+               MERROR_GOTO (MERROR_FLT, end);
+             elt = MPLIST_NEXT (elt);
+             if (! MPLIST_INTEGER_P (elt))
+               MERROR_GOTO (MERROR_FLT, end);
+             category_code = MPLIST_INTEGER (elt);
+             if (! isalnum (category_code))
+               MERROR_GOTO (MERROR_FLT, end);
+             apply_otf_feature (font, &spec, from, to, table, category_code);
+           }
+         else
+           need_otf = 1;
+         continue;
+       }
       else
        {
          if (! MPLIST_INTEGER_P (elt))
       else
        {
          if (! MPLIST_INTEGER_P (elt))
-           MERROR (MERROR_FONT, NULL);
+           MERROR_GOTO (MERROR_FLT, end);
          category_code = MPLIST_INTEGER (elt);
        }
       if (! isalnum (category_code))
          category_code = MPLIST_INTEGER (elt);
        }
       if (! isalnum (category_code))
-       MERROR (MERROR_FONT, NULL);
+       MERROR_GOTO (MERROR_FLT, end);
 
       if (from == to)
        mchartable_set (table, from, (void *) category_code);
 
       if (from == to)
        mchartable_set (table, from, (void *) category_code);
@@ -508,19 +610,75 @@ load_category_table (MPlist *plist)
        mchartable_set_range (table, from, to, (void *) category_code);
     }
 
        mchartable_set_range (table, from, to, (void *) category_code);
     }
 
-  return table;
+ end:
+  category = calloc (1, sizeof (FontLayoutCategory));
+  category->table = table;
+  if (need_otf)
+    {
+      category->definition = plist;
+      M17N_OBJECT_REF (plist);
+    }
+  else
+    category->definition = NULL;
+  if (feature_table_head)
+    {
+      int i = 0;
+      category->feature_table.size = feature_table_size;
+      category->feature_table.tag = malloc (sizeof (unsigned int)
+                                           * feature_table_size);
+      category->feature_table.code = malloc (feature_table_size);
+
+      MPLIST_DO (p, feature_table_head)
+       {
+         MPlist *elt;
+         MSymbol feature;
+         if (! MPLIST_PLIST_P (p))
+           continue;
+         elt = MPLIST_PLIST (p);
+         if (! MPLIST_SYMBOL_P (elt))
+           continue;
+         feature = MPLIST_SYMBOL (elt);
+         elt = MPLIST_NEXT (elt);
+         if (! MPLIST_INTEGER_P (elt))
+           continue;
+         category->feature_table.tag[i]
+           = gen_otf_tag (MSYMBOL_NAME (feature), 7);
+         category->feature_table.code[i] = MPLIST_INTEGER (elt);
+         i++;
+       }
+    }
+  return category;
+}
+
+#define ref_category_table(CATEGORY) M17N_OBJECT_REF ((CATEGORY)->table)
+
+static void
+unref_category_table (FontLayoutCategory *category)
+{
+  M17N_OBJECT_UNREF (category->table);
+  if (! category->table)
+    {
+      if (category->definition)
+       M17N_OBJECT_UNREF (category->definition);
+      if (category->feature_table.size > 0)
+       {
+         free (category->feature_table.tag);
+         free (category->feature_table.code);
+       }
+      free (category);
+    }
 }
 
 static unsigned int
 }
 
 static unsigned int
-gen_otf_tag (char *p)
+gen_otf_tag (char *p, int shift)
 {
   unsigned int tag = 0;
   int i;
 
   for (i = 0; i < 4 && *p; i++, p++)
 {
   unsigned int tag = 0;
   int i;
 
   for (i = 0; i < 4 && *p; i++, p++)
-    tag = (tag << 8) | *p;
+    tag = (tag << shift) | *p;
   for (; i < 4; i++)
   for (; i < 4; i++)
-    tag = (tag << 8) | 0x20;
+    tag = (tag << shift) | 0x20;
   return tag;
 }
 
   return tag;
 }
 
@@ -576,86 +734,99 @@ otf_store_features (char *p, char *end, unsigned *buf)
        {
          if (negative++ == 0)
            buf[i++] = 0xFFFFFFFF;
        {
          if (negative++ == 0)
            buf[i++] = 0xFFFFFFFF;
-         buf[i++] = gen_otf_tag (p + 1), p += 6;
+         buf[i++] = gen_otf_tag (p + 1, 8), p += 6;
        }
       else
        }
       else
-       buf[i++] = gen_otf_tag (p), p += 5;
+       buf[i++] = gen_otf_tag (p, 8), p += 5;
     }
   buf[i] = 0;
 }
 
     }
   buf[i] = 0;
 }
 
+/* SYMBOL's name       features[0]    [1]      for checking    for applying
+   -------------       ------------------      ------------    ------------
+   SCRIPT              [-1,0]      [-1,0]          any|any        all  all
+   SCRIPT=             NULL        [-1,0]         none&1         none  all
+   SCRIPT+             [-1,0]        NULL            1&none       all  none
+   SCRIPT=F1           [F1,0]      [-1,0]           F1&1           F1  all
+   SCRIPT+F1           [-1][0]     [F1,0]         none&F1        none  F1
+   SCRIPT=F1+          [F1,0]        NULL           F1&none        F1  none
+   SCRIPT=~F2          [-1,F2,0]   [-1,0]          ~F2&1       all~F2  all
+   SCRIPT=F1,~F2       [F1,-1,A2,0][-1,0]       F1&~F2&1       F1 (*1) all
+
+   (*1) Invalid specification
+ */
+
 static int
 parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec)
 {
   char *str = MSYMBOL_NAME (symbol);
   char *end = str + MSYMBOL_NAMELEN (symbol);
   unsigned int script, langsys;
 static int
 parse_otf_command (MSymbol symbol, MFLTOtfSpec *spec)
 {
   char *str = MSYMBOL_NAME (symbol);
   char *end = str + MSYMBOL_NAMELEN (symbol);
   unsigned int script, langsys;
-  char *gsub, *gpos;
-  int gsub_count = 0, gpos_count = 0;
+  char *features[3];
+  int feature_count[2];                /* [0]:GSUB, [1]:GPOS */
+  int i;
   char *p;
 
   memset (spec, 0, sizeof (MFLTOtfSpec));
 
   spec->sym = symbol;
   char *p;
 
   memset (spec, 0, sizeof (MFLTOtfSpec));
 
   spec->sym = symbol;
-  str += 5;                    /* skip the heading ":otf=" */
-  script = gen_otf_tag (str);
+  str += 5;                    /* skip the heading ":otf=" or ":otf?" */
+  if (str[-1] == '?')
+    {
+      if (! mflt_enable_new_feature)
+       /* The client can't use this command.  */
+       return -1;
+      if (! *str)
+       /* This is a spec to reset category codes.  */
+       return 0;
+    }
+  spec->script = gen_otf_tag (str, 8);
   str += 4;
   if (*str == '/')
     {
   str += 4;
   if (*str == '/')
     {
-      langsys = gen_otf_tag (str);
+      spec->langsys = gen_otf_tag (str, 8);
       str += 4;
     }
   else
       str += 4;
     }
   else
-    langsys = 0;
-  gsub = str;
+    spec->langsys = 0;
+  features[0] = str;
   if (*str != '=')
     /* Apply all GSUB features.  */
   if (*str != '=')
     /* Apply all GSUB features.  */
-      gsub_count = 1;
+      feature_count[0] = -1;
   else
     {
       p = str + 1;
   else
     {
       p = str + 1;
-      str = otf_count_features (p, end, '+', &gsub_count);
+      str = otf_count_features (p, end, '+', feature_count);
       if (! str)
        MERROR (MERROR_FLT, -1);
     }
       if (! str)
        MERROR (MERROR_FLT, -1);
     }
-  gpos = str;
+  features[1] = str;
   if (*str != '+')
     /* Apply all GPOS features.  */
   if (*str != '+')
     /* Apply all GPOS features.  */
-    gpos_count = 1;
+    feature_count[1] = -1;
   else
     {
       p = str + 1;
   else
     {
       p = str + 1;
-      str = otf_count_features (p, end, '\0', &gpos_count);
+      str = otf_count_features (p, end, '\0', feature_count + 1);
       if (! str)
        MERROR (MERROR_FLT, -1);
     }
       if (! str)
        MERROR (MERROR_FLT, -1);
     }
-
-  spec->script = script;
-  spec->langsys = langsys;
-  if (gsub_count > 0)
-    {
-      spec->features[0] = malloc (sizeof (int) * (gsub_count + 1));
-      if (! spec->features[0])
-       return -2;
-      if (*gsub == '=')
-       otf_store_features (gsub + 1, gpos, spec->features[0]);
-      else
-       spec->features[0][0] = 0xFFFFFFFF, spec->features[0][1] = 0;
-    }
-  if (gpos_count > 0)
-    {
-      spec->features[1] = malloc (sizeof (int) * (gpos_count + 1));
-      if (! spec->features[1])
-       {
-         if (spec->features[0])
-           free (spec->features[0]);
+  features[2] = str;
+  for (i = 0; i < 2; i++)
+    if (feature_count[i])
+      {
+       spec->features[i] = malloc (sizeof (int)
+                                   * (feature_count[i] < 0 ? 2
+                                      : feature_count[i] + 1));
+       if (! spec->features[i])
          return -2;
          return -2;
-       }
-      if (*gpos == '+')
-       otf_store_features (gpos + 1, str, spec->features[1]);
-      else
-       spec->features[1][0] = 0xFFFFFFFF, spec->features[1][1] = 0;
-    }
+       if (feature_count[i] > 0)
+         otf_store_features (features[i] + 1, features[i + 1],
+                             spec->features[i]);
+       else
+         spec->features[i][0] = 0xFFFFFFFF, spec->features[i][1] = 0;
+      }
+
   return 0;
 }
 
   return 0;
 }
 
@@ -685,7 +856,8 @@ load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
   result = parse_otf_command (sym, &cmd->body.otf);
   if (result == -2)
     return result;
   result = parse_otf_command (sym, &cmd->body.otf);
   if (result == -2)
     return result;
-  cmd->type = FontLayoutCmdTypeOTF;
+  cmd->type = (name[4] == '?' ? FontLayoutCmdTypeOTFCategory
+              : FontLayoutCmdTypeOTF);
   return 0;
 }
 
   return 0;
 }
 
@@ -811,6 +983,7 @@ load_command (FontLayoutStage *stage, MPlist *plist,
       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
                   | ( ( INTEGER INTEGER ) ... )
                   | ( ( range INTEGER INTEGER ) ... )
       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
                   | ( ( INTEGER INTEGER ) ... )
                   | ( ( range INTEGER INTEGER ) ... )
+                  | ( ( SYMBOL STRING ) ... )
                   | ( ( font-facilty [ INTEGER ] ) ... )
                   | ( ( font-facilty OTF-SPEC ) ... )  */
       MPlist *elt = MPLIST_PLIST (plist);
                   | ( ( font-facilty [ INTEGER ] ) ... )
                   | ( ( font-facilty OTF-SPEC ) ... )  */
       MPlist *elt = MPLIST_PLIST (plist);
@@ -916,7 +1089,7 @@ load_command (FontLayoutStage *stage, MPlist *plist,
            }
          else if (MPLIST_PLIST_P (elt))
            {
            }
          else if (MPLIST_PLIST_P (elt))
            {
-             MPlist *pl = MPLIST_PLIST (elt);
+             MPlist *pl = MPLIST_PLIST (elt), *p;
              int size = MPLIST_LENGTH (pl);
 
              if (MPLIST_INTEGER_P (pl))
              int size = MPLIST_LENGTH (pl);
 
              if (MPLIST_INTEGER_P (pl))
@@ -935,47 +1108,58 @@ load_command (FontLayoutStage *stage, MPlist *plist,
                        = (unsigned) MPLIST_INTEGER (pl);
                    }
                }
                        = (unsigned) MPLIST_INTEGER (pl);
                    }
                }
-             else if (MPLIST_SYMBOL_P (pl) && size == 3)
+             else if (MPLIST_SYMBOL_P (pl))
                {
                {
-                 if (MPLIST_SYMBOL (pl) != Mrange)
-                   MERROR (MERROR_FLT, INVALID_CMD_ID);
-                 cmd->body.rule.src_type = SRC_RANGE;
-                 pl = MPLIST_NEXT (pl);
-                 if (! MPLIST_INTEGER_P (pl))
-                   MERROR (MERROR_DRAW, INVALID_CMD_ID);
-                 cmd->body.rule.src.range.from
-                   = (unsigned) MPLIST_INTEGER (pl);
-                 pl = MPLIST_NEXT (pl);
-                 if (! MPLIST_INTEGER_P (pl))
-                   MERROR (MERROR_DRAW, INVALID_CMD_ID);
-                 cmd->body.rule.src.range.to
-                   = (unsigned) MPLIST_INTEGER (pl);
-               }
-             else if (MPLIST_SYMBOL_P (pl) && size <= 2)
-               {
-                 if (MPLIST_SYMBOL (pl) != Mfont_facility)
-                   MERROR (MERROR_FLT, INVALID_CMD_ID);
-                 pl = MPLIST_NEXT (pl);
-                 if (MPLIST_SYMBOL_P (pl))
+                 if (MPLIST_SYMBOL (pl) == Mrange)
                    {
                    {
-                     MSymbol sym = MPLIST_SYMBOL (pl);
-                     char *otf_spec = MSYMBOL_NAME (sym);
-
-                     if (otf_spec[0] == ':' && otf_spec[1] == 'o'
-                         && otf_spec[2] == 't' && otf_spec[3] == 'f')
-                       parse_otf_command (sym, &cmd->body.rule.src.otf_spec);
-                     else
+                     if (size != 3)
                        MERROR (MERROR_FLT, INVALID_CMD_ID);
                        MERROR (MERROR_FLT, INVALID_CMD_ID);
-                     cmd->body.rule.src_type = SRC_OTF_SPEC;
+                     cmd->body.rule.src_type = SRC_RANGE;
+                     pl = MPLIST_NEXT (pl);
+                     if (! MPLIST_INTEGER_P (pl))
+                       MERROR (MERROR_DRAW, INVALID_CMD_ID);
+                     cmd->body.rule.src.range.from
+                       = (unsigned) MPLIST_INTEGER (pl);
+                     pl = MPLIST_NEXT (pl);
+                     if (! MPLIST_INTEGER_P (pl))
+                       MERROR (MERROR_DRAW, INVALID_CMD_ID);
+                     cmd->body.rule.src.range.to
+                       = (unsigned) MPLIST_INTEGER (pl);
                    }
                    }
-                 else
+                 else if (MPLIST_SYMBOL (pl) == Mfont_facility)
                    {
                    {
-                     cmd->body.rule.src_type = SRC_HAS_GLYPH;
-                     if (MPLIST_INTEGER_P (pl))
-                       cmd->body.rule.src.supported_glyph
-                         = MPLIST_INTEGER (pl);
+                     FontLayoutCmdRule *rule = &cmd->body.rule;
+
+                     pl = MPLIST_NEXT (pl);
+                     if (MPLIST_SYMBOL_P (pl))
+                       {
+                         MSymbol sym = MPLIST_SYMBOL (pl);
+                         char *otf_spec = MSYMBOL_NAME (sym);
+
+                         if (otf_spec[0] == ':' && otf_spec[1] == 'o'
+                             && otf_spec[2] == 't' && otf_spec[3] == 'f')
+                           parse_otf_command (sym, &rule->src.facility.otf_spec);
+                         else
+                           MERROR (MERROR_FLT, INVALID_CMD_ID);
+                         rule->src_type = SRC_OTF_SPEC;
+                         pl = MPLIST_NEXT (pl);
+                       }
+                     else if (MPLIST_TAIL_P (pl))
+                       MERROR (MERROR_FLT, INVALID_CMD_ID);
                      else
                      else
-                       cmd->body.rule.src.supported_glyph = -1;
+                       rule->src_type = SRC_HAS_GLYPH;
+                     rule->src.facility.len = 0;
+                     MPLIST_DO (p, pl)
+                       {
+                         if (! MPLIST_INTEGER_P (p)
+                             && (MPLIST_SYMBOL_P (p)
+                                 ? MPLIST_SYMBOL (p) != Mequal
+                                 : 1))
+                           MERROR (MERROR_FLT, INVALID_CMD_ID);
+                         rule->src.facility.len++;
+                       }
+                     rule->src.facility.codes = pl;
+                     M17N_OBJECT_REF (pl);
                    }
                }
              else
                    }
                }
              else
@@ -1011,7 +1195,7 @@ load_command (FontLayoutStage *stage, MPlist *plist,
          && ((name[0] == 'o' && name[1] == 't'
               && name[2] == 'f' && name[3] == ':')
              || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
          && ((name[0] == 'o' && name[1] == 't'
               && name[2] == 'f' && name[3] == ':')
              || (name[0] == ':' && name[1] == 'o' && name[2] == 't'
-                 && name[3] == 'f' && name[4] == '=')))
+                 && name[3] == 'f' && (name[4] == '=' || name[4] == '?'))))
        {
          result = load_otf_command (&cmd, sym);
          if (result < 0)
        {
          result = load_otf_command (&cmd, sym);
          if (result < 0)
@@ -1091,7 +1275,8 @@ free_flt_command (FontLayoutCmd *cmd)
     }
   else if (cmd->type == FontLayoutCmdTypeCond)
     free (cmd->body.cond.cmd_ids);
     }
   else if (cmd->type == FontLayoutCmdTypeCond)
     free (cmd->body.cond.cmd_ids);
-  else if (cmd->type == FontLayoutCmdTypeOTF)
+  else if (cmd->type == FontLayoutCmdTypeOTF
+          || cmd->type == FontLayoutCmdTypeOTFCategory)
     {
       if (cmd->body.otf.features[0])
        free (cmd->body.otf.features[0]);
     {
       if (cmd->body.otf.features[0])
        free (cmd->body.otf.features[0]);
@@ -1149,7 +1334,7 @@ static int
 load_flt (MFLT *flt, MPlist *key_list)
 {
   MPlist *top, *plist, *pl, *p;
 load_flt (MFLT *flt, MPlist *key_list)
 {
   MPlist *top, *plist, *pl, *p;
-  MCharTable *category = NULL;
+  FontLayoutCategory *category = NULL;
   MSymbol sym;
 
   if (key_list)
   MSymbol sym;
 
   if (key_list)
@@ -1226,18 +1411,23 @@ load_flt (MFLT *flt, MPlist *key_list)
       if (sym == Mcategory)
        {
          if (category)
       if (sym == Mcategory)
        {
          if (category)
-           M17N_OBJECT_UNREF (category);
+           unref_category_table (category);
          else if (flt->coverage)
            {
              category = flt->coverage;
          else if (flt->coverage)
            {
              category = flt->coverage;
+             ref_category_table (category);
              continue;
            }
              continue;
            }
-         category = load_category_table (pl);
+         category = load_category_table (pl, NULL);
+         if (! category)
+           goto err;
          if (! flt->coverage)
            {
              flt->coverage = category;
          if (! flt->coverage)
            {
              flt->coverage = category;
-             M17N_OBJECT_REF (category);
+             ref_category_table (category);
            }
            }
+         if (category->definition)
+           flt->need_config = 1;
        }
       else if (sym == Mgenerator)
        {
        }
       else if (sym == Mgenerator)
        {
@@ -1249,14 +1439,15 @@ load_flt (MFLT *flt, MPlist *key_list)
          if (! stage)
            break;
          stage->category = category;
          if (! stage)
            break;
          stage->category = category;
-         M17N_OBJECT_REF (category);
+         M17N_OBJECT_REF (category->table);
          if (! flt->stages)
            flt->stages = mplist ();
          mplist_add (flt->stages, Mt, stage);
        }
     }
   if (category)
          if (! flt->stages)
            flt->stages = mplist ();
          mplist_add (flt->stages, Mt, stage);
        }
     }
   if (category)
-    M17N_OBJECT_UNREF (category);
+    unref_category_table (category);
+ err:
   if (! MPLIST_TAIL_P (plist))
     {
       M17N_OBJECT_UNREF (top);
   if (! MPLIST_TAIL_P (plist))
     {
       M17N_OBJECT_UNREF (top);
@@ -1269,14 +1460,17 @@ load_flt (MFLT *flt, MPlist *key_list)
 
 
 static void
 
 
 static void
-free_flt_stage (FontLayoutStage *stage)
+free_flt_stage (MFLT *flt, FontLayoutStage *stage)
 {
   int i;
 
 {
   int i;
 
-  M17N_OBJECT_UNREF (stage->category);
-  for (i = 0; i < stage->used; i++)
-    free_flt_command (stage->cmds + i);
-  MLIST_FREE1 (stage, cmds);
+  unref_category_table (stage->category);
+  if (! flt->font_id)
+    {
+      for (i = 0; i < stage->used; i++)
+       free_flt_command (stage->cmds + i);
+      MLIST_FREE1 (stage, cmds);
+    }
   free (stage);
 }
 
   free (stage);
 }
 
@@ -1292,13 +1486,15 @@ free_flt_list ()
          MFLT *flt = MPLIST_VAL (plist);
 
          if (flt->coverage)
          MFLT *flt = MPLIST_VAL (plist);
 
          if (flt->coverage)
-           M17N_OBJECT_UNREF (flt->coverage);
+           unref_category_table (flt->coverage);
          if (flt->stages)
            {
              MPLIST_DO (pl, MPLIST_NEXT (flt->stages))
          if (flt->stages)
            {
              MPLIST_DO (pl, MPLIST_NEXT (flt->stages))
-               free_flt_stage (MPLIST_VAL (pl));
+               free_flt_stage (flt, MPLIST_VAL (pl));
              M17N_OBJECT_UNREF (flt->stages);
            }
              M17N_OBJECT_UNREF (flt->stages);
            }
+         free (flt);
+         MPLIST_VAL (plist) = NULL;
        }
       M17N_OBJECT_UNREF (flt_list);
     }
        }
       M17N_OBJECT_UNREF (flt_list);
     }
@@ -1336,17 +1532,17 @@ list_flt ()
        {
          if (MPLIST_TAIL_P (flt_list))
            {
        {
          if (MPLIST_TAIL_P (flt_list))
            {
-             flt_min_coverage = mchartable_min_char (flt->coverage);
-             flt_max_coverage = mchartable_max_char (flt->coverage);
+             flt_min_coverage = mchartable_min_char (flt->coverage->table);
+             flt_max_coverage = mchartable_max_char (flt->coverage->table);
            }
          else
            {
              int c;
 
            }
          else
            {
              int c;
 
-             c = mchartable_min_char (flt->coverage);
+             c = mchartable_min_char (flt->coverage->table);
              if (flt_min_coverage > c)
                flt_min_coverage = c;
              if (flt_min_coverage > c)
                flt_min_coverage = c;
-             c = mchartable_max_char (flt->coverage);
+             c = mchartable_max_char (flt->coverage->table);
              if (flt_max_coverage < c)
                flt_max_coverage = c;
            }
              if (flt_max_coverage < c)
                flt_max_coverage = c;
            }
@@ -1374,6 +1570,9 @@ typedef struct
   /* Pointer to the current stage.  */
   FontLayoutStage *stage;
 
   /* Pointer to the current stage.  */
   FontLayoutStage *stage;
 
+  /* Pointer to the category table of the next stage or NULL if none.  */
+  FontLayoutCategory *category;
+
   /* Pointer to the font.  */
   MFLTFont *font;
 
   /* Pointer to the font.  */
   MFLTFont *font;
 
@@ -1384,6 +1583,7 @@ typedef struct
      table into this array.  An element is a category letter used for
      a regular expression matching.  */
   char *encoded;
      table into this array.  An element is a category letter used for
      a regular expression matching.  */
   char *encoded;
+  int encoded_offset;
   int *match_indices;
   int code_offset;
   int cluster_begin_idx;
   int *match_indices;
   int code_offset;
   int cluster_begin_idx;
@@ -1395,6 +1595,8 @@ typedef struct
 } FontLayoutContext;
 
 static int run_command (int, int, int, int, FontLayoutContext *);
 } FontLayoutContext;
 
 static int run_command (int, int, int, int, FontLayoutContext *);
+static int run_otf (int, MFLTOtfSpec *, int, int, FontLayoutContext *);
+static int try_otf (int, MFLTOtfSpec *, int, int, FontLayoutContext *);
 
 #define NMATCH 20
 
 
 #define NMATCH 20
 
@@ -1407,40 +1609,9 @@ run_rule (int depth,
   int consumed;
   int i;
   int orig_from = from;
   int consumed;
   int i;
   int orig_from = from;
+  int need_cluster_update = 0;
 
 
-  if (rule->src_type == SRC_SEQ)
-    {
-      int len;
-
-      len = rule->src.seq.n_codes;
-      if (len > (to - from))
-       return 0;
-      for (i = 0; i < len; i++)
-       if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->code)
-         break;
-      if (i < len)
-       return 0;
-      to = from + len;
-      if (MDEBUG_FLAG () > 2)
-       MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
-                      rule->src.seq.codes[0]);
-    }
-  else if (rule->src_type == SRC_RANGE)
-    {
-      int head;
-
-      if (from >= to)
-       return 0;
-      head = GREF (ctx->in, from)->code;
-      if (head < rule->src.range.from || head > rule->src.range.to)
-       return 0;
-      ctx->code_offset = head - rule->src.range.from;
-      to = from + 1;
-      if (MDEBUG_FLAG () > 2)
-       MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
-                      rule->src.range.from, rule->src.range.to);
-    }
-  else if (rule->src_type == SRC_REGEX)
+  if (rule->src_type == SRC_REGEX)
     {
       regmatch_t pmatch[NMATCH];
       char saved_code;
     {
       regmatch_t pmatch[NMATCH];
       char saved_code;
@@ -1448,18 +1619,19 @@ run_rule (int depth,
 
       if (from > to)
        return 0;
 
       if (from > to)
        return 0;
-      saved_code = ctx->encoded[to];
-      ctx->encoded[to] = '\0';
+      saved_code = ctx->encoded[to - ctx->encoded_offset];
+      ctx->encoded[to - ctx->encoded_offset] = '\0';
       result = regexec (&(rule->src.re.preg),
       result = regexec (&(rule->src.re.preg),
-                       ctx->encoded + from, NMATCH, pmatch, 0);
+                       ctx->encoded + (from - ctx->encoded_offset),
+                       NMATCH, pmatch, 0);
       if (result == 0 && pmatch[0].rm_so == 0)
        {
          if (MDEBUG_FLAG () > 2)
            MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
                           rule->src.re.pattern,
       if (result == 0 && pmatch[0].rm_so == 0)
        {
          if (MDEBUG_FLAG () > 2)
            MDEBUG_PRINT5 ("\n [FLT] %*s(REGEX \"%s\" \"%s\" %d", depth, "",
                           rule->src.re.pattern,
-                          ctx->encoded + from,
+                          ctx->encoded + (from - ctx->encoded_offset),
                           pmatch[0].rm_eo);
                           pmatch[0].rm_eo);
-         ctx->encoded[to] = saved_code;
+         ctx->encoded[to - ctx->encoded_offset] = saved_code;
          for (i = 0; i < NMATCH; i++)
            {
              if (pmatch[i].rm_so < 0)
          for (i = 0; i < NMATCH; i++)
            {
              if (pmatch[i].rm_so < 0)
@@ -1475,9 +1647,44 @@ run_rule (int depth,
        }
       else
        {
        }
       else
        {
-         ctx->encoded[to] = saved_code;
+         ctx->encoded[to - ctx->encoded_offset] = saved_code;
          return 0;
        }
          return 0;
        }
+      need_cluster_update = 1;
+    }
+  else if (rule->src_type == SRC_SEQ)
+    {
+      int len;
+
+      len = rule->src.seq.n_codes;
+      if (len > (to - from))
+       return 0;
+      for (i = 0; i < len; i++)
+       if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->c)
+         break;
+      if (i < len)
+       return 0;
+      to = from + len;
+      if (MDEBUG_FLAG () > 2)
+       MDEBUG_PRINT3 ("\n [FLT] %*s(SEQ 0x%X", depth, "",
+                      rule->src.seq.codes[0]);
+      need_cluster_update = 1;
+    }
+  else if (rule->src_type == SRC_RANGE)
+    {
+      int head;
+
+      if (from >= to)
+       return 0;
+      head = GREF (ctx->in, from)->c;
+      if (head < rule->src.range.from || head > rule->src.range.to)
+       return 0;
+      ctx->code_offset = head - rule->src.range.from;
+      to = from + 1;
+      if (MDEBUG_FLAG () > 2)
+       MDEBUG_PRINT4 ("\n [FLT] %*s(RANGE 0x%X-0x%X", depth, "",
+                      rule->src.range.from, rule->src.range.to);
+      need_cluster_update = 1;
     }
   else if (rule->src_type == SRC_INDEX)
     {
     }
   else if (rule->src_type == SRC_INDEX)
     {
@@ -1488,56 +1695,128 @@ run_rule (int depth,
        return 0;
       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
       if (MDEBUG_FLAG () > 2)
        return 0;
       to = ctx->match_indices[rule->src.match_idx * 2 + 1];
       if (MDEBUG_FLAG () > 2)
-       MDEBUG_PRINT3 ("\n [FLT] %*s(INDEX %d", depth, "", rule->src.match_idx);
+       MDEBUG_PRINT3 ("\n [FLT] %*s(SUBPART %d", depth, "",
+                      rule->src.match_idx);
+      need_cluster_update = 1;
     }
     }
-  else if (rule->src_type == SRC_HAS_GLYPH)
+  else if (rule->src_type == SRC_HAS_GLYPH
+          || rule->src_type == SRC_OTF_SPEC)
     {
     {
-      int encoded;
-      unsigned code;
+      static MFLTGlyphString gstring;
+      MPlist *p;
+      int idx;
 
 
-      if (rule->src.supported_glyph < 0)
+      if (rule->src.facility.len > 0)
        {
        {
-         if (from >= to)
-           return 0;
-         code = GREF (ctx->in, from)->code;
-         to = from + 1;
-         encoded = GREF (ctx->in, from)->encoded;
+         if (! gstring.glyph_size)
+           {
+             gstring.glyph_size = ctx->in->glyph_size;
+             gstring.glyphs = calloc (rule->src.facility.len,
+                                      gstring.glyph_size);
+             gstring.allocated = rule->src.facility.len;
+             gstring.used = rule->src.facility.len;
+           }
+         else if (rule->src.facility.len < gstring.allocated)
+           {
+             gstring.glyphs = realloc (gstring.glyphs,
+                                       gstring.glyph_size
+                                       * rule->src.facility.len);
+             gstring.allocated = rule->src.facility.len;
+             gstring.used = rule->src.facility.len;
+           }
+
+         for (i = 0, p = rule->src.facility.codes, idx = from;
+              i < rule->src.facility.len; i++, p = MPLIST_NEXT (p))
+           {
+             if (MPLIST_INTEGER_P (p))
+               {
+                 GREF (&gstring, i)->c = MPLIST_INTEGER (p);
+                 GREF (&gstring, i)->encoded = 0;
+               }
+             else
+               {
+                 GREF (&gstring, i)->c = GREF (ctx->in, idx)->code;
+                 GREF (&gstring, i)->encoded = GREF (ctx->in, idx)->encoded;
+                 idx++;
+               }
+           }
        }
        }
-      else
+
+      if (MDEBUG_FLAG () > 2)
        {
        {
-         code = rule->src.supported_glyph;
-         to = from;
-         encoded = 0;
+         if (rule->src_type == SRC_HAS_GLYPH)
+           MDEBUG_PRINT2 ("\n [FLT] %*s(HAS-GLYPH", depth, "");
+         else
+           MDEBUG_PRINT2 ("\n [FLT] %*s(OTF-SPEC", depth, "");
+         for (i = 0; i < rule->src.facility.len; i++)
+           MDEBUG_PRINT1 (" %04X", GREF (&gstring, i)->code);
        }
        }
-      if (! encoded)
+      if (ctx->font->get_glyph_id (ctx->font, &gstring, 0,
+                                  rule->src.facility.len) < 0)
        {
        {
-         static MFLTGlyphString gstring;
+         MDEBUG_PRINT (") FAIL!");
+         return 0;
+       }
+      if (rule->src_type == SRC_OTF_SPEC)
+       {
+         MFLTOtfSpec *spec = &rule->src.facility.otf_spec;
 
 
-         if (! gstring.glyph_size)
+         if (! ctx->font->check_otf)
            {
            {
-             gstring.glyph_size = ctx->in->glyph_size;
-             gstring.glyphs = calloc (1, gstring.glyph_size);
-             gstring.allocated = 1;
-             gstring.used = 1;
+             if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
+                 || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
+               return 0;
+           }
+         else
+           {
+             if (rule->src.facility.len == 0)
+               {
+                 if (! ctx->font->check_otf (ctx->font, spec))
+                   return 0;
+               }
+             else
+               {
+                 int prev_out_used = ctx->out->used, out_used;
+                 MFLTGlyphAdjustment *adjustment;
+
+                 adjustment = alloca ((sizeof *adjustment)
+                                      * (ctx->out->allocated - ctx->out->used));
+                 if (! adjustment)
+                   MERROR (MERROR_FLT, -1);
+                 memset (adjustment, 0,
+                         (sizeof *adjustment)
+                         * (ctx->out->allocated - ctx->out->used));
+                 ctx->font->drive_otf (ctx->font, &rule->src.facility.otf_spec,
+                                       &gstring, 0, rule->src.facility.len,
+                                       ctx->out,
+                                       adjustment);
+                 out_used = ctx->out->used;
+                 ctx->out->used = prev_out_used;
+                 if (rule->src.facility.len == out_used - prev_out_used)
+                   {
+                     for (i = prev_out_used; i < out_used; i++)
+                       {
+                         if (GREF (&gstring, i - prev_out_used)->code
+                             != GREF (ctx->out, i)->code)
+                           break;
+                         if (adjustment[i - prev_out_used].set)
+                           break;
+                       }
+                     if (i == out_used)
+                       return 0;
+                   }
+               }
            }
            }
-         gstring.glyphs[0].code = code;
-         if (ctx->font->get_glyph_id (ctx->font, &gstring, 0, 1) < 0
-             || ! gstring.glyphs[0].encoded)
-           return 0;
        }
     }
        }
     }
-  else if (rule->src_type == SRC_OTF_SPEC)
-    {
-      MFLTOtfSpec *spec = &rule->src.otf_spec;
 
 
-      if (! ctx->font->check_otf)
+  if (need_cluster_update && ctx->cluster_begin_idx >= 0)
+    {
+      for (i = from; i < to; i++)
        {
        {
-         if ((spec->features[0] && spec->features[0][0] != 0xFFFFFFFF)
-             || (spec->features[1] && spec->features[1][0] != 0xFFFFFFFF))
-           return 0;
+         MFLTGlyph *g = GREF (ctx->in, i);
+         UPDATE_CLUSTER_RANGE (ctx, g);
        }
        }
-      else if (! ctx->font->check_otf (ctx->font, spec))
-       return 0;
     }
 
   consumed = 0;
     }
 
   consumed = 0;
@@ -1590,6 +1869,46 @@ run_cond (int depth,
   return (pos);
 }
 
   return (pos);
 }
 
+static void
+decode_packed_otf_tag (FontLayoutContext *ctx, MFLTGlyphString *gstring,
+                      int from, int to, FontLayoutCategory *category)
+{
+  for (; from < to; from++)
+    {
+      MFLTGlyph *g = GREF (gstring, from);
+      unsigned int tag = g->internal & 0xFFFFFFF;
+      char enc;
+
+      if (GET_COMBINED (g))
+       continue;
+      if (! category)
+       {
+         SET_CATEGORY_CODE (g, 0);
+         continue;
+       }
+      enc = '\0';
+      if (tag & 0xFFFFF80)
+       {
+         int i;
+
+         /* Clear the feature tag code.  */
+         g->internal &= ~0xFFFFFFF;
+         for (i = 0; i < category->feature_table.size; i++)
+           if (category->feature_table.tag[i] == tag)
+             {
+               enc = category->feature_table.code[i];
+               if (ctx->in == gstring)
+                 ctx->encoded[from - ctx->encoded_offset] = enc;
+               break;
+             }
+       }
+      if (! enc)
+       enc = (g->c > 0 ? (int) mchartable_lookup (category->table, g->c)
+              : g->c == 0 ? 1 : ' ');
+      SET_CATEGORY_CODE (g, enc);
+    }
+}
+
 static int
 run_otf (int depth,
         MFLTOtfSpec *otf_spec, int from, int to, FontLayoutContext *ctx)
 static int
 run_otf (int depth,
         MFLTOtfSpec *otf_spec, int from, int to, FontLayoutContext *ctx)
@@ -1625,6 +1944,8 @@ run_otf (int depth,
                            adjustment);
       if (to < 0)
        return to;
                            adjustment);
       if (to < 0)
        return to;
+      decode_packed_otf_tag (ctx, ctx->out, from_idx, ctx->out->used,
+                            ctx->category);
       out_len = ctx->out->used - from_idx;
       if (otf_spec->features[1])
        {
       out_len = ctx->out->used - from_idx;
       if (otf_spec->features[1])
        {
@@ -1651,7 +1972,7 @@ run_otf (int depth,
                        g->xadv += a->xadv;
                        g->yadv += a->yadv;
                      }
                        g->xadv += a->xadv;
                        g->yadv += a->yadv;
                      }
-                   if (a->xoff || a->yoff)
+                   if (a->xoff || a->yoff || a->back)
                      {
                        int j;
                        MFLTGlyph *gg = PREV (ctx->out, g);
                      {
                        int j;
                        MFLTGlyph *gg = PREV (ctx->out, g);
@@ -1659,14 +1980,26 @@ run_otf (int depth,
 
                        g->xoff = a->xoff;
                        g->yoff = a->yoff;
 
                        g->xoff = a->xoff;
                        g->yoff = a->yoff;
+                       g->lbearing += a->xoff;
+                       g->rbearing += a->xoff;
+                       g->ascent -= a->yoff;
+                       g->descent -= a->yoff;
                        while (aa->back > 0)
                          {
                            for (j = 0; j < aa->back;
                                 j++, gg = PREV (ctx->out, gg))
                        while (aa->back > 0)
                          {
                            for (j = 0; j < aa->back;
                                 j++, gg = PREV (ctx->out, gg))
-                             g->xoff -= gg->xadv;
+                             {
+                               g->xoff -= gg->xadv;
+                               g->lbearing -= gg->xadv;
+                               g->rbearing -= gg->xadv;
+                             }
                            aa = aa - aa->back;
                            g->xoff += aa->xoff;
                            g->yoff += aa->yoff;
                            aa = aa - aa->back;
                            g->xoff += aa->xoff;
                            g->yoff += aa->yoff;
+                           g->lbearing += aa->xoff;
+                           g->rbearing += aa->xoff;
+                           g->ascent -= aa->yoff;
+                           g->descent -= aa->yoff;
                          }
                      }
                    g->adjusted = 1;
                          }
                      }
                    g->adjusted = 1;
@@ -1684,6 +2017,54 @@ run_otf (int depth,
   return to;
 }
 
   return to;
 }
 
+static int
+try_otf (int depth, MFLTOtfSpec *otf_spec, int from, int to,
+        FontLayoutContext *ctx)
+{
+  MFLTFont *font = ctx->font;
+
+  if (MDEBUG_FLAG () > 2)
+    MDEBUG_PRINT3 ("\n [FLT] %*s%s", depth, "", MSYMBOL_NAME (otf_spec->sym));
+
+  if (! otf_spec->features[0] && ! otf_spec->features[1])
+    {
+      /* Reset categories.  */
+      MCharTable *table = ctx->category->table;
+      int i;
+
+      for (i = from; i < to; i++)
+       {
+         MFLTGlyph *g = GREF (ctx->in, i);
+
+         if (! GET_COMBINED (g))
+           {
+             char enc = (GET_ENCODED (g)
+                         ? (g->c > 0 ? (int) mchartable_lookup (table, g->c)
+                            : 1)
+                         : g->code
+                         ? (int) mchartable_lookup (table, g->code)
+                         : ' ');
+             SET_CATEGORY_CODE (g, enc);
+             ctx->encoded[i - ctx->encoded_offset] = enc;
+           }
+       }
+      return from;
+    }
+
+  if (ctx->stage->category->feature_table.size == 0)
+    return from;
+
+  font->get_glyph_id (font, ctx->in, from, to);
+  if (mflt_try_otf)
+    {
+      to = mflt_try_otf (font, otf_spec, ctx->in, from, to);
+      if (to < 0)
+       return from;
+      decode_packed_otf_tag (ctx, ctx->in, from, to, ctx->stage->category);
+    }
+  return from;
+}
+
 static char work[16];
 
 static char *
 static char work[16];
 
 static char *
@@ -1726,6 +2107,8 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
   if (id >= 0)
     {
       int i;
   if (id >= 0)
     {
       int i;
+      MCharTable *table = ctx->category ? ctx->category->table : NULL;
+      char enc;
 
       /* Direct code (== ctx->code_offset + id) output.
         The source is not consumed.  */
 
       /* Direct code (== ctx->code_offset + id) output.
         The source is not consumed.  */
@@ -1735,11 +2118,20 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
       i = (from < to || from == 0) ? from : from - 1;
       GDUP (ctx, i);
       g = GREF (ctx->out, ctx->out->used - 1);
       i = (from < to || from == 0) ? from : from - 1;
       GDUP (ctx, i);
       g = GREF (ctx->out, ctx->out->used - 1);
-      g->code = ctx->code_offset + id;
-      SET_ENCODED (g, 0);
-      SET_MEASURED (g, 0);
+      g->c = g->code = ctx->code_offset + id;
       if (ctx->combining_code)
        SET_COMBINING_CODE (g, ctx, ctx->combining_code);
       if (ctx->combining_code)
        SET_COMBINING_CODE (g, ctx, ctx->combining_code);
+      else if (table)
+       {
+         enc = (GET_ENCODED (g)
+                ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
+                : g->code
+                ? (int) mchartable_lookup (table, g->code)
+                : ' ');
+         SET_CATEGORY_CODE (g, enc);
+       }
+      SET_ENCODED (g, 0);
+      SET_MEASURED (g, 0);
       if (ctx->left_padding)
        SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
       for (i = from; i < to; i++)
       if (ctx->left_padding)
        SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
       for (i = from; i < to; i++)
@@ -1751,7 +2143,8 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
          else if (g->to < tmp->to)
            g->to = tmp->to;
        }
          else if (g->to < tmp->to)
            g->to = tmp->to;
        }
-      UPDATE_CLUSTER_RANGE (ctx, g);
+      if (ctx->cluster_begin_idx >= 0)
+       UPDATE_CLUSTER_RANGE (ctx, g);
       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
       if (MDEBUG_FLAG () > 2)
        MDEBUG_PRINT (")");
       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
       if (MDEBUG_FLAG () > 2)
        MDEBUG_PRINT (")");
@@ -1772,6 +2165,8 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
        to = run_cond (depth, &cmd->body.cond, from, to, ctx);
       else if (cmd->type == FontLayoutCmdTypeOTF)
        to = run_otf (depth, &cmd->body.otf, from, to, ctx);
        to = run_cond (depth, &cmd->body.cond, from, to, ctx);
       else if (cmd->type == FontLayoutCmdTypeOTF)
        to = run_otf (depth, &cmd->body.otf, from, to, ctx);
+      else if (cmd->type == FontLayoutCmdTypeOTFCategory)
+       to = try_otf (depth, &cmd->body.otf, from, to, ctx);
       return to;
     }
 
       return to;
     }
 
@@ -1796,13 +2191,14 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
          SET_COMBINING_CODE (g, ctx, ctx->combining_code);
        if (ctx->left_padding)
          SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
          SET_COMBINING_CODE (g, ctx, ctx->combining_code);
        if (ctx->left_padding)
          SET_LEFT_PADDING (g, ctx, LeftPaddingMask);
-       UPDATE_CLUSTER_RANGE (ctx, g);
+       if (ctx->cluster_begin_idx >= 0)
+         UPDATE_CLUSTER_RANGE (ctx, g);
        if (MDEBUG_FLAG () > 2)
          {
            if (g->c < 0)
              MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
            else
        if (MDEBUG_FLAG () > 2)
          {
            if (g->c < 0)
              MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
            else
-             MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->code);
+             MDEBUG_PRINT3 ("\n [FLT] %*s(COPY 0x%X)", depth, "", g->c);
          }
        ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
        return (from + 1);
          }
        ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
        return (from + 1);
@@ -1841,13 +2237,16 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
       {
        int i;
 
       {
        int i;
 
+       if (MDEBUG_FLAG () > 2)
+         MDEBUG_PRINT2 ("\n [FLT] %*s|", depth, "");
        i = from < to ? from : from - 1;
        GDUP (ctx, i);
        g = GREF (ctx->out, ctx->out->used - 1);
        g->c = -1, g->code = 0;
        g->xadv = g->yadv = 0;
        i = from < to ? from : from - 1;
        GDUP (ctx, i);
        g = GREF (ctx->out, ctx->out->used - 1);
        g->c = -1, g->code = 0;
        g->xadv = g->yadv = 0;
-       SET_ENCODED (g, 0);
+       SET_ENCODED (g, 1);
        SET_MEASURED (g, 0);
        SET_MEASURED (g, 0);
+       SET_CATEGORY_CODE (g, ' ');
        return from;
       }
 
        return from;
       }
 
@@ -1882,6 +2281,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
   int i, j;
   MFLTGlyph *g;
   MPlist *stages = flt->stages;
   int i, j;
   MFLTGlyph *g;
   MPlist *stages = flt->stages;
+  FontLayoutCategory *prev_category = NULL;
 
   from_pos = GREF (ctx->in, from)->from;
   to_pos = GREF (ctx->in, to - 1)->to;
 
   from_pos = GREF (ctx->in, from)->from;
   to_pos = GREF (ctx->in, to - 1)->to;
@@ -1900,25 +2300,40 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       int result;
 
       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
       int result;
 
       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
-      table = ctx->stage->category;
+      table = ctx->stage->category->table;
+      stages = MPLIST_NEXT (stages);
+      if (MPLIST_TAIL_P (stages))
+       ctx->category = NULL;
+      else
+       ctx->category = ((FontLayoutStage *) MPLIST_VAL (stages))->category;
       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
       ctx->code_offset = ctx->combining_code = ctx->left_padding = 0;
+      ctx->encoded_offset = from;
       for (i = from; i < to; i++)
        {
          MFLTGlyph *g = GREF (ctx->in, i);
       for (i = from; i < to; i++)
        {
          MFLTGlyph *g = GREF (ctx->in, i);
-         char enc = (GET_ENCODED (g)
-                     ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
-                     : g->code
-                     ? (int) mchartable_lookup (table, g->code)
-                     : ' ');
+         char enc;
 
 
-         ctx->encoded[i] = enc;
+         if (GET_COMBINED (g)
+             || (prev_category && prev_category != ctx->stage->category))
+           {
+             enc = (GET_ENCODED (g)
+                    ? (g->c > 0 ? (int) mchartable_lookup (table, g->c) : 1)
+                    : g->code
+                    ? (int) mchartable_lookup (table, g->code)
+                    : ' ');
+             if (! GET_COMBINED (g))
+               SET_CATEGORY_CODE (g, enc);
+           }
+         else
+           enc = GET_CATEGORY_CODE (g);
+         ctx->encoded[i - from] = enc;
          if (! enc && stage_idx == 0)
            {
              to = i;
              break;
            }
        }
          if (! enc && stage_idx == 0)
            {
              to = i;
              break;
            }
        }
-      ctx->encoded[i] = '\0';
+      ctx->encoded[i - from] = '\0';
       ctx->match_indices[0] = from;
       ctx->match_indices[1] = to;
       for (i = 2; i < NMATCH; i++)
       ctx->match_indices[0] = from;
       ctx->match_indices[1] = to;
       for (i = 2; i < NMATCH; i++)
@@ -1927,7 +2342,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       if (MDEBUG_FLAG () > 2)
        {
          MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
       if (MDEBUG_FLAG () > 2)
        {
          MDEBUG_PRINT2 ("\n [FLT]   (STAGE %d \"%s\"", stage_idx,
-                        ctx->encoded + from);
+                        ctx->encoded);
          MDEBUG_PRINT (" (");
          for (i = from; i < to; i++)
            {
          MDEBUG_PRINT (" (");
          for (i = from; i < to; i++)
            {
@@ -1945,12 +2360,12 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       if (result < 0)
        return result;
 
       if (result < 0)
        return result;
 
-      stages = MPLIST_NEXT (stages);
       /* If this is the last stage, break the loop. */
       if (MPLIST_TAIL_P (stages))
        break;
 
       /* Otherwise, prepare for the next stage.   */
       /* If this is the last stage, break the loop. */
       if (MPLIST_TAIL_P (stages))
        break;
 
       /* Otherwise, prepare for the next stage.   */
+      prev_category = ctx->stage->category;
       temp = ctx->in;
       ctx->in = ctx->out;
       if (buf.glyphs)
       temp = ctx->in;
       ctx->in = ctx->out;
       if (buf.glyphs)
@@ -1997,8 +2412,8 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
 
          g = GREF (ctx->out, i);
          for (pos = g->from; pos <= g->to; pos++)
 
          g = GREF (ctx->out, i);
          for (pos = g->from; pos <= g->to; pos++)
-           if (g_indices[pos - orig_from] < 0)
-             g_indices[pos - orig_from] = i;
+           if (g_indices[pos - from_pos] < 0)
+             g_indices[pos - from_pos] = i;
        }
       for (i = 0; i < len; i++)
        if (g_indices[i] < 0)
        }
       for (i = 0; i < len; i++)
        if (g_indices[i] < 0)
@@ -2035,18 +2450,21 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
 
       /* Handle combining.  */
       ctx->font->get_metrics (ctx->font, ctx->out, 0, ctx->out->used);
 
       /* Handle combining.  */
-      if (ctx->check_mask & CombiningCodeMask)
+      if (ctx->check_mask & CombinedMask)
        {
          MFLTGlyph *base = GREF (ctx->out, 0);
          int base_height = base->ascent + base->descent;
        {
          MFLTGlyph *base = GREF (ctx->out, 0);
          int base_height = base->ascent + base->descent;
+         int base_width = base->rbearing - base->lbearing;
          int combining_code;
 
          for (i = 1; i < ctx->out->used; i++)
            {
              if ((g = GREF (ctx->out, i))
          int combining_code;
 
          for (i = 1; i < ctx->out->used; i++)
            {
              if ((g = GREF (ctx->out, i))
+                 && GET_COMBINED (g)
                  && (combining_code = GET_COMBINING_CODE (g)))
                {
                  int height = g->ascent + g->descent;
                  && (combining_code = GET_COMBINING_CODE (g)))
                {
                  int height = g->ascent + g->descent;
+                 int width = g->rbearing - g->lbearing;
                  int base_x, base_y, add_x, add_y, off_x, off_y;
 
                  if (base->from > g->from)
                  int base_x, base_y, add_x, add_y, off_x, off_y;
 
                  if (base->from > g->from)
@@ -2061,8 +2479,9 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
                  off_x = COMBINING_CODE_OFF_X (combining_code);
                  off_y = COMBINING_CODE_OFF_Y (combining_code);
 
                  off_x = COMBINING_CODE_OFF_X (combining_code);
                  off_y = COMBINING_CODE_OFF_Y (combining_code);
 
-                 g->xoff = ((base->xadv * base_x - g->xadv * add_x) / 2
-                            + x_ppem * off_x / 100 - base->xadv);
+                 g->xoff = ((base_width * base_x - width * add_x) / 2
+                            + x_ppem * off_x / 100
+                            - (base->xadv - base->lbearing) - g->lbearing);
                  if (base_y < 3)
                    g->yoff = base_height * base_y / 2 - base->ascent;
                  else
                  if (base_y < 3)
                    g->yoff = base_height * base_y / 2 - base->ascent;
                  else
@@ -2072,8 +2491,8 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
                  g->yoff -= y_ppem * off_y / 100;
                  if (base->lbearing > base->xadv + g->lbearing + g->xoff)
                    base->lbearing = base->xadv + g->lbearing + g->xoff;
                  g->yoff -= y_ppem * off_y / 100;
                  if (base->lbearing > base->xadv + g->lbearing + g->xoff)
                    base->lbearing = base->xadv + g->lbearing + g->xoff;
-                 if (base->rbearing < base->xadv + g->xadv + g->xoff)
-                   base->rbearing = base->xadv + g->xadv + g->xoff;
+                 if (base->rbearing < base->xadv + g->rbearing + g->xoff)
+                   base->rbearing = base->xadv + g->rbearing + g->xoff;
                  if (base->ascent < g->ascent - g->yoff)
                    base->ascent = g->ascent - g->yoff;
                  if (base->descent < g->descent - g->yoff)
                  if (base->ascent < g->ascent - g->yoff)
                    base->ascent = g->ascent - g->yoff;
                  if (base->descent < g->descent - g->yoff)
@@ -2087,6 +2506,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
                {
                  base = g;
                  base_height = g->ascent + g->descent;
                {
                  base = g;
                  base_height = g->ascent + g->descent;
+                 base_width = g->rbearing - g->lbearing;
                }
            }
        }
                }
            }
        }
@@ -2096,7 +2516,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
        for (i = 0; i < ctx->out->used; i++)
          {
            g = GREF (ctx->out, i);
        for (i = 0; i < ctx->out->used; i++)
          {
            g = GREF (ctx->out, i);
-           if (! GET_COMBINING_CODE (g))
+           if (! GET_COMBINED (g))
              {
                if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
                  {
              {
                if (GET_RIGHT_PADDING (g) && g->rbearing > g->xadv)
                  {
@@ -2154,14 +2574,67 @@ setup_combining_flt (MFLT *flt)
   MCharTable *combininig_class_table
     = mchar_get_prop_table (Mcombining_class, &type);
 
   MCharTable *combininig_class_table
     = mchar_get_prop_table (Mcombining_class, &type);
 
-  mchartable_set_range (flt->coverage, 0, 0x10FFFF, (void *) 'u');
+  mchartable_set_range (flt->coverage->table, 0, 0x10FFFF, (void *) 'u');
   if (combininig_class_table)
     mchartable_map (combininig_class_table, (void *) 0,
   if (combininig_class_table)
     mchartable_map (combininig_class_table, (void *) 0,
-                   setup_combining_coverage, flt->coverage);
+                   setup_combining_coverage, flt->coverage->table);
 }
 
 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
 
 }
 
 #define CHECK_FLT_STAGES(flt) ((flt)->stages || load_flt (flt, NULL) == 0)
 
+static FontLayoutCategory *
+configure_category (FontLayoutCategory *category, MFLTFont *font)
+{
+  if (! mflt_font_id || ! mflt_iterate_otf_feature)
+    {
+      FontLayoutCategory *new = malloc (sizeof (FontLayoutCategory));
+      new->definition = NULL;
+      new->table = category->table;
+      M17N_OBJECT_REF (new->table);
+      return new;
+    }
+  return load_category_table (category->definition, font);
+}
+
+static MFLT *
+configure_flt (MFLT *flt, MFLTFont *font, MSymbol font_id)
+{
+  MPlist *plist;
+  MFLT *configured;
+
+  if (! mflt_font_id || ! mflt_iterate_otf_feature)
+    return flt;
+  MPLIST_DO (plist, flt_list)
+    {
+      configured = MPLIST_VAL (plist);
+      if (! configured->font_id)
+       break;
+      if (configured->name == flt->name
+         && configured->font_id == font_id)
+       return configured;
+    }
+  if (! MSTRUCT_CALLOC_SAFE (configured))
+    return flt;
+  *configured = *flt;
+  configured->stages = mplist_copy (flt->stages);
+  MPLIST_DO (plist, configured->stages)
+    {
+      FontLayoutStage *stage = MPLIST_VAL (plist);
+      if (stage->category->definition)
+       {
+         MSTRUCT_CALLOC (stage, MERROR_FLT);
+         *stage = *((FontLayoutStage *) MPLIST_VAL (plist));
+         stage->category = configure_category (stage->category, font);
+         MPLIST_VAL (plist) = stage;
+       }
+      else
+       M17N_OBJECT_REF (stage->category->table);
+    }
+  configured->need_config = 0;
+  configured->font_id = font_id;
+  mplist_push (flt_list, flt->name, configured);
+  return configured;
+}
 \f
 /* Internal API */
 
 \f
 /* Internal API */
 
@@ -2196,10 +2669,16 @@ m17n_init_flt (void)
   Mlayouter = msymbol ("layouter");
   Mcombining = msymbol ("combining");
   Mfont_facility = msymbol ("font-facility");
   Mlayouter = msymbol ("layouter");
   Mcombining = msymbol ("combining");
   Mfont_facility = msymbol ("font-facility");
+  Mequal = msymbol ("=");
   Mgenerator = msymbol ("generator");
   Mend = msymbol ("end");
 
   Mgenerator = msymbol ("generator");
   Mend = msymbol ("end");
 
-  MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
+  mflt_enable_new_feature = 0;
+  mflt_iterate_otf_feature = NULL;
+  mflt_font_id = NULL;
+  mflt_try_otf = NULL;
+
+  MDEBUG_PRINT_TIME ("INIT", (mdebug__output, " to initialize the flt modules."));
   MDEBUG_POP_TIME ();
 }
 
   MDEBUG_POP_TIME ();
 }
 
@@ -2214,7 +2693,7 @@ m17n_fini_flt (void)
 
   MDEBUG_PUSH_TIME ();
   free_flt_list ();
 
   MDEBUG_PUSH_TIME ();
   free_flt_list ();
-  MDEBUG_PRINT_TIME ("FINI", (stderr, " to finalize the flt modules."));
+  MDEBUG_PRINT_TIME ("FINI", (mdebug__output, " to finalize the flt modules."));
   MDEBUG_POP_TIME ();
   m17n_fini_core ();
 }
   MDEBUG_POP_TIME ();
   m17n_fini_core ();
 }
@@ -2228,26 +2707,39 @@ m17n_fini_flt (void)
 
 /*=*/
 /***en
 
 /*=*/
 /***en
-    @brief Return a FLT object whose name is NAME.
+    @brief Return an FLT object that has a specified name.
+
+    The mflt_get () function returns an FLT object whose name is $NAME.
+
+    @return
+    If the operation was successful, mflt_get () returns a pointer
+    to the found FLT object.  Otherwise, it returns @c NULL.  */
+
+/***ja
+    @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹.
 
 
-    The mflt_get () function returns a FLT object whose name is $NAME.
+    ´Ø¿ô mflt_get () ¤Ï¡¢$NAME ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹¡£
 
     @return
 
     @return
-    If the operation was successfully, mflt_get () returns a pointer
-    to a FLT object.  Otherwise, it returns @c NULL.  */
+    ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_get () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
+    ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
 
 MFLT *
 mflt_get (MSymbol name)
 {
   MFLT *flt;
 
 MFLT *
 mflt_get (MSymbol name)
 {
   MFLT *flt;
+  MPlist *plist;
 
   if (! flt_list && list_flt () < 0)
     return NULL;
 
   if (! flt_list && list_flt () < 0)
     return NULL;
-  flt = mplist_get (flt_list, name);
+  for (plist = flt_list; plist; plist = plist->next)
+    if (((MFLT *) MPLIST_VAL (plist))->font_id == Mnil)
+      break;
+  flt = mplist_get (plist, name);
   if (! flt || ! CHECK_FLT_STAGES (flt))
     return NULL;
   if (flt->name == Mcombining
   if (! flt || ! CHECK_FLT_STAGES (flt))
     return NULL;
   if (flt->name == Mcombining
-      && ! mchartable_lookup (flt->coverage, 0))
+      && ! mchartable_lookup (flt->coverage->table, 0))
     setup_combining_flt (flt);
 
   return flt;
     setup_combining_flt (flt);
 
   return flt;
@@ -2255,19 +2747,29 @@ mflt_get (MSymbol name)
 
 /*=*/
 /***en
 
 /*=*/
 /***en
-    @brief Find a FLT suitable for a specified character and font.
+    @brief Find an FLT suitable for the specified character and font.
 
     The mflt_find () function returns the most appropriate FLT for
 
     The mflt_find () function returns the most appropriate FLT for
-    rendering the character $C by font $FONT.
+    layouting character $C with font $FONT.
+
+    @return
+    If the operation was successful, mflt_find () returns a pointer
+    to the found FLT object.  Otherwise, it returns @c NULL.  */
+
+/***ja
+    @brief »ØÄꤵ¤ì¤¿Ê¸»ú¤È¥Õ¥©¥ó¥È¤Ë¹ç¤Ã¤¿ FLT ¤òõ¤¹.
+
+    ´Ø¿ô mflt_find () ¤Ï¡¢Ê¸»ú $C ¤ò¥Õ¥©¥ó¥È $FONT
+    ¤Ç¥ì¥¤¥¢¥¦¥È¤¹¤ë¤¿¤á¤ËºÇ¤âŬÀڤʠFLT ¤òÊÖ¤¹¡£
 
     @return
 
     @return
-    If the operation was successfully, mflt_find () returns a pointer
-    to a FLT object.  Otherwise, it returns @c NULL.  */
+    ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_find () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
+    ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
 
 MFLT *
 mflt_find (int c, MFLTFont *font)
 {
 
 MFLT *
 mflt_find (int c, MFLTFont *font)
 {
-  MPlist *plist;
+  MPlist *plist, *pl;
   MFLT *flt;
   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
 
   MFLT *flt;
   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
 
@@ -2279,20 +2781,27 @@ mflt_find (int c, MFLTFont *font)
 
   if (! flt_list && list_flt () < 0)
     return NULL;
 
   if (! flt_list && list_flt () < 0)
     return NULL;
+  /* Skip configured FLTs.  */
+  MPLIST_DO (plist, flt_list)
+    if (((MFLT *) MPLIST_VAL (plist))->font_id == Mnil)
+      break;
   if (font)
     {
       MFLT *best = NULL;
 
   if (font)
     {
       MFLT *best = NULL;
 
-      MPLIST_DO (plist, flt_list)
+      MPLIST_DO (pl, plist)
        {
        {
-         flt = MPLIST_VAL (plist);
+         flt = MPLIST_VAL (pl);
          if (flt->registry != unicode_bmp
              && flt->registry != unicode_full)
            continue;
          if (flt->family && flt->family != font->family)
            continue;
          if (flt->registry != unicode_bmp
              && flt->registry != unicode_full)
            continue;
          if (flt->family && flt->family != font->family)
            continue;
+         if (flt->name == Mcombining
+             && ! mchartable_lookup (flt->coverage->table, 0))
+           setup_combining_flt (flt);
          if (c >= 0
          if (c >= 0
-             && ! mchartable_lookup (flt->coverage, c))
+             && ! mchartable_lookup (flt->coverage->table, c))
            continue;
          if (flt->otf.sym)
            {
            continue;
          if (flt->otf.sym)
            {
@@ -2306,30 +2815,45 @@ mflt_find (int c, MFLTFont *font)
                }
              else if (! font->check_otf (font, spec))
                continue;
                }
              else if (! font->check_otf (font, spec))
                continue;
-             return flt;
+             goto found;
            }
          best = flt;
        }
            }
          best = flt;
        }
-      return best;
+      if (best == NULL)
+       return NULL;
+      flt = best;
+      goto found;
     }
   if (c >= 0)
     {
     }
   if (c >= 0)
     {
-      MPLIST_DO (plist, flt_list)
+      MPLIST_DO (pl, plist)
        {
        {
-         flt = MPLIST_VAL (plist);
-         if (mchartable_lookup (flt->coverage, c))
-           return flt;
+         flt = MPLIST_VAL (pl);
+         if (mchartable_lookup (flt->coverage->table, c))
+           goto found;
        }
     }
   return NULL;
        }
     }
   return NULL;
+
+ found:
+  if (! CHECK_FLT_STAGES (flt))
+    return NULL;
+  if (font && flt->need_config && mflt_font_id)
+    flt = configure_flt (flt, font, mflt_font_id (font));
+  return flt;
 }
 
 /*=*/
 /***en
 }
 
 /*=*/
 /***en
-    @brief Return a name of a FLT.
+    @brief Return the name of an FLT.
 
     The mflt_name () function returns the name of $FLT.  */
 
 
     The mflt_name () function returns the name of $FLT.  */
 
+/***ja
+    @brief FLT ¤Î̾Á°¤òÊÖ¤¹.
+
+    ´Ø¿ô mflt_name () ¤Ï $FLT ¤Î̾Á°¤òÊÖ¤¹¡£  */
+
 const char *
 mflt_name (MFLT *flt)
 {
 const char *
 mflt_name (MFLT *flt)
 {
@@ -2341,34 +2865,60 @@ mflt_name (MFLT *flt)
     @brief Return a coverage of a FLT.
 
     The mflt_coverage () function returns a char-table that contains
     @brief Return a coverage of a FLT.
 
     The mflt_coverage () function returns a char-table that contains
-    nonzero value for characters supported by $FLT.  */
+    nonzero values for characters supported by $FLT.  */
+
+/***ja
+    @brief FLT ¤ÎÈϰϤòÊÖ¤¹.
+
+    ´Ø¿ô mflt_coverage () ¤Ï¡¢$FLT ¤¬¥µ¥Ý¡¼¥È¤¹¤ëʸ»ú¤ËÂФ·¤Æ
+    0 ¤Ç¤Ê¤¤Ãͤò´Þ¤àʸ»ú¥Æ¡¼¥Ö¥ë¤òÊÖ¤¹¡£  */
 
 MCharTable *
 mflt_coverage (MFLT *flt)
 {
 
 MCharTable *
 mflt_coverage (MFLT *flt)
 {
-  return flt->coverage;
+  return flt->coverage->table;
 }
 
 /*=*/
 /***en
 }
 
 /*=*/
 /***en
-    @brief Layout characters by Font Layout Table.
+    @brief Layout characters with an FLT.
 
 
-    The mflt_run () function layout characters in $GSTRING between
-    $FROM (inclusive) and $TO (exclusive) by $FONT.  If $FLT is
+    The mflt_run () function layouts characters in $GSTRING between
+    $FROM (inclusive) and $TO (exclusive) with $FONT.  If $FLT is
     nonzero, it is used for all the charaters.  Otherwise, appropriate
     FLTs are automatically chosen.
 
     @retval >=0
     nonzero, it is used for all the charaters.  Otherwise, appropriate
     FLTs are automatically chosen.
 
     @retval >=0
-    The operation was successful.  The value is an index to the
-    $GSTRING->glyphs which was previously indexed by $TO.
+    The operation was successful.  The value is the index to the
+    glyph, which was previously indexed by $TO, in $GSTRING->glyphs.
 
     @retval -2
 
     @retval -2
-    $GSTRING->glyphs is too short to store the result.  A caller can
-    call this fucntion again with the larger $GSTRING->glyphs.
+    $GSTRING->glyphs is too short to store the result.  The caller can
+    call this fucntion again with a longer $GSTRING->glyphs.
 
     @retval -1
     Some other error occurred.  */
 
 
     @retval -1
     Some other error occurred.  */
 
+/***ja
+    @brief FLT ¤ò»È¤Ã¤Æʸ»ú¤ò¥ì¥¤¥¢¥¦¥È¤¹¤ë.
+
+    ´Ø¿ô mflt_run () ¤Ï¡¢$GSTRING Ãæ¤Î $FROM ¤«¤é $TO Ä¾Á°¤Þ¤Ç¤Îʸ»ú¤ò
+    $FONT ¤òÍѤ¤¤Æ¥ì¥¤¥¢¥¦¥È¤¹¤ë¡£¤â¤· $FLT
+    ¤¬¥¼¥í¤Ç¤Ê¤±¤ì¤Ð¡¢¤½¤ÎÃͤò¤¹¤Ù¤Æ¤Îʸ»ú¤ËÂФ·¤ÆÍѤ¤¤ë¡£
+    ¤½¤¦¤Ç¤Ê¤±¤ì¤ÐŬÀڤʠFLT ¤ò¼«Æ°Åª¤ËÁªÂò¤¹¤ë¡£
+
+    @retval >=0
+    ¼Â¹ÔÀ®¸ù¤ò¼¨¤¹¡£ÊÖ¤µ¤ì¤ëÃͤϡ¢$GSTRING->glyphs Ãæ¤Ç°ÊÁ° $TO
+    ¤Ë¤è¤Ã¤Æ¼¨¤µ¤ì¤Æ¤¤¤¿¥°¥ê¥Õ¤Ø¤Î¥¤¥ó¥Ç¥¯¥¹¤Ç¤¢¤ë¡£
+
+    @retval -2
+    ·ë²Ì¤ò³ÊǼ¤¹¤ë¤Ë¤Ï $GSTRING->glyphs ¤¬Ã»¤¹¤®¤ë¤³¤È¤ò¼¨¤¹¡£
+    ¸Æ¤Ó½Ð¤·Â¦¤Ï¡¢¤è¤êŤ¤ $GSTRING->glyphs
+    ¤òÍѤ¤¤ÆºÆÅÙ¤³¤Î´Ø¿ô¤ò¸Æ¤Ö¤³¤È¤¬¤Ç¤­¤ë¡£
+
+    @retval -1
+    ¤½¤Î¾¤Î¥¨¥é¡¼¤¬µ¯¤­¤¿¤³¤È¤ò¼¨¤¹¡£  */
+
 int
 mflt_run (MFLTGlyphString *gstring, int from, int to,
          MFLTFont *font, MFLT *flt)
 int
 mflt_run (MFLTGlyphString *gstring, int from, int to,
          MFLTFont *font, MFLT *flt)
@@ -2380,6 +2930,7 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
   int auto_flt = ! flt;
   int c, i, j, k;
   int this_from, this_to;
   int auto_flt = ! flt;
   int c, i, j, k;
   int this_from, this_to;
+  MSymbol font_id = mflt_font_id ? mflt_font_id (font) : Mnil;
 
   out = *gstring;
   out.glyphs = NULL;
 
   out = *gstring;
   out.glyphs = NULL;
@@ -2391,9 +2942,12 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
   for (i = from; i < to; i++)
     {
       g = GREF (gstring, i);
   for (i = from; i < to; i++)
     {
       g = GREF (gstring, i);
-      c = g->c;
-      memset (g, 0, sizeof (MFLTGlyph));
-      g->code = g->c = c;
+      if (! g->encoded)
+       {
+         c = g->c;
+         memset (g, 0, sizeof (MFLTGlyph));
+         g->code = g->c = c;
+       }
       g->from = g->to = i;
     }
 
       g->from = g->to = i;
     }
 
@@ -2402,7 +2956,8 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
       if (! auto_flt)
        {
          for (this_to = this_from; this_to < to; this_to++)
       if (! auto_flt)
        {
          for (this_to = this_from; this_to < to; this_to++)
-           if (mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
+           if (mchartable_lookup (flt->coverage->table,
+                                  GREF (gstring, this_to)->c))
              break;
        }
       else
              break;
        }
       else
@@ -2424,7 +2979,7 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
            {
              c = GREF (gstring, this_to)->c;
              if (font->internal
            {
              c = GREF (gstring, this_to)->c;
              if (font->internal
-                 && mchartable_lookup (((MFLT *) font->internal)->coverage, c))
+                 && mchartable_lookup (((MFLT *) font->internal)->coverage->table, c))
                {
                  flt = font->internal;
                  break;
                {
                  flt = font->internal;
                  break;
@@ -2452,9 +3007,18 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
 
       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
 
 
       MDEBUG_PRINT1 (" [FLT] (%s", MSYMBOL_NAME (flt->name));
 
+      if (flt->need_config && font_id != Mnil)
+       flt = configure_flt (flt, font, font_id);
+
       for (; this_to < to; this_to++)
       for (; this_to < to; this_to++)
-       if (! mchartable_lookup (flt->coverage, GREF (gstring, this_to)->c))
-         break;
+       {
+         char enc;
+         g = GREF (gstring, this_to);
+         enc = (int) mchartable_lookup (flt->coverage->table, g->c);
+         if (! enc)
+           break;
+         SET_CATEGORY_CODE (g, enc);
+       }
 
       if (MDEBUG_FLAG ())
        {
 
       if (MDEBUG_FLAG ())
        {
@@ -2495,14 +3059,24 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
        {
          MDEBUG_PRINT ("\n [FLT]   (RESULT");
          if (MDEBUG_FLAG () > 1)
        {
          MDEBUG_PRINT ("\n [FLT]   (RESULT");
          if (MDEBUG_FLAG () > 1)
-           for (i = 0; this_from < this_to; this_from++, i++)
-             {
-               if (i > 0 && i % 4 == 0)
-                 MDEBUG_PRINT ("\n [FLT]          ");
-               g = GREF (gstring, this_from);
-               MDEBUG_PRINT4 (" (%04X %d %d %d)",
-                              g->code, g->xadv, g->xoff, g->yoff);
-             }
+           {
+             int idx = -1;
+             for (i = 0; this_from < this_to; i++, this_from++)
+               {
+                 g = GREF (gstring, this_from);
+                 if (g->from != idx)
+                   {
+                     if (i > 0)
+                       MDEBUG_PRINT2 ("\n [FLT]           %02d-%02d",
+                                      g->from, g->to);
+                     else
+                       MDEBUG_PRINT2 (" %02d-%02d", g->from, g->to);
+                     idx = g->from;
+                   }
+                 MDEBUG_PRINT4 (" (%04X %d %d %d)",
+                                g->code, g->xadv, g->xoff, g->yoff);
+               }
+           }
          else
            for (; this_from < this_to; this_from++)
              MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
          else
            for (; this_from < this_to; this_from++)
              MDEBUG_PRINT1 (" %04X", GREF (gstring, this_from)->code);
@@ -2531,6 +3105,18 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
   return to;
 }
 
   return to;
 }
 
+int mflt_enable_new_feature;
+
+int (*mflt_iterate_otf_feature) (struct _MFLTFont *font,
+                                MFLTOtfSpec *spec,
+                                int from, int to,
+                                unsigned char *table);
+
+MSymbol (*mflt_font_id) (struct _MFLTFont *font);
+
+int (*mflt_try_otf) (struct _MFLTFont *font, MFLTOtfSpec *spec,
+                    MFLTGlyphString *gstring, int from, int to);
+
 \f
 /* for debugging... */
 
 \f
 /* for debugging... */
 
@@ -2543,7 +3129,7 @@ dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
   prefix[indent] = 0;
 
   if (id >= 0)
   prefix[indent] = 0;
 
   if (id >= 0)
-    fprintf (stderr, "0x%02X", id);
+    fprintf (mdebug__output, "0x%02X", id);
   else if (id <= CMD_ID_OFFSET_INDEX)
     {
       int idx = CMD_ID_TO_INDEX (id);
   else if (id <= CMD_ID_OFFSET_INDEX)
     {
       int idx = CMD_ID_TO_INDEX (id);
@@ -2554,52 +3140,63 @@ dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
          FontLayoutCmdRule *rule = &cmd->body.rule;
          int i;
 
          FontLayoutCmdRule *rule = &cmd->body.rule;
          int i;
 
-         fprintf (stderr, "(rule ");
+         fprintf (mdebug__output, "(rule ");
          if (rule->src_type == SRC_REGEX)
          if (rule->src_type == SRC_REGEX)
-           fprintf (stderr, "\"%s\"", rule->src.re.pattern);
+           fprintf (mdebug__output, "\"%s\"", rule->src.re.pattern);
          else if (rule->src_type == SRC_INDEX)
          else if (rule->src_type == SRC_INDEX)
-           fprintf (stderr, "%d", rule->src.match_idx);
+           fprintf (mdebug__output, "%d", rule->src.match_idx);
          else if (rule->src_type == SRC_SEQ)
          else if (rule->src_type == SRC_SEQ)
-           fprintf (stderr, "(seq)");
+           fprintf (mdebug__output, "(seq)");
          else if (rule->src_type == SRC_RANGE)
          else if (rule->src_type == SRC_RANGE)
-           fprintf (stderr, "(range)");
+           fprintf (mdebug__output, "(range)");
          else
          else
-           fprintf (stderr, "(invalid src)");
+           fprintf (mdebug__output, "(invalid src)");
 
          for (i = 0; i < rule->n_cmds; i++)
            {
 
          for (i = 0; i < rule->n_cmds; i++)
            {
-             fprintf (stderr, "\n%s  ", prefix);
+             fprintf (mdebug__output, "\n%s  ", prefix);
              dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
            }
              dump_flt_cmd (stage, rule->cmd_ids[i], indent + 2);
            }
-         fprintf (stderr, ")");
+         fprintf (mdebug__output, ")");
        }
       else if (cmd->type == FontLayoutCmdTypeCond)
        {
          FontLayoutCmdCond *cond = &cmd->body.cond;
          int i;
 
        }
       else if (cmd->type == FontLayoutCmdTypeCond)
        {
          FontLayoutCmdCond *cond = &cmd->body.cond;
          int i;
 
-         fprintf (stderr, "(cond");
+         fprintf (mdebug__output, "(cond");
          for (i = 0; i < cond->n_cmds; i++)
            {
          for (i = 0; i < cond->n_cmds; i++)
            {
-             fprintf (stderr, "\n%s  ", prefix);
+             fprintf (mdebug__output, "\n%s  ", prefix);
              dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
            }
              dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
            }
-         fprintf (stderr, ")");
+         fprintf (mdebug__output, ")");
        }
       else if (cmd->type == FontLayoutCmdTypeOTF)
        {
        }
       else if (cmd->type == FontLayoutCmdTypeOTF)
        {
-         fprintf (stderr, "(otf)");
+         fprintf (mdebug__output, "(otf)");
        }
       else
        }
       else
-       fprintf (stderr, "(error-command)");
+       fprintf (mdebug__output, "(error-command)");
     }
   else if (id <= CMD_ID_OFFSET_COMBINING)
     }
   else if (id <= CMD_ID_OFFSET_COMBINING)
-    fprintf (stderr, "cominging-code");
+    fprintf (mdebug__output, "cominging-code");
   else
   else
-    fprintf (stderr, "(predefiend %d)", id);
+    fprintf (mdebug__output, "(predefiend %d)", id);
 }
 
 }
 
-void
+/***en
+    @brief Dump a Font Layout Table.
+
+    The mdebug_dump_flt () function prints the Font Layout Table $FLT
+    in a human readable way to the stderr or to what specified by the
+    environment variable MDEBUG_OUTPUT_FILE.  $INDENT specifies how
+    many columns to indent the lines but the first one.
+
+    @return
+    This function returns $FLT.  */
+
+MFLT *
 mdebug_dump_flt (MFLT *flt, int indent)
 {
   char *prefix = (char *) alloca (indent + 1);
 mdebug_dump_flt (MFLT *flt, int indent)
 {
   char *prefix = (char *) alloca (indent + 1);
@@ -2608,22 +3205,38 @@ mdebug_dump_flt (MFLT *flt, int indent)
 
   memset (prefix, 32, indent);
   prefix[indent] = 0;
 
   memset (prefix, 32, indent);
   prefix[indent] = 0;
-  fprintf (stderr, "(flt");
+  fprintf (mdebug__output, "(flt");
   MPLIST_DO (plist, flt->stages)
     {
       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
       int i;
 
   MPLIST_DO (plist, flt->stages)
     {
       FontLayoutStage *stage = (FontLayoutStage *) MPLIST_VAL (plist);
       int i;
 
-      fprintf (stderr, "\n%s  (stage %d", prefix, stage_idx);
+      fprintf (mdebug__output, "\n%s  (stage %d", prefix, stage_idx);
       for (i = 0; i < stage->used; i++)
        {
       for (i = 0; i < stage->used; i++)
        {
-         fprintf (stderr, "\n%s    ", prefix);
+         fprintf (mdebug__output, "\n%s    ", prefix);
          dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
        }
          dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
        }
-      fprintf (stderr, ")");
+      fprintf (mdebug__output, ")");
       stage_idx++;
     }
       stage_idx++;
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
+  return flt;
+}
+
+void
+mflt_dump_gstring (MFLTGlyphString *gstring)
+{
+  int i;
+
+  fprintf (mdebug__output, "(flt-gstring");
+  for (i = 0; i < gstring->used; i++)
+    {
+      MFLTGlyph *g = GREF (gstring, i);
+      fprintf (mdebug__output, "\n  (%02d pos:%d-%d c:%04X code:%04X cat:%c)",
+              i, g->from, g->to, g->c, g->code, GET_CATEGORY_CODE (g));
+    }
+  fprintf (mdebug__output, ")\n");
 }
 
 /*** @} */
 }
 
 /*** @} */