*** empty log message ***
[m17n/m17n-lib.git] / src / fontset.c
index 4924acf..016c556 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,6 +334,96 @@ realize_font_group (MFrame *frame, MFont *request, MPlist *font_group,
     }
 }
 
+static void
+realize_fontset_elements (MFrame *frame, MRealizedFontset *realized,
+                         MFontset *fontset, MFont *request)
+{
+  MPlist *per_script, *per_lang, *per_charset, *font_group;
+  MPlist *plist, *pl;
+
+  realized->fontset = fontset;
+  realized->tick = fontset->tick;
+  realized->spec = *request;
+  realized->frame = frame;
+  realized->per_script = per_script = mplist ();
+  MPLIST_DO (plist, fontset->per_script)
+    {
+      per_lang = mplist ();
+      per_script = mplist_add (per_script, MPLIST_KEY (plist), per_lang);
+      MPLIST_DO (pl, MPLIST_PLIST (plist))
+       {
+         font_group = mplist ();
+         mplist_add (font_group, Mplist, MPLIST_VAL (pl));
+         per_lang = mplist_add (per_lang, MPLIST_KEY (pl), font_group);
+       }
+    }
+
+  realized->per_charset = per_charset = mplist ();
+  MPLIST_DO (plist, fontset->per_charset)
+    {
+      font_group = mplist ();
+      mplist_add (font_group, Mplist, MPLIST_VAL (plist));
+      per_charset = mplist_add (per_charset, MPLIST_KEY (plist), font_group);
+    }
+
+  realized->fallback = mplist ();
+  mplist_add (realized->fallback, Mplist, fontset->fallback);
+
+}
+
+static void
+free_realized_fontset_elements (MRealizedFontset *realized)
+{
+  MPlist *plist, *pl, *p;
+  MRealizedFont *rfont;
+
+  if (realized->per_script)
+    {
+      MPLIST_DO (plist, realized->per_script)
+       {
+         MPLIST_DO (pl, MPLIST_PLIST (plist))
+           {
+             MPLIST_DO (p, MPLIST_PLIST (pl))
+               if ((rfont = MPLIST_VAL (p)) && ! rfont->frame)
+                 free (rfont);
+             p = MPLIST_PLIST (pl);
+             M17N_OBJECT_UNREF (p);
+           }
+         pl = MPLIST_PLIST (plist);
+         M17N_OBJECT_UNREF (pl);
+       }
+      M17N_OBJECT_UNREF (realized->per_script);
+    }
+  if (realized->per_charset)
+    {
+      MPLIST_DO (plist, realized->per_charset)
+       {
+         MPLIST_DO (pl, MPLIST_PLIST (plist))
+           if ((rfont = MPLIST_VAL (pl)) && ! rfont->frame)
+             free (rfont);
+         pl = MPLIST_PLIST (plist);
+         M17N_OBJECT_UNREF (pl);
+       }
+      M17N_OBJECT_UNREF (realized->per_charset);
+    }
+  if (realized->fallback)
+    {
+      MPLIST_DO (plist, realized->fallback)
+       if ((rfont = MPLIST_VAL (plist)) && ! rfont->frame)
+         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 */
@@ -375,8 +466,7 @@ mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face)
 {
   MRealizedFontset *realized;
   MFont request;
-  MPlist *per_script, *per_lang, *per_charset, *font_group;
-  MPlist *plist, *pl, *p;
+  MPlist *plist;
 
   if (fontset->mdb)
     load_fontset_contents (fontset);
@@ -387,43 +477,16 @@ mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face)
       mdebug_hook ();
       request.property[MFONT_SIZE] = 120;
     }
