*** empty log message ***
[m17n/m17n-lib.git] / src / face.c
index 757c245..f5d2ebf 100644 (file)
@@ -1,5 +1,5 @@
 /* face.c -- face module.
-   Copyright (C) 2003, 2004
+   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
      National Institute of Advanced Industrial Science and Technology (AIST)
      Registration Number H15PRO112
 
@@ -17,7 +17,7 @@
 
    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., 59 Temple Place, Suite 330, Boston, MA
+   Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    02111-1307, USA.  */
 
 /***en
@@ -33,8 +33,8 @@
     #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle,
     #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg
 
-    "The face property that belongs to face F and whose key is @c xxx"
-    may be shortened to "the xxx property of F".
+    The notation "xxx property of F" means the face property that
+    belongs to face F and whose key is @c Mxxx.
 
     The M-text drawing functions first search an M-text for the text
     property whose key is the symbol #Mface, then draw the M-text
@@ -164,7 +164,7 @@ get_box_create (MFaceBoxProp *prop)
     FACE.  */
 
 static MRealizedFace *
-find_realized_face (MFrame *frame, MFace *face)
+find_realized_face (MFrame *frame, MFace *face, MFont *font)
 {
   MPlist *plist;
 
@@ -172,8 +172,11 @@ find_realized_face (MFrame *frame, MFace *face)
     {
       MRealizedFace *rface = MPLIST_VAL (plist);
 
-      if (memcmp (rface->face.property, face->property, sizeof face->property)
-         == 0)
+      if (memcmp (rface->face.property, face->property,
+                 sizeof face->property) == 0
+         && (rface->font
+             ? (font && ! memcmp (rface->font, font, sizeof (MFont)))
+             : ! font))
        return rface;
     }
   return NULL;
@@ -240,7 +243,7 @@ serialize_face (void *val)
     MSymbol *key;
     MSymbol *type;
     MPlist *(*func) (MPlist *plist, void *val);
-  } serializer[MFACE_PROPERTY_MAX]
+  } serializer[MFACE_RATIO + 1]
       = { { &Mfoundry,         &Msymbol },
          { &Mfamily,           &Msymbol },
          { &Mweight,           &Msymbol },
@@ -254,11 +257,9 @@ serialize_face (void *val)
          { &Mhline,            NULL },
          { &Mbox,              NULL },
          { &Mvideomode,        &Msymbol },
-         { NULL,               NULL}, /* MFACE_HOOK_FUNC */
-         { NULL,               NULL}, /* MFACE_HOOK_ARG */
          { &Mratio,            &Minteger } };
   
-  for (i = 0; i < MFACE_PROPERTY_MAX; i++)
+  for (i = 0; i <= MFACE_RATIO; i++)
     if (face->property[i] && serializer[i].key)
       {
        pl = mplist_add (pl, Msymbol, *serializer[i].key);
@@ -360,7 +361,7 @@ deserialize_face (MPlist *plist)
       plist = MPLIST_NEXT (plist);
       if (MPLIST_TAIL_P (plist))
        break;
-      if (index < 0 || index >= MFACE_PROPERTY_MAX)
+      if (index < 0 || index > MFACE_RATIO)
        continue;
       if (key == Mfoundry || key == Mfamily || key == Mweight || key == Mstyle
          || key == Mstretch || key == Madstyle
@@ -414,10 +415,12 @@ mface__init ()
   MFaceHLineProp *hline;
   MFaceBoxProp *box;
 
-  face_table.count = 0;
+  M17N_OBJECT_ADD_ARRAY (face_table, "Face");
   Mface = msymbol_as_managing_key ("face");
-  msymbol_put (Mface, Mtext_prop_serializer, (void *) serialize_face);
-  msymbol_put (Mface, Mtext_prop_deserializer, (void *) deserialize_face);
+  msymbol_put_func (Mface, Mtext_prop_serializer,
+                   M17N_FUNC (serialize_face));
+  msymbol_put_func (Mface, Mtext_prop_deserializer,
+                   M17N_FUNC (deserialize_face));
 
   Mforeground = msymbol ("foreground");
   Mbackground = msymbol ("background");
@@ -439,7 +442,7 @@ mface__init ()
       MSymbol *key;
       /* Index (enum face_property) of the face property. */
       int index;
-    } mface_prop_data[MFACE_PROPERTY_MAX] =
+    } mface_prop_data[MFACE_HOOK_ARG + 1] =
        { { &Mfoundry,          MFACE_FOUNDRY },
          { &Mfamily,           MFACE_FAMILY },
          { &Mweight,           MFACE_WEIGHT },
@@ -453,10 +456,8 @@ mface__init ()
          { &Mhline,            MFACE_HLINE },
          { &Mbox,              MFACE_BOX },
          { &Mvideomode,        MFACE_VIDEOMODE },
-         { &Mhook_func,        MFACE_HOOK_FUNC },
-         { &Mhook_arg,         MFACE_HOOK_ARG },
          { &Mratio,            MFACE_RATIO },
-       };
+         { &Mhook_arg,         MFACE_HOOK_ARG } };
 
     for (i = 0; i < MFACE_PROPERTY_MAX; i++)
       /* We add one to distinguish it from no-property.  */
