1 #define __SP_SYMBOL_C__
3 /*
4 * SVG <symbol> implementation
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * Copyright (C) 1999-2003 Lauris Kaplinski
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #include "config.h"
16 #include "libnr/nr-matrix-fns.h"
17 #include "libnr/nr-matrix-ops.h"
18 #include "display/nr-arena-group.h"
19 #include "xml/repr.h"
20 #include "attributes.h"
21 #include "print.h"
22 #include "sp-symbol.h"
24 static void sp_symbol_class_init (SPSymbolClass *klass);
25 static void sp_symbol_init (SPSymbol *symbol);
27 static void sp_symbol_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
28 static void sp_symbol_release (SPObject *object);
29 static void sp_symbol_set (SPObject *object, unsigned int key, const gchar *value);
30 static void sp_symbol_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
31 static void sp_symbol_update (SPObject *object, SPCtx *ctx, guint flags);
32 static void sp_symbol_modified (SPObject *object, guint flags);
33 static Inkscape::XML::Node *sp_symbol_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
35 static NRArenaItem *sp_symbol_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
36 static void sp_symbol_hide (SPItem *item, unsigned int key);
37 static void sp_symbol_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
38 static void sp_symbol_print (SPItem *item, SPPrintContext *ctx);
40 static SPGroupClass *parent_class;
42 GType
43 sp_symbol_get_type (void)
44 {
45 static GType type = 0;
46 if (!type) {
47 GTypeInfo info = {
48 sizeof (SPSymbolClass),
49 NULL, NULL,
50 (GClassInitFunc) sp_symbol_class_init,
51 NULL, NULL,
52 sizeof (SPSymbol),
53 16,
54 (GInstanceInitFunc) sp_symbol_init,
55 NULL, /* value_table */
56 };
57 type = g_type_register_static (SP_TYPE_GROUP, "SPSymbol", &info, (GTypeFlags)0);
58 }
59 return type;
60 }
62 static void
63 sp_symbol_class_init (SPSymbolClass *klass)
64 {
65 GObjectClass *object_class;
66 SPObjectClass *sp_object_class;
67 SPItemClass *sp_item_class;
69 object_class = G_OBJECT_CLASS (klass);
70 sp_object_class = (SPObjectClass *) klass;
71 sp_item_class = (SPItemClass *) klass;
73 parent_class = (SPGroupClass *)g_type_class_ref (SP_TYPE_GROUP);
75 sp_object_class->build = sp_symbol_build;
76 sp_object_class->release = sp_symbol_release;
77 sp_object_class->set = sp_symbol_set;
78 sp_object_class->child_added = sp_symbol_child_added;
79 sp_object_class->update = sp_symbol_update;
80 sp_object_class->modified = sp_symbol_modified;
81 sp_object_class->write = sp_symbol_write;
83 sp_item_class->show = sp_symbol_show;
84 sp_item_class->hide = sp_symbol_hide;
85 sp_item_class->bbox = sp_symbol_bbox;
86 sp_item_class->print = sp_symbol_print;
87 }
89 static void
90 sp_symbol_init (SPSymbol *symbol)
91 {
92 symbol->viewBox_set = FALSE;
94 nr_matrix_set_identity (&symbol->c2p);
95 }
97 static void
98 sp_symbol_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
99 {
100 SPGroup *group;
101 SPSymbol *symbol;
103 group = (SPGroup *) object;
104 symbol = (SPSymbol *) object;
106 sp_object_read_attr (object, "viewBox");
107 sp_object_read_attr (object, "preserveAspectRatio");
109 if (((SPObjectClass *) parent_class)->build)
110 ((SPObjectClass *) parent_class)->build (object, document, repr);
111 }
113 static void
114 sp_symbol_release (SPObject *object)
115 {
116 SPSymbol * symbol;
118 symbol = (SPSymbol *) object;
120 if (((SPObjectClass *) parent_class)->release)
121 ((SPObjectClass *) parent_class)->release (object);
122 }
124 static void
125 sp_symbol_set (SPObject *object, unsigned int key, const gchar *value)
126 {
127 SPItem *item;
128 SPSymbol *symbol;
130 item = SP_ITEM (object);
131 symbol = SP_SYMBOL (object);
133 switch (key) {
134 case SP_ATTR_VIEWBOX:
135 if (value) {
136 double x, y, width, height;
137 char *eptr;
138 /* fixme: We have to take original item affine into account */
139 /* fixme: Think (Lauris) */
140 eptr = (gchar *) value;
141 x = g_ascii_strtod (eptr, &eptr);
142 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
143 y = g_ascii_strtod (eptr, &eptr);
144 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
145 width = g_ascii_strtod (eptr, &eptr);
146 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
147 height = g_ascii_strtod (eptr, &eptr);
148 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
149 if ((width > 0) && (height > 0)) {
150 /* Set viewbox */
151 symbol->viewBox.x0 = x;
152 symbol->viewBox.y0 = y;
153 symbol->viewBox.x1 = x + width;
154 symbol->viewBox.y1 = y + height;
155 symbol->viewBox_set = TRUE;
156 } else {
157 symbol->viewBox_set = FALSE;
158 }
159 } else {
160 symbol->viewBox_set = FALSE;
161 }
162 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
163 break;
164 case SP_ATTR_PRESERVEASPECTRATIO:
165 /* Do setup before, so we can use break to escape */
166 symbol->aspect_set = FALSE;
167 symbol->aspect_align = SP_ASPECT_NONE;
168 symbol->aspect_clip = SP_ASPECT_MEET;
169 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
170 if (value) {
171 int len;
172 gchar c[256];
173 const gchar *p, *e;
174 unsigned int align, clip;
175 p = value;
176 while (*p && *p == 32) p += 1;
177 if (!*p) break;
178 e = p;
179 while (*e && *e != 32) e += 1;
180 len = e - p;
181 if (len > 8) break;
182 memcpy (c, value, len);
183 c[len] = 0;
184 /* Now the actual part */
185 if (!strcmp (c, "none")) {
186 align = SP_ASPECT_NONE;
187 } else if (!strcmp (c, "xMinYMin")) {
188 align = SP_ASPECT_XMIN_YMIN;
189 } else if (!strcmp (c, "xMidYMin")) {
190 align = SP_ASPECT_XMID_YMIN;
191 } else if (!strcmp (c, "xMaxYMin")) {
192 align = SP_ASPECT_XMAX_YMIN;
193 } else if (!strcmp (c, "xMinYMid")) {
194 align = SP_ASPECT_XMIN_YMID;
195 } else if (!strcmp (c, "xMidYMid")) {
196 align = SP_ASPECT_XMID_YMID;
197 } else if (!strcmp (c, "xMaxYMin")) {
198 align = SP_ASPECT_XMAX_YMID;
199 } else if (!strcmp (c, "xMinYMax")) {
200 align = SP_ASPECT_XMIN_YMAX;
201 } else if (!strcmp (c, "xMidYMax")) {
202 align = SP_ASPECT_XMID_YMAX;
203 } else if (!strcmp (c, "xMaxYMax")) {
204 align = SP_ASPECT_XMAX_YMAX;
205 } else {
206 break;
207 }
208 clip = SP_ASPECT_MEET;
209 while (*e && *e == 32) e += 1;
210 if (e) {
211 if (!strcmp (e, "meet")) {
212 clip = SP_ASPECT_MEET;
213 } else if (!strcmp (e, "slice")) {
214 clip = SP_ASPECT_SLICE;
215 } else {
216 break;
217 }
218 }
219 symbol->aspect_set = TRUE;
220 symbol->aspect_align = align;
221 symbol->aspect_clip = clip;
222 }
223 break;
224 default:
225 if (((SPObjectClass *) parent_class)->set)
226 ((SPObjectClass *) parent_class)->set (object, key, value);
227 break;
228 }
229 }
231 static void
232 sp_symbol_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
233 {
234 SPSymbol *symbol;
235 SPGroup *group;
237 symbol = (SPSymbol *) object;
238 group = (SPGroup *) object;
240 if (((SPObjectClass *) (parent_class))->child_added)
241 ((SPObjectClass *) (parent_class))->child_added (object, child, ref);
242 }
244 static void
245 sp_symbol_update (SPObject *object, SPCtx *ctx, guint flags)
246 {
247 SPItem *item;
248 SPSymbol *symbol;
249 SPItemCtx *ictx, rctx;
250 SPItemView *v;
252 item = SP_ITEM (object);
253 symbol = SP_SYMBOL (object);
254 ictx = (SPItemCtx *) ctx;
256 if (SP_OBJECT_IS_CLONED (object)) {
257 /* Cloned <symbol> is actually renderable */
259 /* fixme: We have to set up clip here too */
261 /* Create copy of item context */
262 rctx = *ictx;
264 /* Calculate child to parent transformation */
265 /* Apply parent <use> translation (set up as vewport) */
266 nr_matrix_set_translate (&symbol->c2p, rctx.vp.x0, rctx.vp.y0);
268 if (symbol->viewBox_set) {
269 double x, y, width, height;
270 NRMatrix q;
271 /* Determine actual viewbox in viewport coordinates */
272 if (symbol->aspect_align == SP_ASPECT_NONE) {
273 x = 0.0;
274 y = 0.0;
275 width = rctx.vp.x1 - rctx.vp.x0;
276 height = rctx.vp.y1 - rctx.vp.y0;
277 } else {
278 double scalex, scaley, scale;
279 /* Things are getting interesting */
280 scalex = (rctx.vp.x1 - rctx.vp.x0) / (symbol->viewBox.x1 - symbol->viewBox.x0);
281 scaley = (rctx.vp.y1 - rctx.vp.y0) / (symbol->viewBox.y1 - symbol->viewBox.y0);
282 scale = (symbol->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley);
283 width = (symbol->viewBox.x1 - symbol->viewBox.x0) * scale;
284 height = (symbol->viewBox.y1 - symbol->viewBox.y0) * scale;
285 /* Now place viewbox to requested position */
286 switch (symbol->aspect_align) {
287 case SP_ASPECT_XMIN_YMIN:
288 x = 0.0;
289 y = 0.0;
290 break;
291 case SP_ASPECT_XMID_YMIN:
292 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
293 y = 0.0;
294 break;
295 case SP_ASPECT_XMAX_YMIN:
296 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
297 y = 0.0;
298 break;
299 case SP_ASPECT_XMIN_YMID:
300 x = 0.0;
301 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
302 break;
303 case SP_ASPECT_XMID_YMID:
304 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
305 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
306 break;
307 case SP_ASPECT_XMAX_YMID:
308 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
309 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
310 break;
311 case SP_ASPECT_XMIN_YMAX:
312 x = 0.0;
313 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
314 break;
315 case SP_ASPECT_XMID_YMAX:
316 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
317 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
318 break;
319 case SP_ASPECT_XMAX_YMAX:
320 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
321 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
322 break;
323 default:
324 x = 0.0;
325 y = 0.0;
326 break;
327 }
328 }
329 /* Compose additional transformation from scale and position */
330 q.c[0] = width / (symbol->viewBox.x1 - symbol->viewBox.x0);
331 q.c[1] = 0.0;
332 q.c[2] = 0.0;
333 q.c[3] = height / (symbol->viewBox.y1 - symbol->viewBox.y0);
334 q.c[4] = -symbol->viewBox.x0 * q.c[0] + x;
335 q.c[5] = -symbol->viewBox.y0 * q.c[3] + y;
336 /* Append viewbox transformation */
337 nr_matrix_multiply (&symbol->c2p, &q, &symbol->c2p);
338 }
340 rctx.i2doc = symbol->c2p * rctx.i2doc;
342 /* If viewBox is set initialize child viewport */
343 /* Otherwise <use> has set it up already */
344 if (symbol->viewBox_set) {
345 rctx.vp.x0 = symbol->viewBox.x0;
346 rctx.vp.y0 = symbol->viewBox.y0;
347 rctx.vp.x1 = symbol->viewBox.x1;
348 rctx.vp.y1 = symbol->viewBox.y1;
349 rctx.i2vp = NR::identity();
350 }
352 /* And invoke parent method */
353 if (((SPObjectClass *) (parent_class))->update)
354 ((SPObjectClass *) (parent_class))->update (object, (SPCtx *) &rctx, flags);
356 /* As last step set additional transform of arena group */
357 for (v = item->display; v != NULL; v = v->next) {
358 nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->arenaitem), &symbol->c2p);
359 }
360 } else {
361 /* No-op */
362 if (((SPObjectClass *) (parent_class))->update)
363 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
364 }
365 }
367 static void
368 sp_symbol_modified (SPObject *object, guint flags)
369 {
370 SPSymbol *symbol;
372 symbol = SP_SYMBOL (object);
374 if (((SPObjectClass *) (parent_class))->modified)
375 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
376 }
378 static Inkscape::XML::Node *
379 sp_symbol_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
380 {
381 SPSymbol *symbol;
383 symbol = SP_SYMBOL (object);
385 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
386 repr = sp_repr_new ("svg:symbol");
387 }
389 repr->setAttribute("viewBox", object->repr->attribute("viewBox"));
390 repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
392 if (((SPObjectClass *) (parent_class))->write)
393 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
395 return repr;
396 }
398 static NRArenaItem *
399 sp_symbol_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
400 {
401 SPSymbol *symbol;
402 NRArenaItem *ai;
404 symbol = SP_SYMBOL (item);
406 if (SP_OBJECT_IS_CLONED (symbol)) {
407 /* Cloned <symbol> is actually renderable */
408 if (((SPItemClass *) (parent_class))->show) {
409 ai = ((SPItemClass *) (parent_class))->show (item, arena, key, flags);
410 if (ai) {
411 nr_arena_group_set_child_transform(NR_ARENA_GROUP(ai), &symbol->c2p);
412 }
413 } else {
414 ai = NULL;
415 }
416 } else {
417 ai = NULL;
418 }
420 return ai;
421 }
423 static void
424 sp_symbol_hide (SPItem *item, unsigned int key)
425 {
426 SPSymbol *symbol;
428 symbol = SP_SYMBOL (item);
430 if (SP_OBJECT_IS_CLONED (symbol)) {
431 /* Cloned <symbol> is actually renderable */
432 if (((SPItemClass *) (parent_class))->hide)
433 ((SPItemClass *) (parent_class))->hide (item, key);
434 }
435 }
437 static void
438 sp_symbol_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
439 {
440 SPSymbol const *symbol = SP_SYMBOL(item);
442 if (SP_OBJECT_IS_CLONED (symbol)) {
443 /* Cloned <symbol> is actually renderable */
445 if (((SPItemClass *) (parent_class))->bbox) {
446 NR::Matrix const a( symbol->c2p * transform );
447 ((SPItemClass *) (parent_class))->bbox(item, bbox, a, flags);
448 }
449 }
450 }
452 static void
453 sp_symbol_print (SPItem *item, SPPrintContext *ctx)
454 {
455 SPSymbol *symbol = SP_SYMBOL(item);
456 if (SP_OBJECT_IS_CLONED (symbol)) {
457 /* Cloned <symbol> is actually renderable */
459 sp_print_bind(ctx, &symbol->c2p, 1.0);
461 if (((SPItemClass *) (parent_class))->print) {
462 ((SPItemClass *) (parent_class))->print (item, ctx);
463 }
465 sp_print_release (ctx);
466 }
467 }