*** empty log message ***
[m17n/m17n-lib.git] / src / fontset.c
index d7c0a1a..d3f3c7c 100644 (file)
@@ -63,6 +63,7 @@
 #include <string.h>
 #include <ctype.h>
 
+#include "config.h"
 #include "m17n-gui.h"
 #include "m17n-misc.h"
 #include "internal.h"
@@ -333,72 +334,16 @@ realize_font_group (MFrame *frame, MFont *request, MPlist *font_group,
     }
 }
 
-\f
-
-/* Internal API */
-
-int
-mfont__fontset_init ()
-{
-  Mfontset = msymbol ("fontset");
-  Mfontset->managing_key = 1;
-  fontset_list = mplist ();
-  default_fontset = mfontset ("default");
-  if (! default_fontset->mdb)
-    {
-      MFont font;
-
-      MFONT_INIT (&font);
-      mfont_put_prop (&font, Mregistry, msymbol ("iso8859-1"));
-      mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil,
-                            &font, Mnil, 1);
-      mfont_put_prop (&font, Mregistry, msymbol ("iso10646-1"));
-      mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil,
-                            &font, Mnil, 1);
-    }
-  return 0;
-}
-
-
-void
-mfont__fontset_fini ()
-{
-  while (! MPLIST_TAIL_P (fontset_list))
-    free_fontset ((MFontset *) MPLIST_VAL (fontset_list));
-  M17N_OBJECT_UNREF (fontset_list);
-  fontset_list = NULL;
-}
-
-
-MRealizedFontset *
-mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face)
+static void
+realize_fontset_elements (MFrame *frame, MRealizedFontset *realized,
+                         MFontset *fontset, MFont *request)
 {
-  MRealizedFontset *realized;
-  MFont request;
   MPlist *per_script, *per_lang, *per_charset, *font_group;
-  MPlist *plist, *pl, *p;
-
-  if (fontset->mdb)
-    load_fontset_contents (fontset);
-
-  mfont__set_spec_from_face (&request, face);
-  if (request.property[MFONT_SIZE] <= 0)
-    {
-      mdebug_hook ();
-      request.property[MFONT_SIZE] = 120;
-    }
-  MPLIST_DO (p, frame->realized_fontset_list)
-    {
-      realized = (MRealizedFontset *) MPLIST_VAL (p);
-      if (fontset->name == MPLIST_KEY (p)
-         && ! memcmp (&request, &realized->spec, sizeof (request)))
-       return realized;
-    }
+  MPlist *plist, *pl;
 
-  MSTRUCT_MALLOC (realized, MERROR_FONTSET);
   realized->fontset = fontset;
   realized->tick = fontset->tick;
-  realized->spec = request;
+  realized->spec = *request;
   realized->frame = frame;
   realized->per_script = per_script = mplist ();
   MPLIST_DO (plist, fontset->per_script)
@@ -424,13 +369,10 @@ mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face)
   realized->fallback = mplist ();
   mplist_add (realized->fallback, Mplist, fontset->fallback);
 
-  mplist_add (frame->realized_fontset_list, fontset->name, realized);
-  return realized;
 }
 
-
-void
-mfont__free_realized_fontset (MRealizedFontset *realized)
+static void
+free_realized_fontset_elements (MRealizedFontset *realized)
 {
   MPlist *plist, *pl, *p;
   MRealizedFont *rfont;
@@ -471,7 +413,89 @@ mfont__free_realized_fontset (MRealizedFontset *realized)
          free (rfont);
       M17N_OBJECT_UNREF (realized->fallback);
     }
+}
+
+static void
+update_fontset_elements (MRealizedFontset *realized)
+{
+  free_realized_fontset_elements (realized);
+  realize_fontset_elements (realized->frame, realized, realized->fontset,
+                           &realized->spec);
+}
+
+
+\f
+
+/* Internal API */
+
+int
+mfont__fontset_init ()
+{
+  Mfontset = msymbol ("fontset");
+  Mfontset->managing_key = 1;
+  fontset_list = mplist ();
+  default_fontset = mfontset ("default");
+  if (! default_fontset->mdb)
+    {
+      MFont font;
+
+      MFONT_INIT (&font);
+      mfont_put_prop (&font, Mregistry, msymbol ("iso8859-1"));
+      mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil,
+                            &font, Mnil, 1);
+      mfont_put_prop (&font, Mregistry, msymbol ("iso10646-1"));
+      mfontset_modify_entry (default_fontset, Mnil, Mnil, Mnil,
+                            &font, Mnil, 1);
+    }
+  return 0;
+}
+
+
+void
+mfont__fontset_fini ()
+{
+  while (! MPLIST_TAIL_P (fontset_list))
+    free_fontset ((MFontset *) MPLIST_VAL (fontset_list));
+  M17N_OBJECT_UNREF (fontset_list);
+  fontset_list = NULL;
+}
+
 
