*** empty log message ***
[m17n/m17n-test.git] / flt.c
diff --git a/flt.c b/flt.c
index ae17b48..5cf2479 100644 (file)
--- a/flt.c
+++ b/flt.c
@@ -9,49 +9,82 @@
 #if defined (FLT_GUI)
 
 #include <m17n-gui.h>
+#include <m17n-misc.h>
 #define PROGNAME "flt-gui"
 
 #elif defined (FLT_OTF)
 
 #include <m17n-flt.h>
+#include <m17n-misc.h>
 #include <otf.h>
 #define PROGNAME "flt-otf"
 
-#else  /* defined (FLT_HB) */
+#elif defined (FLT_HB)
 
 #include <m17n-flt.h>
 #include <harfbuzz.h>
 #define PROGNAME "flt-hb"
 
+#else  /* (defined (FLT_PANGO)) */
+
+#include <m17n-flt.h>
+#include <m17n-misc.h>
+#define PANGO_ENABLE_ENGINE
+#define PANGO_ENABLE_BACKEND
+#include <pango/pango.h>
+#include <pango/pango-ot.h>
+#include <pango/pangoft2.h>
+#include <pango/pangofc-font.h>
 #endif
 
 static FT_Library ft_library;
 
-FT_Face
-new_face (char *fontname, char **err)
+int
+parse_font_name (char *fontname, char **family, int *size, char **err)
 {
   FcPattern *pat;
-  FcChar8 *fam, *file;
+  FcChar8 *fam;
   double pixelsize;
-  FcFontSet *fs;
-  FcObjectSet *os;
-  FT_Face face;
 
   pat = FcNameParse ((FcChar8 *) fontname);
   if (! pat)
     {
       *err = "invalid name format";
-      return NULL;
+      return -1;
     }
   if (FcPatternGetString (pat, FC_FAMILY, 0, &fam) != FcResultMatch)
     {
+      FcPatternDestroy (pat);
       *err = "no family name";
-      return NULL;
+      return -1;
     }
   if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &pixelsize) != FcResultMatch)
     pixelsize = 20;
-  FcPatternAddBool (pat, FC_SCALABLE, FcTrue);
-  FcPatternDel (pat, FC_PIXEL_SIZE);
+  *family = strdup ((char *) fam);
+  *size = pixelsize;
+  FcPatternDestroy (pat);
+  return 0;
+}
+
+FT_Face
+new_face (char *fontname, char **err)
+{
+  FcPattern *pat;
+  FcChar8 *fam, *file;
+  int size;
+  double pixelsize;
+  FcFontSet *fs;
+  FcObjectSet *os;
+  FT_Face face;
+
+  if (parse_font_name (fontname, (char **) &fam, &size, err) < 0)
+    return NULL;
+  pixelsize = size;
+  pat = FcPatternBuild (0,
+                       FC_FAMILY, FcTypeString, fam,
+                       FC_SCALABLE, FcTypeBool, FcTrue,
+                       NULL);
+  free (fam);
   os = FcObjectSetBuild (FC_FILE, NULL);
   fs = FcFontList (NULL, pat, os);
   if (fs->nfont == 0)
@@ -83,11 +116,23 @@ new_face (char *fontname, char **err)
 
 #ifdef FLT_GUI
 
-typedef struct
+typedef struct _MFLTFont MFLTFont;
+
+struct _MFLTFont
 {
+  int x_ppem, y_ppem;
+  void (*get_glyph_id) (void);
+  void (*get_metrics) (void);
+  void (*suitable_p) (void);
+  void (*drive_otf) (void);
   MFont *font;
   FT_Face face;
-} MFLTFont;
+};
+
+void get_glyph_id (void) {}
+void get_metrics (void) {}
+void suitable_p (void) {}
+void drive_otf (void) {}
 
 MFrame *frame;
 
@@ -127,15 +172,7 @@ close_font (MFLTFont *font)
   m17n_object_unref (frame);
 }
 