-  MPLIST_DO (p, frame->realized_fontset_list)
+  MPLIST_DO (plist, frame->realized_fontset_list)
     {
-      realized = (MRealizedFontset *) MPLIST_VAL (p);
-      if (fontset->name == MPLIST_KEY (p)
+      realized = (MRealizedFontset *) MPLIST_VAL (plist);
+      if (fontset->name == MPLIST_KEY (plist)
          && ! memcmp (&request, &realized->spec, sizeof (request)))
        return realized;
     }
 
   MSTRUCT_MALLOC (realized, MERROR_FONTSET);
-  realized->fontset = fontset;
-  realized->tick = fontset->tick;
-  realized->spec = request;
-  realized->frame = frame;
-  realized->per_script = per_script = mplist ();
-  MPLIST_DO (plist, fontset->per_script)
-    {
-      per_lang = mplist ();
-      per_script = mplist_add (per_script, MPLIST_KEY (plist), per_lang);
-      MPLIST_DO (pl, MPLIST_PLIST (plist))
-       {
-         font_group = mplist ();
-         mplist_add (font_group, Mplist, MPLIST_VAL (pl));
-         per_lang = mplist_add (per_lang, MPLIST_KEY (pl), font_group);
-       }
-    }
-
-  realized->per_charset = per_charset = mplist ();
-  MPLIST_DO (plist, fontset->per_charset)
-    {
-      font_group = mplist ();
-      mplist_add (font_group, Mplist, MPLIST_VAL (plist));
-      per_charset = mplist_add (per_charset, MPLIST_KEY (plist), font_group);
-    }
-
-  realized->fallback = mplist ();
-  mplist_add (realized->fallback, Mplist, fontset->fallback);
-
+  realize_fontset_elements (frame, realized, fontset, &request);
   mplist_add (frame->realized_fontset_list, fontset->name, realized);
   return realized;
 }
@@ -432,155 +495,163 @@ mfont__realize_fontset (MFrame *frame, MFontset *fontset, MFace *face)
 void
 mfont__free_realized_fontset (MRealizedFontset *realized)
 {
-  MPlist *plist, *pl, *p;
+  free_realized_fontset_elements (realized);
+  free (realized);
+}
+
+
+static MRealizedFont *
+try_font_group (MRealizedFontset *realized,
+               MPlist *font_group, MGlyph *g, int *num, int size)
+{
+  MFrame *frame = realized->frame;
   MRealizedFont *rfont;
+  MPlist *plist;
+  int i;
 
-  if (realized->per_script)
+  if (MPLIST_PLIST_P (font_group))
+    realize_font_group (frame, &realized->spec, font_group, size);
+
+  MPLIST_DO (plist, font_group)
     {
-      MPLIST_DO (plist, realized->per_script)
+      rfont = (MRealizedFont *) MPLIST_VAL (plist);
+      if (rfont->status < 0)
+       continue;
+      /* Check if this font can display all glyphs.  */
+      for (i = 0; i < *num; i++)
        {
-         MPLIST_DO (pl, MPLIST_PLIST (plist))
-           {
-             MPLIST_DO (p, MPLIST_PLIST (pl))
-               if ((rfont = MPLIST_VAL (p)) && ! rfont->frame)
-                 free (rfont);
-             p = MPLIST_PLIST (pl);
-             M17N_OBJECT_UNREF (p);
-           }
-         pl = MPLIST_PLIST (plist);
-         M17N_OBJECT_UNREF (pl);
+         g[i].code = mfont__encode_char (rfont,
+                                         g[i].type == GLYPH_CHAR ? g[i].c
+                                         : ' ');
+         if (g[i].code == MCHAR_INVALID_CODE)
+           break;
        }
-      M17N_OBJECT_UNREF (realized->per_script);
-    }
-  if (realized->per_charset)
-    {
-      MPLIST_DO (plist, realized->per_charset)
+      if (i == *num)
        {
-         MPLIST_DO (pl, MPLIST_PLIST (plist))
-           if ((rfont = MPLIST_VAL (pl)) && ! rfont->frame)
-             free (rfont);
-         pl = MPLIST_PLIST (plist);
-         M17N_OBJECT_UNREF (pl);
+         if (rfont->status > 0
+             || mfont__open (rfont) == 0)
+           /* We found a font that can display all glyphs.  */
+           return rfont;
        }
-      M17N_OBJECT_UNREF (realized->per_charset);
     }
-  if (realized->fallback)
+
+  /* We couldn't find a font that can display all glyphs.  Find one
+     that can display at least the first glyph.  */
+  MPLIST_DO (plist, font_group)
     {
-      MPLIST_DO (plist, realized->fallback)
-       if ((rfont = MPLIST_VAL (plist)) && ! rfont->frame)
-         free (rfont);
-      M17N_OBJECT_UNREF (realized->fallback);
+      rfont = (MRealizedFont *) MPLIST_VAL (plist);
+      if (rfont->status < 0)
+       continue;
+      g->code = mfont__encode_char (rfont,
+                                   g->type == GLYPH_CHAR ? g->c : ' ');
+      if (g->code != MCHAR_INVALID_CODE)
+       {
+         if (rfont->status > 0
+             || mfont__open (rfont) == 0)
+           {
+             /* Ok, let's use this font.  Check how many more
+                characters it supports.  */
+             int i;
+
+             for (i = 1; i < *num; i++)
+               {
+                 g[i].code = mfont__encode_char (rfont,
+                                                 g[i].type == GLYPH_CHAR
+                                                 ? g[i].c : ' ');
+                 if (g[i].code == MCHAR_INVALID_CODE)
+                   break;
+               }
+             *num = i;
+             return rfont;
+           }
+       }
     }
 
-  free (realized);
+  return NULL;
 }
 
