/* font-ft.c -- FreeType interface sub-module.
   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
     National Institute of Advanced Industrial Science and Technology (AIST)
     Registration Number H15PRO112

   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 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.

   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., 51 Franklin Street, Fifth Floor,
   02111-1307, USA.  */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>

#include "m17n-gui.h"
#include "m17n-misc.h"
#include "internal.h"
#include "plist.h"
#include "symbol.h"
#include "language.h"
#include "internal-flt.h"
#include "internal-gui.h"
#include "font.h"
#include "face.h"

#ifdef HAVE_FREETYPE

#ifdef HAVE_FTBDF_H
#include FT_BDF_H
#endif

static int mdebug_flag = MDEBUG_FONT;

#ifdef HAVE_FONTCONFIG
#include <fontconfig/fcfreetype.h>

static FcConfig *fc_config;
static MSymbol Mgeneric_family;
#endif	/* HAVE_FONTCONFIG */

/* Font properties; Mnormal is already defined in face.c.  */
static MSymbol Mmedium, Mr, Mnull;

static MSymbol M0[5], M3_1, M1_0;

static FT_Library ft_library;

#ifdef HAVE_OTF
static OTF *invalid_otf = (OTF *) "";
static OTF *get_otf (MFLTFont *font, FT_Face *ft_face);
#endif /* HAVE_OTF */

typedef struct
{
  MFont font;
#ifdef HAVE_OTF
  /* NULL if not yet opened.  invalid_otf if not OTF.  */
  OTF *otf;
#endif /* HAVE_OTF */
#ifdef HAVE_FONTCONFIG
  FcLangSet *langset;
  FcCharSet *charset;
#endif	/* HAVE_FONTCONFIG */
} MFontFT;

typedef struct
{
  M17NObject control;
  FT_Face ft_face;		/* This must be the 2nd member. */
  MPlist *charmap_list;
  int face_encapsulated;
} MRealizedFontFT;

typedef struct
{
  char *ft_style;
  int len;
  enum MFontProperty prop;
  char *val;
} MFTtoProp;

static MFTtoProp ft_to_prop[] =
  { { "italic", 0, MFONT_STYLE, "i" },
    { "roman", 0, MFONT_STYLE, "r" },
    { "oblique", 0, MFONT_STYLE, "o" },
    { "regular", 0, MFONT_WEIGHT, "normal" },
    { "normal", 0, MFONT_WEIGHT, "normal" },
    /* 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
    containing fonts of the corresponding family.  In the deeper
    plist, keys are file names, values are (MFontFT *).  */
static MPlist *ft_font_list;

/** List of FreeType fonts.  Keys are script names, values are plists
    containing fonts supporting the corresponding script.  In the
    deeper plist, keys are family names, values are (MFontFT *).  */
static MPlist *ft_script_list;

/** List of FreeType fonts.  Keys are language names, values are
    plists containing fonts supporting the corresponding language.  In
    the deeper plist, keys are family names, values are (MFontFT *).  */
static MPlist *ft_language_list;

static MPlist *ft_file_list;

static int all_fonts_scaned;

#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 = '\0';							\
  } while (0)


static MPlist *ft_list_family (MSymbol, int, int);

static void
free_ft_rfont (void *object)
{
  MRealizedFontFT *ft_rfont = object;

  if (! ft_rfont->face_encapsulated)
    {
      M17N_OBJECT_UNREF (ft_rfont->charmap_list);
      FT_Done_Face (ft_rfont->ft_face);
    }
  free (ft_rfont);
}

static void
free_ft_info (MFontFT *ft_info)
{
#ifdef HAVE_OTF
  if (ft_info->otf && ft_info->otf != invalid_otf)
    OTF_close (ft_info->otf);
#endif /* HAVE_OTF */
#ifdef HAVE_FONTCONFIG
  if (ft_info->langset)
    FcLangSetDestroy (ft_info->langset);
  if (ft_info->charset)
    FcCharSetDestroy (ft_info->charset);
#endif	/* HAVE_FONTCONFIG */
  free (ft_info);
}

static MPlist *
ft_get_charmaps (FT_Face ft_face)
{
  MPlist *plist = mplist ();
  int unicode_bmp = -1, unicode_full = -1;
  int i;

  mplist_add (plist, Mt, (void *) -1);
  for (i = 0; i < ft_face->num_charmaps; i++)
    {
      MSymbol registry = Mnil;

      if (ft_face->charmaps[i]->platform_id == 0)
	{
	  if (ft_face->charmaps[i]->encoding_id <= 4)
	    registry = M0[ft_face->charmaps[i]->encoding_id], unicode_bmp = i;
	  if (ft_face->charmaps[i]->encoding_id == 4)
	    unicode_bmp = unicode_full = i;
	}
      else if (ft_face->charmaps[i]->platform_id == 3)
	{
	  if (ft_face->charmaps[i]->encoding_id == 1)
	    registry = M3_1, unicode_bmp = i;
	  else if (ft_face->charmaps[i]->encoding_id == 10)
	    unicode_bmp = unicode_full = i;
	}
      else if (ft_face->charmaps[i]->platform_id == 1
	       && ft_face->charmaps[i]->encoding_id == 0)
	{
	  registry = M1_0;
	  mplist_add (plist, Mapple_roman, (void *) i);
	}
      if (registry == Mnil)
	{
	  char registry_buf[16];

	  sprintf (registry_buf, "%d-%d",
		   ft_face->charmaps[i]->platform_id,
		   ft_face->charmaps[i]->encoding_id);
	  registry = msymbol (registry_buf);
	}
      mplist_add (plist, registry, (void *) i);
    }
  if (unicode_full >= 0)
    mplist_add (plist, Municode_full, (void *) unicode_full);
  if (unicode_bmp >= 0)
    {
      int i;

      mplist_add (plist, Municode_bmp, (void *) unicode_bmp);
      FT_Set_Charmap (ft_face, ft_face->charmaps[unicode_bmp]);
      for (i = 0x21; i < 0x7F && FT_Get_Char_Index (ft_face, i) > 0; i++);
      if (i == 0x7F)
	{
	  for (i = 0xC0; i < 0x100 && FT_Get_Char_Index (ft_face, i) > 0; i++);
	  if (i == 0x100)
	    mplist_add (plist, Miso8859_1, (void *) unicode_bmp);
	}
    }

  return plist;
}

static MFontFT *
ft_gen_font (FT_Face ft_face)
{
  MFontFT *ft_info;
  MFont *font;
  char *buf;
  int bufsize = 0;
  char *stylename;
  MSymbol family;
  int size;

  if (FT_IS_SCALABLE (ft_face))
    size = ft_face->size->metrics.y_ppem;
  else if (ft_face->num_fixed_sizes == 0)
    return NULL;
  else
    size = ft_face->available_sizes[0].height;

  MSTRUCT_CALLOC (ft_info, MERROR_FONT_FT);
  font = &ft_info->font;
  STRDUP_LOWER (buf, bufsize, ft_face->family_name);
  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);
  mfont__set_property (font, MFONT_REGISTRY, Municode_bmp);
  font->size = size * 10;
  font->type = MFONT_TYPE_OBJECT;
  font->source = MFONT_SOURCE_FT;
  font->file = NULL;

  stylename = ft_face->style_name;
  while (*stylename)
    {
      int i;

      for (i = 0; i < ft_to_prop_size; i++)
	if (! strncasecmp (ft_to_prop[i].ft_style, stylename,
			   ft_to_prop[i].len))
	  {
	    mfont__set_property (font, ft_to_prop[i].prop,
				 msymbol (ft_to_prop[i].val));
	    stylename += ft_to_prop[i].len;
	    break;
	  }
      if (i == ft_to_prop_size)
	{
	  char *p1 = stylename + 1;
	  MSymbol sym;

	  while (*p1 >= 'a' && *p1 <= 'z') p1++;
	  sym = msymbol__with_len (stylename, p1 - stylename);
	  for (i = MFONT_WEIGHT; i <= MFONT_STRETCH; i++)
	    if (msymbol_get (sym, mfont__property_table[i].property))
	      {
		mfont__set_property (font, i, sym);
		break;
	      }
	  stylename = p1;
	}
      while (*stylename && ! isalpha (*stylename))
	stylename++;
    }
  return ft_info;
}

#ifdef HAVE_FONTCONFIG

typedef struct
{
  int fc_value;
  char *m17n_value;
  MSymbol sym;
} FC_vs_M17N_font_prop;

static FC_vs_M17N_font_prop fc_weight_table[] =
  { { FC_WEIGHT_THIN, "thin" },
    { FC_WEIGHT_ULTRALIGHT, "extralight" },
    { FC_WEIGHT_LIGHT, "light" },
#ifdef FC_WEIGHT_BOOK
    { FC_WEIGHT_BOOK, "book" },
#endif	/* FC_WEIGHT_BOOK */
    { FC_WEIGHT_REGULAR, "normal" },
    { FC_WEIGHT_NORMAL, "normal" },
    { FC_WEIGHT_MEDIUM, "medium" },
    { FC_WEIGHT_DEMIBOLD, "demibold" },
    { FC_WEIGHT_BOLD, "bold" },
    { FC_WEIGHT_EXTRABOLD, "extrabold" },
    { FC_WEIGHT_BLACK, "black" },
    { FC_WEIGHT_HEAVY, "heavy" },
    { FC_WEIGHT_MEDIUM, NULL } };
int fc_weight_table_size =
  sizeof fc_weight_table / sizeof (FC_vs_M17N_font_prop);

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 } };
int fc_slant_table_size =
  sizeof fc_slant_table / sizeof (FC_vs_M17N_font_prop);

static FC_vs_M17N_font_prop fc_width_table[] =
  { { FC_WIDTH_ULTRACONDENSED, "ultracondensed" },
    { FC_WIDTH_EXTRACONDENSED, "extracondensed" },
    { FC_WIDTH_CONDENSED, "condensed" },
    { FC_WIDTH_SEMICONDENSED, "semicondensed" },
    { FC_WIDTH_NORMAL, "normal" },
    { FC_WIDTH_SEMIEXPANDED, "semiexpanded" },
    { FC_WIDTH_EXPANDED, "expanded" },
    { FC_WIDTH_EXTRAEXPANDED, "extraexpanded" },
    { FC_WIDTH_ULTRAEXPANDED, "ultraexpanded" },
    { FC_WIDTH_NORMAL, NULL } };
int fc_width_table_size =
  sizeof fc_width_table / sizeof (FC_vs_M17N_font_prop);


static FC_vs_M17N_font_prop *fc_all_table[] =
  { fc_weight_table, fc_slant_table, fc_width_table };

static MSymbol
fc_decode_prop (int val, FC_vs_M17N_font_prop *table, int size)
{
  int i = size / 2;

  if (val < table[i].fc_value)
    {
      for (i--; i >= 0; i--)
	if (val > table[i].fc_value)
	  break;
      i++;
    }
  else if (val > table[i].fc_value)
    {
      for (i++; i < size; i++)
	if (val < table[i].fc_value)
	  break;
      i--;
    }
  return table[i].sym;
}

static int
fc_encode_prop (MSymbol sym, FC_vs_M17N_font_prop *table)
{
  int i;

  for (i = 0; table[i].m17n_value; i++)
    if (table[i].sym == sym)
      break;
  return table[i].fc_value;
}

