(msymbol_is_managing_key): Extern it.
[m17n/m17n-lib.git] / src / font-ft.c
index 0f6a451..ee3f5a2 100644 (file)
 
 #ifdef HAVE_FREETYPE
 
+#ifdef HAVE_FTBDF_H
 #include <freetype/ftbdf.h>
+#endif
 
 #ifdef HAVE_FONTCONFIG
-#include <fontconfig/fontconfig.h>
-
-int fontconfig_initialized = 0;
-FcConfig *fc_config;
-#endif /* not HAVE_FONTCONFIG */
+static FcConfig *fc_config;
+#endif /* HAVE_FONTCONFIG */
 
+/* Registries.  */
 static MSymbol Municode_bmp, Municode_full, Miso10646_1, Miso8859_1;
 
+/* Font properties; Mnormal is already defined in face.c.  */
+static MSymbol Mmedium, Mr, Mnull;
+
 static FT_Library ft_library;
 
 typedef struct
 {
-  MSymbol ft_style;
-  MSymbol weight, style, stretch;
+  char *ft_style;
+  int len;
+  enum MFontProperty prop;
+  char *val;
 } MFTtoProp;
 
-static int ft_to_prop_size;
-static MFTtoProp *ft_to_prop;
+static MFTtoProp ft_to_prop[] =
+  { { "italic", 0, MFONT_STYLE, "i" },
+    { "roman", 0, MFONT_STYLE, "r" },
+    { "oblique", 0, MFONT_STYLE, "o" },
+    { "regular", 0, MFONT_WEIGHT, "medium" },
+    { "normal", 0, MFONT_WEIGHT, "medium" },
+    /* We need this entry even if "bold" is in commone_weight[] to
+       handle such style names as "bolditalic" and "boldoblique".  */
+    { "bold", 0, MFONT_WEIGHT, "bold" },
+    { "demi bold", 0, MFONT_WEIGHT, "demibold" },
+    { "demi", 0, MFONT_WEIGHT, "demibold" } };
+static int ft_to_prop_size = sizeof ft_to_prop / sizeof ft_to_prop[0];
 
 /** List of FreeType fonts.  Keys are family names, values are plists
-    contains fonts of the corresponding family.  In the deeper plist,
-    keys are Mt, values are (MFTInfo *).  */
+    containing fonts of the corresponding family.  In the deeper
+    plist, keys are Mt, values are (MFTInfo *).  */
 static MPlist *ft_font_list;
 
+/** List of FreeType base fonts.  Keys are family names, values are
+    (MFTInfo *).  */
+static MPlist *ft_family_list;
+
 static int all_fonts_scaned;
 
+static MSymbol M_generic_family_info;
+
+enum GenericFamilyType {
+  GENERIC_FAMILY_SERIF,
+  GENERIC_FAMILY_SANS_SERIF,
+  GENERIC_FAMILY_MONOSPACE,
+  GENERIC_FAMILY_MAX
+};
+
+/** Table for each generic family.  */
+
+typedef struct
+{
+  char *name;
+  MPlist *list;
+} GenericFamilyInfo;
+
+static GenericFamilyInfo generic_family_table[GENERIC_FAMILY_MAX] =
+  { { "serif" }, { "sans-serif" }, { "monospace" } };
+
 /** Return 0 if NAME implies TrueType or OpenType fonts.  Othersize
     return -1.  */
 
@@ -89,62 +128,75 @@ check_otf_filename (const char *name)
   return 0;
 }
 
-/** Setup members of FT_INFO from FT_FACE.  Return the family name.  */
+#define STRDUP_LOWER(s1, size, s2)                             \
+  do {                                                         \
+    int len = strlen (s2) + 1;                                 \
+    char *p1, *p2;                                             \
+                                                               \
+    if (size < len)                                            \
+      s1 = alloca (len), size = len;                           \
+    for (p1 = s1, p2 = s2; *p2; p1++, p2++)                    \
+      *p1 = (*p2 >= 'A' && *p2 <= 'Z' ? *p2 + 'a' - 'A' : *p2);        \
+    *p1 = *p2;                                                 \
+  } while (0)
+
+/** Setup members of FT_INFO from FT_FACE.  If the font is a base one
+    (i.e. medium-r-normal), set BASEP to 1.  Otherwise set BASEP to 0.
+    Return the family name.  */
 
 static MSymbol
-set_font_info (FT_Face ft_face, MFTInfo *ft_info, MSymbol family)
+set_font_info (FT_Face ft_face, MFTInfo *ft_info,
+              MSymbol family, MSymbol style, int *basep)
 {
   MFont *font = &ft_info->font;
-  MSymbol style;
-  int len;
-  char *buf, *p;
   MPlist *charmap_list;
   int unicode_bmp = -1, unicode_full = -1, unicode = -1;
   int i;
 
   MFONT_INIT (font);
 
-  if (family == Mnil)
-    {
-      len = strlen (ft_face->family_name) + 1;
-      buf = (char *) alloca (len);
-      memcpy (buf, ft_face->family_name, len);
-      for (p = buf; *p; p++)
-       if (*p >= 'A' && *p <= 'Z')
-         *p += 'a' - 'A';
-      family = msymbol (buf);
-    }
   mfont__set_property (font, MFONT_FAMILY, family);
+  mfont__set_property (font, MFONT_WEIGHT, Mmedium);
+  mfont__set_property (font, MFONT_STYLE, Mr);
+  mfont__set_property (font, MFONT_STRETCH, Mnormal);
+  mfont__set_property (font, MFONT_ADSTYLE, Mnull);
+  *basep = 1;
 
-  if (ft_face->style_name)
+  if (style != Mnull)
     {
-      len = strlen (ft_face->style_name) + 1;
-      buf = (char *) alloca (len);
-      memcpy (buf, ft_face->style_name, len);
-      for (p = buf; *p; p++)
-       if (*p >= 'A' && *p <= 'Z')
-         *p += 'a' - 'A';
-      style = msymbol (buf);
-      for (i = 0; i < ft_to_prop_size; i++)
-       if (ft_to_prop[i].ft_style == style)
-         {
-           mfont__set_property (font, MFONT_WEIGHT, ft_to_prop[i].weight);
-           mfont__set_property (font, MFONT_STYLE, ft_to_prop[i].style);
-           mfont__set_property (font, MFONT_STRETCH, ft_to_prop[i].stretch);
-           break;
-         }
-    }
-  else
-    i = ft_to_prop_size;
+      char *p = MSYMBOL_NAME (style);
 
-  if (i == ft_to_prop_size)
-    {
-      mfont__set_property (font, MFONT_WEIGHT, msymbol ("medium"));
-      mfont__set_property (font, MFONT_STYLE, msymbol ("r"));
-      mfont__set_property (font, MFONT_STRETCH, msymbol ("normal"));
-    }
+      while (*p)
+       {
+         for (i = 0; i < ft_to_prop_size; i++)
+           if (! strncmp (ft_to_prop[i].ft_style, p, ft_to_prop[i].len))
+             {
+               mfont__set_property (font, ft_to_prop[i].prop,
+                                    msymbol (ft_to_prop[i].val));
+               p += ft_to_prop[i].len;
+               break;
+             }
+         if (i == ft_to_prop_size)
+           {
+             char *p1 = p + 1;
+             MSymbol sym;
 
-  mfont__set_property (font, MFONT_ADSTYLE, msymbol (""));
+             while (*p1 >= 'a' && *p1 <= 'z') p1++;
+             sym = msymbol__with_len (p, p1 - p);
+             for (i = MFONT_WEIGHT; i <= MFONT_STYLE; i++)
+               if (msymbol_get (sym, mfont__property_table[i].property))
+                 {
+                   mfont__set_property (font, i, sym);
+                   break;
+                 }
+             p = p1;
+           }
+         while (*p && (*p < 'a' || *p > 'z')) p++;
+       }
+      *basep = (FONT_PROPERTY (font, MFONT_WEIGHT) == Mmedium
+               && FONT_PROPERTY (font, MFONT_STYLE) == Mr
+               && FONT_PROPERTY (font, MFONT_STRETCH) == Mnormal);
+    }
 
   charmap_list = mplist ();
   mplist_add (charmap_list, Mt, (void *) -1);
