*** empty log message ***
[m17n/m17n-pango.git] / m17n-fc.c
index 0ff05ba..9989d18 100644 (file)
--- a/m17n-fc.c
+++ b/m17n-fc.c
@@ -1,85 +1,42 @@
 /* Pango
  * m17n-fc.c: Generic shaper using the m17n library for FreeType-based backends
 
-   Copyright (C) 2004, 2005
+   Copyright (C) 2004, 2005, 2006, 2007, 2008
      National Institute of Advanced Industrial Science and Technology (AIST)
-     Registration Number H15PRO112
+     Registration Number H16PRO276
 
-   This file is part of the m17n library.
-
-   The m17n library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public License
-   as published by the Free Software Foundation; either version 2.1 of
+   The library is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
    the License, or (at your option) any later version.
 
-   The m17n library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
+   The pango-m17n library is distributed in the hope that it will be
+   useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the m17n library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307, USA.  */
+   License along with the pango-m17n library; if not, write to the
+   Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+   MA 02111-1307, USA.  */
 
 #include <config.h>
-
 #include <string.h>
 #include <stdlib.h>
+#include <ctype.h>
 
+#include <pango/pango-ot.h>
 #include <pango/pango-engine.h>
-#include <pango/pango-utils.h>
 #include <pango/pangofc-font.h>
 
-#include <m17n-gui.h>
-#include <m17n-misc.h>
-
 #ifdef G_LOG_DOMAIN
 #undef G_LOG_DOMAIN
 #endif
-#define G_LOG_DOMAIN "M17N-Pango-FC"
+#define G_LOG_DOMAIN "Pango-M17N-FC"
 
-/* Control printing of debug information.  It is initialized from the
-   environment variable PANGO_M17N_FC_DEBUG.  Currently, any positive
-   value turns on all debug messages.  */
-static int debug_level;
-
-/* Commonly used frame on nulldevice.  */
-static MFrame *frame;
-
-/* Initialize this module.  The main task is to open a frame on
-   nulldevice.  */
-
-static void
-init_module ()
-{
-  const char *envvar = g_getenv ("PANGO_M17N_FC_DEBUG");
-  MPlist *param;
-  MFace *face;
-  MFontset *fontset;
-
-  debug_level = envvar ? atoi (envvar) : 0;
-
-  param = mplist ();
-  face = mface ();
-  fontset = (mdatabase_find (Mfontset, msymbol ("generic"), Mnil, Mnil)
-            ? mfontset ("generic") : mfontset (NULL));
-  mface_put_prop (face, Mfontset, fontset);
-  mplist_put (param, Mface, face);
-  mplist_put (param, Mdevice, Mnil);
-  frame = mframe (param);
-  m17n_object_unref (fontset);
-  m17n_object_unref (face);
-  m17n_object_unref (param);
-}
-
-/* Finalize this module by freeing frame.  */
-
-static void
-fini_module ()
-{
-  m17n_object_unref (frame);
-}
+/**************************************/
+/* Standard preamble for Pango module */
+/**************************************/
 
 /* No extra fields needed for these structures.  */
 typedef PangoEngineShape      M17NEngineFc;
