diff --git a/configure.in b/configure.in
index eecb01d..bd5f3d2 100644
--- a/configure.in
+++ b/configure.in
@@ -29,17 +29,17 @@ AC_SUBST([RSVG_API_MINOR_VERSION])
 AC_SUBST([RSVG_API_VERSION_U],[AS_TR_SH([$RSVG_API_VERSION])])
 
 dnl ===========================================================================
 
 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
 CROCO_REQUIRED=0.6.1
 
 # ===========================================================================
 
 AC_SUBST([RSVG_LT_VERSION_INFO],[rsvg_lt_version_info])
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index a086bde..31012a0 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -385,46 +385,61 @@ rsvg_cairo_create_pango_context (RsvgDrawingCtx * ctx)
 
 void
 rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, double x, double y)
 {
     RsvgCairoRender *render = RSVG_CAIRO_RENDER (ctx->render);
     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);
 
     _set_rsvg_affine (render, &state->affine);
 
     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 / (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;
+    } else {
+        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;
+    }
     bbox.virgin = 0;
 
+    rotation = pango_gravity_to_rotation (gravity);
     if (state->fill) {
         cairo_move_to (render->cr, x, y);
         rsvg_bbox_insert (&render->bbox, &bbox);
         _set_source_rsvg_paint_server (ctx,
                                        state->current_color,
                                        state->fill,
                                        state->fill_opacity,
                                        bbox, rsvg_current_state (ctx)->current_color);
 
+        cairo_rotate (render->cr, -rotation);
         pango_cairo_show_layout (render->cr, layout);
     }
 
     if (state->stroke) {
         cairo_move_to (render->cr, x, y);
         rsvg_bbox_insert (&render->bbox, &bbox);
+        cairo_save (render->cr);
+        cairo_rotate (render->cr, -rotation);
         pango_cairo_layout_path (render->cr, layout);
+        cairo_restore (render->cr);
 
         _set_source_rsvg_paint_server (ctx,
                                        state->current_color,
                                        state->stroke,
                                        state->stroke_opacity,
                                        bbox, rsvg_current_state (ctx)->current_color);
 
         cairo_set_line_width (render->cr, _rsvg_css_normalize_length (&state->stroke_width, ctx, 'h'));
diff --git a/rsvg-styles.c b/rsvg-styles.c
index 5437ef5..1dc2048 100644
--- a/rsvg-styles.c
+++ b/rsvg-styles.c
@@ -137,16 +137,17 @@ rsvg_state_init (RsvgState * state)
 
     state->font_family = g_strdup (RSVG_DEFAULT_FONT);
     state->font_size = _rsvg_css_parse_length ("12.0");
     state->font_style = PANGO_STYLE_NORMAL;
     state->font_variant = PANGO_VARIANT_NORMAL;
     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");
     state->visible = TRUE;
     state->cond_true = TRUE;
     state->filter = NULL;
     state->clip_path_ref = NULL;
     state->startMarker = NULL;
@@ -176,16 +177,17 @@ rsvg_state_init (RsvgState * state)
     state->has_font_family = FALSE;
     state->has_lang = FALSE;
     state->has_font_style = FALSE;
     state->has_font_variant = FALSE;
     state->has_font_weight = FALSE;
     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;
     state->has_startMarker = FALSE;
     state->has_middleMarker = FALSE;
     state->has_endMarker = FALSE;
     state->has_overflow = FALSE;
 
@@ -297,16 +299,18 @@ rsvg_state_inherit_run (RsvgState * dst, const RsvgState * src,
     if (function (dst->has_font_weight, src->has_font_weight))
         dst->font_weight = src->font_weight;
     if (function (dst->has_font_stretch, src->has_font_stretch))
         dst->font_stretch = src->font_stretch;
     if (function (dst->has_font_decor, src->has_font_decor))
         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))
         dst->text_anchor = src->text_anchor;
     if (function (dst->has_letter_spacing, src->has_letter_spacing))
 	dst->letter_spacing = src->letter_spacing;
     if (function (dst->has_startMarker, src->has_startMarker))
         dst->startMarker = src->startMarker;
