Commit 1af28cd1 authored by KUROSAWA Takeshi's avatar KUROSAWA Takeshi Committed by Christian Persch

Support basic vertical writing (Gnome Bug #664533)

Support vertical writing text (writing-mode: tb-rl | tb)
http://www.w3.org/TR/SVG11/text.html#SettingInlineProgressionDirection

Bump pango dependency to 1.16.0 to use its vertical text APIs.

Currently, we ignore both glyph-orientation-vertical and
glyph-orientation-horizontal.
parent 52672aff
......@@ -34,7 +34,7 @@ GLIB_REQUIRED=2.12.0
GIO_REQUIRED=2.24.0
LIBXML_REQUIRED=2.7.0
CAIRO_REQUIRED=1.2.0
PANGOCAIRO_REQUIRED=1.10.0
PANGOCAIRO_REQUIRED=1.16.0
GDK_PIXBUF_REQUIRED=1.3.7
GTK2_REQUIRED=2.16.0
GTK3_REQUIRED=3.0.0
......
......@@ -390,6 +390,8 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
RsvgState *state = rsvg_current_state (ctx);
PangoRectangle ink;
RsvgBbox bbox;
PangoGravity gravity = pango_context_get_gravity (pango_layout_get_context (layout));
double rotation;
cairo_set_antialias (render->cr, state->text_rendering_type);
......@@ -398,13 +400,22 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
pango_layout_get_extents (layout, &ink, NULL);
rsvg_bbox_init (&bbox, &state->affine);
bbox.rect.x = x + ink.x / (double)PANGO_SCALE;
bbox.rect.y = y + ink.y / (double)PANGO_SCALE;
bbox.rect.width = ink.width / (double)PANGO_SCALE;
bbox.rect.height = ink.height / (double)PANGO_SCALE;
if (PANGO_GRAVITY_IS_VERTICAL (gravity)) {
bbox.rect.x = x + (ink.x - ink.height) / (double)PANGO_SCALE;
bbox.rect.y = y + ink.y / (double)PANGO_SCALE;
bbox.rect.width = ink.height / (double)PANGO_SCALE;
bbox.rect.height = ink.width / (double)PANGO_SCALE;
} else {
bbox.rect.x = x + ink.x / (double)PANGO_SCALE;
bbox.rect.y = y + ink.y / (double)PANGO_SCALE;
bbox.rect.width = ink.width / (double)PANGO_SCALE;
bbox.rect.height = ink.height / (double)PANGO_SCALE;
}
bbox.virgin = 0;
rotation = pango_gravity_to_rotation (gravity);
if (state->fill) {
cairo_save (render->cr);
cairo_move_to (render->cr, x, y);
rsvg_bbox_insert (&render->bbox, &bbox);
_set_source_rsvg_paint_server (ctx,
......@@ -412,14 +423,16 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
state->fill,
state->fill_opacity,
bbox, rsvg_current_state (ctx)->current_color);
if (rotation != 0.)
cairo_rotate (render->cr, -rotation);
pango_cairo_show_layout (render->cr, layout);
cairo_restore (render->cr);
}
if (state->stroke) {
cairo_save (render->cr);
cairo_move_to (render->cr, x, y);
rsvg_bbox_insert (&render->bbox, &bbox);
pango_cairo_layout_path (render->cr, layout);
_set_source_rsvg_paint_server (ctx,
state->current_color,
......@@ -427,6 +440,10 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
state->stroke_opacity,
bbox, rsvg_current_state (ctx)->current_color);
if (rotation != 0.)
cairo_rotate (render->cr, -rotation);
pango_cairo_layout_path (render->cr, layout);
cairo_set_line_width (render->cr, _rsvg_css_normalize_length (&state->stroke_width, ctx, 'h'));
cairo_set_miter_limit (render->cr, state->miter_limit);
cairo_set_line_cap (render->cr, (cairo_line_cap_t) state->cap);
......@@ -434,6 +451,7 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
cairo_set_dash (render->cr, state->dash.dash, state->dash.n_dash,
_rsvg_css_normalize_length (&state->dash.offset, ctx, 'o'));
cairo_stroke (render->cr);
cairo_restore (render->cr);
}
}
......
......@@ -142,6 +142,7 @@ rsvg_state_init (RsvgState * state)
state->font_weight = PANGO_WEIGHT_NORMAL;
state->font_stretch = PANGO_STRETCH_NORMAL;
state->text_dir = PANGO_DIRECTION_LTR;
state->text_gravity = PANGO_GRAVITY_SOUTH;
state->unicode_bidi = UNICODE_BIDI_NORMAL;
state->text_anchor = TEXT_ANCHOR_START;
state->letter_spacing = _rsvg_css_parse_length ("0.0");
......@@ -181,6 +182,7 @@ rsvg_state_init (RsvgState * state)
state->has_font_stretch = FALSE;
state->has_font_decor = FALSE;
state->has_text_dir = FALSE;
state->has_text_gravity = FALSE;
state->has_unicode_bidi = FALSE;
state->has_text_anchor = FALSE;
state->has_letter_spacing = FALSE;
......@@ -302,6 +304,8 @@ rsvg_state_inherit_run (RsvgState * dst, const RsvgState * src,
dst->font_decor = src->font_decor;
if (function (dst->has_text_dir, src->has_text_dir))
dst->text_dir = src->text_dir;
if (function (dst->has_text_gravity, src->has_text_gravity))
dst->text_gravity = src->text_gravity;
if (function (dst->has_unicode_bidi, src->has_unicode_bidi))
dst->unicode_bidi = src->unicode_bidi;
if (function (dst->has_text_anchor, src->has_text_anchor))
......@@ -715,17 +719,22 @@ rsvg_parse_style_pair (RsvgHandle * ctx,
/* TODO: these aren't quite right... */
state->has_text_dir = TRUE;
state->has_text_gravity = TRUE;
if (g_str_equal (value, "inherit")) {
state->text_dir = PANGO_DIRECTION_LTR;
state->has_text_dir = FALSE;
} else if (g_str_equal (value, "lr-tb") || g_str_equal (value, "tb"))
state->text_dir = PANGO_DIRECTION_TTB_LTR;
else if (g_str_equal (value, "rl"))
state->text_gravity = PANGO_GRAVITY_SOUTH;
state->has_text_gravity = FALSE;
} else if (g_str_equal (value, "lr-tb") || g_str_equal (value, "lr")) {
state->text_dir = PANGO_DIRECTION_LTR;
state->text_gravity = PANGO_GRAVITY_SOUTH;
} else if (g_str_equal (value, "rl-tb") || g_str_equal (value, "rl")) {
state->text_dir = PANGO_DIRECTION_RTL;
else if (g_str_equal (value, "tb-rl") || g_str_equal (value, "rl-tb"))
state->text_dir = PANGO_DIRECTION_TTB_RTL;
else
state->text_gravity = PANGO_GRAVITY_SOUTH;
} else if (g_str_equal (value, "tb-rl") || g_str_equal (value, "tb")) {
state->text_dir = PANGO_DIRECTION_LTR;
state->text_gravity = PANGO_GRAVITY_EAST;
}
} else if (g_str_equal (name, "text-anchor")) {
state->has_text_anchor = TRUE;
if (g_str_equal (value, "inherit")) {
......
......@@ -129,6 +129,8 @@ struct _RsvgState {
gboolean has_font_decor;
PangoDirection text_dir;
gboolean has_text_dir;
PangoGravity text_gravity;
gboolean has_text_gravity;
UnicodeBidi unicode_bidi;
gboolean has_unicode_bidi;
TextAnchor text_anchor;
......
......@@ -216,7 +216,7 @@ static gdouble rsvg_text_length_text_as_string (RsvgDrawingCtx * ctx, const char
static int
_rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx,
gdouble * x, gboolean * lastwasspace,
gdouble * length, gboolean * lastwasspace,
gboolean usetextonly)
{
guint i;
......@@ -230,22 +230,22 @@ _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx,
if (type == RSVG_NODE_TYPE_CHARS) {
RsvgNodeChars *chars = (RsvgNodeChars *) node;
GString *str = _rsvg_text_chomp (rsvg_current_state (ctx), chars->contents, lastwasspace);
*x += rsvg_text_length_text_as_string (ctx, str->str);
*length += rsvg_text_length_text_as_string (ctx, str->str);
g_string_free (str, TRUE);
} else {
if (usetextonly) {
out = _rsvg_node_text_length_children(node, ctx, x,
out = _rsvg_node_text_length_children(node, ctx, length,
lastwasspace,
usetextonly);
} else {
if (type == RSVG_NODE_TYPE_TSPAN) {
RsvgNodeText *tspan = (RsvgNodeText *) node;
out = _rsvg_node_text_length_tspan (tspan, ctx, x,
out = _rsvg_node_text_length_tspan (tspan, ctx, length,
lastwasspace,
usetextonly);
} else if (type == RSVG_NODE_TYPE_TREF) {
RsvgNodeTref *tref = (RsvgNodeTref *) node;
out = _rsvg_node_text_length_tref (tref, ctx, x,
out = _rsvg_node_text_length_tref (tref, ctx, length,
lastwasspace,
usetextonly);
}
......@@ -262,24 +262,36 @@ _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx,
static void
_rsvg_node_text_draw (RsvgNode * self, RsvgDrawingCtx * ctx, int dominate)
{
double x, y;
double x, y, dx, dy, length = 0;
gboolean lastwasspace = TRUE;
RsvgNodeText *text = (RsvgNodeText *) self;
rsvg_state_reinherit_top (ctx, self->state, dominate);
x = _rsvg_css_normalize_length (&text->x, ctx, 'h');
y = _rsvg_css_normalize_length (&text->y, ctx, 'v');
x += _rsvg_css_normalize_length (&text->dx, ctx, 'h');
y += _rsvg_css_normalize_length (&text->dy, ctx, 'v');
dx = _rsvg_css_normalize_length (&text->dx, ctx, 'h');
dy = _rsvg_css_normalize_length (&text->dy, ctx, 'v');
if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) {
double length = 0;
_rsvg_node_text_length_children (self, ctx, &length, &lastwasspace, FALSE);
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
length /= 2;
}
if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) {
y -= length;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
dy /= 2;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
x -= length;
dy = 0;
} else {
x -= length;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
x -= length / 2;
dx /= 2;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
dx = 0;
}
x += dx;
y += dy;
lastwasspace = TRUE;
_rsvg_node_text_type_children (self, ctx, &x, &y, &lastwasspace, FALSE);
......@@ -302,37 +314,62 @@ _rsvg_node_text_type_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx,
gdouble * x, gdouble * y, gboolean * lastwasspace,
gboolean usetextonly)
{
double dx, dy, length = 0;
rsvg_state_reinherit_top (ctx, self->super.state, 0);
dx = _rsvg_css_normalize_length (&self->dx, ctx, 'h');
dy = _rsvg_css_normalize_length (&self->dy, ctx, 'v');
if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) {
gboolean lws = *lastwasspace;
_rsvg_node_text_length_children (&self->super, ctx, &length, &lws,
usetextonly);
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
length /= 2;
}
if (self->x.factor != 'n') {
*x = _rsvg_css_normalize_length (&self->x, ctx, 'h');
if (rsvg_current_state (ctx)->text_anchor != TEXT_ANCHOR_START) {
double length = 0;
gboolean lws = *lastwasspace;
_rsvg_node_text_length_children (&self->super, ctx, &length, &lws,
usetextonly);
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
*x -= length;
if (!PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) {
*x -= length;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
*x -= length / 2;
dx /= 2;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
dx = 0;
}
}
if (self->y.factor != 'n')
*x += dx;
if (self->y.factor != 'n') {
*y = _rsvg_css_normalize_length (&self->y, ctx, 'v');
*x += _rsvg_css_normalize_length (&self->dx, ctx, 'h');
*y += _rsvg_css_normalize_length (&self->dy, ctx, 'v');
if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity)) {
*y -= length;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
dy /= 2;
if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
dy = 0;
}
}
*y += dy;
_rsvg_node_text_type_children (&self->super, ctx, x, y, lastwasspace,
usetextonly);
}
static int
_rsvg_node_text_length_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx, gdouble * x,
_rsvg_node_text_length_tspan (RsvgNodeText * self,
RsvgDrawingCtx * ctx, gdouble * length,
gboolean * lastwasspace, gboolean usetextonly)
{
if (self->x.factor != 'n' || self->y.factor != 'n')
return TRUE;
return _rsvg_node_text_length_children (&self->super, ctx, x, lastwasspace,
usetextonly);
if (PANGO_GRAVITY_IS_VERTICAL (rsvg_current_state (ctx)->text_gravity))
*length += _rsvg_css_normalize_length (&self->dy, ctx, 'v');
else
*length += _rsvg_css_normalize_length (&self->dx, ctx, 'h');
return _rsvg_node_text_length_children (&self->super, ctx, length,
lastwasspace, usetextonly);
}
static void
......@@ -448,6 +485,9 @@ rsvg_text_create_layout (RsvgDrawingCtx * ctx,
if (state->unicode_bidi == UNICODE_BIDI_OVERRIDE || state->unicode_bidi == UNICODE_BIDI_EMBED)
pango_context_set_base_dir (context, state->text_dir);
if (PANGO_GRAVITY_IS_VERTICAL (state->text_gravity))
pango_context_set_base_gravity (context, state->text_gravity);
font_desc = pango_font_description_copy (pango_context_get_font_description (context));
if (state->font_family)
......@@ -495,8 +535,7 @@ rsvg_text_create_layout (RsvgDrawingCtx * ctx,
else
pango_layout_set_text (layout, NULL, 0);
pango_layout_set_alignment (layout, (state->text_dir == PANGO_DIRECTION_LTR ||
state->text_dir == PANGO_DIRECTION_TTB_LTR) ?
pango_layout_set_alignment (layout, (state->text_dir == PANGO_DIRECTION_LTR) ?
PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT);
return layout;
......@@ -528,7 +567,7 @@ rsvg_text_render_text (RsvgDrawingCtx * ctx, const char *text, gdouble * x, gdou
PangoLayout *layout;
PangoLayoutIter *iter;
RsvgState *state;
gint w, h, baseline;
gint w, h, offsetX, offsetY;
state = rsvg_current_state (ctx);
......@@ -540,10 +579,20 @@ rsvg_text_render_text (RsvgDrawingCtx * ctx, const char *text, gdouble * x, gdou
layout = rsvg_text_create_layout (ctx, state, text, context);
pango_layout_get_size (layout, &w, &h);
iter = pango_layout_get_iter (layout);
baseline = pango_layout_iter_get_baseline (iter) / (double)PANGO_SCALE;
if (PANGO_GRAVITY_IS_VERTICAL (state->text_gravity)) {
offsetX = -pango_layout_iter_get_baseline (iter) / (double)PANGO_SCALE;
offsetY = 0;
} else {
offsetX = 0;
offsetY = pango_layout_iter_get_baseline (iter) / (double)PANGO_SCALE;
}
pango_layout_iter_free (iter);
ctx->render->render_pango_layout (ctx, layout, *x, *y - baseline);
*x += w / (double)PANGO_SCALE;
ctx->render->render_pango_layout (ctx, layout, *x - offsetX, *y - offsetY);
if (PANGO_GRAVITY_IS_VERTICAL (state->text_gravity))
*y += w / (double)PANGO_SCALE;
else
*x += w / (double)PANGO_SCALE;
g_object_unref (layout);
g_object_unref (context);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment