/* 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
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
#include "m17n-misc.h"
#include "internal.h"
#include "symbol.h"
+#include "mtext.h"
#include "textprop.h"
#include "internal-gui.h"
#include "face.h"
static MSymbol M_glyph_string;
/* Special scripts */
-static MSymbol Minherited;
+static MSymbol Mcommon;
/* Special categories */
static MSymbol McatCc, McatCf;
+static MCharTable *linebreak_table;
+static MSymbol M_break_at_space, M_break_at_word, M_break_at_any;
+static MSymbol M_kinsoku_bol, M_kinsoku_eol;
+
\f
/* Glyph-string composer. */
static MSymbol MbidiRLO;
static MSymbol MbidiBN;
static MSymbol MbidiS;
+static MSymbol MbidiNSM;
static void
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++)
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;
}
/* 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];
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;
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;
{
if (pos == to)
break;
- if (pos == face_change)
+ if (pos < mtext_nchars (mt))
{
+ MFont *font = rface->font;
MFace *faces[64];
- int num = mtext_get_prop_values (mt, pos, Mface,
- (void **) faces, 64);
+ int num;
- 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);
+ 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++;
+ }
+ else
+ {
+ 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;
}
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. */
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;
{
int start = i++;
- if (this->rface->rfont->layouter != Mnil)
+ if (this->rface->layouter != Mnil)
{
MGlyph *prev;
unsigned code;
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++)
{
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)
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;
}
{
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
{
(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)
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);
}
*from_idx = *to_idx = 0;
+ *to_x = x;
while (g->type != GLYPH_ANCHOR)
{
if (g->pos >= from && g->pos < to)
gend--;
}
if (g != gend)
- while (gend[-1].to == gend->to) gend++;
+ while (gend->type != GLYPH_ANCHOR && gend[-1].to == gend->to)
+ gend++;
}
}
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;
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;
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
if (gstring->control.line_break)
{
pos = (*gstring->control.line_break) (mt, gstring->from + i,
- gstring->from, gstring->to, 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);
+ }
}
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");
MbidiRLO = msymbol ("RLO");
MbidiBN = msymbol ("BN");
MbidiS = msymbol ("S");
+ MbidiNSM = msymbol ("NSM");
#ifdef HAVE_FRIBIDI
fribidi_set_mirroring (TRUE);
#endif
+ M_break_at_space = msymbol ("bs");
+ M_break_at_word = msymbol ("bw");
+ M_break_at_any = msymbol ("ba");
+ M_kinsoku_bol = msymbol ("kb");
+ M_kinsoku_eol = msymbol ("ke");
+
return 0;
}
mdraw__fini ()
{
MLIST_FREE1 (&scratch_gstring, glyphs);
+ M17N_OBJECT_UNREF (linebreak_table);
+ linebreak_table = NULL;
}
/*** @} */
{
MGlyphString *gstring;
int y = 0;
- int width, rbearing;
+ int width, lbearing, rbearing;
ASSURE_CONTROL (control);
M_CHECK_POS_X (mt, from, -1);
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;
}
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. */
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,
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
}
/*=*/
-/***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
mdraw_default_line_break (MText *mt, int pos,
int from, int to, int line, int y)
{
- int c = mtext_ref_char (mt, pos);
- int orig_pos = pos;
-
- if (c == ' ' || c == '\t')
- {
- pos++;
- while (pos < to
- && ((c = mtext_ref_char (mt, pos)) == ' ' || c == '\t'))
- pos++;
- }
- else
- {
- while (pos > from)
- {
- if (c == ' ' || c == '\t')
- break;
- pos--;
- c = mtext_ref_char (mt, pos);
- }
- if (pos == from)
- pos = orig_pos;
- else
- pos++;
- }
- return pos;
+ 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;
}
/*=*/