@@ -485,7 +486,7 @@ mface__init ()
   mface__default->property[MFACE_HLINE] = hline;
   mface__default->property[MFACE_BOX] = box;
   mface__default->property[MFACE_VIDEOMODE] = Mnormal;
-  mface__default->property[MFACE_HOOK_FUNC] = (void *) noop_hook;
+  mface__default->hook = noop_hook;
 
   mface_normal_video = mface ();
   mface_normal_video->property[MFACE_VIDEOMODE] = (void *) Mnormal;
@@ -589,27 +590,31 @@ mface__fini ()
   M17N_OBJECT_UNREF (box_prop_list);
 
   free (work_gstring.glyphs);
-
-  mdebug__report_object ("Face", &face_table);
 }
 
-/** Return a realized face for ASCII characters from NUM number of
-    base faces pointed by FACES on the frame FRAME.  */
+/** Return a face realized from NUM number of base faces pointed by
+    FACES on the frame FRAME.  If SIZE is nonzero, it specifies the
+    maximum font size.  */
 
 MRealizedFace *
-mface__realize (MFrame *frame, MFace **faces, int num, int size)
+mface__realize (MFrame *frame, MFace **faces, int num, int size, MFont *font)
 {
   MRealizedFace *rface;
   MRealizedFont *rfont;
   MFace merged_face = *(frame->face);
-  void **props;
   int i, j;
-  MGlyph g;
   MFaceHookFunc func;
+  MFont spec;
 
-  if (num == 0 && frame->rface)
+  if (num == 0 && frame->rface && ! font)
     return frame->rface;
 
+  if (! mplist_find_by_value (frame->face->frame_list, frame))
+    mplist_push (frame->face->frame_list, Mt, frame);
+  for (i = 0; i < num; i++)
+    if (! mplist_find_by_value (faces[i]->frame_list, frame))
+      mplist_push (faces[i]->frame_list, Mt, frame);
+
   for (i = 0; i < MFACE_PROPERTY_MAX; i++)
     for (j = num - 1; j >= 0; j--)
       if (faces[j]->property[i])
@@ -618,52 +623,142 @@ mface__realize (MFrame *frame, MFace **faces, int num, int size)
          break;
        }
 
-  if (! mplist_find_by_value (frame->face->frame_list, frame))
-    mplist_push (frame->face->frame_list, Mt, frame);
-  for (i = 0; i < num; i++)
-    if (! mplist_find_by_value (faces[i]->frame_list, frame))
-      mplist_push (faces[i]->frame_list, Mt, frame);
+  if (font)
+    {
+      if (font->type != MFONT_TYPE_REALIZED)
+       font = mfont_copy (font);
+      for (i = 0; i <= MFACE_ADSTYLE; i++)
+       if (font->property[i])
+         merged_face.property[i] = FONT_PROPERTY (font, i);
+      if (font->size)
+       {
+         int font_size;
 
-  if (merged_face.property[MFACE_RATIO])
+         if (font->size < 0)
+           font->size = ((double) (- font->size)) * frame->dpi / 72.27 + 0.5;
+         font_size = font->size;
+         merged_face.property[MFACE_SIZE] = (void *) font_size;
+         merged_face.property[MFACE_RATIO] = (void *) 0;
+       }
+    }
+
+  if (! font || ! font->size)
     {
-      int font_size = (int) merged_face.property[MFACE_SIZE];
+      double font_size = (int) merged_face.property[MFACE_SIZE];
+      int ifont_size;
 
-      font_size *= (int) merged_face.property[MFACE_RATIO];
-      font_size /= 100;
-      merged_face.property[MFACE_SIZE] = (void *) font_size;
-      merged_face.property[MFACE_RATIO] = 0;
+      if (font_size < 0)
+       font_size = - font_size * frame->dpi / 72.27;
+      if (merged_face.property[MFACE_RATIO]
+         && (int) merged_face.property[MFACE_RATIO] != 100)
+       {
+         font_size *= (int) merged_face.property[MFACE_RATIO];
+         font_size /= 100;
+       }
+      ifont_size = font_size + 0.5;
+      merged_face.property[MFACE_SIZE] = (void *) ifont_size;
+      merged_face.property[MFACE_RATIO] = (void *) 0;
     }
 
