*** empty log message ***
[m17n/m17n-lib.git] / src / draw.c
index c4d3c4a..6155d4a 100644 (file)
@@ -1,5 +1,5 @@
 /* draw.c -- drawing module.
-   Copyright (C) 2003, 2004
+   Copyright (C) 2003, 2004, 2005, 2006
      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
@@ -80,7 +80,7 @@
 static MSymbol M_glyph_string;
 
 /* Special scripts */
-static MSymbol Minherited;
+static MSymbol Mcommon;
 /* Special categories */
 static MSymbol McatCc, McatCf;
 
@@ -97,6 +97,7 @@ static MSymbol MbidiRLE;
 static MSymbol MbidiRLO;
 static MSymbol MbidiBN;
 static MSymbol MbidiS;
+static MSymbol MbidiNSM;
 
 static void
 visual_order (MGlyphString *gstring)
@@ -116,6 +117,8 @@ visual_order (MGlyphString *gstring)
   int *logical = alloca (sizeof (int) * len);
   int *indices;
   char *levels = alloca (len);
+
+  memset (levels, 0, sizeof (int) * len);
 #endif /* not HAVE_FRIBIDI */
 
   for (g = MGLYPH (1), i = 0; g->type != GLYPH_ANCHOR; g++, i++)
@@ -136,6 +139,10 @@ visual_order (MGlyphString *gstring)
              levels[i] = 1;
 #endif /* not HAVE_FRIBIDI */
            }
+#ifndef HAVE_FRIBIDI
+         else if (bidi == MbidiNSM && i > 0 && levels[i - 1])
+           levels[i] = 1;          
+#endif /* not HAVE_FRIBIDI */
        }
       logical[i] = g->c;
     }
@@ -183,7 +190,8 @@ visual_order (MGlyphString *gstring)
          /* Mirrored.  */
          g->c = visual[j];
          if (g->rface->rfont)
-           g->code = mfont__encode_char (g->rface->rfont, g->c);
+           g->code = mfont__encode_char (NULL, (MFont *) g->rface->rfont,
+                                         NULL, g->c);
        }
 #endif /* HAVE_FRIBIDI */
       g->bidi_level = levels[i];