@@ -195,7 +247,7 @@ set_font_info (FT_Face ft_face, MFTInfo *ft_info, MSymbol family)
       FT_Set_Charmap (ft_face, ft_face->charmaps[unicode]);
       for (i = 255; i >= 32; i--)
        {
-         if (i == 160)
+         if (i == 192)
            i = 126;
          if (FT_Get_Char_Index (ft_face, (FT_ULong) i) == 0)
            break;
@@ -206,6 +258,7 @@ set_font_info (FT_Face ft_face, MFTInfo *ft_info, MSymbol family)
 
   ft_info->charmap_list = charmap_list;
 
+#ifdef HAVE_FTBDF_H
   if (! FT_IS_SCALABLE (ft_face))
     {
       BDF_PropertyRec prop;
@@ -215,6 +268,7 @@ set_font_info (FT_Face ft_face, MFTInfo *ft_info, MSymbol family)
       FT_Get_BDF_Property (ft_face, "RESOLUTION_Y", &prop);
       font->property[MFONT_RESY] = prop.u.integer;
     }
+#endif
 
   return family;
 }
@@ -236,232 +290,326 @@ close_ft (void *object)
 #endif /* HAVE_OTF */
     }
   free (ft_info->filename);
+  if (ft_info->languages)
+    free (ft_info->languages);
   M17N_OBJECT_UNREF (ft_info->charmap_list);
   free (ft_info);
 }
 
 static void
-add_font_info (char *filename, MSymbol family)
+add_font_info (char *filename, MSymbol family, void *langset, MPlist *plist)
 {
   FT_Face ft_face;
+#ifdef HAVE_FTBDF_H
   BDF_PropertyRec prop;
-  MFTInfo *ft_info = NULL;
+#endif
 
   if (FT_New_Face (ft_library, filename, 0, &ft_face) == 0)
     {
-      if (FT_IS_SCALABLE (ft_face)
-         || FT_Get_BDF_Property (ft_face, "PIXEL_SIZE", &prop) == 0)
+      char *buf;
+      int bufsize = 0;
+      MSymbol style;
+
+      if (family == Mnil)
        {
-         MSymbol fam;
-         MPlist *plist;
+         if (ft_face->family_name)
+           {
+             STRDUP_LOWER (buf, bufsize, ft_face->family_name);
+             family = msymbol (buf);
+           }
+         else
+           family = Mnull;
+         if (! (plist = mplist_get (ft_font_list, family)))
+           {
+             plist = mplist ();
+             mplist_add (ft_font_list, family, plist);
+           }
+       }
+      if (ft_face->style_name)
+       {
+         STRDUP_LOWER (buf, bufsize, ft_face->style_name);
+         style = msymbol (buf);
+       }
+      else
+       style = Mnull;
+
+      if (! mplist_get (plist, style)
+         && (FT_IS_SCALABLE (ft_face)
+#ifdef HAVE_FTBDF_H
+             || FT_Get_BDF_Property (ft_face, "PIXEL_SIZE", &prop) == 0
+#endif
+             ))
+       {
+         int basep;
+         MFTInfo *ft_info;
 
          M17N_OBJECT (ft_info, close_ft, MERROR_FONT_FT);
          ft_info->filename = strdup (filename);
          ft_info->otf_flag = check_otf_filename (filename);
-         fam = set_font_info (ft_face, ft_info, family);
-         plist = mplist_get (ft_font_list, fam);
-         if (! plist)
-           {
-             plist = mplist ();
-             mplist_add (ft_font_list, fam, plist);
-           }
-         mplist_add (plist, fam, ft_info);
+#ifdef HAVE_FONTCONFIG
+         if (langset)
+           ft_info->langset = FcLangSetCopy (langset);
+#endif
+         set_font_info (ft_face, ft_info, family, style, &basep);
+         mplist_add (plist, style, ft_info);
+
+         if (basep)
+           mplist_put (ft_family_list, family, ft_info);
+         else if (! mplist_get (ft_family_list, family))
+           mplist_add (ft_family_list, family, ft_info);
        }
       FT_Done_Face (ft_face);
     }
 }
 