-  rface = find_realized_face (frame, &merged_face);
+  merged_face.property[MFACE_FOUNDRY] = Mnil;
+  rface = find_realized_face (frame, &merged_face, font);
   if (rface)
-    return rface;
+    {
+      if (font && font->type != MFONT_TYPE_REALIZED)
+       free (font);
+      return rface;
+    }
 
   MSTRUCT_CALLOC (rface, MERROR_FACE);
   mplist_push (frame->realized_face_list, Mt, rface);
   rface->frame = frame;
   rface->face = merged_face;
-  props = rface->face.property;
-  rface->rfontset = mfont__realize_fontset (frame,
-                                           (MFontset *) props[MFACE_FONTSET],
-                                           &merged_face);
-  g.type = GLYPH_SPACE;
-  g.c = ' ';
-  num = 1;
-  rfont = mfont__lookup_fontset (rface->rfontset, &g, &num,
-                                Mlatin, Mnil, Mnil, size);
+  rface->font = font;
+
+  if (font)
+    {
+      if (font->type == MFONT_TYPE_SPEC)
+       rfont = (MRealizedFont *) mfont_find (frame, font, NULL, 0);
+      else if (font->type == MFONT_TYPE_OBJECT)
+       {
+         MFONT_INIT (&spec);
+         spec.size = (int) merged_face.property[MFONT_SIZE];
+         if (font->property[MFONT_REGISTRY])
+           spec.property[MFONT_REGISTRY] = font->property[MFONT_REGISTRY];
+         else
+           mfont_put_prop (&spec, Mregistry,
+                           (font->source == MFONT_SOURCE_X
+                            ? Miso8859_1 : Municode_bmp));
+         rfont = mfont__open (frame, font, &spec);
+       }
+      else
+       rfont = (MRealizedFont *) font;
+    }
+  else
+    {
+      MFontset *fontset = (MFontset *) merged_face.property[MFACE_FONTSET];
+
+      rface->rfontset = mfont__realize_fontset (frame, fontset, &merged_face,
+                                               font);
+      rfont = NULL;
+      mfont__set_spec_from_face (&spec, &merged_face);
+      mfont_put_prop (&spec, Mregistry, Municode_bmp);
+      spec.source = MFONT_SOURCE_FT;
+      font = mfont__select (frame, &spec, 0);
+      if (font)
+       rfont = mfont__open (frame, font, &spec);
+      if (! rfont)
+       {
+         mfont_put_prop (&spec, Mregistry, Miso8859_1);
+         spec.source = MFONT_SOURCE_X;
+         font = mfont__select (frame, &spec, 0);
+         if (font)
+           rfont = mfont__open (frame, font, &spec);
+       }
+      if (! rfont)
+       {
+         num = 0;
+         rfont = mfont__lookup_fontset (rface->rfontset, NULL, &num,
+                                        Mlatin, Mnil, Mnil, size, 0);
+       }
+    }
+
   if (rfont)
     {
       rface->rfont = rfont;
-      g.otf_encoded = 0;
-      work_gstring.frame = frame;
-      work_gstring.glyphs[0] = g;
+      rface->layouter = rfont->layouter;
+      rfont->layouter = Mnil;
       work_gstring.glyphs[0].rface = rface;
-      work_gstring.glyphs[1].code = MCHAR_INVALID_CODE;
-      work_gstring.glyphs[1].rface = rface;
-      mfont__get_metric (&work_gstring, 0, 2);
-      rface->space_width = work_gstring.glyphs[0].width;
-      rface->ascent = work_gstring.glyphs[1].ascent;
-      rface->descent = work_gstring.glyphs[1].descent;
+      work_gstring.glyphs[0].g.code = MCHAR_INVALID_CODE;
+      work_gstring.glyphs[0].g.measured = 0;
+      mfont__get_metric (&work_gstring, 0, 1);
+      rface->ascent = work_gstring.glyphs[0].g.ascent;
+      rface->descent = work_gstring.glyphs[0].g.descent;
+      work_gstring.glyphs[0].g.code
+       = mfont__encode_char (frame, (MFont *) rfont, NULL, ' ');
+      if (work_gstring.glyphs[0].g.code != MCHAR_INVALID_CODE)
+       {
+         work_gstring.glyphs[0].g.measured = 0;
+         mfont__get_metric (&work_gstring, 0, 1);
+         rface->space_width = work_gstring.glyphs[0].g.xadv;
+       }
+      else
+       rface->space_width = rfont->spec.size / 10;
+      if (rfont->average_width)
+       rface->average_width = rfont->average_width >> 6;
+      else
+       {
+         work_gstring.glyphs[0].g.code
+           = mfont__encode_char (frame, (MFont *) rfont, NULL, 'x');
+         if (work_gstring.glyphs[0].g.code != MCHAR_INVALID_CODE)
+           {
+             work_gstring.glyphs[0].g.measured = 0;
+             mfont__get_metric (&work_gstring, 0, 1);
+             rface->average_width = work_gstring.glyphs[0].g.xadv;
+           }
+         else
+           rface->average_width = rface->space_width;
+       }
     }
   else
     {
@@ -671,16 +766,16 @@ mface__realize (MFrame *frame, MFace **faces, int num, int size)
       rface->space_width = frame->space_width;
     }
 
