merge FLT
[m17n/m17n-lib.git] / src / face.c
index 36ce285..983228c 100644 (file)
@@ -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
@@ -243,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 },
@@ -257,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);
@@ -363,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
@@ -419,8 +417,10 @@ mface__init ()
 
   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");
@@ -442,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 },
@@ -456,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.  */
@@ -488,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;
@@ -604,14 +602,19 @@ 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;
   MFaceHookFunc func;
-  MFont adjusted;
+  MFont spec;
 
   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])
@@ -619,34 +622,27 @@ mface__realize (MFrame *frame, MFace **faces, int num, int size, MFont *font)
          merged_face.property[i] = faces[j]->property[i];
          break;
        }
+
   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)
        {
-         if (font->size < 0)
-           {
-             double pt = - font->size;
+         int font_size;
 
-             adjusted = *font;
-             adjusted.size = pt * frame->dpi / 72.27 + 0.5;
-             font = &adjusted;
-           }
-         merged_face.property[MFACE_SIZE] = (void *) font->size;
+         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 (! 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 ((int) merged_face.property[MFACE_SIZE] < 0
-      || (merged_face.property[MFACE_RATIO]
-         && (int) merged_face.property[MFACE_RATIO] != 100))
+  if (! font || ! font->size)
     {
       double font_size = (int) merged_face.property[MFACE_SIZE];
       int ifont_size;
@@ -658,70 +654,111 @@ mface__realize (MFrame *frame, MFace **faces, int num, int size, MFont *font)
        {
          font_size *= (int) merged_face.property[MFACE_RATIO];
          font_size /= 100;
-         merged_face.property[MFACE_RATIO] = 0;
        }
       ifont_size = font_size + 0.5;
       merged_face.property[MFACE_SIZE] = (void *) ifont_size;
+      merged_face.property[MFACE_RATIO] = (void *) 0;
     }
 
   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;
+  rface->font = font;
+
   if (font)
     {
-      rface->font = mfont ();
-      *rface->font = *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;
     }
-  props = merged_face.property;
-  rface->rfontset = mfont__realize_fontset (frame,
-                                           (MFontset *) props[MFACE_FONTSET],
-                                           &merged_face, font);
-  num = 0;
-  rfont = NULL;
-  if (! font)
+  else
     {
-      MFont spec;
+      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, Miso8859_1);
-      spec.source = MFONT_SOURCE_X;
+      mfont_put_prop (&spec, Mregistry, Municode_bmp);
+      spec.source = MFONT_SOURCE_FT;
       font = mfont__select (frame, &spec, 0);
-      if (! font)
+      if (font)
+       rfont = mfont__open (frame, font, &spec);
+      if (! rfont)
        {
-         mfont_put_prop (&spec, Mregistry, Municode_bmp);
-         spec.source = MFONT_SOURCE_FT;
+         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 (font)
-       rfont = mfont__open (frame, font, &spec);
     }
-  if (! rfont)
-    rfont = mfont__lookup_fontset (rface->rfontset, NULL, &num,
-                                  Mlatin, Mnil, Mnil, size, 0);
+
   if (rfont)
     {
       rface->rfont = rfont;
       rface->layouter = rfont->layouter;
+      rfont->layouter = Mnil;
       work_gstring.glyphs[0].rface = rface;
-      work_gstring.glyphs[0].code = MCHAR_INVALID_CODE;
+      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].ascent;
-      rface->descent = work_gstring.glyphs[0].descent;
-      work_gstring.glyphs[0].code
+      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].code != MCHAR_INVALID_CODE)
+      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].width;
+         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
     {
@@ -729,16 +766,16 @@ mface__realize (MFrame *frame, MFace **faces, int num, int size, MFont *font)
       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]);
 
@@ -762,33 +799,99 @@ 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;
 
-  rfont = from_g->rface->rfont;
-  if (script == Mlatin)
+  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].c);
+                                             NULL, from_g[i].g.c);
          if (code == MCHAR_INVALID_CODE)
            break;
-         from_g[i].code = code;
+         from_g[i].g.code = code;
        }
-      if (i == num)
-       return to_g;
+      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, 0);
   if (rfont)
-    layouter = rfont->layouter;
+    {
+      layouter = rfont->layouter;
+      rfont->layouter = Mnil;
+    }
   else
     {
-      from_g->code = MCHAR_INVALID_CODE;
+      from_g->g.code = MCHAR_INVALID_CODE;
       num = 1;
       rfont = NULL;
       layouter = Mnil;
@@ -824,8 +927,8 @@ mface__for_chars (MSymbol script, MSymbol language, MSymbol charset,
              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)
@@ -844,7 +947,7 @@ 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)
+  if (rface->font && rface->font->type != MFONT_TYPE_REALIZED)
     free (rface->font);
   free (rface);
 }
@@ -855,6 +958,7 @@ mface__update_frame_face (MFrame *frame)
   frame->rface = NULL;
   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;
 }
@@ -1725,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 ¤Ë°Í¸¤¹¤ë¡£¾åµ­¤Î¥­¡¼¤ÎÀâÌÀ¤ò»²¾È¤¹¤ë¤³¤È¡£
@@ -1734,7 +1838,7 @@ mface_from_font (MFont *font)
 
 /***
     @seealso
-    mface_put_prop ()
+    mface_put_prop (), mface_put_hook ()
 
     @errors
     @c MERROR_FACE  */
@@ -1745,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
@@ -1806,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 (*(void **) &face->hook == val)
+       return 0;
+      *(void **) &face->hook = 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)
     {
@@ -1838,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
@@ -1852,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;
 
@@ -1861,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);
        }