(mflt_enable_new_feature): Document it.
[m17n/m17n-lib.git] / src / m17n-flt.c
index 360ae20..76f0f99 100644 (file)
@@ -1,5 +1,5 @@
 /* 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
 
     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 ¤Ëµ­½Ò¤µ¤ì¤Æ¤¤¤ë¡£  */
+
 /*=*/
 
 #if !defined (FOR_DOXYGEN) || defined (DOXYGEN_INTERNAL_MODULE)
@@ -114,9 +122,9 @@ PREDEFIND-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,
-;; consume nothing.
+;; consume all glyphs in the current range.
 
 SCRIPT ::= OTF-TAG
 ;;     OTF's ScriptTag name (four letters) listed at:
@@ -236,7 +244,7 @@ MACRO-NAME ::= SYMBOL
 
 */
 
-static int mdebug_flag = MDEBUG_FONT_FLT;
+static int mdebug_flag = MDEBUG_FLT;
 
 MSymbol Mfont, Mlayouter, Mcombining;
 
@@ -247,18 +255,26 @@ static int flt_min_coverage, flt_max_coverage;
 
 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 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 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)
@@ -359,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))
 
-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)
@@ -368,13 +384,10 @@ static MSymbol Mcond, Mrange, Mfont_facility;
 
 #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
@@ -403,8 +416,11 @@ typedef struct
     struct {
       int from, to;
     } range;
-    int supported_glyph;
-    MFLTOtfSpec otf_spec;
+    struct {
+      int len;
+      MPlist *codes;
+      MFLTOtfSpec otf_spec;
+    } facility;
   } src;
 
   int n_cmds;
@@ -427,6 +443,7 @@ enum FontLayoutCmdType
     FontLayoutCmdTypeRule,
     FontLayoutCmdTypeCond,
     FontLayoutCmdTypeOTF,
+    FontLayoutCmdTypeOTFCategory,
     FontLayoutCmdTypeMAX
   };
 
@@ -440,9 +457,25 @@ typedef struct
   } 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 
 {
-  MCharTable *category;
+  FontLayoutCategory *category;
   int size, inc, used;
   FontLayoutCmd *cmds;
 } FontLayoutStage;
@@ -454,39 +487,83 @@ struct _MFLT
   MSymbol registry;
   MFLTOtfSpec otf;
   MDatabase *mdb;
-  MCharTable *coverage;
+  FontLayoutCategory *coverage;
   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);
 
+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 ) *
 */
 
-static MCharTable *
-load_category_table (MPlist *plist)
+static FontLayoutCategory *
+load_category_table (MPlist *plist, MFLTFont *font)
 {
+  FontLayoutCategory *category;
   MCharTable *table;
+  MPlist *feature_table_head = NULL;
+  int feature_table_size = 0;
+  MPlist *p;
+  int need_otf = 0;
 
   table = mchartable (Minteger, (void *) 0);
-
-  MPLIST_DO (plist, plist)
+  MPLIST_DO (p, plist)
     {
       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))
-       MERROR (MERROR_FONT, NULL);
+       MERROR_GOTO (MERROR_FLT, end);
       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))
@@ -494,14 +571,38 @@ load_category_table (MPlist *plist)
          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))
-           MERROR (MERROR_FONT, NULL);
+           MERROR_GOTO (MERROR_FLT, end);
          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);
@@ -509,19 +610,75 @@ load_category_table (MPlist *plist)
        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
-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++)
-    tag = (tag << 8) | *p;
+    tag = (tag << shift) | *p;
   for (; i < 4; i++)
-    tag = (tag << 8) | 0x20;
+    tag = (tag << shift) | 0x20;
   return tag;
 }
 
@@ -577,86 +734,101 @@ otf_store_features (char *p, char *end, unsigned *buf)
        {
          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
-       buf[i++] = gen_otf_tag (p), p += 5;
+       buf[i++] = gen_otf_tag (p, 8), p += 5;
     }
   buf[i] = 0;
 }
 
+/* SYMBOL's name       features[0]             for checking    for applying
+                              features[1]      GSUB     GPOS   GSUB    GPOS
+   -------------       ------------------      -------------   ------------
+   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=+            NULL          NULL        none & none     none  none
+   SCRIPT=F1           [F1,0]      [-1,0]          F1 & 1          F1  all
+   SCRIPT+F1           [-1][0]     [F1,0]           1 & 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,F2,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;
-  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;
-  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 == '/')
     {
-      langsys = gen_otf_tag (str);
+      spec->langsys = gen_otf_tag (str, 8);
       str += 4;
     }
   else
-    langsys = 0;
-  gsub = str;
+    spec->langsys = 0;
+  features[0] = str;
   if (*str != '=')
     /* Apply all GSUB features.  */