+MRealizedFontset *
+mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face)
+{
+  MRealizedFontset *realized;
+  MFont request;
+  MPlist *plist;
+
+  if (fontset->mdb)
+    load_fontset_contents (fontset);
+
+  mfont__set_spec_from_face (&request, face);
+  if (request.property[MFONT_SIZE] <= 0)
+    {
+      mdebug_hook ();
+      request.property[MFONT_SIZE] = 120;
+    }
+  MPLIST_DO (plist, frame->realized_fontset_list)
+    {
+      realized = (MRealizedFontset *) MPLIST_VAL (plist);
+      if (fontset->name == MPLIST_KEY (plist)
+         && ! memcmp (&request, &realized->spec, sizeof (request)))
+       return realized;
+    }
+
+  MSTRUCT_MALLOC (realized, MERROR_FONTSET);
+  realize_fontset_elements (frame, realized, fontset, &request);
+  mplist_add (frame->realized_fontset_list, fontset->name, realized);
+  return realized;
+}
+
+
+void
+mfont__free_realized_fontset (MRealizedFontset *realized)
+{
+  free_realized_fontset_elements (realized);
   free (realized);
 }
 
@@ -483,13 +507,15 @@ mfont__lookup_fontset (MRealizedFontset *realized, MGlyph *g, int *num,
 {
   MFrame *frame = realized->frame;
   MCharset *preferred_charset = (charset == Mnil ? NULL : MCHARSET (charset));
-  MPlist *per_charset, *per_script, *per_lang, *font_group;
+  MPlist *per_charset, *per_script, *per_lang;
   MPlist *font_groups[256], *plist;
   int n_font_group = 0;
-  MRealizedFont *first = NULL, *rfont;
-  int first_len;
+  MRealizedFont *rfont;
   int i;
 
+  if (realized->tick != realized->fontset->tick)
+    update_fontset_elements (realized);
+
   if (preferred_charset
       && (per_charset = mplist_get (realized->per_charset, charset)) != NULL)
     font_groups[n_font_group++] = per_charset;
@@ -497,16 +523,33 @@ mfont__lookup_fontset (MRealizedFontset *realized, MGlyph *g, int *num,
       && ((per_script = mplist_find_by_key (realized->per_script, script))
          != NULL))
     {
-      /* The first loop is for matching language (if any), and the second
-        loop is for non-matching languages.  */
+      /* We prefer font groups in this order:
+         (1) group matching LANGUAGE
+         (2) group for generic LANGUAGE
+         (3) group non-matching LANGUAGE  */
       if (language == Mnil)
        language = Mt;
-      for (i = 0; i < 2; i++)
+      per_lang = mplist_find_by_key (MPLIST_PLIST (per_script), language);
+      if (per_lang)
+       {
+         font_groups[n_font_group++] = MPLIST_PLIST (per_lang);
+         if (language == Mt)
+           {
+             MPLIST_DO (per_lang, MPLIST_PLIST (per_script))
+               if (MPLIST_KEY (per_lang) != language)
+                 font_groups[n_font_group++] = MPLIST_PLIST (per_lang);
+           }
+       }
+      if (language != Mt)
        {
-         MPLIST_DO (per_lang, MPLIST_PLIST (per_script))
-           if ((MPLIST_KEY (per_lang) == language) != i)
-             font_groups[n_font_group++] = MPLIST_PLIST (per_lang);
+         plist = mplist_get (MPLIST_PLIST (per_script), Mt);
+         if (plist)
+           font_groups[n_font_group++] = plist;
        }
+      MPLIST_DO (per_lang, MPLIST_PLIST (per_script))
+       if (MPLIST_KEY (per_lang) != language
+           && MPLIST_KEY (per_lang) != Mt)
+         font_groups[n_font_group++] = MPLIST_PLIST (per_lang);
     }
   font_groups[n_font_group++] = realized->fallback;
 
@@ -533,58 +576,52 @@ mfont__lookup_fontset (MRealizedFontset *realized, MGlyph *g, int *num,
          rfont = (MRealizedFont *) MPLIST_VAL (plist);
          if (rfont->status < 0)
            continue;
-         g->code = mfont__encode_char (rfont, g->c);
-         if (g->code != MCHAR_INVALID_CODE)
-           break;
-       }
-      if (MPLIST_TAIL_P (plist))
-       continue;
-      for (j = 1; j < *num; j++)
-       {
-         g[j].code = mfont__encode_char (rfont, g[j].c);
-         if (g[j].code == MCHAR_INVALID_CODE)
-           break;
+         /* Check if this font can display all glyphs.  */
+         for (j = 0; j < *num; j++)
+           {
+             g[j].code = mfont__encode_char (rfont,
+                                             g[j].type == GLYPH_CHAR ? g[j].c
+                                             : ' ');
+             if (g[j].code == MCHAR_INVALID_CODE)
+               break;
+           }
+         if (j == *num)
+           {
+             if (rfont->status > 0
+                 || mfont__open (rfont) == 0)
+               /* We found a font that can display all glyphs.  */
+               break;
+           }
        }
-      if (! first)
-       first = rfont, first_len = j;
-      if (j == *num)
-       /* We found a font that can display all requested
-          characters.  */
+      if (! MPLIST_TAIL_P (plist))
        break;
+    }
+
+  if (i < n_font_group) 
+    return rfont;
 
-      MPLIST_DO (plist, MPLIST_NEXT (plist))
+  /* We couldn't find a font that can display all glyphs.  Find one
+     that can display at least the first glyph.  */
+  for (i = 0; i < n_font_group; i++)
+    {
+      MPLIST_DO (plist, font_groups[i])
        {
          rfont = (MRealizedFont *) MPLIST_VAL (plist);
          if (rfont->status < 0)
            continue;
-         for (j = 0; j < *num; j++)
+         g->code = mfont__encode_char (rfont,
+                                       g->type == GLYPH_CHAR ? g->c : ' ');
+         if (g->code != MCHAR_INVALID_CODE)
            {
-             g[j].code = mfont__encode_char (rfont, g[j].c);
-             if (g[j].code == MCHAR_INVALID_CODE)
+             if (rfont->status > 0
+                 || mfont__open (rfont) == 0)
                break;
            }
-         if (j == *num)
-           break;
        }
       if (! MPLIST_TAIL_P (plist))
        break;
     }
-
-  if (i == n_font_group) 
-    {
-      if (! first)
-       return NULL;
-      rfont = first, *num = first_len;
-      for (i = 0; i < *num; i++)
-       g[i].code = mfont__encode_char (rfont, g[i].c);
-    }
-  if (! rfont->status
-      && mfont__open (rfont) < 0)
-    {
-      MPLIST_VAL (font_group) = NULL;
-      return NULL;
-    }
-  return rfont;
+  return (i < n_font_group ? rfont : NULL);
 }
 
 /*** @} */
