(mflt_dump_gstring): Document it.
[m17n/m17n-lib.git] / src / m17n-flt.c
index 0ca5d80..ec3fbcc 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
 
@@ -122,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:
@@ -255,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)
@@ -435,6 +443,7 @@ enum FontLayoutCmdType
     FontLayoutCmdTypeRule,
     FontLayoutCmdTypeCond,
     FontLayoutCmdTypeOTF,
     FontLayoutCmdTypeRule,
     FontLayoutCmdTypeCond,
     FontLayoutCmdTypeOTF,
+    FontLayoutCmdTypeOTFCategory,
     FontLayoutCmdTypeMAX
   };
 
     FontLayoutCmdTypeMAX
   };
 
@@ -450,7 +459,15 @@ typedef struct
 
 typedef struct
 {
 
 typedef struct
 {
+  int size;
+  unsigned int *tag;
+  char *code;
+} FeatureCodeTable;
+
+typedef struct
+{
   MCharTable *table;
   MCharTable *table;
+  FeatureCodeTable feature_table;
   /* Non-null if the table must be re-configured by OTF specs included
      in the definition.  */
   MPlist *definition;
   /* Non-null if the table must be re-configured by OTF specs included
      in the definition.  */
   MPlist *definition;
@@ -499,6 +516,8 @@ apply_otf_feature (MFLTFont *font, MFLTOtfSpec *spec,
       mchartable_set (table, from + i, (void *) category);
 }
 
       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 ) *
 */
@@ -508,6 +527,8 @@ load_category_table (MPlist *plist, MFLTFont *font)
 {
   FontLayoutCategory *category;
   MCharTable *table;
 {
   FontLayoutCategory *category;
   MCharTable *table;
+  MPlist *feature_table_head = NULL;
+  int feature_table_size = 0;
   MPlist *p;
   int need_otf = 0;
 
   MPlist *p;
   int need_otf = 0;
 
@@ -517,15 +538,32 @@ load_category_table (MPlist *plist, MFLTFont *font)
       MPlist *elt;
       int from, to, category_code;
 
       MPlist *elt;
       int from, to, category_code;
 
-      if (! MPLIST_PLIST (p))
-       MERROR_GOTO (MERROR_FONT, end);
+      if (! MPLIST_PLIST_P (p))
+       MERROR_GOTO (MERROR_FLT, end);
       elt = MPLIST_PLIST (p);
       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_GOTO (MERROR_FONT, end);
+       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_GOTO (MERROR_FONT, end);
+       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))
@@ -535,17 +573,22 @@ load_category_table (MPlist *plist, MFLTFont *font)
        }
       else if (MPLIST_SYMBOL_P (elt))
        {
        }
       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)
          if (font)
            {
              MFLTOtfSpec spec;
              if (parse_otf_command (MPLIST_SYMBOL (elt), &spec) < 0)
-               MERROR_GOTO (MERROR_FONT, end);
+               MERROR_GOTO (MERROR_FLT, end);
              elt = MPLIST_NEXT (elt);
              if (! MPLIST_INTEGER_P (elt))
              elt = MPLIST_NEXT (elt);
              if (! MPLIST_INTEGER_P (elt))
-               MERROR_GOTO (MERROR_FONT, end);
+               MERROR_GOTO (MERROR_FLT, end);
              category_code = MPLIST_INTEGER (elt);
              if (! isalnum (category_code))
              category_code = MPLIST_INTEGER (elt);
              if (! isalnum (category_code))
-               MERROR_GOTO (MERROR_FONT, end);
+               MERROR_GOTO (MERROR_FLT, end);
              apply_otf_feature (font, &spec, from, to, table, category_code);
            }
          else
              apply_otf_feature (font, &spec, from, to, table, category_code);
            }
          else
@@ -555,11 +598,11 @@ load_category_table (MPlist *plist, MFLTFont *font)
       else
        {
          if (! MPLIST_INTEGER_P (elt))
       else
        {
          if (! MPLIST_INTEGER_P (elt))
-           MERROR_GOTO (MERROR_FONT, end);
+           MERROR_GOTO (MERROR_FLT, end);
          category_code = MPLIST_INTEGER (elt);
        }
       if (! isalnum (category_code))
          category_code = MPLIST_INTEGER (elt);
        }
       if (! isalnum (category_code))