-
 MRealizedFont *
 mfont__lookup_fontset (MRealizedFontset *realized, MGlyph *g, int *num,
                       MSymbol script, MSymbol language, MSymbol charset,
                       int size)
 {
-  MFrame *frame = realized->frame;
   MCharset *preferred_charset = (charset == Mnil ? NULL : MCHARSET (charset));
-  MPlist *per_charset, *per_script, *per_lang, *font_group;
-  MPlist *font_groups[256], *plist;
-  int n_font_group = 0;
-  MRealizedFont *first = NULL, *rfont;
-  int first_len;
-  int i;
+  MPlist *per_charset, *per_script, *per_lang;
+  MPlist *plist;
+  MRealizedFont *rfont = NULL;
+
+  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;
+      && (per_charset = mplist_get (realized->per_charset, charset)) != NULL
+      && (rfont = try_font_group (realized, per_charset, g, num, size)))
+    return rfont;
+
   if (script != Mnil
-      && ((per_script = mplist_find_by_key (realized->per_script, script))
-         != NULL))
+      && (per_script = mplist_get (realized->per_script, script)))
     {
-      /* 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 with LANGUAGE if LANGUAGE is not Mnil
+         (2) group for generic language
+         (3) group not matching with LANGUAGE  */
       if (language == Mnil)
        language = Mt;
-      for (i = 0; i < 2; i++)
-       {
-         MPLIST_DO (per_lang, MPLIST_PLIST (per_script))
-           if ((MPLIST_KEY (per_lang) == language) != i)
-             font_groups[n_font_group++] = MPLIST_PLIST (per_lang);
-       }
-    }
-  font_groups[n_font_group++] = realized->fallback;
-
-  if (n_font_group == 1)
-    {
-      /* As we only have a fallback font group, try all the other
-        fonts too.  */
-      MPLIST_DO (per_script, realized->per_script)
-       MPLIST_DO (per_lang, MPLIST_PLIST (per_script))
-         font_groups[n_font_group++] = MPLIST_PLIST (per_lang);
-      MPLIST_DO (per_charset, realized->per_charset)
-       font_groups[n_font_group++] = MPLIST_PLIST (per_charset);
-    }
-
-  for (i = 0; i < n_font_group; i++)
-    {
-      int j;
-      
-      if (MPLIST_PLIST_P (font_groups[i]))
-       realize_font_group (frame, &realized->spec, font_groups[i], size);
+      if ((per_lang = mplist_get (per_script, language))
+         && (rfont = try_font_group (realized, per_lang, g, num, size)))
+       return rfont;
 
-      MPLIST_DO (plist, font_groups[i])
-        {
-         rfont = (MRealizedFont *) MPLIST_VAL (plist);
-         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++)
+      if (language == Mt)
        {
-         g[j].code = mfont__encode_char (rfont, g[j].c);
-         if (g[j].code == MCHAR_INVALID_CODE)
-           break;
+         /* Try the above (3) */
+         MPLIST_DO (plist, per_script)
+           if (MPLIST_KEY (plist) != language
+               && (rfont = try_font_group (realized, MPLIST_PLIST (plist),
+                                           g, num, size)))
+             return rfont;
        }
-      if (! first)
-       first = rfont, first_len = j;
-      if (j == *num)
-       /* We found a font that can display all requested
-          characters.  */
-       break;
-
-      MPLIST_DO (plist, MPLIST_NEXT (plist))
+      else
        {
-         rfont = (MRealizedFont *) MPLIST_VAL (plist);
-         for (j = 0; j < *num; j++)
-           {
-             g[j].code = mfont__encode_char (rfont, g[j].c);
-             if (g[j].code == MCHAR_INVALID_CODE)
-               break;
-           }
-         if (j == *num)
-           break;
+         /* At first try the above (2) */
+         if ((per_lang = mplist_get (per_script, Mt))
+             && (rfont = try_font_group (realized, per_lang, g, num, size)))
+           return rfont;
+
+         /* Then try the above (3) */
+         MPLIST_DO (plist, per_script)
+           if (MPLIST_KEY (plist) != language
+               && MPLIST_KEY (plist) != Mt
+               && (rfont = try_font_group (realized, MPLIST_PLIST (plist),
+                                           g, num, size)))
+             return rfont;
        }
-      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)
+  if (language != Mnil)
+    /* Find a font group for this language from all scripts.  */
+    MPLIST_DO (plist, realized->per_script)
+      if ((per_lang = mplist_get (MPLIST_PLIST (plist), language))
+         && (rfont = try_font_group (realized, per_lang, g, num, size)))
+       return rfont;
+
+  /* Try fallback fonts.  */
+  if ((rfont = try_font_group (realized, realized->fallback, g, num, size)))
+    return rfont;
+
+  /* At last try all fonts.  */
+  MPLIST_DO (per_script, realized->per_script)
     {
-      MPLIST_VAL (font_group) = NULL;
-      return NULL;
+      MPLIST_DO (per_lang, MPLIST_PLIST (per_script))
+       if ((rfont = try_font_group (realized, MPLIST_PLIST (per_lang),
+                                    g, num, size)))
+         return rfont;
     }