FcPattern *
fc_get_pattern (MFont *font)
{
  FcPattern *pat = FcPatternCreate ();
  MSymbol sym, weight, style, stretch;


  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)
    FcPatternAddInteger (pat, FC_WEIGHT,
			 fc_encode_prop (weight, fc_weight_table));
  if ((style = (MSymbol) FONT_PROPERTY (font, MFONT_STYLE)) != Mnil)
    FcPatternAddInteger (pat, FC_SLANT,
			 fc_encode_prop (style, fc_slant_table));
  if ((stretch = (MSymbol) FONT_PROPERTY (font, MFONT_STRETCH)) != Mnil)
    FcPatternAddInteger (pat, FC_WIDTH,
			 fc_encode_prop (stretch, fc_width_table));
  if (font->size > 0)
    {
      double size = font->size;
      FcPatternAddDouble (pat, FC_PIXEL_SIZE, size / 10);
    }
  else if (font->size < 0)
    {
      double size = - font->size;
      FcPatternAddDouble (pat, FC_SIZE, size / 10);
    }
  return pat;
}

static void
fc_parse_pattern (FcPattern *pat, char *family, MFontFT *ft_info)
{
  FcChar8 *str;
  int val;
  double size;
  char *buf;
  int bufsize = 0;
  MSymbol sym;
  FcLangSet *ls;
  FcCharSet *cs;
  MFont *font = &ft_info->font;

  MFONT_INIT (font);
  if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch)
    {
      STRDUP_LOWER (buf, bufsize, (char *) str);
      mfont__set_property (font, MFONT_FOUNDRY, msymbol (buf));
    }
  if (family)
    mfont__set_property (font, MFONT_FAMILY, msymbol (family));
  else if (FcPatternGetString (pat, FC_FAMILY, 0, &str) == FcResultMatch)
    {
      STRDUP_LOWER (buf, bufsize, (char *) str);
      mfont__set_property (font, MFONT_FAMILY, msymbol (buf));
    }
  if (FcPatternGetInteger (pat, FC_WEIGHT, 0, &val) == FcResultMatch)
    {
      sym = fc_decode_prop (val, fc_weight_table, fc_weight_table_size);
      mfont__set_property (font, MFONT_WEIGHT, sym);
    }
  if (FcPatternGetInteger (pat, FC_SLANT, 0, &val) == FcResultMatch)
    {
      sym = fc_decode_prop (val, fc_slant_table, fc_slant_table_size);
      mfont__set_property (font, MFONT_STYLE, sym);
    }
  if (FcPatternGetInteger (pat, FC_WIDTH, 0, &val) == FcResultMatch)
    {
      sym = fc_decode_prop (val, fc_width_table, fc_width_table_size);
      mfont__set_property (font, MFONT_STRETCH, sym);
    }
  if (FcPatternGetLangSet (pat, FC_LANG, 0, &ls) == FcResultMatch)
    {
      if (FcLangSetHasLang (ls, (FcChar8 *) "ja") != FcLangDifferentLang
	  || FcLangSetHasLang (ls, (FcChar8 *) "zh") != FcLangDifferentLang
	  || FcLangSetHasLang (ls, (FcChar8 *) "ko") != FcLangDifferentLang)
	font->for_full_width = 1;
      ft_info->langset = FcLangSetCopy (ls);
    }
  if (FcPatternGetCharSet (pat, FC_CHARSET, 0, &cs) == FcResultMatch)
    ft_info->charset = FcCharSetCopy (cs);

  mfont__set_property (font, MFONT_REGISTRY, Municode_bmp);
  font->type = MFONT_TYPE_SPEC;
  font->source = MFONT_SOURCE_FT;
  if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
    font->size = size * 10;
  if (FcPatternGetString (pat, FC_FILE, 0, &str) == FcResultMatch)
    font->file = msymbol ((char *) str);
}


static MFontFT *
fc_gen_font (FcPattern *pat, char *family)
{
  MFontFT *ft_info;

  MSTRUCT_CALLOC (ft_info, MERROR_FONT_FT);
  fc_parse_pattern (pat, family, ft_info);
  ft_info->font.type = MFONT_TYPE_OBJECT;
  return ft_info;
}

static void
fc_init_font_list (void)
{
  FcPattern *pattern = FcPatternCreate ();
  FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, NULL);
  FcFontSet *fs = FcFontList (fc_config, pattern, os);
  MPlist *plist = mplist ();
  char *buf;
  int bufsize = 0;
  int i;

  ft_font_list = plist;
  for (i = 0; i < fs->nfont; i++)
    {
      char *fam;

      if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
			      (FcChar8 **) &fam) != FcResultMatch)
	continue;
      STRDUP_LOWER (buf, bufsize, fam);
      plist = mplist_add (plist, msymbol (buf), NULL);
    }
  FcFontSetDestroy (fs);
  FcObjectSetDestroy (os);
  FcPatternDestroy (pattern);
}

static MPlist *
fc_list_pattern (FcPattern *pattern)
{
  FcObjectSet *os = NULL;
  FcFontSet *fs = NULL;
  MSymbol last_family = Mnil;
  MPlist *plist = NULL, *pl = NULL;
  char *buf;
  int bufsize = 0;
  int i;

  if (! (os = FcObjectSetBuild (FC_FAMILY, FC_FILE, NULL)))
    goto err;
  if (! (fs = FcFontList (fc_config, pattern, os)))
    goto err;

  for (i = 0; i < fs->nfont; i++)
    {
      MSymbol family, file;
      char *fam, *filename;
      MFontFT *ft_info;

      if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
			      (FcChar8 **) &fam) != FcResultMatch)
	continue;
      if (FcPatternGetString (fs->fonts[i], FC_FILE, 0,
			      (FcChar8 **) &filename) != FcResultMatch)
	continue;
      STRDUP_LOWER (buf, bufsize, fam);
      family = msymbol (buf);
      file = msymbol (filename);
      if (family != last_family)
	{
	  pl = MPLIST_PLIST (ft_list_family (family, 0, 1));
	  last_family = family;
	}
      ft_info = mplist_get (pl, file);
      if (ft_info)
	{
	  if (! plist)
	    plist = mplist ();
	  mplist_add (plist, family, ft_info);
	}
    }

 err:
  if (fs) FcFontSetDestroy (fs);
  if (os) FcObjectSetDestroy (os);
  return plist;
}

/* Return FcCharSet object built from CHAR_LIST or MT.  In the latter
   case, it is assured that the M-text contains at least one
   character.  */

static FcCharSet *
fc_build_charset (MPlist *char_list, MText *mt)
{
  FcCharSet *cs = FcCharSetCreate ();

  if (! cs)
    return NULL;
  if (char_list)
    {
      for (; ! MPLIST_TAIL_P (char_list); char_list = MPLIST_NEXT (char_list))
	if (! FcCharSetAddChar (cs, (FcChar32) MPLIST_INTEGER (char_list)))
	  {
	    FcCharSetDestroy (cs);
	    return NULL;
	  }
    }
  else
    {
      int i;

      for (i = mtext_nchars (mt) - 1; i >= 0; i--)
	if (! FcCharSetAddChar (cs, (FcChar32) mtext_ref_char (mt, i)))
	  {
	    FcCharSetDestroy (cs);
	    return NULL;
	  }
      if (mtext_nchars (mt) > 0
	  && (mt = mtext_get_prop (mt, 0, Mtext)))
	for (i = mtext_nchars (mt) - 1; i >= 0; i--)
	  if (! FcCharSetAddChar (cs, (FcChar32) mtext_ref_char (mt, i)))
	    {
	      FcCharSetDestroy (cs);
	      return NULL;
	    }
      }
  return cs;
}

#else	/* not HAVE_FONTCONFIG */

static MPlist *
ft_add_font (char *filename)
{
  FT_Face ft_face;
  char *stylename;
  int size = 0;
  MSymbol family;
  MFontFT *ft_info;
  MFont *font;
  MPlist *plist;
  int i;
  char *buf;
  int bufsize = 0;

  if (FT_New_Face (ft_library, filename, 0, &ft_face) != 0)
    return NULL;
  ft_info = ft_gen_font (ft_face);
  FT_Done_Face (ft_face);
  if (! ft_info)
    return NULL;

  font = &ft_info->font;
  font->file = msymbol (filename);

  plist = mplist_find_by_key (ft_font_list, family);
  if (plist)
    mplist_push (MPLIST_PLIST (plist), font->file, ft_info);
  else
    {
      plist = mplist ();
      mplist_add (plist, font->file, ft_info);
      plist = mplist_push (ft_font_list, family, plist);
    }
  return plist;
}

static void
ft_init_font_list (void)
{
  MPlist *plist;
  struct stat buf;
  char *pathname;
  char *path;
  USE_SAFE_ALLOCA;

  ft_font_list = mplist ();
  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))
	  ft_add_font (pathname);
	else if (S_ISDIR (buf.st_mode))
	  {
	    DIR *dir = opendir (pathname);

	    if (dir)
	      {
		int len = strlen (pathname);
		struct dirent *dp;

		while ((dp = readdir (dir)) != NULL)
		  {
		    SAFE_ALLOCA (path, len + strlen (dp->d_name) + 2);
		    strcpy (path, pathname);
		    path[len] =  '/';
		    strcpy (path + len + 1, dp->d_name);
		    ft_add_font (path);
		  }
		closedir (dir);
	      }
	  }
      }
  SAFE_FREE (path);
}

/* Return 1 iff the font pointed by FT_INFO has all characters in
   CHAR_LIST.  */

static int
ft_has_char_list_p (MFontFT *ft_info, MPlist *char_list)
{
  FT_Face ft_face;
  MPlist *cl;

  if (FT_New_Face (ft_library, MSYMBOL_NAME (ft_info->font.file), 0, &ft_face))
    return 0;
  MPLIST_DO (cl, char_list)
    if (FT_Get_Char_Index (ft_face, (FT_ULong) MPLIST_INTEGER (cl)) == 0)
      break;
  FT_Done_Face (ft_face);
  return MPLIST_TAIL_P (cl);
}

/* Return ((FAMILY . FONT) ...) where FONT is a pointer to MFontFT
   that supports characters in CHAR_LIST or MT.  One of CHAR_LIST or
   MT must be NULL.  */

static MPlist *
ft_list_char_list (MPlist *char_list, MText *mt)
{
  MPlist *plist = NULL, *pl, *p;

  if (! ft_font_list)
    ft_list_family (Mnil, 0, 1);

  if (mt)
    {
      int len = mtext_nchars (mt);
      MText *extra = mtext_get_prop (mt, 0, Mtext);
      int total_len = len + (extra ? mtext_nchars (extra) : 0);
      int i;

      char_list = mplist ();
      for (i = 0; i < total_len; i++)
	{
	  int c = (i < len ? mtext_ref_char (mt, i)
		   : mtext_ref_char (extra, i - len));

	  if (! mplist_find_by_value (char_list, (void *) c))
	    mplist_push (char_list, Minteger, (void *) c);
	}
    }

  MPLIST_DO (pl, ft_font_list)
    {
      MPLIST_DO (p, MPLIST_PLIST (pl))
	{
	  MFontFT *ft_info = MPLIST_VAL (p);

	  if (ft_has_char_list_p (ft_info, char_list))
	    {
	      MSymbol family = mfont_get_prop (&ft_info->font, Mfamily);

	      if (! plist)
		plist = mplist ();
	      mplist_push (plist, family, ft_info);
	    }
	}
    }
  if (mt)
    M17N_OBJECT_UNREF (char_list);
  return plist;
}
#endif	/* not HAVE_FONTCONFIG */