-int
-flt (MText *mt, int from, int to, MFLTFont *font, char *flt_name)
-{
-  mtext_put_prop (mt, from, to, Mfont, font->font);
-  mdraw_text_extents (frame, mt, from, to, NULL, NULL, NULL, NULL);
-  return 0;
-}
-
-#else  /* FLT_OTF or FLT_HB */
+#elif defined (FLT_OTF) || defined (FLT_HB)
 
 typedef struct {
   MFLTFont font;
@@ -143,55 +180,50 @@ typedef struct {
 } FontInfo;
 
 int
-get_glyph_id (MFLTFont *font, MFLTGlyph *g)
+get_glyph_id (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
 {
   FT_Face face = ((FontInfo *) font)->face;
 
-  g->code = FT_Get_Char_Index (face, g->code);
+  for (; from < to; from++)
+    {
+      MFLTGlyph *g = gstring->glyphs + from;
 
-  return (g->code ? 0 : -1);
+      if (! g->encoded
+         && ! (g->code = FT_Get_Char_Index (face, g->code)))
+       return -1;
+      g->encoded = 1;
+    }
+  return 0;
 }
 
 int 
-get_metric (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
+get_metrics (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
 {
   FT_Face face = ((FontInfo *) font)->face;
 
   for (; from < to; from++)
     {
       MFLTGlyph *g = gstring->glyphs + from;
-      FT_Glyph_Metrics *metrics;
 
-      if (FT_Load_Glyph (face, g->code, FT_LOAD_DEFAULT))
-       return -1;
-      metrics = &face->glyph->metrics;
-      g->lbearing = metrics->horiBearingX;
-      g->rbearing = metrics->horiBearingX + metrics->width;
-      g->xadv = metrics->horiAdvance;
-      g->yadv = metrics->vertAdvance;
-      g->ascent = metrics->horiBearingY;
-      g->descent = metrics->height - metrics->horiBearingY;
+      if (! g->measured)
+       {
+         FT_Glyph_Metrics *metrics;
+
+         if (FT_Load_Glyph (face, g->code, FT_LOAD_DEFAULT))
+           return -1;
+         metrics = &face->glyph->metrics;
+         g->lbearing = metrics->horiBearingX;
+         g->rbearing = metrics->horiBearingX + metrics->width;
+         g->xadv = metrics->horiAdvance;
+         g->yadv = metrics->vertAdvance;
+         g->ascent = metrics->horiBearingY;
+         g->descent = metrics->height - metrics->horiBearingY;
+         g->measured = 1;
+       }
     }
   return 0;
 }
 
-int
-flt (MText *mt, int from, int to, MFLTFont *font, char *flt_name)
-{
-  MFLTGlyphString gstring;
-  int len = to - from;
-  int i;
-
-  memset (&gstring, 0, sizeof (MFLTGlyphString));
-  gstring.glyph_size = sizeof (MFLTGlyph);
-  gstring.allocated = len * 2;
-  gstring.glyphs = alloca (sizeof (MFLTGlyph) * gstring.allocated);
-  for (i = 0; i < len; i++)
-    gstring.glyphs[i].c = mtext_ref_char (mt, from + i);
-  gstring.used = len;
-  return mflt_run (&gstring, 0, len, font, msymbol (flt_name));
-}
-
 #ifdef FLT_OTF
 
 typedef struct {
@@ -272,7 +304,7 @@ encode_features (char *str, int count, unsigned *features)
 }
 
 int
-drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
+drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
           MFLTGlyphString *in, int from, int to,
           MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
 {
@@ -345,10 +377,21 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
       for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, otfg++)
        {
          MFLTGlyph *g = out->glyphs + out->used;
+         int j;
 
          *g = in->glyphs[from + otfg->f.index.from];
-         g->c = otfg->c;
-         g->code = otfg->glyph_id;
+         g->c = 0;
+         for (j = from + otfg->f.index.from; j <= from + otfg->f.index.to; j++)
+           if (in->glyphs[j].code == otfg->glyph_id)
+             {
+               g->c = in->glyphs[j].c;
+               break;
+             }
+         if (g->code != otfg->glyph_id)
+           {
+             g->code = otfg->glyph_id;
+             g->measured = 0;
+           }
          out->used++;
        }
     }
@@ -360,8 +403,6 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
        out->glyphs[out->used++] = in->glyphs[from + i];
     }
 
-  font->get_metric (font, out, gidx, out->used);
-
   if (gpos_features)
     {
       FT_Face face;
@@ -415,7 +456,7 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
                  adjustment[i].yadv
                    += otfg->f.f1.value->YAdvance * y_scale / 0x10000;
                if (format & OTF_YAdvDevice)
-                 adjustment[i].xadv
+                 adjustment[i].yadv
                    += DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem);
                adjustment[i].set = 1;
              }
@@ -466,7 +507,7 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
   return to;
 
  simple_copy:
-  font->get_metric (font, in, from, to);
+  font->get_metrics (font, in, from, to);
   for (i = 0; i < len; i++)
     {
       MFLTGlyph *g = in->glyphs + (from + i);
@@ -487,9 +528,6 @@ open_font (char *fontname, char **err)
   if (! face)
     return NULL;
   font_info = malloc (sizeof (FontInfoOTF));
-  font_info->font.get_glyph_id = get_glyph_id;
-  font_info->font.get_metric = get_metric;
-  font_info->font.drive_otf = drive_otf;
   font_info->face = face;
   font_info->otf = OTF_open_ft_face (face);
   return ((MFLTFont *) font_info);
@@ -690,8 +728,8 @@ const HB_FontClass hb_fontClass = {
     getPointInOutline, getGlyphMetrics, getFontMetric
 };
 
-HB_Error
-setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec,
+void
+setup_features (int gsubp, FontInfoHB *font_info, MFLTOtfSpec *spec,
                FeatureInfo *info)
 {
   int count, preordered;
@@ -700,24 +738,23 @@ setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec,
   FT_UShort req_feature, index;
   FT_UInt *feature_list;
   int i, j, k;
-  HB_Error err;
 
   if (gsubp)
     {
       if (! font_info->gsub)
        {
-         if ((err = load_gsub (font_info)) != HB_Err_Ok)
-           return err;
+         if (load_gsub (font_info) != HB_Err_Ok)
+           return;
        }
-      if ((err = HB_GSUB_Select_Script (font_info->gsub, spec->script, &script))
+      if (HB_GSUB_Select_Script (font_info->gsub, spec->script, &script)
          != HB_Err_Ok)
-       return err;
+       return;
       if (spec->langsys)
        {
-         if ((err = HB_GSUB_Select_Language (font_info->gsub, spec->langsys,
-                                             script, &langsys, &req_feature))
+         if (HB_GSUB_Select_Language (font_info->gsub, spec->langsys,
+                                      script, &langsys, &req_feature)
              != HB_Err_Ok)
-           return err;
+           return;
        }
       else
        langsys = req_feature = 0xFFFF;
@@ -728,18 +765,18 @@ setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec,
     {
       if (! font_info->gpos)
        {
-         if ((err = load_gpos (font_info)) != HB_Err_Ok)
-           return err;
+         if (load_gpos (font_info) != HB_Err_Ok)
+           return;
        }
-      if ((err = HB_GPOS_Select_Script (font_info->gpos, spec->script, &script))
+      if (HB_GPOS_Select_Script (font_info->gpos, spec->script, &script)
          != HB_Err_Ok)
-       return err;
+       return;
       if (spec->langsys)
        {
-         if ((err = HB_GPOS_Select_Language (font_info->gpos, spec->langsys,
-                                             script, &langsys, &req_feature))
+         if (HB_GPOS_Select_Language (font_info->gpos, spec->langsys,
+                                      script, &langsys, &req_feature)
              != HB_Err_Ok)
-           return err;
+           return;
        }
       else
        langsys = req_feature = 0xFFFF;
@@ -750,13 +787,12 @@ setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec,
   for (preordered = 0; preordered < count; preordered++)
     if (features[preordered] == 0)
       {
-       if ((err = (gsubp
-                   ? HB_GSUB_Query_Features (font_info->gsub, script, langsys,
-                                             &feature_list)
-                   : HB_GPOS_Query_Features (font_info->gpos, script, langsys,
-                                             &feature_list)))
+       if ((gsubp ? HB_GSUB_Query_Features (font_info->gsub, script, langsys,
+                                            &feature_list)
+            : HB_GPOS_Query_Features (font_info->gpos, script, langsys,
+                                      &feature_list))
            != HB_Err_Ok)
-         return err;
+         return;
        break;
       }
   if (feature_list)
@@ -768,11 +804,10 @@ setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec,
   if (req_feature != 0xFFFF)
     info->indices[i++] = req_feature;
   for (j = 0; j < preordered; j++)
-    if ((err = (gsubp
-               ? HB_GSUB_Select_Feature (font_info->gsub, features[j],
-                                         script, langsys, &index)
-               : HB_GPOS_Select_Feature (font_info->gpos, features[j],
-                                         script, langsys, &index)))
+    if ((gsubp ? HB_GSUB_Select_Feature (font_info->gsub, features[j],
+                                        script, langsys, &index)
+        : HB_GPOS_Select_Feature (font_info->gpos, features[j],
+                                  script, langsys, &index))
        == HB_Err_Ok)
       info->indices[i++] = index;
   if (feature_list)
@@ -787,15 +822,14 @@ setup_features (int gsubp, FontInfoHB *font_info, MFLT_OTF_Spec *spec,
                                           script, langsys, &index)
                 : HB_GPOS_Select_Feature (font_info->gpos, feature_list[j],
                                           script, langsys, &index))
-               == FT_Err_Ok))
+               == HB_Err_Ok))
          info->indices[i++] = index;
       }
   info->count = i;
-  return HB_Err_Ok;
 }
 
 GsubGposInfo *
-setup_otf_spec (MFLTFont *font, MFLT_OTF_Spec *spec)
+setup_otf_spec (MFLTFont *font, MFLTOtfSpec *spec)
 {
   FontInfoHB *font_info = (FontInfoHB *) font;
   GsubGposInfo *ginfo;
@@ -807,26 +841,23 @@ setup_otf_spec (MFLTFont *font, MFLT_OTF_Spec *spec)
     return (ginfo->gsub.count + ginfo->gpos.count > 0 ? ginfo : NULL);
   ginfo = calloc (1, sizeof (GsubGposInfo));
   mplist_push (font_info->otf_spec_cache, spec->sym, ginfo);
-  if (setup_features (1, font_info, spec, &(ginfo->gsub)) != HB_Err_Ok)
-    return NULL;
-  if (setup_features (0, font_info, spec, &(ginfo->gpos)) != HB_Err_Ok)
-    return NULL;
+  setup_features (1, font_info, spec, &(ginfo->gsub));
+  setup_features (0, font_info, spec, &(ginfo->gpos));
   return ginfo;
 }
 
 int
-drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
+drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
           MFLTGlyphString *in, int from, int to,
           MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
 {
   FontInfoHB *font_info = (FontInfoHB *) font;
   int len = to - from;
-  int i, gidx;
+  int i;
   HB_Buffer buf;
   GsubGposInfo *ginfo;
   HB_UShort *apply_order_save;
   HB_UShort apply_count_save;
-  int apply_saved = 0;
   int gpos_applied = 0;
   HB_Error err;
 
@@ -851,10 +882,11 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
        goto simple_copy;
       apply_order_save = font_info->gsub->FeatureList.ApplyOrder;
       apply_count_save = font_info->gsub->FeatureList.ApplyCount;
-      apply_saved = 1;
       font_info->gsub->FeatureList.ApplyOrder = ginfo->gsub.indices;
       font_info->gsub->FeatureList.ApplyCount = ginfo->gsub.count;
       err = HB_GSUB_Apply_String (font_info->gsub, buf);
+      font_info->gsub->FeatureList.ApplyOrder = apply_order_save;
+      font_info->gsub->FeatureList.ApplyCount = apply_count_save;
       if (err != HB_Err_Ok && err != HB_Err_Not_Covered)
        goto simple_copy;
       if (out->used + buf->in_length > out->allocated)
@@ -864,28 +896,18 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
   if (ginfo->gpos.count > 0
       && (font_info->gpos || load_gpos (font_info) == HB_Err_Ok))
     {
-      if (! apply_saved)
-       {
-         apply_order_save = font_info->gsub->FeatureList.ApplyOrder;
-         apply_count_save = font_info->gsub->FeatureList.ApplyCount;
-         apply_saved = 1;
-       }
+      apply_order_save = font_info->gpos->FeatureList.ApplyOrder;
+      apply_count_save = font_info->gpos->FeatureList.ApplyCount;
       font_info->gpos->FeatureList.ApplyOrder = ginfo->gpos.indices;
       font_info->gpos->FeatureList.ApplyCount = ginfo->gpos.count;
       err = HB_GPOS_Apply_String (&font_info->hb_font, font_info->gpos,
                                  FT_LOAD_DEFAULT, buf, FALSE, FALSE);
+      font_info->gpos->FeatureList.ApplyOrder = apply_order_save;
+      font_info->gpos->FeatureList.ApplyCount = apply_count_save;
       if (err == HB_Err_Ok)
        gpos_applied = 1;
     }
 
-  if (apply_saved)
-    {
-      font_info->gsub->FeatureList.ApplyOrder = apply_order_save;
-      font_info->gsub->FeatureList.ApplyCount = apply_count_save;
-    }
-
-  gidx = out->used;
-
   for (i = 0; i < buf->in_length; i++)
     {
       HB_GlyphItem hg = buf->in_string + i;
@@ -895,7 +917,11 @@ drive_otf (MFLTFont *font, MFLT_OTF_Spec *spec,
       out->glyphs[out->used] = in->glyphs[hg->cluster];
       g = out->glyphs + out->used++;
       if (g->code != hg->gindex)
-       g->c = 0, g->code = hg->gindex;
+       {
+         g->c = 0;
+         g->code = hg->gindex;
+         g->measured = 0;
+       }
       adjustment[i].set = gpos_applied;
       if (gpos_applied)
        {
@@ -926,9 +952,6 @@ open_font (char *fontname, char **err)
   if (! face)
     return NULL;
   font_info = calloc (1, sizeof (FontInfoHB));
-  font_info->font.get_glyph_id = get_glyph_id;
-  font_info->font.get_metric = get_metric;
-  font_info->font.drive_otf = drive_otf;
   font_info->face = face;
   font_info->hb_font.klass = &hb_fontClass;
   font_info->hb_font.faceData = face;
@@ -972,8 +995,363 @@ close_font (MFLTFont *font)
 }
 
 #endif
+
+#else  /* (defined (FLT_PANGO) */
+
+typedef struct {
+  int count;
+  guint *indices;
+  PangoOTRuleset *ruleset;
+} FeatureInfo;
+
+typedef struct {
+  FeatureInfo gsub;
+  FeatureInfo gpos;
+} GsubGposInfo;
+
+typedef struct {
+  MFLTFont font;
+  PangoFontMap *fontmap;
+  PangoContext *context;
+  PangoFcFont *pango_font;
+  MPlist *otf_spec_cache;
+} FontInfoPango;
+
+int
+get_glyph_id (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
+{
+  FontInfoPango *font_info = (FontInfoPango *) font;
+
+  for (; from < to; from++)
+    {
+      MFLTGlyph *g = gstring->glyphs + from;
+
+      if (! g->encoded
+         && ! (g->code = pango_fc_font_get_glyph (font_info->pango_font,
+                                                  g->code)))
+       return -1;
+      g->encoded = 1;
+    }
+  return 0;
+}
+
+#define PANGO_SCALE_TO_26_6 (PANGO_SCALE / (1<<6))
+
+int
+get_metrics (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
+{
+  FontInfoPango *font_info = (FontInfoPango *) font;
+  int i;
+
+  for (i = from; i < to; i++)
+    {
+      MFLTGlyph *g = gstring->glyphs + from;
+
+      if (! g->measured)
+       {
+         PangoRectangle inc, logical;
+
+         pango_font_get_glyph_extents (PANGO_FONT (font_info->pango_font),
+                                       gstring->glyphs[i].code, &inc, &logical);
+         g->lbearing = inc.x / PANGO_SCALE_TO_26_6;
+         g->rbearing = (inc.x + inc.width) / PANGO_SCALE_TO_26_6;
+         g->xadv = logical.width / PANGO_SCALE_TO_26_6;
+         g->yadv = 0;
+         g->ascent = - inc.y / PANGO_SCALE_TO_26_6;
+         g->descent = (inc.height + inc.y) / PANGO_SCALE_TO_26_6;
+         g->measured = 1;
+       }
+    }
+  return 0;
+}
+
+
+#ifndef PANGO_OT_DEFAULT_LANGUAGE
+#define PANGO_OT_DEFAULT_LANGUAGE ((guint) 0xFFFF)
+#endif
+#ifndef PANGO_OT_ALL_GLYPHS
+#define PANGO_OT_ALL_GLYPHS ((guint) 0xFFFF)
 #endif
 
+void
+setup_features (PangoOTTableType type, FontInfoPango *font_info,
+               MFLTOtfSpec *spec, FeatureInfo *info)
+{
+  int count, preordered;
+  unsigned int *features;
+  guint script, langsys;
+  guint req_feature, index;
+  guint *feature_list;
+  int i, j, k;
+  FT_Face face;
+  PangoOTInfo *ot_info; 
+
+  face = pango_fc_font_lock_face (font_info->pango_font);
+  ot_info = pango_ot_info_get (face);
+  pango_fc_font_unlock_face (font_info->pango_font);
+
+  if (! pango_ot_info_find_script (ot_info, type, spec->script, &script))
+    return;
+  if (spec->langsys)
+    {
+      if (! pango_ot_info_find_language (ot_info, type, script,
+                                        spec->langsys,
+                                        &langsys, &req_feature))
+       return;
+    }
+  else
+    {
+      langsys = PANGO_OT_DEFAULT_LANGUAGE;
+      req_feature = 0xFFFF;
+    }
+  if (type == PANGO_OT_TABLE_GSUB)
+    {
+      count = spec->gsub_count;
+      features = spec->gsub;
+    }
+  else
+    {
+      count = spec->gpos_count;
+      features = spec->gpos;
+    }
+  feature_list = NULL;
+  for (preordered = 0; preordered < count; preordered++)
+    if (features[preordered] == 0)
+      {
+       feature_list = pango_ot_info_list_features (ot_info, type, 0,
+                                                   script, langsys);
+       if (! feature_list)
+         return;
+       break;
+      }
+  if (feature_list)
+    for (i = 0; feature_list[i]; i++);
+  else
+    i = preordered;
+  info->indices = malloc (sizeof (guint) * ((req_feature != 0xFFFF) + i));
+  i = 0;
+  if (req_feature != 0xFFFF)
+    info->indices[i++] = req_feature;
+  for (j = 0; j < preordered; j++)
+    if (pango_ot_info_find_feature (ot_info, type, features[j],
+                                   script, langsys, &index))
+      info->indices[i++] = index;
+  if (feature_list)
+    for (j = 0; feature_list[j]; j++)
+      {
+       for (k = preordered + 1; k < count; k++)
+         if (feature_list[j] == features[k])
+           break;
+       if (k == count
+           && pango_ot_info_find_feature (ot_info, type, feature_list[j],
+                                          script, langsys, &index))
+         info->indices[i++] = index;
+      }
+  info->count = i;
+  info->ruleset = pango_ot_ruleset_new (ot_info);
+#if 1
+  for (i = 0; i < info->count; i++)
+    pango_ot_ruleset_add_feature (info->ruleset, type, info->indices[i],
+                                 PANGO_OT_ALL_GLYPHS);
+#else
+  for (i = info->count - 1; i >= 0; i--)
+    pango_ot_ruleset_add_feature (info->ruleset, type, info->indices[i],
+                                 PANGO_OT_ALL_GLYPHS);
+#endif
+}
+
+GsubGposInfo *
+setup_otf_spec (FontInfoPango *font_info, MFLTOtfSpec *spec)
+{
+  GsubGposInfo *ginfo;
+
+  if (spec->gsub_count + spec->gpos_count == 0)
+    return NULL;
+  ginfo = mplist_get (font_info->otf_spec_cache, spec->sym);
+  if (ginfo)
+    return (ginfo->gsub.count + ginfo->gpos.count > 0 ? ginfo : NULL);
+  ginfo = calloc (1, sizeof (GsubGposInfo));
+  mplist_push (font_info->otf_spec_cache, spec->sym, ginfo);
+  setup_features (PANGO_OT_TABLE_GSUB, font_info, spec, &(ginfo->gsub));
+  setup_features (PANGO_OT_TABLE_GPOS, font_info, spec, &(ginfo->gpos));
+  return ginfo;
+}
+
+int
+drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
+          MFLTGlyphString *in, int from, int to,
+          MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
+{
+  FontInfoPango *font_info = (FontInfoPango *) font;
+  PangoOTBuffer *buffer;
+  GsubGposInfo *ginfo;
+  PangoGlyphString *glyphs;
+  int i;
+
+  buffer = pango_ot_buffer_new (font_info->pango_font);
+  for (i = from; i < to; i++)
+    pango_ot_buffer_add_glyph (buffer, in->glyphs[i].code, 0, i);
+  ginfo = setup_otf_spec (font_info, spec);
+  if (! ginfo)
+    goto simple_copy;
+  if (ginfo->gsub.count > 0)
+    pango_ot_ruleset_substitute (ginfo->gsub.ruleset, buffer);
+  if (ginfo->gpos.count > 0)
+    pango_ot_ruleset_position (ginfo->gpos.ruleset, buffer);
+  glyphs = pango_glyph_string_new ();
+  pango_ot_buffer_output (buffer, glyphs);
+  for (i = 0; i < glyphs->num_glyphs; i++)
+    {
+      PangoGlyphInfo *glyph_info = glyphs->glyphs + i;
+      MFLTGlyph *g;
+
+      out->glyphs[out->used] = in->glyphs[glyphs->log_clusters[i]];
+      g = out->glyphs + out->used++;
+      if (g->code != glyph_info->glyph)
+       {
+         g->c = 0;
+         g->code = glyph_info->glyph;
+         g->measured = 0;
+       }
+      g->xoff = glyph_info->geometry.x_offset / PANGO_SCALE_TO_26_6;
+      g->yoff = glyph_info->geometry.y_offset / PANGO_SCALE_TO_26_6;
+      g->xadv = glyph_info->geometry.width / PANGO_SCALE_TO_26_6;
+      g->yadv = 0;
+      adjustment[i].set = 0;
+    }
+  pango_ot_buffer_destroy (buffer);
+  pango_glyph_string_free (glyphs);
+  return to;
+
+ simple_copy:
+  pango_ot_buffer_destroy (buffer);
+  for (i = from; i < to; i++)
+    out->glyphs[out->used++] = in->glyphs[i];
+  return to;
+}
+
+
+MFLTFont *
+open_font (char *fontname, char **err)
+{
+  FontInfoPango *font_info = NULL;
+  PangoFontMap *fontmap;
+  PangoContext *context;
+  PangoFontDescription *desc;
+  PangoFont *pango_font;
+  char *family;
+  int size;
+  double pango_size;
+  
+  if (parse_font_name (fontname, &family, &size, err) < 0)
+    return NULL;
+  desc = pango_font_description_new ();
+  pango_font_description_set_family (desc, family);
+  free (family);
+  pango_size = size * PANGO_SCALE;
+  pango_font_description_set_absolute_size (desc, pango_size);
+
+  fontmap = pango_ft2_font_map_new ();
+  context = pango_context_new ();
+  pango_context_set_font_map (context, fontmap);
+  pango_font = pango_context_load_font (context, desc);
+  pango_font_description_free (desc);
+  if (pango_font)
+    {
+      font_info = calloc (1, sizeof (FontInfoPango));
+      font_info->fontmap = fontmap;
+      font_info->context = context;
+      font_info->pango_font = PANGO_FC_FONT (pango_font);
+      font_info->otf_spec_cache = mplist ();
+    }
+  else
+    {
+      g_object_unref (context);
+      g_object_unref (fontmap);
+    }
+  return (MFLTFont *) font_info;
+}
+
+void
+close_font (MFLTFont *font)
+{
+  FontInfoPango *font_info = (FontInfoPango *) font;
+
+  g_object_unref (font_info->context);
+  g_object_unref (font_info->fontmap);
+}
+
+#endif
+
+#ifdef FLT_GUI
+
+int
+run_flt (MText *mt, int from, int to, MFLTFont *font, char *flt_name)
+{
+  MDrawControl control;
+
+  memset (&control, 0, sizeof (MDrawControl));
+  control.two_dimensional = 1;
+  control.enable_bidi = 1;
+  control.anti_alias = 1;
+  /*control.disable_caching = 1;*/
+  mtext_put_prop (mt, from, to, Mfont, font->font);
+  mdraw_text_extents (frame, mt, from, to, &control, NULL, NULL, NULL);
+  return 0;
+}
+
+#else
+
+int
+run_flt (MText *mt, int from, int to, MFLTFont *font, char *flt_name)
+{
+  static MFLT *flt = NULL;
+  MCharTable *coverage;
+  static MFLTGlyphString gstring;
+
+  if (! flt
+      && ! (flt = mflt_get (flt_name)))
+    {
+      fprintf (stderr, "Invalid FLT name: %s\n", flt_name);
+      exit (1);
+    }
+  coverage = mflt_coverage (flt);
+  while (from < to)
+    {
+      int len = 0, i;
+
+      for (; from < to; from++)
+       if (mchartable_lookup (coverage, mtext_ref_char (mt, from)))
+         {
+           for (i = from + 1; i < to; i++)
+             if (! mchartable_lookup (coverage, mtext_ref_char (mt, i)))
+               break;
+           len = i - from;
+           break;
+         }
+      if (len > 0)
+       {
+         if (gstring.allocated < len)
+           {
+             gstring.allocated = len * 2;
+             gstring.glyphs = realloc (gstring.glyphs,
+                                       sizeof (MFLTGlyph) * len * 2);
+           }
+         gstring.glyph_size = sizeof (MFLTGlyph);
+         for (i = 0; i < len; i++)
+           gstring.glyphs[i].c = mtext_ref_char (mt, from + i);
+         gstring.used = len;
+         i = mflt_run (&gstring, 0, len, font, flt);
+         if (i < 0)
+           return i;
+         from += len;
+       }
+    }
+  return to;
+}
+#endif
+
+
 int
 main (int argc, char **argv)
 {
@@ -982,6 +1360,7 @@ main (int argc, char **argv)
   MText *mt;
   MFLTFont *font;
   int len, i, j;
+  unsigned char *buf;
 
   if (argc < 3)
     {
@@ -1000,21 +1379,36 @@ main (int argc, char **argv)
       fprintf (stderr, "Font error: %s: %s\n", argv[1], err);
       exit (1);
     }
+  font->get_glyph_id = get_glyph_id;
+  font->get_metrics = get_metrics;
+  font->drive_otf = drive_otf;
 
-  mt = mconv_decode_stream (msymbol ("utf-8"), stdin);
+  i = 0;
+  buf = malloc (4096);
+  while ((j = fread (buf + i, 1, 4096, stdin)) == 4096)
+    {
+      i += 4096;
+      buf = realloc (buf, i + 4096);
+    }
+  mt = mtext_from_data (buf, i, MTEXT_FORMAT_UTF_8);
   len = mtext_len (mt);
   for (i = 0, j = mtext_character (mt, i, len, '\n'); i < len;
        i = j + 1, j = mtext_character (mt, i, len, '\n'))
     {
+      int to;
+
       if (j < 0)
        j = len;
-      if (flt (mt, i, j, font, flt_name) < 0)
+      to = run_flt (mt, i, j, font, flt_name);
+      if (to < 0)
        {
          fprintf (stderr, "Error in FLT processing.\n");
          exit (1);
        }
+      i = j;
     }
   m17n_object_unref (mt);
+  free (buf);
   close_font (font);
   M17N_FINI ();
   return 0;