-  rface->hline = (MFaceHLineProp *) props[MFACE_HLINE];
+  rface->hline = (MFaceHLineProp *) merged_face.property[MFACE_HLINE];
   if (rface->hline && rface->hline->width == 0)
     rface->hline = NULL;
-  rface->box = (MFaceBoxProp *) props[MFACE_BOX];
+  rface->box = (MFaceBoxProp *) merged_face.property[MFACE_BOX];
   if (rface->box && rface->box->width == 0)
     rface->box = NULL;
   rface->ascii_rface = rface;
   (*frame->driver->realize_face) (rface);
 
-  func = (MFaceHookFunc) rface->face.property[MFACE_HOOK_FUNC];
+  func = rface->face.hook;
   if (func && func != noop_hook)
     (func) (&(rface->face), rface->info, rface->face.property[MFACE_HOOK_ARG]);
 
@@ -704,13 +799,103 @@ MGlyph *
 mface__for_chars (MSymbol script, MSymbol language, MSymbol charset,
                  MGlyph *from_g, MGlyph *to_g, int size)
 {
-  MRealizedFont *rfont;
+  MRealizedFont *rfont = from_g->rface->rfont;
+  MSymbol layouter;
   int num = to_g - from_g;
+  int i;
+
+  if (from_g->rface->font)
+    {
+      MRealizedFace *rface = from_g->rface, *new;
+
+      if (! rfont)
+       rfont = mfontset__get_font (rface->frame,
+                                   rface->face.property[MFACE_FONTSET], 
+                                   script, language,
+                                   rface->font, NULL);
+      else if (script != Mlatin)
+       rfont = mfontset__get_font (rface->frame,
+                                   rface->face.property[MFACE_FONTSET],
+                                   script, language,
+                                   (MFont *) rfont, NULL);
+      if (! rfont)
+       {
+         for (; from_g < to_g && from_g->rface->font; from_g++)
+           from_g->g.code = MCHAR_INVALID_CODE;
+       }
+      else
+       {
+         if (rface->rfont == rfont && rfont->layouter == Mnil)
+           new = rface;
+         else
+           {
+             MSTRUCT_MALLOC (new, MERROR_FACE);
+             mplist_push (rface->non_ascii_list, Mt, new);
+             *new = *rface;
+             new->rfont = rfont;
+             new->layouter = rfont->layouter;
+             rfont->layouter = Mnil;
+             new->non_ascii_list = NULL;
+             new->ascent = rfont->ascent >> 6;
+             new->descent = rfont->descent >> 6;
+           } 
+         for (; from_g < to_g && from_g->rface->font; from_g++)
+           {
+             from_g->rface = new;
+             if (new->layouter)
+               {
+                 MFLT *flt = mflt_get (new->layouter);
+                 MCharTable *coverage;
+
+                 if (! flt
+                     || ((coverage = mflt_coverage (flt))
+                         && ! (from_g->g.code
+                               = (unsigned) mchartable_lookup (coverage,
+                                                               from_g->g.c))))
+                   {
+                     from_g->rface = rface;
+                     from_g->g.code = mfont__encode_char (rfont->frame, 
+                                                          (MFont *) rfont,
+                                                          NULL, from_g->g.c);
+                   }
+               }
+             else
+               from_g->g.code = mfont__encode_char (rfont->frame, 
+                                                    (MFont *) rfont,
+                                                    NULL, from_g->g.c);
+           }
+       }
+      return from_g;
+    }
+
+  if (rfont && script == Mlatin)
+    {
+      for (i = 0; i < num; i++)
+       {
+         unsigned code = mfont__encode_char (rfont->frame, (MFont *) rfont,
+                                             NULL, from_g[i].g.c);
+         if (code == MCHAR_INVALID_CODE)
+           break;
+         from_g[i].g.code = code;
+       }
+      if (i == num || from_g[i].rface->font)
+       return from_g + i;
+    }
 
   rfont = mfont__lookup_fontset (from_g->rface->rfontset, from_g, &num,
-                                script, language, charset, size);
-  if (! rfont)
-    num = 1;
+                                script, language, charset, size, 0);
+  if (rfont)
+    {
+      layouter = rfont->layouter;
+      rfont->layouter = Mnil;
+    }
+  else
+    {
+      from_g->g.code = MCHAR_INVALID_CODE;
+      num = 1;
+      rfont = NULL;
+      layouter = Mnil;
+    }
   
   to_g = from_g + num;
   while (from_g < to_g)
