• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
Keine Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

作図ソフト dia の改良版


Commit MetaInfo

Revisiond5d5b662517bbf7c67cc8832b545ba8d47b80833 (tree)
Zeit2014-10-02 04:39:29
AutorHans Breuer <hans@breu...>
CommiterHans Breuer

Log Message

[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.

Ändern Zusammenfassung

Diff

--- a/lib/group.c
+++ b/lib/group.c
@@ -68,6 +68,7 @@ static DiaObject *group_copy(Group *group);
6868 static const PropDescription *group_describe_props(Group *group);
6969 static void group_get_props(Group *group, GPtrArray *props);
7070 static void group_set_props(Group *group, GPtrArray *props);
71+static void group_transform (Group *group, const DiaMatrix *m);
7172
7273 static ObjectOps group_ops = {
7374 (DestroyFunc) group_destroy,
@@ -85,6 +86,7 @@ static ObjectOps group_ops = {
8586 (SetPropsFunc) group_set_props,
8687 (TextEditFunc) 0,
8788 (ApplyPropertiesListFunc) group_apply_properties_list,
89+ (TransformFunc) group_transform
8890 };
8991
9092 DiaObjectType group_type = {
@@ -888,7 +890,7 @@ group_prop_change_free(GroupPropChange *change)
888890 g_list_free(change->changes_per_object);
889891 }
890892
891-void
893+static void
892894 group_transform (Group *group, const DiaMatrix *m)
893895 {
894896 g_return_if_fail (m != NULL);
--- a/lib/group.h
+++ b/lib/group.h
@@ -33,7 +33,6 @@ DiaObject *group_create_with_matrix(GList *objects, DiaMatrix *matrix);
3333 GList *group_objects(DiaObject *group);
3434
3535 void group_destroy_shallow(DiaObject *group);
36-void group_transform (Group *group, const DiaMatrix *mat);
3736 const DiaMatrix *group_get_transform (Group *group);
3837
3938 #define IS_GROUP(obj) ((obj)->type == &group_type)
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -300,6 +300,7 @@ EXPORTS
300300 pixbuf_decode_base64
301301
302302 dia_path_renderer_get_type
303+ path_build_ellipse
303304
304305 dia_pattern_new
305306 dia_pattern_add_color
@@ -491,7 +492,6 @@ EXPORTS
491492 group_destroy_shallow
492493 group_objects
493494 group_type
494- group_transform
495495
496496 get_active_focus
497497 give_focus
--- a/lib/object.h
+++ b/lib/object.h
@@ -403,6 +403,20 @@ typedef DiaMenu *(*ObjectMenuFunc) (DiaObject* obj, Point *position);
403403 */
404404 typedef gboolean (*TextEditFunc) (DiaObject *obj, Text *text, TextEditState state, gchar *textchange);
405405
406+/*!
407+ * \brief Transform the object with the given matrix
408+ *
409+ * This function - if not null - will apply the transformation matrix to the
410+ * object. It should be implemented for every standard object, because it's
411+ * main use-case is the support of transformations from SVG.
412+ *
413+ * @param obj Explicit this pointer
414+ * @param m The transformation matrix
415+ * @returns TRUE if the matrix can be applied to the object, FALSE otherwise.
416+ * \public \memberof _DiaObject
417+ */
418+typedef gboolean (*TransformFunc) (DiaObject *obj, const DiaMatrix *m);
419+
406420 /*************************************
407421 ** The functions provided in object.c
408422 *************************************/
@@ -479,14 +493,15 @@ struct _ObjectOps {
479493 TextEditFunc edit_text;
480494
481495 ApplyPropertiesListFunc apply_properties_list;
482-
496+ /*! check for NULL before calling */
497+ TransformFunc transform;
483498 /*!
484499 Unused places (for extension).
485500 These should be NULL for now. In the future they might be used.
486501 Then an older object will be binary compatible, because all new code
487502 checks if new ops are supported (!= NULL)
488503 */
489- void (*(unused[4]))(DiaObject *obj,...);
504+ void (*(unused[3]))(DiaObject *obj,...);
490505 };
491506
492507 /*!
@@ -498,7 +513,7 @@ struct _ObjectOps {
498513 when connection objects. (Then handles and
499514 connections are changed).
500515
501- position is not necessarly the corner of the object, but rather
516+ position is not necessarily the corner of the object, but rather
502517 some 'good' spot on it which will be natural to snap to.
503518 */
504519 struct _DiaObject {
@@ -540,13 +555,13 @@ struct _DiaObject {
540555 * should not be accessed directly, but through dia_object_get_bounding_box().
541556 * Note that handles and connection points are not included by this, but
542557 * added by that which needs it.
543- * Internal: If this is set to a 0x0 box, returns bounding_box. That is for
558+ * Internal: If this is set to a NULL, returns bounding_box. That is for
544559 * those objects that don't actually calculate it, but can just use the BB.
545560 * Since handles and CPs are not in the BB, that will be the case for most
546561 * objects.
547562 */
548563 Rectangle *enclosing_box;
549- /*! Metainfo of the object, should not be manipulated directy. Use dia_object_set_meta() */
564+ /*! Metainfo of the object, should not be manipulated directly. Use dia_object_set_meta() */
550565 GHashTable *meta;
551566 };
552567
--- a/lib/standard-path.c
+++ b/lib/standard-path.c
@@ -186,6 +186,7 @@ static DiaMenu *stdpath_get_object_menu(StdPath *stdpath,
186186 Point *clickedpoint);
187187 static void stdpath_get_props(StdPath *stdpath, GPtrArray *props);
188188 static void stdpath_set_props(StdPath *stdpath, GPtrArray *props);
189+static gboolean stdpath_transform(StdPath *stdpath, const DiaMatrix *m);
189190
190191 static ObjectOps stdpath_ops = {
191192 (DestroyFunc) stdpath_destroy,
@@ -203,6 +204,7 @@ static ObjectOps stdpath_ops = {
203204 (SetPropsFunc) stdpath_set_props,
204205 (TextEditFunc) 0,
205206 (ApplyPropertiesListFunc) object_apply_props,
207+ (TransformFunc) stdpath_transform,
206208 };
207209
208210 /*!
@@ -1030,6 +1032,23 @@ stdpath_select (StdPath *stdpath, Point *clicked_point,
10301032 {
10311033 stdpath_update_handles (stdpath);
10321034 }
1035+/*!
1036+ * \brief Change the object state regarding selection
1037+ * \memberof _StdPath
1038+ */
1039+static gboolean
1040+stdpath_transform(StdPath *stdpath, const DiaMatrix *m)
1041+{
1042+ int i;
1043+
1044+ g_return_val_if_fail (m != NULL, FALSE);
1045+
1046+ for (i = 0; i < stdpath->num_points; i++)
1047+ transform_bezpoint (&stdpath->points[i], m);
1048+
1049+ stdpath_update_data(stdpath);
1050+ return TRUE;
1051+}
10331052
10341053 gboolean
10351054 text_to_path (const Text *text, GArray *points)
--- a/objects/standard/bezier.c
+++ b/objects/standard/bezier.c
@@ -85,6 +85,7 @@ static void bezierline_save(Bezierline *bezierline, ObjectNode obj_node,
8585 DiaContext *ctx);
8686 static DiaObject *bezierline_load(ObjectNode obj_node, int version, DiaContext *ctx);
8787 static DiaMenu *bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint);
88+static gboolean bezierline_transform(Bezierline *bezierline, const DiaMatrix *m);
8889
8990 static void compute_gap_points(Bezierline *bezierline, Point *gap_points);
9091 static real approx_bez_length(BezierConn *bez);
@@ -129,6 +130,7 @@ static ObjectOps bezierline_ops = {
129130 (SetPropsFunc) bezierline_set_props,
130131 (TextEditFunc) 0,
131132 (ApplyPropertiesListFunc) object_apply_props,
133+ (TransformFunc) bezierline_transform,
132134 };
133135
134136 static PropNumData gap_range = { -G_MAXFLOAT, G_MAXFLOAT, 0.1};
@@ -812,3 +814,17 @@ bezierline_get_object_menu(Bezierline *bezierline, Point *clickedpoint)
812814 (ctype != BEZ_CORNER_CUSP);
813815 return &bezierline_menu;
814816 }
817+
818+static gboolean
819+bezierline_transform(Bezierline *bezierline, const DiaMatrix *m)
820+{
821+ int i;
822+
823+ g_return_val_if_fail (m != NULL, FALSE);
824+
825+ for (i = 0; i < bezierline->bez.bezier.num_points; i++)
826+ transform_bezpoint (&bezierline->bez.bezier.points[i], m);
827+
828+ bezierline_update_data(bezierline);
829+ return TRUE;
830+}
--- a/objects/standard/beziergon.c
+++ b/objects/standard/beziergon.c
@@ -88,6 +88,7 @@ static void beziergon_save(Beziergon *beziergon, ObjectNode obj_node,
8888 static DiaObject *beziergon_load(ObjectNode obj_node, int version, DiaContext *ctx);
8989 static DiaMenu *beziergon_get_object_menu(Beziergon *beziergon,
9090 Point *clickedpoint);
91+static gboolean beziergon_transform(Beziergon *beziergon, const DiaMatrix *m);
9192
9293 static ObjectTypeOps beziergon_type_ops =
9394 {
@@ -155,6 +156,7 @@ static ObjectOps beziergon_ops = {
155156 (SetPropsFunc) beziergon_set_props,
156157 (TextEditFunc) 0,
157158 (ApplyPropertiesListFunc) object_apply_props,
159+ (TransformFunc) beziergon_transform,
158160 };
159161
160162 static void
@@ -545,3 +547,17 @@ beziergon_get_object_menu(Beziergon *beziergon, Point *clickedpoint)
545547 beziergon_menu_items[1].active = beziergon->bezier.bezier.num_points > 3;
546548 return &beziergon_menu;
547549 }
550+
551+static gboolean
552+beziergon_transform(Beziergon *beziergon, const DiaMatrix *m)
553+{
554+ int i;
555+
556+ g_return_val_if_fail (m != NULL, FALSE);
557+
558+ for (i = 0; i < beziergon->bezier.bezier.num_points; i++)
559+ transform_bezpoint (&beziergon->bezier.bezier.points[i], m);
560+
561+ beziergon_update_data(beziergon);
562+ return TRUE;
563+}
--- a/objects/standard/box.c
+++ b/objects/standard/box.c
@@ -31,6 +31,7 @@
3131 #include "attributes.h"
3232 #include "properties.h"
3333 #include "create.h"
34+#include "message.h"
3435 #include "pattern.h"
3536
3637 #include "tool-icons.h"
@@ -69,6 +70,7 @@ struct _Box {
6970 real corner_radius;
7071 AspectType aspect;
7172 DiaPattern *pattern;
73+ real angle; /*!< between [-45°-45°] to simplify connection point handling */
7274 };
7375
7476 static struct _BoxProperties {
@@ -99,6 +101,7 @@ static void box_set_props(Box *box, GPtrArray *props);
99101 static void box_save(Box *box, ObjectNode obj_node, DiaContext *ctx);
100102 static DiaObject *box_load(ObjectNode obj_node, int version, DiaContext *ctx);
101103 static DiaMenu *box_get_object_menu(Box *box, Point *clickedpoint);
104+static gboolean box_transform(Box *box, const DiaMatrix *m);
102105
103106 static ObjectTypeOps box_type_ops =
104107 {
@@ -121,10 +124,12 @@ static PropOffset box_offsets[] = {
121124 { "line_join", PROP_TYPE_ENUM, offsetof(Box, line_join) },
122125 { "corner_radius", PROP_TYPE_REAL, offsetof(Box, corner_radius) },
123126 { "pattern", PROP_TYPE_PATTERN, offsetof(Box, pattern) },
127+ { "angle", PROP_TYPE_REAL, offsetof(Box, angle) },
124128 { NULL, 0, 0 }
125129 };
126130
127131 static PropNumData corner_radius_data = { 0.0, 10.0, 0.1 };
132+static PropNumData angle_data = { -45, 45, 1 };
128133
129134 static PropEnumData prop_aspect_data[] = {
130135 { N_("Free"), FREE_ASPECT },
@@ -145,6 +150,8 @@ static PropDescription box_props[] = {
145150 { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
146151 N_("Aspect ratio"), NULL, prop_aspect_data },
147152 PROP_STD_PATTERN,
153+ { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
154+ N_("Rotation"), N_("Rotation angle"), &angle_data },
148155 PROP_DESC_END
149156 };
150157
@@ -179,6 +186,7 @@ static ObjectOps box_ops = {
179186 (SetPropsFunc) box_set_props,
180187 (TextEditFunc) 0,
181188 (ApplyPropertiesListFunc) object_apply_props,
189+ (TransformFunc) box_transform,
182190 };
183191
184192 static void
@@ -189,17 +197,50 @@ box_set_props(Box *box, GPtrArray *props)
189197 box_update_data(box);
190198 }
191199
200+static void
201+_box_get_poly (const Box *box, Point corners[4])
202+{
203+ const Element *elem = &box->element;
204+
205+ corners[0] = elem->corner;
206+ corners[1] = corners[0];
207+ corners[1].x += elem->width;
208+ corners[2] = corners[1];
209+ corners[2].y += elem->height;
210+ corners[3] = corners[2];
211+ corners[3].x -= elem->width;
212+
213+ if (box->angle != 0) {
214+ real cx = elem->corner.x + elem->width / 2.0;
215+ real cy = elem->corner.y + elem->height / 2.0;
216+ DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy };
217+ DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy };
218+ int i;
219+
220+ dia_matrix_set_angle_and_scales (&m, G_PI*box->angle/180, 1.0, 1.0);
221+ dia_matrix_multiply (&m, &t, &m);
222+ for (i = 0; i < 4; ++i)
223+ transform_point (&corners[i], &m);
224+ }
225+}
226+
192227 static real
193228 box_distance_from(Box *box, Point *point)
194229 {
195230 Element *elem = &box->element;
196- Rectangle rect;
197231
198- rect.left = elem->corner.x - box->border_width/2;
199- rect.right = elem->corner.x + elem->width + box->border_width/2;
200- rect.top = elem->corner.y - box->border_width/2;
201- rect.bottom = elem->corner.y + elem->height + box->border_width/2;
202- return distance_rectangle_point(&rect, point);
232+ if (box->angle == 0) {
233+ Rectangle rect;
234+ rect.left = elem->corner.x - box->border_width/2;
235+ rect.right = elem->corner.x + elem->width + box->border_width/2;
236+ rect.top = elem->corner.y - box->border_width/2;
237+ rect.bottom = elem->corner.y + elem->height + box->border_width/2;
238+ return distance_rectangle_point(&rect, point);
239+ } else {
240+ Point corners[4];
241+ _box_get_poly (box, corners);
242+ return distance_polygon_point (corners, 4, box->border_width, point);
243+ }
203244 }
204245
205246 static void
@@ -326,31 +367,28 @@ box_draw(Box *box, DiaRenderer *renderer)
326367 if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
327368 renderer_ops->set_pattern (renderer, box->pattern);
328369 }
329- /* we only want separate calls for potential pattern fill */
330- if (box->corner_radius > 0) {
370+ if (box->angle == 0) {
331371 renderer_ops->draw_rounded_rect (renderer,
332372 &elem->corner, &lr_corner,
333373 &fill, &box->border_color,
334374 box->corner_radius);
335375 } else {
336- renderer_ops->draw_rect(renderer,
337- &elem->corner,
338- &lr_corner,
339- &fill, &box->border_color);
376+ Point poly[4];
377+ _box_get_poly (box, poly);
378+ renderer_ops->draw_polygon (renderer, poly, 4, &fill, &box->border_color);
340379 }
341380 if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
342381 renderer_ops->set_pattern (renderer, NULL);
343382 } else {
344- if (box->corner_radius > 0) {
383+ if (box->angle == 0) {
345384 renderer_ops->draw_rounded_rect (renderer,
346385 &elem->corner, &lr_corner,
347386 NULL, &box->border_color,
348387 box->corner_radius);
349388 } else {
350- renderer_ops->draw_rect (renderer,
351- &elem->corner,
352- &lr_corner,
353- NULL, &box->border_color);
389+ Point poly[4];
390+ _box_get_poly (box, poly);
391+ renderer_ops->draw_polygon (renderer, poly, 4, &box->inner_color, &box->border_color);
354392 }
355393 }
356394 }
@@ -393,6 +431,18 @@ box_update_data(Box *box)
393431 box->connections[8].pos.x = elem->corner.x + elem->width / 2.0;
394432 box->connections[8].pos.y = elem->corner.y + elem->height / 2.0;
395433
434+ if (box->angle != 0) {
435+ real cx = elem->corner.x + elem->width / 2.0;
436+ real cy = elem->corner.y + elem->height / 2.0;
437+ DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, cx, cy };
438+ DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -cx, -cy };
439+ int i;
440+
441+ dia_matrix_set_angle_and_scales (&m, G_PI*box->angle/180, 1.0, 1.0);
442+ dia_matrix_multiply (&m, &t, &m);
443+ for (i = 0; i < 8; ++i)
444+ transform_point (&box->connections[i].pos, &m);
445+ }
396446 box->connections[0].directions = DIR_NORTH|DIR_WEST;
397447 box->connections[1].directions = DIR_NORTH;
398448 box->connections[2].directions = DIR_NORTH|DIR_EAST;
@@ -455,6 +505,7 @@ box_create(Point *startpoint,
455505 box->show_background = default_properties.show_background;
456506 box->corner_radius = default_properties.corner_radius;
457507 box->aspect = default_properties.aspect;
508+ box->angle = 0.0;
458509
459510 element_init(elem, 8, NUM_CONNECTIONS);
460511
@@ -505,6 +556,7 @@ box_copy(Box *box)
505556 newbox->dashlength = box->dashlength;
506557 newbox->corner_radius = box->corner_radius;
507558 newbox->aspect = box->aspect;
559+ newbox->angle = box->angle;
508560 if (box->pattern)
509561 newbox->pattern = g_object_ref (box->pattern);
510562
@@ -563,6 +615,11 @@ box_save(Box *box, ObjectNode obj_node, DiaContext *ctx)
563615 if (box->pattern)
564616 data_add_pattern(new_attribute(obj_node, "pattern"),
565617 box->pattern, ctx);
618+
619+ if (box->angle != 0.0)
620+ data_add_real(new_attribute(obj_node, "angle"),
621+ box->angle, ctx);
622+
566623 }
567624
568625 static DiaObject *
@@ -632,6 +689,11 @@ box_load(ObjectNode obj_node, int version, DiaContext *ctx)
632689 if (attr != NULL)
633690 box->pattern = data_pattern(attribute_first_data(attr), ctx);
634691
692+ box->angle = 0.0;
693+ attr = object_find_attribute(obj_node, "angle");
694+ if (attr != NULL)
695+ box->angle = data_real(attribute_first_data(attr), ctx);
696+
635697 element_init(elem, 8, NUM_CONNECTIONS);
636698
637699 for (i=0;i<NUM_CONNECTIONS;i++) {
@@ -744,3 +806,33 @@ box_get_object_menu(Box *box, Point *clickedpoint)
744806
745807 return &box_menu;
746808 }
809+
810+static gboolean
811+box_transform(Box *box, const DiaMatrix *m)
812+{
813+ real a, sx, sy;
814+
815+ g_return_val_if_fail(m != NULL, FALSE);
816+
817+ if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
818+ dia_log_message ("box_transform() can't convert given matrix");
819+ return FALSE;
820+ } else {
821+ real width = box->element.width * sx;
822+ real height = box->element.height * sy;
823+ real angle = a*180/G_PI;
824+ Point c = { box->element.corner.x + width/2.0, box->element.corner.y + height/2.0 };
825+
826+ /* rotation is invariant to the center */
827+ transform_point (&c, m);
828+ /* XXX: we have to bring angle in range [-45..45] which may swap width and height */
829+ box->angle = angle;
830+ box->element.width = width;
831+ box->element.height = height;
832+ box->element.corner.x = c.x - width / 2.0;
833+ box->element.corner.y = c.y - height / 2.0;
834+ }
835+
836+ box_update_data(box);
837+ return TRUE;
838+}
--- a/objects/standard/ellipse.c
+++ b/objects/standard/ellipse.c
@@ -31,6 +31,8 @@
3131 #include "attributes.h"
3232 #include "properties.h"
3333 #include "pattern.h"
34+#include "diapathrenderer.h"
35+#include "message.h"
3436
3537 #include "tool-icons.h"
3638
@@ -65,6 +67,7 @@ struct _Ellipse {
6567 LineStyle line_style;
6668 real dashlength;
6769 DiaPattern *pattern;
70+ real angle; /*!< between [-45ー-45ー] to simplify connection point handling */
6871 };
6972
7073 static struct _EllipseProperties {
@@ -93,6 +96,7 @@ static void ellipse_set_props(Ellipse *ellipse, GPtrArray *props);
9396 static void ellipse_save(Ellipse *ellipse, ObjectNode obj_node, DiaContext *ctx);
9497 static DiaObject *ellipse_load(ObjectNode obj_node, int version, DiaContext *ctx);
9598 static DiaMenu *ellipse_get_object_menu(Ellipse *ellipse, Point *clickedpoint);
99+static gboolean ellipse_transform(Ellipse *ellipse, const DiaMatrix *m);
96100
97101 static ObjectTypeOps ellipse_type_ops =
98102 {
@@ -113,9 +117,12 @@ static PropOffset ellipse_offsets[] = {
113117 { "line_style", PROP_TYPE_LINESTYLE,
114118 offsetof(Ellipse, line_style), offsetof(Ellipse, dashlength) },
115119 { "pattern", PROP_TYPE_PATTERN, offsetof(Ellipse, pattern) },
120+ { "angle", PROP_TYPE_REAL, offsetof(Ellipse, angle) },
116121 { NULL, 0, 0 }
117122 };
118123
124+static PropNumData angle_data = { -45, 45, 1 };
125+
119126 static PropEnumData prop_aspect_data[] = {
120127 { N_("Free"), FREE_ASPECT },
121128 { N_("Fixed"), FIXED_ASPECT },
@@ -132,6 +139,8 @@ static PropDescription ellipse_props[] = {
132139 { "aspect", PROP_TYPE_ENUM, PROP_FLAG_VISIBLE,
133140 N_("Aspect ratio"), NULL, prop_aspect_data },
134141 PROP_STD_PATTERN,
142+ { "angle", PROP_TYPE_REAL, PROP_FLAG_VISIBLE|PROP_FLAG_OPTIONAL,
143+ N_("Rotation"), N_("Rotation angle"), &angle_data },
135144 PROP_DESC_END
136145 };
137146
@@ -166,6 +175,7 @@ static ObjectOps ellipse_ops = {
166175 (SetPropsFunc) ellipse_set_props,
167176 (TextEditFunc) 0,
168177 (ApplyPropertiesListFunc) object_apply_props,
178+ (TransformFunc) ellipse_transform,
169179 };
170180
171181 static void
@@ -178,6 +188,24 @@ ellipse_set_props(Ellipse *ellipse, GPtrArray *props)
178188 ellipse_update_data(ellipse);
179189 }
180190
191+static GArray *
192+_ellipse_to_path (Ellipse *ellipse, Point *center)
193+{
194+ Element *elem = &ellipse->element;
195+ DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, center->x, center->y };
196+ DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -center->x, -center->y };
197+ GArray *path;
198+ int i;
199+
200+ dia_matrix_set_angle_and_scales (&m, G_PI*ellipse->angle/180, 1.0, 1.0);
201+ dia_matrix_multiply (&m, &t, &m);
202+ path = g_array_new (FALSE, FALSE, sizeof(BezPoint));
203+ path_build_ellipse (path, center, elem->width, elem->height);
204+ for (i = 0; i < path->len; ++i)
205+ transform_bezpoint (&g_array_index (path, BezPoint, i), &m);
206+ return path;
207+}
208+
181209 static real
182210 ellipse_distance_from(Ellipse *ellipse, Point *point)
183211 {
@@ -187,6 +215,15 @@ ellipse_distance_from(Ellipse *ellipse, Point *point)
187215 center.x = elem->corner.x+elem->width/2;
188216 center.y = elem->corner.y+elem->height/2;
189217
218+ if (ellipse->angle != 0) {
219+ real dist;
220+ GArray *path = _ellipse_to_path (ellipse, &center);
221+
222+ dist = distance_bez_shape_point (&g_array_index (path, BezPoint, 0), path->len,
223+ ellipse->border_width, point);
224+ g_array_free (path, TRUE);
225+ return dist;
226+ }
190227 return distance_ellipse_point(&center, elem->width, elem->height,
191228 ellipse->border_width, point);
192229 }
@@ -298,7 +335,8 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer)
298335 DiaRendererClass *renderer_ops = DIA_RENDERER_GET_CLASS (renderer);
299336 Point center;
300337 Element *elem;
301-
338+ GArray *path = NULL;
339+
302340 assert(ellipse != NULL);
303341 assert(renderer != NULL);
304342
@@ -307,6 +345,9 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer)
307345 center.x = elem->corner.x + elem->width/2;
308346 center.y = elem->corner.y + elem->height/2;
309347
348+ if (ellipse->angle != 0)
349+ path = _ellipse_to_path (ellipse, &center);
350+
310351 renderer_ops->set_linewidth(renderer, ellipse->border_width);
311352 renderer_ops->set_linestyle(renderer, ellipse->line_style, ellipse->dashlength);
312353 if (ellipse->show_background) {
@@ -317,18 +358,30 @@ ellipse_draw(Ellipse *ellipse, DiaRenderer *renderer)
317358 if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
318359 renderer_ops->set_pattern (renderer, ellipse->pattern);
319360 }
320- renderer_ops->draw_ellipse (renderer,
321- &center,
322- elem->width, elem->height,
323- &fill, &ellipse->border_color);
361+ if (!path)
362+ renderer_ops->draw_ellipse (renderer,
363+ &center,
364+ elem->width, elem->height,
365+ &fill, &ellipse->border_color);
366+ else
367+ renderer_ops->draw_beziergon (renderer,
368+ &g_array_index (path, BezPoint, 0), path->len,
369+ &fill, &ellipse->border_color);
324370 if (renderer_ops->is_capable_to(renderer, RENDER_PATTERN))
325371 renderer_ops->set_pattern (renderer, NULL);
326372 } else {
327- renderer_ops->draw_ellipse (renderer,
328- &center,
329- elem->width, elem->height,
330- NULL, &ellipse->border_color);
373+ if (!path)
374+ renderer_ops->draw_ellipse (renderer,
375+ &center,
376+ elem->width, elem->height,
377+ NULL, &ellipse->border_color);
378+ else
379+ renderer_ops->draw_beziergon (renderer,
380+ &g_array_index (path, BezPoint, 0), path->len,
381+ NULL, &ellipse->border_color);
331382 }
383+ if (path)
384+ g_array_free (path, TRUE);
332385 }
333386
334387 static void
@@ -372,6 +425,16 @@ ellipse_update_data(Ellipse *ellipse)
372425 ellipse->connections[8].pos.x = center.x;
373426 ellipse->connections[8].pos.y = center.y;
374427
428+ if (ellipse->angle != 0) {
429+ DiaMatrix m = { 1.0, 0.0, 0.0, 1.0, center.x, center.y };
430+ DiaMatrix t = { 1.0, 0.0, 0.0, 1.0, -center.x, -center.y };
431+ int i;
432+
433+ dia_matrix_set_angle_and_scales (&m, G_PI*ellipse->angle/180, 1.0, 1.0);
434+ dia_matrix_multiply (&m, &t, &m);
435+ for (i = 0; i < 8; ++i)
436+ transform_point (&ellipse->connections[i].pos, &m);
437+ }
375438 /* Update directions -- if the ellipse is very thin, these may not be good */
376439 ellipse->connections[0].directions = DIR_NORTH|DIR_WEST;
377440 ellipse->connections[1].directions = DIR_NORTH;
@@ -476,6 +539,7 @@ ellipse_copy(Ellipse *ellipse)
476539 newellipse->dashlength = ellipse->dashlength;
477540 newellipse->show_background = ellipse->show_background;
478541 newellipse->aspect = ellipse->aspect;
542+ newellipse->angle = ellipse->angle;
479543 newellipse->line_style = ellipse->line_style;
480544 if (ellipse->pattern)
481545 newellipse->pattern = g_object_ref (ellipse->pattern);
@@ -520,6 +584,9 @@ ellipse_save(Ellipse *ellipse, ObjectNode obj_node, DiaContext *ctx)
520584 if (ellipse->aspect != FREE_ASPECT)
521585 data_add_enum(new_attribute(obj_node, "aspect"),
522586 ellipse->aspect, ctx);
587+ if (ellipse->angle != 0.0)
588+ data_add_real(new_attribute(obj_node, "angle"),
589+ ellipse->angle, ctx);
523590
524591 if (ellipse->line_style != LINESTYLE_SOLID) {
525592 data_add_enum(new_attribute(obj_node, "line_style"),
@@ -577,6 +644,11 @@ static DiaObject *ellipse_load(ObjectNode obj_node, int version, DiaContext *ctx
577644 if (attr != NULL)
578645 ellipse->aspect = data_enum(attribute_first_data(attr), ctx);
579646
647+ ellipse->angle = 0.0;
648+ attr = object_find_attribute(obj_node, "angle");
649+ if (attr != NULL)
650+ ellipse->angle = data_real(attribute_first_data(attr), ctx);
651+
580652 ellipse->line_style = LINESTYLE_SOLID;
581653 attr = object_find_attribute(obj_node, "line_style");
582654 if (attr != NULL)
@@ -708,3 +780,33 @@ ellipse_get_object_menu(Ellipse *ellipse, Point *clickedpoint)
708780
709781 return &ellipse_menu;
710782 }
783+
784+static gboolean
785+ellipse_transform(Ellipse *ellipse, const DiaMatrix *m)
786+{
787+ real a, sx, sy;
788+
789+ g_return_val_if_fail(m != NULL, FALSE);
790+
791+ if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
792+ dia_log_message ("ellipse_transform() can't convert given matrix");
793+ return FALSE;
794+ } else {
795+ real width = ellipse->element.width * sx;
796+ real height = ellipse->element.height * sy;
797+ real angle = a*180/G_PI;
798+ Point c = { ellipse->element.corner.x + width/2.0, ellipse->element.corner.y + height/2.0 };
799+
800+ /* rotation is invariant to the center */
801+ transform_point (&c, m);
802+ /* XXX: we have to bring angle in range [-45..45] which may swap width and height */
803+ ellipse->angle = angle;
804+ ellipse->element.width = width;
805+ ellipse->element.height = height;
806+ ellipse->element.corner.x = c.x - width / 2.0;
807+ ellipse->element.corner.y = c.y - height / 2.0;
808+ }
809+
810+ ellipse_update_data(ellipse);
811+ return TRUE;
812+}
--- a/objects/standard/image.c
+++ b/objects/standard/image.c
@@ -89,6 +89,7 @@ static ObjectChange* image_move_handle(Image *image, Handle *handle,
8989 HandleMoveReason reason, ModifierKeys modifiers);
9090 static ObjectChange* image_move(Image *image, Point *to);
9191 static void image_draw(Image *image, DiaRenderer *renderer);
92+static gboolean image_transform(Image *image, const DiaMatrix *m);
9293 static void image_update_data(Image *image);
9394 static DiaObject *image_create(Point *startpoint,
9495 void *user_data,
@@ -160,6 +161,7 @@ static ObjectOps image_ops = {
160161 (SetPropsFunc) image_set_props,
161162 (TextEditFunc) 0,
162163 (ApplyPropertiesListFunc) object_apply_props,
164+ (TransformFunc) image_transform,
163165 };
164166
165167 static PropOffset image_offsets[] = {
@@ -463,6 +465,34 @@ image_draw(Image *image, DiaRenderer *renderer)
463465 }
464466 }
465467
468+static gboolean
469+image_transform(Image *image, const DiaMatrix *m)
470+{
471+ Element *elem = &image->element;
472+ real a, sx, sy;
473+
474+ g_return_val_if_fail(m != NULL, FALSE);
475+
476+ if (!dia_matrix_get_angle_and_scales (m, &a, &sx, &sy)) {
477+ dia_log_message ("image_transform() can't convert given matrix");
478+ return FALSE;
479+ } else {
480+ real width = elem->width * sx;
481+ real height = elem->height * sy;
482+ real angle = a*180/G_PI;
483+ Point c = { elem->corner.x + width/2.0, elem->corner.y + height/2.0 };
484+
485+ /* rotation is invariant to the center */
486+ transform_point (&c, m);
487+ /* XXX: implement image rotate! */
488+ elem->width = width;
489+ elem->height = height;
490+ elem->corner.x = c.x - width / 2.0;
491+ elem->corner.y = c.y - height / 2.0;
492+ }
493+ return TRUE;
494+}
495+
466496 static void
467497 image_update_data(Image *image)
468498 {
--- a/objects/standard/line.c
+++ b/objects/standard/line.c
@@ -94,6 +94,7 @@ static void line_set_props(Line *line, GPtrArray *props);
9494 static void line_save(Line *line, ObjectNode obj_node, DiaContext *ctx);
9595 static DiaObject *line_load(ObjectNode obj_node, int version, DiaContext *ctx);
9696 static DiaMenu *line_get_object_menu(Line *line, Point *clickedpoint);
97+static gboolean line_transform(Line *line, const DiaMatrix *m);
9798
9899 void Line_adjust_for_absolute_gap(Line *line, Point *gap_endpoints);
99100
@@ -181,6 +182,7 @@ static ObjectOps line_ops = {
181182 (SetPropsFunc) line_set_props,
182183 (TextEditFunc) 0,
183184 (ApplyPropertiesListFunc) object_apply_props,
185+ (TransformFunc) line_transform,
184186 };
185187
186188 static void
@@ -342,7 +344,19 @@ line_get_object_menu(Line *line, Point *clickedpoint)
342344 return &object_menu;
343345 }
344346
347+static gboolean
348+line_transform(Line *line, const DiaMatrix *m)
349+{
350+ int i;
351+
352+ g_return_val_if_fail (m != NULL, FALSE);
353+
354+ for (i = 0; i < 2; i++)
355+ transform_point (&line->connection.endpoints[i], m);
345356
357+ line_update_data(line);
358+ return TRUE;
359+}
346360
347361 /*!
348362 * \brief Gap calculation for _Line
--- a/objects/standard/polygon.c
+++ b/objects/standard/polygon.c
@@ -90,6 +90,7 @@ static void polygon_save(Polygon *polygon, ObjectNode obj_node,
9090 DiaContext *ctx);
9191 static DiaObject *polygon_load(ObjectNode obj_node, int version, DiaContext *ctx);
9292 static DiaMenu *polygon_get_object_menu(Polygon *polygon, Point *clickedpoint);
93+static gboolean polygon_transform(Polygon *polygon, const DiaMatrix *m);
9394
9495 static ObjectTypeOps polygon_type_ops =
9596 {
@@ -157,6 +158,7 @@ static ObjectOps polygon_ops = {
157158 (SetPropsFunc) polygon_set_props,
158159 (TextEditFunc) 0,
159160 (ApplyPropertiesListFunc) object_apply_props,
161+ (TransformFunc) polygon_transform,
160162 };
161163
162164 static void
@@ -500,3 +502,18 @@ polygon_get_object_menu(Polygon *polygon, Point *clickedpoint)
500502 polygon_menu_items[1].active = polygon->poly.numpoints > 3;
501503 return &polygon_menu;
502504 }
505+
506+static gboolean
507+polygon_transform(Polygon *polygon, const DiaMatrix *m)
508+{
509+ PolyShape *poly = &polygon->poly;
510+ int i;
511+
512+ g_return_val_if_fail (m != NULL, FALSE);
513+
514+ for (i = 0; i < poly->numpoints; i++)
515+ transform_point (&poly->points[i], m);
516+
517+ polygon_update_data(polygon);
518+ return TRUE;
519+}
--- 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
8181 static DiaMenu *polyline_get_object_menu(Polyline *polyline, Point *clickedpoint);
8282 void polyline_calculate_gap_endpoints(Polyline *polyline, Point *gap_endpoints);
8383 static void polyline_exchange_gap_points(Polyline *polyline, Point *gap_points);
84+static gboolean polyline_transform(Polyline *polyline, const DiaMatrix *m);
8485
8586 static ObjectTypeOps polyline_type_ops =
8687 {
@@ -164,6 +165,7 @@ static ObjectOps polyline_ops = {
164165 (SetPropsFunc) polyline_set_props,
165166 (TextEditFunc) 0,
166167 (ApplyPropertiesListFunc) object_apply_props,
168+ (TransformFunc) polyline_transform,
167169 };
168170
169171 static void
@@ -638,3 +640,18 @@ polyline_get_object_menu(Polyline *polyline, Point *clickedpoint)
638640 polyline_menu_items[1].active = polyline->poly.numpoints > 2;
639641 return &polyline_menu;
640642 }
643+
644+static gboolean
645+polyline_transform(Polyline *polyline, const DiaMatrix *m)
646+{
647+ PolyConn *poly = &polyline->poly;
648+ int i;
649+
650+ g_return_val_if_fail (m != NULL, FALSE);
651+
652+ for (i = 0; i < poly->numpoints; i++)
653+ transform_point (&poly->points[i], m);
654+
655+ polyline_update_data(polyline);
656+ return TRUE;
657+}
--- a/plug-ins/svg/svg-import.c
+++ b/plug-ins/svg/svg-import.c
@@ -4,7 +4,7 @@
44 *
55 * svg-import.c: SVG import filter for dia
66 * Copyright (C) 2002 Steffen Macke
7- * Copyright (C) 2005 Hans Breuer
7+ * Copyright (C) 2005-2014 Hans Breuer
88 *
99 * This program is free software; you can redistribute it and/or modify
1010 * 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)
216216 * \ingroup SvgImport
217217 */
218218 static void
219-use_position (DiaObject *obj, xmlNodePtr node)
219+use_position (DiaObject *obj, xmlNodePtr node, DiaContext *ctx)
220220 {
221221 Point pos = {0, 0};
222222 xmlChar *str;
@@ -234,41 +234,29 @@ use_position (DiaObject *obj, xmlNodePtr node)
234234 DiaMatrix *m = dia_svg_parse_transform ((char *)str, user_scale);
235235
236236 if (m) {
237- if (IS_GROUP (obj)) {
237+ if (obj->ops->transform) {
238238 /* it is the only one transformation aware yet */
239- Group *grp = (Group *)obj;
240-
241- group_transform (grp, m);
239+ if (!obj->ops->transform (obj, m))
240+ dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
241+ obj->type->name);
242242 } else {
243243 GPtrArray *props = g_ptr_array_new ();
244244
245245 PointProperty *pp;
246246 RealProperty *pr;
247- PointarrayProperty *pap = NULL;
248- BezPointarrayProperty *bpap = NULL;
249- Property *prop = NULL;
250247
251248 /* setting obj_pos is pointless, it is read-only in all objects */
252249 prop_list_add_point (props, "obj_pos", &pos);
253250 prop_list_add_point (props, "elem_corner", &pos);
254251 prop_list_add_real (props, "elem_width", 1.0);
255252 prop_list_add_real (props, "elem_height", 1.0);
256- prop_list_add_real (props, PROP_STDNAME_LINE_WIDTH, 0.1);
257-
258- if ((prop = object_prop_by_name_type (obj, "bez_points", PROP_TYPE_BEZPOINTARRAY)) != NULL) {
259- prop_list_add_list (props, prop_list_from_single (prop));
260- bpap = g_ptr_array_index (props, 5);
261- } else if ((prop = object_prop_by_name_type (obj, "poly_points", PROP_TYPE_POINTARRAY)) != NULL) {
262- prop_list_add_list (props, prop_list_from_single (prop));
263- pap = g_ptr_array_index (props, 5);
264- }
265253
266254 obj->ops->get_props (obj, props);
267- /* try to transform the object without the full matrix */
255+ /* XXX: try to transform the object without the full matrix */
268256 pp = g_ptr_array_index (props, 0);
269257 pp->point_data.x += m->x0;
270258 pp->point_data.y += m->y0;
271- /* set position a second time, now for non-elements */
259+ /* XXX: set position a second time, now for non-elements */
272260 pp = g_ptr_array_index (props, 1);
273261 pp->point_data.x += m->x0;
274262 pp->point_data.y += m->y0;
@@ -277,24 +265,8 @@ use_position (DiaObject *obj, xmlNodePtr node)
277265 pr->real_data *= m->xx;
278266 pr = g_ptr_array_index (props, 3);
279267 pr->real_data *= m->yy;
280- pr = g_ptr_array_index (props, 4);
281- pr->real_data *= m->yy;
282-
283- if (bpap) {
284- GArray *data = bpap->bezpointarray_data;
285- int i;
286- for (i = 0; i < data->len; ++i)
287- transform_bezpoint (&g_array_index(data, BezPoint, i), m);
288- } else if (pap) {
289- GArray *data = pap->pointarray_data;
290- int i;
291- for (i = 0; i < data->len; ++i)
292- transform_point (&g_array_index(data, Point, i), m);
293- }
294268
295269 obj->ops->set_props (obj, props);
296- if (prop)
297- prop->ops->free (prop);
298270 prop_list_free (props);
299271 }
300272 g_free (m);
@@ -967,7 +939,7 @@ read_poly_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
967939 static GList *
968940 read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
969941 GHashTable *style_ht, GHashTable *pattern_ht,
970- GList *list)
942+ GList *list, DiaContext *ctx)
971943 {
972944 xmlChar *str;
973945 real width, height;
@@ -995,15 +967,6 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
995967 width = height = get_value_as_cm((char *) str, NULL)*2;
996968 xmlFree(str);
997969 }
998- if (matrix) {
999- /* TODO: transform angle - when it is supported */
1000- Point wh = {width, height};
1001- transform_point (&wh, matrix);
1002- width = wh.x;
1003- height = wh.y;
1004- transform_point (&start, matrix);
1005- g_free (matrix);
1006- }
1007970 /* A negative value is an error [...]. A value of zero disables rendering of the element. */
1008971 if (width <= 0.0 || height <= 0.0)
1009972 return list;
@@ -1014,6 +977,13 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
1014977 props = make_element_props(start.x-(width/2), start.y-(height/2),
1015978 width, height);
1016979 new_obj->ops->set_props(new_obj, props);
980+ if (matrix) {
981+ g_return_val_if_fail (new_obj->ops->transform, list);
982+ if (!new_obj->ops->transform (new_obj, matrix))
983+ dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
984+ new_obj->type->name);
985+ g_free (matrix);
986+ }
1017987 prop_list_free(props);
1018988 return g_list_append (list, new_obj);
1019989 }
@@ -1025,7 +995,7 @@ read_ellipse_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
1025995 static GList *
1026996 read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
1027997 GHashTable *style_ht, GHashTable *pattern_ht,
1028- GList *list)
998+ GList *list, DiaContext *ctx)
1029999 {
10301000 xmlChar *str;
10311001 DiaObjectType *otype = object_get_type("Standard - Line");
@@ -1047,12 +1017,6 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
10471017 end.x = _node_get_real (node, "x2", start.x);
10481018 end.y = _node_get_real (node, "y2", start.y);
10491019
1050- if (matrix) {
1051- transform_point (&start, matrix);
1052- transform_point (&end, matrix);
1053- g_free (matrix);
1054- }
1055-
10561020 new_obj = otype->ops->create(&start, otype->default_user_data,
10571021 &h1, &h2);
10581022
@@ -1072,6 +1036,14 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
10721036
10731037 apply_style(new_obj, node, parent_style, style_ht, pattern_ht, TRUE);
10741038
1039+ if (matrix) {
1040+ g_return_val_if_fail (new_obj->ops->transform, list);
1041+ if (!new_obj->ops->transform (new_obj, matrix))
1042+ dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
1043+ new_obj->type->name);
1044+ g_free (matrix);
1045+ }
1046+
10751047 return g_list_append (list, new_obj);
10761048 }
10771049
@@ -1082,7 +1054,7 @@ read_line_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
10821054 static GList *
10831055 read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
10841056 GHashTable *style_ht, GHashTable *pattern_ht,
1085- GList *list)
1057+ GList *list, DiaContext *ctx)
10861058 {
10871059 xmlChar *str;
10881060 real width, height;
@@ -1126,19 +1098,12 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
11261098 end.x = start.x + width;
11271099 end.y = start.y + height;
11281100
1129- if (matrix) {
1130- /* TODO: for rotated rects we would need to create a polygon */
1131- transform_point (&start, matrix);
1132- transform_point (&end, matrix);
1133- g_free (matrix);
1134- width = end.x - start.x;
1135- height = end.y - start.y;
1136- }
11371101 /* A negative value is an error [...]. A value of zero disables rendering of the element. */
11381102 if (width <= 0.0 || height <= 0.0)
11391103 return list; /* just ignore it w/o much complaints */
11401104 new_obj = otype->ops->create(&start, otype->default_user_data,
11411105 &h1, &h2);
1106+
11421107 list = g_list_append (list, new_obj);
11431108 props = prop_list_from_descs(svg_rect_prop_descs, pdtpp_true);
11441109 g_assert(props->len == 3);
@@ -1160,6 +1125,13 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
11601125 apply_style(new_obj, node, parent_style, style_ht, pattern_ht, TRUE);
11611126 prop_list_free(props);
11621127
1128+ if (matrix) {
1129+ g_return_val_if_fail (new_obj->ops->transform != NULL, list);
1130+ if (!new_obj->ops->transform (new_obj, matrix))
1131+ dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
1132+ new_obj->type->name);
1133+ g_free (matrix);
1134+ }
11631135 return list;
11641136 }
11651137
@@ -1171,7 +1143,7 @@ read_rect_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
11711143 static GList *
11721144 read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
11731145 GHashTable *style_ht, GHashTable *pattern_ht,
1174- GList *list, const gchar *filename_svg)
1146+ GList *list, const gchar *filename_svg, DiaContext *ctx)
11751147 {
11761148 xmlChar *str;
11771149 real x, y, width, height;
@@ -1191,21 +1163,6 @@ read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
11911163
11921164 /* TODO: aspect ratio? */
11931165
1194- if (matrix) {
1195- /* TODO: transform angle - when it is supported */
1196- Point xy = {x, y};
1197- Point wh = {width, height};
1198-
1199- transform_point (&xy, matrix);
1200- transform_point (&wh, matrix);
1201- width = wh.x;
1202- height = wh.y;
1203- x = xy.x;
1204- y = xy.y;
1205-
1206- g_free (matrix);
1207- }
1208-
12091166 str = xmlGetNsProp (node, (const xmlChar *)"href", (const xmlChar *)"http://www.w3.org/1999/xlink");
12101167 if (str) {
12111168 if (strncmp ((char *)str, "data:image/", 11) == 0) {
@@ -1265,6 +1222,13 @@ read_image_svg(xmlNodePtr node, DiaSvgStyle *parent_style,
12651222 xmlFree(str);
12661223 }
12671224
1225+ if (matrix) {
1226+ g_return_val_if_fail (new_obj->ops->transform, list);
1227+ if (!new_obj->ops->transform (new_obj, matrix))
1228+ dia_context_add_message (ctx, _("Failed to apply transformation for '%s'"),
1229+ new_obj->type->name);
1230+ g_free (matrix);
1231+ }
12681232 if (new_obj)
12691233 return g_list_append (list, new_obj);
12701234
@@ -1629,15 +1593,15 @@ read_items (xmlNodePtr startnode,
16291593 else if (moreitems)
16301594 obj = g_list_last(moreitems)->data;
16311595 } else if (!xmlStrcmp(node->name, (const xmlChar *)"rect")) {
1632- items = read_rect_svg(node, parent_gs, style_ht, pattern_ht, items);
1596+ items = read_rect_svg(node, parent_gs, style_ht, pattern_ht, items, ctx);
16331597 if (items)
16341598 obj = g_list_last(items)->data;
16351599 } else if (!xmlStrcmp(node->name, (const xmlChar *)"line")) {
1636- items = read_line_svg(node, parent_gs, style_ht, pattern_ht, items);
1600+ items = read_line_svg(node, parent_gs, style_ht, pattern_ht, items, ctx);
16371601 if (items)
16381602 obj = g_list_last(items)->data;
16391603 } else if (!xmlStrcmp(node->name, (const xmlChar *)"ellipse") || !xmlStrcmp(node->name, (const xmlChar *)"circle")) {
1640- items = read_ellipse_svg(node, parent_gs, style_ht, pattern_ht, items);
1604+ items = read_ellipse_svg(node, parent_gs, style_ht, pattern_ht, items, ctx);
16411605 if (items)
16421606 obj = g_list_last(items)->data;
16431607 } else if (!xmlStrcmp(node->name, (const xmlChar *)"polyline")) {
@@ -1659,7 +1623,7 @@ read_items (xmlNodePtr startnode,
16591623 if (items && g_list_nth(items, first))
16601624 obj = g_list_nth(items, first)->data;
16611625 } else if(!xmlStrcmp(node->name, (const xmlChar *)"image")) {
1662- items = read_image_svg(node, parent_gs, style_ht, pattern_ht, items, filename_svg);
1626+ items = read_image_svg(node, parent_gs, style_ht, pattern_ht, items, filename_svg, ctx);
16631627 if (items)
16641628 obj = g_list_last(items)->data;
16651629 } else if(!xmlStrcmp(node->name, (const xmlChar *)"linearGradient") ||
@@ -1680,7 +1644,7 @@ read_items (xmlNodePtr startnode,
16801644 * be target for meta info, comment or something. */
16811645 obj = otemp->ops->copy (otemp);
16821646
1683- use_position (obj, node);
1647+ use_position (obj, node, ctx);
16841648 /* this should only be styled from the containing group,
16851649 * if it has no style on it's own. Sorry Dia can't create
16861650 * objects w/o style so we have two options beside complete
--- /dev/null
+++ b/samples/transform-variations.svg
@@ -0,0 +1,98 @@
1+<?xml version="1.0" encoding="UTF-8"?>
2+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3+<svg version="1.1"
4+ xmlns="http://www.w3.org/2000/svg"
5+ xmlns:xlink="http://www.w3.org/1999/xlink"
6+ x="0" y="0" width="28cm" height="28cm" viewBox="0, -5, 280, 275"
7+ stroke-width="2"
8+ text-anchor="middle" font-size="6" font-family="sans-serif">
9+ <text x="210" y="30" text-anchor="start">Standard - Box</text>
10+ <text x="210" y="40" text-anchor="start">Standard - Line</text>
11+ <text x="210" y="80" text-anchor="start">Standard - Ellipse</text>
12+ <text x="210" y="90" text-anchor="start">Standard - Text</text>
13+ <text x="210" y="130" text-anchor="start">Standard - Polygon</text>
14+ <text x="210" y="140" text-anchor="start">Standard - Polyline</text>
15+ <text x="210" y="180" text-anchor="start">Standard - Beziergon</text>
16+ <text x="210" y="190" text-anchor="start">Standard - Bezierline</text>
17+ <text x="210" y="230" text-anchor="start">Standard - Image</text>
18+
19+ <text x="30" y="0" >Reference</text>
20+ <!-- to be used below and directly -->
21+ <rect id="box" x="18" y="10" width="24" height="40" fill="yellow" stroke="red"/>
22+ <line id="line" x1="10" y1="30" x2="50" y2="30" stroke="red"/>
23+ <ellipse id="ell" cx="30" cy="80" rx="20" ry="15" fill="lime" stroke="blue"/>
24+ <text id="txt" x="30" y="80" text-anchor="middle" fill="blue">Rotate!</text>
25+ <polygon id="pg1" points="30,110,50,145,10,145" fill="lightblue"/>
26+ <polyline id="pl1" points="30,110,50,145,10,145" stroke="green" fill="none"/>
27+ <path id="pc1" d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160z" fill="cyan"/>
28+ <path id="po1" d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160" fill="none" stroke="gold"/>
29+ <image id="img" x="10" y="210" width="40" height="40"
30+ 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+AbbsMlxq/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="/>
31+
32+ <text x="80" y="0">Element rotate</text>
33+ <!-- transform object directly - must not have copied id="...", otherwise Dia would
34+ translate the wrong object (not the first of id, but the last) -->
35+ <rect x="18" y="10" width="24" height="40" fill="yellow" stroke="red"
36+ transform="translate(80,30) rotate(30) translate(-30,-30)"/>
37+ <line x1="10" y1="30" x2="50" y2="30" stroke="red"
38+ transform="translate(80,30) rotate(30) translate(-30,-30)"/>
39+ <ellipse cx="30" cy="80" rx="20" ry="15" fill="lime" stroke="blue"
40+ transform="translate(80,80) rotate(30) translate(-30,-80)"/>
41+ <text x="30" y="80" text-anchor="middle" fill="blue"
42+ transform="translate(80,80) rotate(30) translate(-30,-80)">Rotate!</text>
43+ <polygon points="30,110,50,145,10,145" fill="lightblue"
44+ transform="translate(80,130)rotate(30)translate(-30,-130)"/>
45+ <polyline points="30,110,50,145,10,145" fill="none" stroke="green"
46+ transform="translate(80,130) rotate(30) translate(-30,-130)"/>
47+ <path d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160z" fill="cyan"
48+ transform="translate(80,180) rotate(30) translate(-30,-180)"/>
49+ <path d="M30,160C40,170,50,200,30,200C10,200,20,170,30,160" fill="none" stroke="gold"
50+ transform="translate(80,180) rotate(30) translate(-30,-180)"/>
51+ <image x="10" y="210" width="40" height="40"
52+ transform="translate(80,230) rotate(30) translate(-30,-230)"
53+ 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+AbbsMlxq/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="/>
54+
55+ <text x="130" y="0">Group rotate</text>
56+ <!-- just shift with use, transform by group -->
57+ <g transform="translate(130,30)rotate(45)translate(-130,-30)">
58+ <use x="100" y="0" xlink:href="#box"/>
59+ <use x="100" y="0" xlink:href="#line"/>
60+ </g>
61+ <g transform="translate(130,80)rotate(45)translate(-130,-80)">
62+ <use x="100" y="0" xlink:href="#ell"/>
63+ <use x="100" y="0" xlink:href="#txt"/>
64+ </g>
65+ <g transform="translate(130,130)rotate(45)translate(-130,-130)">
66+ <use x="100" y="0" xlink:href="#pg1"/>
67+ <use x="100" y="0" xlink:href="#pl1"/>
68+ </g>
69+ <g transform="translate(130,180)rotate(45)translate(-130,-180)">
70+ <use x="100" y="0" xlink:href="#pc1"/>
71+ <use x="100" y="0" xlink:href="#po1"/>
72+ </g>
73+ <g transform="translate(130,230)rotate(45)translate(-130,-230)">
74+ <use x="100" y="0" xlink:href="#img"/>
75+ </g>
76+
77+ <text x="180" y="0">Use rotate</text>
78+ <!-- transformed use, shifted by group -->
79+ <g transform="translate(180,30)">
80+ <use x="-30" y="-30" xlink:href="#box" transform="rotate(60)"/>
81+ <use x="-30" y="-30" xlink:href="#line" transform="rotate(60)"/>
82+ </g>
83+ <g transform="translate(180,80)">
84+ <use x="-30" y="-80" xlink:href="#ell" transform="rotate(60)"/>
85+ <use x="-30" y="-80" xlink:href="#txt" transform="rotate(60)"/>
86+ </g>
87+ <g transform="translate(180,130)">
88+ <use x="-30" y="-130" xlink:href="#pg1" transform="rotate(60)"/>
89+ <use x="-30" y="-130" xlink:href="#pl1" transform="rotate(60)"/>
90+ </g>
91+ <g transform="translate(180,180)">
92+ <use x="-30" y="-180" xlink:href="#pc1" transform="rotate(60)"/>
93+ <use x="-30" y="-180" xlink:href="#po1" transform="rotate(60)"/>
94+ </g>
95+ <g transform="translate(180,230)">
96+ <use x="-30" y="-230" xlink:href="#img" transform="rotate(60)"/>
97+ </g>
98+</svg>