-  return rfont;
+  MPLIST_DO (per_charset, realized->per_charset)
+    if ((rfont = try_font_group (realized, MPLIST_PLIST (per_charset),
+                                g, num, size)))
+      return rfont;
+
+  return NULL;
 }
 
 /*** @} */
@@ -640,21 +711,26 @@ 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 ();
+             fontset->font_spec_list = mplist ();
+           }
+         mplist_put (fontset_list, sym, fontset);
+       }
     }
-  mplist_put (fontset_list, sym, fontset);
   M17N_OBJECT_REF (fontset);
   return fontset;
 }
@@ -704,6 +780,9 @@ mfontset_copy (MFontset *fontset, char *name)
   M17N_OBJECT (copy, free_fontset, MERROR_FONTSET);
   copy->name = sym;
 
+  if (fontset->mdb)
+    load_fontset_contents (fontset);
+
   if (fontset->per_script)
     {
       copy->per_script = mplist ();
@@ -839,14 +918,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 ();
@@ -883,9 +965,9 @@ mfontset_modify_entry (MFontset *fontset,
     layouter_name = Mt;
   for (i--; i >= 0; i--)
     {
-      if (how == -1)
+      if (how == 1)
        mplist_push (plist[i], layouter_name, font);
-      else if (how == 1)
+      else if (how == -1)
        mplist_add (plist[i], layouter_name, font);
       else
        {
@@ -894,9 +976,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 */