@@ -719,24 +904,31 @@ mface__for_chars (MSymbol script, MSymbol language, MSymbol charset,
       MRealizedFace *rface = from_g++->rface;
 
       while (from_g < to_g && rface == from_g->rface) from_g++;
-      if (rface->rfont != rfont)
+      if (rface->rfont != rfont
+         || rface->layouter != layouter)
        {
          MPlist *plist = mplist_find_by_value (rface->non_ascii_list, rfont);
-         MRealizedFace *new;
+         MRealizedFace *new = NULL;
 
-         if (plist)
-           new = MPLIST_VAL (plist);
-         else
+         while (plist)
+           {
+             new = MPLIST_VAL (plist);
+             if (new->layouter == layouter)
+               break;
+             plist = mplist_find_by_value (MPLIST_NEXT (plist), rfont);
+           }
+         if (! plist)
            {
              MSTRUCT_MALLOC (new, MERROR_FACE);
              mplist_push (rface->non_ascii_list, Mt, new);
              *new = *rface;
              new->rfont = rfont;
+             new->layouter = layouter;
              new->non_ascii_list = NULL;
              if (rfont)
                {
-                 new->ascent = rfont->ascent;
-                 new->descent = rfont->descent;
+                 new->ascent = rfont->ascent >> 6;
+                 new->descent = rfont->descent >> 6;
                }
            }
          while (g < from_g)
@@ -755,6 +947,8 @@ mface__free_realized (MRealizedFace *rface)
   MPLIST_DO (plist, rface->non_ascii_list)
     free (MPLIST_VAL (plist));
   M17N_OBJECT_UNREF (rface->non_ascii_list);
+  if (rface->font && rface->font->type != MFONT_TYPE_REALIZED)
+    free (rface->font);
   free (rface);
 }
 
@@ -762,8 +956,9 @@ void
 mface__update_frame_face (MFrame *frame)
 {
   frame->rface = NULL;
-  frame->rface = mface__realize (frame, NULL, 0, 0);
+  frame->rface = mface__realize (frame, NULL, 0, 0, NULL);
   frame->space_width = frame->rface->space_width;
+  frame->average_width = frame->rface->average_width;
   frame->ascent = frame->rface->ascent;
   frame->descent = frame->rface->descent;
 }
@@ -828,13 +1023,13 @@ MSymbol Mbackground;
     @brief Key of a face property specifying video mode.
 
     The variable #Mvideomode is used as a key of face property.  The
-    property value must be #Mnormal, #Mreverse, or #Mnil.
+    property value must be @b Mnormal, @b Mreverse, or #Mnil.
 
-    #Mnormal means that an M-text is drawn in normal video mode
+    @b Mnormal means that an M-text is drawn in normal video mode
     (i.e. the foreground is drawn by foreground color, the background
     is drawn by background color).
 
-    #Mreverse means that an M-text is drawn in reverse video mode
+    @b Mreverse means that an M-text is drawn in reverse video mode
     (i.e. the foreground is drawn by background color, the background
     is drawn by foreground color).
 
@@ -843,12 +1038,12 @@ MSymbol Mbackground;
     @brief ¥Ó¥Ç¥ª¥â¡¼¥É¤ò»ØÄꤹ¤ë¤¿¤á¤Î¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¡¼¤Î¥­¡¼.
 
     ÊÑ¿ô #Mvideomode ¤Ï¥Õ¥§¡¼¥¹¥×¥í¥Ñ¥Æ¥£¤Î¥­¡¼¤È¤·¤ÆÍѤ¤¤é¤ì¤ë¡£¥×¥í¥Ñ¥Æ¥£¤ÎÃͤϡ¢
-    #Mnormal, #Mreverse, #Mnil ¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
+    @b Mnormal, @b Mreverse, #Mnil ¤Î¤¤¤º¤ì¤«¤Ç¤Ê¤¯¤Æ¤Ï¤Ê¤é¤Ê¤¤¡£
 
-    #Mnormal ¤Î¾ì¹ç¤Ï¡¢M-text 
+    @b Mnormal ¤Î¾ì¹ç¤Ï¡¢M-text 
     ¤òɸ½à¤Î¥Ó¥Ç¥ª¥â¡¼¥É¡ÊÁ°·Ê¤òÁ°·Ê¿§¤Ç¡¢ÇطʤòÇØ·Ê¿§¤Ç¡Ë¤Çɽ¼¨¤¹¤ë¡£
 
-    #Mreverse ¤Î¾ì¹ç¤Ï¥ê¥Ð¡¼¥¹¥Ó¥Ç¥ª¥â¡¼¥É¤Ç¡ÊÁ°·Ê¤òÇØ·Ê¿§¤Ç¡¢ÇطʤòÁ°·Ê¿§¤Ç¡Ëɽ¼¨¤¹¤ë¡£
+    @b Mreverse ¤Î¾ì¹ç¤Ï¥ê¥Ð¡¼¥¹¥Ó¥Ç¥ª¥â¡¼¥É¤Ç¡ÊÁ°·Ê¤òÇØ·Ê¿§¤Ç¡¢ÇطʤòÁ°·Ê¿§¤Ç¡Ëɽ¼¨¤¹¤ë¡£
 
     #Mnil ¤Î¾ì¹ç¤Ï¥Ó¥Ç¥ª¥â¡¼¥É¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£
     */
@@ -981,7 +1176,6 @@ MSymbol Mhook_arg;
 /***en @name Variables: Possible values of #Mvideomode property of face */
 /***ja @name ÊÑ¿ô¡§  ¥Õ¥§¡¼¥¹¤Î #Mvideomode ¥×¥í¥Ñ¥Æ¥£¤Î²Äǽ¤ÊÃÍ */
 /*** @{ */
-/*=*/
 
 /***en
     See the documentation of the variable #Mvideomode.  */ 
@@ -1002,14 +1196,14 @@ MSymbol Mreverse;
     @brief Normal video face.
 
     The variable #mface_normal_video points to a face that has the
-    #Mvideomode property with value #Mnormal.  The other properties
+    #Mvideomode property with value @b Mnormal.  The other properties
     are not specified.  An M-text drawn with this face appear normal
     colors (i.e. the foreground is drawn by foreground color, and
     background is drawn by background color).  */
 /***ja
     @brief É¸½à¥Ó¥Ç¥ª¥Õ¥§¡¼¥¹.
 
-    ÊÑ¿ô #mface_normal_video ¤Ï #Mvideomode ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬ #Mnormal 
+    ÊÑ¿ô #mface_normal_video ¤Ï #Mvideomode ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬ @b Mnormal 
     ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¤ò»Ø¤¹¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£Â¾¤Î¥×¥í¥Ñ¥Æ¥£¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£
     ¤³¤Î¥Õ¥§¡¼¥¹¤Çɽ¼¨¤µ¤ì¤ëM-text 
     ¤Ïɸ½à¤Î¿§ (¤¹¤Ê¤ï¤ÁÁ°·Ê¤ÏÁ°·Ê¿§¡¢ÇطʤÏÇØ·Ê¿§¡Ë¤ÇÉÁ¤«¤ì¤ë¡£  */
@@ -1020,7 +1214,7 @@ MFace *mface_normal_video;
     @brief Reverse video face.
 
     The variable #mface_reverse_video points to a face that has the
-    #Mvideomode property with value #Mreverse.  The other properties
+    #Mvideomode property with value @b Mreverse.  The other properties
     are not specified.  An M-text drawn with this face appear in
     reversed colors (i.e. the foreground is drawn by background
     color, and background is drawn by foreground color).  */
@@ -1028,7 +1222,7 @@ MFace *mface_normal_video;
     @brief ¥ê¥Ð¡¼¥¹¥Ó¥Ç¥ª¥Õ¥§¡¼¥¹.
 
     ÊÑ¿ô #mface_reverse_video ¤Ï #Mvideomode ¥×¥í¥Ñ¥Æ¥£¤ÎÃͤ¬ 
-    #Mreverse ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¤ò»Ø¤¹¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£Â¾¤Î¥×¥í¥Ñ¥Æ¥£¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£
+    @b Mreverse ¤Ç¤¢¤ë¥Õ¥§¡¼¥¹¤ò»Ø¤¹¥Ý¥¤¥ó¥¿¤Ç¤¢¤ë¡£Â¾¤Î¥×¥í¥Ñ¥Æ¥£¤Ï»ØÄꤵ¤ì¤Ê¤¤¡£
     ¤³¤Î¥Õ¥§¡¼¥¹¤Çɽ¼¨¤µ¤ì¤ëM-text 
     ¤ÏÁ°·Ê¿§¤ÈÇØ·Ê¿§¤¬Æþ¤ìÂؤï¤Ã¤Æ (¤¹¤Ê¤ï¤ÁÁ°·Ê¤ÏÇØ·Ê¿§¡¢ÇطʤÏÁ°·Ê¿§¡ËÉÁ¤«¤ì¤ë¡£  */
 
@@ -1483,6 +1677,55 @@ mface_copy (MFace *face)
 
 /*=*/
 /***en
+    @brief Compare faces.
+
+    The mface_equal () function compares faces $FACE1 and $FACE2.
+
+    @return
+    If two faces have the same property values, return 1.
+    Otherwise return 0.  */
+
+int
+mface_equal (MFace *face1, MFace *face2)
+{
+  MFaceHLineProp *hline1, *hline2;
+  MFaceBoxProp *box1, *box2;
+  int i;
+
+  if (face1 == face2)
+    return 1;
+  if (memcmp (face1->property, face2->property, sizeof face1->property) == 0)
+    return 1;
+  for (i = MFACE_FOUNDRY; i <= MFACE_BACKGROUND; i++)
+    if (face1->property[i] != face2->property[i])
+      return 0;
+  for (i = MFACE_VIDEOMODE; i <= MFACE_RATIO; i++)
+    if (face1->property[i] != face2->property[i])
+      return 0;
+  hline1 = (MFaceHLineProp *) face1->property[MFACE_HLINE];
+  hline2 = (MFaceHLineProp *) face2->property[MFACE_HLINE];
+  if (hline1 != hline2)
+    {
+      if (! hline1 || ! hline2)
+       return 0;
+      if (memcmp (hline1, hline2, sizeof (MFaceHLineProp)) != 0)
+       return 0;
+    }
+  box1 = (MFaceBoxProp *) face1->property[MFACE_BOX];
+  box2 = (MFaceBoxProp *) face2->property[MFACE_BOX];
+  if (box1 != box2)
+    {
+      if (! box1 || ! box2)
+       return 0;
+      if (memcmp (box1, box2, sizeof (MFaceBoxProp)) != 0)
+       return 0;
+    }
+  return 1;
+}
+
+
+/*=*/
+/***en
     @brief Merge faces.
 
     The mface_merge () functions merges the properties of face $SRC
@@ -1586,7 +1829,7 @@ mface_from_font (MFont *font)
 
         #Mforeground, #Mbackground, #Mvideomode, #Mhline, #Mbox,
         #Mfoundry, #Mfamily, #Mweight, #Mstyle, #Mstretch, #Madstyle,
-        #Msize, #Mfontset, #Mratio, #Mhook_func, #Mhook_arg
+        #Msize, #Mfontset, #Mratio, #Mhook_arg
 
     @return 
     Ìá¤êÃͤη¿¤Ï $KEY ¤Ë°Í¸¤¹¤ë¡£¾åµ­¤Î¥­¡¼¤ÎÀâÌÀ¤ò»²¾È¤¹¤ë¤³¤È¡£
@@ -1595,7 +1838,7 @@ mface_from_font (MFont *font)
 
 /***
     @seealso
-    mface_put_prop ()
+    mface_put_prop (), mface_put_hook ()
 
     @errors
     @c MERROR_FACE  */
@@ -1606,13 +1849,37 @@ mface_get_prop (MFace *face, MSymbol key)
   int index = (int) msymbol_get (key, M_face_prop_index) - 1;
 
   if (index < 0)
-    MERROR (MERROR_FACE, NULL);
+    {
+      if (key == Mhook_func)
+       /* This unsafe code is for backward compatiblity.  */
+       return (void *) face->hook;
+      MERROR (MERROR_FACE, NULL);
+    }
   return face->property[index];
 }
 
 /*=*/
 
 /***en
+    @brief Get the hook function of a face.
+
+    The mface_get_hook () function returns the hook function of face
+    $FACE.  */
+
+/***ja
+    @brief ¥Õ¥§¡¼¥¹¤Î¥Õ¥Ã¥¯´Ø¿ô¤òÆÀ¤ë.
+
+    ´Ø¿ô mface_get_hook () ¤Ï¥Õ¥§¡¼¥¹ $FACE ¤Î¥Õ¥Ã¥¯´Ø¿ô¤òÊÖ¤¹¡£ */
+
+MFaceHookFunc
+mface_get_hook (MFace *face)
+{
+  return face->hook;
+}
+
+/*=*/
+
+/***en
     @brief Set a value of a face property.
 
     The mface_put_prop () function assigns $VAL to the property whose
@@ -1667,22 +1934,32 @@ mface_put_prop (MFace *face, MSymbol key, void *val)
   int index = (int) msymbol_get (key, M_face_prop_index) - 1;
   MPlist *plist;
 
-  if (index < 0)
-    MERROR (MERROR_FACE, -1);
-  if (key == Mfontset)
+  if (key == Mhook_func)
     {
-      if (face->property[index])
-       M17N_OBJECT_UNREF (face->property[index]);
-      M17N_OBJECT_REF (val);
+      /* This unsafe code is for backward compatiblity.  */
+      if (face->hook == (MFaceHookFunc) val)
+       return 0;
+      face->hook = (MFaceHookFunc) val;
     }