-      gsub_count = 1;
+      feature_count[0] = -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);
     }
-  gpos = str;
+  features[1] = str;
   if (*str != '+')
     /* Apply all GPOS features.  */
-    gpos_count = 1;
+    feature_count[1] = -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);
     }
-
-  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;
-       }
-      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;
 }
 
@@ -686,7 +858,8 @@ load_otf_command (FontLayoutCmd *cmd, MSymbol sym)
   result = parse_otf_command (sym, &cmd->body.otf);
   if (result == -2)
     return result;
-  cmd->type = FontLayoutCmdTypeOTF;
+  cmd->type = (name[4] == '?' ? FontLayoutCmdTypeOTFCategory
+              : FontLayoutCmdTypeOTF);
   return 0;
 }
 
@@ -812,6 +985,7 @@ load_command (FontLayoutStage *stage, MPlist *plist,
       /* PLIST ::= ( cond ... ) | ( STRING ... ) | ( INTEGER ... )
                   | ( ( INTEGER INTEGER ) ... )
                   | ( ( range INTEGER INTEGER ) ... )
+                  | ( ( SYMBOL STRING ) ... )
                   | ( ( font-facilty [ INTEGER ] ) ... )
                   | ( ( font-facilty OTF-SPEC ) ... )  */
       MPlist *elt = MPLIST_PLIST (plist);
@@ -917,7 +1091,7 @@ load_command (FontLayoutStage *stage, MPlist *plist,
            }
          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))
@@ -936,47 +1110,58 @@ load_command (FontLayoutStage *stage, MPlist *plist,
                        = (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);
-                     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
-                       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
@@ -1012,7 +1197,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[3] == 'f' && name[4] == '=')))
+                 && name[3] == 'f' && (name[4] == '=' || name[4] == '?'))))
        {
          result = load_otf_command (&cmd, sym);
          if (result < 0)
@@ -1092,7 +1277,8 @@ free_flt_command (FontLayoutCmd *cmd)
     }
   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]);
@@ -1150,7 +1336,7 @@ static int
 load_flt (MFLT *flt, MPlist *key_list)
 {
   MPlist *top, *plist, *pl, *p;
-  MCharTable *category = NULL;
+  FontLayoutCategory *category = NULL;
   MSymbol sym;
 
   if (key_list)
@@ -1227,18 +1413,23 @@ load_flt (MFLT *flt, MPlist *key_list)
       if (sym == Mcategory)
        {
          if (category)
-           M17N_OBJECT_UNREF (category);
+           unref_category_table (category);
          else if (flt->coverage)
            {
              category = flt->coverage;
+             ref_category_table (category);
              continue;
            }
-         category = load_category_table (pl);
+         category = load_category_table (pl, NULL);
+         if (! category)
+           goto err;
          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)
        {
@@ -1250,14 +1441,15 @@ load_flt (MFLT *flt, MPlist *key_list)
          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)
-    M17N_OBJECT_UNREF (category);
+    unref_category_table (category);
+ err:
   if (! MPLIST_TAIL_P (plist))
     {
       M17N_OBJECT_UNREF (top);
@@ -1270,14 +1462,17 @@ load_flt (MFLT *flt, MPlist *key_list)
 
 
 static void
-free_flt_stage (FontLayoutStage *stage)
+free_flt_stage (MFLT *flt, FontLayoutStage *stage)
 {
   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);
 }
 
@@ -1293,13 +1488,15 @@ free_flt_list ()
          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))
-               free_flt_stage (MPLIST_VAL (pl));
+               free_flt_stage (flt, MPLIST_VAL (pl));
              M17N_OBJECT_UNREF (flt->stages);
            }
+         free (flt);
+         MPLIST_VAL (plist) = NULL;
        }
       M17N_OBJECT_UNREF (flt_list);
     }
@@ -1337,17 +1534,17 @@ list_flt ()
        {
          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;
 
-             c = mchartable_min_char (flt->coverage);
+             c = mchartable_min_char (flt->coverage->table);
              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;
            }