/* 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, int check_generic, int check_alias)
{
  MPlist *plist;
#ifdef HAVE_FONTCONFIG
  char *fam;
  MPlist *pl, *p;
  FcPattern *pattern;
  FcObjectSet *os;
  FcFontSet *fs;
  int i;
  char *buf;
  int bufsize = 0;
  MSymbol generic;

  if (! ft_font_list)
    {
      MSymbol sym;

      plist = ft_font_list = mplist ();
      pattern = FcPatternCreate ();
      os = FcObjectSetBuild (FC_FAMILY, NULL);
      fs = FcFontList (fc_config, pattern, os);
      for (i = 0; i < fs->nfont; i++)
	{
	  if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
				  (FcChar8 **) &fam) != FcResultMatch)
	    continue;
	  STRDUP_LOWER (buf, bufsize, fam);
	  sym = msymbol (buf);
	  if (! mplist_find_by_key (ft_font_list, sym))
	    plist = mplist_add (plist, sym, NULL);
	}
      FcFontSetDestroy (fs);
      FcObjectSetDestroy (os);
      FcPatternDestroy (pattern);
    }

  if (family == Mnil)
    {
      if (! all_fonts_scaned)
	{
	  MPLIST_DO (plist, ft_font_list)
	    {
	      if (! MPLIST_VAL (plist))
		ft_list_family (MPLIST_KEY (plist), 0, 1);
	    }
	  all_fonts_scaned = 1;
	}
      return ft_font_list;
    }

  plist = mplist_find_by_key (ft_font_list, family);
  if (plist)
    {
      if (! MPLIST_VAL (plist))
	{
	  fam = MSYMBOL_NAME (family);
	  pattern = FcPatternCreate ();
	  FcPatternAddString (pattern, FC_FAMILY, (FcChar8 *) fam);
	  os = FcObjectSetBuild (FC_FOUNDRY, FC_WEIGHT, FC_SLANT, FC_WIDTH,
				 FC_PIXEL_SIZE, FC_LANG, FC_CHARSET, FC_FILE,
				 NULL);
	  fs = FcFontList (fc_config, pattern, os);
	  p = pl = mplist ();
	  for (i = 0; i < fs->nfont; i++)
	    {
	      MFontFT *ft_info = fc_gen_font (fs->fonts[i], fam);
	      p = mplist_add (p, ft_info->font.file, ft_info);
	    }
	  MPLIST_VAL (plist) = pl;
	  FcFontSetDestroy (fs);
	  FcObjectSetDestroy (os);
	  FcPatternDestroy (pattern);
	}
    }
  else if (check_generic
	   && (generic = msymbol_get (family, Mgeneric_family)) != Mnil)
    {
      /* Check if FAMILY is a geneneric family (e.g. `serif').  */
      FcChar8 *fam8;
      
      if (family != generic)
	plist = ft_list_family (generic, 1, 1);
      else
	{
	  fam = MSYMBOL_NAME (family);
	  plist = mplist ();
	  mplist_push (ft_font_list, family, plist);
	  pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, fam, NULL);
	  FcConfigSubstitute (fc_config, pattern, FcMatchPattern);
	  for (i = 0; 1; i++)
	    {
	      if (FcPatternGetString (pattern, FC_FAMILY, i, &fam8)
		  != FcResultMatch)
		break;
	      STRDUP_LOWER (buf, bufsize, (char *) fam8);
	      family = msymbol (buf);
	      if (msymbol_get (family, Mgeneric_family))
		break;
	      pl = ft_list_family (family, 0, 1);
	      if (! pl)
		continue;
	      MPLIST_DO (pl, MPLIST_PLIST (pl))
		plist = mplist_add (plist, Mt, MPLIST_VAL (pl));
	    }
	  plist = ft_font_list;
	}
    }
  else if (check_alias)
    {
      /* Check if there exist an alias.  */
      pl = mplist ();
      plist = mplist_add (ft_font_list, family, pl);

      pattern = FcPatternBuild (NULL,
				FC_FAMILY, FcTypeString, MSYMBOL_NAME (family),
				NULL);
      FcConfigSubstitute (fc_config, pattern, FcMatchPattern);

      for (i = 0; FcPatternGetString (pattern, FC_FAMILY, i,
				      (FcChar8 **) &fam) == FcResultMatch;
	   i++);
      if (i > 0)
	{
	  /* The last one is a generic family.  */
	  MSymbol sym;
	  int j;
	  FcPattern *pat = FcPatternBuild (NULL, FC_FAMILY, FcTypeString, fam,
					   NULL);

	  FcConfigSubstitute (fc_config, pat, FcMatchPattern);
	  for (j = 0; FcPatternGetString (pat, FC_FAMILY, j,
					  (FcChar8 **) &fam) == FcResultMatch;
	       j++);

	  /* Now we know that the last J fonts in PATTERN are from
	     generic font, and the first one is not available.  So,
	     the remaining ones are aliases.  */
	  j = i - j;
	  for (i = 1; i < j; i++)
	    {
	      FcPatternGetString (pattern, FC_FAMILY, i, (FcChar8 **) &fam);
	      STRDUP_LOWER (buf, bufsize, fam);
	      sym = msymbol (buf);
	      p = MPLIST_PLIST (ft_list_family (sym, 0, 0));
	      if (! MPLIST_TAIL_P (p))
		MPLIST_DO (p, p)
		  mplist_push (pl, Mt, MPLIST_VAL (p));
	    }
	}
    }
  else
    {
      pl = mplist ();
      plist = mplist_add (ft_font_list, family, pl);
    }

#else  /* not HAVE_FONTCONFIG */

  if (! all_fonts_scaned)
    {
      ft_init_font_list ();
      all_fonts_scaned = 1;
    }
  if (family == Mnil)
    plist = ft_font_list;
  else
    {
      plist = mplist_find_by_key (ft_font_list, family);
      if (! plist)
	plist = mplist_push (ft_font_list, family, mplist ());
    }
#endif  /* not HAVE_FONTCONFIG */

  return plist;
}

static MPlist *
ft_list_language (MSymbol language)
{
  MPlist *plist = NULL;
  MText *mt;

  if (! ft_language_list)
    ft_language_list = mplist ();
  else if ((plist = mplist_find_by_key (ft_language_list, language)))
    return (MPLIST_VAL (plist) ? MPLIST_PLIST (plist) : NULL);

  mt = mlanguage_text (language);

#ifdef HAVE_FONTCONFIG
  {
    FcPattern *pattern = NULL;
    FcCharSet *cs = NULL;
    FcLangSet *ls = NULL;

    if (! (pattern = FcPatternCreate ()))
      goto err;

    if (mt && mtext_nchars (mt) > 0)
      {
	cs = fc_build_charset (NULL, mt);	
	if (cs && ! FcPatternAddCharSet (pattern, FC_CHARSET, cs))
	  goto err;
      }
    else
      {
	if (! (ls = FcLangSetCreate ()))
	  goto err;
	if (! FcLangSetAdd (ls, (FcChar8 *) MSYMBOL_NAME (language))
	    || ! FcPatternAddLangSet (pattern, FC_LANG, ls))
	  goto err;
      }

    plist = fc_list_pattern (pattern);
  err:
    if (cs) FcCharSetDestroy (cs);
    if (ls) FcLangSetDestroy (ls);
    if (pattern) FcPatternDestroy (pattern);
  }
#else	/* not HAVE_FONTCONFIG */
  if (mt && mtext_nchars (mt) > 0)
    plist = ft_list_char_list (NULL, mt);
#endif  /* not HAVE_FONTCONFIG */

  mplist_push (ft_language_list, language, plist);
  return plist;
}

static MPlist *
ft_list_script (MSymbol script)
{
  MPlist *plist = NULL;
  MPlist *char_list;

  if (! ft_script_list)
    ft_script_list = mplist ();
  else if ((plist = mplist_find_by_key (ft_script_list, script)))
    return (MPLIST_VAL (plist) ? MPLIST_PLIST (plist) : NULL);

  char_list = mscript__char_list (script);

#ifdef HAVE_FONTCONFIG
  if (char_list)
    {
      FcPattern *pattern = NULL;
      FcCharSet *cs;

      if (! (pattern = FcPatternCreate ()))
	goto err;
      cs = fc_build_charset (char_list, NULL);
      if (cs && ! FcPatternAddCharSet (pattern, FC_CHARSET, cs))
	  goto err;
      plist = fc_list_pattern (pattern);
    err:
      if (cs) FcCharSetDestroy (cs);
      if (pattern) FcPatternDestroy (pattern);
    }
#else  /* not HAVE_FONTCONFIG */
  if (char_list)
    plist = ft_list_char_list (char_list, NULL);
#endif	/* not HAVE_FONTCONFIG */

  mplist_push (ft_script_list, script, plist);
  return (plist);
}

static int
ft_check_cap_otf (MFontFT *ft_info, MFontCapability *cap, FT_Face ft_face)
{
#ifdef HAVE_OTF
  if (ft_info->otf == invalid_otf)
    return -1;
  if (! ft_info->otf)
    {
#if (LIBOTF_MAJOR_VERSION > 0 || LIBOTF_MINOR_VERSION > 9 || LIBOTF_RELEASE_NUMBER > 4)
      if (ft_face)
	ft_info->otf = OTF_open_ft_face (ft_face);
      else
#endif
	ft_info->otf = OTF_open (MSYMBOL_NAME (ft_info->font.file));
      if (! ft_info->otf)
	{
	  ft_info->otf = invalid_otf;
	  return -1;
	}
    }
  if (cap->features[MFONT_OTT_GSUB].nfeatures
      && (OTF_check_features
	  (ft_info->otf, 1,
	   cap->script_tag, cap->langsys_tag,
	   cap->features[MFONT_OTT_GSUB].tags,
	   cap->features[MFONT_OTT_GSUB].nfeatures) != 1))
    return -1;
  if (cap->features[MFONT_OTT_GPOS].nfeatures
      && (OTF_check_features
	  (ft_info->otf, 0,
	   cap->script_tag, cap->langsys_tag,
	   cap->features[MFONT_OTT_GPOS].tags,
	   cap->features[MFONT_OTT_GPOS].nfeatures) != 1))
    return -1;
  return 0;
#else	/* not HAVE_OTF */
  return -1;
#endif	/* not HAVE_OTF */
}

static int
ft_check_language (MFontFT *ft_info, MSymbol language, FT_Face ft_face)
{
  MText *mt;
  MText *extra;
  int ft_face_allocaed = 0;
  int len, total_len;
  int i;

#ifdef HAVE_FONTCONFIG
  if (ft_info->langset
      && (FcLangSetHasLang (ft_info->langset,
			    (FcChar8 *) MSYMBOL_NAME (language))
	  != FcLangDifferentLang))
    return 0;
#endif	/* HAVE_FONTCONFIG */

  mt = mlanguage_text (language);
  if (! mt || mtext_nchars (mt) == 0)
    return -1;

  if (! ft_face)
    {
      char *filename = MSYMBOL_NAME (ft_info->font.file);

      if (FT_New_Face (ft_library, filename, 0, &ft_face))
	return -1;
      ft_face_allocaed = 1;
    }

  len = mtext_nchars (mt);
  extra = mtext_get_prop (mt, 0, Mtext);
  total_len = len + (extra ? mtext_nchars (extra) : 0);

  for (i = 0; i < total_len; i++)
    {
      int c = (i < len ? mtext_ref_char (mt, i)
	       : mtext_ref_char (extra, i - len));

#ifdef HAVE_FONTCONFIG
      if (ft_info->charset
	  && FcCharSetHasChar (ft_info->charset, (FcChar32) c) == FcFalse)
	break;
#endif	/* HAVE_FONTCONFIG */
      if (FT_Get_Char_Index (ft_face, (FT_ULong) c) == 0)
	break;
    }

  if (ft_face_allocaed)
    FT_Done_Face (ft_face);

  return (i == total_len ? 0 : -1);
}