-#ifdef HAVE_FONTCONFIG
-
-static void
-fc_list (MSymbol family)
+/* Return an element of ft_font_list for FAMILY.  If FAMILY is Mnil,
+   scan all fonts and return ft_font_list.  */
+static MPlist *
+ft_list_family (MSymbol family)
 {
-  FcPattern *pattern;
-  FcObjectSet *os;
-  FcFontSet *fs;
-  int i;
+  MPlist *plist, *head;
 
-  if (! fc_config)
+  if (! ft_font_list)
     {
-      char *pathname;
-      struct stat buf;
-      MPlist *plist;
+      ft_font_list = mplist ();
+      ft_family_list = mplist ();
+    }
 
-      FcInit ();
-      fc_config = FcConfigGetCurrent ();
-      MPLIST_DO (plist, mfont_freetype_path)
-       if (MPLIST_STRING_P (plist)
-           && (pathname = MPLIST_STRING (plist))
-           && stat (pathname, &buf) == 0)
-         FcConfigAppFontAddDir (fc_config, (FcChar8 *) pathname);
+  if (family == Mnil)
+    head = ft_font_list;
+  else
+    {
+      head = mplist_find_by_key (ft_font_list, family);
+      if (head)
+       return head;
+      head = mplist_add (ft_font_list, family, mplist ());
     }
 
-  pattern = FcPatternCreate ();
-  if (family)
-    FcPatternAddString (pattern, FC_FAMILY,
-                       (FcChar8 *) (msymbol_name (family)));
-  os = FcObjectSetBuild (FC_FILE, FC_FOUNDRY, FC_FAMILY, FC_STYLE, FC_PIXEL_SIZE, NULL);
-  fs = FcFontList (fc_config, pattern, os);
-  if (fs)
+#ifdef HAVE_FONTCONFIG
+  if (! all_fonts_scaned)
     {
-      char *filename;
+      FcPattern *pattern;
+      FcObjectSet *os;
+      FcFontSet *fs;
+      char *buf;
+      int bufsize = 0;
+      int i;
 
-      if (fs->nfont > 0)
-       for (i = 0; i < fs->nfont; i++)
-         {
-           FcPatternGetString (fs->fonts[i], FC_FILE, 0, 
-                               (FcChar8 **) &filename);
-           add_font_info (filename, family);
-         }
+      pattern = FcPatternCreate ();
+      if (family != Mnil)
+       {
+         FcPatternAddString (pattern, FC_FAMILY,
+                             (FcChar8 *) (msymbol_name (family)));
+         plist = head;
+         os = FcObjectSetBuild (FC_FILE, FC_LANG, NULL);
+       }
       else
        {
-         FcPattern *match;
-         FcResult result;
-
-         FcConfigSubstitute (fc_config, pattern, FcMatchPattern);
-         FcDefaultSubstitute (pattern);
-         match = FcFontMatch (fc_config, pattern, &result);
-         if (FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &filename)
-             == FcResultMatch)
-           add_font_info (filename, family);
-         FcPatternDestroy (match);
+         plist = NULL;
+         os = FcObjectSetBuild (FC_FILE, FC_FAMILY, FC_LANG, NULL);
+       }
+      fs = FcFontList (fc_config, pattern, os);
+      for (i = 0; i < fs->nfont; i++)
+       {
+         char *filename;
+         FcLangSet *langset;
+
+         if (FcPatternGetString (fs->fonts[i], FC_FILE, 0,
+                                 (FcChar8 **) &filename) != FcResultMatch)
+           continue;
+         if (FcPatternGetLangSet (fs->fonts[i], FC_LANG, 0,
+                                  &langset) != FcResultMatch)
+           langset = NULL;
+         if (family == Mnil)
+           {
+             MSymbol fam;
+             char *fname;
+
+             if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
+                                     (FcChar8 **) &fname) == FcResultMatch)
+               {
+                 STRDUP_LOWER (buf, bufsize, fname);
+                 fam = msymbol (buf);
+                 if (! plist || MPLIST_KEY (plist) != fam)
+                   {
+                     plist = mplist_find_by_key (ft_font_list, fam);
+                     if (! plist)
+                       plist = mplist_add (ft_font_list, fam, mplist ());
+                   }
+                 add_font_info (filename, fam, langset, MPLIST_PLIST (plist));
+               }
+           }
+         else
+           add_font_info (filename, family, langset, MPLIST_PLIST (plist));
        }
+
       FcFontSetDestroy (fs);
+      FcObjectSetDestroy (os);
+      FcPatternDestroy (pattern);
+      all_fonts_scaned = family == Mnil;
     }
-  FcObjectSetDestroy (os);
-  FcPatternDestroy (pattern);
-}
 
 #else  /* not HAVE_FONTCONFIG */
 
