(ftfont_pattern_entity): Use the numeric value 100 for
[bpt/emacs.git] / src / ftfont.c
index fff8dd7..b7f3ea0 100644 (file)
@@ -40,15 +40,23 @@ Boston, MA 02110-1301, USA.  */
 #include "fontset.h"
 #include "font.h"
 
+/* Symbolic type of this font-driver.  */
 Lisp_Object Qfreetype;
 
+/* Fontconfig's generic families and their aliases.  */
+static Lisp_Object Qmonospace, Qsans_serif, Qserif, Qmono, Qsans, Qsans__serif;
+
+/* Flag to tell if FcInit is areadly called or not.  */
 static int fc_initialized;
+
+/* Handle to a FreeType library instance.  */
 static FT_Library ft_library;
 
+/* Cache for FreeType fonts.  */
 static Lisp_Object freetype_font_cache;
 
-static Lisp_Object Qiso8859_1, Qiso10646_1, Qunicode_bmp;
-
+/* Fontconfig's charset used for finding fonts of registry
+   "iso8859-1".  */
 static FcCharSet *cs_iso8859_1;
 
 /* The actual structure for FreeType font that can be casted to struct
@@ -60,6 +68,14 @@ struct ftfont_info
   FT_Size ft_size;
 };
 
+static int ftfont_build_basic_charsets P_ ((void));
+static Lisp_Object ftfont_pattern_entity P_ ((FcPattern *,
+                                             Lisp_Object, Lisp_Object));
+static Lisp_Object ftfont_list_generic_family P_ ((Lisp_Object, Lisp_Object,
+                                                  Lisp_Object));
+
+#define SYMBOL_FcChar8(SYM) (FcChar8 *) SDATA (SYMBOL_NAME (SYM))
+
 static int
 ftfont_build_basic_charsets ()
 {
@@ -77,8 +93,156 @@ ftfont_build_basic_charsets ()
   return 0;
 }
 
+static Lisp_Object
+ftfont_pattern_entity (p, frame, registry)
+     FcPattern *p;
+     Lisp_Object frame, registry;
+{
+  Lisp_Object entity;
+  FcChar8 *file;
+  FcCharSet *charset;
+  char *str;
+  int numeric;
+  double dbl;
+
+  if (FcPatternGetString (p, FC_FILE, 0, &file) != FcResultMatch)
+    return Qnil;
+  if (FcPatternGetCharSet (p, FC_CHARSET, 0, &charset) != FcResultMatch)
+    charset = NULL;
+
+  entity = Fmake_vector (make_number (FONT_ENTITY_MAX), null_string);
+
+  ASET (entity, FONT_TYPE_INDEX, Qfreetype);
+  ASET (entity, FONT_REGISTRY_INDEX, registry);
+  ASET (entity, FONT_FRAME_INDEX, frame);
+  ASET (entity, FONT_OBJLIST_INDEX, Qnil);
+
+  if (FcPatternGetString (p, FC_FOUNDRY, 0, (FcChar8 **) &str) == FcResultMatch)
+    ASET (entity, FONT_FOUNDRY_INDEX, intern_downcase (str, strlen (str)));
+  if (FcPatternGetString (p, FC_FAMILY, 0, (FcChar8 **) &str) == FcResultMatch)
+    ASET (entity, FONT_FAMILY_INDEX, intern_downcase (str, strlen (str)));
+  if (FcPatternGetInteger (p, FC_WEIGHT, 0, &numeric) == FcResultMatch)
+    {
+      if (numeric == FC_WEIGHT_REGULAR)
+       numeric = 100;
+      ASET (entity, FONT_WEIGHT_INDEX, make_number (numeric));
+    }
+  if (FcPatternGetInteger (p, FC_SLANT, 0, &numeric) == FcResultMatch)
+    ASET (entity, FONT_SLANT_INDEX, make_number (numeric + 100));
+  if (FcPatternGetInteger (p, FC_WIDTH, 0, &numeric) == FcResultMatch)
+    ASET (entity, FONT_WIDTH_INDEX, make_number (numeric));
+  if (FcPatternGetDouble (p, FC_PIXEL_SIZE, 0, &dbl) == FcResultMatch)
+    ASET (entity, FONT_SIZE_INDEX, make_number (dbl));
+  else
+    ASET (entity, FONT_SIZE_INDEX, make_number (0));
+
+  if (FcPatternGetInteger (p, FC_SPACING, 0, &numeric) != FcResultMatch)
+    numeric = FC_MONO;
+  file = FcStrCopy (file);
+  if (! file)
+    return Qnil;
+
+  p = FcPatternCreate ();
+  if (! p)
+    return Qnil;
+
+  if (FcPatternAddString (p, FC_FILE, file) == FcFalse
+      || (charset && FcPatternAddCharSet (p, FC_CHARSET, charset) == FcFalse)
+      || FcPatternAddInteger (p, FC_SPACING, numeric) == FcFalse)
+    {
+      FcPatternDestroy (p);
+      return Qnil;
+    }
+  ASET (entity, FONT_EXTRA_INDEX, make_save_value (p, 0));
+  return entity;
+}
+
+static Lisp_Object ftfont_generic_family_list;
+
+static Lisp_Object
+ftfont_list_generic_family (spec, frame, registry)
+     Lisp_Object spec, frame, registry;
+{
+  Lisp_Object family = AREF (spec, FONT_FAMILY_INDEX);
+  Lisp_Object slot, list, val;
+
+  if (EQ (family, Qmono))
+    family = Qmonospace;
+  else if (EQ (family, Qsans) || EQ (family, Qsans__serif))
+    family = Qsans_serif;
+  slot = assq_no_quit (family, ftfont_generic_family_list);
+  if (! CONSP (slot))
+    return null_vector;
+  list = XCDR (slot);
+  if (EQ (list, Qt))
+    {
+      /* Not yet listed.  */
+      FcObjectSet *objset = NULL;
+      FcPattern *pattern = NULL, *pat = NULL;
+      FcFontSet *fontset = NULL;
+      FcChar8 *fam;
+      int i, j;
+
+      objset = FcObjectSetBuild (FC_FOUNDRY, FC_FAMILY, FC_WEIGHT, FC_SLANT,
+                                FC_WIDTH, FC_PIXEL_SIZE, FC_SPACING,
+                                FC_CHARSET, FC_FILE, NULL);
+      if (! objset)
+       goto err;
+      pattern = FcPatternBuild (NULL, FC_FAMILY, FcTypeString,
+                               SYMBOL_FcChar8 (family), (char *) 0);
+      if (! pattern)
+       goto err;
+      pat = FcPatternCreate ();
+      if (! pat)
+       goto err;
+      FcConfigSubstitute (NULL, pattern, FcMatchPattern);
+      for (i = 0, val = Qnil;
+          FcPatternGetString (pattern, FC_FAMILY, i, &fam) == FcResultMatch;
+          i++)
+       {
+         if (strcmp ((char *) fam, (char *) SYMBOL_FcChar8 (family)) == 0)
+           continue;
+         if (! FcPatternAddString (pat, FC_FAMILY, fam))
+           goto err;
+         fontset = FcFontList (NULL, pat, objset);
+         if (! fontset)
+           goto err;
+         /* Here we build the list in reverse order so that the last
+            loop in this function build a list in the correct
+            order.  */
+         for (j = 0; j < fontset->nfont; j++)
+           {
+             Lisp_Object entity;
+
+             entity = ftfont_pattern_entity (fontset->fonts[j],
+                                             frame, registry);
+             if (! NILP (entity))
+               val = Fcons (entity, val);
+           }
+         FcFontSetDestroy (fontset);
+         fontset = NULL;
+         FcPatternDel (pat, FC_FAMILY);
+       }
+      list = val;
+      XSETCDR (slot, list);
+    err:
+      if (pat) FcPatternDestroy (pat);
+      if (pattern) FcPatternDestroy (pattern);
+      if (fontset) FcFontSetDestroy (fontset);
+      if (objset) FcObjectSetDestroy (objset);
+      if (EQ (list, Qt))
+       return Qnil;
+    }
+  ASET (spec, FONT_FAMILY_INDEX, Qnil);
+  for (val = Qnil; CONSP (list); list = XCDR (list))
+    if (font_match_p (spec, XCAR (list)))
+      val = Fcons (XCAR (list), val);
+  ASET (spec, FONT_FAMILY_INDEX, family);
+  return Fvconcat (1, &val);
+}
+
+
 static Lisp_Object ftfont_get_cache P_ ((Lisp_Object));