@@ -710,27 +714,38 @@ rsvg_parse_style_pair (RsvgHandle * ctx,
         else if (g_str_equal (value, "bidi-override"))
             state->unicode_bidi = UNICODE_BIDI_OVERRIDE;
         else                    /* normal */
             state->unicode_bidi = UNICODE_BIDI_NORMAL;
     } else if (g_str_equal (name, "writing-mode")) {
         /* 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_gravity = PANGO_GRAVITY_SOUTH;
+            state->has_text_gravity = FALSE;
+        } else if (g_str_equal (value, "lr-tb")) {
             state->text_dir = PANGO_DIRECTION_TTB_LTR;
-        else if (g_str_equal (value, "rl"))
+            state->text_gravity = PANGO_GRAVITY_SOUTH;
+        } else if (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_gravity = PANGO_GRAVITY_SOUTH;
+        } else if (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 {
             state->text_dir = PANGO_DIRECTION_LTR;
+            state->text_gravity = PANGO_GRAVITY_SOUTH;
+        }
     } else if (g_str_equal (name, "text-anchor")) {
         state->has_text_anchor = TRUE;
         if (g_str_equal (value, "inherit")) {
             state->text_anchor = TEXT_ANCHOR_START;
             state->has_text_anchor = FALSE;
         } else {
             if (strstr (value, "start"))
                 state->text_anchor = TEXT_ANCHOR_START;
diff --git a/rsvg-styles.h b/rsvg-styles.h
index 248d8d7..87c36eb 100644
--- a/rsvg-styles.h
+++ b/rsvg-styles.h
@@ -124,16 +124,18 @@ struct _RsvgState {
     PangoWeight font_weight;
     gboolean has_font_weight;
     PangoStretch font_stretch;
     gboolean has_font_stretch;
     TextDecoration font_decor;
     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;
     gboolean has_text_anchor;
     RsvgLength letter_spacing;
     gboolean has_letter_spacing;
 
     guint text_offset;
diff --git a/rsvg-text.c b/rsvg-text.c
index cad5d7f..a4070bb 100644
--- a/rsvg-text.c
+++ b/rsvg-text.c
@@ -211,80 +211,92 @@ static int
  _rsvg_node_text_length_tspan (RsvgNodeText * self, RsvgDrawingCtx * ctx,
                                gdouble * x, gboolean * lastwasspace,
                                gboolean usetextonly);
 
 static gdouble rsvg_text_length_text_as_string (RsvgDrawingCtx * ctx, const char *text);
 
 static int
 _rsvg_node_text_length_children (RsvgNode * self, RsvgDrawingCtx * ctx,
-                                 gdouble * x, gboolean * lastwasspace,
+                                 gdouble * length, gboolean * lastwasspace,
                                  gboolean usetextonly)
 {
     guint i;
     int out = FALSE;
     for (i = 0; i < self->children->len; i++) {
         RsvgNode *node = g_ptr_array_index (self->children, i);
         RsvgNodeType type = RSVG_NODE_TYPE (node);
 
         rsvg_state_push (ctx);
         rsvg_state_reinherit_top (ctx, node->state, 0);
         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);
                 }
             }
         }
         rsvg_state_pop (ctx);
         if (out)
             break;
     }
     return out;
 }
 
 
 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)) {
+        x -= length;
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
+            dx /= 2;
         if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
-            x -= length;
+            dx = 0;
+    } else {
+        y -= length;
         if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_MIDDLE)
-            x -= length / 2;
+            dy /= 2;
+        if (rsvg_current_state (ctx)->text_anchor == TEXT_ANCHOR_END)
+            dy = 0;
     }
+    x += dx;
+    y += dy;
 
     lastwasspace = TRUE;
     _rsvg_node_text_type_children (self, ctx, &x, &y, &lastwasspace, FALSE);
 }
 
 RsvgNode *
 rsvg_new_text (void)
 {
@@ -297,47 +309,72 @@ rsvg_new_text (void)
     return &text->super;
 }
 
 static void
 _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->dx, ctx, 'h');
+    else
+        *length += _rsvg_css_normalize_length (&self->dy, ctx, 'v');
+
+    return _rsvg_node_text_length_children (&self->super, ctx, length,
+                                             lastwasspace, usetextonly);
 }
 
 static void
 _rsvg_node_tspan_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
 {
     const char *klazz = NULL, *id = NULL, *value;
     RsvgNodeText *text = (RsvgNodeText *) self;
 
@@ -444,16 +481,19 @@ rsvg_text_create_layout (RsvgDrawingCtx * ctx,
     PangoAttribute *attribute;
 
     if (state->lang)
         pango_context_set_language (context, pango_language_from_string (state->lang));
 
     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)
         pango_font_description_set_family_static (font_desc, state->font_family);
 
     pango_font_description_set_style (font_desc, state->font_style);
     pango_font_description_set_variant (font_desc, state->font_variant);
     pango_font_description_set_weight (font_desc, state->font_weight);
@@ -524,32 +564,42 @@ rsvg_text_layout_new (RsvgDrawingCtx * ctx, RsvgState * state, const char *text)
 
 void
 rsvg_text_render_text (RsvgDrawingCtx * ctx, const char *text, gdouble * x, gdouble * y)
 {
     PangoContext *context;
     PangoLayout *layout;
     PangoLayoutIter *iter;
     RsvgState *state;
-    gint w, h, baseline;
+    gint w, h, offsetX, offsetY;
 
     state = rsvg_current_state (ctx);
 
     /* Do not render the text if the font size is zero. See bug #581491. */
     if (state->font_size.length == 0)
         return;
 
     context = ctx->render->create_pango_context (ctx);
     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 = 0;
+        offsetY = pango_layout_iter_get_baseline (iter) / (double)PANGO_SCALE;
+    } else {
+        offsetX = -pango_layout_iter_get_baseline (iter) / (double)PANGO_SCALE;
+        offsetY = 0;
+    }
     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)) {
+        *x += w / (double)PANGO_SCALE;
+    } else {
+        *y += w / (double)PANGO_SCALE;
+    }
     g_object_unref (layout);
     g_object_unref (context);
 }
 
 static gdouble
 rsvg_text_layout_width (RsvgTextLayout * layout)
 {
     gint width;