-static void
-ft_list ()
-{
-  MPlist *plist;
-  struct stat buf;
-  char *pathname;
-
-  MPLIST_DO (plist, mfont_freetype_path)
-    if (MPLIST_STRING_P (plist)
-       && (pathname = MPLIST_STRING (plist))
-       && stat (pathname, &buf) == 0)
-      {
-       if (S_ISREG (buf.st_mode))
-         add_font_info (pathname, Mnil);
-       else if (S_ISDIR (buf.st_mode))
-         {
-           int len = strlen (pathname);
-           char path[PATH_MAX];
-           DIR *dir = opendir (pathname);
-           struct dirent *dp;
+  if (! all_fonts_scaned)
+    {
+      struct stat buf;
+      char *pathname;
 
-           if (dir)
+      MPLIST_DO (plist, mfont_freetype_path)
+       if (MPLIST_STRING_P (plist)
+           && (pathname = MPLIST_STRING (plist))
+           && stat (pathname, &buf) == 0)
+         {
+           if (S_ISREG (buf.st_mode))
+             add_font_info (pathname, Mnil, NULL, NULL);
+           else if (S_ISDIR (buf.st_mode))
              {
-               strcpy (path, pathname);
-               strcpy (path + len, "/");
-               len++;
-               while ((dp = readdir (dir)) != NULL)
+               int len = strlen (pathname);
+               char path[PATH_MAX];
+               DIR *dir = opendir (pathname);
+               struct dirent *dp;
+
+               if (dir)
                  {
-                   strcpy (path + len, dp->d_name);
-                   add_font_info (path, Mnil);
+                   strcpy (path, pathname);
+                   strcpy (path + len, "/");
+                   len++;
+                   while ((dp = readdir (dir)) != NULL)
+                     {
+                       strcpy (path + len, dp->d_name);
+                       add_font_info (path, Mnil, NULL, NULL);
+                     }
+                   closedir (dir);
                  }
-               closedir (dir);
              }
          }
-      }
+      all_fonts_scaned = 1;
+    }
+
+#endif  /* not HAVE_FONTCONFIG */
+
+  return head;
 }
 
+static MPlist *
+ft_list_generic (MSymbol generic)
+{
+#ifdef HAVE_FONTCONFIG
+  GenericFamilyInfo *info = msymbol_get (generic, M_generic_family_info);
+  FcPattern *pattern;
+
+  if (! info)
+    return NULL;
+  if (info->list)
+    return info->list;
+
+  info->list = mplist ();
+  pattern = FcPatternCreate ();
+  FcPatternAddString (pattern, FC_FAMILY,
+                     (FcChar8 *) info->name);
+  if (FcConfigSubstitute (fc_config, pattern, FcMatchPattern) == FcTrue)
+    {
+      int i = 0;
+      char *family, *buf;
+      int bufsize = 0;
+    
+      while (FcPatternGetString (pattern, FC_FAMILY, i, (FcChar8 **) &family)
+            == FcResultMatch)
+       {
+         MPlist *plist;
+
+         STRDUP_LOWER (buf, bufsize, family);
+         plist = ft_list_family (msymbol (buf));
+         mplist_add (info->list, MPLIST_KEY (plist), MPLIST_VAL (plist));
+         i++;
+       }
+    }
+  return info->list;
+#else   /* not HAVE_FONTCONFIG */
+  return NULL;
 #endif /* not HAVE_FONTCONFIG */
+}
+
 
 /* The FreeType font driver function SELECT.  */
 
 static MRealizedFont *
 ft_select (MFrame *frame, MFont *spec, MFont *request, int limited_size)
 {
-  MPlist *plist, *pl;
+  MPlist *plist, *pl, *p;
   MFTInfo *best_font;
   int best_score;
   MRealizedFont *rfont;
-  MSymbol family, registry;
+  MSymbol registry, family;
+  unsigned short spec_family_id, request_family_id;
 
-  best_font = NULL;
-  best_score = 0;
-  family = FONT_PROPERTY (spec, MFONT_FAMILY);
-  if (family == Mnil)
-    family = FONT_PROPERTY (request, MFONT_FAMILY);
   registry = FONT_PROPERTY (spec, MFONT_REGISTRY);
   if (registry == Mnil)
     registry = Mt;
+  family = FONT_PROPERTY (spec, MFONT_FAMILY);
+  spec_family_id = spec->property[MFONT_FAMILY];
+  request_family_id = request->property[MFONT_FAMILY];
 
-  if (! ft_font_list)
-    ft_font_list = mplist ();
-#ifdef HAVE_FONTCONFIG
-  if (family != Mnil)
+  if (spec_family_id)
     {
-      plist = mplist_get (ft_font_list, family);
-      if (! plist)
+      plist = ft_list_generic (family);
+      if (plist)
        {
-         fc_list (family);
-         plist = mplist_get (ft_font_list, family);
-         if (! plist)
-           {
-             mplist_add (ft_font_list, family, plist = mplist ());
-             return NULL;
-           }
+         family = Mnil;
+         spec->property[MFONT_FAMILY] = 0;
+         if (spec_family_id == request_family_id)
+           request->property[MFONT_FAMILY] = 0;
        }
-    }
-  else
-    {
-      if (! all_fonts_scaned)
+      else
        {
-         fc_list (Mnil);
-         all_fonts_scaned = 1;
+         if (request_family_id
+             && (plist
+                 = ft_list_generic (FONT_PROPERTY (request, MFONT_FAMILY)))
+             && mplist_get (plist, family))
+           request->property[MFONT_FAMILY] = 0;
+         plist = ft_list_family (family);
        }
     }
-#else  /* not HAVE_FONTCONFIG */
-  if (! all_fonts_scaned)
-    {
-      ft_list ();
-      all_fonts_scaned = 1;
-    }
-  if (family != Mnil)
+  else
     {
-      plist = mplist_get (ft_font_list, family);
-      if (! plist)
-       return NULL;
+      if (request_family_id
+         && (plist = ft_list_generic (FONT_PROPERTY (request, MFONT_FAMILY))))
+       request->property[MFONT_FAMILY] = 0;
+      else
+       plist = ft_list_family (FONT_PROPERTY (request, MFONT_FAMILY));
     }
-#endif /* not HAVE_FONTCONFIG */
 
-  if (family == Mnil)
-    plist = MPLIST_VAL (ft_font_list);
-
- retry:
+  best_font = NULL;
+  best_score = -1;
   MPLIST_DO (pl, plist)
     {
-      MFTInfo *ft_info = MPLIST_VAL (pl);
-      int score;
-
-      if (! mplist_find_by_key (ft_info->charmap_list, registry))
-       continue;
-      /* We always ignore FOUNDRY.  */
-      ft_info->font.property[MFONT_FOUNDRY] = spec->property[MFONT_FOUNDRY];
-      score = mfont__score (&ft_info->font, spec, request, limited_size);
-      if (score >= 0
-         && (! best_font
-             || best_score > score))
+      MPLIST_DO (p, MPLIST_VAL (pl))
        {
-         best_font = ft_info;
-         best_score = score;
-         if (score == 0)
-           break;
+         MFTInfo *ft_info = MPLIST_VAL (p);
+         int score;
+
+         if (! mplist_find_by_key (ft_info->charmap_list, registry))
+           continue;
+
+         /* Always ignore FOUNDRY.  */
+         ft_info->font.property[MFONT_FOUNDRY] = spec->property[MFONT_FOUNDRY];
+         score = mfont__score (&ft_info->font, spec, request, limited_size);
+         if (score >= 0
+             && (! best_font
+                 || best_score > score))
+           {
+             best_font = ft_info;
+             best_score = score;
+             if (best_score == 0)
+               break;
+           }
        }
+      if (best_score == 0 || family != Mnil)
+       break;
     }
-  if (family == Mnil)
-    {
-      plist = MPLIST_NEXT (plist);
-      if (! MPLIST_TAIL_P (plist))
-       goto retry;
-    }
+  spec->property[MFONT_FAMILY] = spec_family_id;
+  request->property[MFONT_FAMILY] = request_family_id;
   if (! best_font)
     return NULL;
 
@@ -516,8 +664,10 @@ ft_open (MRealizedFont *rfont)
 
   MDEBUG_PRINT1 (" [FT-FONT] o %s\n", ft_info->filename);
   rfont->status = 1;
-  rfont->ascent = ft_info->ft_face->ascender >> 6;
-  rfont->descent = - (ft_info->ft_face->descender >> 6);
+  rfont->ascent = ft_info->ft_face->size->metrics.ascender >> 6;
+  rfont->descent = - (ft_info->ft_face->size->metrics.descender >> 6);
+  rfont->type = Mfreetype;
+  rfont->fontp = ft_info->ft_face;
   return 0;
 
  err:
@@ -540,13 +690,6 @@ ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring,
   MFTInfo *ft_info = (MFTInfo *) rfont->info;
   FT_Face ft_face = ft_info->ft_face;
   MGlyph *g = MGLYPH (from), *gend = MGLYPH (to);
-  FT_Int32 load_flags = FT_LOAD_RENDER;
-
-#ifdef FT_LOAD_TARGET_MONO
-  load_flags |= FT_LOAD_TARGET_MONO;
-#else
-  load_flags |= FT_LOAD_MONOCHROME;
-#endif
 
   for (; g != gend; g++)
     {
@@ -565,10 +708,13 @@ ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring,
            }
          else
            {
+#ifdef HAVE_FTBDF_H
              BDF_PropertyRec prop;
+#endif
 
              g->lbearing = 0;
              g->rbearing = g->width = ft_face->available_sizes->width;
+#ifdef HAVE_FTBDF_H
              if (FT_Get_BDF_Property (ft_face, "ASCENT", &prop) == 0)
                {
                  g->ascent = prop.u.integer;
@@ -576,6 +722,7 @@ ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring,
                  g->descent = prop.u.integer;
                }
              else
+#endif
                {
                  g->ascent = ft_face->available_sizes->height;
                  g->descent = 0;
@@ -586,7 +733,7 @@ ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring,
        {
          FT_Glyph_Metrics *metrics;
 
-         FT_Load_Glyph (ft_face, (FT_UInt) g->code, FT_LOAD_RENDER);
+         FT_Load_Glyph (ft_face, (FT_UInt) g->code, FT_LOAD_DEFAULT);
          metrics = &ft_face->glyph->metrics;
          g->lbearing = (metrics->horiBearingX >> 6);
          g->rbearing = (metrics->horiBearingX + metrics->width) >> 6;
@@ -746,51 +893,137 @@ ft_render (MDrawWindow win, int x, int y,
     }
 }
 
+static int
+ft_list (MFrame *frame, MPlist *plist, MFont *font, MSymbol language,
+        int maxnum)
+{
+  MPlist *pl, *p;
+#ifdef HAVE_FONTCONFIG
+  FcChar8 *lang = (language != Mnil ? (FcChar8 *) MSYMBOL_NAME (language)
+                  : NULL);
+#endif
+  int num = 0;
+
+  if (font)
+    {
+      MSymbol family = FONT_PROPERTY (font, MFONT_FAMILY);
+      pl = ft_list_generic (family);
+      if (pl)
+       family = Mnil;
+      else
+       pl = ft_list_family (family);
+      MPLIST_DO (pl, pl)
+       {
+         MPLIST_DO (p, MPLIST_PLIST (pl))
+           {
+             MFTInfo *ft_info = MPLIST_VAL (p);
+
+#ifdef HAVE_FONTCONFIG
+             if (lang && ft_info->langset
+                 && FcLangSetHasLang (ft_info->langset, lang) != FcLangEqual)
+               continue;
+#endif
+             mplist_add (plist, MPLIST_KEY (pl), &ft_info->font);
+             num++;
+             if (num == maxnum)
+               return num;
+           }
+         if (family != Mnil)
+           break;
+       }
+    }
+  else
+    {
+      ft_list_family (Mnil);
+      MPLIST_DO (p, ft_family_list)
+       {
+         MFTInfo *ft_info = MPLIST_VAL (p);
+
+#ifdef HAVE_FONTCONFIG
+         if (lang && ft_info->langset
+             && FcLangSetHasLang (ft_info->langset, lang) != FcLangEqual)
+           continue;
+#endif
+         mplist_add (plist, MPLIST_KEY (p), &ft_info->font);
+         num++;
+         if (num == maxnum)
+           break;
+       }
+    }
+  return num;
+}
+
 \f
 /* Internal API */
 
 MFontDriver mfont__ft_driver =
-  { ft_select, ft_open, ft_find_metric, ft_encode_char, ft_render };
+  { ft_select, ft_open, ft_find_metric, ft_encode_char, ft_render, ft_list };
 
 int
 mfont__ft_init ()
 {
-  struct {
-    char *ft_style;
-    char *weight, *style, *stretch;
-  } ft_to_prop_name[] =
-    { { "regular", "medium", "r", "normal" },
-      { "italic", "medium", "i", "normal" },
-      { "bold", "bold", "r", "normal" },
-      { "bold italic", "bold", "i", "normal" },
-      { "narrow", "medium", "r", "condensed" },
-      { "narrow italic", "medium", "i", "condensed" },
-      { "narrow bold", "bold", "r", "condensed" },
-      { "narrow bold italic", "bold", "i", "condensed" },
-      { "black", "black", "r", "normal" },
-      { "black italic", "black", "i", "normal" },
-      { "oblique", "medium", "o", "normal" },
-      { "boldoblique", "bold", "o", "normal" } };
   int i;
 
   if (FT_Init_FreeType (&ft_library) != 0)
     MERROR (MERROR_FONT_FT, -1);
 
-  ft_to_prop_size = sizeof (ft_to_prop_name) / sizeof (ft_to_prop_name[0]);
-  MTABLE_MALLOC (ft_to_prop, ft_to_prop_size, MERROR_FONT_FT);
   for (i = 0; i < ft_to_prop_size; i++)
-    {
-      ft_to_prop[i].ft_style = msymbol (ft_to_prop_name[i].ft_style);
-      ft_to_prop[i].weight = msymbol (ft_to_prop_name[i].weight);
-      ft_to_prop[i].style = msymbol (ft_to_prop_name[i].style);
-      ft_to_prop[i].stretch = msymbol (ft_to_prop_name[i].stretch);
-    }
+    ft_to_prop[i].len = strlen (ft_to_prop[i].ft_style);
 
   Municode_bmp = msymbol ("unicode-bmp");
   Municode_full = msymbol ("unicode-full");
   Miso10646_1 = msymbol ("iso10646-1");
   Miso8859_1 = msymbol ("iso8859-1");
 
+  Mmedium = msymbol ("medium");
+  Mr = msymbol ("r");
+  Mnull = msymbol ("");
+
+  for (i = 0; i < GENERIC_FAMILY_MAX; i++)
+    generic_family_table[i].list = NULL;
+  M_generic_family_info = msymbol ("  generic_family_info");
+  msymbol_put (msymbol ("serif"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_SERIF);
+  msymbol_put (msymbol ("sans-serif"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_SANS_SERIF);
+  msymbol_put (msymbol ("sans"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_SANS_SERIF);
+  msymbol_put (msymbol ("sans serif"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_SANS_SERIF);
+  msymbol_put (msymbol ("monospace"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_MONOSPACE);
+  msymbol_put (msymbol ("mono"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_MONOSPACE);
+  msymbol_put (msymbol ("m"), M_generic_family_info,
+              generic_family_table + GENERIC_FAMILY_MONOSPACE);
+
+#ifdef HAVE_FONTCONFIG
+  if (! fc_config)
+    {
+      char *pathname;
+      struct stat buf;
+      MPlist *plist;
+
+      FcInit ();
+      fc_config = FcConfigGetCurrent ();
+      MPLIST_DO (plist, mfont_freetype_path)
+       if (MPLIST_STRING_P (plist)
+           && (pathname = MPLIST_STRING (plist))
+           && stat (pathname, &buf) == 0)
+         {
+           FcStrList *strlist = FcConfigGetFontDirs (fc_config);
+           FcChar8 *dir;
+
+           while ((dir = FcStrListNext (strlist)))
+             if (strcmp ((char *) dir, pathname) == 0)
+               break;
+           if (! dir)
+             FcConfigAppFontAddDir (fc_config, (FcChar8 *) pathname);
+           FcStrListDone (strlist);
+         }
+    }
+#endif
+
   return 0;
 }
 
@@ -798,7 +1031,11 @@ void
 mfont__ft_fini ()
 {
   MPlist *plist, *p;
+  int i;
 
+  for (i = 0; i < GENERIC_FAMILY_MAX; i++)
+    if (generic_family_table[i].list)
+      M17N_OBJECT_UNREF (generic_family_table[i].list);
   if (ft_font_list)
     {
       MPLIST_DO (plist, ft_font_list)
@@ -813,44 +1050,91 @@ mfont__ft_fini ()
        }
       M17N_OBJECT_UNREF (ft_font_list);
       ft_font_list = NULL;
+
+      M17N_OBJECT_UNREF (ft_family_list);
+      ft_family_list = NULL;
     }
-  free (ft_to_prop);
   FT_Done_FreeType (ft_library);
   all_fonts_scaned = 0;
 }
 
 
 #ifdef HAVE_FONTCONFIG
+typedef struct
+{
+  int fc_value;
+  char *m17n_value;
+} FC_vs_M17N_font_prop;
+
+static FC_vs_M17N_font_prop fc_weight_table[] =
+  { { FC_WEIGHT_ULTRALIGHT, "extralight" },
+    { FC_WEIGHT_LIGHT, "light" },
+    { FC_WEIGHT_NORMAL, "normal" },
+    { FC_WEIGHT_MEDIUM, "medium" },
+    { FC_WEIGHT_DEMIBOLD, "demibold" },
+    { FC_WEIGHT_EXTRABOLD, "extrabold" },
+    { FC_WEIGHT_BLACK, "black" },
+    { FC_WEIGHT_MEDIUM, NULL } };
+
+static FC_vs_M17N_font_prop fc_slant_table[] =
+  { { FC_SLANT_ROMAN, "r" },
+    { FC_SLANT_ITALIC, "i" },
+    { FC_SLANT_OBLIQUE, "o" },
+    { FC_SLANT_ROMAN, NULL } };
+
+static FC_vs_M17N_font_prop fc_width_table[] =
+  { { FC_WIDTH_CONDENSED, "condensed" },
+    { FC_WIDTH_SEMIEXPANDED, "semicondensed" },
+    { FC_WIDTH_NORMAL, "normal" },
+    { FC_WIDTH_SEMIEXPANDED, "semiexpanded" },
+    { FC_WIDTH_EXPANDED, "expanded" },
+    { FC_WIDTH_NORMAL, NULL } };
+
+
+static MSymbol
+fc_decode_prop (int val, FC_vs_M17N_font_prop *table)
+{
+  int i;
+
+  for (i = 0; table[i].m17n_value; i++)
+    if (val <= table[i].fc_value)
+      return msymbol (table[i].m17n_value);
+  return msymbol (table[i - 1].m17n_value);
+}
+
+static int
+fc_encode_prop (char *name, FC_vs_M17N_font_prop *table)
+{
+  int i;
+
+  for (i = 0; table[i].m17n_value && strcmp (name, table[i].m17n_value); i++);
+  return table[i].fc_value;
+}
+
 int
 mfont__ft_parse_name (char *name, MFont *font)
 {
   FcPattern *pat = FcNameParse ((FcChar8 *) name);
-  FcResult result;
   FcChar8 *str;
+  int val;
   double size;
   
   if (! pat)
     return -1;
-  if ((result = FcPatternGetString (pat, FC_FOUNDRY, 0, &str)) == FcResultMatch)
+  if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch)
     mfont__set_property (font, MFONT_FOUNDRY, msymbol ((char *) str));
-  if ((result = FcPatternGetString (pat, FC_FAMILY, 0, &str)) == FcResultMatch)
+  if (FcPatternGetString (pat, FC_FAMILY, 0, &str) == FcResultMatch)
     mfont__set_property (font, MFONT_FAMILY, msymbol ((char *) str));
-  if ((result = FcPatternGetString (pat, FC_STYLE, 0, &str)) == FcResultMatch)
-    {
-      MSymbol style = msymbol ((char *) str);
-      int i;
-
-      for (i = 0; i < ft_to_prop_size; i++)
-       if (ft_to_prop[i].ft_style == style)
-         {
-           mfont__set_property (font, MFONT_WEIGHT, ft_to_prop[i].weight);
-           mfont__set_property (font, MFONT_STYLE, ft_to_prop[i].style);
-           mfont__set_property (font, MFONT_STRETCH, ft_to_prop[i].stretch);
-           break;
-         }
-    }
-  if ((result = FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size))
-      == FcResultMatch)
+  if (FcPatternGetInteger (pat, FC_WEIGHT, 0, &val) == FcResultMatch)
+    mfont__set_property (font, MFONT_WEIGHT,
+                        fc_decode_prop (val, fc_weight_table));
+  if (FcPatternGetInteger (pat, FC_SLANT, 0, &val) == FcResultMatch)
+    mfont__set_property (font, MFONT_STYLE,
+                        fc_decode_prop (val, fc_slant_table));
+  if (FcPatternGetInteger (pat, FC_WIDTH, 0, &val) == FcResultMatch)
+    mfont__set_property (font, MFONT_STRETCH,
+                        fc_decode_prop (val, fc_width_table));
+  if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
     font->property[MFONT_SIZE] = size * 10;
   FcPatternDestroy (pat);
   return 0;
@@ -862,97 +1146,124 @@ mfont__ft_unparse_name (MFont *font)
   FcPattern *pat = FcPatternCreate ();
   MSymbol sym, weight, style, stretch;
   char *name;
-  int i;
 
   if ((sym = (MSymbol) FONT_PROPERTY (font, MFONT_FOUNDRY)) != Mnil)
     FcPatternAddString (pat, FC_FOUNDRY, (FcChar8 *) MSYMBOL_NAME (sym));
   if ((sym = (MSymbol) FONT_PROPERTY (font, MFONT_FAMILY)) != Mnil)
     FcPatternAddString (pat, FC_FAMILY, (FcChar8 *) MSYMBOL_NAME (sym));
-  if ((weight = (MSymbol) FONT_PROPERTY (font, MFONT_WEIGHT)) == Mnil)
-    weight = msymbol ("medium");
-  if ((style = (MSymbol) FONT_PROPERTY (font, MFONT_STYLE)) == Mnil)
-    style = msymbol ("r");
-  if ((stretch = (MSymbol) FONT_PROPERTY (font, MFONT_STRETCH)) == Mnil)
-    stretch = msymbol ("normal");
-  for (i = 0; i < ft_to_prop_size; i++)
-    if (ft_to_prop[i].weight == weight
-       && ft_to_prop[i].style == style
-       && ft_to_prop[i].stretch == stretch)
-      FcPatternAddString (pat, FC_STYLE,
-                         (FcChar8 *) MSYMBOL_NAME (ft_to_prop[i].ft_style));
+  if ((weight = (MSymbol) FONT_PROPERTY (font, MFONT_WEIGHT)) != Mnil)
+    FcPatternAddInteger (pat, FC_WEIGHT, fc_encode_prop (MSYMBOL_NAME (weight),
+                                                        fc_weight_table));
+  if ((style = (MSymbol) FONT_PROPERTY (font, MFONT_STYLE)) != Mnil)
+    FcPatternAddInteger (pat, FC_SLANT, fc_encode_prop (MSYMBOL_NAME (style),
+                                                       fc_slant_table));
+  if ((stretch = (MSymbol) FONT_PROPERTY (font, MFONT_STRETCH)) != Mnil)
+    FcPatternAddInteger (pat, FC_WIDTH, fc_encode_prop (MSYMBOL_NAME (stretch),
+                                                       fc_width_table));
   name = (char *) FcNameUnparse (pat);
   FcPatternDestroy (pat);
   return name;
 }
 #endif /* HAVE_FONTCONFIG */
 
+\f
+#ifdef HAVE_OTF
+
+#define DEVICE_DELTA(table, size)                              \
+  (((size) >= (table).StartSize && (size) <= (table).EndSize)  \
+   ? (table).DeltaValue[(size) >= (table).StartSize]           \
+   : 0)
+
+void
+adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
+              unsigned code, int size, int *x, int *y)
+{
+  if (anchor->AnchorFormat == 2)
+    {
+      FT_Outline *outline;
+      int ap = anchor->f.f1.AnchorPoint;
+
+      FT_Load_Glyph (ft_face, (FT_UInt) code, FT_LOAD_MONOCHROME);
+      outline = &ft_face->glyph->outline;
+      if (ap < outline->n_points)
+       {
+         *x = outline->points[ap].x;
+         *y = outline->points[ap].y;
+       }
+    }
+  else if (anchor->AnchorFormat == 3)
+    {
+      *x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, size);
+      *y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, size);
+    }
+}
+
 int
 mfont__ft_drive_otf (MGlyphString *gstring, int from, int to,
-                    MRealizedFont *rfont,
-                    MSymbol script, MSymbol langsys, 
+                    MSymbol script, MSymbol langsys,
                     MSymbol gsub_features, MSymbol gpos_features)
 {
   int len = to - from;
-  MGlyph g;
-  int i;
-#ifdef HAVE_OTF
+  MGlyph *g = MGLYPH (from);
+  int i, gidx;
+  MRealizedFont *rfont;
   MFTInfo *ft_info;
   OTF *otf;
   OTF_GlyphString otf_gstring;
   OTF_Glyph *otfg;
   char *script_name, *language_name;
   char *gsub_feature_names, *gpos_feature_names;
-  int from_pos, to_pos;
-  int unitsPerEm;
+  int need_cmap;
 
   if (len == 0)
     return from;
 
-  ft_info = (MFTInfo *) rfont->info;
+  rfont = g->rface->rfont;
+  ft_info = rfont->info;
   if (ft_info->otf_flag < 0)
     goto simple_copy;
   otf = ft_info->otf;
-  if (! otf && (otf = OTF_open (ft_info->filename)))
+  if (! otf)
     {
-      if (OTF_get_table (otf, "head") < 0
-         || (OTF_check_table (otf, "GSUB") < 0
-             && OTF_check_table (otf, "GPOS") < 0))
+      otf = OTF_open (ft_info->filename);
+      if (otf && OTF_get_table (otf, "head") < 0)
        {
          OTF_close (otf);
+         otf = NULL;
+       }
+      if (! otf)
+       {
          ft_info->otf_flag = -1;
-         ft_info->otf = NULL;
          goto simple_copy;
        }
       ft_info->otf = otf;
     }
 
-  script_name = msymbol_name (script);
-  language_name = langsys != Mnil ? msymbol_name (langsys) : NULL;
+  if (script != Mnil)
+    script_name = msymbol_name (script);
+  else
+    script_name = NULL;
+  if (langsys != Mnil)
+    language_name = msymbol_name (langsys);
+  else
+    language_name = NULL;
   gsub_feature_names
     = (gsub_features == Mt ? "*"
        : gsub_features == Mnil ? NULL
        : msymbol_name (gsub_features));
+  if (gsub_feature_names && OTF_check_table (otf, "GSUB") < 0)
+    gsub_feature_names = NULL;
   gpos_feature_names
     = (gpos_features == Mt ? "*"
        : gpos_features == Mnil ? NULL
        : msymbol_name (gpos_features));
+  if (gpos_feature_names && OTF_check_table (otf, "GPOS") < 0)
+    gpos_feature_names = NULL;
 
-  g = gstring->glyphs[from];
-  from_pos = g.pos;
-  to_pos = g.to;
-  for (i = from + 1; i < to; i++)
-    {
-      if (from_pos > gstring->glyphs[i].pos)
-       from_pos = gstring->glyphs[i].pos;
-      if (to_pos < gstring->glyphs[i].to)
-       to_pos = gstring->glyphs[i].to;
-    }
-
-  unitsPerEm = otf->head->unitsPerEm;
   otf_gstring.size = otf_gstring.used = len;
-  otf_gstring.glyphs = (OTF_Glyph *) alloca (sizeof (OTF_Glyph) * len);
+  otf_gstring.glyphs = (OTF_Glyph *) malloc (sizeof (OTF_Glyph) * len);
   memset (otf_gstring.glyphs, 0, sizeof (OTF_Glyph) * len);
-  for (i = 0; i < len; i++)
+  for (i = 0, need_cmap = 0; i < len; i++)
     {
       if (gstring->glyphs[from + i].otf_encoded)
        {
@@ -962,96 +1273,164 @@ mfont__ft_drive_otf (MGlyphString *gstring, int from, int to,
       else
        {
          otf_gstring.glyphs[i].c = gstring->glyphs[from + i].code;
+         need_cmap++;
        }
     }
-      
-  if (OTF_drive_tables (otf, &otf_gstring, script_name, language_name,
-                       gsub_feature_names, gpos_feature_names) < 0)
+  if (need_cmap
+      && OTF_drive_cmap (otf, &otf_gstring) < 0)
     goto simple_copy;
-  g.pos = from_pos;
-  g.to = to_pos;
-  for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, otfg++)
+
+  OTF_drive_gdef (otf, &otf_gstring);
+  gidx = gstring->used;
+
+  if (gsub_feature_names)
     {
-      g.combining_code = 0;
-      g.c = otfg->c;
-      if (otfg->glyph_id)
+      if (OTF_drive_gsub (otf, &otf_gstring, script_name, language_name,
+                         gsub_feature_names) < 0)
+       goto simple_copy;
+      for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used; i++, otfg++)
        {
-         g.code = otfg->glyph_id;
+         MGlyph temp = *(MGLYPH (from + otfg->f.index.from));
+
+         temp.c = otfg->c;
+         temp.combining_code = 0;
+         if (otfg->glyph_id)
+           {
+             temp.code = otfg->glyph_id;
+             temp.otf_encoded = 1;
+           }
+         else
+           {
+             temp.code = temp.c;
+             temp.otf_encoded = 0;
+           }
+         temp.to = MGLYPH (from + otfg->f.index.to)->to;
+         MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF);
+       }
+    }
+  else
+    for (i = 0; i < len; i++)
+      {
+       MGlyph temp = gstring->glyphs[from + i];
+
+       if (otf_gstring.glyphs[i].glyph_id)
+         {
+           temp.code = otf_gstring.glyphs[i].glyph_id;
+           temp.otf_encoded = 1;
+         }
+       MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF);
+      }
+
+  ft_find_metric (rfont, gstring, gidx, gstring->used);
+
+  if (gpos_feature_names)
+    {
+      int u;
+      int size10, size;
+      MGlyph *base = NULL, *mark = NULL;
+
+      if (OTF_drive_gpos (otf, &otf_gstring, script_name, language_name,
+                         gpos_feature_names) < 0)
+       return to;
+
+      u = otf->head->unitsPerEm;
+      size10 = rfont->font.property[MFONT_SIZE];
+      size = size10 / 10;
+
+      for (i = 0, otfg = otf_gstring.glyphs, g = MGLYPH (gidx);
+          i < otf_gstring.used; i++, otfg++, g++)
+       {
+         MGlyph *prev;
+
+         if (! otfg->glyph_id)
+           continue;
          switch (otfg->positioning_type)
            {
+           case 0:
+             break;
            case 1: case 2:
              {
-               int off_x = 128, off_y = 128;
-
-               if (otfg->f.f1.format & OTF_XPlacement)
-                 off_x = ((double) (otfg->f.f1.value->XPlacement)
-                          * 100 / unitsPerEm + 128);
-               if (otfg->f.f1.format & OTF_YPlacement)
-                 off_y = ((double) (otfg->f.f1.value->YPlacement)
-                          * 100 / unitsPerEm + 128);
-               g.combining_code
-                 = MAKE_COMBINING_CODE (3, 2, 3, 0, off_y, off_x);
-               if ((otfg->f.f1.format & OTF_XAdvance)
-                   || (otfg->f.f1.format & OTF_YAdvance))
-                 off_y--;
+               int format = otfg->f.f1.format;
+
+               if (format & OTF_XPlacement)
+                 g->xoff = otfg->f.f1.value->XPlacement * size10 / u / 10;
+               if (format & OTF_XPlaDevice)
+                 g->xoff += DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, size);
+               if (format & OTF_YPlacement)
+                 g->yoff = - (otfg->f.f1.value->YPlacement * size10 / u / 10);
+               if (format & OTF_YPlaDevice)
+                 g->yoff -= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, size);
+               if (format & OTF_XAdvance)
+                 g->width += otfg->f.f1.value->XAdvance * size10 / u / 10;
+               if (format & OTF_XAdvDevice)
+                 g->width += DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, size);
              }
              break;
            case 3:
              /* Not yet supported.  */
              break;
-           case 4:
+           case 4: case 5:
+             if (! base)
+               break;
+             prev = base;
+             goto label_adjust_anchor;
+           default:            /* i.e. case 6 */
+             if (! mark)
+               break;
+             prev = mark;
+
+           label_adjust_anchor:
              {
-               int off_x, off_y;
-
-               off_x = ((double) (otfg->f.f4.base_anchor->XCoordinate
-                                  - otfg->f.f4.mark_anchor->XCoordinate)
-                        * 100 / unitsPerEm + 128);
-               off_y = ((double) (otfg->f.f4.base_anchor->YCoordinate
-                                  - otfg->f.f4.mark_anchor->YCoordinate)
-                        * 100 / unitsPerEm + 128);
-               g.combining_code
-                 = MAKE_COMBINING_CODE (3, 0, 3, 0, off_y, off_x);
+               int base_x, base_y, mark_x, mark_y;
+
+               base_x = otfg->f.f4.base_anchor->XCoordinate * size10 / u / 10;
+               base_y = otfg->f.f4.base_anchor->YCoordinate * size10 / u / 10;
+               mark_x = otfg->f.f4.mark_anchor->XCoordinate * size10 / u / 10;
+               mark_y = otfg->f.f4.mark_anchor->YCoordinate * size10 / u / 10;
+
+               if (otfg->f.f4.base_anchor->AnchorFormat != 1)
+                 adjust_anchor (otfg->f.f4.base_anchor, ft_info->ft_face,
+                                prev->code, size, &base_x, &base_y);
+               if (otfg->f.f4.mark_anchor->AnchorFormat != 1)
+                 adjust_anchor (otfg->f.f4.mark_anchor, ft_info->ft_face,
+                                g->code, size, &mark_x, &mark_y);
+               g->xoff = prev->xoff + (base_x - prev->width) - mark_x;
+               g->yoff = prev->yoff + mark_y - base_y;
+               g->combining_code = MAKE_PRECOMPUTED_COMBINDING_CODE ();
              }
-             break;
-           case 5:
-             /* Not yet supported.  */
-             break;
-           default:            /* i.e case 6 */
-             /* Not yet supported.  */
-             break;
            }
-         g.otf_encoded = 1;
-       }
-      else
-       {
-         g.code = otfg->c;
-         g.otf_encoded = 0;
+         if (otfg->GlyphClass == OTF_GlyphClass0)
+           base = mark = g;
+         else if (otfg->GlyphClass == OTF_GlyphClassMark)
+           mark = g;
+         else
+           base = g;
        }
-      MLIST_APPEND1 (gstring, glyphs, g, MERROR_FONT_OTF);
     }
+  free (otf_gstring.glyphs);
   return to;
 
  simple_copy:
-#endif /* HAVE_OTF */
+  ft_find_metric (rfont, gstring, from, to);
   for (i = 0; i < len; i++)
     {
-      g = gstring->glyphs[from + i];
-      MLIST_APPEND1 (gstring, glyphs, g, MERROR_FONT_OTF);
+      MGlyph temp = gstring->glyphs[from + i];
+      MLIST_APPEND1 (gstring, glyphs, temp, MERROR_FONT_OTF);
     }
+  free (otf_gstring.glyphs);
   return to;
 }
 
+
 int
 mfont__ft_decode_otf (MGlyph *g)
 {
-#ifdef HAVE_OTF
   MFTInfo *ft_info = (MFTInfo *) g->rface->rfont->info;
   int c = OTF_get_unicode (ft_info->otf, (OTF_GlyphID) g->code);
 
   return (c ? c : -1);
-#else  /* not HAVE_OTF */
-  return -1;
-#endif /* not HAVE_OTF */
 }
 
+#endif /* HAVE_OTF */
+
 #endif /* HAVE_FREETYPE */