-static int ftfont_parse_name P_ ((FRAME_PTR, char *, Lisp_Object));
 static Lisp_Object ftfont_list P_ ((Lisp_Object, Lisp_Object));
 static Lisp_Object ftfont_list_family P_ ((Lisp_Object));
 static void ftfont_free_entity P_ ((Lisp_Object));
@@ -97,7 +261,6 @@ struct font_driver ftfont_driver =
   {
     (Lisp_Object) NULL,                /* Qfreetype */
     ftfont_get_cache,
-    ftfont_parse_name,    
     ftfont_list,
     ftfont_list_family,
     ftfont_free_entity,
@@ -127,55 +290,15 @@ struct font_driver ftfont_driver =
 #endif /* HAVE_LIBOTF */
   };
 
-#define SYMBOL_FcChar8(SYM) (FcChar8 *) SDATA (SYMBOL_NAME (SYM))
-
 extern Lisp_Object QCname;
 
 static Lisp_Object
 ftfont_get_cache (frame)
      Lisp_Object frame;
 {
-  if (NILP (freetype_font_cache))
-    freetype_font_cache = Fcons (Qt, Qnil);
   return freetype_font_cache;
 }
 
-static int
-ftfont_parse_name (f, name, spec)
-     FRAME_PTR f;
-     char *name;
-     Lisp_Object spec;
-{
-  FcPattern *p;
-  FcChar8 *str;
-  int numeric;
-  double dbl;
-
-  if (name[0] == '-' || strchr (name, '*'))
-    /* It seems that NAME is XLFD.  */
-    return -1;
-  p = FcNameParse ((FcChar8 *) name);
-  if (! p)
-    return -1;
-  if (FcPatternGetString (p, FC_FOUNDRY, 0, &str) == FcResultMatch)
-    ASET (spec, FONT_FOUNDRY_INDEX,
-         intern_downcase ((char *) str, strlen ((char *) str)));
-  if (FcPatternGetString (p, FC_FAMILY, 0, &str) == FcResultMatch)
-    ASET (spec, FONT_FAMILY_INDEX,
-         intern_downcase ((char *) str, strlen ((char *) str)));
-  if (FcPatternGetInteger (p, FC_WEIGHT, 0, &numeric) == FcResultMatch)
-    ASET (spec, FONT_WEIGHT_INDEX, make_number (numeric));
-  if (FcPatternGetInteger (p, FC_SLANT, 0, &numeric) == FcResultMatch)
-    ASET (spec, FONT_SLANT_INDEX, make_number (numeric + 100));
-  if (FcPatternGetInteger (p, FC_WIDTH, 0, &numeric) == FcResultMatch)
-    ASET (spec, FONT_WIDTH_INDEX, make_number (numeric));
-  if (FcPatternGetDouble (p, FC_PIXEL_SIZE, 0, &dbl) == FcResultMatch)
-    ASET (spec, FONT_SIZE_INDEX, make_number (dbl));
-  else if (FcPatternGetDouble (p, FC_SIZE, 0, &dbl) == FcResultMatch)
-    ASET (spec, FONT_SIZE_INDEX, make_float (dbl));
-  return 0;
-}
-
 static Lisp_Object
 ftfont_list (frame, spec)
      Lisp_Object frame, spec;