@@ -644,21 +681,25 @@ mfontset (char *name)
   MFontset *fontset;
 
   if (! name)
-    return default_fontset;
-  sym = msymbol (name);
-  fontset = mplist_get (fontset_list, sym);
-  if (fontset)
-    return fontset;
-  M17N_OBJECT (fontset, free_fontset, MERROR_FONTSET);
-  fontset->name = sym;
-  fontset->mdb = mdatabase_find (Mfontset, sym, Mnil, Mnil);
-  if (! fontset->mdb)
+    fontset = default_fontset;
+  else
     {
-      fontset->per_script = mplist ();
-      fontset->per_charset = mplist ();
-      fontset->fallback = mplist ();
+      sym = msymbol (name);
+      fontset = mplist_get (fontset_list, sym);
+      if (! fontset)
+       {
+         M17N_OBJECT (fontset, free_fontset, MERROR_FONTSET);
+         fontset->name = sym;
+         fontset->mdb = mdatabase_find (Mfontset, sym, Mnil, Mnil);
+         if (! fontset->mdb)
+           {
+             fontset->per_script = mplist ();
+             fontset->per_charset = mplist ();
+             fontset->fallback = mplist ();
+           }
+         mplist_put (fontset_list, sym, fontset);
+       }
     }
-  mplist_put (fontset_list, sym, fontset);
   M17N_OBJECT_REF (fontset);
   return fontset;
 }
