[dia] [transform] svg: more transformation support for Standard objects



commit d5d5b662517bbf7c67cc8832b545ba8d47b80833
Author: Hans Breuer <hans breuer org>
Date:   Wed Oct 1 20:20:31 2014 +0200

    [transform] svg: more transformation support for Standard objects
    
    New optional method DiaObject::transform() to apply an affine transformation
    to the object. Immediately visible effect - without SVG - is rotation support
    for 'Standard - Box' and 'Standard - Ellipse'.
    'Standard - Text' and 'Standard - Image' are not fully supported yet, because
    they require significant work on the renderer level.
    Standard - Arc, Outline and ZigZagLine are not included because they wont be
    used from SVG and the direct support is considered pointless.
    
    Currently the intended main use-case is improved transformation from SVG
    especially on the level of single elements. The included test file
    transform-variations.svg shows the issues solved and open.

 lib/group.c                      |    4 +-
 lib/group.h                      |    1 -
 lib/libdia.def                   |    2 +-
 lib/object.h                     |   25 ++++++--
 lib/standard-path.c              |   19 ++++++
 objects/standard/bezier.c        |   16 +++++
 objects/standard/beziergon.c     |   16 +++++
 objects/standard/box.c           |  126 +++++++++++++++++++++++++++++++-----
 objects/standard/ellipse.c       |  120 ++++++++++++++++++++++++++++++++---
 objects/standard/image.c         |   30 +++++++++
 objects/standard/line.c          |   14 ++++
 objects/standard/polygon.c       |   17 +++++
 objects/standard/polyline.c      |   17 +++++
 plug-ins/svg/svg-import.c        |  130 ++++++++++++++------------------------
 samples/transform-variations.svg |   98 ++++++++++++++++++++++++++++
 15 files changed, 518 insertions(+), 117 deletions(-)
---
diff --git a/lib/group.c b/lib/group.c
index 86d8e49..879ce64 100644
--- a/lib/group.c
+++ b/lib/group.c
@@ -68,6 +68,7 @@ static DiaObject *group_copy(Group *group);
 static const PropDescription *group_describe_props(Group *group);
 static void group_get_props(Group *group, GPtrArray *props);
 static void group_set_props(Group *group, GPtrArray *props);
+static void group_transform (Group *group, const DiaMatrix *m);
 
 static ObjectOps group_ops = {
   (DestroyFunc)         group_destroy,
@@ -85,6 +86,7 @@ static ObjectOps group_ops = {
   (SetPropsFunc)        group_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) group_apply_properties_list,
+  (TransformFunc)       group_transform
 };
 
 DiaObjectType group_type = {
@@ -888,7 +890,7 @@ group_prop_change_free(GroupPropChange *change)
   g_list_free(change->changes_per_object);
 }
 
-void
+static void
 group_transform (Group *group, const DiaMatrix *m)
 {
   g_return_if_fail (m != NULL);
diff --git a/lib/group.h b/lib/group.h
index ce3705e..9059008 100644
--- a/lib/group.h
+++ b/lib/group.h
@@ -33,7 +33,6 @@ DiaObject *group_create_with_matrix(GList *objects, DiaMatrix *matrix);
 GList *group_objects(DiaObject *group);
 
 void group_destroy_shallow(DiaObject *group);
-void group_transform (Group *group, const DiaMatrix *mat);
 const DiaMatrix *group_get_transform (Group *group);
 
 #define IS_GROUP(obj) ((obj)->type == &group_type)
diff --git a/lib/libdia.def b/lib/libdia.def
index e56ed53..26026dc 100644
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -300,6 +300,7 @@ EXPORTS
  pixbuf_decode_base64
 
  dia_path_renderer_get_type
+ path_build_ellipse
 
  dia_pattern_new
  dia_pattern_add_color
@@ -491,7 +492,6 @@ EXPORTS
  group_destroy_shallow
  group_objects
  group_type
- group_transform
 
  get_active_focus
  give_focus
diff --git a/lib/object.h b/lib/object.h
index 84bad53..637459f 100644
--- a/lib/object.h
+++ b/lib/object.h
@@ -403,6 +403,20 @@ typedef DiaMenu *(*ObjectMenuFunc) (DiaObject* obj, Point *position);
  */
 typedef gboolean (*TextEditFunc) (DiaObject *obj, Text *text, TextEditState state, gchar *textchange);
 
+/*!
+ * \brief Transform the object with the given matrix
+ *
+ * This function - if not null - will apply the transformation matrix to the
+ * object. It should be implemented for every standard object, because it's
+ * main use-case is the support of transformations from SVG.
+ *
+ * @param obj Explicit this pointer
+ * @param m The transformation matrix
+ * @returns TRUE if the matrix can be applied to the object, FALSE otherwise.
+ * \public \memberof _DiaObject
+ */
+typedef gboolean (*TransformFunc) (DiaObject *obj, const DiaMatrix *m);
+
 /*************************************
  **  The functions provided in object.c
  *************************************/
@@ -479,14 +493,15 @@ struct _ObjectOps {
   TextEditFunc        edit_text;
   
   ApplyPropertiesListFunc apply_properties_list;
-
+  /*! check for NULL before calling */
+  TransformFunc       transform;
   /*!
     Unused places (for extension).
     These should be NULL for now. In the future they might be used.
     Then an older object will be binary compatible, because all new code
     checks if new ops are supported (!= NULL)
   */
-  void      (*(unused[4]))(DiaObject *obj,...);
+  void      (*(unused[3]))(DiaObject *obj,...);
 };
 
 /*!
@@ -498,7 +513,7 @@ struct _ObjectOps {
   when connection objects. (Then handles and
   connections are changed).
 
-  position is not necessarly the corner of the object, but rather
+  position is not necessarily the corner of the object, but rather
   some 'good' spot on it which will be natural to snap to.
 */
 struct _DiaObject {
@@ -540,13 +555,13 @@ struct _DiaObject {
    *  should not be accessed directly, but through dia_object_get_bounding_box().
    *  Note that handles and connection points are not included by this, but
    *  added by that which needs it.
-   *  Internal:  If this is set to a 0x0 box, returns bounding_box.  That is for
+   *  Internal:  If this is set to a NULL, returns bounding_box.  That is for
    *  those objects that don't actually calculate it, but can just use the BB.
    *  Since handles and CPs are not in the BB, that will be the case for most
    *  objects.
    */
   Rectangle        *enclosing_box;
-  /*! Metainfo of the object, should not be manipulated directy. Use dia_object_set_meta() */
+  /*! Metainfo of the object, should not be manipulated directly. Use dia_object_set_meta() */
   GHashTable       *meta;
 };
 
diff --git a/lib/standard-path.c b/lib/standard-path.c
index 82036d2..0da1645 100644
--- a/lib/standard-path.c
+++ b/lib/standard-path.c
@@ -186,6 +186,7 @@ static DiaMenu *stdpath_get_object_menu(StdPath *stdpath,
                                        Point *clickedpoint);
 static void stdpath_get_props(StdPath *stdpath, GPtrArray *props);
 static void stdpath_set_props(StdPath *stdpath, GPtrArray *props);
+static gboolean stdpath_transform(StdPath *stdpath, const DiaMatrix *m);
 
 static ObjectOps stdpath_ops = {
   (DestroyFunc)         stdpath_destroy,
@@ -203,6 +204,7 @@ static ObjectOps stdpath_ops = {
   (SetPropsFunc)        stdpath_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       stdpath_transform,
 };
 
 /*!
@@ -1030,6 +1032,23 @@ stdpath_select (StdPath *stdpath, Point *clicked_point,
 {
   stdpath_update_handles (stdpath);
 }
+/*!
+ * \brief Change the object state regarding selection
+ * \memberof _StdPath
+ */
+static gboolean
+stdpath_transform(StdPath *stdpath, const DiaMatrix *m)
+{
+  int i;
+
+  g_return_val_if_fail (m != NULL, FALSE);
+
+  for (i = 0; i < stdpath->num_points; i++)
+    transform_bezpoint (&stdpath->points[i], m);
+
+  stdpath_update_data(stdpath);
+  return TRUE;
+}
 
 gboolean
 text_to_path (const Text *text, GArray *points)
diff --git a/objects/standard/bezier.c b/objects/standard/bezier.c
index caa64a1..005d503 100644
--- a/objects/standard/bezier.c
+++ b/objects/standard/bezier.c
@@ -85,6 +85,7 @@ static void bezierline_save(Bezierline *bezierline, ObjectNode obj_node,
                            DiaContext *ctx);
 static DiaObject *bezierline_load(ObjectNode obj_node, int version, DiaContext *ctx);
 static DiaMenu *bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint);
+static gboolean bezierline_transform(Bezierline *bezierline, const DiaMatrix *m);
 
 static void compute_gap_points(Bezierline *bezierline, Point *gap_points);
 static real approx_bez_length(BezierConn *bez);
@@ -129,6 +130,7 @@ static ObjectOps bezierline_ops = {
   (SetPropsFunc)        bezierline_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       bezierline_transform,
 };
 
 static PropNumData gap_range = { -G_MAXFLOAT, G_MAXFLOAT, 0.1};
@@ -812,3 +814,17 @@ bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint)
     (ctype != BEZ_CORNER_CUSP);
   return &bezierline_menu;
 }