@@ -1375,6 +1572,9 @@ typedef struct
   /* 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;
 
@@ -1397,6 +1597,8 @@ typedef struct
 } 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
 
@@ -1409,40 +1611,9 @@ run_rule (int depth,
   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;
@@ -1481,6 +1652,41 @@ run_rule (int depth,
          ctx->encoded[to - ctx->encoded_offset] = saved_code;
          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)
     {
@@ -1491,56 +1697,128 @@ run_rule (int depth,
        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.facility.len > 0)
+       {
+         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++;
+               }
+           }
+       }
 
-      if (rule->src.supported_glyph < 0)
+      if (MDEBUG_FLAG () > 2)
        {
-         if (from >= to)
-           return 0;
-         code = GREF (ctx->in, from)->code;
-         to = from + 1;
-         encoded = GREF (ctx->in, from)->encoded;
+         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);
        }
-      else
+      if (ctx->font->get_glyph_id (ctx->font, &gstring, 0,
+                                  rule->src.facility.len) < 0)
        {
-         code = rule->src.supported_glyph;
-         to = from;
-         encoded = 0;
+         MDEBUG_PRINT (") FAIL!");
+         return 0;
        }
-      if (! encoded)
+      if (rule->src_type == SRC_OTF_SPEC)
        {
-         static MFLTGlyphString gstring;
+         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;
@@ -1593,6 +1871,46 @@ run_cond (int depth,
   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)
@@ -1628,6 +1946,8 @@ run_otf (int depth,
                            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])
        {
@@ -1654,7 +1974,7 @@ run_otf (int depth,
                        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);
@@ -1662,14 +1982,26 @@ run_otf (int depth,
 
                        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))
-                             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;
+                           g->lbearing += aa->xoff;
+                           g->rbearing += aa->xoff;
+                           g->ascent -= aa->yoff;
+                           g->descent -= aa->yoff;
                          }
                      }
                    g->adjusted = 1;
@@ -1687,6 +2019,54 @@ run_otf (int depth,
   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 *
@@ -1729,6 +2109,8 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
   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.  */
@@ -1739,10 +2121,19 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
       GDUP (ctx, i);
       g = GREF (ctx->out, ctx->out->used - 1);
       g->c = g->code = ctx->code_offset + id;
-      SET_ENCODED (g, 0);
-      SET_MEASURED (g, 0);
       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++)
@@ -1754,7 +2145,8 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
          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 (")");
@@ -1775,6 +2167,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);
+      else if (cmd->type == FontLayoutCmdTypeOTFCategory)
+       to = try_otf (depth, &cmd->body.otf, from, to, ctx);
       return to;
     }
 
@@ -1799,13 +2193,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);
-       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
-             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);
@@ -1844,13 +2239,16 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
       {
        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;
-       SET_ENCODED (g, 0);
+       SET_ENCODED (g, 1);
        SET_MEASURED (g, 0);
+       SET_CATEGORY_CODE (g, ' ');
        return from;
       }
 
@@ -1885,6 +2283,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
   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;
@@ -1903,18 +2302,32 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       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->encoded_offset = from;
       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;
 
+         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)
            {
@@ -1949,12 +2362,12 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       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.   */
+      prev_category = ctx->stage->category;
       temp = ctx->in;
       ctx->in = ctx->out;
       if (buf.glyphs)
@@ -2001,8 +2414,8 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
 
          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)
@@ -2039,18 +2452,21 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
       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;
+         int base_width = base->rbearing - base->lbearing;
          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;
+                 int width = g->rbearing - g->lbearing;
                  int base_x, base_y, add_x, add_y, off_x, off_y;
 
                  if (base->from > g->from)
@@ -2065,8 +2481,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);
 
-                 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
@@ -2076,8 +2493,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;
-                 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)
@@ -2091,6 +2508,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
                {
                  base = g;
                  base_height = g->ascent + g->descent;
+                 base_width = g->rbearing - g->lbearing;
                }
            }
        }
@@ -2100,7 +2518,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
        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)
                  {
@@ -2158,14 +2576,67 @@ setup_combining_flt (MFLT *flt)
   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,
-                   setup_combining_coverage, flt->coverage);
+                   setup_combining_coverage, flt->coverage->table);
 }
 
 #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 */
 
@@ -2174,32 +2645,24 @@ int m17n__flt_initialized;
 \f
 /* External API */
 