-       MERROR_GOTO (MERROR_FONT, end);
+       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);
@@ -568,7 +611,7 @@ load_category_table (MPlist *plist, MFLTFont *font)
     }
 
  end:
     }
 
  end:
-  category = malloc (sizeof (FontLayoutCategory));
+  category = calloc (1, sizeof (FontLayoutCategory));
   category->table = table;
   if (need_otf)
     {
   category->table = table;
   if (need_otf)
     {
@@ -577,6 +620,33 @@ load_category_table (MPlist *plist, MFLTFont *font)
     }
   else
     category->definition = NULL;
     }
   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;
 }
 
   return category;
 }
 
@@ -590,20 +660,25 @@ unref_category_table (FontLayoutCategory *category)
     {
       if (category->definition)
        M17N_OBJECT_UNREF (category->definition);
     {
       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
       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++)
 {
   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;
 }
 
@@ -659,86 +734,101 @@ 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]             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;
 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;
 }
 
@@ -768,7 +858,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;
 }
 
@@ -894,6 +985,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);
@@ -1105,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[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)
@@ -1185,7 +1277,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]);
@@ -1328,6 +1421,8 @@ load_flt (MFLT *flt, MPlist *key_list)
              continue;
            }
          category = load_category_table (pl, NULL);
              continue;
            }
          category = load_category_table (pl, NULL);
+         if (! category)
+           goto err;
          if (! flt->coverage)
            {
              flt->coverage = category;
          if (! flt->coverage)
            {
              flt->coverage = category;
@@ -1354,7 +1449,7 @@ load_flt (MFLT *flt, MPlist *key_list)
     }
   if (category)
     unref_category_table (category);
     }
   if (category)
     unref_category_table (category);
-
+ err:
   if (! MPLIST_TAIL_P (plist))
     {
       M17N_OBJECT_UNREF (top);
   if (! MPLIST_TAIL_P (plist))
     {
       M17N_OBJECT_UNREF (top);
@@ -1477,6 +1572,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;
 
@@ -1499,6 +1597,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
 
@@ -1562,7 +1662,7 @@ run_rule (int depth,
       if (len > (to - from))
        return 0;
       for (i = 0; i < len; i++)
       if (len > (to - from))
        return 0;
       for (i = 0; i < len; i++)
-       if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->code)
+       if (rule->src.seq.codes[i] != GREF (ctx->in, from + i)->c)
          break;
       if (i < len)
        return 0;
          break;
       if (i < len)
        return 0;
@@ -1578,7 +1678,7 @@ run_rule (int depth,
 
       if (from >= to)
        return 0;
 
       if (from >= to)
        return 0;
-      head = GREF (ctx->in, from)->code;
+      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;
       if (head < rule->src.range.from || head > rule->src.range.to)
        return 0;
       ctx->code_offset = head - rule->src.range.from;
@@ -1632,12 +1732,12 @@ run_rule (int depth,
            {
              if (MPLIST_INTEGER_P (p))
                {
            {
              if (MPLIST_INTEGER_P (p))
                {
-                 GREF (&gstring, i)->code = MPLIST_INTEGER (p);
+                 GREF (&gstring, i)->c = MPLIST_INTEGER (p);
                  GREF (&gstring, i)->encoded = 0;
                }
              else
                {
                  GREF (&gstring, i)->encoded = 0;
                }
              else
                {
-                 GREF (&gstring, i)->code = GREF (ctx->in, idx)->code;
+                 GREF (&gstring, i)->c = GREF (ctx->in, idx)->code;
                  GREF (&gstring, i)->encoded = GREF (ctx->in, idx)->encoded;
                  idx++;
                }
                  GREF (&gstring, i)->encoded = GREF (ctx->in, idx)->encoded;
                  idx++;
                }
@@ -1771,6 +1871,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)
@@ -1806,6 +1946,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])
        {
@@ -1840,6 +1982,10 @@ 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;
                        while (aa->back > 0)
                          {
                            for (j = 0; j < aa->back;
@@ -1858,7 +2004,6 @@ run_otf (int depth,
                            g->descent -= aa->yoff;
                          }
                      }
                            g->descent -= aa->yoff;
                          }
                      }
-                   SET_COMBINING_CODE (g, ctx, 0);
                    g->adjusted = 1;
                  }
            }
                    g->adjusted = 1;
                  }
            }