+
+static gboolean
+bezierline_transform(Bezierline *bezierline, const DiaMatrix *m)
+{
+  int i;
+
+  g_return_val_if_fail (m != NULL, FALSE);
+
+  for (i = 0; i < bezierline->bez.bezier.num_points; i++)
+    transform_bezpoint (&bezierline->bez.bezier.points[i], m);
+
+  bezierline_update_data(bezierline);
+  return TRUE;
+}
diff --git a/objects/standard/beziergon.c b/objects/standard/beziergon.c
index 0ae7456..52d1b1a 100644
--- a/objects/standard/beziergon.c
+++ b/objects/standard/beziergon.c
@@ -88,6 +88,7 @@ static void beziergon_save(Beziergon *beziergon, ObjectNode obj_node,
 static DiaObject *beziergon_load(ObjectNode obj_node, int version, DiaContext *ctx);
 static DiaMenu *beziergon_get_object_menu(Beziergon *beziergon,
                                          Point *clickedpoint);
+static gboolean beziergon_transform(Beziergon *beziergon, const DiaMatrix *m);
 
 static ObjectTypeOps beziergon_type_ops =
 {
@@ -155,6 +156,7 @@ static ObjectOps beziergon_ops = {
   (SetPropsFunc)        beziergon_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       beziergon_transform,
 };
 
 static void
@@ -545,3 +547,17 @@ beziergon_get_object_menu(Beziergon *beziergon, Point *clickedpoint)
   beziergon_menu_items[1].active = beziergon->bezier.bezier.num_points > 3;
   return &beziergon_menu;
 }
+
+static gboolean
+beziergon_transform(Beziergon *beziergon, const DiaMatrix *m)
+{
+  int i;
+
+  g_return_val_if_fail (m != NULL, FALSE);
+
+  for (i = 0; i < beziergon->bezier.bezier.num_points; i++)
+    transform_bezpoint (&beziergon->bezier.bezier.points[i], m);
+
+  beziergon_update_data(beziergon);
+  return TRUE;
+}
diff --git a/objects/standard/box.c b/objects/standard/box.c
index 64dac85..e855d02 100644
--- a/objects/standard/box.c
+++ b/objects/standard/box.c
@@ -31,6 +31,7 @@
 #include "attributes.h"
 #include "properties.h"
 #include "create.h"
+#include "message.h"
 #include "pattern.h"
 
 #include "tool-icons.h"
@@ -69,6 +70,7 @@ struct _Box {
   real corner_radius;
   AspectType aspect;
   DiaPattern *pattern;
+  real angle; /*!< between [-45°-45°] to simplify connection point handling */
 };
 
 static struct _BoxProperties {
@@ -99,6 +101,7 @@ static void box_set_props(Box *box, GPtrArray *props);
 static void box_save(Box *box, ObjectNode obj_node, DiaContext *ctx);
 static DiaObject *box_load(ObjectNode obj_node, int version, DiaContext *ctx);
 static DiaMenu *box_get_object_menu(Box *box, Point *clickedpoint);
+static gboolean box_transform(Box *box, const DiaMatrix *m);
 
 static ObjectTypeOps box_type_ops =
 {
@@ -121,10 +124,12 @@ static PropOffset box_offsets[] = {
   { "line_join", PROP_TYPE_ENUM, offsetof(Box, line_join) },
   { "corner_radius", PROP_TYPE_REAL, offsetof(Box, corner_radius) },
   { "pattern", PROP_TYPE_PATTERN, offsetof(Box, pattern) },
+  { "angle", PROP_TYPE_REAL, offsetof(Box, angle) },
   { NULL, 0, 0 }
 };
 
 static PropNumData corner_radius_data = { 0.0, 10.0, 0.1 };
+static PropNumData angle_data = { -45, 45, 1 };
 
 static PropEnumData prop_aspect_data[] = {
   { N_("Free"), FREE_ASPECT },
@@ -145,6 +150,8 @@ static PropDescription box_props[] = {
   { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
     N_("Aspect ratio"), NULL, prop_aspect_data },
   PROP_STD_PATTERN,
+  { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
+    N_("Rotation"), N_("Rotation angle"), &angle_data },
   PROP_DESC_END
 };
 
@@ -179,6 +186,7 @@ static ObjectOps box_ops = {
   (SetPropsFunc)        box_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       box_transform,
 };
 
 static void
@@ -189,17 +197,50 @@ box_set_props(Box *box, GPtrArray *props)
   box_update_data(box);
 }
 
+static void
+_box_get_poly (const Box *box, Point corners[4])
+{
+  const Element *elem = &box->element;
+
+  corners[0] = elem->corner;
+  corners[1] = corners[0];
+  corners[1].x += elem->width;
+  corners[2] = corners[1];
+  corners[2].y += elem->height;
+  corners[3] = corners[2];
+  corners[3].x -= elem->width;
+
+  if (box->angle != 0) {
+    real cx = elem->corner.x + elem->width / 2.0;
+    real cy = elem->corner.y + elem->height / 2.0;
+    DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy };
+    DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy };
+    int i;
+
+    dia_matrix_set_angle_and_scales (&m, G_PI*box->angle/180, 1.0, 1.0);
+    dia_matrix_multiply (&m, &t, &m);
+    for (i = 0; i < 4; ++i)
+      transform_point (&corners[i], &m);
+  }
+}
+
 static real
 box_distance_from(Box *box, Point *point)
 {
   Element *elem = &box->element;
-  Rectangle rect;
 
-  rect.left = elem->corner.x - box->border_width/2;
-  rect.right = elem->corner.x + elem->width + box->border_width/2;
-  rect.top = elem->corner.y - box->border_width/2;
-  rect.bottom = elem->corner.y + elem->height + box->border_width/2;
-  return distance_rectangle_point(&rect, point);
+  if (box->angle == 0) {
+    Rectangle rect;
+    rect.left = elem->corner.x - box->border_width/2;
+    rect.right = elem->corner.x + elem->width + box->border_width/2;
+    rect.top = elem->corner.y - box->border_width/2;
+    rect.bottom = elem->corner.y + elem->height + box->border_width/2;
+    return distance_rectangle_point(&rect, point);
+  } else {
+    Point corners[4];
+    _box_get_poly (box, corners);
+    return distance_polygon_point (corners, 4, box->border_width, point);
+  }
 }
 
 static void
@@ -326,31 +367,28 @@ box_draw(Box *box, DiaRenderer *renderer)
       if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
         renderer_ops->set_pattern (renderer, box->pattern);
     }
