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"
23 #include "document.h"
25 static void sp_symbol_class_init (SPSymbolClass *klass);
26 static void sp_symbol_init (SPSymbol *symbol);
28 static void sp_symbol_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
29 static void sp_symbol_release (SPObject *object);
30 static void sp_symbol_set (SPObject *object, unsigned int key, const gchar *value);
31 static void sp_symbol_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref);
32 static void sp_symbol_update (SPObject *object, SPCtx *ctx, guint flags);
33 static void sp_symbol_modified (SPObject *object, guint flags);
34 static Inkscape::XML::Node *sp_symbol_write (SPObject *object, Inkscape::XML::Node *repr, guint flags);
36 static NRArenaItem *sp_symbol_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags);
37 static void sp_symbol_hide (SPItem *item, unsigned int key);
38 static void sp_symbol_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags);
39 static void sp_symbol_print (SPItem *item, SPPrintContext *ctx);
41 static SPGroupClass *parent_class;
43 GType
44 sp_symbol_get_type (void)
45 {
46 static GType type = 0;
47 if (!type) {
48 GTypeInfo info = {
49 sizeof (SPSymbolClass),
50 NULL, NULL,
51 (GClassInitFunc) sp_symbol_class_init,
52 NULL, NULL,
53 sizeof (SPSymbol),
54 16,
55 (GInstanceInitFunc) sp_symbol_init,
56 NULL, /* value_table */
57 };
58 type = g_type_register_static (SP_TYPE_GROUP, "SPSymbol", &info, (GTypeFlags)0);
59 }
60 return type;
61 }
63 static void
64 sp_symbol_class_init (SPSymbolClass *klass)
65 {
66 GObjectClass *object_class;
67 SPObjectClass *sp_object_class;
68 SPItemClass *sp_item_class;
70 object_class = G_OBJECT_CLASS (klass);
71 sp_object_class = (SPObjectClass *) klass;
72 sp_item_class = (SPItemClass *) klass;
74 parent_class = (SPGroupClass *)g_type_class_ref (SP_TYPE_GROUP);
76 sp_object_class->build = sp_symbol_build;
77 sp_object_class->release = sp_symbol_release;
78 sp_object_class->set = sp_symbol_set;
79 sp_object_class->child_added = sp_symbol_child_added;
80 sp_object_class->update = sp_symbol_update;
81 sp_object_class->modified = sp_symbol_modified;
82 sp_object_class->write = sp_symbol_write;
84 sp_item_class->show = sp_symbol_show;
85 sp_item_class->hide = sp_symbol_hide;
86 sp_item_class->bbox = sp_symbol_bbox;
87 sp_item_class->print = sp_symbol_print;
88 }
90 static void
91 sp_symbol_init (SPSymbol *symbol)
92 {
93 symbol->viewBox_set = FALSE;
95 nr_matrix_set_identity (&symbol->c2p);
96 }
98 static void
99 sp_symbol_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
100 {
101 SPGroup *group;
102 SPSymbol *symbol;
104 group = (SPGroup *) object;
105 symbol = (SPSymbol *) object;
107 sp_object_read_attr (object, "viewBox");
108 sp_object_read_attr (object, "preserveAspectRatio");
110 if (((SPObjectClass *) parent_class)->build)
111 ((SPObjectClass *) parent_class)->build (object, document, repr);
112 }
114 static void
115 sp_symbol_release (SPObject *object)
116 {
117 SPSymbol * symbol;
119 symbol = (SPSymbol *) object;
121 if (((SPObjectClass *) parent_class)->release)
122 ((SPObjectClass *) parent_class)->release (object);
123 }
125 static void
126 sp_symbol_set (SPObject *object, unsigned int key, const gchar *value)
127 {
128 SPItem *item;
129 SPSymbol *symbol;
131 item = SP_ITEM (object);
132 symbol = SP_SYMBOL (object);
134 switch (key) {
135 case SP_ATTR_VIEWBOX:
136 if (value) {
137 double x, y, width, height;
138 char *eptr;
139 /* fixme: We have to take original item affine into account */
140 /* fixme: Think (Lauris) */
141 eptr = (gchar *) value;
142 x = g_ascii_strtod (eptr, &eptr);
143 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
144 y = g_ascii_strtod (eptr, &eptr);
145 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
146 width = g_ascii_strtod (eptr, &eptr);
147 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
148 height = g_ascii_strtod (eptr, &eptr);
149 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) eptr++;
150 if ((width > 0) && (height > 0)) {
151 /* Set viewbox */
152 symbol->viewBox.x0 = x;
153 symbol->viewBox.y0 = y;
154 symbol->viewBox.x1 = x + width;
155 symbol->viewBox.y1 = y + height;
156 symbol->viewBox_set = TRUE;
157 } else {
158 symbol->viewBox_set = FALSE;
159 }
160 } else {
161 symbol->viewBox_set = FALSE;
162 }
163 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
164 break;
165 case SP_ATTR_PRESERVEASPECTRATIO:
166 /* Do setup before, so we can use break to escape */
167 symbol->aspect_set = FALSE;
168 symbol->aspect_align = SP_ASPECT_NONE;
169 symbol->aspect_clip = SP_ASPECT_MEET;
170 object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_VIEWPORT_MODIFIED_FLAG);
171 if (value) {
172 int len;
173 gchar c[256];
174 const gchar *p, *e;
175 unsigned int align, clip;
176 p = value;
177 while (*p && *p == 32) p += 1;
178 if (!*p) break;
179 e = p;
180 while (*e && *e != 32) e += 1;
181 len = e - p;
182 if (len > 8) break;
183 memcpy (c, value, len);
184 c[len] = 0;
185 /* Now the actual part */
186 if (!strcmp (c, "none")) {
187 align = SP_ASPECT_NONE;
188 } else if (!strcmp (c, "xMinYMin")) {
189 align = SP_ASPECT_XMIN_YMIN;
190 } else if (!strcmp (c, "xMidYMin")) {
191 align = SP_ASPECT_XMID_YMIN;
192 } else if (!strcmp (c, "xMaxYMin")) {
193 align = SP_ASPECT_XMAX_YMIN;
194 } else if (!strcmp (c, "xMinYMid")) {
195 align = SP_ASPECT_XMIN_YMID;
196 } else if (!strcmp (c, "xMidYMid")) {
197 align = SP_ASPECT_XMID_YMID;
198 } else if (!strcmp (c, "xMaxYMin")) {
199 align = SP_ASPECT_XMAX_YMID;
200 } else if (!strcmp (c, "xMinYMax")) {
201 align = SP_ASPECT_XMIN_YMAX;
202 } else if (!strcmp (c, "xMidYMax")) {
203 align = SP_ASPECT_XMID_YMAX;
204 } else if (!strcmp (c, "xMaxYMax")) {
205 align = SP_ASPECT_XMAX_YMAX;
206 } else {
207 break;
208 }
209 clip = SP_ASPECT_MEET;
210 while (*e && *e == 32) e += 1;
211 if (e) {
212 if (!strcmp (e, "meet")) {
213 clip = SP_ASPECT_MEET;
214 } else if (!strcmp (e, "slice")) {
215 clip = SP_ASPECT_SLICE;
216 } else {
217 break;
218 }
219 }
220 symbol->aspect_set = TRUE;
221 symbol->aspect_align = align;
222 symbol->aspect_clip = clip;
223 }
224 break;
225 default:
226 if (((SPObjectClass *) parent_class)->set)
227 ((SPObjectClass *) parent_class)->set (object, key, value);
228 break;
229 }
230 }
232 static void
233 sp_symbol_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
234 {
235 SPSymbol *symbol;
236 SPGroup *group;
238 symbol = (SPSymbol *) object;
239 group = (SPGroup *) object;
241 if (((SPObjectClass *) (parent_class))->child_added)
242 ((SPObjectClass *) (parent_class))->child_added (object, child, ref);
243 }
245 static void
246 sp_symbol_update (SPObject *object, SPCtx *ctx, guint flags)
247 {
248 SPItem *item;
249 SPSymbol *symbol;
250 SPItemCtx *ictx, rctx;
251 SPItemView *v;
253 item = SP_ITEM (object);
254 symbol = SP_SYMBOL (object);
255 ictx = (SPItemCtx *) ctx;
257 if (SP_OBJECT_IS_CLONED (object)) {
258 /* Cloned <symbol> is actually renderable */
260 /* fixme: We have to set up clip here too */
262 /* Create copy of item context */
263 rctx = *ictx;
265 /* Calculate child to parent transformation */
266 /* Apply parent <use> translation (set up as vewport) */
267 nr_matrix_set_translate (&symbol->c2p, rctx.vp.x0, rctx.vp.y0);
269 if (symbol->viewBox_set) {
270 double x, y, width, height;
271 NRMatrix q;
272 /* Determine actual viewbox in viewport coordinates */
273 if (symbol->aspect_align == SP_ASPECT_NONE) {
274 x = 0.0;
275 y = 0.0;
276 width = rctx.vp.x1 - rctx.vp.x0;
277 height = rctx.vp.y1 - rctx.vp.y0;
278 } else {
279 double scalex, scaley, scale;
280 /* Things are getting interesting */
281 scalex = (rctx.vp.x1 - rctx.vp.x0) / (symbol->viewBox.x1 - symbol->viewBox.x0);
282 scaley = (rctx.vp.y1 - rctx.vp.y0) / (symbol->viewBox.y1 - symbol->viewBox.y0);
283 scale = (symbol->aspect_clip == SP_ASPECT_MEET) ? MIN (scalex, scaley) : MAX (scalex, scaley);
284 width = (symbol->viewBox.x1 - symbol->viewBox.x0) * scale;
285 height = (symbol->viewBox.y1 - symbol->viewBox.y0) * scale;
286 /* Now place viewbox to requested position */
287 switch (symbol->aspect_align) {
288 case SP_ASPECT_XMIN_YMIN:
289 x = 0.0;
290 y = 0.0;
291 break;
292 case SP_ASPECT_XMID_YMIN:
293 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
294 y = 0.0;
295 break;
296 case SP_ASPECT_XMAX_YMIN:
297 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
298 y = 0.0;
299 break;
300 case SP_ASPECT_XMIN_YMID:
301 x = 0.0;
302 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
303 break;
304 case SP_ASPECT_XMID_YMID:
305 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
306 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
307 break;
308 case SP_ASPECT_XMAX_YMID:
309 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
310 y = 0.5 * ((rctx.vp.y1 - rctx.vp.y0) - height);
311 break;
312 case SP_ASPECT_XMIN_YMAX:
313 x = 0.0;
314 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
315 break;
316 case SP_ASPECT_XMID_YMAX:
317 x = 0.5 * ((rctx.vp.x1 - rctx.vp.x0) - width);
318 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
319 break;
320 case SP_ASPECT_XMAX_YMAX:
321 x = 1.0 * ((rctx.vp.x1 - rctx.vp.x0) - width);
322 y = 1.0 * ((rctx.vp.y1 - rctx.vp.y0) - height);
323 break;
324 default:
325 x = 0.0;
326 y = 0.0;
327 break;
328 }
329 }
330 /* Compose additional transformation from scale and position */
331 q.c[0] = width / (symbol->viewBox.x1 - symbol->viewBox.x0);
332 q.c[1] = 0.0;
333 q.c[2] = 0.0;
334 q.c[3] = height / (symbol->viewBox.y1 - symbol->viewBox.y0);
335 q.c[4] = -symbol->viewBox.x0 * q.c[0] + x;
336 q.c[5] = -symbol->viewBox.y0 * q.c[3] + y;
337 /* Append viewbox transformation */
338 nr_matrix_multiply (&symbol->c2p, &q, &symbol->c2p);
339 }
341 rctx.i2doc = symbol->c2p * rctx.i2doc;
343 /* If viewBox is set initialize child viewport */
344 /* Otherwise <use> has set it up already */
345 if (symbol->viewBox_set) {
346 rctx.vp.x0 = symbol->viewBox.x0;
347 rctx.vp.y0 = symbol->viewBox.y0;
348 rctx.vp.x1 = symbol->viewBox.x1;
349 rctx.vp.y1 = symbol->viewBox.y1;
350 rctx.i2vp = NR::identity();
351 }
353 /* And invoke parent method */
354 if (((SPObjectClass *) (parent_class))->update)
355 ((SPObjectClass *) (parent_class))->update (object, (SPCtx *) &rctx, flags);
357 /* As last step set additional transform of arena group */
358 for (v = item->display; v != NULL; v = v->next) {
359 nr_arena_group_set_child_transform(NR_ARENA_GROUP(v->arenaitem), &symbol->c2p);
360 }
361 } else {
362 /* No-op */
363 if (((SPObjectClass *) (parent_class))->update)
364 ((SPObjectClass *) (parent_class))->update (object, ctx, flags);
365 }
366 }
368 static void
369 sp_symbol_modified (SPObject *object, guint flags)
370 {
371 SPSymbol *symbol;
373 symbol = SP_SYMBOL (object);
375 if (((SPObjectClass *) (parent_class))->modified)
376 (* ((SPObjectClass *) (parent_class))->modified) (object, flags);
377 }
379 static Inkscape::XML::Node *
380 sp_symbol_write (SPObject *object, Inkscape::XML::Node *repr, guint flags)
381 {
382 SPSymbol *symbol;
384 symbol = SP_SYMBOL (object);
386 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
387 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
388 repr = xml_doc->createElement("svg:symbol");
389 }
391 repr->setAttribute("viewBox", object->repr->attribute("viewBox"));
392 repr->setAttribute("preserveAspectRatio", object->repr->attribute("preserveAspectRatio"));
394 if (((SPObjectClass *) (parent_class))->write)
395 ((SPObjectClass *) (parent_class))->write (object, repr, flags);
397 return repr;
398 }
400 static NRArenaItem *
401 sp_symbol_show (SPItem *item, NRArena *arena, unsigned int key, unsigned int flags)
402 {
403 SPSymbol *symbol;
404 NRArenaItem *ai;
406 symbol = SP_SYMBOL (item);
408 if (SP_OBJECT_IS_CLONED (symbol)) {
409 /* Cloned <symbol> is actually renderable */
410 if (((SPItemClass *) (parent_class))->show) {
411 ai = ((SPItemClass *) (parent_class))->show (item, arena, key, flags);
412 if (ai) {
413 nr_arena_group_set_child_transform(NR_ARENA_GROUP(ai), &symbol->c2p);
414 }
415 } else {
416 ai = NULL;
417 }
418 } else {
419 ai = NULL;
420 }
422 return ai;
423 }
425 static void
426 sp_symbol_hide (SPItem *item, unsigned int key)
427 {
428 SPSymbol *symbol;
430 symbol = SP_SYMBOL (item);
432 if (SP_OBJECT_IS_CLONED (symbol)) {
433 /* Cloned <symbol> is actually renderable */
434 if (((SPItemClass *) (parent_class))->hide)
435 ((SPItemClass *) (parent_class))->hide (item, key);
436 }
437 }
439 static void
440 sp_symbol_bbox(SPItem const *item, NRRect *bbox, NR::Matrix const &transform, unsigned const flags)
441 {
442 SPSymbol const *symbol = SP_SYMBOL(item);
444 if (SP_OBJECT_IS_CLONED (symbol)) {
445 /* Cloned <symbol> is actually renderable */
447 if (((SPItemClass *) (parent_class))->bbox) {
448 NR::Matrix const a( symbol->c2p * transform );
449 ((SPItemClass *) (parent_class))->bbox(item, bbox, a, flags);
450 }
451 }
452 }
454 static void
455 sp_symbol_print (SPItem *item, SPPrintContext *ctx)
456 {
457 SPSymbol *symbol = SP_SYMBOL(item);
458 if (SP_OBJECT_IS_CLONED (symbol)) {
459 /* Cloned <symbol> is actually renderable */
461 sp_print_bind(ctx, &symbol->c2p, 1.0);
463 if (((SPItemClass *) (parent_class))->print) {
464 ((SPItemClass *) (parent_class))->print (item, ctx);
465 }
467 sp_print_release (ctx);
468 }
469 }