static int
ft_check_script (MFontFT *ft_info, MSymbol script, FT_Face ft_face)
{
  MPlist *char_list = mscript__char_list (script);

  if (! char_list)
    return -1;
#ifdef HAVE_FONTCONFIG
  if (ft_info->charset)
    {
      MPLIST_DO (char_list, char_list)
	if (FcCharSetHasChar (ft_info->charset,
			      (FcChar32) MPLIST_INTEGER (char_list)) == FcFalse)
	  break;
    }
  else
#endif	/* HAVE_FONTCONFIG */
    {
      int ft_face_allocaed = 0;

      if (! ft_face)
	{
	  char *filename = MSYMBOL_NAME (ft_info->font.file);

	  if (FT_New_Face (ft_library, filename, 0, &ft_face))
	    return -1;
	  ft_face_allocaed = 1;
	}

      MPLIST_DO (char_list, char_list)
	if (FT_Get_Char_Index (ft_face, (FT_ULong) MPLIST_INTEGER (char_list))
	    == 0)
	  break;
      if (ft_face_allocaed)
	FT_Done_Face (ft_face);
    }

  return (MPLIST_TAIL_P (char_list) ? 0 : -1);
}

static MPlist *ft_default_list;

static MPlist *
ft_list_default ()
{
  if (ft_default_list)
    return ft_default_list;
  ft_default_list = mplist ();
#ifdef HAVE_FONTCONFIG
  {
    FcPattern *pat = FcPatternCreate ();
    FcChar8 *fam;
    char *buf;
    int bufsize = 0;
    int i;

    FcConfigSubstitute (fc_config, pat, FcMatchPattern);
    for (i = 0; FcPatternGetString (pat, FC_FAMILY, i, &fam) == FcResultMatch;
	 i++)
      {
	MSymbol family;
	MPlist *plist;

	STRDUP_LOWER (buf, bufsize, (char *) fam);
	family = msymbol (buf);
	if (msymbol_get (family, Mgeneric_family))
	  continue;
	plist = MPLIST_PLIST (ft_list_family (family, 0, 1));
	MPLIST_DO (plist, plist)
	  mplist_add (ft_default_list, family, MPLIST_VAL (plist));
      }
  }
#else  /* not HAVE_FONTCONFIG */
  {
    MPlist *plist, *pl;

    MPLIST_DO (plist, ft_list_family (Mnil, 0, 1))
      {
	pl = MPLIST_PLIST (plist);
	if (! MPLIST_TAIL_P (pl))
	  mplist_add (ft_default_list, MPLIST_KEY (plist), pl);
      }
  }
#endif	/* not HAVE_FONTCONFIG */
  return ft_default_list;
}


static MPlist *ft_capability_list;

static MPlist *
ft_list_capability (MSymbol capability)
{
  MFontCapability *cap;
  MPlist *plist = NULL, *pl;

  if (! ft_capability_list)
    ft_capability_list = mplist ();
  else if ((plist = mplist_find_by_key (ft_capability_list, capability)))
    return (MPLIST_VAL (plist) ? MPLIST_VAL (plist) : NULL);

  cap = mfont__get_capability (capability);

  if (cap && cap->language != Mnil)
    {
      plist = ft_list_language (cap->language);
      if (! plist)
	return NULL;
      plist = mplist_copy (plist);
    }

  if (cap && cap->script != Mnil)
    {
      if (! plist)
	{
	  plist = ft_list_script (cap->script);
	  if (! plist)
	    return NULL;
	  plist = mplist_copy (plist);
	}
      else
	{
	  for (pl = plist; ! MPLIST_TAIL_P (pl);)
	    {
	      if (ft_check_script (MPLIST_VAL (pl), cap->script, NULL) < 0)
		mplist_pop (pl);
	      else
		pl = MPLIST_NEXT (pl);
	    }
	}

      if (cap->script_tag)
	{
	  for (pl = plist; ! MPLIST_TAIL_P (pl);)
	    {
	      if (ft_check_cap_otf (MPLIST_VAL (pl), cap, NULL) < 0)
		mplist_pop (pl);
	      else
		pl = MPLIST_NEXT (pl);
	    }
	}

      if (MPLIST_TAIL_P (plist))
	{
	  M17N_OBJECT_UNREF (plist);
	  plist = NULL;
	}
    }

  mplist_push (ft_capability_list, capability, plist);
  return plist;
}


static MPlist *
ft_list_file (MSymbol filename)
{
  MPlist *plist = NULL;

  if (! ft_file_list)
    ft_file_list = mplist ();
  else if ((plist = mplist_find_by_key (ft_file_list, filename)))
    return (MPLIST_VAL (plist) ? MPLIST_PLIST (plist) : NULL);

#ifdef HAVE_FONTCONFIG
  {
    FcPattern *pattern = FcPatternCreate ();
    FcObjectSet *os;
    FcFontSet *fs;

    FcPatternAddString (pattern, FC_FILE, (FcChar8 *) MSYMBOL_NAME (filename));
    os = FcObjectSetBuild (FC_FAMILY, NULL);
    fs = FcFontList (fc_config, pattern, os);
    if (fs->nfont > 0)
      {
	char *fam;
	char *buf;
	int bufsize = 0;

	if (FcPatternGetString (fs->fonts[0], FC_FAMILY, 0,
				(FcChar8 **) &fam) == FcResultMatch)
	  {
	    MSymbol family;
	    MPlist *pl;

	    STRDUP_LOWER (buf, bufsize, fam);
	    family = msymbol (buf);
	    pl = ft_list_family (family, 0, 1);
	    MPLIST_DO (pl, MPLIST_PLIST (pl))
	      {
		MFontFT *ft_info = MPLIST_VAL (pl);

		if (ft_info->font.file == filename)
		  {
		    plist = mplist ();
		    mplist_add (plist, family, ft_info);
		    break;
		  }
	      }
	  }
      }
  }
#else  /* not HAVE_FONTCONFIG */
  {
    MPlist *pl, *p;

    MPLIST_DO (pl, ft_list_family (Mnil, 0, 1))
      {
	MPLIST_DO (p, MPLIST_PLIST (pl))
	  {
	    MFontFT *ft_info = MPLIST_VAL (pl);

	    if (ft_info->font.file == filename)
	      {
		plist = mplist ();
		mplist_add (plist, MPLIST_KEY (pl), ft_info);
		break;
	      }
	  }
	if (plist)
	  break;
      }
  }
#endif	/* not HAVE_FONTCONFIG */

  mplist_push (ft_file_list, filename, plist);
  return plist;
}

/* The FreeType font driver function SELECT.  */

static MFont *
ft_select (MFrame *frame, MFont *font, int limited_size)
{
  MFont *found = NULL;
#ifdef HAVE_FONTCONFIG
  MPlist *plist, *pl;
  MFontFT *ft_info;
  int check_font_property = 1;

  if (font->file != Mnil)
    {
      plist = ft_list_file (font->file);
      if (! plist)
	return NULL;
      check_font_property = 0;
    }
  else
    {
      MSymbol family = FONT_PROPERTY (font, MFONT_FAMILY);

      if (family)
	plist = MPLIST_PLIST (ft_list_family (family, 1, 1));
      else
	plist = ft_list_default ();
      if (MPLIST_TAIL_P (plist))
	return NULL;
    }

  plist = mplist_copy (plist);

  if (font->capability != Mnil)
    {
      MFontCapability *cap = mfont__get_capability (font->capability);

      for (pl = plist; ! MPLIST_TAIL_P (pl);)
	{
	  if (cap->script_tag && ft_check_cap_otf (MPLIST_VAL (pl), cap, NULL) < 0)
	    {
	      mplist_pop (pl);
	      continue;
	    }
	  if (cap->language
	      && ft_check_language (MPLIST_VAL (pl), cap->language, NULL) < 0)
	    mplist_pop (pl);
	  else
	    pl = MPLIST_NEXT (pl);
	}
    }

  if (check_font_property)
    {
      MSymbol weight = FONT_PROPERTY (font, MFONT_WEIGHT);
      MSymbol style = FONT_PROPERTY (font, MFONT_STYLE);
      MSymbol stretch = FONT_PROPERTY (font, MFONT_STRETCH);
      MSymbol alternate_weight = Mnil;

      if (weight == Mnormal)
	alternate_weight = Mmedium;
      else if (weight == Mmedium)
	alternate_weight = Mnormal;
      if (weight != Mnil || style != Mnil || stretch != Mnil || font->size > 0)
	for (pl = plist; ! MPLIST_TAIL_P (pl); )
	  {
	    ft_info = MPLIST_VAL (pl);
	    if ((weight != Mnil
		 && (weight != FONT_PROPERTY (&ft_info->font, MFONT_WEIGHT)
		     && alternate_weight != FONT_PROPERTY (&ft_info->font, MFONT_WEIGHT)))
		|| (style != Mnil
		    && style != FONT_PROPERTY (&ft_info->font, MFONT_STYLE))
		|| (stretch != Mnil
		    && stretch != FONT_PROPERTY (&ft_info->font,
						 MFONT_STRETCH))
		|| (font->size > 0
		    && ft_info->font.size > 0
		    && ft_info->font.size != font->size))
	      mplist_pop (pl);
	    else
	      pl = MPLIST_NEXT (pl);
	  }
    }

  MPLIST_DO (pl, plist)
    {
      font = MPLIST_VAL (plist);
      if (limited_size == 0
	  || font->size == 0
	  || font->size <= limited_size)
	{
	  found = font;
	  break;
	}
    }
  M17N_OBJECT_UNREF (plist);
#endif	/* HAVE_FONTCONFIG */
  return found;
}