-    /* we only want separate calls for potential pattern fill */
-    if (box->corner_radius > 0) {
+    if (box->angle == 0) {
       renderer_ops->draw_rounded_rect (renderer,
                                       &elem->corner, &lr_corner,
                                       &fill, &box->border_color,
                                       box->corner_radius);
     } else {
-      renderer_ops->draw_rect(renderer, 
-                              &elem->corner,
-                              &lr_corner, 
-                              &fill, &box->border_color);
+      Point poly[4];
+      _box_get_poly (box, poly);
+      renderer_ops->draw_polygon (renderer, poly, 4, &fill, &box->border_color);
     }
     if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
       renderer_ops->set_pattern (renderer, NULL);
   } else {
-    if (box->corner_radius > 0) {
+    if (box->angle == 0) {
       renderer_ops->draw_rounded_rect (renderer, 
                                       &elem->corner, &lr_corner,
                                       NULL, &box->border_color,
                                       box->corner_radius);
     } else {
-      renderer_ops->draw_rect (renderer, 
-                              &elem->corner,
-                              &lr_corner,
-                              NULL, &box->border_color);
+      Point poly[4];
+      _box_get_poly (box, poly);
+      renderer_ops->draw_polygon (renderer, poly, 4, &box->inner_color, &box->border_color);
     }
   }
 }
@@ -393,6 +431,18 @@ box_update_data(Box *box)
   box->connections[8].pos.x = elem->corner.x + elem->width / 2.0;
   box->connections[8].pos.y = elem->corner.y + elem->height / 2.0;
 
+  if (box->angle != 0) {
+    real cx = elem->corner.x + elem->width / 2.0;
+    real cy = elem->corner.y + elem->height / 2.0;
+    DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy };
+    DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy };
+    int i;
+
+    dia_matrix_set_angle_and_scales (&m, G_PI*box->angle/180, 1.0, 1.0);
+    dia_matrix_multiply (&m, &t, &m);
+    for (i = 0; i < 8; ++i)
+      transform_point (&box->connections[i].pos, &m);
+  }
   box->connections[0].directions = DIR_NORTH|DIR_WEST;
   box->connections[1].directions = DIR_NORTH;
   box->connections[2].directions = DIR_NORTH|DIR_EAST;
@@ -455,6 +505,7 @@ box_create(Point *startpoint,
   box->show_background = default_properties.show_background;
   box->corner_radius = default_properties.corner_radius;
   box->aspect = default_properties.aspect;
+  box->angle = 0.0;
 
   element_init(elem, 8, NUM_CONNECTIONS);
 
@@ -505,6 +556,7 @@ box_copy(Box *box)
   newbox->dashlength = box->dashlength;
   newbox->corner_radius = box->corner_radius;
   newbox->aspect = box->aspect;
+  newbox->angle = box->angle;
   if (box->pattern)
     newbox->pattern = g_object_ref (box->pattern);
 
@@ -563,6 +615,11 @@ box_save(Box *box, ObjectNode obj_node, DiaContext *ctx)
   if (box->pattern)
     data_add_pattern(new_attribute(obj_node, "pattern"),
                     box->pattern, ctx);
+
+  if (box->angle != 0.0)
+    data_add_real(new_attribute(obj_node, "angle"),
+                 box->angle, ctx);
+
 }
 
 static DiaObject *
@@ -632,6 +689,11 @@ box_load(ObjectNode obj_node, int version, DiaContext *ctx)
   if (attr != NULL)
     box->pattern = data_pattern(attribute_first_data(attr), ctx);
 
+  box->angle = 0.0;
+  attr = object_find_attribute(obj_node, "angle");
+  if (attr != NULL)
+    box->angle =  data_real(attribute_first_data(attr), ctx);
+
   element_init(elem, 8, NUM_CONNECTIONS);
 
   for (i=0;i<NUM_CONNECTIONS;i++) {
@@ -744,3 +806,33 @@ box_get_object_menu(Box *box, Point *clickedpoint)
   
   return &box_menu;
 }
+
+static gboolean
+box_transform(Box *box, const DiaMatrix *m)
+{
+  real a, sx, sy;
+
+  g_return_val_if_fail(m != NULL, FALSE);
+
+  if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
+    dia_log_message ("box_transform() can't convert given matrix");
+    return FALSE;
+  } else {
+    real width = box->element.width * sx;
+    real height = box->element.height * sy;
+    real angle = a*180/G_PI;
+    Point c = { box->element.corner.x + width/2.0, box->element.corner.y + height/2.0 };
+
+    /* rotation is invariant to the center */
+    transform_point (&c, m);
+    /* XXX: we have to bring angle in range [-45..45] which may swap width and height */
+    box->angle = angle;
+    box->element.width = width;
+    box->element.height = height;
+    box->element.corner.x = c.x - width / 2.0;
+    box->element.corner.y = c.y - height / 2.0;
+ }
+
+  box_update_data(box);
+  return TRUE;
+}
diff --git a/objects/standard/ellipse.c b/objects/standard/ellipse.c
index 1b4a409..7496463 100644
--- a/objects/standard/ellipse.c
+++ b/objects/standard/ellipse.c
@@ -31,6 +31,8 @@
 #include "attributes.h"
 #include "properties.h"
 #include "pattern.h"
+#include "diapathrenderer.h"
+#include "message.h"
 
 #include "tool-icons.h"
 
@@ -65,6 +67,7 @@ struct _Ellipse {
   LineStyle line_style;
   real dashlength;
   DiaPattern *pattern;
+  real angle; /*!< between [-45�-45�] to simplify connection point handling */
 };
 
 static struct _EllipseProperties {
@@ -93,6 +96,7 @@ static void ellipse_set_props(Ellipse *ellipse, GPtrArray *props);
 static void ellipse_save(Ellipse *ellipse, ObjectNode obj_node, DiaContext *ctx);
 static DiaObject *ellipse_load(ObjectNode obj_node, int version, DiaContext *ctx);
 static DiaMenu *ellipse_get_object_menu(Ellipse *ellipse, Point *clickedpoint);
+static gboolean ellipse_transform(Ellipse *ellipse, const DiaMatrix *m);
 
 static ObjectTypeOps ellipse_type_ops =
 {
@@ -113,9 +117,12 @@ static PropOffset ellipse_offsets[] = {
   { "line_style", PROP_TYPE_LINESTYLE,
     offsetof(Ellipse, line_style), offsetof(Ellipse, dashlength) },
   { "pattern", PROP_TYPE_PATTERN, offsetof(Ellipse, pattern) },
+  { "angle", PROP_TYPE_REAL, offsetof(Ellipse, angle) },
   { NULL, 0, 0 }
 };
 
+static PropNumData angle_data = { -45, 45, 1 };
+
 static PropEnumData prop_aspect_data[] = {
   { N_("Free"), FREE_ASPECT },
   { N_("Fixed"), FIXED_ASPECT },
@@ -132,6 +139,8 @@ static PropDescription ellipse_props[] = {
   { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
     N_("Aspect ratio"), NULL, prop_aspect_data },
   PROP_STD_PATTERN,
+  { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
+    N_("Rotation"), N_("Rotation angle"), &angle_data },
   PROP_DESC_END
 };
 
@@ -166,6 +175,7 @@ static ObjectOps ellipse_ops = {
   (SetPropsFunc)        ellipse_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       ellipse_transform,
 };
 
 static void
@@ -178,6 +188,24 @@ ellipse_set_props(Ellipse *ellipse, GPtrArray *props)
   ellipse_update_data(ellipse);
 }
 
+static GArray *
+_ellipse_to_path (Ellipse *ellipse, Point *center)
+{
+  Element *elem = &ellipse->element;
+  DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, center->x, center->y };
+  DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -center->x, -center->y };
+  GArray *path;
+  int i;
+
+  dia_matrix_set_angle_and_scales (&m, G_PI*ellipse->angle/180, 1.0, 1.0);
+  dia_matrix_multiply (&m, &t, &m);
+  path = g_array_new (FALSE, FALSE, sizeof(BezPoint));
+  path_build_ellipse (path, center, elem->width, elem->height);
+  for (i = 0; i < path->len; ++i)
+    transform_bezpoint (&g_array_index (path, BezPoint, i), &m);
+  return path;
+}
+
 static real
 ellipse_distance_from(Ellipse *ellipse, Point *point)
 {
@@ -187,6 +215,15 @@ ellipse_distance_from(Ellipse *ellipse, Point *point)
   center.x = elem->corner.x+elem->width/2;
   center.y = elem->corner.y+elem->height/2;
 
+  if (ellipse->angle != 0) {
+    real dist;
+    GArray *path = _ellipse_to_path (ellipse, &center);
+
+    dist = distance_bez_shape_point (&g_array_index (path, BezPoint, 0), path->len,
+                                    ellipse->border_width, point);
+    g_array_free (path, TRUE);
+    return dist;
+  }
   return distance_ellipse_point(&center, elem->width, elem->height,
                                ellipse->border_width, point);
 }