@@ -187,7 +310,7 @@ ftfont_list (frame, spec)
   FcLangSet *langset = NULL;
   FcFontSet *fontset = NULL;
   FcObjectSet *objset = NULL;
-  Lisp_Object registry = Qnil;
+  Lisp_Object registry = Qunicode_bmp;
   
   val = null_vector;
 
@@ -206,10 +329,11 @@ ftfont_list (frame, spec)
        {
          if (! cs_iso8859_1
              && ftfont_build_basic_charsets () < 0)
-           goto err;
+           return Qnil;
          charset = cs_iso8859_1;
-         registry = Qnil;
        }
+      else if (! EQ (registry, Qiso10646_1) && ! EQ (registry, Qunicode_bmp))
+       return val;
     }
 
   extra = AREF (spec, FONT_EXTRA_INDEX);
@@ -263,47 +387,44 @@ ftfont_list (frame, spec)
        }
     }
 
-  if (! NILP (registry) && ! charset)
-    goto finish;
-
   if (STRINGP (font_name))
     {
-      if (! isalpha (SDATA (font_name)[0]))
-       goto finish;
       pattern = FcNameParse (SDATA (font_name));
-      if (! pattern)
-       goto err;
+      /* Ignore these values in listing.  */
+      FcPatternDel (pattern, FC_PIXEL_SIZE);
+      FcPatternDel (pattern, FC_SIZE);
+      FcPatternDel (pattern, FC_FAMILY);
     }
   else