@@ -843,14 +884,17 @@ mfontset_modify_entry (MFontset *fontset,
   if (fontset->mdb)
     load_fontset_contents (fontset);
 
-  MPLIST_DO (pl, fontset->font_spec_list)
-    {
-      if (! memcmp (MPLIST_VAL (pl), spec, sizeof (MFont)))
-       {
-         font = MPLIST_VAL (pl);
-         break;
-       }
-    }
+  if (! fontset->font_spec_list)
+    fontset->font_spec_list = mplist ();
+  else
+    MPLIST_DO (pl, fontset->font_spec_list)
+      {
+       if (! memcmp (MPLIST_VAL (pl), spec, sizeof (MFont)))
+         {
+           font = MPLIST_VAL (pl);
+           break;
+         }
+      }
   if (! font)
     {
       font = mfont ();
@@ -898,9 +942,110 @@ mfontset_modify_entry (MFontset *fontset,
        }
     }
 
+  fontset->tick++;
   return 0;
 }
 
+/*=*/
+
+/***en
+    @brief Lookup a fontset.
+
+    The mfontset_lookup () function lookups $FONTSET and returns a
+    plist that describes the contents of $FONTSET corresponding to the
+    specified script, language, and charset.
+
+    If $SCRIPT is @c Mt, keys of the returned plist are script name
+    symbols for which some fonts are specified and values are NULL.
+
+    If $SCIRPT is a script symbol, the returned plist is decided by
+    $LANGUAGE.
+
+    If $LANGUAGE is @c Mt, keys of the plist are language name symbols
+    for which some fonts are specified and values are NULL.  A key may
+    be @c Mt which means some fallback fonts are specified for the
+    script.
+
+    If $LANGUAGE is a language name symbol, the plist is a @c
+    FONT-GROUP for the specified script and langauge.
+
+    If $LANGAUGE is @c Mt, the plist is fallback @c FONT-GROUP for the
+    script.
+
+    If $SCRIPT is @c Mnil, the returned plist is decided as below.
+
+    If $CHARSET is @c Mt, keys of the returned plist are charset name
+    symbols for which some fonts are specified and values are NULL.
+
+    If $CHARSET is a charset symbol, the plist is a @c FONT-GROUP for
+    the charset.
+
+    If $CHARSET is @c Mnil, the plist is a fallback @c FONT-GROUP.
+
+    @c FONT-GROUP is a plist whose keys are FLT name symbols (@c Mt if
+    no FLT is associated with the font) and values are pointers to
+    #MFont.
+
+    @return
+    It returns a plist describing the contents of a fontset.  The
+    plist should be freed by m17n_object_unref ().  */
+
+MPlist *
+mfontset_lookup (MFontset *fontset,
+                MSymbol script, MSymbol language, MSymbol charset)
+{
+  MPlist *plist = mplist (), *pl, *p;
+
+  if (fontset->mdb)
+    load_fontset_contents (fontset);
+  if (script == Mt)
+    {
+      if (! fontset->per_script)
+       return plist;
+      p = plist;
+      MPLIST_DO (pl, fontset->per_script)
+       p = mplist_add (p, MPLIST_KEY (pl), NULL);
+      return plist;
+    }
+  if (script != Mnil)
+    {
+      if (! fontset->per_script)
+       return plist;
+      pl = mplist_get (fontset->per_script, script);
+      if (! pl)
+       return plist;
+      if (language == Mt)
+       {
+         p = plist;
+         MPLIST_DO (pl, pl)
+           p = mplist_add (p, MPLIST_KEY (pl), NULL);
+         return plist;
+       }
+      if (language == Mnil)
+       language = Mt;
+      pl = mplist_get (pl, language);
+    }
+  else if (charset != Mnil)
+    {
+      if (! fontset->per_charset)
+       return plist;
+      if (charset == Mt)
+       {
+         p = plist;
+         MPLIST_DO (pl, fontset->per_charset)
+           p = mplist_add (p, MPLIST_KEY (pl), NULL);
+         return plist;
+       }
+      pl = mplist_get (fontset->per_charset, charset);
+    }
+  else
+    pl = fontset->fallback;
+  if (! pl)
+    return plist;
+  return mplist_copy (pl);
+}
+
+
 /*** @} */
 
 /*** @addtogroup m17nDebug */