@@ -298,7 +335,8 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer)
   DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
   Point center;
   Element *elem;
-  
+  GArray *path = NULL;
+
   assert(ellipse != NULL);
   assert(renderer != NULL);
 
@@ -307,6 +345,9 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer)
   center.x = elem->corner.x + elem->width/2;
   center.y = elem->corner.y + elem->height/2;
 
+  if (ellipse->angle != 0)
+    path = _ellipse_to_path (ellipse, &center);
+
   renderer_ops->set_linewidth(renderer, ellipse->border_width);
   renderer_ops->set_linestyle(renderer, ellipse->line_style, ellipse->dashlength);
   if (ellipse->show_background) {
@@ -317,18 +358,30 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer)
       if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
         renderer_ops->set_pattern (renderer, ellipse->pattern);
     }
-    renderer_ops->draw_ellipse (renderer, 
-                               &center,
-                               elem->width, elem->height,
-                               &fill, &ellipse->border_color);
+    if (!path)
+      renderer_ops->draw_ellipse (renderer,
+                                 &center,
+                                 elem->width, elem->height,
+                                 &fill, &ellipse->border_color);
+    else
+      renderer_ops->draw_beziergon (renderer,
+                                   &g_array_index (path, BezPoint, 0), path->len,
+                                   &fill, &ellipse->border_color);
     if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
       renderer_ops->set_pattern (renderer, NULL);
   } else {
-    renderer_ops->draw_ellipse (renderer,
-                               &center,
-                               elem->width, elem->height,
-                               NULL, &ellipse->border_color);
+    if (!path)
+      renderer_ops->draw_ellipse (renderer,
+                                 &center,
+                                 elem->width, elem->height,
+                                 NULL, &ellipse->border_color);
+    else
+      renderer_ops->draw_beziergon (renderer,
+                                   &g_array_index (path, BezPoint, 0), path->len,
+                                   NULL, &ellipse->border_color);
   }
+  if (path)
+    g_array_free (path, TRUE);
 }
 
 static void
@@ -372,6 +425,16 @@ ellipse_update_data(Ellipse *ellipse)
   ellipse->connections[8].pos.x = center.x;
   ellipse->connections[8].pos.y = center.y;
 
+  if (ellipse->angle != 0) {
+    DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, center.x, center.y };
+    DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -center.x, -center.y };
+    int i;
+
+    dia_matrix_set_angle_and_scales (&m, G_PI*ellipse->angle/180, 1.0, 1.0);
+    dia_matrix_multiply (&m, &t, &m);
+    for (i = 0; i < 8; ++i)
+      transform_point (&ellipse->connections[i].pos, &m);
+  }
   /* Update directions -- if the ellipse is very thin, these may not be good */
   ellipse->connections[0].directions = DIR_NORTH|DIR_WEST;
   ellipse->connections[1].directions = DIR_NORTH;
@@ -476,6 +539,7 @@ ellipse_copy(Ellipse *ellipse)
   newellipse->dashlength = ellipse->dashlength;
   newellipse->show_background = ellipse->show_background;
   newellipse->aspect = ellipse->aspect;
+  newellipse->angle = ellipse->angle;
   newellipse->line_style = ellipse->line_style;
   if (ellipse->pattern)
     newellipse->pattern = g_object_ref (ellipse->pattern);
@@ -520,6 +584,9 @@ ellipse_save(Ellipse *ellipse, ObjectNode obj_node, DiaContext *ctx)
   if (ellipse->aspect != FREE_ASPECT)
     data_add_enum(new_attribute(obj_node, "aspect"),
                  ellipse->aspect, ctx);
+  if (ellipse->angle != 0.0)
+    data_add_real(new_attribute(obj_node, "angle"),
+                 ellipse->angle, ctx);
 
   if (ellipse->line_style != LINESTYLE_SOLID) {
     data_add_enum(new_attribute(obj_node, "line_style"),
@@ -577,6 +644,11 @@ static DiaObject *ellipse_load(ObjectNode obj_node, int version, DiaContext *ctx
   if (attr != NULL)
     ellipse->aspect = data_enum(attribute_first_data(attr), ctx);
 
+  ellipse->angle = 0.0;
+  attr = object_find_attribute(obj_node, "angle");
+  if (attr != NULL)
+    ellipse->angle =  data_real(attribute_first_data(attr), ctx);
+
   ellipse->line_style = LINESTYLE_SOLID;
   attr = object_find_attribute(obj_node, "line_style");
   if (attr != NULL)
@@ -708,3 +780,33 @@ ellipse_get_object_menu(Ellipse *ellipse, Point *clickedpoint)
   
   return &ellipse_menu;
 }
+
+static gboolean
+ellipse_transform(Ellipse *ellipse, const DiaMatrix *m)
+{
+  real a, sx, sy;
+
+  g_return_val_if_fail(m != NULL, FALSE);
+
+  if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
+    dia_log_message ("ellipse_transform() can't convert given matrix");
+    return FALSE;
+  } else {
+    real width = ellipse->element.width * sx;
+    real height = ellipse->element.height * sy;
+    real angle = a*180/G_PI;
+    Point c = { ellipse->element.corner.x + width/2.0, ellipse->element.corner.y + height/2.0 };
+
+    /* rotation is invariant to the center */
+    transform_point (&c, m);
+    /* XXX: we have to bring angle in range [-45..45] which may swap width and height */
+    ellipse->angle = angle;
+    ellipse->element.width = width;
+    ellipse->element.height = height;
+    ellipse->element.corner.x = c.x - width / 2.0;
+    ellipse->element.corner.y = c.y - height / 2.0;
+ }
+
+  ellipse_update_data(ellipse);
+  return TRUE;
+}
diff --git a/objects/standard/image.c b/objects/standard/image.c
index 4d42026..712190c 100644
--- a/objects/standard/image.c
+++ b/objects/standard/image.c
@@ -89,6 +89,7 @@ static ObjectChange* image_move_handle(Image *image, Handle *handle,
                                       HandleMoveReason reason, ModifierKeys modifiers);
 static ObjectChange* image_move(Image *image, Point *to);
 static void image_draw(Image *image, DiaRenderer *renderer);
+static gboolean image_transform(Image *image, const DiaMatrix *m);
 static void image_update_data(Image *image);
 static DiaObject *image_create(Point *startpoint,
                          void *user_data,
@@ -160,6 +161,7 @@ static ObjectOps image_ops = {
   (SetPropsFunc)        image_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       image_transform,
 };
 
 static PropOffset image_offsets[] = {
@@ -463,6 +465,34 @@ image_draw(Image *image, DiaRenderer *renderer)
   }
 }
 
+static gboolean
+image_transform(Image *image, const DiaMatrix *m)
+{
+  Element *elem = &image->element;
+  real a, sx, sy;
+
+  g_return_val_if_fail(m != NULL, FALSE);
+
+  if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
+    dia_log_message ("image_transform() can't convert given matrix");
+    return FALSE;
+  } else {
+    real width = elem->width * sx;
+    real height = elem->height * sy;
+    real angle = a*180/G_PI;
+    Point c = { elem->corner.x + width/2.0, elem->corner.y + height/2.0 };
+
+    /* rotation is invariant to the center */
+    transform_point (&c, m);
+    /* XXX: implement image rotate! */
+    elem->width = width;
+    elem->height = height;
+    elem->corner.x = c.x - width / 2.0;
+    elem->corner.y = c.y - height / 2.0;
+  }
+  return TRUE;
+}
+
 static void
 image_update_data(Image *image)
 {
diff --git a/objects/standard/line.c b/objects/standard/line.c
index 3627a38..53c5762 100644
--- a/objects/standard/line.c
+++ b/objects/standard/line.c
@@ -94,6 +94,7 @@ static void line_set_props(Line *line, GPtrArray *props);
 static void line_save(Line *line, ObjectNode obj_node, DiaContext *ctx);
 static DiaObject *line_load(ObjectNode obj_node, int version, DiaContext *ctx);
 static DiaMenu *line_get_object_menu(Line *line, Point *clickedpoint);
+static gboolean line_transform(Line *line, const DiaMatrix *m);
 
 void Line_adjust_for_absolute_gap(Line *line, Point *gap_endpoints);
 
@@ -181,6 +182,7 @@ static ObjectOps line_ops = {
   (SetPropsFunc)        line_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       line_transform,
 };
 
 static void
@@ -342,7 +344,19 @@ line_get_object_menu(Line *line, Point *clickedpoint)
   return &object_menu;
 }
 