@@ -90,19 +47,34 @@ typedef PangoEngineShapeClass M17NEngineFcClass;
 
 static PangoEngineScriptInfo m17n_scripts[] = {
   { PANGO_SCRIPT_ARABIC,   "*" },
+  { PANGO_SCRIPT_ARMENIAN, "*" },
   { PANGO_SCRIPT_BENGALI,  "*" },
+  { PANGO_SCRIPT_BOPOMOFO, "*" },
+  { PANGO_SCRIPT_CHEROKEE, "*" },
+  { PANGO_SCRIPT_COPTIC,   "*" },
+  { PANGO_SCRIPT_CYRILLIC, "*" },
+  { PANGO_SCRIPT_DESERET,  "*" },
   { PANGO_SCRIPT_DEVANAGARI, "*" },
   { PANGO_SCRIPT_ETHIOPIC, "*" },
   { PANGO_SCRIPT_GEORGIAN, "*" },
+  { PANGO_SCRIPT_GOTHIC,   "*" },
+  { PANGO_SCRIPT_GREEK,    "*" },
   { PANGO_SCRIPT_GUJARATI, "*" },
   { PANGO_SCRIPT_GURMUKHI, "*" },
+  { PANGO_SCRIPT_HAN,      "*" },
+  { PANGO_SCRIPT_HANGUL,   "*" },
   { PANGO_SCRIPT_HEBREW,   "*" },
+  { PANGO_SCRIPT_HIRAGANA, "*" },
   { PANGO_SCRIPT_KANNADA,  "*" },
+  { PANGO_SCRIPT_KATAKANA, "*" },
   { PANGO_SCRIPT_KHMER,    "*" },
   { PANGO_SCRIPT_LAO,      "*" },
   { PANGO_SCRIPT_MALAYALAM, "*" },
   { PANGO_SCRIPT_MYANMAR,  "*" },
+  { PANGO_SCRIPT_OGHAM,    "*" },
+  { PANGO_SCRIPT_OLD_ITALIC, "*" },
   { PANGO_SCRIPT_ORIYA,     "*" },
+  { PANGO_SCRIPT_RUNIC,     "*" },
   { PANGO_SCRIPT_SINHALA,   "*" },
   { PANGO_SCRIPT_SYRIAC,    "*" },
   { PANGO_SCRIPT_TAMIL,     "*" },
@@ -110,6 +82,26 @@ static PangoEngineScriptInfo m17n_scripts[] = {
   { PANGO_SCRIPT_THAANA,    "*" },
   { PANGO_SCRIPT_THAI,      "*" },
   { PANGO_SCRIPT_TIBETAN,   "*" },
+  { PANGO_SCRIPT_CANADIAN_ABORIGINAL, "*" },
+  { PANGO_SCRIPT_YI,       "*" },
+
+  { PANGO_SCRIPT_BRAILLE,  "*" },
+  { PANGO_SCRIPT_CYPRIOT,  "*" },
+  { PANGO_SCRIPT_LIMBU,    "*" },
+  { PANGO_SCRIPT_OSMANYA,  "*" },
+  { PANGO_SCRIPT_SHAVIAN,  "*" },
+  { PANGO_SCRIPT_LINEAR_B, "*" },
+  { PANGO_SCRIPT_TAI_LE,   "*" },
+  { PANGO_SCRIPT_UGARITIC, "*" },
+
+  { PANGO_SCRIPT_NEW_TAI_LUE, "*" },
+  { PANGO_SCRIPT_BUGINESE, "*" },
+  { PANGO_SCRIPT_GLAGOLITIC, "*" },
+  { PANGO_SCRIPT_TIFINAGH, "*" },
+  { PANGO_SCRIPT_SYLOTI_NAGRI, "*" },
+  { PANGO_SCRIPT_OLD_PERSIAN, "*" },
+  { PANGO_SCRIPT_KHAROSHTHI, "*" },
+  { PANGO_SCRIPT_COMMON,   "" }
 };
 
 static PangoEngineInfo script_engines[] = {
@@ -121,272 +113,524 @@ static PangoEngineInfo script_engines[] = {
   }
 };
 
+/***************************/
+/* Interface with m17n-flt */
+/***************************/
+
+#include <m17n-flt.h>
+#include <m17n-misc.h>
+
+/* Return a pointer to the IDXth glyph in GSTRING.  */
+#define GREF(GSTRING, IDX) ((GSTRING)->glyphs + (IDX))
+
+/* Convert Pango scale to 26.6 fractional pixel.  */
+#define PANGO_SCALE_TO_26_6(N) ((N) / (PANGO_SCALE) * (1<<6))
+
+/* Convert 26.6 fractional pixel to Pango scale.  */
+#define PANGO_SCALE_FROM_26_6(N) ((N) * (PANGO_SCALE) / (1<<6))
+
+/* Control printing of debug information.  It is initialized from the
+   environment variable PANGO_M17N_FC_DEBUG.  Currently, any positive
+   value turns on all debug messages.  */
+static int debug_level;
+
+
+/* Structure containing information about OT features decoded from
+   MFLTOtfSpec.  */
+
+typedef struct {
+  /* Rulesets for GSUB (1st element) and GPOS (2nd element).  */
+  PangoOTRuleset *ruleset[2];
+} FeatureInfo;
+
+
+/* Structure containing information about a font.  */
+
+typedef struct {
+  MFLTFont font;
+  PangoFcFont *pango_font;
+  FT_Face face;
+  /* Cache that maps MFLTOtfSpec->sym to FeatureInfo.  */
+  MPlist *otf_spec_cache;
+} FontInfo;
+
+
+/* Prototypes for callback functions of FontInfo.font.  */
+
+static int get_glyph_id (MFLTFont *, MFLTGlyphString *, int, int);
+static int get_metrics (MFLTFont *, MFLTGlyphString *, int, int);
+static int check_otf (MFLTFont *, MFLTOtfSpec *);
+static int drive_otf (MFLTFont *, MFLTOtfSpec *,
+                     MFLTGlyphString *, int, int,
+                     MFLTGlyphString *, MFLTGlyphAdjustment *);
+
+
+/* This function works in two ways.
+
+   If INFO is NULL, it checks if the features specified in SPEC match
+   with the OpenType font in OT_INFO, and return 1 (match) or 0 (no
+   match).
 
-/* Convert PANGO_LANGUAGE to m17n-lib's language symbol.  If m17n-lib
-   doesn't know about PANGO_LANGUAGE, return Mnil.  */
+   If INFO is non-NULL, it creates a PangoOTRuleset object from SPEC
+   and stores it in INFO.  In this case, non-existing features in SPEC
+   are just ignored.
 
-static MSymbol
-m17n_fc_get_language (PangoLanguage *pango_language)
+   Features in SPEC are stored in an array in this format:
+       P P ... [ 0xFFFFFFFF [ N N ... ] ]
+   where
+       Ps are positive features that should be in the font (INFO is
+       NULL), or should be applied in this order (INFO is non-NULL),
+
+        0xFFFFFFFF is a separater between Ps and Ns (INFO is NULL), or
+       is a wildcard meaning that all the other features (except for
+       Ns) must be applied (INFO is non-NULL),
+
+       Ns are negative features that should not be in the font (INFO
+       is NULL), or should not be applied (INFO is non-NULL).  */
+
+static int
+otf_check_features (PangoOTInfo *ot_info, PangoOTTableType type, 
+                   MFLTOtfSpec *spec, FeatureInfo *info)
 {
-  MSymbol language;
+  unsigned int *features;
+  unsigned int langsys_tag;
+  guint script_idx, langsys_idx;
+  guint index;
+  guint *feature_list;
+  int i, j;
+  /* Used as an index to spec->features[] and info->ruleset[].  */
+  int for_gpos = type == PANGO_OT_TABLE_GPOS;
+
+  features = spec->features[for_gpos];
+  if (! features)
+    return 1;
+  if (! pango_ot_info_find_script (ot_info, type, spec->script, &script_idx))
+    /* No feature for the specified script exist.  If the first
+       feature in SPEC is a wildcard, the remaining features are all
+       negative, so we can return 1.  */
+    return (features[0] == 0xFFFFFFFF);
+  langsys_tag = spec->langsys;
+  if (! langsys_tag)
+    langsys_tag = PANGO_OT_DEFAULT_LANGUAGE;
+  if (! pango_ot_info_find_language (ot_info, type, script_idx,
+                                    langsys_tag, &langsys_idx, &index))
+    {
+      langsys_idx = PANGO_OT_DEFAULT_LANGUAGE;
+      index = 0xFFFF;
+    }
 
-  if (pango_language)
+  if (info && index != 0xFFFF)
     {
-      const char *lang = pango_language_to_string (pango_language);
+      info->ruleset[for_gpos] = pango_ot_ruleset_new (ot_info);
+      pango_ot_ruleset_add_feature (info->ruleset[for_gpos], type,
+                                   index, PANGO_OT_ALL_GLYPHS);
+    }
 
-      language = msymbol (lang);
-      if (! msymbol_get (language, Mlanguage))
+  for (i = 0; features[i] && features[i] != 0xFFFFFFFF; i++)
+    {
+      if (pango_ot_info_find_feature (ot_info, type, features[i],
+                                     script_idx, langsys_idx, &index))
        {
-         if (strlen (lang) <= 2)
-           language = Mnil;
-         else
+         if (info)
            {
-             /* Remove region part (e.g. "zh_CN" -> "zh").  */
-             char shortlang[3];
-         
-             shortlang[0] = lang[0], shortlang[1] = lang[1], shortlang[2] = 0;
-             language = msymbol (shortlang);
-             if (! msymbol_get (language, Mlanguage))
-               language = Mnil;
+             if (! info->ruleset[for_gpos])
+               info->ruleset[for_gpos] = pango_ot_ruleset_new (ot_info);
+             pango_ot_ruleset_add_feature (info->ruleset[for_gpos], type,
+                                           index, PANGO_OT_ALL_GLYPHS);
            }
        }
+      else
+       {
+         if (! info)
+           return 0;
+       }
     }
-  else
-    language = Mnil;
-  return language;
+  if (! features[i])
+    /* No wildcard, no negative features.  */
+    return 1;
+
+  feature_list = pango_ot_info_list_features (ot_info, type, 0,
+                                             script_idx, langsys_idx);
+  if (! feature_list)
+    /* No feature for the specified script/language exist.  As the
+       remaining features are all negative, we can return 1.  */
+    return 1;
+
+  if (! info)
+    {
+      /* Check if the remaining features don't appear in
+        FEATURE_LIST.  */
+      for (i++; features[i]; i++)
+       for (j = 0; feature_list[j]; j++)
+         if (features[i] == feature_list[j])
+           return 0;
+      return 1;
+    }
+
+  /* Add all features in FEATURE_LIST except for those appearing in
+     FEATURES.  */
+  for (i = 0; feature_list[i]; i++)
+    {
+      guint tag = feature_list[i];
+
+      for (j = 0; features[j] && tag != features[j]; j++);
+      if (! features[j])
+       {
+         pango_ot_info_find_feature (ot_info, type, tag,
+                                     script_idx, langsys_idx, &index);
+         if (! info->ruleset[for_gpos])
+           info->ruleset[for_gpos] = pango_ot_ruleset_new (ot_info);
+         pango_ot_ruleset_add_feature (info->ruleset[for_gpos], type, index,
+                                       PANGO_OT_ALL_GLYPHS);
+       }
+    }
+  return 1;
 }
 
-/* Shaper function.  */
 
-static void 
-m17n_fc_engine_shape (PangoEngineShape *engine,
-                     PangoFont        *pango_font,
-                     const char       *text,
-                     gint              length,
-                     PangoAnalysis    *analysis,
-                     PangoGlyphString *glyphs)
+/* Return a FeatureInfo object (newly created if necessary) for the
+   font in FONT_INFO and for the OpenType feature specification in
+   SPEC.  */
+
+static FeatureInfo *
+get_feature_info (FontInfo *font_info, MFLTOtfSpec *spec)
 {
-  /* Symbols for character property `category'.  */
-  static MSymbol MZs, MCf;
-  static MDrawControl control;
-  MSymbol language;
-  PangoGlyphInfo *g;
-  MText *mt;
-  MDrawGlyph *m_glyphs;
-  int nchars, nglyphs;
-  PangoFcFont *fc_font;
-  char *fontname;
-  MFont *font;
-  int i;
-  const char *p;
-  int *offsets;
+  FeatureInfo *feature_info;
+  PangoOTInfo *ot_info; 
 
-  g_return_if_fail (pango_font != NULL);
-  g_return_if_fail (text != NULL);
-  g_return_if_fail (length >= 0);
-  g_return_if_fail (analysis != NULL);
+  if (! spec->features[0] && ! spec->features[1])
+    return NULL;
+  if (font_info->otf_spec_cache)
+    {
+      feature_info = mplist_get (font_info->otf_spec_cache, spec->sym);
+      if (feature_info)
+       return feature_info;
+    }
+  else
+    font_info->otf_spec_cache = mplist ();
+  feature_info = g_new0 (FeatureInfo, 1);
+  mplist_push (font_info->otf_spec_cache, spec->sym, feature_info);
+  ot_info = pango_ot_info_get (font_info->face);
+  otf_check_features (ot_info, PANGO_OT_TABLE_GSUB, spec, feature_info);
+  otf_check_features (ot_info, PANGO_OT_TABLE_GPOS, spec, feature_info);
+
+  return feature_info;
+}
 
-  if (debug_level > 0)
+
+/* Free the object pointed by FONT_INFO.  */
+
+static void
+free_font_info (FontInfo *font_info)
+{
+  if (font_info->otf_spec_cache)
     {
-      char *msg = alloca (length + 1);
+      MPlist *p;
 
-      if (msg)
+      for (p = font_info->otf_spec_cache; mplist_key (p) != Mnil;
+          p = mplist_next (p))
        {
-         memcpy (msg, text, length);
-         msg[length] = '\0';
+         FeatureInfo *feature_info = mplist_value (p);
+
+         if (feature_info->ruleset[0])
+           g_object_unref (feature_info->ruleset[0]);
+         if (feature_info->ruleset[1])
+           g_object_unref (feature_info->ruleset[1]);
        }
-      else
-       msg = "...";
-      g_debug ("shape \"%s\"", msg);
+      m17n_object_unref (font_info->otf_spec_cache);
     }
+  pango_fc_font_unlock_face (font_info->pango_font);
+  g_free (font_info);
+}
 
-  if (! MZs)
-    {
-      MZs = msymbol ("Zs");
-      MCf = msymbol ("Cf");
-    }
 
-  language = m17n_fc_get_language (analysis->language);
+/* Return a FontInfo object corresponding to FONT.  It is newly
+   created if necessary.  */
 
-  mt = mtext_from_data (text, length, MTEXT_FORMAT_UTF_8);
-  nchars = mtext_len (mt);
-  offsets = alloca (sizeof (int) * nchars);
-  g_return_if_fail (offsets != NULL);
-  for (i = 0, p = text; i < nchars; i++, p = g_utf8_next_char (p))
-    offsets[i] = p - text;
+static FontInfo *
+get_font_info  (PangoFont *font)
+{
+  FontInfo *font_info;
+  static GQuark info_id = 0;
+  
+  if (G_UNLIKELY (! info_id))
+    info_id = g_quark_from_string ("FontInfo");
+  font_info = g_object_get_qdata (G_OBJECT (font), info_id);
+  if (G_UNLIKELY (! font_info))
+    {
+      PangoFontDescription *desc;
+      const char *family_name;
+      char *buf;
+      int len, i;
+
+      font_info = g_new0 (FontInfo, 1);
+      font_info->pango_font = PANGO_FC_FONT (font);
+      font_info->face = pango_fc_font_lock_face (font_info->pango_font);
+      desc = pango_font_describe_with_absolute_size (font);
+      family_name = pango_font_description_get_family (desc);
+      if (family_name)
+       {
+         len = strlen (family_name);
+         buf = g_alloca (len + 1);
+         for (i = 0; i < len + 1; i++)
+           buf[i] = tolower (family_name[i]);
+         font_info->font.family = msymbol (buf);
+       }
+      font_info->font.x_ppem = font_info->font.y_ppem
+       = PANGO_SCALE_TO_26_6 (pango_font_description_get_size (desc));
+      pango_font_description_free (desc);
+      font_info->font.check_otf = check_otf;
+      font_info->font.get_glyph_id = get_glyph_id;
+      font_info->font.get_metrics = get_metrics;
+      font_info->font.drive_otf = drive_otf;
+      g_object_set_qdata_full (G_OBJECT (font), info_id,
+                              font_info, (GDestroyNotify) free_font_info);
+    }
+  return font_info;
+}
 
-  fc_font = PANGO_FC_FONT (pango_font);
-  fontname = (char *) FcNameUnparse (fc_font->font_pattern);
-  font = mfont_parse_name (fontname, Mfontconfig);
-  free (fontname);
-  {
-    FcChar8 *filename;
-    int size;
-
-    if (FcPatternGetString (fc_font->font_pattern, FC_FILE, 0, &filename)
-       == FcResultMatch)
-      mfont_put_prop (font, Mfontfile, msymbol ((char *) filename));
-    size = (int) mfont_get_prop (font, Msize);
-    if (size)
-      mfont_put_prop (font, Msize, (void *) (size * 256));
-    if (debug_level > 0)
-      g_debug ("  by %s", (char *) filename);
-  }
+/* Four callback functions for MFLTFont.  */
 
-  mtext_put_prop (mt, 0, nchars, Mfont, font);
-  if (language != Mnil)
-    mtext_put_prop (mt, 0, nchars, Mlanguage, language);
+static int
+get_glyph_id (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
+{
+  FontInfo *font_info = (FontInfo *) font;
+  MFLTGlyph *g = gstring->glyphs + from, *g_end = g + (to - from);
+  int err = 0;
 
-  control.enable_bidi = 1;
-  m_glyphs = alloca (sizeof (MDrawGlyph) * nchars * 2);
-  g_return_if_fail (m_glyphs != NULL);
-  if (mdraw_glyph_list (frame, mt, 0, nchars, &control, m_glyphs,
-                       nchars * 2, &nglyphs) < 0)
+  for (; g < g_end; g++)
     {
-      m_glyphs = alloca (sizeof (MDrawGlyph) * nglyphs);
-      g_return_if_fail (m_glyphs != NULL);
-      mdraw_glyph_list (frame, mt, 0, nchars, &control, m_glyphs,
-                       nglyphs, &nglyphs);
+      if (! g->encoded)
+       {
+         g->code = pango_fc_font_get_glyph (font_info->pango_font, g->code);
+         g->encoded = 1;
+         err |= (! g->code);
+       }
     }
+  return err;
+}
 
-  pango_glyph_string_set_size (glyphs, nglyphs);
+static int
+get_metrics (MFLTFont *font, MFLTGlyphString *gstring, int from, int to)
+{
+  FontInfo *font_info = (FontInfo *) font;
+  MFLTGlyph *g = gstring->glyphs + from, *g_end = g + (to - from);
+  int err = 0;
 
-  for (i = 0, g = glyphs->glyphs; i < nglyphs; i++, g++)
+  for (; g < g_end; g++)
     {
-      if (m_glyphs[i].glyph_code >= 0)
-       {
-         g->glyph = m_glyphs[i].glyph_code;
-         g->geometry.x_offset = m_glyphs[i].x_off * PANGO_SCALE / 256;
-         g->geometry.y_offset = m_glyphs[i].y_off * PANGO_SCALE / 256;
-         g->geometry.width = m_glyphs[i].x_advance * PANGO_SCALE / 256;
-       }
-      else
+      if (! g->measured)
        {
-         int c = mtext_ref_char (mt, m_glyphs[i].from);
-         MSymbol category = mchar_get_prop (c, Mcategory);
-         PangoRectangle logical_rect;
-
-         if (category == MZs)
-           {
-             g->glyph = 0;
-             g->geometry.width = m_glyphs[i].x_advance * PANGO_SCALE / 256;
-           }
-         else if (category == MCf)
-           {
-             g->glyph = 0;
-             g->geometry.width = 0;
-           }
-         else
-           {
-             g->glyph = pango_fc_font_get_unknown_glyph (fc_font, c);
-             pango_font_get_glyph_extents (pango_font, g->glyph, NULL,
-                                           &logical_rect);
-             g->geometry.width = logical_rect.width;
-           }
-         g->geometry.x_offset = 0;
-         g->geometry.y_offset = 0;
+         PangoRectangle inc, logical;
+
+         if (! g->encoded)
+           g->code = pango_fc_font_get_glyph (font_info->pango_font, g->code);
+         pango_font_get_glyph_extents (PANGO_FONT (font_info->pango_font),
+                                       g->code, &inc, &logical);
+         g->lbearing = PANGO_SCALE_TO_26_6 (inc.x);
+         g->rbearing = PANGO_SCALE_TO_26_6 (inc.x + inc.width);
+         g->xadv = PANGO_SCALE_TO_26_6 (logical.width);
+         g->yadv = 0;
+         g->ascent = - PANGO_SCALE_TO_26_6 (inc.y);
+         g->descent = PANGO_SCALE_TO_26_6 (inc.height + inc.y);
+         g->measured = 1;
+         err |= (! g->code);
        }
-      g->attr.is_cluster_start
-       = (i == 0
-          || m_glyphs[i - 1].from != m_glyphs[i].from);
-      glyphs->log_clusters[i] = offsets[m_glyphs[i].from];
     }
-
-  m17n_object_unref (mt);
-  free (font);
+  return err;
 }
 
-/* Check if PANGO_FONT is suitable for the character WC.  */
+static int
+check_otf (MFLTFont *font, MFLTOtfSpec *spec)
+{
+  FontInfo *font_info = (FontInfo *) font;
+  PangoOTInfo *ot_info = pango_ot_info_get (font_info->face);
+
+  if (! ot_info)
+    return 0;
+  if (! otf_check_features (ot_info, PANGO_OT_TABLE_GSUB, spec, NULL)
+      || ! otf_check_features (ot_info, PANGO_OT_TABLE_GPOS, spec, NULL))
+    return 0;
+  return 1;
+}
 
-static PangoCoverageLevel
-m17n_fc_engine_covers (PangoEngineShape *engine,
-                      PangoFont        *pango_font,
-                      PangoLanguage    *pango_language,
-                      gunichar          wc)
+static int
+drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
+          MFLTGlyphString *in, int from, int to,
+          MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
 {
-  static gunichar last_wc = 0;
-  /* Symbols for character property `script'.  */
-  static MSymbol Mlatin, Mcommon, Minherited;
-  PangoCoverage *coverage;
-  PangoCoverageLevel result;
-  PangoFcFont *fc_font;
-  char *fontname;
-  MFont *font;
-  MSymbol script, language;
-  int check_result;
-
-  if (! Mlatin)
+  FontInfo *font_info = (FontInfo *) font;
+  PangoOTBuffer *buffer;
+  FeatureInfo *feature_info;
+  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);
+  feature_info = get_feature_info (font_info, spec);
+  if (! feature_info)
+    goto simple_copy;
+  if (feature_info->ruleset[0])
+    pango_ot_ruleset_substitute (feature_info->ruleset[0], buffer);
+  if (feature_info->ruleset[1])
+    pango_ot_ruleset_position (feature_info->ruleset[1], buffer);
+  glyphs = pango_glyph_string_new ();
+  pango_ot_buffer_output (buffer, glyphs);
+  for (i = 0; i < glyphs->num_glyphs; i++)
     {
-      Mlatin = msymbol ("latin");
-      Mcommon = msymbol ("common");
-      Minherited = msymbol ("inherited");
+      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 = PANGO_SCALE_TO_26_6 (glyph_info->geometry.x_offset);
+      g->yoff = PANGO_SCALE_TO_26_6 (glyph_info->geometry.y_offset);
+      g->xadv =  PANGO_SCALE_TO_26_6 (glyph_info->geometry.width);
+      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;
+}
 
-  if (debug_level > 0 && wc != last_wc)
+
+/* Shaper function.  */
+
+/* Shape TEXT of length GSTRING->used by a font in FONT_INFO, and
+   store the result in GLYPHS.  GSTRING->glyphs is not yet allocated
+   but GSTRING->allocated tells how many glyphs to allocate.  */
+
+static int
+shape_text (const char *text, int *offsets, MFLTGlyphString *gstring,
+           FontInfo *font_info, PangoGlyphString *glyphs)
+{
+  int i;
+  PangoGlyphInfo *g;
+  MFLTGlyph *mg;
+
+  gstring->glyphs = g_newa (MFLTGlyph, gstring->allocated);
+  for (i = 0; i < gstring->used; i++)
+    gstring->glyphs[i].c = g_utf8_get_char (text + offsets[i]);
+  i = mflt_run (gstring, 0, gstring->used, (MFLTFont *) font_info, NULL);
+  if (i < 0)
+    return i;
+
+  pango_glyph_string_set_size (glyphs, gstring->used);
+
+  for (i = 0, g = glyphs->glyphs, mg = gstring->glyphs; i < gstring->used;
+       i++, g++, mg++)
     {
-      g_debug ("covers for U+%04X", wc);
-      last_wc = wc;
+      g->glyph = mg->code;
+      g->geometry.x_offset = PANGO_SCALE_FROM_26_6 (mg->xoff);
+      g->geometry.y_offset = PANGO_SCALE_FROM_26_6 (mg->yoff);
+      g->geometry.width = PANGO_SCALE_FROM_26_6 (mg->xadv);
+      g->attr.is_cluster_start = (i == 0 || mg->from != mg[-1].from);
+      glyphs->log_clusters[i]
+       = (g->attr.is_cluster_start ? offsets[mg->from]
+          : glyphs->log_clusters[i - 1]);
     }
+  return 0;
+}
+
+static void 
+m17n_fc_engine_shape (PangoEngineShape *engine,
+                     PangoFont        *font,
+                     const char       *text,
+                     int              length,
+                     const PangoAnalysis    *analysis,
+                     PangoGlyphString *glyphs)
+{
+  FontInfo *font_info;
+  MFLTGlyphString gstring;
+  int i;
+  const char *p, *pend;
+  int *offsets;
+
+  g_return_if_fail (font != NULL);
+  g_return_if_fail (text != NULL);
+  g_return_if_fail (length >= 0);
+  g_return_if_fail (analysis != NULL);
+
+  font_info = get_font_info (font);
+
+  gstring.glyph_size = sizeof (MFLTGlyph);
+  gstring.used = 0;
+  gstring.r2l = analysis->level % 2;
 
-  coverage = pango_font_get_coverage (pango_font, pango_language);
-  result = pango_coverage_get (coverage, wc);
-  pango_coverage_unref (coverage);
+  offsets = g_newa (int, length);
+  for (i = 0, p = text, pend = text + length; p < pend;
+       i++, p = g_utf8_next_char (p))
+    offsets[i] = p - text;
+  gstring.used = i;
 
-  if (result == PANGO_COVERAGE_NONE)
+  /* Try at most three times, each time with larger GSTRING->glyphs.  */
+  gstring.allocated = length;
+  for (i = 0; i < 3; i++, gstring.allocated += length)
     {
-      if (debug_level > 0)
+      int result = shape_text (text, offsets, &gstring, font_info, glyphs);
+       
+      if (result != -2)
        {
-         fc_font = PANGO_FC_FONT (pango_font);
-         fontname = (char *) FcNameUnparse (fc_font->font_pattern);
-         font = mfont_parse_name (fontname, Mfontconfig);
-         g_debug ("  %s none", 
-                  msymbol_name (mfont_get_prop (font, Mfamily)));
-         free (fontname);
-         free (font);
+         g_return_if_fail (result >= 0);
+         break;
        }
-      return result;
     }
+}
 
-  fc_font = PANGO_FC_FONT (pango_font);
-  fontname = (char *) FcNameUnparse (fc_font->font_pattern);
-  font = mfont_parse_name (fontname, Mfontconfig);
+#if 0
 
-  if (wc < 0x100)
-    script = Mlatin;
-  else
-    {
-      script = mchar_get_prop ((int) wc, Mscript);
-      if (script == Mcommon || script == Minherited)
-       script = mchar_get_prop ((int) wc, msymbol ("block"));
-    }
-  language = m17n_fc_get_language (pango_language);
+/* Check if FONT is suitable for the character WC.  For the moment,
+   this is commented out because even if it returns
+   PANGO_COVERAGE_NONE, the other engine will use the font.  In
+   addition, the caller doesn't handle PANGO_COVERAGE_APPROXIMATE and
+   PANGO_COVERAGE_FALLBACK well.  */
 
-  check_result = mfont_check (frame, NULL, script, language, font);
-  if (debug_level > 0)
+static PangoCoverageLevel
+m17n_fc_engine_covers (PangoEngineShape *engine,
+                      PangoFont        *font,
+                      PangoLanguage    *language,
+                      gunichar          wc)
+{
+  FontInfo *font_info = get_font_info (font);
+  MFLT *flt = mflt_find (wc, (MFLTFont *) font_info);
+  const char *lang = pango_language_to_string (language);
+
+  if (! flt)
     {
-      char *result_text = (check_result == 2 ? "exact"
-                          : check_result == 1 ? "approximate"
-                          : "fallback");
-
-      g_debug ("  (%s,%s): %s %s",
-              (script ? msymbol_name (script) : ""),
-              (language ? msymbol_name (language) : ""),
-              msymbol_name (mfont_get_prop (font, Mfamily)),
-              result_text);
-    }      
-
-  free (fontname);
-  free (font);
-  return (check_result == 2 ? PANGO_COVERAGE_EXACT
-         : check_result == 1 ? PANGO_COVERAGE_APPROXIMATE
-         : PANGO_COVERAGE_FALLBACK);
+      if (debug_level)
+       g_debug (" %04X (%s/%s): no FLT\n", wc, lang,
+                msymbol_name (font_info->font.family));
+      return PANGO_COVERAGE_NONE;
+    }
+  if (debug_level)
+    g_debug (" %04X (%s/%s): FLT %s found\n", wc, lang,
+            msymbol_name (font_info->font.family), mflt_name (flt));
+  return PANGO_COVERAGE_EXACT;
 }
 
+#endif
+
 static void
 m17n_engine_fc_class_init (PangoEngineShapeClass *class)
 {
   class->script_shape = m17n_fc_engine_shape;
+#if 0
   class->covers = m17n_fc_engine_covers;
+#endif
 }
 
 PANGO_ENGINE_SHAPE_DEFINE_TYPE (M17NEngineFc, m17n_engine_fc,
@@ -396,15 +640,14 @@ void
 PANGO_MODULE_ENTRY(init) (GTypeModule *module)
 {
   m17n_engine_fc_register_type (module);
-
   M17N_INIT ();
-  init_module ();
+  if (getenv ("PANGO_M17N_DEBUG") != NULL)
+    debug_level = 1;
 }
 
 void 
 PANGO_MODULE_ENTRY(exit) (void)
 {
-  fini_module ();
   M17N_FINI ();
 }