-    {
-      pattern = FcPatternCreate ();
-      if (! pattern)
-       goto err;
+    pattern = FcPatternCreate ();
+  if (! pattern)
+    goto err;
 
-      tmp = AREF (spec, FONT_FOUNDRY_INDEX);
-      if (SYMBOLP (tmp) && ! NILP (tmp)
-         && ! FcPatternAddString (pattern, FC_FOUNDRY, SYMBOL_FcChar8 (tmp)))
-       goto err;
-      tmp = AREF (spec, FONT_FAMILY_INDEX);
-      if (SYMBOLP (tmp) && ! NILP (tmp)
-         && ! FcPatternAddString (pattern, FC_FAMILY, SYMBOL_FcChar8 (tmp)))
-       goto err;
-      tmp = AREF (spec, FONT_WEIGHT_INDEX);
-      if (INTEGERP (tmp)
-         && ! FcPatternAddInteger (pattern, FC_WEIGHT, XINT (tmp)))
-       goto err;
-      tmp = AREF (spec, FONT_SLANT_INDEX);
-      if (INTEGERP (tmp)
-         && XINT (tmp) >= 100
-         && ! FcPatternAddInteger (pattern, FC_SLANT, XINT (tmp) - 100))
-       goto err;
-      tmp = AREF (spec, FONT_WIDTH_INDEX);
-      if (INTEGERP (tmp)
-         && ! FcPatternAddInteger (pattern, FC_WIDTH, XINT (tmp)))
-       goto err;
-      if (! FcPatternAddBool (pattern, FC_SCALABLE, FcTrue))
-       goto err;
-    }
+  tmp = AREF (spec, FONT_FOUNDRY_INDEX);
+  if (SYMBOLP (tmp) && ! NILP (tmp)
+      && ! FcPatternAddString (pattern, FC_FOUNDRY, SYMBOL_FcChar8 (tmp)))
+    goto err;
+  tmp = AREF (spec, FONT_FAMILY_INDEX);
+  if (SYMBOLP (tmp) && ! NILP (tmp)
+      && ! FcPatternAddString (pattern, FC_FAMILY, SYMBOL_FcChar8 (tmp)))
+    goto err;
+  tmp = AREF (spec, FONT_WEIGHT_INDEX);
+  if (INTEGERP (tmp)
+      && ! FcPatternAddInteger (pattern, FC_WEIGHT, XINT (tmp)))
+    goto err;
+  tmp = AREF (spec, FONT_SLANT_INDEX);
+  if (INTEGERP (tmp)
+      && XINT (tmp) >= 100
+      && ! FcPatternAddInteger (pattern, FC_SLANT, XINT (tmp) - 100))
+    goto err;
+  tmp = AREF (spec, FONT_WIDTH_INDEX);
+  if (INTEGERP (tmp)
+      && ! FcPatternAddInteger (pattern, FC_WIDTH, XINT (tmp)))
+    goto err;
+#if 0
+  if (! FcPatternAddBool (pattern, FC_SCALABLE, FcTrue))
+    goto err;
+#endif
 
   if (charset
       && ! FcPatternAddCharSet (pattern, FC_CHARSET, charset))