+static gboolean
+line_transform(Line *line, const DiaMatrix *m)
+{
+  int i;
+
+  g_return_val_if_fail (m != NULL, FALSE);
+
+  for (i = 0; i < 2; i++)
+    transform_point (&line->connection.endpoints[i], m);
 
+  line_update_data(line);
+  return TRUE;
+}
 
 /*!
  * \brief Gap calculation for _Line
diff --git a/objects/standard/polygon.c b/objects/standard/polygon.c
index e4313dd..103ce79 100644
--- a/objects/standard/polygon.c
+++ b/objects/standard/polygon.c
@@ -90,6 +90,7 @@ static void polygon_save(Polygon *polygon, ObjectNode obj_node,
                         DiaContext *ctx);
 static DiaObject *polygon_load(ObjectNode obj_node, int version, DiaContext *ctx);
 static DiaMenu *polygon_get_object_menu(Polygon *polygon, Point *clickedpoint);
+static gboolean polygon_transform(Polygon *polygon, const DiaMatrix *m);
 
 static ObjectTypeOps polygon_type_ops =
 {
@@ -157,6 +158,7 @@ static ObjectOps polygon_ops = {
   (SetPropsFunc)        polygon_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       polygon_transform,
 };
 
 static void
@@ -500,3 +502,18 @@ polygon_get_object_menu(Polygon *polygon, Point *clickedpoint)
   polygon_menu_items[1].active = polygon->poly.numpoints > 3;
   return &polygon_menu;
 }
+
+static gboolean
+polygon_transform(Polygon *polygon, const DiaMatrix *m)
+{
+  PolyShape *poly = &polygon->poly;
+  int i;
+
+  g_return_val_if_fail (m != NULL, FALSE);
+
+  for (i = 0; i < poly->numpoints; i++)
+    transform_point (&poly->points[i], m);
+
+  polygon_update_data(polygon);
+  return TRUE;
+}
diff --git a/objects/standard/polyline.c b/objects/standard/polyline.c
index ce27995..80468ca 100644
--- a/objects/standard/polyline.c
+++ b/objects/standard/polyline.c
@@ -81,6 +81,7 @@ static DiaObject *polyline_load(ObjectNode obj_node, int version, DiaContext *ct
 static DiaMenu *polyline_get_object_menu(Polyline *polyline, Point *clickedpoint);
 void polyline_calculate_gap_endpoints(Polyline *polyline, Point *gap_endpoints);
 static void polyline_exchange_gap_points(Polyline *polyline,  Point *gap_points);
+static gboolean polyline_transform(Polyline *polyline, const DiaMatrix *m);
 
 static ObjectTypeOps polyline_type_ops =
 {
@@ -164,6 +165,7 @@ static ObjectOps polyline_ops = {
   (SetPropsFunc)        polyline_set_props,
   (TextEditFunc) 0,
   (ApplyPropertiesListFunc) object_apply_props,
+  (TransformFunc)       polyline_transform,
 };
 
 static void
@@ -638,3 +640,18 @@ polyline_get_object_menu(Polyline *polyline, Point *clickedpoint)
   polyline_menu_items[1].active = polyline->poly.numpoints > 2;
   return &polyline_menu;
 }
+
+static gboolean
+polyline_transform(Polyline *polyline, const DiaMatrix *m)
+{
+  PolyConn *poly = &polyline->poly;
+  int i;
+
+  g_return_val_if_fail (m != NULL, FALSE);
+
+  for (i = 0; i < poly->numpoints; i++)
+    transform_point (&poly->points[i], m);
+
+  polyline_update_data(polyline);
+  return TRUE;
+}
diff --git a/plug-ins/svg/svg-import.c b/plug-ins/svg/svg-import.c
index ff2edad..9c8783e 100644
--- a/plug-ins/svg/svg-import.c
+++ b/plug-ins/svg/svg-import.c
@@ -4,7 +4,7 @@
  *
  * svg-import.c: SVG import filter for dia
  * Copyright (C) 2002 Steffen Macke
- * Copyright (C) 2005 Hans Breuer
+ * Copyright (C) 2005-2014 Hans Breuer
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -216,7 +216,7 @@ _node_get_real (xmlNodePtr node, const char *name, real defval)
  * \ingroup SvgImport
  */
 static void