static MRealizedFont *
ft_open (MFrame *frame, MFont *font, MFont *spec, MRealizedFont *rfont)
{
  MFontFT *ft_info = (MFontFT *) font;
  int reg = spec->property[MFONT_REGISTRY];
  MSymbol registry = FONT_PROPERTY (spec, MFONT_REGISTRY);
  MRealizedFontFT *ft_rfont;
  FT_Face ft_face;
  MPlist *plist, *charmap_list = NULL;
  int charmap_index;
  int size;

  if (font->size)
    /* non-scalable font */
    size = font->size;
  else if (spec->size)
    {
      int ratio = mfont_resize_ratio (font);

      size = ratio == 100 ? spec->size : spec->size * ratio / 100;
    }
  else
    size = 120;

  if (rfont)
    {
      charmap_list = ((MRealizedFontFT *) rfont->info)->charmap_list;
      for (; rfont; rfont = rfont->next)
	if (rfont->font == font
	    && (rfont->font->size ? rfont->font->size == size
		: rfont->spec.size == size)
	    && rfont->spec.property[MFONT_REGISTRY] == reg
	    && rfont->driver == &mfont__ft_driver)
	  return rfont;
    }

  MDEBUG_DUMP (" [FONT-FT] opening ", "", mdebug_dump_font (&ft_info->font));

  if (FT_New_Face (ft_library, MSYMBOL_NAME (ft_info->font.file), 0,
		   &ft_face))
    {
      font->type = MFONT_TYPE_FAILURE;
      MDEBUG_PRINT ("  no (FT_New_Face)\n");
      return NULL;
    }
  if (charmap_list)
    M17N_OBJECT_REF (charmap_list);
  else
    charmap_list = ft_get_charmaps (ft_face);
  if (registry == Mnil)
    registry = Municode_bmp;
  plist = mplist_find_by_key (charmap_list, registry);
  if (! plist)
    {
      FT_Done_Face (ft_face);
      M17N_OBJECT_UNREF (charmap_list);
      MDEBUG_PRINT1 ("  no (%s)\n", MSYMBOL_NAME (registry));
      return NULL;
    }
  charmap_index = (int) MPLIST_VAL (plist);
  if ((charmap_index >= 0
       && FT_Set_Charmap (ft_face, ft_face->charmaps[charmap_index]))
      || FT_Set_Pixel_Sizes (ft_face, 0, size / 10))
    {
      FT_Done_Face (ft_face);
      M17N_OBJECT_UNREF (charmap_list);
      font->type = MFONT_TYPE_FAILURE;
      MDEBUG_PRINT1 ("  no (size %d)\n", size);
      return NULL;
    }

  M17N_OBJECT (ft_rfont, free_ft_rfont, MERROR_FONT_FT);
  ft_rfont->ft_face = ft_face;
  ft_rfont->charmap_list = charmap_list;
  MSTRUCT_CALLOC (rfont, MERROR_FONT_FT);
  rfont->id = ft_info->font.file;
  rfont->spec = *font;
  rfont->spec.type = MFONT_TYPE_REALIZED;
  rfont->spec.property[MFONT_REGISTRY] = reg;
  rfont->spec.size = size;
  rfont->frame = frame;
  rfont->font = font;
  rfont->driver = &mfont__ft_driver;
  rfont->info = ft_rfont;
  rfont->fontp = ft_face;
  rfont->ascent = ft_face->size->metrics.ascender;
  rfont->descent = - ft_face->size->metrics.descender;
  rfont->max_advance = ft_face->size->metrics.max_advance;
  rfont->baseline_offset = 0;
  rfont->x_ppem = ft_face->size->metrics.x_ppem;
  rfont->y_ppem = ft_face->size->metrics.y_ppem;
#ifdef HAVE_FTBDF_H
  {
    BDF_PropertyRec prop;

    if (! FT_IS_SCALABLE (ft_face)
	&& FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET", &prop) == 0)
      {
	rfont->baseline_offset = prop.u.integer << 6;
	rfont->ascent += prop.u.integer << 6;
	rfont->descent -= prop.u.integer << 6;
      }
  }
#endif	/* HAVE_FTBDF_H */
  if (FT_IS_SCALABLE (ft_face))
    rfont->average_width = 0;
  else
    rfont->average_width = ft_face->available_sizes->width << 6;
  rfont->next = MPLIST_VAL (frame->realized_font_list);
  MPLIST_VAL (frame->realized_font_list) = rfont;
  MDEBUG_PRINT ("  ok\n");
  return rfont;
}

/* The FreeType font driver function FIND_METRIC.  */

static void
ft_find_metric (MRealizedFont *rfont, MGlyphString *gstring,
		int from, int to)
{
  FT_Face ft_face = rfont->fontp;
  MGlyph *g = MGLYPH (from), *gend = MGLYPH (to);

  for (; g != gend; g++)
    {
      if (g->g.measured)
	continue;
      if (g->g.code == MCHAR_INVALID_CODE)
	{
	  if (FT_IS_SCALABLE (ft_face))
	    {
	      g->g.lbearing = 0;
	      g->g.rbearing = ft_face->size->metrics.max_advance;
	      g->g.xadv = g->g.rbearing;
	      g->g.ascent = ft_face->size->metrics.ascender;
	      g->g.descent = - ft_face->size->metrics.descender;
	    }
	  else
	    {
#ifdef HAVE_FTBDF_H
	      BDF_PropertyRec prop;
#endif	/* HAVE_FTBDF_H */

	      g->g.lbearing = 0;
	      g->g.rbearing = g->g.xadv = ft_face->available_sizes->width << 6;
#ifdef HAVE_FTBDF_H
	      if (FT_Get_BDF_Property (ft_face, "ASCENT", &prop) == 0)
		{
		  g->g.ascent = prop.u.integer << 6;
		  FT_Get_BDF_Property (ft_face, "DESCENT", &prop);
		  g->g.descent = prop.u.integer << 6;
		  if (FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET",
					   & prop) == 0)
		    {
		      g->g.ascent += prop.u.integer << 6;
		      g->g.descent -= prop.u.integer << 6;
		    }
		}
	      else
#endif	/* HAVE_FTBDF_H */
		{
		  g->g.ascent = ft_face->available_sizes->height << 6;
		  g->g.descent = 0;
		}
	    }
	}
      else
	{
	  FT_Glyph_Metrics *metrics;

	  FT_Load_Glyph (ft_face, (FT_UInt) g->g.code, FT_LOAD_DEFAULT);
	  metrics = &ft_face->glyph->metrics;
	  g->g.lbearing = metrics->horiBearingX;
	  g->g.rbearing = metrics->horiBearingX + metrics->width;
	  g->g.xadv = metrics->horiAdvance;
	  g->g.ascent = metrics->horiBearingY;
	  g->g.descent = metrics->height - metrics->horiBearingY;
	}
      g->g.yadv = 0;
      g->g.ascent += rfont->baseline_offset;
      g->g.descent -= rfont->baseline_offset;
      g->g.measured = 1;
    }
}

static int
ft_has_char (MFrame *frame, MFont *font, MFont *spec, int c, unsigned code)
{
  MRealizedFont *rfont = NULL;
  MRealizedFontFT *ft_rfont;
  FT_UInt idx;

  if (font->type == MFONT_TYPE_REALIZED)
    rfont = (MRealizedFont *) font;
  else if (font->type == MFONT_TYPE_OBJECT)
    {
      for (rfont = MPLIST_VAL (frame->realized_font_list); rfont;
	   rfont = rfont->next)
	if (rfont->font == font && rfont->driver == &mfont__ft_driver)
	  break;
      if (! rfont)
	{
#ifdef HAVE_FONTCONFIG
	  MFontFT *ft_info = (MFontFT *) font;

	  if (! ft_info->charset)
	    {
	      FcPattern *pat = FcPatternBuild (NULL, FC_FILE, FcTypeString,
					       MSYMBOL_NAME (font->file),
					       NULL);
	      FcObjectSet *os = FcObjectSetBuild (FC_CHARSET, NULL);
	      FcFontSet *fs = FcFontList (fc_config, pat, os);

	      if (fs->nfont > 0
		  && FcPatternGetCharSet (fs->fonts[0], FC_CHARSET, 0,
					  &ft_info->charset) == FcResultMatch)
		ft_info->charset = FcCharSetCopy (ft_info->charset);
	      else
		ft_info->charset = FcCharSetCreate ();
	      FcFontSetDestroy (fs);
	      FcObjectSetDestroy (os);
	      FcPatternDestroy (pat);
	    }
	  return (FcCharSetHasChar (ft_info->charset, (FcChar32) c) == FcTrue);
#else  /* not HAVE_FONTCONFIG */
	  rfont = ft_open (frame, font, spec, NULL);
#endif	/* not HAVE_FONTCONFIG */
	}
    }
  else
    MFATAL (MERROR_FONT_FT);

  if (! rfont)
    return 0;
  ft_rfont = rfont->info;
  idx = FT_Get_Char_Index (ft_rfont->ft_face, (FT_ULong) code);
  return (idx != 0);
}

/* The FreeType font driver function ENCODE_CHAR.  */

static unsigned
ft_encode_char (MFrame *frame, MFont *font, MFont *spec, unsigned code)
{
  MRealizedFont *rfont;
  MRealizedFontFT *ft_rfont;
  FT_UInt idx;

  if (font->type == MFONT_TYPE_REALIZED)
    rfont = (MRealizedFont *) font;
  else if (font->type == MFONT_TYPE_OBJECT)
    {
      for (rfont = MPLIST_VAL (frame->realized_font_list); rfont;
	   rfont = rfont->next)
	if (rfont->font == font && rfont->driver == &mfont__ft_driver)
	  break;
      if (! rfont)
	{
	  rfont = ft_open (frame, font, spec, NULL);
	  if (! rfont)
	    return -1;
	}
    }
  else
    MFATAL (MERROR_FONT_FT);

  ft_rfont = rfont->info;
  idx = FT_Get_Char_Index (ft_rfont->ft_face, (FT_ULong) code);
  return (idx ? (unsigned) idx : MCHAR_INVALID_CODE);
}

/* The FreeType font driver function RENDER.  */

#define NUM_POINTS 0x1000

typedef struct {
  MDrawPoint points[NUM_POINTS];
  MDrawPoint *p;
} MPointTable;