-extern void m17n_init (void);
-extern void m17n_fini (void);
-
 /* The following two are actually not exposed to a user but concealed
    by the macro M17N_INIT (). */
 
 void
-m17n_init_flt (int with_shell)
+m17n_init_flt (void)
 {
   int mdebug_flag = MDEBUG_INIT;
 
   merror_code = MERROR_NONE;
-  if (m17n__flt_initialized)
-    {
-      m17n__flt_initialized++;
-      return;
-    }
+  if (m17n__flt_initialized++)
+    return;
   m17n_init_core ();
   if (merror_code != MERROR_NONE)
-    return;
-  if (with_shell)
     {
-      m17n_init ();
-      if (merror_code != MERROR_NONE)
-       return;
+      m17n__flt_initialized--;
+      return;
     }
+
   MDEBUG_PUSH_TIME ();
 
   Mcond = msymbol ("cond");
@@ -2208,16 +2671,21 @@ m17n_init_flt (int with_shell)
   Mlayouter = msymbol ("layouter");
   Mcombining = msymbol ("combining");
   Mfont_facility = msymbol ("font-facility");
+  Mequal = msymbol ("=");
   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 ();
-  m17n__flt_initialized++;
 }
 
 void
-m17n_fini_flt (int with_shell)
+m17n_fini_flt (void)
 {
   int mdebug_flag = MDEBUG_FINI;
 
@@ -2227,10 +2695,8 @@ m17n_fini_flt (int with_shell)
 
   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 ();
-  if (with_shell)
-    m17n_fini ();
   m17n_fini_core ();
 }
 
@@ -2243,26 +2709,39 @@ m17n_fini_flt (int with_shell)
 
 /*=*/
 /***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 a FLT object whose name is $NAME.
+    The mflt_get () function returns an FLT object whose name is $NAME.
 
     @return
-    If the operation was successfully, mflt_get () returns a pointer
-    to a FLT object.  Otherwise, it returns @c NULL.  */
+    If the operation was successful, mflt_get () returns a pointer
+    to the found FLT object.  Otherwise, it returns @c NULL.  */
+
+/***ja
+    @brief »ØÄꤵ¤ì¤¿Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹.
+
+    ´Ø¿ô mflt_get () ¤Ï¡¢$NAME ¤È¤¤¤¦Ì¾Á°¤ò»ý¤Ä FLT ¥ª¥Ö¥¸¥§¥¯¥È¤òÊÖ¤¹¡£
+
+    @return
+    ¤â¤·À®¸ù¤¹¤ì¤Ð¡¢mflt_get () ¤Ï¸«¤Ä¤«¤Ã¤¿ FLT
+    ¥ª¥Ö¥¸¥§¥¯¥È¤Ø¤Î¥Ý¥¤¥ó¥¿¤òÊÖ¤¹¡£¼ºÇÔ¤·¤¿¾ì¹ç¤Ï @c NULL ¤òÊÖ¤¹¡£  */
 
 MFLT *
 mflt_get (MSymbol name)
 {
   MFLT *flt;
+  MPlist *plist;
 
   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
-      && ! mchartable_lookup (flt->coverage, 0))
+      && ! mchartable_lookup (flt->coverage->table, 0))
     setup_combining_flt (flt);
 
   return flt;
@@ -2270,19 +2749,29 @@ mflt_get (MSymbol name)
 
 /*=*/
 /***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
-    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
-    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)
 {
-  MPlist *plist;
+  MPlist *plist, *pl;
   MFLT *flt;
   static MSymbol unicode_bmp = NULL, unicode_full = NULL;
 
@@ -2294,20 +2783,27 @@ mflt_find (int c, MFLTFont *font)
 
   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;
 
-      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->name == Mcombining
+             && ! mchartable_lookup (flt->coverage->table, 0))
+           setup_combining_flt (flt);
          if (c >= 0
-             && ! mchartable_lookup (flt->coverage, c))
+             && ! mchartable_lookup (flt->coverage->table, c))
            continue;
          if (flt->otf.sym)
            {
@@ -2321,30 +2817,45 @@ mflt_find (int c, MFLTFont *font)
                }
              else if (! font->check_otf (font, spec))
                continue;
-             return flt;
+             goto found;
            }
          best = flt;
        }
-      return best;
+      if (best == NULL)
+       return NULL;
+      flt = best;
+      goto found;
     }
   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;
+
+ 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
-    @brief Return a name of a FLT.
+    @brief Return the name of an FLT.
 
     The mflt_name () function returns the name of $FLT.  */
 