-use_position (DiaObject *obj, xmlNodePtr node)
+use_position (DiaObject *obj, xmlNodePtr node, DiaContext *ctx)
 {
     Point pos = {0, 0};
     xmlChar *str;
@@ -234,41 +234,29 @@ use_position (DiaObject *obj, xmlNodePtr node)
         DiaMatrix *m = dia_svg_parse_transform ((char *)str, user_scale);
 
        if (m) {
-           if (IS_GROUP (obj)) {
+           if (obj->ops->transform) {
              /* it is the only one transformation aware yet */
-             Group *grp = (Group *)obj;
-
-             group_transform (grp, m);
+             if (!obj->ops->transform (obj, m))
+               dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
+                                        obj->type->name);
            } else {
              GPtrArray *props = g_ptr_array_new ();
 
              PointProperty *pp;
              RealProperty  *pr;
-             PointarrayProperty *pap = NULL;
-             BezPointarrayProperty *bpap = NULL;
-             Property *prop = NULL;
 
              /* setting obj_pos is pointless, it is read-only in all objects */
              prop_list_add_point (props, "obj_pos", &pos); 
              prop_list_add_point (props, "elem_corner", &pos); 
              prop_list_add_real (props, "elem_width", 1.0);
              prop_list_add_real (props, "elem_height", 1.0);
-             prop_list_add_real (props, PROP_STDNAME_LINE_WIDTH, 0.1);
-
-             if ((prop = object_prop_by_name_type (obj, "bez_points", PROP_TYPE_BEZPOINTARRAY)) != NULL) {
-               prop_list_add_list (props, prop_list_from_single (prop));
-               bpap = g_ptr_array_index (props, 5);
-             } else if ((prop = object_prop_by_name_type (obj, "poly_points", PROP_TYPE_POINTARRAY)) != 
NULL) {
-               prop_list_add_list (props, prop_list_from_single (prop));
-               pap = g_ptr_array_index (props, 5);
-             }
 
              obj->ops->get_props (obj, props);
-             /* try to transform the object without the full matrix  */
+             /* XXX: try to transform the object without the full matrix  */
              pp = g_ptr_array_index (props, 0);
              pp->point_data.x +=  m->x0;
              pp->point_data.y +=  m->y0;
-             /* set position a second time, now for non-elements */
+             /* XXX: set position a second time, now for non-elements */
              pp = g_ptr_array_index (props, 1);
              pp->point_data.x +=  m->x0;
              pp->point_data.y +=  m->y0;
@@ -277,24 +265,8 @@ use_position (DiaObject *obj, xmlNodePtr node)
              pr->real_data *= m->xx;
              pr = g_ptr_array_index (props, 3);
              pr->real_data *= m->yy;
-             pr = g_ptr_array_index (props, 4);
-             pr->real_data *= m->yy;
-
-             if (bpap) {
-               GArray *data = bpap->bezpointarray_data;
-               int i;
-               for (i = 0; i < data->len; ++i)
-                 transform_bezpoint (&g_array_index(data, BezPoint, i), m);
-             } else if (pap) {
-               GArray *data = pap->pointarray_data;
-               int i;
-               for (i = 0; i < data->len; ++i)
-                 transform_point (&g_array_index(data, Point, i), m);
-             }
 
              obj->ops->set_props (obj, props);
-             if (prop)
-               prop->ops->free (prop);
              prop_list_free (props);
            }
            g_free (m);
@@ -967,7 +939,7 @@ read_poly_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
 static GList *
 read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
                 GHashTable *style_ht, GHashTable *pattern_ht,
-                GList *list) 
+                GList *list, DiaContext *ctx)
 {
   xmlChar *str;
   real width, height;
@@ -995,15 +967,6 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
     width = height = get_value_as_cm((char *) str, NULL)*2;
     xmlFree(str);
   }
-  if (matrix) {
-    /* TODO: transform angle - when it is supported */
-    Point wh = {width, height};
-    transform_point (&wh, matrix);
-    width = wh.x;
-    height = wh.y;
-    transform_point (&start, matrix);
-    g_free (matrix);
-  }
   /* A negative value is an error [...]. A value of zero disables rendering of the element. */
   if (width <= 0.0 || height <= 0.0)
     return list;
@@ -1014,6 +977,13 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
   props = make_element_props(start.x-(width/2), start.y-(height/2),
                             width, height);
   new_obj->ops->set_props(new_obj, props);
+  if (matrix) {
+    g_return_val_if_fail (new_obj->ops->transform, list);
+    if (!new_obj->ops->transform (new_obj, matrix))
+      dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
+                              new_obj->type->name);
+    g_free (matrix);
+  }
   prop_list_free(props);
   return g_list_append (list, new_obj);
 }
@@ -1025,7 +995,7 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
 static GList *
 read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
              GHashTable *style_ht, GHashTable *pattern_ht,
-             GList *list) 
+             GList *list, DiaContext *ctx)
 {
   xmlChar *str;
   DiaObjectType *otype = object_get_type("Standard - Line");
@@ -1047,12 +1017,6 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
   end.x = _node_get_real (node, "x2", start.x);
   end.y = _node_get_real (node, "y2", start.y);
 
-  if (matrix) {
-    transform_point (&start, matrix);
-    transform_point (&end, matrix);
-    g_free (matrix);
-  }
-
   new_obj = otype->ops->create(&start, otype->default_user_data,
                                 &h1, &h2);
   
@@ -1072,6 +1036,14 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
 
   apply_style(new_obj, node, parent_style, style_ht, pattern_ht, TRUE);
 
+  if (matrix) {
+    g_return_val_if_fail (new_obj->ops->transform, list);
+    if (!new_obj->ops->transform (new_obj, matrix))
+      dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
+                              new_obj->type->name);
+    g_free (matrix);
+  }
+
   return g_list_append (list, new_obj);
 }
 
@@ -1082,7 +1054,7 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
 static GList *
 read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
              GHashTable *style_ht, GHashTable *pattern_ht,
-             GList *list) 
+             GList *list, DiaContext *ctx)
 {
   xmlChar *str;
   real width, height;
@@ -1126,19 +1098,12 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
   end.x = start.x + width;
   end.y = start.y + height;
 
-  if (matrix) {
-    /* TODO: for rotated rects we would need to create a polygon */
-    transform_point (&start, matrix);
-    transform_point (&end, matrix);
-    g_free (matrix);
-    width = end.x - start.x;
-    height = end.y - start.y;
-  }
   /* A negative value is an error [...]. A value of zero disables rendering of the element. */
   if (width <= 0.0 || height <= 0.0)
     return list; /* just ignore it w/o much complaints */
   new_obj = otype->ops->create(&start, otype->default_user_data,
                                 &h1, &h2);
+
   list = g_list_append (list, new_obj);
   props = prop_list_from_descs(svg_rect_prop_descs, pdtpp_true);
   g_assert(props->len == 3);
@@ -1160,6 +1125,13 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
   apply_style(new_obj, node, parent_style, style_ht, pattern_ht, TRUE);
   prop_list_free(props);
 
+  if (matrix) {
+    g_return_val_if_fail (new_obj->ops->transform != NULL, list);
+    if (!new_obj->ops->transform (new_obj, matrix))
+      dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
+                              new_obj->type->name);
+    g_free (matrix);
+  }
   return list;
 }
 
@@ -1171,7 +1143,7 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
 static GList *
 read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
               GHashTable *style_ht, GHashTable *pattern_ht,
