[librsvg] Support basic vertical writing (Gnome Bug #664533)



commit 1af28cd186ace50c87ce8d90203c52f50ed45f83
Author: KUROSAWA Takeshi <taken spc gmail com>
Date:   Sat Nov 26 00:11:38 2011 +0900

    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.

 configure.in      |    2 +-
 rsvg-cairo-draw.c |   30 +++++++++++---
 rsvg-styles.c     |   21 +++++++---
 rsvg-styles.h     |    2 +
 rsvg-text.c       |  111 ++++++++++++++++++++++++++++++++++++++---------------
 5 files changed, 122 insertions(+), 44 deletions(-)
---
diff --git a/configure.in b/configure.in
index 4595c99..85bf155 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index c8dc6d3..0029a03 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -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);
     }
 }
 
diff --git a/rsvg-styles.c b/rsvg-styles.c
index d137cd6..dead491 100644
--- a/rsvg-styles.c
+++ b/rsvg-styles.c
@@ -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")) {
diff --git a/rsvg-styles.h b/rsvg-styles.h
index 248d8d7..87c36eb 100644
--- a/rsvg-styles.h
+++ b/rsvg-styles.h
@@ -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;
diff --git a/rsvg-text.c b/rsvg-text.c
index 7417054..03d9201 100644
--- a/rsvg-text.c
+++ b/rsvg-text.c
@@ -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);
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]