+/***ja
+    @brief FLT ¤Î̾Á°¤òÊÖ¤¹.
+
+    ´Ø¿ô mflt_name () ¤Ï $FLT ¤Î̾Á°¤òÊÖ¤¹¡£  */
+
 const char *
 mflt_name (MFLT *flt)
 {
@@ -2356,34 +2867,60 @@ mflt_name (MFLT *flt)
     @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)
 {
-  return flt->coverage;
+  return flt->coverage->table;
 }
 
 /*=*/
 /***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
-    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
-    $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.  */
 
+/***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)
@@ -2395,6 +2932,7 @@ mflt_run (MFLTGlyphString *gstring, int from, int 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;
@@ -2406,9 +2944,12 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
   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;
     }
 
@@ -2417,7 +2958,8 @@ mflt_run (MFLTGlyphString *gstring, int from, int 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
@@ -2439,7 +2981,7 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
            {
              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;
@@ -2467,9 +3009,18 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
 
       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++)
-       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 ())
        {
@@ -2510,14 +3061,24 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
        {
          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);
@@ -2546,6 +3107,25 @@ mflt_run (MFLTGlyphString *gstring, int from, int to,
   return to;
 }
 
+/***en
+    @brief Flag to control several new OTF handling commands.
+
+    If the variable mflt_enable_new_feature is nonzero, the function
+    #mflt_run () can drive a Font Layout Table that contains the new
+    OTF-related commands ":otf?" and/or OTF feature specification in a
+    category table.  */
+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... */
 
@@ -2558,7 +3138,7 @@ dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
   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);
@@ -2569,52 +3149,63 @@ dump_flt_cmd (FontLayoutStage *stage, int id, int indent)
          FontLayoutCmdRule *rule = &cmd->body.rule;
          int i;
 
-         fprintf (stderr, "(rule ");
+         fprintf (mdebug__output, "(rule ");
          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)
-           fprintf (stderr, "%d", rule->src.match_idx);
+           fprintf (mdebug__output, "%d", rule->src.match_idx);
          else if (rule->src_type == SRC_SEQ)
-           fprintf (stderr, "(seq)");
+           fprintf (mdebug__output, "(seq)");
          else if (rule->src_type == SRC_RANGE)
-           fprintf (stderr, "(range)");
+           fprintf (mdebug__output, "(range)");
          else
-           fprintf (stderr, "(invalid src)");
+           fprintf (mdebug__output, "(invalid src)");
 
          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);
            }
-         fprintf (stderr, ")");
+         fprintf (mdebug__output, ")");
        }
       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++)
            {
-             fprintf (stderr, "\n%s  ", prefix);
+             fprintf (mdebug__output, "\n%s  ", prefix);
              dump_flt_cmd (stage, cond->cmd_ids[i], indent + 2);
            }
-         fprintf (stderr, ")");
+         fprintf (mdebug__output, ")");
        }
       else if (cmd->type == FontLayoutCmdTypeOTF)
        {
-         fprintf (stderr, "(otf)");
+         fprintf (mdebug__output, "(otf)");
        }
       else
-       fprintf (stderr, "(error-command)");
+       fprintf (mdebug__output, "(error-command)");
     }
   else if (id <= CMD_ID_OFFSET_COMBINING)
-    fprintf (stderr, "cominging-code");
+    fprintf (mdebug__output, "cominging-code");
   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);
@@ -2623,22 +3214,45 @@ mdebug_dump_flt (MFLT *flt, int indent)
 
   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;
 
-      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++)
        {
-         fprintf (stderr, "\n%s    ", prefix);
+         fprintf (mdebug__output, "\n%s    ", prefix);
          dump_flt_cmd (stage, INDEX_TO_CMD_ID (i), indent + 4);
        }
-      fprintf (stderr, ")");
+      fprintf (mdebug__output, ")");
       stage_idx++;
     }
-  fprintf (stderr, ")");
+  fprintf (mdebug__output, ")");
+  return flt;
+}
+
+/***en
+    @brief Dump an MFLTGlyphString.
+
+    The mflt_dump_gstring () function prints the glyph sequence
+    $GSTRING in a human readable way to the stderr or to what
+    specified by the environment variable MDEBUG_OUTPUT_FILE.  */
+
+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");
 }
 
 /*** @} */