static void
ft_render (MDrawWindow win, int x, int y,
	   MGlyphString *gstring, MGlyph *from, MGlyph *to,
	   int reverse, MDrawRegion region)
{
  FT_Face ft_face;
  MRealizedFace *rface = from->rface;
  MFrame *frame = rface->frame;
  FT_Int32 load_flags = FT_LOAD_RENDER;
  MGlyph *g;
  int i, j;
  MPointTable point_table[8];
  int baseline_offset;
  int pixel_mode = -1;

  if (from == to)
    return;

  /* It is assured that the all glyphs in the current range use the
     same realized face.  */
  ft_face = rface->rfont->fontp;
  baseline_offset = rface->rfont->baseline_offset >> 6;

  if (! gstring->anti_alias)
    {
#ifdef FT_LOAD_TARGET_MONO
      load_flags |= FT_LOAD_TARGET_MONO;
#else
      load_flags |= FT_LOAD_MONOCHROME;
#endif
    }

  for (i = 0; i < 8; i++)
    point_table[i].p = point_table[i].points;

  for (g = from; g < to; x += g++->g.xadv)
    {
      unsigned char *bmp;
      int intensity;
      MPointTable *ptable;
      int xoff, yoff;
      int width, pitch;

      FT_Load_Glyph (ft_face, (FT_UInt) g->g.code, load_flags);
      if (pixel_mode < 0)
	pixel_mode = ft_face->glyph->bitmap.pixel_mode;
      yoff = y - ft_face->glyph->bitmap_top + g->g.yoff;
      bmp = ft_face->glyph->bitmap.buffer;
      width = ft_face->glyph->bitmap.width;
      pitch = ft_face->glyph->bitmap.pitch;

      if (pixel_mode != FT_PIXEL_MODE_MONO)
	for (i = 0; i < ft_face->glyph->bitmap.rows;
	     i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
	  {
	    xoff = x + ft_face->glyph->bitmap_left + g->g.xoff;
	    for (j = 0; j < width; j++, xoff++)
	      {
		intensity = bmp[j] >> 5;
		if (intensity)
		  {
		    ptable = point_table + intensity;
		    ptable->p->x = xoff;
		    ptable->p->y = yoff - baseline_offset;
		    ptable->p++;
		    if (ptable->p - ptable->points == NUM_POINTS)
		      {
			(*frame->driver->draw_points)
			  (frame, win, rface,
			   reverse ? 7 - intensity : intensity,
			   ptable->points, NUM_POINTS, region);
			ptable->p = ptable->points;
		      }
		  }
	      }
	  }
      else
	for (i = 0; i < ft_face->glyph->bitmap.rows;
	     i++, bmp += ft_face->glyph->bitmap.pitch, yoff++)
	  {
	    xoff = x + ft_face->glyph->bitmap_left + g->g.xoff;
	    for (j = 0; j < width; j++, xoff++)
	      {
		intensity = bmp[j / 8] & (1 << (7 - (j % 8)));
		if (intensity)
		  {
		    ptable = point_table;
		    ptable->p->x = xoff;
		    ptable->p->y = yoff - baseline_offset;
		    ptable->p++;
		    if (ptable->p - ptable->points == NUM_POINTS)
		      {
			(*frame->driver->draw_points) (frame, win, rface,
					   reverse ? 0 : 7,
					   ptable->points, NUM_POINTS, region);
			ptable->p = ptable->points;
		      }		    
		  }
	      }
	}
    }

  if (pixel_mode != FT_PIXEL_MODE_MONO)
    {
      for (i = 1; i < 8; i++)
	if (point_table[i].p != point_table[i].points)
	  (*frame->driver->draw_points) (frame, win, rface, reverse ? 7 - i : i,
			     point_table[i].points,
			     point_table[i].p - point_table[i].points, region);
    }
  else
    {
      if (point_table[0].p != point_table[0].points)
	(*frame->driver->draw_points) (frame, win, rface, reverse ? 0 : 7,
			   point_table[0].points,
			   point_table[0].p - point_table[0].points, region);
    }
}

static int
ft_list (MFrame *frame, MPlist *plist, MFont *font, int maxnum)
{
  MPlist *pl = NULL, *p;
  int num = 0;
  MPlist *file_list = NULL;
  MPlist *family_list = NULL, *capability_list = NULL;
  MSymbol registry = Mnil;

  MDEBUG_DUMP (" [FONT-FT] listing ", "", mdebug_dump_font (font));

  if (font)
    {
      MSymbol family;

      registry = FONT_PROPERTY (font, MFONT_REGISTRY);
      if (registry != Mnil && registry != Miso8859_1)
	{
	  char *reg = MSYMBOL_NAME (registry);

	  if (strncmp (reg, "unicode-", 8)
	      && strncmp (reg, "apple-roman", 11)
	      && (reg[0] < '0' || reg[0] > '9' || reg[1] != '-'))
	    goto done;
	}

      if (font->file != Mnil
	  && ! (file_list = ft_list_file (font->file)))
	goto done;
      family = FONT_PROPERTY (font, MFONT_FAMILY);
      if (family != Mnil
	  && (family_list = MPLIST_PLIST (ft_list_family (family, 1, 1)))
	  && MPLIST_TAIL_P (family_list))
	goto done;
      if (font->capability != Mnil)
	{
	  capability_list = ft_list_capability (font->capability);
	  if (! capability_list || MPLIST_TAIL_P (capability_list))
	    goto done;
	}
    }

  if (! file_list && ! family_list && ! capability_list)
    {
      /* No restriction.  Get all fonts.  */
      pl = mplist ();
      MPLIST_DO (family_list, ft_list_family (Mnil, 0, 1))
	{
	  MPLIST_DO (p, MPLIST_PLIST (family_list))
	    mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p));
	}
    }
  else
    {
      if (file_list)
	{
	  pl = mplist ();
	  mplist_push (pl, MPLIST_KEY (file_list), MPLIST_VAL (file_list));
	}
      if (family_list)
	{
	  if (pl)
	    for (p = pl; ! MPLIST_TAIL_P (p);)
	      {
		if (mplist_find_by_value (family_list, MPLIST_VAL (p)))
		  p = MPLIST_NEXT (p);
		else
		  mplist_pop (p);
	      }
	  else
	    {
	      pl = mplist ();
	      MPLIST_DO (p, family_list)
		mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p));
	    }
	}
      if (capability_list)
	{
	  if (pl)
	    for (p = pl; ! MPLIST_TAIL_P (p);)
	      {
		if (mplist_find_by_value (capability_list, MPLIST_VAL (p)))
		  p = MPLIST_NEXT (p);
		else
		  mplist_pop (p);
	      }
	  else
	    {
	      pl = mplist ();
	      MPLIST_DO (p, capability_list)
		mplist_push (pl, MPLIST_KEY (p), MPLIST_VAL (p));
	    }
	}
    }
	      
  if (font
      && (font->property[MFONT_WEIGHT] + font->property[MFONT_STYLE]
	  + font->property[MFONT_STRETCH] + font->size) > 0)
    {
      MSymbol weight = FONT_PROPERTY (font, MFONT_WEIGHT);
      MSymbol style = FONT_PROPERTY (font, MFONT_STYLE);
      MSymbol stretch = FONT_PROPERTY (font, MFONT_STRETCH);
      int size = font->size;

      for (p = pl; ! MPLIST_TAIL_P (p); )
	{
	  MFontFT *ft_info = MPLIST_VAL (p);

	  if ((weight != Mnil
	       && weight != FONT_PROPERTY (&ft_info->font, MFONT_WEIGHT))
	      || (style != Mnil
		  && style != FONT_PROPERTY (&ft_info->font, MFONT_STYLE))
	      || (stretch != Mnil
		  && stretch != FONT_PROPERTY (&ft_info->font,
					       MFONT_STRETCH))
	      || (size > 0
		  && ft_info->font.size > 0
		  && ft_info->font.size != size))
	    mplist_pop (p);
	  else
	    p = MPLIST_NEXT (p);
	}
    }

  MPLIST_DO (p, pl)
    {
      mplist_push (plist, MPLIST_KEY (p), MPLIST_VAL (p));
      num++;
      if (maxnum && maxnum <= num)
	break;
    }
  M17N_OBJECT_UNREF (pl);

 done:
  MDEBUG_PRINT1 ("  %d found\n", num);
  return num;
}

static void
ft_list_family_names (MFrame *frame, MPlist *plist)
{
  MPlist *pl;

  if (! ft_font_list)
    {
#ifdef HAVE_FONTCONFIG
      fc_init_font_list ();
#else  /* not HAVE_FONTCONFIG */
      ft_init_font_list ();
#endif	/* not HAVE_FONTCONFIG */
    }

  MPLIST_DO (pl, ft_font_list)
    {
      MSymbol family = MPLIST_KEY (pl);
      MPlist *p;

#ifdef HAVE_FONTCONFIG
      if (msymbol_get (family, Mgeneric_family) != Mnil)
	continue;
#endif	/* HAVE_FONTCONFIG */
      MPLIST_DO (p, plist)
	{
	  MSymbol sym = MPLIST_SYMBOL (p);

	  if (sym == family)
	    break;
	  if (strcmp (MSYMBOL_NAME (sym), MSYMBOL_NAME (family)) > 0)
	    {
	      mplist_push (p, Msymbol, family);
	      break;
	    }
	}
      if (MPLIST_TAIL_P (p))
	mplist_push (p, Msymbol, family);
    }
}

static int 
ft_check_capability (MRealizedFont *rfont, MSymbol capability)
{
  MFontFT *ft_info = (MFontFT *) rfont->font;
  MRealizedFontFT *ft_rfont = rfont->info;
  MFontCapability *cap = mfont__get_capability (capability);

  if (cap->script_tag)
    {
      if (ft_check_cap_otf (ft_info, cap, ft_rfont->ft_face) < 0)
	return -1;
    }
  else if (cap->script != Mnil
	   && ft_check_script (ft_info, cap->script, ft_rfont->ft_face) < 0)
    return -1;
  if (cap->language != Mnil
      && ft_check_language (ft_info, cap->language, ft_rfont->ft_face) < 0)
    return -1;
  return 0;
}

static MRealizedFont *
ft_encapsulate (MFrame *frame, MSymbol data_type, void *data)
{
  MFontFT *ft_info;
  MRealizedFont *rfont;
  MRealizedFontFT *ft_rfont;
  FT_Face ft_face;

  if (data_type == Mfontconfig)
    {
#ifdef HAVE_FONTCONFIG
      FcPattern *pattern = data;

      if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &ft_face)
	  != FcResultMatch)
	return NULL;
      ft_info = fc_gen_font (pattern, NULL);
#else  /* not HAVE_FONTCONFIG */
      return NULL;
#endif	/* not HAVE_FONTCONFIG */
    }
  else if (data_type == Mfreetype)
    {
      ft_face = data;
      ft_info = ft_gen_font (ft_face);
    }
  else
    return NULL;

  M17N_OBJECT (ft_rfont, free_ft_rfont, MERROR_FONT_FT);
  ft_rfont->ft_face = ft_face;
  ft_rfont->face_encapsulated = 1;

  MDEBUG_PRINT1 (" [FONT-FT] encapsulating %s", (char *) ft_face->family_name);

  MSTRUCT_CALLOC (rfont, MERROR_FONT_FT);
  rfont->id = ft_info->font.file;
  rfont->font = (MFont *) ft_info;
  rfont->info = ft_rfont;
  rfont->fontp = ft_face;
  rfont->driver = &mfont__ft_driver;
  rfont->spec = ft_info->font;
  rfont->spec.type = MFONT_TYPE_REALIZED;
  rfont->frame = frame;
  rfont->ascent = ft_face->size->metrics.ascender >> 6;
  rfont->descent = - ft_face->size->metrics.descender >> 6;
  rfont->max_advance = ft_face->size->metrics.max_advance >> 6;
  rfont->baseline_offset = 0;
  rfont->x_ppem = ft_face->size->metrics.x_ppem;
  rfont->y_ppem = ft_face->size->metrics.y_ppem;
#ifdef HAVE_FTBDF_H
  {
    BDF_PropertyRec prop;

    if (! FT_IS_SCALABLE (ft_face)
	&& FT_Get_BDF_Property (ft_face, "_MULE_BASELINE_OFFSET", &prop) == 0)
      {
	rfont->baseline_offset = prop.u.integer << 6;
	rfont->ascent += prop.u.integer << 6;
	rfont->descent -= prop.u.integer << 6;
      }
  }
#endif	/* HAVE_FTBDF_H */
  if (FT_IS_SCALABLE (ft_face))
    rfont->average_width = 0;
  else
    rfont->average_width = ft_face->available_sizes->width << 6;
  rfont->next = MPLIST_VAL (frame->realized_font_list);
  MPLIST_VAL (frame->realized_font_list) = rfont;

  return rfont;
}

static void
ft_close (MRealizedFont *rfont)
{
  if (! rfont->encapsulating)
    return;
  free (rfont->font);
  M17N_OBJECT_UNREF (rfont->info);
  free (rfont);
}

/* See the comment of parse_otf_command (m17n-flt.c).  */

static int 
ft_check_otf (MFLTFont *font, MFLTOtfSpec *spec)
{
#define FEATURE_NONE(IDX) (! spec->features[IDX])

#define FEATURE_ANY(IDX)	\
  (spec->features[IDX]		\
   && spec->features[IDX][0] == 0xFFFFFFFF && spec->features[IDX][1] == 0)

#ifdef HAVE_OTF
  OTF_Tag *tags;
  int i, n, negative;
  OTF *otf = get_otf (font, NULL);
  
  if (FEATURE_ANY (0) && FEATURE_ANY (1))
    /* Return 1 iff any of GSUB or GPOS support the script (and language).  */
    return (otf
	    && (OTF_check_features (otf, 0, spec->script, spec->langsys,
				    NULL, 0) > 0
		|| OTF_check_features (otf, 1, spec->script, spec->langsys,
				       NULL, 0) > 0));

  for (i = 0; i < 2; i++)
    if (! FEATURE_ANY (i))
      {
	if (FEATURE_NONE (i))
	  {
	    if (otf
		&& OTF_check_features (otf, i == 0, spec->script, spec->langsys,
				       NULL, 0) > 0)
	      return 0;
	    continue;
	  }
	if (spec->features[i][0] == 0xFFFFFFFF)
	  {
	    if (! otf
		|| OTF_check_features (otf, i == 0, spec->script, spec->langsys,
				       NULL, 0) <= 0)
	      continue;
	  }
	else if (! otf)
	  return 0;
	for (n = 1; spec->features[i][n]; n++);
	tags = alloca (sizeof (OTF_Tag) * n);
	for (n = 0, negative = 0; spec->features[i][n]; n++)
	  {
	    if (spec->features[i][n] == 0xFFFFFFFF)
	      negative = 1;
	    else if (negative)
	      tags[n - 1] = spec->features[i][n] | 0x80000000;
	    else
	      tags[n] = spec->features[i][n];
	  }
	if (OTF_check_features (otf, i == 0, spec->script, spec->langsys,
				tags, n - negative) != 1)
	  return 0;
      }
  return 1;

#endif	/* HAVE_OTF */
  return (FEATURE_NONE (0) ? (FEATURE_NONE (1) || FEATURE_ANY (1))
	  : (FEATURE_NONE (1) && FEATURE_ANY (0)));
}