-  else if (key == Mhline)
-    val = get_hline_create (val);
-  else if (key == Mbox)
-    val = get_box_create (val);
+  else
+    {
+      if (index < 0)
+       MERROR (MERROR_FACE, -1);
+      if (key == Mfontset)
+       {
+         if (face->property[index])
+           M17N_OBJECT_UNREF (face->property[index]);
+         M17N_OBJECT_REF (val);
+       }
+      else if (key == Mhline)
+       val = get_hline_create (val);
+      else if (key == Mbox)
+       val = get_box_create (val);
 
-  if (face->property[index] == val)
-    return 0;
-  face->property[index] = val;
+      if (face->property[index] == val)
+       return 0;
+      face->property[index] = val;
+    }
 
   MPLIST_DO (plist, face->frame_list)
     {
@@ -1699,6 +1976,40 @@ mface_put_prop (MFace *face, MSymbol key, void *val)
 /*=*/
 
 /***en
+    @brief Set a hook function to a face.
+
+    The mface_set_hook () function sets the hook function of face
+    $FACE to $FUNC.  */
+
+/***ja
+    @brief ¥Õ¥§¡¼¥¹¤Î¥Õ¥Ã¥¯´Ø¿ô¤òÀßÄꤹ¤ë.
+
+    ´Ø¿ô mface_set_hook () ¤Ï¡¢¥Õ¥§¡¼¥¹ $FACE ¤Î¥Õ¥Ã¥¯´Ø¿ô¤ò$FUNC ¤ËÀß
+    Äꤹ¤ë¡£  */
+
+int
+mface_put_hook (MFace *face, MFaceHookFunc func)
+{
+  if (face->hook != func)
+    {
+      MPlist *plist;
+      face->hook = func;
+
+      MPLIST_DO (plist, face->frame_list)
+       {
+         MFrame *frame = MPLIST_VAL (plist);
+
+         frame->tick++;
+         if (face == frame->face)
+           mface__update_frame_face (frame);
+       }
+    }
+  return 0;
+}
+
+/*=*/
+
+/***en
     @brief Update a face.
 
     The mface_update () function update face $FACE on frame $FRAME by
@@ -1713,7 +2024,7 @@ mface_put_prop (MFace *face, MSymbol key, void *val)
 void
 mface_update (MFrame *frame, MFace *face)
 {
-  MFaceHookFunc func = (MFaceHookFunc) face->property[MFACE_HOOK_FUNC];
+  MFaceHookFunc func = face->hook;
   MPlist *rface_list;
   MRealizedFace *rface;
 
@@ -1722,7 +2033,7 @@ mface_update (MFrame *frame, MFace *face)
       MPLIST_DO (rface_list, frame->realized_face_list)
        {
          rface = MPLIST_VAL (rface_list);
-         if ((MFaceHookFunc) rface->face.property[MFACE_HOOK_FUNC] == func)
+         if (rface->face.hook == func)
            (func) (&(rface->face), rface->face.property[MFACE_HOOK_ARG],
                    rface->info);
        }