@@ -236,7 +244,7 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
                      MGlyphString *gstring)
 {
   MRealizedFace *default_rface = frame->rface;
-  int stop, face_change, language_change, charset_change;
+  int stop, face_change, language_change, charset_change, font_change;
   MGlyph g_tmp, *g, *last_g;
   int pos;
   MSymbol language = Mnil, script = Mnil, charset = Mnil;
@@ -257,7 +265,7 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
   g_tmp.type = GLYPH_ANCHOR;
   g_tmp.pos = g_tmp.to = from;
   APPEND_GLYPH (gstring, g_tmp);
-  stop = face_change = pos = from;
+  stop = face_change = font_change = pos = from;
   while (1)
     {
       int c;
@@ -267,24 +275,39 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
        {
          if (pos == to)
            break;
-         if (pos == face_change)
+         if (pos < mtext_nchars (mt))
            {
-             if (pos < mtext_nchars (mt))
-               {
-                 MFace *faces[64];
-                 int num = mtext_get_prop_values (mt, pos, Mface,
-                                                  (void **) faces, 64);
+             MFont *font = rface->font;
+             MFace *faces[64];
+             int num;
 
+             if (pos == font_change)
+               {
+                 font = mtext_get_prop (mt, pos, Mfont);
+                 mtext_prop_range (mt, Mfont, pos, NULL, &font_change, 0);
+                 if (font_change == mtext_nchars (mt))
+                   font_change++;
+               }
+             if (pos == face_change)
+               {
+                 num = mtext_get_prop_values (mt, pos, Mface,
+                                              (void **) faces, 64);
                  mtext_prop_range (mt, Mface, pos, NULL, &face_change, 1);
                  if (face_change == mtext_nchars (mt))
                    face_change++;
-                 rface = (num > 0 ? mface__realize (frame, faces, num, size)
-                          : default_rface);
                }
              else
-               rface = default_rface;
+               {
+                 faces[0] = &rface->face;
+                 num = 1;
+               }
+             rface = mface__realize (frame, faces, num, size, font);
            }
+         else
+           rface = default_rface;
          stop = to;
+         if (stop > font_change)
+           stop = font_change;         
          if (stop > face_change)
            stop = face_change;         
        }
@@ -344,11 +367,11 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
       else
        {
          this_script = (MSymbol) mchar_get_prop (c, Mscript);
-         if (this_script == Minherited || this_script == Mnil)
+         if (this_script == Minherited || this_script == Mcommon)
            this_script = script;
-         if (this_script == Mnil)
+         if (this_script == Mcommon)
            this_script = non_latin_script;
-         if (this_script == Mnil)
+         if (this_script == Mcommon)
            {
              /* Search forward for a character that explicitly
                 specifies a non-latin script.  */
@@ -357,7 +380,7 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
 
              for (g1 = g + 1; g1->type != GLYPH_ANCHOR; g1++)
                if (g1->c >= 0x100
-                   && (sym = mchar_get_prop (g1->c, Mscript)) != Mnil
+                   && (sym = mchar_get_prop (g1->c, Mscript)) != Mcommon
                    && sym != Minherited)
                  {
                    this_script = sym;
@@ -411,7 +434,7 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
        {
          int start = i++;
 
-         if (this->rface->rfont->layouter != Mnil)
+         if (this->rface->layouter != Mnil)
            {
              MGlyph *prev;
              unsigned code;
@@ -419,16 +442,25 @@ compose_glyph_string (MFrame *frame, MText *mt, int from, int to,
              for (prev = MGLYPH (start - 1);
                   (prev->type == GLYPH_CHAR
                    && prev->category == GLYPH_CATEGORY_FORMATTER
-                   && (code = mfont__encode_char (this->rface->rfont, prev->c)
+                   && (code = mfont__encode_char (NULL,
+                                                  (MFont *) this->rface->rfont,
+                                                  NULL, prev->c)
                        != MCHAR_INVALID_CODE));
                   start--, prev--)
-               prev->code = code;
+               if (prev->rface->rfont != this->rface->rfont)
+                 {
+                   prev->rface->rfont = this->rface->rfont;
+                   prev->code = code;
+                 }
 
              for (g++;
                   (g->type == GLYPH_CHAR
+                   && g->rface->layouter == this->rface->layouter
                    && (g->rface->rfont == this->rface->rfont
                        || (g->category == GLYPH_CATEGORY_FORMATTER
-                           && ((code = mfont__encode_char (this->rface->rfont,
+                           && ((code = mfont__encode_char (NULL,
+                                                           (MFont *) this->rface->rfont,
+                                                           NULL,
                                                            g->c))
                                != MCHAR_INVALID_CODE))));
                   i++, g++)
@@ -556,7 +588,7 @@ layout_glyphs (MFrame *frame, MGlyphString *gstring, int from, int to,
     {
       MGlyph *base = g++;
       MRealizedFont *rfont = base->rface->rfont;
-      int size = rfont->font.property[MFONT_SIZE];
+      int size = rfont->spec.size;
       int width, lbearing, rbearing;
 
       if (g == last_g || ! g->combining_code)
@@ -580,7 +612,10 @@ layout_glyphs (MFrame *frame, MGlyphString *gstring, int from, int to,
          if (base->left_padding && base->lbearing < 0)
            {
              base->xoff = - base->lbearing;
-             base->width += base->xoff;
+             if (base->rbearing < 0)
+               base->width = base->rbearing - base->lbearing;
+             else
+               base->width += base->xoff;
              base->rbearing += base->xoff;
              base->lbearing = 0;
            }
@@ -588,9 +623,8 @@ layout_glyphs (MFrame *frame, MGlyphString *gstring, int from, int to,
            {
              base->width = base->rbearing;
            }
-         lbearing = (base->xoff + base->lbearing < 0
-                     ? base->xoff + base->lbearing : 0);
-         rbearing = base->xoff + base->rbearing;
+         lbearing = base->lbearing;
+         rbearing = base->rbearing;
        }
       else
        {
@@ -627,7 +661,7 @@ layout_glyphs (MFrame *frame, MGlyphString *gstring, int from, int to,
                                                   (combining_code));
 
                  rfont = g->rface->rfont;
-                 size = rfont->font.property[MFONT_SIZE];
+                 size = rfont->spec.size;
                  off_x = (size * (COMBINING_CODE_OFF_X (combining_code) - 128)
                           / 1000);
                  off_y = (size * (COMBINING_CODE_OFF_Y (combining_code) - 128)
@@ -692,6 +726,12 @@ layout_glyphs (MFrame *frame, MGlyphString *gstring, int from, int to,
              base[i].pos = begin;
              base[i].to = end;
            }
+         if (base->left_padding && lbearing < 0)
+           {
+             base->xoff -= lbearing;
+             base->width -= lbearing;
+             lbearing = 0;
+           }
        }
 
       g_physical_ascent = MAX (g_physical_ascent, base->ascent);
@@ -1072,6 +1112,7 @@ draw_background (MFrame *frame, MDrawWindow win, int x, int y,
     }
 
   *from_idx = *to_idx = 0;
+  *to_x = x;
   while (g->type != GLYPH_ANCHOR)
     {
       if (g->pos >= from && g->pos < to)
@@ -1240,7 +1281,8 @@ render_glyphs (MFrame *frame, MDrawWindow win, int x, int y, int width,
                gend--;
            }
          if (g != gend)
-           while (gend[-1].to == gend->to) gend++;
+           while (gend->type != GLYPH_ANCHOR && gend[-1].to == gend->to)
+             gend++;
        }
     }
 
@@ -1351,23 +1393,30 @@ find_overlapping_glyphs (MGlyphString *gstring, int *left, int *right,
 
 
 static int
-gstring_width (MGlyphString *gstring, int from, int to, int *rbearing)
+gstring_width (MGlyphString *gstring, int from, int to,
+              int *lbearing, int *rbearing)
 {
   MGlyph *g;
   int width;
 
   if (from <= gstring->from && to >= gstring->to)
     {
+      if (lbearing)
+       *lbearing = gstring->lbearing;
       if (rbearing)
        *rbearing = gstring->rbearing;
       return gstring->width;
     }
 
+  if (lbearing)
+    *lbearing = 0;
   if (rbearing)
     *rbearing = 0;
   for (g = MGLYPH (1), width = 0; g->type != GLYPH_ANCHOR; g++)
     if (g->pos >= from && g->pos < to)
       {
+       if (lbearing && width + g->lbearing < *lbearing)
+         *lbearing = width + g->lbearing;
        if (rbearing && width + g->rbearing > *rbearing)
          *rbearing = width + g->rbearing;
        width += g->width;
@@ -1386,8 +1435,10 @@ render_glyph_string (MFrame *frame, MDrawWindow win, int x, int y,
   int from_idx, to_idx;
   int to_x;
 
+  if (from == to)
+    return;
   if (control->orientation_reversed)
-    x -= gstring->indent + gstring_width (gstring, from, to, NULL);
+    x -= gstring->indent + gstring_width (gstring, from, to, NULL, NULL);
   else
     x += gstring->indent;
 
@@ -1503,6 +1554,9 @@ alloc_gstring (MFrame *frame, MText *mt, int pos, MDrawControl *control,
   return gstring;
 }
 
+static MGlyph *find_glyph_in_gstring (MGlyphString *gstring, int pos,
+                                     int forwardp);
+
 /* Truncate the line width of GSTRING to GSTRING->width_limit.  */
 
 static void
@@ -1536,16 +1590,26 @@ truncate_gstring (MFrame *frame, MText *mt, MGlyphString *gstring)
   if (gstring->control.line_break)
     {
       pos = (*gstring->control.line_break) (mt, gstring->from + i,
-                                           gstring->from, gstring->from + i, 0, 0);
+                                           gstring->from, gstring->from + i,
+                                           0, 0);
       if (pos <= gstring->from)
-       pos = gstring->from + 1;
+       {
+         g = find_glyph_in_gstring (gstring, gstring->from, 1);
+         pos = g->to;
+       }
       else if (pos >= gstring->to)
        pos = gstring->to;
     }
   else if (i == 0)
-    pos++;
-  compose_glyph_string (frame, mt, gstring->from, pos, gstring);
-  layout_glyph_string (frame, gstring);
+    {
+      g = find_glyph_in_gstring (gstring, gstring->from, 1);
+      pos = g->to;
+    }
+  if (pos < gstring->to)
+    {
+      compose_glyph_string (frame, mt, gstring->from, pos, gstring);
+      layout_glyph_string (frame, gstring);
+    }
 }
 
 
@@ -1742,100 +1806,6 @@ find_glyph_in_gstring (MGlyphString *gstring, int pos, int forwardp)
   return g;
 }
 
-#define GET_LB_TYPE(MT, POS, LB_TYPE)                                  \
-  do {                                                                 \
-    int c = mtext_ref_char ((MT), (POS));                              \
-    (LB_TYPE) = ((c == ' ' || c == '\t' || c == '\n') ? M_kinsoku_bol  \
-                : mchartable_lookup (linebreak_table, c));             \
-  } while (0)
-
-static int
-find_break_backward (MText *mt, int pos, int limit)
-{
-  MSymbol lb_type;
-
-  if (pos <= limit)
-    return limit;
-
-  GET_LB_TYPE (mt, pos, lb_type);
-  if (lb_type == M_kinsoku_bol)
-    return find_break_backward (mt, pos - 1, limit);
-  if (lb_type == Mnil)
-    {
-      while (pos > limit)
-       {
-         GET_LB_TYPE (mt, pos - 1, lb_type);
-         if (lb_type != Mnil)
-           break;
-         pos--;
-       }
-    }
-  else if (lb_type == M_break_at_word)
-    {
-      int beg = limit, end = mtext_nchars (mt);
-      int in_word = mtext__word_segment (mt, pos, &beg, &end);
-
-      if (in_word)
-       pos = beg;
-      else if (beg > limit)
-       {
-         end = beg;
-         beg = limit;
-         mtext__word_segment (mt, beg - 1, &beg, &end);
-         pos = beg;
-       }
-    }
-  while (pos > limit)
-    {
-      GET_LB_TYPE (mt, pos - 1, lb_type);
-      if (lb_type != M_kinsoku_eol)
-       return pos;
-      pos--;
-    }
-  return limit;
-}
-
-static int
-find_break_forward (MText *mt, int pos, int limit)
-{
-  MSymbol lb_type;
-
-  GET_LB_TYPE (mt, pos, lb_type);
-  if (lb_type == Mnil)
-    {
-      while (pos < limit)
-       {
-         pos++;
-         GET_LB_TYPE (mt, pos, lb_type);
-       }
-    }
-  else if (lb_type == M_break_at_word)
-    {
-      int beg = 0, end = mtext_nchars (mt);
-      int in_word = mtext__word_segment (mt, pos, &beg, &end);
-
-      if (! in_word)
-       pos = end;
-      else if (end < limit)
-       {
-         beg = end;
-         pos = end = mtext_nchars (mt);
-         mtext__word_segment (mt, pos, &beg, &end);
-         pos = end;
-       }
-    }
-  else if (lb_type == M_kinsoku_bol)
-    pos++;
-  while (pos < limit)
-    {
-      GET_LB_TYPE (mt, pos, lb_type);
-      if (lb_type != M_kinsoku_bol)
-       return pos;
-      pos++;
-    }
-  return limit;
-}
-
 \f
 /* for debugging... */
 char work[16];
@@ -1910,7 +1880,7 @@ mdraw__init ()
   memset (&scratch_gstring, 0, sizeof (scratch_gstring));
   MLIST_INIT1 (&scratch_gstring, glyphs, 3);
 
-  Minherited = msymbol ("inherited");
+  Mcommon = msymbol ("common");
 
   McatCc = msymbol ("Cc");
   McatCf = msymbol ("Cf");
@@ -1921,6 +1891,7 @@ mdraw__init ()
   MbidiRLO = msymbol ("RLO");
   MbidiBN = msymbol ("BN");
   MbidiS = msymbol ("S");
+  MbidiNSM = msymbol ("NSM");
 #ifdef HAVE_FRIBIDI
   fribidi_set_mirroring (TRUE);
 #endif
@@ -2283,7 +2254,7 @@ mdraw_text_extents (MFrame *frame,
 {
   MGlyphString *gstring;
   int y = 0;
-  int width, rbearing;
+  int width, lbearing, rbearing;
 
   ASSURE_CONTROL (control);
   M_CHECK_POS_X (mt, from, -1);
@@ -2295,52 +2266,49 @@ mdraw_text_extents (MFrame *frame,
   gstring = get_gstring (frame, mt, from, to, control);
   if (! gstring)
     MERROR (MERROR_DRAW, -1);
-  width = gstring_width (gstring, from, to, &rbearing);
+  width = gstring_width (gstring, from, to, &lbearing, &rbearing);
   if (overall_ink_return)
-    {
-      overall_ink_return->y = - gstring->physical_ascent;
-      overall_ink_return->x = gstring->lbearing;
-    }
+    overall_ink_return->y = - gstring->physical_ascent;
   if (overall_logical_return)
-    {
-      overall_logical_return->y = - gstring->ascent;
-      overall_logical_return->x = 0;
-    }
+    overall_logical_return->y = - gstring->ascent;
   if (overall_line_return)
-    {
-      overall_line_return->y = - gstring->line_ascent;
-      overall_line_return->x = gstring->lbearing;
-    }
+    overall_line_return->y = - gstring->line_ascent;
 
   for (from = gstring->to; from < to; from = gstring->to)
     {
-      int this_width, this_rbearing;
+      int this_width, this_lbearing, this_rbearing;
 
       y += gstring->line_descent;
       M17N_OBJECT_UNREF (gstring->top);
       gstring = get_gstring (frame, mt, from, to, control);
-      this_width = gstring_width (gstring, from, to, &this_rbearing);
+      this_width = gstring_width (gstring, from, to,
+                                 &this_lbearing, &this_rbearing);
       y += gstring->line_ascent;
       if (width < this_width)
        width = this_width;
       if (rbearing < this_rbearing)
        rbearing = this_rbearing;
+      if (lbearing > this_lbearing)
+       lbearing = this_lbearing;
     }
   if (overall_ink_return)
     {
-      overall_ink_return->width = rbearing;
+      overall_ink_return->x = lbearing;
+      overall_ink_return->width = rbearing - lbearing;
       overall_ink_return->height
        = y + gstring->physical_descent - overall_ink_return->y;
     }
   if (overall_logical_return)
     {
+      overall_logical_return->x = 0;
       overall_logical_return->width = width;
       overall_logical_return->height
        = y + gstring->descent - overall_logical_return->y;
     }
   if (overall_line_return)
     {
-      overall_line_return->width = MAX (width, rbearing);
+      overall_line_return->x = lbearing;
+      overall_line_return->width = MAX (width, rbearing - lbearing);
       overall_line_return->height
        = y + gstring->line_descent - overall_line_return->y;
     }
@@ -2703,7 +2671,7 @@ mdraw_glyph_info (MFrame *frame, MText *mt, int from, int pos,
   info->metrics.height = gstring->height;
   info->metrics.width = - g->lbearing + g->width;
   if (g->rface->rfont)
-    info->font = &g->rface->rfont->font;
+    info->font = (MFont *) g->rface->rfont;
   else
     info->font = NULL;
   /* info->logical_width is calculated later.  */
@@ -2715,7 +2683,8 @@ mdraw_glyph_info (MFrame *frame, MText *mt, int from, int pos,
 
       info->prev_from = g_tmp->pos;
     }
-  else if (info->line_from > 0)
+  else if (info->line_from > 0
+          && gstring->from > 0)
     {
       /* The logically previous glyph is on the previous line.  */
       MGlyphString *gst = get_gstring (frame, mt, gstring->from - 1,
@@ -2908,8 +2877,11 @@ mdraw_glyph_list (MFrame *frame, MText *mt, int from, int to,
          glyphs->y_advance = 0;
          if (g->rface->rfont)
            {
-             glyphs->font = &g->rface->rfont->font;
-             glyphs->font_type = g->rface->rfont->type;
+             glyphs->font = (MFont *) g->rface->rfont;
+             glyphs->font_type
+               = (glyphs->font->source == MFONT_SOURCE_X ? Mx
+                  : g->rface->rfont->driver == &mfont__ft_driver ? Mfreetype
+                  : Mxft);
              glyphs->fontp = g->rface->rfont->fontp;
            }
          else
@@ -2975,23 +2947,33 @@ mdraw_text_items (MFrame *frame, MDrawWindow win, int x, int y,
 }
 
 /*=*/
-/***en 
-      @brief   calculate a line breaking position.
-
-      The function mdraw_default_line_break () calculates a line
-      breaking position based on the line number $LINE and the
-      coordinate $Y, when a line is too long to fit within the width
-      limit.  $POS is the position of the character next to the last
-      one that fits within the limit.  $FROM is the position of the
-      first character of the line, and $TO is the position of the last
-      character displayed on the line if there were not width limit.
-      $LINE and $Y are reset to 0 when a line is broken by a newline
-      character, and incremented each time when a long line is broken
-      because of the width limit.  
+/***en
+    @brief Option of line breaking for drawing text.
 
-      @return 
-      This function returns a character position to break the
-      line.
+    The variable #mdraw_line_break_option specifies line breaking
+    options by logical-or of the members of #MTextLineBreakOption.  It
+    controls the line breaking algorithm of the function
+    mdraw_default_line_break ().  */
+    
+int mdraw_line_break_option;
+
+/*=*/
+/***en 
+    @brief Calculate a line breaking position.
+
+    The function mdraw_default_line_break () calculates a line
+    breaking position based on the line number $LINE and the
+    coordinate $Y, when a line is too long to fit within the width
+    limit.  $POS is the position of the character next to the last one
+    that fits within the limit.  $FROM is the position of the first
+    character of the line, and $TO is the position of the last
+    character displayed on the line if there were not width limit.
+    $LINE and $Y are reset to 0 when a line is broken by a newline
+    character, and incremented each time when a long line is broken
+    because of the width limit.
+
+    @return This function returns a character position to break the
+    line.
 */
 
 /***ja 
@@ -3013,32 +2995,14 @@ int
 mdraw_default_line_break (MText *mt, int pos,
                          int from, int to, int line, int y)
 {
-  int p;
-
-  if (! linebreak_table)
-    {
-      MDatabase *mdb = mdatabase_find (Mchar_table, Msymbol,
-                                      msymbol ("linebreak"), Mnil);
-
-      if (mdb)
-       linebreak_table = mdatabase_load (mdb);
-      if (! linebreak_table)
-       linebreak_table = mchartable (Msymbol, Mnil);
-    }
-
-  if (pos > from)
-    {
-      p = find_break_backward (mt, pos, from);
-      if (p > from)
-       return p;
-    }
-  if (pos < to)
-    {
-      p = find_break_forward (mt, pos, to);
-      if (p < to)
-       return p;
-    }
-  return to;
+  int p, after;
+
+  p = mtext_line_break (mt, pos, mdraw_line_break_option, &after);
+  if (p < from)
+    p = from;
+  else if (p >= to)
+    p = to;
+  return p;
 }
 
 /*=*/