@@ -311,73 +432,52 @@ ftfont_list (frame, spec)
   if (langset
       && ! FcPatternAddLangSet (pattern, FC_LANG, langset))
     goto err;
+
   objset = FcObjectSetBuild (FC_FOUNDRY, FC_FAMILY, FC_WEIGHT, FC_SLANT,
                             FC_WIDTH, FC_PIXEL_SIZE, FC_SPACING,
                             FC_CHARSET, FC_FILE, NULL);
   if (! objset)
     goto err;
 
-  BLOCK_INPUT;
   fontset = FcFontList (NULL, pattern, objset);
-  UNBLOCK_INPUT;
   if (! fontset)
     goto err;
-  val = Qnil;
-  for (i = 0; i < fontset->nfont; i++)
+
+  if (fontset->nfont > 0)
     {
-      FcPattern *p = fontset->fonts[i];
-      FcChar8 *str, *file;
+      double pixel_size;
 
-      if (FcPatternGetString (p, FC_FILE, 0, &file) == FcResultMatch
-         && FcPatternGetCharSet (p, FC_CHARSET, 0, &charset) == FcResultMatch)
+      if (NILP (AREF (spec, FONT_SIZE_INDEX)))
+       pixel_size = 0;
+      else
+       pixel_size = XINT (AREF (spec, FONT_SIZE_INDEX));
+
+      for (i = 0, val = Qnil; i < fontset->nfont; i++)
        {
-         Lisp_Object entity = Fmake_vector (make_number (FONT_ENTITY_MAX),
-                                            null_string);
-         int numeric;
-         double dbl;
-         FcPattern *p0;
-
-         ASET (entity, FONT_TYPE_INDEX, Qfreetype);
-         ASET (entity, FONT_REGISTRY_INDEX, Qiso10646_1);
-         ASET (entity, FONT_FRAME_INDEX, frame);
-         ASET (entity, FONT_OBJLIST_INDEX, Qnil);
-
-         if (FcPatternGetString (p, FC_FOUNDRY, 0, &str) == FcResultMatch)
-           ASET (entity, FONT_FOUNDRY_INDEX,
-                 intern_downcase ((char *) str, strlen ((char *) str)));
-         if (FcPatternGetString (p, FC_FAMILY, 0, &str) == FcResultMatch)
-           ASET (entity, FONT_FAMILY_INDEX,
-                 intern_downcase ((char *) str, strlen ((char *) str)));
-         if (FcPatternGetInteger (p, FC_WEIGHT, 0, &numeric) == FcResultMatch)
-           ASET (entity, FONT_WEIGHT_INDEX, make_number (numeric));
-         if (FcPatternGetInteger (p, FC_SLANT, 0, &numeric) == FcResultMatch)
-           ASET (entity, FONT_SLANT_INDEX, make_number (numeric + 100));
-         if (FcPatternGetInteger (p, FC_WIDTH, 0, &numeric) == FcResultMatch)
-           ASET (entity, FONT_WIDTH_INDEX, make_number (numeric));
-         if (FcPatternGetDouble (p, FC_PIXEL_SIZE, 0, &dbl) == FcResultMatch)
-           ASET (entity, FONT_SIZE_INDEX, make_number (dbl));
-         else
-           ASET (entity, FONT_SIZE_INDEX, make_number (0));
-
-         if (FcPatternGetInteger (p, FC_SPACING, 0, &numeric) != FcResultMatch)
-           numeric = FC_MONO;
-         p0 = FcPatternCreate ();
-         if (! p0
-             || FcPatternAddString (p0, FC_FILE, file) == FcFalse
-             || FcPatternAddCharSet (p0, FC_CHARSET, charset) == FcFalse
-             || FcPatternAddInteger (p0, FC_SPACING, numeric) == FcFalse)
-           break;
-         ASET (entity, FONT_EXTRA_INDEX, make_save_value (p0, 0));
+         Lisp_Object entity;
 
-         val = Fcons (entity, val);
+         if (pixel_size > 0)
+           {
+             double this;
+
+             if (FcPatternGetDouble (fontset->fonts[i], FC_PIXEL_SIZE, 0,
+                                     &this) == FcResultMatch
+                 && this != pixel_size)
+               continue;
+           }
+         entity = ftfont_pattern_entity (fontset->fonts[i], frame, registry);
+         if (! NILP (entity))
+           val = Fcons (entity, val);
        }
+      val = Fvconcat (1, &val);
     }