-              GList *list, const gchar *filename_svg)
+              GList *list, const gchar *filename_svg, DiaContext *ctx)
 {
   xmlChar *str;
   real x, y, width, height;
@@ -1191,21 +1163,6 @@ read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
 
   /* TODO: aspect ratio? */
 
-  if (matrix) {
-    /* TODO: transform angle - when it is supported */
-    Point xy = {x, y};
-    Point wh = {width, height};
-
-    transform_point (&xy, matrix);
-    transform_point (&wh, matrix);
-    width = wh.x;
-    height = wh.y;
-    x = xy.x;
-    y = xy.y;
-
-    g_free (matrix);
-  }
-
   str = xmlGetNsProp (node, (const xmlChar *)"href", (const xmlChar *)"http://www.w3.org/1999/xlink";);
   if (str) {
     if (strncmp ((char *)str, "data:image/", 11) == 0) {
@@ -1265,6 +1222,13 @@ read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
     xmlFree(str);
   }
 
+  if (matrix) {
+    g_return_val_if_fail (new_obj->ops->transform, list);
+    if (!new_obj->ops->transform (new_obj, matrix))
+      dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
+                              new_obj->type->name);
+    g_free (matrix);
+  }
   if (new_obj)
     return g_list_append (list, new_obj);
 
@@ -1629,15 +1593,15 @@ read_items (xmlNodePtr   startnode,
       else if (moreitems)
        obj = g_list_last(moreitems)->data;
     } else if (!xmlStrcmp(node->name, (const xmlChar *)"rect")) {
-      items = read_rect_svg(node, parent_gs, style_ht, pattern_ht, items);
+      items = read_rect_svg(node, parent_gs, style_ht, pattern_ht, items, ctx);
       if (items)
        obj = g_list_last(items)->data;
     } else if (!xmlStrcmp(node->name, (const xmlChar *)"line")) {
-      items = read_line_svg(node, parent_gs, style_ht, pattern_ht, items);
+      items = read_line_svg(node, parent_gs, style_ht, pattern_ht, items, ctx);
       if (items)
        obj = g_list_last(items)->data;
     } else if (!xmlStrcmp(node->name, (const xmlChar *)"ellipse") || !xmlStrcmp(node->name, (const xmlChar 
*)"circle")) {
-      items = read_ellipse_svg(node, parent_gs, style_ht, pattern_ht, items);
+      items = read_ellipse_svg(node, parent_gs, style_ht, pattern_ht, items, ctx);
       if (items)
        obj = g_list_last(items)->data;
     } else if (!xmlStrcmp(node->name, (const xmlChar *)"polyline")) {
@@ -1659,7 +1623,7 @@ read_items (xmlNodePtr   startnode,
       if (items && g_list_nth(items, first))
        obj = g_list_nth(items, first)->data;
     } else if(!xmlStrcmp(node->name, (const xmlChar *)"image")) {
-      items = read_image_svg(node, parent_gs, style_ht, pattern_ht, items, filename_svg);
+      items = read_image_svg(node, parent_gs, style_ht, pattern_ht, items, filename_svg, ctx);
       if (items)
        obj = g_list_last(items)->data;
     } else if(!xmlStrcmp(node->name, (const xmlChar *)"linearGradient") ||
@@ -1680,7 +1644,7 @@ read_items (xmlNodePtr   startnode,
           * be target for meta info, comment or something. */
          obj = otemp->ops->copy (otemp);
 
-         use_position (obj, node);
+         use_position (obj, node, ctx);
          /* this should only be styled from the containing group,
           * if it has no style on it's own. Sorry Dia can't create
           * objects w/o style so we have two options beside complete
diff --git a/samples/transform-variations.svg b/samples/transform-variations.svg
new file mode 100644
index 0000000..68e822e
--- /dev/null
+++ b/samples/transform-variations.svg
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1"
+       xmlns="http://www.w3.org/2000/svg";
+       xmlns:xlink="http://www.w3.org/1999/xlink";
+       x="0" y="0" width="28cm" height="28cm" viewBox="0, -5, 280, 275"
+       stroke-width="2"
+       text-anchor="middle" font-size="6" font-family="sans-serif">
+  <text x="210" y="30" text-anchor="start">Standard - Box</text>
+  <text x="210" y="40" text-anchor="start">Standard - Line</text>
+  <text x="210" y="80" text-anchor="start">Standard - Ellipse</text>
+  <text x="210" y="90" text-anchor="start">Standard - Text</text>
+  <text x="210" y="130" text-anchor="start">Standard - Polygon</text>
+  <text x="210" y="140" text-anchor="start">Standard - Polyline</text>
+  <text x="210" y="180" text-anchor="start">Standard - Beziergon</text>
+  <text x="210" y="190" text-anchor="start">Standard - Bezierline</text>
+  <text x="210" y="230" text-anchor="start">Standard - Image</text>
+
+  <text x="30" y="0" >Reference</text>
+  <!-- to be used below and directly -->
+  <rect id="box" x="18" y="10" width="24" height="40" fill="yellow" stroke="red"/>
+  <line id="line" x1="10" y1="30" x2="50" y2="30" stroke="red"/>
+  <ellipse id="ell" cx="30" cy="80" rx="20" ry="15" fill="lime" stroke="blue"/>
+  <text id="txt" x="30" y="80" text-anchor="middle" fill="blue">Rotate!</text>
+  <polygon id="pg1" points="30,110,50,145,10,145" fill="lightblue"/>
+  <polyline id="pl1" points="30,110,50,145,10,145" stroke="green" fill="none"/>
+  <path id="pc1" d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160z" fill="cyan"/>
+  <path id="po1" d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160" fill="none" stroke="gold"/>
+  <image id="img" x="10" y="210" width="40" height="40"
+       
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAACbCAYAAABWOrhZAAAABHNCSVQICAgIfAhkiAAABDBJREFU&#10;eJzt3V2SEkEQReFsw0d34C5diLt0B763L4PBMMBt6Kyqm1XnezNCsYc+ZP9QAxGAsCU8xp7wGM9k&#10;bCNO+DZ6A+CPSCARCaTv6Y949gyFMxA7TBJIRAKJSCARCSQigUQkkIgEEpFAIhJIRAKJSCARCSQi&#10;gUQkkIgEUv56EtaDTIdJAolIIBEJpJ5nELerXzl7KYJJAolIIBEJJCKBRCSQiAQSkUAiEkhEAolI&#10;IN3eGm/90VZ4zvKtCiKpYWg8RFJP92CIpLYuwbBUYJzMF2TT55JIPGQF0+Q55RLYwxbGH7xMJF6y&#10;QkmNhUj82E0VIvFlM1WIxFvWieipUIjE3/DDD5Hc+Pvrp+sNxWGhEMmVSyCE8hmR1NM9FCL5cDs9&#10;jKdJlsM/H5HU1PUtDSKJx1PDfJp0O+wQSW1dQlk+EjUtzKdJRIdQlo/kiAKhNLV0JBPt/KbTZOlI&#10;XjFRUC9bNpIJd3qzabJsJO+YMKxDloxk4p3dZJosF8nZQCYO7KHlIllA+jRZKpIjU+DH7z/ySV5t&#10;miwVCd6zTCSvTJEJpknqIWeZSMw4B/bFEpG8cy7SYZqk/xJVK0tE0lLCYcc+lukjOXNFc2SaJMqO&#10;JW3bp4/krAEnsU6TZY+YPJKs+yKD2MQybSSZr+7Bl8TDY5k2kiNcpsi+77HvsoNhsUwZSYvDTI9p&#10;4hrLyI/DwodHYWzbod3z7C9lPOfblJNkFi6ThUgg5X/DONIkHG5SjIwk/YfrdV/k6Anqk//r6b93&#10;ieNimsNNzxtnrS6dt207EkjWJx8dNk0kvWVeErvGcTHFOUnx2+/K8O1mkpzQ+Ab
 
bsMlxq/wkmXCK&#10;2G1r6UnisM40cZrYTI4rW8QEk0QpMEVabR8fG+50mHlzmjhOjrvKRlJciTguSn4pksO5yLs6Hv7O&#10;Pkf/t5NJMqe1v++m8hSJqLn95SKZQeNQUhYaXf+hVCQVX4UzKBXJTIyD/3JiXSYS4yfVybrf5jlr&#10;IIY/193L85L3SXBX+gnrRYlJAqlZIBFEggOIpL6mUySCSKprHkgEkeAAIqmryxSJIJKqugUSQSQV&#10;dQ0kgkiq6R5IBJFUMiSQCCKpYlggEURSwdBAIhb4vZvCst4hPv1GKpF4Gj49rhGJF5vpcY1IPFjG&#10;cUEkY1nH8ejB3ZbT4bkuq/uYJDV1XfpJJHUMWxNMJL5sFoqzWh4St+UhEQkkIoFEJJCIBBKRQCIS&#10;SEQCiUggEQmkrFvjrZYYcOveQOZOyA6FQExwuIGU/WotsRwPr2m+nkR9Q/bBrzfFQNmHm4w9TjVm&#10;mp+TPJsUTJEaWkRyZs9TjaEuVzf3JgZTpI5WkbxTANWY6naf5HpyMEVqaRnJKyVQjbHWO+fozTUi&#10;MdZj56hQCMQc791AGv0dtUyRApgkkPhdYEhMEtjZg09TKodJAgAAHPwDyEsX4l8AdBUAAAAASUVO&#10;RK5CYII="/>
+
+  <text x="80" y="0">Element rotate</text>
+  <!-- transform object directly - must not have copied id="...", otherwise Dia would
+       translate the wrong object (not the first of id, but the last) -->
+  <rect x="18" y="10" width="24" height="40" fill="yellow" stroke="red"
+       transform="translate(80,30) rotate(30) translate(-30,-30)"/>
+  <line x1="10" y1="30" x2="50" y2="30" stroke="red"
+       transform="translate(80,30) rotate(30) translate(-30,-30)"/>
+  <ellipse cx="30" cy="80" rx="20" ry="15" fill="lime" stroke="blue"
+       transform="translate(80,80) rotate(30) translate(-30,-80)"/>
+  <text x="30" y="80" text-anchor="middle" fill="blue"
+       transform="translate(80,80) rotate(30) translate(-30,-80)">Rotate!</text>
+  <polygon points="30,110,50,145,10,145" fill="lightblue"
+       transform="translate(80,130)rotate(30)translate(-30,-130)"/>
+  <polyline points="30,110,50,145,10,145" fill="none" stroke="green"
+       transform="translate(80,130) rotate(30) translate(-30,-130)"/>
+  <path d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160z" fill="cyan"
+       transform="translate(80,180) rotate(30) translate(-30,-180)"/>
+  <path d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160" fill="none" stroke="gold"
+       transform="translate(80,180) rotate(30) translate(-30,-180)"/>
+  <image x="10" y="210" width="40" height="40"
+       transform="translate(80,230) rotate(30) translate(-30,-230)"
+       
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIkAAACbCAYAAABWOrhZAAAABHNCSVQICAgIfAhkiAAABDBJREFU&#10;eJzt3V2SEkEQReFsw0d34C5diLt0B763L4PBMMBt6Kyqm1XnezNCsYc+ZP9QAxGAsCU8xp7wGM9k&#10;bCNO+DZ6A+CPSCARCaTv6Y949gyFMxA7TBJIRAKJSCARCSQigUQkkIgEEpFAIhJIRAKJSCARCSQi&#10;gUQkkIgEUv56EtaDTIdJAolIIBEJpJ5nELerXzl7KYJJAolIIBEJJCKBRCSQiAQSkUAiEkhEAolI&#10;IN3eGm/90VZ4zvKtCiKpYWg8RFJP92CIpLYuwbBUYJzMF2TT55JIPGQF0+Q55RLYwxbGH7xMJF6y&#10;QkmNhUj82E0VIvFlM1WIxFvWieipUIjE3/DDD5Hc+Pvrp+sNxWGhEMmVSyCE8hmR1NM9FCL5cDs9&#10;jKdJlsM/H5HU1PUtDSKJx1PDfJp0O+wQSW1dQlk+EjUtzKdJRIdQlo/kiAKhNLV0JBPt/KbTZOlI&#10;XjFRUC9bNpIJd3qzabJsJO+YMKxDloxk4p3dZJosF8nZQCYO7KHlIllA+jRZKpIjU+DH7z/ySV5t&#10;miwVCd6zTCSvTJEJpknqIWeZSMw4B/bFEpG8cy7SYZqk/xJVK0tE0lLCYcc+lukjOXNFc2SaJMqO&#10;JW3bp4/krAEnsU6TZY+YPJKs+yKD2MQybSSZr+7Bl8TDY5k2kiNcpsi+77HvsoNhsUwZSYvDTI9p&#10;4hrLyI/DwodHYWzbod3z7C9lPOfblJNkFi6ThUgg5X/DONIkHG5SjIwk/YfrdV/k6Anqk//r6b93&#10;ieNimsNNzxtnrS6dt207EkjWJx8dNk0kvWVeErvGcTHFOUnx2+/K8O1mkpzQ+Ab
 
bsMlxq/wkmXCK&#10;2G1r6UnisM40cZrYTI4rW8QEk0QpMEVabR8fG+50mHlzmjhOjrvKRlJciTguSn4pksO5yLs6Hv7O&#10;Pkf/t5NJMqe1v++m8hSJqLn95SKZQeNQUhYaXf+hVCQVX4UzKBXJTIyD/3JiXSYS4yfVybrf5jlr&#10;IIY/193L85L3SXBX+gnrRYlJAqlZIBFEggOIpL6mUySCSKprHkgEkeAAIqmryxSJIJKqugUSQSQV&#10;dQ0kgkiq6R5IBJFUMiSQCCKpYlggEURSwdBAIhb4vZvCst4hPv1GKpF4Gj49rhGJF5vpcY1IPFjG&#10;cUEkY1nH8ejB3ZbT4bkuq/uYJDV1XfpJJHUMWxNMJL5sFoqzWh4St+UhEQkkIoFEJJCIBBKRQCIS&#10;SEQCiUggEQmkrFvjrZYYcOveQOZOyA6FQExwuIGU/WotsRwPr2m+nkR9Q/bBrzfFQNmHm4w9TjVm&#10;mp+TPJsUTJEaWkRyZs9TjaEuVzf3JgZTpI5WkbxTANWY6naf5HpyMEVqaRnJKyVQjbHWO+fozTUi&#10;MdZj56hQCMQc791AGv0dtUyRApgkkPhdYEhMEtjZg09TKodJAgAAHPwDyEsX4l8AdBUAAAAASUVO&#10;RK5CYII="/>
+
+  <text x="130" y="0">Group rotate</text>
+  <!-- just shift with use, transform by group -->
+  <g transform="translate(130,30)rotate(45)translate(-130,-30)">
+    <use x="100" y="0" xlink:href="#box"/>
+    <use x="100" y="0" xlink:href="#line"/>
+  </g>
+  <g transform="translate(130,80)rotate(45)translate(-130,-80)">
+    <use x="100" y="0" xlink:href="#ell"/>
+    <use x="100" y="0" xlink:href="#txt"/>
+  </g>
+  <g transform="translate(130,130)rotate(45)translate(-130,-130)">
+    <use x="100" y="0" xlink:href="#pg1"/>
+    <use x="100" y="0" xlink:href="#pl1"/>
+  </g>
+  <g transform="translate(130,180)rotate(45)translate(-130,-180)">
+    <use x="100" y="0" xlink:href="#pc1"/>
+    <use x="100" y="0" xlink:href="#po1"/>
+  </g>
+  <g transform="translate(130,230)rotate(45)translate(-130,-230)">
+    <use x="100" y="0" xlink:href="#img"/>
+  </g>
+
+  <text x="180" y="0">Use rotate</text>
+  <!-- transformed use, shifted by group -->
+  <g transform="translate(180,30)">
+    <use x="-30" y="-30" xlink:href="#box" transform="rotate(60)"/>
+    <use x="-30" y="-30" xlink:href="#line" transform="rotate(60)"/>
+  </g>
+  <g transform="translate(180,80)">
+    <use x="-30" y="-80" xlink:href="#ell" transform="rotate(60)"/>
+    <use x="-30" y="-80" xlink:href="#txt" transform="rotate(60)"/>
+  </g>
+  <g transform="translate(180,130)">
+    <use x="-30" y="-130" xlink:href="#pg1" transform="rotate(60)"/>
+    <use x="-30" y="-130" xlink:href="#pl1" transform="rotate(60)"/>
+  </g>
+  <g transform="translate(180,180)">
+    <use x="-30" y="-180" xlink:href="#pc1" transform="rotate(60)"/>
+    <use x="-30" y="-180" xlink:href="#po1" transform="rotate(60)"/>
+  </g>
+  <g transform="translate(180,230)">
+    <use x="-30" y="-230" xlink:href="#img" transform="rotate(60)"/>
+  </g>
+</svg>


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