#ifdef HAVE_OTF

static OTF *
get_otf (MFLTFont *font, FT_Face *ft_face)
{
  MRealizedFont *rfont = ((MFLTFontForRealized *) font)->rfont;
  MFontFT *ft_info = (MFontFT *) rfont->font;
  MRealizedFontFT *ft_rfont = rfont->info;
  OTF *otf = ft_info->otf;

  if (! otf)
    {
#if (LIBOTF_MAJOR_VERSION > 0 || LIBOTF_MINOR_VERSION > 9 || LIBOTF_RELEASE_NUMBER > 4)
      otf = OTF_open_ft_face (ft_rfont->ft_face);
#else
      otf = OTF_open (MSYMBOL_NAME (ft_info->font.file));
#endif
      if (! otf || OTF_get_table (otf, "head") < 0)
	otf = invalid_otf;
      ft_info->otf = otf;
    }
  if (ft_face)
    *ft_face = ft_rfont->ft_face;
  return (otf == invalid_otf ? NULL : otf);
}

#define DEVICE_DELTA(table, size)				\
  (((size) >= (table).StartSize && (size) <= (table).EndSize)	\
   ? (table).DeltaValue[(size) - (table).StartSize] << 6	\
   : 0)

void
adjust_anchor (OTF_Anchor *anchor, FT_Face ft_face,
	       unsigned code, int x_ppem, int y_ppem, 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 << 6;
	  *y = outline->points[ap].y << 6;
	}
    }
  else if (anchor->AnchorFormat == 3)
    {
      if (anchor->f.f2.XDeviceTable.offset)
	*x += DEVICE_DELTA (anchor->f.f2.XDeviceTable, x_ppem);
      if (anchor->f.f2.YDeviceTable.offset)
	*y += DEVICE_DELTA (anchor->f.f2.YDeviceTable, y_ppem);
    }
}
#endif	/* HAVE_OTF */

static int 
ft_drive_otf (MFLTFont *font, MFLTOtfSpec *spec,
	      MFLTGlyphString *in, int from, int to,
	      MFLTGlyphString *out, MFLTGlyphAdjustment *adjustment)
{
  int len = to - from;
#ifdef HAVE_OTF
  int i, j, gidx;
  MGlyph *in_glyphs = (MGlyph *) (in->glyphs);
  MGlyph *out_glyphs = out ? (MGlyph *) (out->glyphs) : NULL;
  OTF *otf;
  FT_Face face;
  OTF_GlyphString otf_gstring;
  OTF_Glyph *otfg;
  char script[5], *langsys = NULL;
  char *gsub_features = NULL, *gpos_features = NULL;
  unsigned int tag;

  if (len == 0)
    return from;
  otf = get_otf (font, &face);
  if (! otf)
    goto simple_copy;
  OTF_tag_name (spec->script, script);
  if (spec->langsys)
    {
      langsys = alloca (5);
      OTF_tag_name (spec->langsys, langsys);
    }
  for (i = 0; i < 2; i++)
    {
      char *p;

      if (spec->features[i])
	{
	  for (j = 0; spec->features[i][j]; j++);
	  if (i == 0)
	    p = gsub_features = alloca (6 * j);
	  else
	    p = gpos_features = alloca (6 * j);
	  for (j = 0; spec->features[i][j]; j++)
	    {
	      if (spec->features[i][j] == 0xFFFFFFFF)
		*p++ = '*', *p++ = ',';
	      else
		{
		  OTF_tag_name (spec->features[i][j], p);
		  p[4] = ',';
		  p += 5;
		}
	    }
	  *--p = '\0';
	}
    }

  otf_gstring.size = otf_gstring.used = 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++)
    {
      otf_gstring.glyphs[i].c = ((MGlyph *)in->glyphs)[from + i].g.c & 0x11FFFF;
      otf_gstring.glyphs[i].glyph_id = ((MGlyph *)in->glyphs)[from + i].g.code;
    }

  OTF_drive_gdef (otf, &otf_gstring);
  gidx = out ? out->used : from;

  if (gsub_features)
    {
      OTF_Feature *features;
      MGlyph *g;

      if (OTF_drive_gsub_with_log (otf, &otf_gstring, script, langsys,
				   gsub_features) < 0)
	goto simple_copy;
      features = otf->gsub->FeatureList.Feature;
      if (out)
	{
	  if (out->allocated < gidx + otf_gstring.used)
	    return -2;
	  for (i = 0, otfg = otf_gstring.glyphs, g = out_glyphs + gidx;
	       i < otf_gstring.used; i++, otfg++, g++, out->used++)
	    {
	      int feature_idx = otfg->positioning_type >> 4;
	      int j;
	      int min_from, max_to;

	      *g = in_glyphs[from + otfg->f.index.from];
	      min_from = g->g.from, max_to = g->g.to;
	      g->g.c = 0;
	      for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
		if (in_glyphs[from + j].g.code == otfg->glyph_id)
		  {
		    g->g.c = in_glyphs[from + j].g.c;
		    break;
		  }
	      if (feature_idx)
		{
		  tag = features[feature_idx - 1].FeatureTag;
		  tag = PACK_OTF_TAG (tag);
		  g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
		}
	      for (j = otfg->f.index.from + 1; j <= otfg->f.index.to; j++)
		{
		  if (min_from > in_glyphs[from + j].g.from)
		    min_from = in_glyphs[from + j].g.from;
		  if (max_to < in_glyphs[from + j].g.to)
		    max_to = in_glyphs[from + j].g.to;
		}
	      if (g->g.code != otfg->glyph_id)
		{
		  g->g.code = otfg->glyph_id;
		  g->g.measured = 0;
		}
	      g->g.from = min_from, g->g.to = max_to;
	    }
	}
      else
	{
	  for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used;
	       i++, otfg++)
	    {
	      int feature_idx = otfg->positioning_type >> 4;

	      if (feature_idx)
		{
		  tag = features[feature_idx - 1].FeatureTag;
		  tag = PACK_OTF_TAG (tag);
		  for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
		    {
		      g = in_glyphs + (from + j);
		      g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
		    }
		}
	    }
	}
    }
  else if (out)
    {
      if (out->allocated < gidx + len)
	return -2;
      for (i = 0; i < len; i++)
	out_glyphs[out->used++] = in_glyphs[from + i];
    }

  if (gpos_features)
    {
      OTF_Feature *features;
      MGlyph *g;

      if (OTF_drive_gpos_with_log (otf, &otf_gstring, script, langsys,
				   gpos_features) < 0)
	return to;
      features = otf->gpos->FeatureList.Feature;
      if (out)
	{
	  MGlyph *base = NULL, *mark = NULL;
	  int x_ppem = face->size->metrics.x_ppem;
	  int y_ppem = face->size->metrics.y_ppem;
	  int x_scale = face->size->metrics.x_scale;
	  int y_scale = face->size->metrics.y_scale;

	  for (i = j = 0, otfg = otf_gstring.glyphs, g = out_glyphs + gidx;
	       i < otf_gstring.used; i++, otfg++)
	    {
	      MGlyph *prev;
	      int adjust_idx = otfg->glyph_id ? j : j - 1;
	      int feature_idx = otfg->positioning_type >> 4;

	      if (feature_idx)
		{
		  tag = features[feature_idx - 1].FeatureTag;
		  tag = PACK_OTF_TAG (tag);
		  g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
		}
	      switch (otfg->positioning_type & 0xF)
		{
		case 0:
		  break;
		case 1: 		/* Single */
		case 2: 		/* Pair */
		  {
		    int format = otfg->f.f1.format;

		    if (format & OTF_XPlacement)
		      adjustment[adjust_idx].xoff
			+= otfg->f.f1.value->XPlacement * x_scale / 0x10000;
		    if (format & OTF_XPlaDevice)
		      adjustment[adjust_idx].xoff
			+= DEVICE_DELTA (otfg->f.f1.value->XPlaDevice, x_ppem);
		    if (format & OTF_YPlacement)
		      adjustment[adjust_idx].yoff
			-= otfg->f.f1.value->YPlacement * y_scale / 0x10000;
		    if (format & OTF_YPlaDevice)
		      adjustment[adjust_idx].yoff
			-= DEVICE_DELTA (otfg->f.f1.value->YPlaDevice, y_ppem);
		    if (format & OTF_XAdvance)
		      adjustment[adjust_idx].xadv
			+= otfg->f.f1.value->XAdvance * x_scale / 0x10000;
		    if (format & OTF_XAdvDevice)
		      adjustment[adjust_idx].xadv
			+= DEVICE_DELTA (otfg->f.f1.value->XAdvDevice, x_ppem);
		    if (format & OTF_YAdvance)
		      adjustment[adjust_idx].yadv
			+= otfg->f.f1.value->YAdvance * y_scale / 0x10000;
		    if (format & OTF_YAdvDevice)
		      adjustment[adjust_idx].yadv
			+= DEVICE_DELTA (otfg->f.f1.value->YAdvDevice, y_ppem);
		    adjustment[adjust_idx].set = 1;
		  }
		  break;
		case 3:		/* Cursive */
		  /* Not yet supported.  */
		  break;
		case 4:		/* Mark-to-Base */
		case 5:		/* Mark-to-Ligature */
		  if (! base)
		    break;
		  prev = base;
		  goto label_adjust_anchor;
		default:		/* i.e. case 6 Mark-to-Mark */
		  if (! mark)
		    break;
		  prev = mark;

		label_adjust_anchor:
		  {
		    int base_x, base_y, mark_x, mark_y;
		    int this_from, this_to;
		    int k;

		    base_x = otfg->f.f4.base_anchor->XCoordinate * x_scale / 0x10000;
		    base_y = otfg->f.f4.base_anchor->YCoordinate * y_scale / 0x10000;
		    mark_x = otfg->f.f4.mark_anchor->XCoordinate * x_scale / 0x10000;
		    mark_y = otfg->f.f4.mark_anchor->YCoordinate * y_scale / 0x10000;;

		    if (otfg->f.f4.base_anchor->AnchorFormat != 1)
		      adjust_anchor (otfg->f.f4.base_anchor, face, prev->g.code,
				     x_ppem, y_ppem, &base_x, &base_y);
		    if (otfg->f.f4.mark_anchor->AnchorFormat != 1)
		      adjust_anchor (otfg->f.f4.mark_anchor, face, g->g.code,
				     x_ppem, y_ppem, &mark_x, &mark_y);
		    adjustment[adjust_idx].xoff = base_x - mark_x;
		    adjustment[adjust_idx].yoff = - (base_y - mark_y);
		    adjustment[adjust_idx].back = (g - prev);
		    adjustment[adjust_idx].xadv = 0;
		    adjustment[adjust_idx].advance_is_absolute = 1;
		    adjustment[adjust_idx].set = 1;
		    this_from = g->g.from;
		    this_to = g->g.to;
		    for (k = 0; prev + k < g; k++)
		      {
			if (this_from > prev[k].g.from)
			  this_from = prev[k].g.from;
			if (this_to < prev[k].g.to)
			  this_to = prev[k].g.to;
		      }
		    for (; prev <= g; prev++)
		      {
			prev->g.from = this_from;
			prev->g.to = this_to;
		      }
		  }
		}
	      if (otfg->glyph_id)
		{
		  if (otfg->GlyphClass == OTF_GlyphClass0)
		    base = mark = g;
		  else if (otfg->GlyphClass == OTF_GlyphClassMark)
		    mark = g;
		  else
		    base = g;
		  j++, g++;
		}
	    }
	}
      else
	{
	  for (i = 0, otfg = otf_gstring.glyphs; i < otf_gstring.used;
	       i++, otfg++)
	    if (otfg->positioning_type & 0xF)
	      {
		int feature_idx = otfg->positioning_type >> 4;

		if (feature_idx)
		  {
		    tag = features[feature_idx - 1].FeatureTag;
		    tag = PACK_OTF_TAG (tag);
		    for (j = otfg->f.index.from; j <= otfg->f.index.to; j++)
		      {
			g = in_glyphs + (from + j);
			g->g.internal = (g->g.internal & ~0x1FFFFFFF) | tag;
		      }
		  }
	      }
	}
    }

  free (otf_gstring.glyphs);
  return to;

 simple_copy:
  if (otf_gstring.glyphs)
    free (otf_gstring.glyphs);