-  val = Fvconcat (1, &val);
+  else if (! NILP (AREF (spec, FONT_FAMILY_INDEX)))
+    val = ftfont_list_generic_family (spec, frame, registry);
   goto finish;
 
  err:
   /* We come here because of unexpected error in fontconfig API call
-     (usually insufficiency memory).  */
+     (usually insufficient memory).  */
   val = Qnil;
 
  finish:
@@ -409,7 +509,7 @@ ftfont_list_family (frame)
   pattern = FcPatternCreate ();
   if (! pattern)
     goto finish;
-  objset = FcObjectSetBuild (FC_FAMILY);
+  objset = FcObjectSetBuild (FC_FAMILY, NULL);
   if (! objset)
     goto finish;
   fontset = FcFontList (NULL, pattern, objset);
@@ -586,8 +686,8 @@ ftfont_has_char (entity, c)
 
   val = AREF (entity, FONT_EXTRA_INDEX);
   pattern = XSAVE_VALUE (val)->pointer;
-  FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset);
-
+  if (FcPatternGetCharSet (pattern, FC_CHARSET, 0, &charset) != FcResultMatch)
+    return -1;
   return (FcCharSetHasChar (charset, (FcChar32) c) == FcTrue);
 }
 
@@ -718,14 +818,26 @@ ftfont_anchor_point (font, code, index, x, y)
 void
 syms_of_ftfont ()
 {
+  DEFSYM (Qfreetype, "freetype");
+  DEFSYM (Qmonospace, "monospace");
+  DEFSYM (Qsans_serif, "sans-serif");
+  DEFSYM (Qserif, "serif");
+  DEFSYM (Qmono, "mono");
+  DEFSYM (Qsans, "sans");
+  DEFSYM (Qsans__serif, "sans serif");
+
   staticpro (&freetype_font_cache);
-  freetype_font_cache = Qnil;
+  freetype_font_cache = Fcons (Qt, Qnil);
 
-  DEFSYM (Qfreetype, "freetype");
-  DEFSYM (Qiso8859_1, "iso8859-1");
-  DEFSYM (Qiso10646_1, "iso10646-1");
-  DEFSYM (Qunicode_bmp, "unicode-bmp");
+  staticpro (&ftfont_generic_family_list);
+  ftfont_generic_family_list
+    = Fcons (Fcons (Qmonospace, Qt),
+            Fcons (Fcons (Qsans_serif, Qt),
+                   Fcons (Fcons (Qsans, Qt), Qnil)));
 
   ftfont_driver.type = Qfreetype;
   register_font_driver (&ftfont_driver, NULL);
 }
+
+/* arch-tag: 7cfa432c-33a6-4988-83d2-a82ed8604aca
+   (do not change this comment) */