@@ -1874,6 +2019,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 *
@@ -1916,6 +2109,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.  */
@@ -1926,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;
       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);
       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++)
@@ -1963,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);
        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;
     }
 
@@ -1994,7 +2200,7 @@ run_command (int depth, int id, int from, int to, FontLayoutContext *ctx)
            if (g->c < 0)
              MDEBUG_PRINT2 ("\n [FLT] %*s(COPY |)", depth, "");
            else
            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);
@@ -2033,13 +2239,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;
       }
 
@@ -2074,6 +2283,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;
@@ -2093,17 +2303,31 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
 
       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
       table = ctx->stage->category->table;
 
       ctx->stage = (FontLayoutStage *) MPLIST_VAL (stages);
       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);
       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)
            {
          ctx->encoded[i - from] = enc;
          if (! enc && stage_idx == 0)
            {
@@ -2138,12 +2362,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)
@@ -2228,7 +2452,7 @@ 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;
@@ -2238,6 +2462,7 @@ run_stages (MFLTGlyphString *gstring, int from, int to,
          for (i = 1; i < ctx->out->used; i++)
            {
              if ((g = GREF (ctx->out, i))
          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;
@@ -2293,7 +2518,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)
                  {
@@ -2450,10 +2675,12 @@ m17n_init_flt (void)
   Mgenerator = msymbol ("generator");
   Mend = msymbol ("end");
 
   Mgenerator = msymbol ("generator");
   Mend = msymbol ("end");
 
+  mflt_enable_new_feature = 0;
   mflt_iterate_otf_feature = NULL;
   mflt_font_id = NULL;
   mflt_iterate_otf_feature = NULL;
   mflt_font_id = NULL;
+  mflt_try_otf = NULL;
 
 
-  MDEBUG_PRINT_TIME ("INIT", (stderr, " to initialize the flt modules."));
+  MDEBUG_PRINT_TIME ("INIT", (mdebug__output, " to initialize the flt modules."));
   MDEBUG_POP_TIME ();
 }
 
   MDEBUG_POP_TIME ();
 }
 
@@ -2468,7 +2695,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 ();
 }
@@ -2594,6 +2821,8 @@ mflt_find (int c, MFLTFont *font)
            }
          best = flt;
        }
            }
          best = flt;
        }
+      if (best == NULL)
+       return NULL;
       flt = best;
       goto found;
     }
       flt = best;
       goto found;
     }
@@ -2611,7 +2840,7 @@ mflt_find (int c, MFLTFont *font)
  found:
   if (! CHECK_FLT_STAGES (flt))
     return NULL;
  found:
   if (! CHECK_FLT_STAGES (flt))
     return NULL;
-  if (font && flt->need_config)
+  if (font && flt->need_config && mflt_font_id)
     flt = configure_flt (flt, font, mflt_font_id (font));
   return flt;
 }
     flt = configure_flt (flt, font, mflt_font_id (font));
   return flt;
 }
@@ -2780,13 +3009,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)
+      if (flt->need_config && font_id != Mnil)
        flt = configure_flt (flt, font, font_id);
 
       for (; this_to < to; this_to++)
        flt = configure_flt (flt, font, font_id);
 
       for (; this_to < to; this_to++)
-       if (! mchartable_lookup (flt->coverage->table,
-                                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 ())
        {
@@ -2827,14 +3061,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);
@@ -2863,6 +3107,8 @@ 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,
 int (*mflt_iterate_otf_feature) (struct _MFLTFont *font,
                                 MFLTOtfSpec *spec,
                                 int from, int to,
@@ -2870,6 +3116,9 @@ int (*mflt_iterate_otf_feature) (struct _MFLTFont *font,
 
 MSymbol (*mflt_font_id) (struct _MFLTFont *font);
 
 
 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... */
 
@@ -2882,7 +3131,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);
@@ -2893,57 +3142,58 @@ 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);
 }
 
 /***en
     @brief Dump a Font Layout Table.
 
     The mdebug_dump_flt () function prints the Font Layout Table $FLT
 }
 
 /***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.  $INDENT specifies how many
-    columns to indent the lines but the first one.
+    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.  */
 
     @return
     This function returns $FLT.  */
@@ -2957,25 +3207,47 @@ 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;
 }
 
   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");
+}
+
 /*** @} */
 
 /*
 /*** @} */
 
 /*