#endif	/* HAVE_OTF */
  if (out)
    {
      if (out->allocated < out->used + len)
	return -2;
      font->get_metrics (font, in, from, to);
      memcpy ((MGlyph *)out->glyphs + out->used, (MGlyph *) in->glyphs + from,
	      sizeof (MGlyph) * len);
      out->used += len;
    }
  return to;
}

static int 
ft_try_otf (MFLTFont *font, MFLTOtfSpec *spec,
	    MFLTGlyphString *in, int from, int to)
{
  return ft_drive_otf (font, spec, in, from, to, NULL, NULL);
}


#ifdef HAVE_OTF
static unsigned char *iterate_bitmap;

static int
iterate_callback (OTF *otf, const char *feature, unsigned glyph_id)
{
  if (glyph_id <= otf->cmap->max_glyph_id)
    iterate_bitmap[glyph_id / 8] |= 1 << (glyph_id % 8);
  return 0;
}

static int
ft_iterate_otf_feature (MFLTFont *font, MFLTOtfSpec *spec,
			int from, int to, unsigned char *table)
{
  OTF *otf = get_otf (font, NULL);
  char id[13];
  int bmp_size;
  unsigned char *bitmap = NULL;
  int i, j;
  char script[5], *langsys = NULL;

  if (! otf)
    return -1;
  if (OTF_get_table (otf, "cmap") < 0)
    return -1;
  if (! spec->features[0])
    return -1;
  strcpy (id, "feature-");
  id[12] = '\0';
  OTF_tag_name (spec->script, script);
  if (spec->langsys)
    {
      langsys = alloca (5);
      OTF_tag_name (spec->langsys, langsys);
    }
  bmp_size = (otf->cmap->max_glyph_id / 8) + 1;
  for (i = 0; spec->features[0][i]; i++)
    {
      unsigned char *bmp;

      OTF_tag_name (spec->features[0][i], id + 8);
      bmp = OTF_get_data (otf, id);
      if (! bmp)
	{
	  iterate_bitmap = bmp = calloc (bmp_size, 1);
	  OTF_iterate_gsub_feature (otf, iterate_callback,
				    script, langsys, id + 8);
	  OTF_put_data (otf, id, bmp, free);
	}
      if (i == 0 && ! spec->features[0][1])
	/* Single feature */
	bitmap = bmp;
      else
	{
	  if (! bitmap)
	    {
	      bitmap = alloca (bmp_size);
	      memcpy (bitmap, bmp, bmp_size);
	    }
	  else
	    {
	      int j;

	      for (j = 0; j < bmp_size; j++)
		bitmap[j] &= bmp[j];
	    }
	}
    }
  for (i = 0; i < bmp_size; i++)
    if (bitmap[i])
      {
	for (j = 0; j < 8; j++)
	  if (bitmap[i] & (1 << j))
	    {
	      int c = OTF_get_unicode (otf, (i * 8) + j);

	      if (c >= from && c <= to)
		table[c - from] = 1;
	    }
      }
  return 0;
}
#endif



/* Internal API */

MFontDriver mfont__ft_driver =
  { ft_select, ft_open, ft_find_metric, ft_has_char, ft_encode_char,
    ft_render, ft_list, ft_list_family_names, ft_check_capability,
    ft_encapsulate, ft_close, ft_check_otf, ft_drive_otf, ft_try_otf,
#ifdef HAVE_OTF
    ft_iterate_otf_feature
#endif	/* HAVE_OTF */
  };

int
mfont__ft_init ()
{
  int i;

  if (FT_Init_FreeType (&ft_library) != 0)
    MERROR (MERROR_FONT_FT, -1);

  for (i = 0; i < ft_to_prop_size; i++)
    ft_to_prop[i].len = strlen (ft_to_prop[i].ft_style);

  Mmedium = msymbol ("medium");
  Mr = msymbol ("r");
  Mnull = msymbol ("");

  M0[0] = msymbol ("0-0");
  M0[1] = msymbol ("0-1");
  M0[2] = msymbol ("0-2");
  M0[3] = msymbol ("0-3");
  M0[4] = msymbol ("0-4");
  M3_1 = msymbol ("3-1");
  M1_0 = msymbol ("1-0");

#ifdef HAVE_FONTCONFIG
  for (i = 0; i < (sizeof (fc_all_table) / sizeof fc_all_table[0]); i++)
    {
      FC_vs_M17N_font_prop *table = fc_all_table[i];
      int j;

      for (j = 0; table[j].m17n_value; j++)
	table[j].sym = msymbol (table[j].m17n_value);
      table[j].sym = table[j - 1].sym;
    }

  {
    char *pathname;
    struct stat buf;
    MPlist *plist;
    MSymbol serif, sans_serif, monospace;

    fc_config = FcInitLoadConfigAndFonts ();
    if (mfont_freetype_path)
      {
	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);
	    }
      }
    Mgeneric_family = msymbol ("generic famly");
    serif = msymbol ("serif");
    msymbol_put (serif, Mgeneric_family, serif);
    sans_serif = msymbol ("sans-serif");
    msymbol_put (sans_serif, Mgeneric_family, sans_serif);
    msymbol_put (msymbol ("sans serif"), Mgeneric_family, sans_serif);
    msymbol_put (msymbol ("sans"), Mgeneric_family, sans_serif);
    monospace = msymbol ("monospace");
    msymbol_put (monospace, Mgeneric_family, monospace);
    msymbol_put (msymbol ("mono"), Mgeneric_family, monospace);
  }
#endif	/* HAVE_FONTCONFIG */

  return 0;
}

void
mfont__ft_fini ()
{
  MPlist *plist, *p;

  if (ft_default_list)
    {
      M17N_OBJECT_UNREF (ft_default_list);
      ft_default_list = NULL;
    }

  if (ft_font_list)
    {
      MPLIST_DO (plist, ft_font_list)
	{
	  if (MPLIST_VAL (plist))
	    MPLIST_DO (p, MPLIST_VAL (plist))
	      {
		if (MPLIST_KEY (p) != Mt)
		  free_ft_info (MPLIST_VAL (p));
	      }
	  M17N_OBJECT_UNREF (MPLIST_VAL (plist));
	}
      M17N_OBJECT_UNREF (ft_font_list);
      ft_font_list = NULL;

      if (ft_language_list)
	{
	  MPLIST_DO (plist, ft_language_list)
	    M17N_OBJECT_UNREF (MPLIST_VAL (plist));
	  M17N_OBJECT_UNREF (ft_language_list);
	  ft_language_list = NULL;
	}

      if (ft_script_list)
	{
	  MPLIST_DO (plist, ft_script_list)
	    M17N_OBJECT_UNREF (MPLIST_VAL (plist));
	  M17N_OBJECT_UNREF (ft_script_list);
	  ft_script_list = NULL;
	}

      if (ft_capability_list)
	{
	  MPLIST_DO (plist, ft_capability_list)
	    M17N_OBJECT_UNREF (MPLIST_VAL (plist));
	  M17N_OBJECT_UNREF (ft_capability_list);
	  ft_capability_list = NULL;
	}

      if (ft_file_list)
	{
	  MPLIST_DO (plist, ft_file_list)
	    M17N_OBJECT_UNREF (MPLIST_VAL (plist));
	  M17N_OBJECT_UNREF (ft_file_list);
	  ft_file_list = NULL;
	}
    }
  FT_Done_FreeType (ft_library);
#ifdef HAVE_FONTCONFIG
  FcConfigDestroy (fc_config);
  fc_config = NULL;
#endif	/* HAVE_FONTCONFIG */
  all_fonts_scaned = 0;
}

#ifdef HAVE_FONTCONFIG

int
mfont__ft_parse_name (const char *name, MFont *font)
{
  FcPattern *pat = FcNameParse ((FcChar8 *) name);
  FcChar8 *str;
  int val;
  double size;
  char *buf;
  int bufsize = 0;
  
  if (! pat)
    return -1;
  if (FcPatternGetString (pat, FC_FOUNDRY, 0, &str) == FcResultMatch)
    {
      STRDUP_LOWER (buf, bufsize, (char *) str);
      mfont__set_property (font, MFONT_FOUNDRY, msymbol (buf));
    }
  if (FcPatternGetString (pat, FC_FAMILY, 0, &str) == FcResultMatch)
    {
      STRDUP_LOWER (buf, bufsize, (char *) str);
      mfont__set_property (font, MFONT_FAMILY, msymbol (buf));
    }
  if (FcPatternGetInteger (pat, FC_WEIGHT, 0, &val) == FcResultMatch)
    mfont__set_property (font, MFONT_WEIGHT,
			 fc_decode_prop (val, fc_weight_table,
					 fc_weight_table_size));
  if (FcPatternGetInteger (pat, FC_SLANT, 0, &val) == FcResultMatch)
    mfont__set_property (font, MFONT_STYLE,
			 fc_decode_prop (val, fc_slant_table,
					 fc_slant_table_size));
  if (FcPatternGetInteger (pat, FC_WIDTH, 0, &val) == FcResultMatch)
    mfont__set_property (font, MFONT_STRETCH,
			 fc_decode_prop (val, fc_width_table,
					 fc_width_table_size));
  if (FcPatternGetDouble (pat, FC_PIXEL_SIZE, 0, &size) == FcResultMatch)
    font->size = size * 10 + 0.5;
  else if (FcPatternGetDouble (pat, FC_SIZE, 0, &size) == FcResultMatch)
    font->size = - (size * 10 + 0.5);
  if (FcPatternGetString (pat, FC_FILE, 0, &str) == FcResultMatch)
    {
      font->file = msymbol ((char *) str);
    }
  mfont__set_property (font, MFONT_REGISTRY, Municode_bmp);
  font->type = MFONT_TYPE_SPEC;
  FcPatternDestroy (pat);
  return 0;
}

char *
mfont__ft_unparse_name (MFont *font)
{
  FcPattern *pat = fc_get_pattern (font);
  char *name = (char *) FcNameUnparse (pat);

  FcPatternDestroy (pat);
  return name;
}
#endif	/* HAVE_FONTCONFIG */

#endif /* HAVE_FREETYPE */
