Code

further fix: remember is paintservers were hreffed in a flag, fixes undo crash
[inkscape.git] / src / style.cpp
1 #define __SP_STYLE_C__
3 /** \file
4  * SVG stylesheets implementation.
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Peter Moulder <pmoulder@mail.csse.monash.edu.au>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 2001-2002 Lauris Kaplinski
12  * Copyright (C) 2001 Ximian, Inc.
13  * Copyright (C) 2005 Monash University
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include "libcroco/cr-sel-eng.h"
23 #include "xml/croco-node-iface.h"
25 #include "svg/svg.h"
27 #include "display/canvas-bpath.h"
28 #include "attributes.h"
29 #include "document.h"
30 #include "extract-uri.h"
31 #include "marker-status.h"
32 #include "uri-references.h"
33 #include "sp-paint-server.h"
34 #include "streq.h"
35 #include "strneq.h"
36 #include "style.h"
37 #include "svg/css-ostringstream.h"
38 #include "xml/repr.h"
39 #include "unit-constants.h"
40 #include "isnan.h"
42 namespace Inkscape {
44 /**
45  * Parses a CSS url() specification; temporary hack until
46  * style stuff is redone.
47  * \param string the CSS string to parse
48  * \return a newly-allocated URL string (or NULL); free with g_free()
49  */
50 gchar *parse_css_url(gchar const *string) {
51     if (!string)
52         return NULL;
54     gchar const *iter = string;
55     for ( ; g_ascii_isspace(*iter) ; iter = g_utf8_next_char(iter) );
56     if (strncmp(iter, "url(", 4))
57         return NULL;
58     iter += 4;
60     gchar const end_char = *iter;
61     if ( *iter == '"' || *iter == '\'' ) {
62         iter += 1;
63     }
65     GString *temp = g_string_new(NULL);
66     for ( ; *iter ; iter = g_utf8_next_char(iter) ) {
67         if ( *iter == '(' || *iter == ')'  ||
68              *iter == '"' || *iter == '\'' ||
69              g_ascii_isspace(*iter)        ||
70              g_ascii_iscntrl(*iter)           )
71         {
72             break;
73         }
74         if ( *iter == '\\' ) {
75             iter = g_utf8_next_char(iter);
76         }
77         if ( *iter & (gchar)0x80 ) {
78             break;
79         } else {
80             g_string_append_c(temp, *iter);
81         }
82     }
84     if ( *iter == end_char && end_char != ')' ) {
85         iter = g_utf8_next_char(iter);
86     }
87     gchar *result;
88     if ( *iter == ')' ) {
89         result = temp->str;
90         g_string_free(temp, FALSE);
91     } else {
92         result = NULL;
93         g_string_free(temp, TRUE);
94     }
96     return result;
97 }
99 }
101 #define BMAX 8192
103 class SPStyleEnum;
105 /*#########################
106 ## FORWARD DECLARATIONS
107 #########################*/
108 static void sp_style_clear(SPStyle *style);
110 static void sp_style_merge_property(SPStyle *style, gint id, gchar const *val);
112 static void sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent);
113 static void sp_style_read_dash(SPStyle *style, gchar const *str);
115 static SPTextStyle *sp_text_style_new(void);
116 static void sp_text_style_clear(SPTextStyle *ts);
117 static SPTextStyle *sp_text_style_unref(SPTextStyle *st);
118 static SPTextStyle *sp_text_style_duplicate_unset(SPTextStyle *st);
119 static guint sp_text_style_write(gchar *p, guint len, SPTextStyle const *st, guint flags = SP_STYLE_FLAG_IFSET);
120 static void sp_style_privatize_text(SPStyle *style);
122 static void sp_style_read_ifloat(SPIFloat *val, gchar const *str);
123 static void sp_style_read_iscale24(SPIScale24 *val, gchar const *str);
124 static void sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum const *dict, bool can_explicitly_inherit);
125 static void sp_style_read_istring(SPIString *val, gchar const *str);
126 static void sp_style_read_ilength(SPILength *val, gchar const *str);
127 static void sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str);
128 static void sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str);
129 static void sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document);
130 static void sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document);
131 static void sp_style_read_ifontsize(SPIFontSize *val, gchar const *str);
133 static void sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr, gchar const *key, SPStyleEnum const *dict, bool can_explicitly_inherit);
134 static void sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key);
135 static void sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const *key);
136 static void sp_style_read_pfloat(SPIFloat *val, Inkscape::XML::Node *repr, gchar const *key);
138 static gint sp_style_write_ifloat(gchar *p, gint len, gchar const *key, SPIFloat const *val, SPIFloat const *base, guint flags);
139 static gint sp_style_write_iscale24(gchar *p, gint len, gchar const *key, SPIScale24 const *val, SPIScale24 const *base, guint flags);
140 static gint sp_style_write_ienum(gchar *p, gint len, gchar const *key, SPStyleEnum const *dict, SPIEnum const *val, SPIEnum const *base, guint flags);
141 static gint sp_style_write_istring(gchar *p, gint len, gchar const *key, SPIString const *val, SPIString const *base, guint flags);
142 static gint sp_style_write_ilength(gchar *p, gint len, gchar const *key, SPILength const *val, SPILength const *base, guint flags);
143 static gint sp_style_write_ipaint(gchar *b, gint len, gchar const *key, SPIPaint const *paint, SPIPaint const *base, guint flags);
144 static gint sp_style_write_ifontsize(gchar *p, gint len, gchar const *key, SPIFontSize const *val, SPIFontSize const *base, guint flags);
145 static gint sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key, SPILengthOrNormal const *const val, SPILengthOrNormal const *const base, guint const flags);
146 static gint sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key, SPITextDecoration const *const val, SPITextDecoration const *const base, guint const flags);
148 void css2_unescape_unquote (SPIString *val);
150 static void sp_style_paint_clear(SPStyle *style, SPIPaint *paint, unsigned hunref, unsigned unset);
152 #define SPS_READ_IENUM_IF_UNSET(v,s,d,i) if (!(v)->set) {sp_style_read_ienum((v), (s), (d), (i));}
153 #define SPS_READ_PENUM_IF_UNSET(v,r,k,d,i) if (!(v)->set) {sp_style_read_penum((v), (r), (k), (d), (i));}
155 #define SPS_READ_ILENGTH_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ilength((v), (s));}
156 #define SPS_READ_PLENGTH_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_plength((v), (r), (k));}
158 #define SPS_READ_PFLOAT_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pfloat((v), (r), (k));}
160 #define SPS_READ_IFONTSIZE_IF_UNSET(v,s) if (!(v)->set) {sp_style_read_ifontsize((v), (s));}
161 #define SPS_READ_PFONTSIZE_IF_UNSET(v,r,k) if (!(v)->set) {sp_style_read_pfontsize((v), (r), (k));}
163 static void sp_style_merge_from_object_stylesheet(SPStyle *, SPObject const *);
165 struct SPStyleEnum {
166     gchar const *key;
167     gint value;
168 };
170 static SPStyleEnum const enum_fill_rule[] = {
171     {"nonzero", SP_WIND_RULE_NONZERO},
172     {"evenodd", SP_WIND_RULE_EVENODD},
173     {NULL, -1}
174 };
176 static SPStyleEnum const enum_stroke_linecap[] = {
177     {"butt", SP_STROKE_LINECAP_BUTT},
178     {"round", SP_STROKE_LINECAP_ROUND},
179     {"square", SP_STROKE_LINECAP_SQUARE},
180     {NULL, -1}
181 };
183 static SPStyleEnum const enum_stroke_linejoin[] = {
184     {"miter", SP_STROKE_LINEJOIN_MITER},
185     {"round", SP_STROKE_LINEJOIN_ROUND},
186     {"bevel", SP_STROKE_LINEJOIN_BEVEL},
187     {NULL, -1}
188 };
190 static SPStyleEnum const enum_font_style[] = {
191     {"normal", SP_CSS_FONT_STYLE_NORMAL},
192     {"italic", SP_CSS_FONT_STYLE_ITALIC},
193     {"oblique", SP_CSS_FONT_STYLE_OBLIQUE},
194     {NULL, -1}
195 };
197 static SPStyleEnum const enum_font_size[] = {
198     {"xx-small", SP_CSS_FONT_SIZE_XX_SMALL},
199     {"x-small", SP_CSS_FONT_SIZE_X_SMALL},
200     {"small", SP_CSS_FONT_SIZE_SMALL},
201     {"medium", SP_CSS_FONT_SIZE_MEDIUM},
202     {"large", SP_CSS_FONT_SIZE_LARGE},
203     {"x-large", SP_CSS_FONT_SIZE_X_LARGE},
204     {"xx-large", SP_CSS_FONT_SIZE_XX_LARGE},
205     {"smaller", SP_CSS_FONT_SIZE_SMALLER},
206     {"larger", SP_CSS_FONT_SIZE_LARGER},
207     {NULL, -1}
208 };
210 static SPStyleEnum const enum_font_variant[] = {
211     {"normal", SP_CSS_FONT_VARIANT_NORMAL},
212     {"small-caps", SP_CSS_FONT_VARIANT_SMALL_CAPS},
213     {NULL, -1}
214 };
216 static SPStyleEnum const enum_font_weight[] = {
217     {"100", SP_CSS_FONT_WEIGHT_100},
218     {"200", SP_CSS_FONT_WEIGHT_200},
219     {"300", SP_CSS_FONT_WEIGHT_300},
220     {"400", SP_CSS_FONT_WEIGHT_400},
221     {"500", SP_CSS_FONT_WEIGHT_500},
222     {"600", SP_CSS_FONT_WEIGHT_600},
223     {"700", SP_CSS_FONT_WEIGHT_700},
224     {"800", SP_CSS_FONT_WEIGHT_800},
225     {"900", SP_CSS_FONT_WEIGHT_900},
226     {"normal", SP_CSS_FONT_WEIGHT_NORMAL},
227     {"bold", SP_CSS_FONT_WEIGHT_BOLD},
228     {"lighter", SP_CSS_FONT_WEIGHT_LIGHTER},
229     {"bolder", SP_CSS_FONT_WEIGHT_BOLDER},
230     {NULL, -1}
231 };
233 static SPStyleEnum const enum_font_stretch[] = {
234     {"ultra-condensed", SP_CSS_FONT_STRETCH_ULTRA_CONDENSED},
235     {"extra-condensed", SP_CSS_FONT_STRETCH_EXTRA_CONDENSED},
236     {"condensed", SP_CSS_FONT_STRETCH_CONDENSED},
237     {"semi-condensed", SP_CSS_FONT_STRETCH_SEMI_CONDENSED},
238     {"normal", SP_CSS_FONT_STRETCH_NORMAL},
239     {"semi-expanded", SP_CSS_FONT_STRETCH_SEMI_EXPANDED},
240     {"expanded", SP_CSS_FONT_STRETCH_EXPANDED},
241     {"extra-expanded", SP_CSS_FONT_STRETCH_EXTRA_EXPANDED},
242     {"ultra-expanded", SP_CSS_FONT_STRETCH_ULTRA_EXPANDED},
243     {"narrower", SP_CSS_FONT_STRETCH_NARROWER},
244     {"wider", SP_CSS_FONT_STRETCH_WIDER},
245     {NULL, -1}
246 };
248 static SPStyleEnum const enum_text_align[] = {
249     {"start", SP_CSS_TEXT_ALIGN_START},
250     {"end", SP_CSS_TEXT_ALIGN_END},
251     {"left", SP_CSS_TEXT_ALIGN_LEFT},
252     {"right", SP_CSS_TEXT_ALIGN_RIGHT},
253     {"center", SP_CSS_TEXT_ALIGN_CENTER},
254     {"justify", SP_CSS_TEXT_ALIGN_JUSTIFY},
255     {NULL, -1}
256 };
258 static SPStyleEnum const enum_text_transform[] = {
259     {"capitalize", SP_CSS_TEXT_TRANSFORM_CAPITALIZE},
260     {"uppercase", SP_CSS_TEXT_TRANSFORM_UPPERCASE},
261     {"lowercase", SP_CSS_TEXT_TRANSFORM_LOWERCASE},
262     {"none", SP_CSS_TEXT_TRANSFORM_NONE},
263     {NULL, -1}
264 };
266 static SPStyleEnum const enum_text_anchor[] = {
267     {"start", SP_CSS_TEXT_ANCHOR_START},
268     {"middle", SP_CSS_TEXT_ANCHOR_MIDDLE},
269     {"end", SP_CSS_TEXT_ANCHOR_END},
270     {NULL, -1}
271 };
273 static SPStyleEnum const enum_direction[] = {
274     {"ltr", SP_CSS_DIRECTION_LTR},
275     {"rtl", SP_CSS_DIRECTION_RTL},
276     {NULL, -1}
277 };
279 static SPStyleEnum const enum_block_progression[] = {
280     {"tb", SP_CSS_BLOCK_PROGRESSION_TB},
281     {"rl", SP_CSS_BLOCK_PROGRESSION_RL},
282     {"lr", SP_CSS_BLOCK_PROGRESSION_LR},
283     {NULL, -1}
284 };
286 static SPStyleEnum const enum_writing_mode[] = {
287     /* Note that using the same enumerator for lr as lr-tb means we write as lr-tb even if the
288      * input file said lr.  We prefer writing lr-tb on the grounds that the spec says the initial
289      * value is lr-tb rather than lr.
290      *
291      * ECMA scripts may be surprised to find tb-rl in DOM if they set the attribute to rl, so
292      * sharing enumerators for different strings may be a bug (once we support ecma script).
293      */
294     {"lr-tb", SP_CSS_WRITING_MODE_LR_TB},
295     {"rl-tb", SP_CSS_WRITING_MODE_RL_TB},
296     {"tb-rl", SP_CSS_WRITING_MODE_TB_RL},
297     {"lr", SP_CSS_WRITING_MODE_LR_TB},
298     {"rl", SP_CSS_WRITING_MODE_RL_TB},
299     {"tb", SP_CSS_WRITING_MODE_TB_RL},
300     {NULL, -1}
301 };
303 static SPStyleEnum const enum_visibility[] = {
304     {"hidden", SP_CSS_VISIBILITY_HIDDEN},
305     {"collapse", SP_CSS_VISIBILITY_COLLAPSE},
306     {"visible", SP_CSS_VISIBILITY_VISIBLE},
307     {NULL, -1}
308 };
310 static SPStyleEnum const enum_overflow[] = {
311     {"visible", SP_CSS_OVERFLOW_VISIBLE},
312     {"hidden", SP_CSS_OVERFLOW_HIDDEN},
313     {"scroll", SP_CSS_OVERFLOW_SCROLL},
314     {"auto", SP_CSS_OVERFLOW_AUTO},
315     {NULL, -1}
316 };
318 static SPStyleEnum const enum_display[] = {
319     {"none",      SP_CSS_DISPLAY_NONE},
320     {"inline",    SP_CSS_DISPLAY_INLINE},
321     {"block",     SP_CSS_DISPLAY_BLOCK},
322     {"list-item", SP_CSS_DISPLAY_LIST_ITEM},
323     {"run-in",    SP_CSS_DISPLAY_RUN_IN},
324     {"compact",   SP_CSS_DISPLAY_COMPACT},
325     {"marker",    SP_CSS_DISPLAY_MARKER},
326     {"table",     SP_CSS_DISPLAY_TABLE},
327     {"inline-table",  SP_CSS_DISPLAY_INLINE_TABLE},
328     {"table-row-group",    SP_CSS_DISPLAY_TABLE_ROW_GROUP},
329     {"table-header-group", SP_CSS_DISPLAY_TABLE_HEADER_GROUP},
330     {"table-footer-group", SP_CSS_DISPLAY_TABLE_FOOTER_GROUP},
331     {"table-row",     SP_CSS_DISPLAY_TABLE_ROW},
332     {"table-column-group", SP_CSS_DISPLAY_TABLE_COLUMN_GROUP},
333     {"table-column",  SP_CSS_DISPLAY_TABLE_COLUMN},
334     {"table-cell",    SP_CSS_DISPLAY_TABLE_CELL},
335     {"table-caption", SP_CSS_DISPLAY_TABLE_CAPTION},
336     {NULL, -1}
337 };
339 static SPStyleEnum const enum_shape_rendering[] = {
340     {"auto", 0},
341     {"optimizeSpeed", 0},
342     {"crispEdges", 0},
343     {"geometricPrecision", 0},
344     {NULL, -1}
345 };
347 static SPStyleEnum const enum_color_rendering[] = {
348     {"auto", 0},
349     {"optimizeSpeed", 0},
350     {"optimizeQuality", 0},
351     {NULL, -1}
352 };
354 static SPStyleEnum const *const enum_image_rendering = enum_color_rendering;
356 static SPStyleEnum const enum_text_rendering[] = {
357     {"auto", 0},
358     {"optimizeSpeed", 0},
359     {"optimizeLegibility", 0},
360     {"geometricPrecision", 0},
361     {NULL, -1}
362 };
364 /**
365  * Release callback.
366  */
367 static void
368 sp_style_object_release(SPObject *object, SPStyle *style)
370     style->object = NULL;
376 /**
377  * Returns a new SPStyle object with settings as per sp_style_clear().
378  */
379 SPStyle *
380 sp_style_new()
382     SPStyle *const style = g_new0(SPStyle, 1);
384     style->refcount = 1;
385     style->object = NULL;
386     style->text = sp_text_style_new();
387     style->text_private = TRUE;
389     sp_style_clear(style);
391     style->cloned = false;
392     style->hreffed = false;
394     return style;
398 /**
399  * Creates a new SPStyle object, and attaches it to the specified SPObject.
400  */
401 SPStyle *
402 sp_style_new_from_object(SPObject *object)
404     g_return_val_if_fail(object != NULL, NULL);
405     g_return_val_if_fail(SP_IS_OBJECT(object), NULL);
407     SPStyle *style = sp_style_new();
408     style->object = object;
409     g_signal_connect(G_OBJECT(object), "release", G_CALLBACK(sp_style_object_release), style);
411     if (object && SP_OBJECT_IS_CLONED(object)) {
412         style->cloned = true;
413     }
415     return style;
419 /**
420  * Increase refcount of style.
421  */
422 SPStyle *
423 sp_style_ref(SPStyle *style)
425     g_return_val_if_fail(style != NULL, NULL);
426     g_return_val_if_fail(style->refcount > 0, NULL);
428     style->refcount += 1;
430     return style;
434 /**
435  * Decrease refcount of style with possible destruction.
436  */
437 SPStyle *
438 sp_style_unref(SPStyle *style)
440     g_return_val_if_fail(style != NULL, NULL);
441     g_return_val_if_fail(style->refcount > 0, NULL);
443     style->refcount -= 1;
445     if (style->refcount < 1) {
446         if (style->object)
447             g_signal_handlers_disconnect_matched(G_OBJECT(style->object),
448                                                  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, style);
449         if (style->text) sp_text_style_unref(style->text);
450         sp_style_paint_clear(style, &style->fill, TRUE, FALSE);
451         sp_style_paint_clear(style, &style->stroke, TRUE, FALSE);
452         g_free(style->stroke_dash.dash);
453         g_free(style);
454     }
456     return NULL;
459 /**
460  *  Reads the various style parameters for an object from repr.
461  */
462 static void
463 sp_style_read(SPStyle *style, SPObject *object, Inkscape::XML::Node *repr)
465     g_assert(style != NULL);
466     g_assert(repr != NULL);
467     g_assert(!object || (SP_OBJECT_REPR(object) == repr));
469     sp_style_clear(style);
471     if (object && SP_OBJECT_IS_CLONED(object)) {
472         style->cloned = true;
473     }
475     /* 1. Style itself */
476     gchar const *val = repr->attribute("style");
477     if (val != NULL) {
478         sp_style_merge_from_style_string(style, val);
479     }
481     if (object) {
482         sp_style_merge_from_object_stylesheet(style, object);
483     } else {
484         /** \todo No stylesheet information. Find out under what circumstances
485          * this occurs, and handle accordingly.  (If we really wanted to, we
486          * could probably get stylesheets by going through repr->doc.)
487          */
488     }
490     /* 2. Presentation attributes */
491     /* CSS2 */
492     SPS_READ_PENUM_IF_UNSET(&style->visibility, repr, "visibility", enum_visibility, true);
493     SPS_READ_PENUM_IF_UNSET(&style->display, repr, "display", enum_display, true);
494     SPS_READ_PENUM_IF_UNSET(&style->overflow, repr, "overflow", enum_overflow, true);
495     /* Font */
496     SPS_READ_PFONTSIZE_IF_UNSET(&style->font_size, repr, "font-size");
497     SPS_READ_PENUM_IF_UNSET(&style->font_style, repr, "font-style", enum_font_style, true);
498     SPS_READ_PENUM_IF_UNSET(&style->font_variant, repr, "font-variant", enum_font_variant, true);
499     SPS_READ_PENUM_IF_UNSET(&style->font_weight, repr, "font-weight", enum_font_weight, true);
500     SPS_READ_PENUM_IF_UNSET(&style->font_stretch, repr, "font-stretch", enum_font_stretch, true);
501     /* Text (css2 chapter 16) */
502     SPS_READ_PLENGTH_IF_UNSET(&style->text_indent, repr, "text-indent");
503     SPS_READ_PENUM_IF_UNSET(&style->text_align, repr, "text-align", enum_text_align, true);
504     if (!style->text_decoration.set) {
505         val = repr->attribute("text-decoration");
506         if (val) {
507             sp_style_read_itextdecoration(&style->text_decoration, val);
508         }
509     }
510     if (!style->line_height.set) {
511         val = repr->attribute("line-height");
512         if (val) {
513             sp_style_read_ilengthornormal(&style->line_height, val);
514         }
515     }
516     if (!style->letter_spacing.set) {
517         val = repr->attribute("letter-spacing");
518         if (val) {
519             sp_style_read_ilengthornormal(&style->letter_spacing, val);
520         }
521     }
522     if (!style->word_spacing.set) {
523         val = repr->attribute("word-spacing");
524         if (val) {
525             sp_style_read_ilengthornormal(&style->word_spacing, val);
526         }
527     }
528     SPS_READ_PENUM_IF_UNSET(&style->text_transform, repr, "text-transform", enum_text_transform, true);
529     SPS_READ_PENUM_IF_UNSET(&style->direction, repr, "direction", enum_direction, true);
530     SPS_READ_PENUM_IF_UNSET(&style->block_progression, repr, "block_progression", enum_block_progression, true);
532     /* SVG */
533     SPS_READ_PENUM_IF_UNSET(&style->writing_mode, repr, "writing-mode",
534                             enum_writing_mode, true);
535     SPS_READ_PENUM_IF_UNSET(&style->text_anchor, repr, "text-anchor",
536                             enum_text_anchor, true);
538     /* opacity */
539     if (!style->opacity.set) {
540         val = repr->attribute("opacity");
541         if (val) {
542             sp_style_read_iscale24(&style->opacity, val);
543         }
544     }
545     /* color */
546     if (!style->color.set) {
547         val = repr->attribute("color");
548         if (val) {
549             sp_style_read_icolor(&style->color, val, style, ( object
550                                                               ? SP_OBJECT_DOCUMENT(object)
551                                                               : NULL ));
552         }
553     }
554     /* fill */
555     if (!style->fill.set) {
556         val = repr->attribute("fill");
557         if (val) {
558             sp_style_read_ipaint(&style->fill, val, style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL);
559         }
560     }
561     /* fill-opacity */
562     if (!style->fill_opacity.set) {
563         val = repr->attribute("fill-opacity");
564         if (val) {
565             sp_style_read_iscale24(&style->fill_opacity, val);
566         }
567     }
568     /* fill-rule */
569     SPS_READ_PENUM_IF_UNSET(&style->fill_rule, repr, "fill-rule", enum_fill_rule, true);
570     /* stroke */
571     if (!style->stroke.set) {
572         val = repr->attribute("stroke");
573         if (val) {
574             sp_style_read_ipaint(&style->stroke, val, style, (object) ? SP_OBJECT_DOCUMENT(object) : NULL);
575         }
576     }
577     SPS_READ_PLENGTH_IF_UNSET(&style->stroke_width, repr, "stroke-width");
578     SPS_READ_PENUM_IF_UNSET(&style->stroke_linecap, repr, "stroke-linecap", enum_stroke_linecap, true);
579     SPS_READ_PENUM_IF_UNSET(&style->stroke_linejoin, repr, "stroke-linejoin", enum_stroke_linejoin, true);
580     SPS_READ_PFLOAT_IF_UNSET(&style->stroke_miterlimit, repr, "stroke-miterlimit");
582     /* markers */
583     if (!style->marker[SP_MARKER_LOC].set) {
584         val = repr->attribute("marker");
585         if (val) {
586             sp_style_read_istring(&style->marker[SP_MARKER_LOC], val);
587         }
588     }
589     if (!style->marker[SP_MARKER_LOC_START].set) {
590         val = repr->attribute("marker-start");
591         if (val) {
592             sp_style_read_istring(&style->marker[SP_MARKER_LOC_START], val);
593         }
594     }
595     if (!style->marker[SP_MARKER_LOC_MID].set) {
596         val = repr->attribute("marker-mid");
597         if (val) {
598             sp_style_read_istring(&style->marker[SP_MARKER_LOC_MID], val);
599         }
600     }
601     if (!style->marker[SP_MARKER_LOC_END].set) {
602         val = repr->attribute("marker-end");
603         if (val) {
604             sp_style_read_istring(&style->marker[SP_MARKER_LOC_END], val);
605         }
606     }
608     /* stroke-opacity */
609     if (!style->stroke_opacity.set) {
610         val = repr->attribute("stroke-opacity");
611         if (val) {
612             sp_style_read_iscale24(&style->stroke_opacity, val);
613         }
614     }
615     if (!style->stroke_dasharray_set) {
616         val = repr->attribute("stroke-dasharray");
617         if (val) {
618             sp_style_read_dash(style, val);
619         }
620     }
621     if (!style->stroke_dashoffset_set) {
622         /* fixme */
623         val = repr->attribute("stroke-dashoffset");
624         if (sp_svg_number_read_d(val, &style->stroke_dash.offset)) {
625             style->stroke_dashoffset_set = TRUE;
626         } else {
627             style->stroke_dashoffset_set = FALSE;
628         }
629     }
631     /* font-family */
632     if (!style->text_private || !style->text->font_family.set) {
633         val = repr->attribute("font-family");
634         if (val) {
635             if (!style->text_private) sp_style_privatize_text(style);
636             sp_style_read_istring(&style->text->font_family, val);
637             css2_unescape_unquote(&style->text->font_family);
638         }
639     }
641     /* 3. Merge from parent */
642     if (object) {
643         if (object->parent) {
644             sp_style_merge_from_parent(style, SP_OBJECT_STYLE(object->parent));
645         }
646     } else {
647         if (sp_repr_parent(repr)) {
648             /// \todo fixme: This is not the prettiest thing (Lauris)
649             SPStyle *parent = sp_style_new();
650             sp_style_read(parent, NULL, sp_repr_parent(repr));
651             sp_style_merge_from_parent(style, parent);
652             sp_style_unref(parent);
653         }
654     }
658 /**
659  * Read style properties from object's repr.
660  * 
661  * 1. Reset existing object style
662  * 2. Load current effective object style
663  * 3. Load i attributes from immediate parent (which has to be up-to-date)
664  */
665 void
666 sp_style_read_from_object(SPStyle *style, SPObject *object)
668     g_return_if_fail(style != NULL);
669     g_return_if_fail(object != NULL);
670     g_return_if_fail(SP_IS_OBJECT(object));
672     Inkscape::XML::Node *repr = SP_OBJECT_REPR(object);
673     g_return_if_fail(repr != NULL);
675     sp_style_read(style, object, repr);
679 /**
680  * Read style properties from repr only.
681  */
682 void
683 sp_style_read_from_repr(SPStyle *style, Inkscape::XML::Node *repr)
685     g_return_if_fail(style != NULL);
686     g_return_if_fail(repr != NULL);
688     sp_style_read(style, NULL, repr);
693 /**
694  * 
695  */
696 static void
697 sp_style_privatize_text(SPStyle *style)
699     SPTextStyle *text = style->text;
700     style->text = sp_text_style_duplicate_unset(style->text);
701     sp_text_style_unref(text);
702     style->text_private = TRUE;
706 /**
707  * Merge property into style.
708  * 
709  * Should be called in order of highest to lowest precedence.
710  * E.g. for a single style string, call from the last declaration to the first,
711  * as CSS says that later declarations override earlier ones.
712  *
713  * \pre val != NULL.
714  */
715 static void
716 sp_style_merge_property(SPStyle *style, gint id, gchar const *val)
718     g_return_if_fail(val != NULL);
720     switch (id) {
721         /* CSS2 */
722         /* Font */
723         case SP_PROP_FONT_FAMILY:
724             if (!style->text_private) sp_style_privatize_text(style);
725             if (!style->text->font_family.set) {
726                 sp_style_read_istring(&style->text->font_family, val);
727                 css2_unescape_unquote (&style->text->font_family);
728             }
729             break;
730         case SP_PROP_FONT_SIZE:
731             SPS_READ_IFONTSIZE_IF_UNSET(&style->font_size, val);
732             break;
733         case SP_PROP_FONT_SIZE_ADJUST:
734             if (strcmp(val, "none") != 0) {
735                 g_warning("Unimplemented style property id SP_PROP_FONT_SIZE_ADJUST: value: %s", val);
736             }
737             break;
738         case SP_PROP_FONT_STYLE:
739             SPS_READ_IENUM_IF_UNSET(&style->font_style, val, enum_font_style, true);
740             break;
741         case SP_PROP_FONT_VARIANT:
742             SPS_READ_IENUM_IF_UNSET(&style->font_variant, val, enum_font_variant, true);
743             break;
744         case SP_PROP_FONT_WEIGHT:
745             SPS_READ_IENUM_IF_UNSET(&style->font_weight, val, enum_font_weight, true);
746             break;
747         case SP_PROP_FONT_STRETCH:
748             SPS_READ_IENUM_IF_UNSET(&style->font_stretch, val, enum_font_stretch, true);
749             break;
750         case SP_PROP_FONT:
751             if (!style->text_private) sp_style_privatize_text(style);
752             if (!style->text->font.set) {
753                 g_free(style->text->font.value);
754                 style->text->font.value = g_strdup(val);
755                 style->text->font.set = TRUE;
756                 style->text->font.inherit = (val && !strcmp(val, "inherit"));
757             }
758             break;
759             /* Text */
760         case SP_PROP_TEXT_INDENT:
761             SPS_READ_ILENGTH_IF_UNSET(&style->text_indent, val);
762             break;
763         case SP_PROP_TEXT_ALIGN:
764             SPS_READ_IENUM_IF_UNSET(&style->text_align, val, enum_text_align, true);
765             break;
766         case SP_PROP_TEXT_DECORATION:
767             if (!style->text_decoration.set) {
768                 sp_style_read_itextdecoration(&style->text_decoration, val);
769             }
770             break;
771         case SP_PROP_LINE_HEIGHT:
772             if (!style->line_height.set) {
773                 sp_style_read_ilengthornormal(&style->line_height, val);
774             }
775             break;
776         case SP_PROP_LETTER_SPACING:
777             if (!style->letter_spacing.set) {
778                 sp_style_read_ilengthornormal(&style->letter_spacing, val);
779             }
780             break;
781         case SP_PROP_WORD_SPACING:
782             if (!style->word_spacing.set) {
783                 sp_style_read_ilengthornormal(&style->word_spacing, val);
784             }
785             break;
786         case SP_PROP_TEXT_TRANSFORM:
787             SPS_READ_IENUM_IF_UNSET(&style->text_transform, val, enum_text_transform, true);
788             break;
789             /* Text (css3) */
790         case SP_PROP_DIRECTION:
791             SPS_READ_IENUM_IF_UNSET(&style->direction, val, enum_direction, true);
792             break;
793         case SP_PROP_BLOCK_PROGRESSION:
794             SPS_READ_IENUM_IF_UNSET(&style->block_progression, val, enum_block_progression, true);
795             break;
796         case SP_PROP_WRITING_MODE:
797             SPS_READ_IENUM_IF_UNSET(&style->writing_mode, val, enum_writing_mode, true);
798             break;
799         case SP_PROP_TEXT_ANCHOR:
800             SPS_READ_IENUM_IF_UNSET(&style->text_anchor, val, enum_text_anchor, true);
801             break;
802             /* Text (unimplemented) */
803         case SP_PROP_TEXT_RENDERING: {
804             /* Ignore the hint. */
805             SPIEnum dummy;
806             SPS_READ_IENUM_IF_UNSET(&dummy, val, enum_text_rendering, true);
807             break;
808         }
809         case SP_PROP_ALIGNMENT_BASELINE:
810             g_warning("Unimplemented style property SP_PROP_ALIGNMENT_BASELINE: value: %s", val);
811             break;
812         case SP_PROP_BASELINE_SHIFT:
813             g_warning("Unimplemented style property SP_PROP_BASELINE_SHIFT: value: %s", val);
814             break;
815         case SP_PROP_DOMINANT_BASELINE:
816             g_warning("Unimplemented style property SP_PROP_DOMINANT_BASELINE: value: %s", val);
817             break;
818         case SP_PROP_GLYPH_ORIENTATION_HORIZONTAL:
819             g_warning("Unimplemented style property SP_PROP_ORIENTATION_HORIZONTAL: value: %s", val);
820             break;
821         case SP_PROP_GLYPH_ORIENTATION_VERTICAL:
822             g_warning("Unimplemented style property SP_PROP_ORIENTATION_VERTICAL: value: %s", val);
823             break;
824         case SP_PROP_KERNING:
825             g_warning("Unimplemented style property SP_PROP_KERNING: value: %s", val);
826             break;
827             /* Misc */
828         case SP_PROP_CLIP:
829             g_warning("Unimplemented style property SP_PROP_CLIP: value: %s", val);
830             break;
831         case SP_PROP_COLOR:
832             if (!style->color.set) {
833                 sp_style_read_icolor(&style->color, val, style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL);
834             }
835             break;
836         case SP_PROP_CURSOR:
837             g_warning("Unimplemented style property SP_PROP_CURSOR: value: %s", val);
838             break;
839         case SP_PROP_DISPLAY:
840             SPS_READ_IENUM_IF_UNSET(&style->display, val, enum_display, true);
841             break;
842         case SP_PROP_OVERFLOW:
843             /** \todo 
844              * FIXME: not supported properly yet, we just read and write it, 
845              * but act as if it is always "display".
846              */
847             SPS_READ_IENUM_IF_UNSET(&style->overflow, val, enum_overflow, true);
848             break;
849         case SP_PROP_VISIBILITY:
850             SPS_READ_IENUM_IF_UNSET(&style->visibility, val, enum_visibility, true);
851             break;
852             /* SVG */
853             /* Clip/Mask */
854         case SP_PROP_CLIP_PATH:
855             g_warning("Unimplemented style property SP_PROP_CLIP_PATH: value: %s", val);
856             break;
857         case SP_PROP_CLIP_RULE:
858             g_warning("Unimplemented style property SP_PROP_CLIP_RULE: value: %s", val);
859             break;
860         case SP_PROP_MASK:
861             g_warning("Unimplemented style property SP_PROP_MASK: value: %s", val);
862             break;
863         case SP_PROP_OPACITY:
864             if (!style->opacity.set) {
865                 sp_style_read_iscale24(&style->opacity, val);
866             }
867             break;
868             /* Filter */
869         case SP_PROP_ENABLE_BACKGROUND:
870             g_warning("Unimplemented style property SP_PROP_ENABLE_BACKGROUND: value: %s", val);
871             break;
872         case SP_PROP_FILTER:
873             g_warning("Unimplemented style property SP_PROP_FILTER: value: %s", val);
874             break;
875         case SP_PROP_FLOOD_COLOR:
876             g_warning("Unimplemented style property SP_PROP_FLOOD_COLOR: value: %s", val);
877             break;
878         case SP_PROP_FLOOD_OPACITY:
879             g_warning("Unimplemented style property SP_PROP_FLOOD_OPACITY: value: %s", val);
880             break;
881         case SP_PROP_LIGHTING_COLOR:
882             g_warning("Unimplemented style property SP_PROP_LIGHTING_COLOR: value: %s", val);
883             break;
884             /* Gradient */
885         case SP_PROP_STOP_COLOR:
886             g_warning("Unimplemented style property SP_PROP_STOP_COLOR: value: %s", val);
887             break;
888         case SP_PROP_STOP_OPACITY:
889             g_warning("Unimplemented style property SP_PROP_STOP_OPACITY: value: %s", val);
890             break;
891             /* Interactivity */
892         case SP_PROP_POINTER_EVENTS:
893             g_warning("Unimplemented style property SP_PROP_POINTER_EVENTS: value: %s", val);
894             break;
895             /* Paint */
896         case SP_PROP_COLOR_INTERPOLATION:
897             g_warning("Unimplemented style property SP_PROP_COLOR_INTERPOLATION: value: %s", val);
898             break;
899         case SP_PROP_COLOR_INTERPOLATION_FILTERS:
900             g_warning("Unimplemented style property SP_PROP_INTERPOLATION_FILTERS: value: %s", val);
901             break;
902         case SP_PROP_COLOR_PROFILE:
903             g_warning("Unimplemented style property SP_PROP_COLOR_PROFILE: value: %s", val);
904             break;
905         case SP_PROP_COLOR_RENDERING: {
906             /* Ignore the hint. */
907             SPIEnum dummy;
908             SPS_READ_IENUM_IF_UNSET(&dummy, val, enum_color_rendering, true);
909             break;
910         }
911         case SP_PROP_FILL:
912             if (!style->fill.set) {
913                 sp_style_read_ipaint(&style->fill, val, style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL);
914             }
915             break;
916         case SP_PROP_FILL_OPACITY:
917             if (!style->fill_opacity.set) {
918                 sp_style_read_iscale24(&style->fill_opacity, val);
919             }
920             break;
921         case SP_PROP_FILL_RULE:
922             if (!style->fill_rule.set) {
923                 sp_style_read_ienum(&style->fill_rule, val, enum_fill_rule, true);
924             }
925             break;
926         case SP_PROP_IMAGE_RENDERING: {
927             /* Ignore the hint. */
928             SPIEnum dummy;
929             SPS_READ_IENUM_IF_UNSET(&dummy, val, enum_image_rendering, true);
930             break;
931         }
932         case SP_PROP_MARKER:
933             /* TODO:  Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
934             /* style->marker[SP_MARKER_LOC] = g_quark_from_string(val); */
935             marker_status("Setting SP_PROP_MARKER");
936             if (!style->marker[SP_MARKER_LOC].set) {
937                 g_free(style->marker[SP_MARKER_LOC].value);
938                 style->marker[SP_MARKER_LOC].value = g_strdup(val);
939                 style->marker[SP_MARKER_LOC].set = TRUE;
940                 style->marker[SP_MARKER_LOC].inherit = (val && !strcmp(val, "inherit"));
941             }
942             break;
944         case SP_PROP_MARKER_START:
945             /* TODO:  Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
946             marker_status("Setting SP_PROP_MARKER_START");
947             if (!style->marker[SP_MARKER_LOC_START].set) {
948                 g_free(style->marker[SP_MARKER_LOC_START].value);
949                 style->marker[SP_MARKER_LOC_START].value = g_strdup(val);
950                 style->marker[SP_MARKER_LOC_START].set = TRUE;
951                 style->marker[SP_MARKER_LOC_START].inherit = (val && !strcmp(val, "inherit"));
952             }
953             break;
954         case SP_PROP_MARKER_MID:
955             /* TODO:  Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
956             marker_status("Setting SP_PROP_MARKER_MID");
957             if (!style->marker[SP_MARKER_LOC_MID].set) {
958                 g_free(style->marker[SP_MARKER_LOC_MID].value);
959                 style->marker[SP_MARKER_LOC_MID].value = g_strdup(val);
960                 style->marker[SP_MARKER_LOC_MID].set = TRUE;
961                 style->marker[SP_MARKER_LOC_MID].inherit = (val && !strcmp(val, "inherit"));
962             }
963             break;
964         case SP_PROP_MARKER_END:
965             /* TODO:  Call sp_uri_reference_resolve(SPDocument *document, guchar const *uri) */
966             marker_status("Setting SP_PROP_MARKER_END");
967             if (!style->marker[SP_MARKER_LOC_END].set) {
968                 g_free(style->marker[SP_MARKER_LOC_END].value);
969                 style->marker[SP_MARKER_LOC_END].value = g_strdup(val);
970                 style->marker[SP_MARKER_LOC_END].set = TRUE;
971                 style->marker[SP_MARKER_LOC_END].inherit = (val && !strcmp(val, "inherit"));
972             }
973             break;
975         case SP_PROP_SHAPE_RENDERING: {
976             /* Ignore the hint. */
977             SPIEnum dummy;
978             SPS_READ_IENUM_IF_UNSET(&dummy, val, enum_shape_rendering, true);
979             break;
980         }
982         case SP_PROP_STROKE:
983             if (!style->stroke.set) {
984                 sp_style_read_ipaint(&style->stroke, val, style, (style->object) ? SP_OBJECT_DOCUMENT(style->object) : NULL);
985             }
986             break;
987         case SP_PROP_STROKE_WIDTH:
988             SPS_READ_ILENGTH_IF_UNSET(&style->stroke_width, val);
989             break;
990         case SP_PROP_STROKE_DASHARRAY:
991             if (!style->stroke_dasharray_set) {
992                 sp_style_read_dash(style, val);
993             }
994             break;
995         case SP_PROP_STROKE_DASHOFFSET:
996             if (!style->stroke_dashoffset_set) {
997                 /* fixme */
998                 if (sp_svg_number_read_d(val, &style->stroke_dash.offset)) {
999                     style->stroke_dashoffset_set = TRUE;
1000                 } else {
1001                     style->stroke_dashoffset_set = FALSE;
1002                 }
1003             }
1004             break;
1005         case SP_PROP_STROKE_LINECAP:
1006             if (!style->stroke_linecap.set) {
1007                 sp_style_read_ienum(&style->stroke_linecap, val, enum_stroke_linecap, true);
1008             }
1009             break;
1010         case SP_PROP_STROKE_LINEJOIN:
1011             if (!style->stroke_linejoin.set) {
1012                 sp_style_read_ienum(&style->stroke_linejoin, val, enum_stroke_linejoin, true);
1013             }
1014             break;
1015         case SP_PROP_STROKE_MITERLIMIT:
1016             if (!style->stroke_miterlimit.set) {
1017                 sp_style_read_ifloat(&style->stroke_miterlimit, val);
1018             }
1019             break;
1020         case SP_PROP_STROKE_OPACITY:
1021             if (!style->stroke_opacity.set) {
1022                 sp_style_read_iscale24(&style->stroke_opacity, val);
1023             }
1024             break;
1026         default:
1027             g_warning("Invalid style property id: %d value: %s", id, val);
1028             break;
1029     }
1032 static void
1033 sp_style_merge_style_from_decl(SPStyle *const style, CRDeclaration const *const decl)
1035     /** \todo Ensure that property is lcased, as per 
1036      * http://www.w3.org/TR/REC-CSS2/syndata.html#q4.
1037      * Should probably be done in libcroco.
1038      */
1039     unsigned const prop_idx = sp_attribute_lookup(decl->property->stryng->str);
1040     if (prop_idx != SP_ATTR_INVALID) {
1041         /** \todo 
1042          * effic: Test whether the property is already set before trying to 
1043          * convert to string. Alternatively, set from CRTerm directly rather 
1044          * than converting to string.
1045          */
1046         guchar *const str_value_unsigned = cr_term_to_string(decl->value);
1047         gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned);
1048         sp_style_merge_property(style, prop_idx, str_value);
1049         g_free(str_value);
1050     }
1053 static void
1054 sp_style_merge_from_props(SPStyle *const style, CRPropList *const props)
1056 #if 0 /* forwards */
1057     for (CRPropList const *cur = props; cur; cur = cr_prop_list_get_next(cur)) {
1058         CRDeclaration *decl = NULL;
1059         cr_prop_list_get_decl(cur, &decl);
1060         sp_style_merge_style_from_decl(style, decl);
1061     }
1062 #else /* in reverse order, as we need later declarations to take precedence over earlier ones. */
1063     if (props) {
1064         sp_style_merge_from_props(style, cr_prop_list_get_next(props));
1065         CRDeclaration *decl = NULL;
1066         cr_prop_list_get_decl(props, &decl);
1067         sp_style_merge_style_from_decl(style, decl);
1068     }
1069 #endif
1072 /**
1073  * \pre decl_list != NULL
1074  */
1075 static void
1076 sp_style_merge_from_decl_list(SPStyle *const style, CRDeclaration const *const decl_list)
1078     if (decl_list->next) {
1079         sp_style_merge_from_decl_list(style, decl_list->next);
1080     }
1081     sp_style_merge_style_from_decl(style, decl_list);
1084 static CRSelEng *
1085 sp_repr_sel_eng()
1087     CRSelEng *const ret = cr_sel_eng_new();
1088     cr_sel_eng_set_node_iface(ret, &Inkscape::XML::croco_node_iface);
1090     /** \todo
1091      * Check whether we need to register any pseudo-class handlers.
1092      * libcroco has its own default handlers for first-child and lang.
1093      *
1094      * We probably want handlers for link and arguably visited (though 
1095      * inkscape can't visit links at the time of writing).  hover etc. 
1096      * more useful in inkview than the editor inkscape.
1097      *
1098      * http://www.w3.org/TR/SVG11/styling.html#StylingWithCSS says that 
1099      * the following should be honoured, at least by inkview: 
1100      * :hover, :active, :focus, :visited, :link.
1101      */
1103     g_assert(ret);
1104     return ret;
1107 static void
1108 sp_style_merge_from_object_stylesheet(SPStyle *const style, SPObject const *const object)
1110     static CRSelEng *sel_eng = NULL;
1111     if (!sel_eng) {
1112         sel_eng = sp_repr_sel_eng();
1113     }
1115     CRPropList *props = NULL;
1116     CRStatus status = cr_sel_eng_get_matched_properties_from_cascade(sel_eng,
1117                                                                      object->document->style_cascade,
1118                                                                      object->repr,
1119                                                                      &props);
1120     g_return_if_fail(status == CR_OK);
1121     /// \todo Check what errors can occur, and handle them properly.
1122     if (props) {
1123         sp_style_merge_from_props(style, props);
1124         cr_prop_list_destroy(props);
1125     }
1128 /**
1129  * Parses a style="..." string and merges it with an existing SPStyle.
1130  */
1131 void
1132 sp_style_merge_from_style_string(SPStyle *const style, gchar const *const p)
1134     /*
1135      * Reference: http://www.w3.org/TR/SVG11/styling.html#StyleAttribute: 
1136      * ``When CSS styling is used, CSS inline style is specified by including 
1137      * semicolon-separated property declarations of the form "name : value" 
1138      * within the style attribute''.
1139      *
1140      * That's fairly ambiguous.  Is a `value' allowed to contain semicolons?  
1141      * Why does it say "including", what else is allowed in the style 
1142      * attribute value?
1143      */
1145     CRDeclaration *const decl_list
1146         = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8);
1147     if (decl_list) {
1148         sp_style_merge_from_decl_list(style, decl_list);
1149         cr_declaration_destroy(decl_list);
1150     }
1153 /** Indexed by SP_CSS_FONT_SIZE_blah. */
1154 static float const font_size_table[] = {6.0, 8.0, 10.0, 12.0, 14.0, 18.0, 24.0};
1156 static void
1157 sp_style_merge_font_size_from_parent(SPIFontSize &child, SPIFontSize const &parent)
1159     /* 'font-size' */
1160     if (!child.set || child.inherit) {
1161         /* Inherit the computed value.  Reference: http://www.w3.org/TR/SVG11/styling.html#Inheritance */
1162         child.computed = parent.computed;
1163     } else if (child.type == SP_FONT_SIZE_LITERAL) {
1164         /** \todo
1165          * fixme: SVG and CSS do not specify clearly, whether we should use 
1166          * user or screen coordinates (Lauris)
1167          */
1168         if (child.value < SP_CSS_FONT_SIZE_SMALLER) {
1169             child.computed = font_size_table[child.value];
1170         } else if (child.value == SP_CSS_FONT_SIZE_SMALLER) {
1171             child.computed = parent.computed / 1.2;
1172         } else if (child.value == SP_CSS_FONT_SIZE_LARGER) {
1173             child.computed = parent.computed * 1.2;
1174         } else {
1175             /* Illegal value */
1176         }
1177     } else if (child.type == SP_FONT_SIZE_PERCENTAGE) {
1178         /* Unlike most other lengths, percentage for font size is relative to parent computed value
1179          * rather than viewport. */
1180         child.computed = parent.computed * SP_F8_16_TO_FLOAT(child.value);
1181     }
1184 /**
1185  * Sets computed values in \a style, which may involve inheriting from (or in some other way
1186  * calculating from) corresponding computed values of \a parent.
1187  *
1188  * References: http://www.w3.org/TR/SVG11/propidx.html shows what properties inherit by default.
1189  * http://www.w3.org/TR/SVG11/styling.html#Inheritance gives general rules as to what it means to
1190  * inherit a value.  http://www.w3.org/TR/REC-CSS2/cascade.html#computed-value is more precise
1191  * about what the computed value is (not obvious for lengths).
1192  *
1193  * \pre \a parent's computed values are already up-to-date.
1194  */
1195 void
1196 sp_style_merge_from_parent(SPStyle *const style, SPStyle const *const parent)
1198     g_return_if_fail(style != NULL);
1200     /** \todo
1201      * fixme: Check for existing callers that might pass null parent.  
1202      * This should probably be g_return_if_fail, or else we should make a 
1203      * best attempt to set computed values correctly without having a parent 
1204      * (i.e., by assuming parent has initial values).
1205      */
1206     if (!parent)
1207         return;
1209     /* CSS2 */
1210     /* Font */
1211     sp_style_merge_font_size_from_parent(style->font_size, parent->font_size);
1213     /* 'font-style' */
1214     if (!style->font_style.set || style->font_style.inherit) {
1215         style->font_style.computed = parent->font_style.computed;
1216     }
1218     /* 'font-variant' */
1219     if (!style->font_variant.set || style->font_variant.inherit) {
1220         style->font_variant.computed = parent->font_variant.computed;
1221     }
1223     /* 'font-weight' */
1224     if (!style->font_weight.set || style->font_weight.inherit) {
1225         style->font_weight.computed = parent->font_weight.computed;
1226     } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_NORMAL) {
1227         /** \todo
1228          * fixme: This is unconditional, i.e., happens even if parent not 
1229          * present.
1230          */
1231         style->font_weight.computed = SP_CSS_FONT_WEIGHT_400;
1232     } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) {
1233         style->font_weight.computed = SP_CSS_FONT_WEIGHT_700;
1234     } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_LIGHTER) {
1235         unsigned const parent_val = parent->font_weight.computed;
1236         g_assert(SP_CSS_FONT_WEIGHT_100 == 0);
1237         // strictly, 'bolder' and 'lighter' should go to the next weight
1238         // expressible in the current font family, but that's difficult to
1239         // find out, so jumping by 3 seems an appropriate approximation
1240         style->font_weight.computed = (parent_val <= SP_CSS_FONT_WEIGHT_100 + 3
1241                            ? (unsigned)SP_CSS_FONT_WEIGHT_100
1242                            : parent_val - 3);
1243         g_assert(style->font_weight.computed <= (unsigned) SP_CSS_FONT_WEIGHT_900);
1244     } else if (style->font_weight.value == SP_CSS_FONT_WEIGHT_BOLDER) {
1245         unsigned const parent_val = parent->font_weight.computed;
1246         g_assert(parent_val <= SP_CSS_FONT_WEIGHT_900);
1247         style->font_weight.computed = (parent_val >= SP_CSS_FONT_WEIGHT_900 - 3
1248                            ? (unsigned)SP_CSS_FONT_WEIGHT_900
1249                            : parent_val + 3);
1250         g_assert(style->font_weight.computed <= (unsigned) SP_CSS_FONT_WEIGHT_900);
1251     }
1253     /* 'font-stretch' */
1254     if (!style->font_stretch.set || style->font_stretch.inherit) {
1255         style->font_stretch.computed = parent->font_stretch.computed;
1256     } else if (style->font_stretch.value == SP_CSS_FONT_STRETCH_NARROWER) {
1257         unsigned const parent_val = parent->font_stretch.computed;
1258         style->font_stretch.computed = (parent_val == SP_CSS_FONT_STRETCH_ULTRA_CONDENSED
1259                         ? parent_val
1260                         : parent_val - 1);
1261         g_assert(style->font_stretch.computed <= (unsigned) SP_CSS_FONT_STRETCH_ULTRA_EXPANDED);
1262     } else if (style->font_stretch.value == SP_CSS_FONT_STRETCH_WIDER) {
1263         unsigned const parent_val = parent->font_stretch.computed;
1264         g_assert(parent_val <= SP_CSS_FONT_STRETCH_ULTRA_EXPANDED);
1265         style->font_stretch.computed = (parent_val == SP_CSS_FONT_STRETCH_ULTRA_EXPANDED
1266                         ? parent_val
1267                         : parent_val + 1);
1268         g_assert(style->font_stretch.computed <= (unsigned) SP_CSS_FONT_STRETCH_ULTRA_EXPANDED);
1269     }
1271     /* text (css2) */
1272     if (!style->text_indent.set || style->text_indent.inherit) {
1273         style->text_indent.computed = parent->text_indent.computed;
1274     }
1276     if (!style->text_align.set || style->text_align.inherit) {
1277         style->text_align.computed = parent->text_align.computed;
1278     }
1280     if (!style->text_decoration.set || style->text_decoration.inherit) {
1281         style->text_decoration.underline = parent->text_decoration.underline;
1282         style->text_decoration.overline = parent->text_decoration.overline;
1283         style->text_decoration.line_through = parent->text_decoration.line_through;
1284         style->text_decoration.blink = parent->text_decoration.blink;
1285     }
1287     if (!style->line_height.set || style->line_height.inherit) {
1288         style->line_height.computed = parent->line_height.computed;
1289         style->line_height.normal = parent->line_height.normal;
1290     }
1292     if (!style->letter_spacing.set || style->letter_spacing.inherit) {
1293         style->letter_spacing.computed = parent->letter_spacing.computed;
1294         style->letter_spacing.normal = parent->letter_spacing.normal;
1295     }
1297     if (!style->word_spacing.set || style->word_spacing.inherit) {
1298         style->word_spacing.computed = parent->word_spacing.computed;
1299         style->word_spacing.normal = parent->word_spacing.normal;
1300     }
1302     if (!style->text_transform.set || style->text_transform.inherit) {
1303         style->text_transform.computed = parent->text_transform.computed;
1304     }
1306     if (!style->direction.set || style->direction.inherit) {
1307         style->direction.computed = parent->direction.computed;
1308     }
1310     if (!style->block_progression.set || style->block_progression.inherit) {
1311         style->block_progression.computed = parent->block_progression.computed;
1312     }
1314     if (!style->writing_mode.set || style->writing_mode.inherit) {
1315         style->writing_mode.computed = parent->writing_mode.computed;
1316     }
1318     if (!style->text_anchor.set || style->text_anchor.inherit) {
1319         style->text_anchor.computed = parent->text_anchor.computed;
1320     }
1322     if (style->opacity.inherit) {
1323         style->opacity.value = parent->opacity.value;
1324     }
1326     /* Color */
1327     if (!style->color.set || style->color.inherit) {
1328         sp_style_merge_ipaint(style, &style->color, &parent->color);
1329     }
1331     /* Fill */
1332     if (!style->fill.set || style->fill.inherit || style->fill.currentcolor) {
1333         sp_style_merge_ipaint(style, &style->fill, &parent->fill);
1334     }
1336     if (!style->fill_opacity.set || style->fill_opacity.inherit) {
1337         style->fill_opacity.value = parent->fill_opacity.value;
1338     }
1340     if (!style->fill_rule.set || style->fill_rule.inherit) {
1341         style->fill_rule.computed = parent->fill_rule.computed;
1342     }
1344     /* Stroke */
1345     if (!style->stroke.set || style->stroke.inherit || style->stroke.currentcolor) {
1346         sp_style_merge_ipaint(style, &style->stroke, &parent->stroke);
1347     }
1349     if (!style->stroke_width.set || style->stroke_width.inherit) {
1350         style->stroke_width.computed = parent->stroke_width.computed;
1351     } else {
1352         /* Update computed value for any change in font inherited from parent. */
1353         double const em = style->font_size.computed;
1354         if (style->stroke_width.unit == SP_CSS_UNIT_EM) {
1355             style->stroke_width.computed = style->stroke_width.value * em;
1356         } else if (style->stroke_width.unit == SP_CSS_UNIT_EX) {
1357             double const ex = em * 0.5;  // fixme: Get x height from libnrtype or pango.
1358             style->stroke_width.computed = style->stroke_width.value * ex;
1359         }
1360     }
1362     if (!style->stroke_linecap.set || style->stroke_linecap.inherit) {
1363         style->stroke_linecap.computed = parent->stroke_linecap.computed;
1364     }
1366     if (!style->stroke_linejoin.set || style->stroke_linejoin.inherit) {
1367         style->stroke_linejoin.computed = parent->stroke_linejoin.computed;
1368     }
1370     if (!style->stroke_miterlimit.set || style->stroke_miterlimit.inherit) {
1371         style->stroke_miterlimit.value = parent->stroke_miterlimit.value;
1372     }
1374     if (!style->stroke_dasharray_set && parent->stroke_dasharray_set) {
1375         /** \todo
1376          * This code looks wrong.  Why does the logic differ from the 
1377          * above properties? Similarly dashoffset below.
1378          */
1379         style->stroke_dash.n_dash = parent->stroke_dash.n_dash;
1380         if (style->stroke_dash.n_dash > 0) {
1381             style->stroke_dash.dash = g_new(gdouble, style->stroke_dash.n_dash);
1382             memcpy(style->stroke_dash.dash, parent->stroke_dash.dash, style->stroke_dash.n_dash * sizeof(gdouble));
1383         }
1384     }
1386     if (!style->stroke_dashoffset_set && parent->stroke_dashoffset_set) {
1387         style->stroke_dash.offset = parent->stroke_dash.offset;
1388     }
1390     if (!style->stroke_opacity.set || style->stroke_opacity.inherit) {
1391         style->stroke_opacity.value = parent->stroke_opacity.value;
1392     }
1394     if (style->text && parent->text) {
1395         if (!style->text->font_family.set || style->text->font_family.inherit) {
1396             g_free(style->text->font_family.value);
1397             style->text->font_family.value = g_strdup(parent->text->font_family.value);
1398         }
1399     }
1401     /* Markers - Free the old value and make copy of the new */
1402     for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) {
1403         if (!style->marker[i].set || style->marker[i].inherit) {
1404             g_free(style->marker[i].value);
1405             style->marker[i].value = g_strdup(parent->marker[i].value);
1406         }
1407     }
1410 template <typename T>
1411 static void
1412 sp_style_merge_prop_from_dying_parent(T &child, T const &parent)
1414     if ( ( !(child.set) || child.inherit )
1415          && parent.set && !(parent.inherit) )
1416     {
1417         child = parent;
1418     }
1421 /**
1422  * Copy SPIString from parent to child.
1423  */
1424 static void
1425 sp_style_merge_string_prop_from_dying_parent(SPIString &child, SPIString const &parent)
1427     if ( ( !(child.set) || child.inherit )
1428          && parent.set && !(parent.inherit) )
1429     {
1430         g_free(child.value);
1431         child.value = g_strdup(parent.value);
1432         child.set = parent.set;
1433         child.inherit = parent.inherit;
1434     }
1437 static void
1438 sp_style_merge_paint_prop_from_dying_parent(SPStyle *style,
1439                                             SPIPaint &child, SPIPaint const &parent)
1441     /** \todo
1442      * I haven't given this much attention.  See comments below about 
1443      * currentColor, colorProfile, and relative URIs.
1444      */
1445     if (!child.set || child.inherit || child.currentcolor) {
1446         sp_style_merge_ipaint(style, &child, &parent);
1447         child.set = parent.set;
1448         child.inherit = parent.inherit;
1449     }
1452 static void
1453 sp_style_merge_rel_enum_prop_from_dying_parent(SPIEnum &child, SPIEnum const &parent,
1454                                                unsigned const max_computed_val,
1455                                                unsigned const smaller_val)
1457     /* We assume that min computed val is 0, contiguous up to max_computed_val,
1458        then zero or more other absolute values, then smaller_val then larger_val. */
1459     unsigned const min_computed_val = 0;
1460     unsigned const larger_val = smaller_val + 1;
1461     g_return_if_fail(min_computed_val < max_computed_val);
1462     g_return_if_fail(max_computed_val < smaller_val);
1464     if (parent.set && !parent.inherit) {
1465         if (!child.set || child.inherit) {
1466             child.value = parent.value;
1467             child.set = parent.set;  // i.e. true
1468             child.inherit = parent.inherit;  // i.e. false
1469         } else if (child.value < smaller_val) {
1470             /* Child has absolute value: leave as is. */
1471         } else if ( ( child.value == smaller_val
1472                       && parent.value == larger_val )
1473                     || ( parent.value == smaller_val
1474                          && child.value == larger_val ) )
1475         {
1476             child.set = false;
1477             /*
1478              * Note that this can result in a change in computed value in the 
1479              * rare case that the parent's setting was a no-op (i.e. if the 
1480              * parent's parent's computed value was already ultra-condensed or 
1481              * ultra-expanded).  However, I'd guess that the change is for the 
1482              * better: I'd guess that if the properties were specified
1483              * relatively, then the intent is to counteract parent's effect.
1484              * I.e. I believe this is the best code even in that case.
1485              */
1486         } else if (child.value == parent.value) {
1487             /* Leave as is. */
1488             /** \todo
1489              * It's unclear what to do if style and parent specify the same 
1490              * relative directive (narrower or wider).  We can either convert 
1491              * to absolute specification or coalesce to a single relative 
1492              * request (of half the strength of the original pair).
1493              *
1494              * Converting to a single level of relative specification is a 
1495              * better choice if the newly-unlinked clone is itself cloned to 
1496              * other contexts (inheriting different font stretchiness): it 
1497              * would preserve the effect that it will be narrower than
1498              * the inherited context in each case.  The disadvantage is that 
1499              * it will ~certainly affect the computed value of the 
1500              * newly-unlinked clone.
1501              */
1502         } else {
1503             unsigned const parent_val = parent.computed;
1504             child.value = ( child.value == smaller_val
1505                             ? ( parent_val == min_computed_val
1506                                 ? parent_val
1507                                 : parent_val - 1 )
1508                             : ( parent_val == max_computed_val
1509                                 ? parent_val
1510                                 : parent_val + 1 ) );
1511             g_assert(child.value <= max_computed_val);
1512             child.inherit = false;
1513             g_assert(child.set);
1514         }
1515     }
1518 template <typename LengthT>
1519 static void
1520 sp_style_merge_length_prop_from_dying_parent(LengthT &child, LengthT const &parent,
1521                                              double const parent_child_em_ratio)
1523     if ( ( !(child.set) || child.inherit )
1524          && parent.set && !(parent.inherit) )
1525     {
1526         child = parent;
1527         switch (parent.unit) {
1528             case SP_CSS_UNIT_EM:
1529             case SP_CSS_UNIT_EX:
1530                 child.value *= parent_child_em_ratio;
1531                 /** \todo
1532                  * fixme: Have separate ex ratio parameter. 
1533                  * Get x height from libnrtype or pango.
1534                  */
1535                 if (!isFinite(child.value)) {
1536                     child.value = child.computed;
1537                     child.unit = SP_CSS_UNIT_NONE;
1538                 }
1539                 break;
1541             default:
1542                 break;
1543         }
1544     }
1547 static double
1548 get_relative_font_size_frac(SPIFontSize const &font_size)
1550     switch (font_size.type) {
1551         case SP_FONT_SIZE_LITERAL: {
1552             switch (font_size.value) {
1553                 case SP_CSS_FONT_SIZE_SMALLER:
1554                     return 5.0 / 6.0;
1556                 case SP_CSS_FONT_SIZE_LARGER:
1557                     return 6.0 / 5.0;
1559                 default:
1560                     g_assert_not_reached();
1561             }
1562         }
1564         case SP_FONT_SIZE_PERCENTAGE:
1565             return SP_F8_16_TO_FLOAT(font_size.value);
1567         case SP_FONT_SIZE_LENGTH:
1568             g_assert_not_reached();
1569     }
1570     g_assert_not_reached();
1573 /**
1574  * Combine \a style and \a parent style specifications into a single style specification that
1575  * preserves (as much as possible) the effect of the existing \a style being a child of \a parent.
1576  *
1577  * Called when the parent repr is to be removed (e.g. the parent is a \<use\> element that is being
1578  * unlinked), in which case we copy/adapt property values that are explicitly set in \a parent,
1579  * trying to retain the same visual appearance once the parent is removed.  Interesting cases are
1580  * when there is unusual interaction with the parent's value (opacity, display) or when the value
1581  * can be specified as relative to the parent computed value (font-size, font-weight etc.).
1582  *
1583  * Doesn't update computed values of \a style.  For correctness, you should subsequently call
1584  * sp_style_merge_from_parent against the new parent (presumably \a parent's parent) even if \a
1585  * style was previously up-to-date wrt \a parent.
1586  *
1587  * \pre \a parent's computed values are already up-to-date.
1588  *   (\a style's computed values needn't be up-to-date.)
1589  */
1590 void
1591 sp_style_merge_from_dying_parent(SPStyle *const style, SPStyle const *const parent)
1593     /** \note
1594      * The general rule for each property is as follows:
1595      *
1596      *   If style is set to an absolute value, then leave it as is.
1597      *
1598      *   Otherwise (i.e. if style has a relative value):
1599      *
1600      *     If parent is set to an absolute value, then set style to the computed value.
1601      *
1602      *     Otherwise, calculate the combined relative value (e.g. multiplying the two percentages).
1603      */
1605     /* We do font-size first, to ensure that em size is up-to-date. */
1606     /** \todo
1607      * fixme: We'll need to have more font-related things up the top once 
1608      * we're getting x-height from pango or libnrtype.
1609      */
1611     /* Some things that allow relative specifications. */
1612     {
1613         /* font-size.  Note that we update the computed font-size of style,
1614            to assist in em calculations later in this function. */
1615         if (parent->font_size.set && !parent->font_size.inherit) {
1616             if (!style->font_size.set || style->font_size.inherit) {
1617                 /* font_size inherits the computed value, so we can use the parent value
1618                  * verbatim. */
1619                 style->font_size = parent->font_size;
1620             } else if ( style->font_size.type == SP_FONT_SIZE_LENGTH ) {
1621                 /* Child already has absolute size (stored in computed value), so do nothing. */
1622             } else if ( style->font_size.type == SP_FONT_SIZE_LITERAL
1623                         && style->font_size.value < SP_CSS_FONT_SIZE_SMALLER ) {
1624                 /* Child already has absolute size, but we ensure that the computed value
1625                    is up-to-date. */
1626                 unsigned const ix = style->font_size.value;
1627                 g_assert(ix < G_N_ELEMENTS(font_size_table));
1628                 style->font_size.computed = font_size_table[ix];
1629             } else {
1630                 /* Child has relative size. */
1631                 double const child_frac(get_relative_font_size_frac(style->font_size));
1632                 style->font_size.set = true;
1633                 style->font_size.inherit = false;
1634                 style->font_size.computed = parent->font_size.computed * child_frac;
1636                 if ( ( parent->font_size.type == SP_FONT_SIZE_LITERAL
1637                        && parent->font_size.value < SP_CSS_FONT_SIZE_SMALLER )
1638                      || parent->font_size.type == SP_FONT_SIZE_LENGTH )
1639                 {
1640                     /* Absolute value. */
1641                     style->font_size.type = SP_FONT_SIZE_LENGTH;
1642                     /* .value is unused for SP_FONT_SIZE_LENGTH. */
1643                 } else {
1644                     /* Relative value. */
1645                     double const parent_frac(get_relative_font_size_frac(parent->font_size));
1646                     style->font_size.type = SP_FONT_SIZE_PERCENTAGE;
1647                     style->font_size.value = SP_F8_16_FROM_FLOAT(parent_frac * child_frac);
1648                 }
1649             }
1650         }
1652         /* 'font-stretch' */
1653         sp_style_merge_rel_enum_prop_from_dying_parent(style->font_stretch,
1654                                                        parent->font_stretch,
1655                                                        SP_CSS_FONT_STRETCH_ULTRA_EXPANDED,
1656                                                        SP_CSS_FONT_STRETCH_NARROWER);
1658         /* font-weight */
1659         sp_style_merge_rel_enum_prop_from_dying_parent(style->font_weight,
1660                                                        parent->font_weight,
1661                                                        SP_CSS_FONT_WEIGHT_900,
1662                                                        SP_CSS_FONT_WEIGHT_LIGHTER);
1663     }
1666     /* Enum values that don't have any relative settings (other than `inherit'). */
1667     {
1668         SPIEnum SPStyle::*const fields[] = {
1669             //nyi: SPStyle::clip_rule,
1670             //nyi: SPStyle::color_interpolation,
1671             //nyi: SPStyle::color_interpolation_filters,
1672             //nyi: SPStyle::color_rendering,
1673             &SPStyle::direction,
1674             &SPStyle::fill_rule,
1675             &SPStyle::font_style,
1676             &SPStyle::font_variant,
1677             //nyi: SPStyle::image_rendering,
1678             //nyi: SPStyle::pointer_events,
1679             //nyi: SPStyle::shape_rendering,
1680             &SPStyle::stroke_linecap,
1681             &SPStyle::stroke_linejoin,
1682             &SPStyle::text_anchor,
1683             //nyi: &SPStyle::text_rendering,
1684             &SPStyle::visibility,
1685             &SPStyle::writing_mode
1686         };
1688         for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) {
1689             SPIEnum SPStyle::*const fld = fields[i];
1690             sp_style_merge_prop_from_dying_parent<SPIEnum>(style->*fld, parent->*fld);
1691         }
1692     }
1694     /* A few other simple inheritance properties. */
1695     {
1696         sp_style_merge_prop_from_dying_parent<SPIScale24>(style->fill_opacity, parent->fill_opacity);
1697         sp_style_merge_prop_from_dying_parent<SPIScale24>(style->stroke_opacity, parent->stroke_opacity);
1698         sp_style_merge_prop_from_dying_parent<SPIFloat>(style->stroke_miterlimit, parent->stroke_miterlimit);
1700         /** \todo
1701          * We currently treat text-decoration as if it were a simple inherited 
1702          * property (fixme). This code may need changing once we do the 
1703          * special fill/stroke inheritance mentioned by the spec.
1704          */
1705         sp_style_merge_prop_from_dying_parent<SPITextDecoration>(style->text_decoration,
1706                                                                  parent->text_decoration);
1708         //nyi: font-size-adjust,  // <number> | none | inherit
1709         //nyi: glyph-orientation-horizontal,
1710         //nyi: glyph-orientation-vertical,
1711     }
1713     /* Properties that involve length but are easy in other respects. */
1714     {
1715         /* The difficulty with lengths is that font-relative units need adjusting if the font
1716          * varies between parent & child.
1717          *
1718          * Lengths specified in the existing child can stay as they are: its computed font
1719          * specification should stay unchanged, so em & ex lengths should continue to mean the same
1720          * size.
1721          *
1722          * Lengths specified in the dying parent in em or ex need to be scaled according to the
1723          * ratio of em or ex size between parent & child.
1724          */
1725         double const parent_child_em_ratio = parent->font_size.computed / style->font_size.computed;
1727         SPILength SPStyle::*const lfields[] = {
1728             &SPStyle::stroke_width,
1729             &SPStyle::text_indent
1730         };
1731         for (unsigned i = 0; i < G_N_ELEMENTS(lfields); ++i) {
1732             SPILength SPStyle::*fld = lfields[i];
1733             sp_style_merge_length_prop_from_dying_parent<SPILength>(style->*fld,
1734                                                                     parent->*fld,
1735                                                                     parent_child_em_ratio);
1736         }
1738         SPILengthOrNormal SPStyle::*const nfields[] = {
1739             &SPStyle::letter_spacing,
1740             &SPStyle::line_height,
1741             &SPStyle::word_spacing
1742         };
1743         for (unsigned i = 0; i < G_N_ELEMENTS(nfields); ++i) {
1744             SPILengthOrNormal SPStyle::*fld = nfields[i];
1745             sp_style_merge_length_prop_from_dying_parent<SPILengthOrNormal>(style->*fld,
1746                                                                             parent->*fld,
1747                                                                             parent_child_em_ratio);
1748         }
1750         //nyi: &SPStyle::kerning: length or `auto'
1752         /* fixme: Move stroke-dash and stroke-dash-offset here once they
1753            can accept units. */
1754     }
1756     /* Properties that involve a URI but are easy in other respects. */
1757     {
1758         /** \todo
1759          * Could cause problems if original object was in another document 
1760          * and it used a relative URL.  (At the time of writing, we don't 
1761          * allow hrefs to other documents, so this isn't a problem yet.)  
1762          * Paint properties also allow URIs.
1763          */
1764         //nyi: cursor,   // may involve change in effect, but we can't do much better
1765         //nyi: color-profile,
1767         // Markers (marker-start, marker-mid, marker-end).
1768         for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) {
1769             sp_style_merge_string_prop_from_dying_parent(style->marker[i], parent->marker[i]);
1770         }
1771     }
1773     /* CSS2 */
1774     /* Font */
1776     if (style->text && parent->text) {
1777         sp_style_merge_string_prop_from_dying_parent(style->text->font_family,
1778                                                      parent->text->font_family);
1779     }
1781     /* Properties that don't inherit by default.  Most of these need special handling. */
1782     {
1783         /*
1784          * opacity's effect is cumulative; we set the new value to the combined effect.  The
1785          * default value for opacity is 1.0, not inherit.  (Note that stroke-opacity and
1786          * fill-opacity are quite different from opacity, and don't need any special handling.)
1787          *
1788          * Cases:
1789          * - parent & child were each previously unset, in which case the effective
1790          *   opacity value is 1.0, and style should remain unset.
1791          * - parent was previously unset (so computed opacity value of 1.0)
1792          *   and child was set to inherit.  The merged child should
1793          *   get a value of 1.0, and shouldn't inherit (lest the new parent
1794          *   has a different opacity value).  Given that opacity's default
1795          *   value is 1.0 (rather than inherit), we might as well have the
1796          *   merged child's opacity be unset.
1797          * - parent was previously unset (so opacity 1.0), and child was set to a number.
1798          *   The merged child should retain its existing settings (though it doesn't matter
1799          *   if we make it unset if that number was 1.0).
1800          * - parent was inherit and child was unset.  Merged child should be set to inherit.
1801          * - parent was inherit and child was inherit.  (We can't in general reproduce this
1802          *   effect (short of introducing a new group), but setting opacity to inherit is rare.)
1803          *   If the inherited value was strictly between 0.0 and 1.0 (exclusive) then the merged
1804          *   child's value should be set to the product of the two, i.e. the square of the
1805          *   inherited value, and should not be marked as inherit.  (This decision assumes that it
1806          *   is more important to retain the effective opacity than to retain the inheriting
1807          *   effect, and assumes that the inheriting effect either isn't important enough to create
1808          *   a group or isn't common enough to bother maintaining the code to create a group.)  If
1809          *   the inherited value was 0.0 or 1.0, then marking the merged child as inherit comes
1810          *   closer to maintaining the effect.
1811          * - parent was inherit and child was set to a numerical value.  If the child's value
1812          *   was 1.0, then the merged child should have the same settings as the parent.
1813          *   If the child's value was 0, then the merged child should also be set to 0.
1814          *   If the child's value was anything else, then we do the same as for the inherit/inherit
1815          *   case above: have the merged child set to the product of the two opacities and not
1816          *   marked as inherit, for the same reasons as for that case.
1817          * - parent was set to a value, and child was unset.  The merged child should have
1818          *   parent's settings.
1819          * - parent was set to a value, and child was inherit.  The merged child should
1820          *   be set to the product, i.e. the square of the parent's value.
1821          * - parent & child are each set to a value.  The merged child should be set to the
1822          *   product.
1823          */
1824         if ( !style->opacity.set
1825              || ( !style->opacity.inherit
1826                   && style->opacity.value == SP_SCALE24_MAX ) )
1827         {
1828             style->opacity = parent->opacity;
1829         } else {
1830             /* Ensure that style's computed value is up-to-date. */
1831             if (style->opacity.inherit) {
1832                 style->opacity.value = parent->opacity.value;
1833             }
1835             /* Multiplication of opacities occurs even if a child's opacity is set to inherit. */
1836             style->opacity.value = SP_SCALE24_MUL(style->opacity.value,
1837                                                   parent->opacity.value);
1839             style->opacity.inherit = (parent->opacity.inherit
1840                                       && style->opacity.inherit
1841                                       && (parent->opacity.value == 0 ||
1842                                           parent->opacity.value == SP_SCALE24_MAX));
1843             style->opacity.set = ( style->opacity.inherit
1844                                    || style->opacity.value < SP_SCALE24_MAX );
1845         }
1847         /* display is in principle similar to opacity, but implementation is easier. */
1848         if ( parent->display.set && !parent->display.inherit
1849                     && parent->display.value == SP_CSS_DISPLAY_NONE ) {
1850             style->display.value = SP_CSS_DISPLAY_NONE;
1851             style->display.set = true;
1852             style->display.inherit = false;
1853         } else if (style->display.inherit) {
1854             style->display.value = parent->display.value;
1855             style->display.set = parent->display.set;
1856             style->display.inherit = parent->display.inherit;
1857         } else {
1858             /* Leave as is.  (display doesn't inherit by default.) */
1859         }
1861         /** \todo
1862          * fixme: Check that we correctly handle all properties that don't 
1863          * inherit by default (as shown in 
1864          * http://www.w3.org/TR/SVG11/propidx.html for most SVG 1.1 properties).
1865          */
1866     }
1868     /* SPIPaint properties (including color). */
1869     {
1870         /** \todo
1871          * Think about the issues involved if specified as currentColor or 
1872          * if specified relative to colorProfile, and if the currentColor or 
1873          * colorProfile differs between parent \& child.  See also comments 
1874          * elsewhere in this function about URIs.
1875          */
1876         SPIPaint SPStyle::*const fields[] = {
1877             &SPStyle::color,
1878             &SPStyle::fill,
1879             &SPStyle::stroke
1880         };
1881         for (unsigned i = 0; i < G_N_ELEMENTS(fields); ++i) {
1882             SPIPaint SPStyle::*const fld = fields[i];
1883             sp_style_merge_paint_prop_from_dying_parent(style, style->*fld, parent->*fld);
1884         }
1885     }
1887     /* Things from SVG 1.2 or CSS3. */
1888     {
1889         /* Note: If we ever support setting string values for text-align then we'd need strdup
1890          * handling here. */
1891         sp_style_merge_prop_from_dying_parent<SPIEnum>(style->text_align, parent->text_align);
1893         sp_style_merge_prop_from_dying_parent<SPIEnum>(style->text_transform, parent->text_transform);
1894         sp_style_merge_prop_from_dying_parent<SPIEnum>(style->block_progression, parent->block_progression);
1895     }
1897     /* Note: this will need length handling once dasharray supports units. */
1898     if ( ( !style->stroke_dasharray_set || style->stroke_dasharray_inherit )
1899          && parent->stroke_dasharray_set && !parent->stroke_dasharray_inherit )
1900     {
1901         style->stroke_dash.n_dash = parent->stroke_dash.n_dash;
1902         if (style->stroke_dash.n_dash > 0) {
1903             style->stroke_dash.dash = g_new(gdouble, style->stroke_dash.n_dash);
1904             memcpy(style->stroke_dash.dash, parent->stroke_dash.dash, style->stroke_dash.n_dash * sizeof(gdouble));
1905         }
1906         style->stroke_dasharray_set = parent->stroke_dasharray_set;
1907         style->stroke_dasharray_inherit = parent->stroke_dasharray_inherit;
1908     }
1910     /* Note: this will need length handling once dasharray_offset supports units. */
1911     if (!style->stroke_dashoffset_set && parent->stroke_dashoffset_set) {
1912         style->stroke_dash.offset = parent->stroke_dash.offset;
1913         style->stroke_dashoffset_set = parent->stroke_dashoffset_set;
1914         /* fixme: we don't currently allow stroke-dashoffset to be `inherit'.  TODO: Try to
1915          * represent it as a normal SPILength; though will need to do something about existing
1916          * users of stroke_dash.offset and stroke_dashoffset_set. */
1917     }
1922 /**
1923  * Disconnects from possible fill and stroke paint servers.
1924  */
1925 static void
1926 sp_style_paint_server_release(SPPaintServer *server, SPStyle *style)
1928     if ((style->fill.type == SP_PAINT_TYPE_PAINTSERVER)
1929         && (server == style->fill.value.paint.server))
1930     {
1931         sp_style_paint_clear(style, &style->fill, TRUE, FALSE);
1932     }
1934     if ((style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)
1935         && (server == style->stroke.value.paint.server))
1936     {
1937         sp_style_paint_clear(style, &style->stroke, TRUE, FALSE);
1938     }
1944 /**
1945  * Emit style modified signal on style's object if server is style's fill
1946  * or stroke paint server.
1947  */
1948 static void
1949 sp_style_paint_server_modified(SPPaintServer *server, guint flags, SPStyle *style)
1951     if ((style->fill.type == SP_PAINT_TYPE_PAINTSERVER)
1952         && (server == style->fill.value.paint.server))
1953     {
1954         if (style->object) {
1955             /** \todo
1956              * fixme: I do not know, whether it is optimal - we are 
1957              * forcing reread of everything (Lauris)
1958              */
1959             /** \todo
1960              * fixme: We have to use object_modified flag, because parent 
1961              * flag is only available downstreams.
1962              */
1963             style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1964         }
1965     } else if ((style->stroke.type == SP_PAINT_TYPE_PAINTSERVER)
1966                && (server == style->stroke.value.paint.server))
1967     {
1968         if (style->object) {
1969             /// \todo fixme:
1970             style->object->requestModified(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
1971         }
1972     } else {
1973         g_assert_not_reached();
1974     }
1979 /**
1980  *
1981  */
1982 static void
1983 sp_style_merge_ipaint(SPStyle *style, SPIPaint *paint, SPIPaint const *parent)
1985     sp_style_paint_clear(style, paint, TRUE, FALSE);
1987     if ((paint->set && paint->currentcolor) || parent->currentcolor) {
1988         paint->currentcolor = TRUE;
1989         paint->type = SP_PAINT_TYPE_COLOR;
1990         sp_color_copy(&paint->value.color, &style->color.value.color);
1991         return;
1992     }
1994     paint->type = parent->type;
1995     switch (paint->type) {
1996         case SP_PAINT_TYPE_COLOR:
1997             sp_color_copy(&paint->value.color, &parent->value.color);
1998             break;
1999         case SP_PAINT_TYPE_PAINTSERVER:
2000             paint->value.paint.server = parent->value.paint.server;
2001             paint->value.paint.uri = parent->value.paint.uri;
2002             if (paint->value.paint.server) {
2003                 if (style->object && !style->cloned) { // href paintserver for style of non-clones only
2004                     style->hreffed = true;
2005                     sp_object_href(SP_OBJECT(paint->value.paint.server), style);
2006                 }
2007                 if (style->object || style->cloned) { // connect to signals for style of real objects or clones (this excludes temp styles)
2008                     g_signal_connect(G_OBJECT(paint->value.paint.server), "release",
2009                                      G_CALLBACK(sp_style_paint_server_release), style);
2010                     g_signal_connect(G_OBJECT(paint->value.paint.server), "modified",
2011                                      G_CALLBACK(sp_style_paint_server_modified), style);
2012                 }
2013             }
2014             break;
2015         case SP_PAINT_TYPE_NONE:
2016             break;
2017         default:
2018             g_assert_not_reached();
2019             break;
2020     }
2024 /**
2025  * Dumps the style to a CSS string, with either SP_STYLE_FLAG_IFSET or 
2026  * SP_STYLE_FLAG_ALWAYS flags. Used with Always for copying an object's 
2027  * complete cascaded style to style_clipboard. When you need a CSS string 
2028  * for an object in the document tree, you normally call 
2029  * sp_style_write_difference instead to take into account the object's parent.
2030  *
2031  * \pre style != NULL.
2032  * \pre flags in {IFSET, ALWAYS}.
2033  * \post ret != NULL.
2034  */
2035 gchar *
2036 sp_style_write_string(SPStyle const *const style, guint const flags)
2038     /** \todo
2039      * Merge with write_difference, much duplicate code!
2040      */
2041     g_return_val_if_fail(style != NULL, NULL);
2042     g_return_val_if_fail(((flags == SP_STYLE_FLAG_IFSET) ||
2043                           (flags == SP_STYLE_FLAG_ALWAYS)  ),
2044                          NULL);
2046     gchar c[BMAX];
2047     gchar *p = c;
2048     *p = '\0';
2050     p += sp_style_write_ifontsize(p, c + BMAX - p, "font-size", &style->font_size, NULL, flags);
2051     p += sp_style_write_ienum(p, c + BMAX - p, "font-style", enum_font_style, &style->font_style, NULL, flags);
2052     p += sp_style_write_ienum(p, c + BMAX - p, "font-variant", enum_font_variant, &style->font_variant, NULL, flags);
2053     p += sp_style_write_ienum(p, c + BMAX - p, "font-weight", enum_font_weight, &style->font_weight, NULL, flags);
2054     p += sp_style_write_ienum(p, c + BMAX - p, "font-stretch", enum_font_stretch, &style->font_stretch, NULL, flags);
2056     /* Text */
2057     p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &style->text_indent, NULL, flags);
2058     p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &style->text_align, NULL, flags);
2059     p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", &style->text_decoration, NULL, flags);
2060     p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &style->line_height, NULL, flags);
2061     p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &style->letter_spacing, NULL, flags);
2062     p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &style->word_spacing, NULL, flags);
2063     p += sp_style_write_ienum(p, c + BMAX - p, "text-transform", enum_text_transform, &style->text_transform, NULL, flags);
2064     p += sp_style_write_ienum(p, c + BMAX - p, "direction", enum_direction, &style->direction, NULL, flags);
2065     p += sp_style_write_ienum(p, c + BMAX - p, "block-progression", enum_block_progression, &style->block_progression, NULL, flags);
2066     p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &style->writing_mode, NULL, flags);
2068     p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &style->text_anchor, NULL, flags);
2070     /// \todo fixme: Per type methods need default flag too (lauris)
2071     p += sp_style_write_iscale24(p, c + BMAX - p, "opacity", &style->opacity, NULL, flags);
2072     p += sp_style_write_ipaint(p, c + BMAX - p, "color", &style->color, NULL, flags);
2073     p += sp_style_write_ipaint(p, c + BMAX - p, "fill", &style->fill, NULL, flags);
2074     p += sp_style_write_iscale24(p, c + BMAX - p, "fill-opacity", &style->fill_opacity, NULL, flags);
2075     p += sp_style_write_ienum(p, c + BMAX - p, "fill-rule", enum_fill_rule, &style->fill_rule, NULL, flags);
2076     p += sp_style_write_ipaint(p, c + BMAX - p, "stroke", &style->stroke, NULL, flags);
2077     p += sp_style_write_ilength(p, c + BMAX - p, "stroke-width", &style->stroke_width, NULL, flags);
2078     p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linecap", enum_stroke_linecap, &style->stroke_linecap, NULL, flags);
2079     p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linejoin", enum_stroke_linejoin, &style->stroke_linejoin, NULL, flags);
2081     marker_status("sp_style_write_string:  Writing markers");
2082     if (style->marker[SP_MARKER_LOC].set) {
2083         p += g_snprintf(p, c + BMAX - p, "marker:%s;", style->marker[SP_MARKER_LOC].value);
2084     } else if (flags == SP_STYLE_FLAG_ALWAYS) {
2085         p += g_snprintf(p, c + BMAX - p, "marker:none;");
2086     }
2087     if (style->marker[SP_MARKER_LOC_START].set) {
2088         p += g_snprintf(p, c + BMAX - p, "marker-start:%s;", style->marker[SP_MARKER_LOC_START].value);
2089     } else if (flags == SP_STYLE_FLAG_ALWAYS) {
2090         p += g_snprintf(p, c + BMAX - p, "marker-start:none;");
2091     }
2092     if (style->marker[SP_MARKER_LOC_MID].set) {
2093         p += g_snprintf(p, c + BMAX - p, "marker-mid:%s;", style->marker[SP_MARKER_LOC_MID].value);
2094     } else if (flags == SP_STYLE_FLAG_ALWAYS) {
2095         p += g_snprintf(p, c + BMAX - p, "marker-mid:none;");
2096     }
2097     if (style->marker[SP_MARKER_LOC_END].set) {
2098         p += g_snprintf(p, c + BMAX - p, "marker-end:%s;", style->marker[SP_MARKER_LOC_END].value);
2099     } else if (flags == SP_STYLE_FLAG_ALWAYS) {
2100         p += g_snprintf(p, c + BMAX - p, "marker-end:none;");
2101     }
2103     p += sp_style_write_ifloat(p, c + BMAX - p, "stroke-miterlimit", &style->stroke_miterlimit, NULL, flags);
2105     /** \todo fixme: */
2106     if ((flags == SP_STYLE_FLAG_ALWAYS)
2107         || style->stroke_dasharray_set)
2108     {
2109         if (style->stroke_dasharray_inherit) {
2110             p += g_snprintf(p, c + BMAX - p, "stroke-dasharray:inherit;");
2111         } else if (style->stroke_dash.n_dash && style->stroke_dash.dash) {
2112             p += g_snprintf(p, c + BMAX - p, "stroke-dasharray:");
2113             gint i;
2114             for (i = 0; i < style->stroke_dash.n_dash; i++) {
2115                 Inkscape::CSSOStringStream os;
2116                 if (i) {
2117                     os << ", ";
2118                 }
2119                 os << style->stroke_dash.dash[i];
2120                 p += g_strlcpy(p, os.str().c_str(), c + BMAX - p);
2121             }
2122             if (p < c + BMAX) {
2123                 *p++ = ';';
2124             }
2125         } else {
2126             p += g_snprintf(p, c + BMAX - p, "stroke-dasharray:none;");
2127         }
2128     }
2130     /** \todo fixme: */
2131     if (style->stroke_dashoffset_set) {
2132         Inkscape::CSSOStringStream os;
2133         os << "stroke-dashoffset:" << style->stroke_dash.offset << ";";
2134         p += g_strlcpy(p, os.str().c_str(), c + BMAX - p);
2135     } else if (flags == SP_STYLE_FLAG_ALWAYS) {
2136         p += g_snprintf(p, c + BMAX - p, "stroke-dashoffset:0;");
2137     }
2139     p += sp_style_write_iscale24(p, c + BMAX - p, "stroke-opacity", &style->stroke_opacity, NULL, flags);
2141     p += sp_style_write_ienum(p, c + BMAX - p, "visibility", enum_visibility, &style->visibility, NULL, flags);
2142     p += sp_style_write_ienum(p, c + BMAX - p, "display", enum_display, &style->display, NULL, flags);
2143     p += sp_style_write_ienum(p, c + BMAX - p, "overflow", enum_overflow, &style->overflow, NULL, flags);
2145     /* fixme: */
2146     p += sp_text_style_write(p, c + BMAX - p, style->text, flags);
2148     /* Get rid of trailing `;'. */
2149     if (p != c) {
2150         --p;
2151         if (*p == ';') {
2152             *p = '\0';
2153         }
2154     }
2156     return g_strdup(c);
2160 #define STYLE_BUF_MAX
2163 /**
2164  * Dumps style to CSS string, see sp_style_write_string()
2165  * 
2166  * \pre from != NULL.
2167  * \pre to != NULL.
2168  * \post ret != NULL.
2169  */
2170 gchar *
2171 sp_style_write_difference(SPStyle const *const from, SPStyle const *const to)
2173     g_return_val_if_fail(from != NULL, NULL);
2174     g_return_val_if_fail(to != NULL, NULL);
2176     gchar c[BMAX], *p = c;
2177     *p = '\0';
2179     p += sp_style_write_ifontsize(p, c + BMAX - p, "font-size", &from->font_size, &to->font_size, SP_STYLE_FLAG_IFDIFF);
2180     p += sp_style_write_ienum(p, c + BMAX - p, "font-style", enum_font_style, &from->font_style, &to->font_style, SP_STYLE_FLAG_IFDIFF);
2181     p += sp_style_write_ienum(p, c + BMAX - p, "font-variant", enum_font_variant, &from->font_variant, &to->font_variant, SP_STYLE_FLAG_IFDIFF);
2182     p += sp_style_write_ienum(p, c + BMAX - p, "font-weight", enum_font_weight, &from->font_weight, &to->font_weight, SP_STYLE_FLAG_IFDIFF);
2183     p += sp_style_write_ienum(p, c + BMAX - p, "font-stretch", enum_font_stretch, &from->font_stretch, &to->font_stretch, SP_STYLE_FLAG_IFDIFF);
2185     /* Text */
2186     p += sp_style_write_ilength(p, c + BMAX - p, "text-indent", &from->text_indent, &to->text_indent, SP_STYLE_FLAG_IFDIFF);
2187     p += sp_style_write_ienum(p, c + BMAX - p, "text-align", enum_text_align, &from->text_align, &to->text_align, SP_STYLE_FLAG_IFDIFF);
2188     p += sp_style_write_itextdecoration(p, c + BMAX - p, "text-decoration", &from->text_decoration, &to->text_decoration, SP_STYLE_FLAG_IFDIFF);
2189     p += sp_style_write_ilengthornormal(p, c + BMAX - p, "line-height", &from->line_height, &to->line_height, SP_STYLE_FLAG_IFDIFF);
2190     p += sp_style_write_ilengthornormal(p, c + BMAX - p, "letter-spacing", &from->letter_spacing, &to->letter_spacing, SP_STYLE_FLAG_IFDIFF);
2191     p += sp_style_write_ilengthornormal(p, c + BMAX - p, "word-spacing", &from->word_spacing, &to->word_spacing, SP_STYLE_FLAG_IFDIFF);
2192     p += sp_style_write_ienum(p, c + BMAX - p, "text-transform", enum_text_transform, &from->text_transform, &to->text_transform, SP_STYLE_FLAG_IFDIFF);
2193     p += sp_style_write_ienum(p, c + BMAX - p, "direction", enum_direction, &from->direction, &to->direction, SP_STYLE_FLAG_IFDIFF);
2194     p += sp_style_write_ienum(p, c + BMAX - p, "block-progression", enum_block_progression, &from->block_progression, &to->block_progression, SP_STYLE_FLAG_IFDIFF);
2195     p += sp_style_write_ienum(p, c + BMAX - p, "writing-mode", enum_writing_mode, &from->writing_mode, &to->writing_mode, SP_STYLE_FLAG_IFDIFF);
2197     p += sp_style_write_ienum(p, c + BMAX - p, "text-anchor", enum_text_anchor, &from->text_anchor, &to->text_anchor, SP_STYLE_FLAG_IFDIFF);
2199     /// \todo fixme: Per type methods need default flag too 
2200     if (from->opacity.set && from->opacity.value != SP_SCALE24_MAX) {
2201         p += sp_style_write_iscale24(p, c + BMAX - p, "opacity", &from->opacity, &to->opacity, SP_STYLE_FLAG_IFSET);
2202     }
2203     p += sp_style_write_ipaint(p, c + BMAX - p, "color", &from->color, &to->color, SP_STYLE_FLAG_IFSET);
2204     p += sp_style_write_ipaint(p, c + BMAX - p, "fill", &from->fill, &to->fill, SP_STYLE_FLAG_IFDIFF);
2205     p += sp_style_write_iscale24(p, c + BMAX - p, "fill-opacity", &from->fill_opacity, &to->fill_opacity, SP_STYLE_FLAG_IFDIFF);
2206     p += sp_style_write_ienum(p, c + BMAX - p, "fill-rule", enum_fill_rule, &from->fill_rule, &to->fill_rule, SP_STYLE_FLAG_IFDIFF);
2207     p += sp_style_write_ipaint(p, c + BMAX - p, "stroke", &from->stroke, &to->stroke, SP_STYLE_FLAG_IFDIFF);
2208     p += sp_style_write_ilength(p, c + BMAX - p, "stroke-width", &from->stroke_width, &to->stroke_width, SP_STYLE_FLAG_IFDIFF);
2209     p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linecap", enum_stroke_linecap,
2210                               &from->stroke_linecap, &to->stroke_linecap, SP_STYLE_FLAG_IFDIFF);
2211     p += sp_style_write_ienum(p, c + BMAX - p, "stroke-linejoin", enum_stroke_linejoin,
2212                               &from->stroke_linejoin, &to->stroke_linejoin, SP_STYLE_FLAG_IFDIFF);
2213     p += sp_style_write_ifloat(p, c + BMAX - p, "stroke-miterlimit",
2214                                &from->stroke_miterlimit, &to->stroke_miterlimit, SP_STYLE_FLAG_IFDIFF);
2215     /** \todo fixme: */
2216     if (from->stroke_dasharray_set) {
2217         if (from->stroke_dasharray_inherit) {
2218             p += g_snprintf(p, c + BMAX - p, "stroke-dasharray:inherit;");
2219         } else if (from->stroke_dash.n_dash && from->stroke_dash.dash) {
2220             p += g_snprintf(p, c + BMAX - p, "stroke-dasharray:");
2221             for (gint i = 0; i < from->stroke_dash.n_dash; i++) {
2222                 Inkscape::CSSOStringStream os;
2223                 if (i) {
2224                     os << ", ";
2225                 }
2226                 os << from->stroke_dash.dash[i];
2227                 p += g_strlcpy(p, os.str().c_str(), c + BMAX - p);
2228             }
2229             p += g_snprintf(p, c + BMAX - p, ";");
2230         }
2231     }
2232     /* fixme: */
2233     if (from->stroke_dashoffset_set) {
2234         Inkscape::CSSOStringStream os;
2235         os << "stroke-dashoffset:" << from->stroke_dash.offset << ";";
2236         p += g_strlcpy(p, os.str().c_str(), c + BMAX - p);
2237     }
2238     p += sp_style_write_iscale24(p, c + BMAX - p, "stroke-opacity", &from->stroke_opacity, &to->stroke_opacity, SP_STYLE_FLAG_IFDIFF);
2240     /* markers */
2241     marker_status("sp_style_write_difference:  Writing markers");
2242     if (from->marker[SP_MARKER_LOC].value != NULL) {
2243         p += g_snprintf(p, c + BMAX - p, "marker:%s;",       from->marker[SP_MARKER_LOC].value);
2244     }
2245     if (from->marker[SP_MARKER_LOC_START].value != NULL) {
2246         p += g_snprintf(p, c + BMAX - p, "marker-start:%s;", from->marker[SP_MARKER_LOC_START].value);
2247     }
2248     if (from->marker[SP_MARKER_LOC_MID].value != NULL) {
2249         p += g_snprintf(p, c + BMAX - p, "marker-mid:%s;",   from->marker[SP_MARKER_LOC_MID].value);
2250     }
2251     if (from->marker[SP_MARKER_LOC_END].value != NULL) {
2252         p += g_snprintf(p, c + BMAX - p, "marker-end:%s;",   from->marker[SP_MARKER_LOC_END].value);
2253     }
2255     p += sp_style_write_ienum(p, c + BMAX - p, "visibility", enum_visibility, &from->visibility, &to->visibility, SP_STYLE_FLAG_IFSET);
2256     p += sp_style_write_ienum(p, c + BMAX - p, "display", enum_display, &from->display, &to->display, SP_STYLE_FLAG_IFSET);
2257     p += sp_style_write_ienum(p, c + BMAX - p, "overflow", enum_overflow, &from->overflow, &to->overflow, SP_STYLE_FLAG_IFSET);
2259     p += sp_text_style_write(p, c + BMAX - p, from->text, SP_STYLE_FLAG_IFDIFF);
2261     /** \todo
2262      * The reason we use IFSET rather than IFDIFF is the belief that the IFDIFF
2263      * flag is mainly only for attributes that don't handle explicit unset well.
2264      * We may need to revisit the behaviour of this routine.
2265      */
2267     /* Get rid of trailing `;'. */
2268     if (p != c) {
2269         --p;
2270         if (*p == ';') {
2271             *p = '\0';
2272         }
2273     }
2275     return g_strdup(c);
2280 /**
2281  * Reset all style properties.
2282  */
2283 static void
2284 sp_style_clear(SPStyle *style)
2286     g_return_if_fail(style != NULL);
2288     sp_style_paint_clear(style, &style->fill, TRUE, FALSE);
2289     sp_style_paint_clear(style, &style->stroke, TRUE, FALSE);
2290     if (style->stroke_dash.dash) {
2291         g_free(style->stroke_dash.dash);
2292     }
2294     /** \todo fixme: Do that text manipulation via parents */
2295     SPObject *object = style->object;
2296     gint const refcount = style->refcount;
2297     SPTextStyle *text = style->text;
2298     unsigned const text_private = style->text_private;
2299     memset(style, 0, sizeof(SPStyle));
2300     style->refcount = refcount;
2301     style->object = object;
2302     style->text = text;
2303     style->text_private = text_private;
2304     /* fixme: */
2305     style->text->font.set = FALSE;
2306     style->text->font_family.set = FALSE;
2308     style->font_size.set = FALSE;
2309     style->font_size.type = SP_FONT_SIZE_LITERAL;
2310     style->font_size.value = SP_CSS_FONT_SIZE_MEDIUM;
2311     style->font_size.computed = 12.0;
2312     style->font_style.set = FALSE;
2313     style->font_style.value = style->font_style.computed = SP_CSS_FONT_STYLE_NORMAL;
2314     style->font_variant.set = FALSE;
2315     style->font_variant.value = style->font_variant.computed = SP_CSS_FONT_VARIANT_NORMAL;
2316     style->font_weight.set = FALSE;
2317     style->font_weight.value = SP_CSS_FONT_WEIGHT_NORMAL;
2318     style->font_weight.computed = SP_CSS_FONT_WEIGHT_400;
2319     style->font_stretch.set = FALSE;
2320     style->font_stretch.value = style->font_stretch.computed = SP_CSS_FONT_STRETCH_NORMAL;
2322     /* text */
2323     style->text_indent.set = FALSE;
2324     style->text_indent.unit = SP_CSS_UNIT_NONE;
2325     style->text_indent.computed = 0.0;
2327     style->text_align.set = FALSE;
2328     style->text_align.value = style->text_align.computed = SP_CSS_TEXT_ALIGN_START;
2330     style->text_decoration.set = FALSE;
2331     style->text_decoration.underline = FALSE;
2332     style->text_decoration.overline = FALSE;
2333     style->text_decoration.line_through = FALSE;
2334     style->text_decoration.blink = FALSE;
2336     style->line_height.set = FALSE;
2337     style->line_height.unit = SP_CSS_UNIT_PERCENT;
2338     style->line_height.normal = TRUE;
2339     style->line_height.value = style->line_height.computed = 1.0;
2341     style->letter_spacing.set = FALSE;
2342     style->letter_spacing.unit = SP_CSS_UNIT_NONE;
2343     style->letter_spacing.normal = TRUE;
2344     style->letter_spacing.value = style->letter_spacing.computed = 0.0;
2346     style->word_spacing.set = FALSE;
2347     style->word_spacing.unit = SP_CSS_UNIT_NONE;
2348     style->word_spacing.normal = TRUE;
2349     style->word_spacing.value = style->word_spacing.computed = 0.0;
2351     style->text_transform.set = FALSE;
2352     style->text_transform.value = style->text_transform.computed = SP_CSS_TEXT_TRANSFORM_NONE;
2354     style->direction.set = FALSE;
2355     style->direction.value = style->direction.computed = SP_CSS_DIRECTION_LTR;
2357     style->block_progression.set = FALSE;
2358     style->block_progression.value = style->block_progression.computed = SP_CSS_BLOCK_PROGRESSION_TB;
2360     style->writing_mode.set = FALSE;
2361     style->writing_mode.value = style->writing_mode.computed = SP_CSS_WRITING_MODE_LR_TB;
2364     style->text_anchor.set = FALSE;
2365     style->text_anchor.value = style->text_anchor.computed = SP_CSS_TEXT_ANCHOR_START;
2368     style->opacity.value = SP_SCALE24_MAX;
2369     style->visibility.set = FALSE;
2370     style->visibility.value = style->visibility.computed = SP_CSS_VISIBILITY_VISIBLE;
2371     style->display.set = FALSE;
2372     style->display.value = style->display.computed = SP_CSS_DISPLAY_INLINE;
2373     style->overflow.set = FALSE;
2374     style->overflow.value = style->overflow.computed = SP_CSS_OVERFLOW_VISIBLE;
2376     style->color.type = SP_PAINT_TYPE_COLOR;
2377     sp_color_set_rgb_float(&style->color.value.color, 0.0, 0.0, 0.0);
2379     style->fill.type = SP_PAINT_TYPE_COLOR;
2380     sp_color_set_rgb_float(&style->fill.value.color, 0.0, 0.0, 0.0);
2381     style->fill_opacity.value = SP_SCALE24_MAX;
2382     style->fill_rule.value = style->fill_rule.computed = SP_WIND_RULE_NONZERO;
2384     style->stroke.type = SP_PAINT_TYPE_NONE;
2385     style->stroke.set = FALSE;
2386     sp_color_set_rgb_float(&style->stroke.value.color, 0.0, 0.0, 0.0);
2387     style->stroke_opacity.value = SP_SCALE24_MAX;
2389     style->stroke_width.set = FALSE;
2390     style->stroke_width.unit = SP_CSS_UNIT_NONE;
2391     style->stroke_width.computed = 1.0;
2393     style->stroke_linecap.set = FALSE;
2394     style->stroke_linecap.value = style->stroke_linecap.computed = SP_STROKE_LINECAP_BUTT;
2395     style->stroke_linejoin.set = FALSE;
2396     style->stroke_linejoin.value = style->stroke_linejoin.computed = SP_STROKE_LINEJOIN_MITER;
2398     style->stroke_miterlimit.set = FALSE;
2399     style->stroke_miterlimit.value = 4.0;
2401     style->stroke_dash.n_dash = 0;
2402     style->stroke_dash.dash = NULL;
2403     style->stroke_dash.offset = 0.0;
2405     for (unsigned i = SP_MARKER_LOC; i < SP_MARKER_LOC_QTY; i++) {
2406         g_free(style->marker[i].value);
2407         style->marker[i].set = FALSE;
2408     }
2413 /**
2414  * 
2415  */
2416 static void
2417 sp_style_read_dash(SPStyle *style, gchar const *str)
2419     /* Ref: http://www.w3.org/TR/SVG11/painting.html#StrokeDasharrayProperty */
2420     style->stroke_dasharray_set = TRUE;
2422     if (strcmp(str, "inherit") == 0) {
2423         style->stroke_dasharray_inherit = true;
2424         return;
2425     }
2426     style->stroke_dasharray_inherit = false;
2428     NRVpathDash &dash = style->stroke_dash;
2429     g_free(dash.dash);
2430     dash.dash = NULL;
2432     if (strcmp(str, "none") == 0) {
2433         dash.n_dash = 0;
2434         return;
2435     }
2437     gint n_dash = 0;
2438     gdouble d[64];
2439     gchar *e = NULL;
2441     bool LineSolid=true;
2442     while (e != str && n_dash < 64) {
2443         /* TODO: Should allow <length> rather than just a unitless (px) number. */
2444         d[n_dash] = g_ascii_strtod(str, (char **) &e);
2445         if (d[n_dash] > 0.00000001)
2446             LineSolid = false;
2447         if (e != str) {
2448             n_dash += 1;
2449             str = e;
2450         }
2451         while (str && *str && !isalnum(*str)) str += 1;
2452     }
2454     if (LineSolid) {
2455         dash.n_dash = 0;
2456         return;
2457     }
2459     if (n_dash > 0) {
2460         dash.dash = g_new(gdouble, n_dash);
2461         memcpy(dash.dash, d, sizeof(gdouble) * n_dash);
2462         dash.n_dash = n_dash;
2463     }
2467 /*#########################
2468 ## SPTextStyle operations
2469 #########################*/
2472 /**
2473  * Return new SPTextStyle object with default settings.
2474  */
2475 static SPTextStyle *
2476 sp_text_style_new()
2478     SPTextStyle *ts = g_new0(SPTextStyle, 1);
2479     ts->refcount = 1;
2480     sp_text_style_clear(ts);
2482     ts->font.value = g_strdup("Bitstream Vera Sans");
2483     ts->font_family.value = g_strdup("Bitstream Vera Sans");
2485     return ts;
2489 /**
2490  * Clear text style settings.
2491  */
2492 static void
2493 sp_text_style_clear(SPTextStyle *ts)
2495     ts->font.set = FALSE;
2496     ts->font_family.set = FALSE;
2501 /**
2502  * Reduce refcount of text style and possibly free it.
2503  */
2504 static SPTextStyle *
2505 sp_text_style_unref(SPTextStyle *st)
2507     st->refcount -= 1;
2509     if (st->refcount < 1) {
2510         g_free(st->font.value);
2511         g_free(st->font_family.value);
2512         g_free(st);
2513     }
2515     return NULL;
2520 /**
2521  * Return duplicate of text style.
2522  */
2523 static SPTextStyle *
2524 sp_text_style_duplicate_unset(SPTextStyle *st)
2526     SPTextStyle *nt = g_new0(SPTextStyle, 1);
2527     nt->refcount = 1;
2529     nt->font.value = g_strdup(st->font.value);
2530     nt->font_family.value = g_strdup(st->font_family.value);
2532     return nt;
2537 /**
2538  * Write SPTextStyle object into string.
2539  */
2540 static guint
2541 sp_text_style_write(gchar *p, guint const len, SPTextStyle const *const st, guint flags)
2543     gint d = 0;
2545     // We do not do diffing for text style
2546     if (flags == SP_STYLE_FLAG_IFDIFF)
2547         flags = SP_STYLE_FLAG_IFSET;
2549     d += sp_style_write_istring(p + d, len - d, "font-family", &st->font_family, NULL, flags);
2550     return d;
2555 /* The following sp_tyle_read_* functions ignore invalid values, as per
2556  * http://www.w3.org/TR/REC-CSS2/syndata.html#parsing-errors.
2557  *
2558  * [However, the SVG spec is somewhat unclear as to whether the style attribute should
2559  * be handled as per CSS2 rules or whether it must simply be a set of PROPERTY:VALUE
2560  * pairs, in which case SVG's error-handling rules
2561  * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing should instead be applied.]
2562  */
2565 /**
2566  * Set SPIFloat object from string.
2567  */
2568 static void
2569 sp_style_read_ifloat(SPIFloat *val, gchar const *str)
2571     if (!strcmp(str, "inherit")) {
2572         val->set = TRUE;
2573         val->inherit = TRUE;
2574     } else {
2575         gfloat value;
2576         if (sp_svg_number_read_f(str, &value)) {
2577             val->set = TRUE;
2578             val->inherit = FALSE;
2579             val->value = value;
2580         }
2581     }
2586 /**
2587  * Set SPIScale24 object from string.
2588  */
2589 static void
2590 sp_style_read_iscale24(SPIScale24 *val, gchar const *str)
2592     if (!strcmp(str, "inherit")) {
2593         val->set = TRUE;
2594         val->inherit = TRUE;
2595     } else {
2596         gfloat value;
2597         if (sp_svg_number_read_f(str, &value)) {
2598             val->set = TRUE;
2599             val->inherit = FALSE;
2600             value = CLAMP(value, 0.0f, (gfloat) SP_SCALE24_MAX);
2601             val->value = SP_SCALE24_FROM_FLOAT(value);
2602         }
2603     }
2606 /**
2607  * Reads a style value and performs lookup based on the given style value enumerations.
2608  */
2609 static void
2610 sp_style_read_ienum(SPIEnum *val, gchar const *str, SPStyleEnum const *dict,
2611                     bool const can_explicitly_inherit)
2613     if ( can_explicitly_inherit && !strcmp(str, "inherit") ) {
2614         val->set = TRUE;
2615         val->inherit = TRUE;
2616     } else {
2617         for (unsigned i = 0; dict[i].key; i++) {
2618             if (!strcmp(str, dict[i].key)) {
2619                 val->set = TRUE;
2620                 val->inherit = FALSE;
2621                 val->value = dict[i].value;
2622                 /* Save copying for values not needing it */
2623                 val->computed = val->value;
2624                 break;
2625             }
2626         }
2627     }
2632 /**
2633  * Set SPIString object from string.
2634  */
2635 static void
2636 sp_style_read_istring(SPIString *val, gchar const *str)
2638     g_free(val->value);
2640     if (!strcmp(str, "inherit")) {
2641         val->set = TRUE;
2642         val->inherit = TRUE;
2643         val->value = NULL;
2644     } else {
2645         val->set = TRUE;
2646         val->inherit = FALSE;
2647         val->value = g_strdup(str);
2648     }
2653 /**
2654  * Set SPILength object from string.
2655  */
2656 static void
2657 sp_style_read_ilength(SPILength *val, gchar const *str)
2659     if (!strcmp(str, "inherit")) {
2660         val->set = TRUE;
2661         val->inherit = TRUE;
2662     } else {
2663         gdouble value;
2664         gchar *e;
2665         /** \todo fixme: Move this to standard place (Lauris) */
2666         value = g_ascii_strtod(str, &e);
2667         if ((gchar const *) e != str) {
2668             /** \todo 
2669              * Allow the number of px per inch to vary (document preferences, 
2670              * X server or whatever).  E.g. don't fill in computed here, do 
2671              * it at the same time as percentage units are done.
2672              */
2673             if (!*e) {
2674                 /* Userspace */
2675                 val->unit = SP_CSS_UNIT_NONE;
2676                 val->computed = value;
2677             } else if (!strcmp(e, "px")) {
2678                 /* Userspace */
2679                 val->unit = SP_CSS_UNIT_PX;
2680                 val->computed = value;
2681             } else if (!strcmp(e, "pt")) {
2682                 /* Userspace / DEVICESCALE */
2683                 val->unit = SP_CSS_UNIT_PT;
2684                 val->computed = value * PX_PER_PT;
2685             } else if (!strcmp(e, "pc")) {
2686                 /* 1 pica = 12pt; FIXME: add it to SPUnit */
2687                 val->unit = SP_CSS_UNIT_PC;
2688                 val->computed = value * PX_PER_PT * 12;
2689             } else if (!strcmp(e, "mm")) {
2690                 val->unit = SP_CSS_UNIT_MM;
2691                 val->computed = value * PX_PER_MM;
2692             } else if (!strcmp(e, "cm")) {
2693                 val->unit = SP_CSS_UNIT_CM;
2694                 val->computed = value * PX_PER_CM;
2695             } else if (!strcmp(e, "in")) {
2696                 val->unit = SP_CSS_UNIT_IN;
2697                 val->computed = value * PX_PER_IN;
2698             } else if (!strcmp(e, "em")) {
2699                 /* EM square */
2700                 val->unit = SP_CSS_UNIT_EM;
2701                 val->value = value;
2702             } else if (!strcmp(e, "ex")) {
2703                 /* ex square */
2704                 val->unit = SP_CSS_UNIT_EX;
2705                 val->value = value;
2706             } else if (!strcmp(e, "%")) {
2707                 /* Percentage */
2708                 val->unit = SP_CSS_UNIT_PERCENT;
2709                 val->value = value * 0.01;
2710             } else {
2711                 /* Invalid */
2712                 return;
2713             }
2714             val->set = TRUE;
2715             val->inherit = FALSE;
2716         }
2717     }
2720 /**
2721  * Set SPILengthOrNormal object from string.
2722  */
2723 static void
2724 sp_style_read_ilengthornormal(SPILengthOrNormal *val, gchar const *str)
2726     if (!strcmp(str, "normal")) {
2727         val->set = TRUE;
2728         val->inherit = FALSE;
2729         val->normal = TRUE;
2730         val->unit = SP_CSS_UNIT_NONE;
2731         val->value = val->computed = 0.0;
2732     } else {
2733         SPILength length;
2734         sp_style_read_ilength(&length, str);
2735         val->set = length.set;
2736         val->inherit = length.inherit;
2737         val->normal = FALSE;
2738         val->unit = length.unit;
2739         val->value = length.value;
2740         val->computed = length.computed;
2741     }
2744 /**
2745  * Set SPITextDecoration object from string.
2746  */
2747 static void
2748 sp_style_read_itextdecoration(SPITextDecoration *val, gchar const *str)
2750     if (!strcmp(str, "inherit")) {
2751         val->set = TRUE;
2752         val->inherit = TRUE;
2753     } else if (!strcmp(str, "none")) {
2754         val->set = TRUE;
2755         val->inherit = FALSE;
2756         val->underline = FALSE;
2757         val->overline = FALSE;
2758         val->line_through = FALSE;
2759         val->blink = FALSE;
2760     } else {
2761         bool found_underline = false;
2762         bool found_overline = false;
2763         bool found_line_through = false;
2764         bool found_blink = false;
2765         for ( ; *str ; str++ ) {
2766             if (*str == ' ') continue;
2767             if (strneq(str, "underline", 9) && (str[9] == ' ' || str[9] == '\0')) {
2768                 found_underline = true;
2769                 str += 9;
2770             } else if (strneq(str, "overline", 8) && (str[8] == ' ' || str[8] == '\0')) {
2771                 found_overline = true;
2772                 str += 8;
2773             } else if (strneq(str, "line-through", 12) && (str[12] == ' ' || str[12] == '\0')) {
2774                 found_line_through = true;
2775                 str += 12;
2776             } else if (strneq(str, "blink", 5) && (str[5] == ' ' || str[5] == '\0')) {
2777                 found_blink = true;
2778                 str += 5;
2779             } else {
2780                 return;  // invalid value
2781             }
2782         }
2783         if (!(found_underline || found_overline || found_line_through || found_blink)) {
2784             return;  // invalid value: empty
2785         }
2786         val->set = TRUE;
2787         val->inherit = FALSE;
2788         val->underline = found_underline;
2789         val->overline = found_overline;
2790         val->line_through = found_line_through;
2791         val->blink = found_blink;
2792     }
2795 /**
2796  * Set SPIPaint object from string containing an integer value.
2797  * \param document Ignored
2798  */
2799 static void
2800 sp_style_read_icolor(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document)
2802     paint->currentcolor = FALSE;  /* currentColor not a valid <color>. */
2803     if (!strcmp(str, "inherit")) {
2804         paint->set = TRUE;
2805         paint->inherit = TRUE;
2806     } else {
2807         guint32 const rgb0 = sp_svg_read_color(str, 0xff);
2808         if (rgb0 != 0xff) {
2809             paint->type = SP_PAINT_TYPE_COLOR;
2810             sp_color_set_rgb_rgba32(&paint->value.color, rgb0);
2811             paint->set = TRUE;
2812             paint->inherit = FALSE;
2813         }
2814     }
2818 /**
2819  * Set SPIPaint object from string. 
2820  */
2821 static void
2822 sp_style_read_ipaint(SPIPaint *paint, gchar const *str, SPStyle *style, SPDocument *document)
2824     while (isspace(*str)) {
2825         ++str;
2826     }
2828     if (streq(str, "inherit")) {
2829         paint->set = TRUE;
2830         paint->inherit = TRUE;
2831         paint->currentcolor = FALSE;
2832     } else if (streq(str, "currentColor")) {
2833         paint->set = TRUE;
2834         paint->inherit = FALSE;
2835         paint->currentcolor = TRUE;
2836     } else if (streq(str, "none")) {
2837         paint->type = SP_PAINT_TYPE_NONE;
2838         paint->set = TRUE;
2839         paint->inherit = FALSE;
2840         paint->currentcolor = FALSE;
2841     } else if (strneq(str, "url", 3)) {
2842         // this is alloc'd uri, but seems to be shared with a parent
2843         // potentially, so it's not any easy g_free case...
2844         paint->value.paint.uri = extract_uri(str);
2845         if (paint->value.paint.uri == NULL || *(paint->value.paint.uri) == '\0') {
2846             paint->type = SP_PAINT_TYPE_NONE;
2847             return;
2848         }
2849         paint->type = SP_PAINT_TYPE_PAINTSERVER;
2850         paint->set = TRUE;
2851         paint->inherit = FALSE;
2852         paint->currentcolor = FALSE;
2853         if (document) {
2854             SPObject *ps = sp_uri_reference_resolve(document, str);
2855             if (ps && SP_IS_PAINT_SERVER(ps)) {
2856                 paint->value.paint.server = SP_PAINT_SERVER(ps);
2857                 if (style->object && !style->cloned) {
2858                     sp_object_href(SP_OBJECT(paint->value.paint.server), style);
2859                     style->hreffed = true;
2860                 }
2861                 if (style->object || style->cloned) {
2862                     g_signal_connect(G_OBJECT(paint->value.paint.server), "release",
2863                                      G_CALLBACK(sp_style_paint_server_release), style);
2864                     g_signal_connect(G_OBJECT(paint->value.paint.server), "modified",
2865                                      G_CALLBACK(sp_style_paint_server_modified), style);
2866                 }
2867             } else {
2868                 paint->value.paint.server = NULL;
2869             }
2870         }
2871     } else {
2872         guint32 const rgb0 = sp_svg_read_color(str, 0xff);
2873         if (rgb0 != 0xff) {
2874             paint->type = SP_PAINT_TYPE_COLOR;
2875             sp_color_set_rgb_rgba32(&paint->value.color, rgb0);
2876             paint->set = TRUE;
2877             paint->inherit = FALSE;
2878             paint->currentcolor = FALSE;
2879         }
2880     }
2885 /**
2886  * Set SPIFontSize object from string.
2887  */
2888 static void
2889 sp_style_read_ifontsize(SPIFontSize *val, gchar const *str)
2891     if (!strcmp(str, "inherit")) {
2892         val->set = TRUE;
2893         val->inherit = TRUE;
2894     } else if ((*str == 'x') || (*str == 's') || (*str == 'm') || (*str == 'l')) {
2895         for (unsigned i = 0; enum_font_size[i].key; i++) {
2896             if (!strcmp(str, enum_font_size[i].key)) {
2897                 val->set = TRUE;
2898                 val->inherit = FALSE;
2899                 val->type = SP_FONT_SIZE_LITERAL;
2900                 val->value = enum_font_size[i].value;
2901                 return;
2902             }
2903         }
2904         /* Invalid */
2905         return;
2906     } else {
2907         gdouble value;
2908         gchar *e;
2909         /* fixme: Move this to standard place (Lauris) */
2910         value = g_ascii_strtod(str, &e);
2911         if ((gchar const *) e != str) {
2912             if (!*e) {
2913                 /* Userspace */
2914             } else if (!strcmp(e, "px")) {
2915                 /* Userspace */
2916             } else if (!strcmp(e, "pt")) {
2917                 /* Userspace * DEVICESCALE */
2918                 value *= PX_PER_PT;
2919             } else if (!strcmp(e, "pc")) {
2920                 /* 12pt */
2921                 value *= PX_PER_PT * 12.0;
2922             } else if (!strcmp(e, "mm")) {
2923                 value *= PX_PER_MM;
2924             } else if (!strcmp(e, "cm")) {
2925                 value *= PX_PER_CM;
2926             } else if (!strcmp(e, "in")) {
2927                 value *= PX_PER_IN;
2928             } else if (!strcmp(e, "%")) {
2929                 /* Percentage */
2930                 val->set = TRUE;
2931                 val->inherit = FALSE;
2932                 val->type = SP_FONT_SIZE_PERCENTAGE;
2933                 val->value = SP_F8_16_FROM_FLOAT(value / 100.0);
2934                 return;
2935             } else {
2936                 /* Invalid */
2937                 return;
2938             }
2939             /* Length */
2940             val->set = TRUE;
2941             val->inherit = FALSE;
2942             val->type = SP_FONT_SIZE_LENGTH;
2943             val->computed = value;
2944             return;
2945         }
2946     }
2951 /**
2952  * Set SPIEnum object from repr attribute.
2953  */
2954 static void
2955 sp_style_read_penum(SPIEnum *val, Inkscape::XML::Node *repr,
2956                     gchar const *key, SPStyleEnum const *dict,
2957                     bool const can_explicitly_inherit)
2959     gchar const *str = repr->attribute(key);
2960     if (str) {
2961         sp_style_read_ienum(val, str, dict, can_explicitly_inherit);
2962     }
2967 /**
2968  * Set SPILength object from repr attribute.
2969  */
2970 static void
2971 sp_style_read_plength(SPILength *val, Inkscape::XML::Node *repr, gchar const *key)
2973     gchar const *str = repr->attribute(key);
2974     if (str) {
2975         sp_style_read_ilength(val, str);
2976     }
2979 /**
2980  * Set SPIFontSize object from repr attribute.
2981  */
2982 static void
2983 sp_style_read_pfontsize(SPIFontSize *val, Inkscape::XML::Node *repr, gchar const *key)
2985     gchar const *str = repr->attribute(key);
2986     if (str) {
2987         sp_style_read_ifontsize(val, str);
2988     }
2992 /**
2993  * Set SPIFloat object from repr attribute.
2994  */
2995 static void
2996 sp_style_read_pfloat(SPIFloat *val, Inkscape::XML::Node *repr, gchar const *key)
2998     gchar const *str = repr->attribute(key);
2999     if (str) {
3000         sp_style_read_ifloat(val, str);
3001     }
3005 /**
3006  * Write SPIFloat object into string.
3007  */
3008 static gint
3009 sp_style_write_ifloat(gchar *p, gint const len, gchar const *const key,
3010                       SPIFloat const *const val, SPIFloat const *const base, guint const flags)
3012     Inkscape::CSSOStringStream os;
3014     if ((flags & SP_STYLE_FLAG_ALWAYS)
3015         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3016         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3017             && (!base->set || (val->value != base->value))))
3018     {
3019         if (val->inherit) {
3020             return g_snprintf(p, len, "%s:inherit;", key);
3021         } else {
3022             os << key << ":" << val->value << ";";
3023             return g_strlcpy(p, os.str().c_str(), len);
3024         }
3025     }
3026     return 0;
3030 /**
3031  * Write SPIScale24 object into string.
3032  */
3033 static gint
3034 sp_style_write_iscale24(gchar *p, gint const len, gchar const *const key,
3035                         SPIScale24 const *const val, SPIScale24 const *const base,
3036                         guint const flags)
3038     Inkscape::CSSOStringStream os;
3040     if ((flags & SP_STYLE_FLAG_ALWAYS)
3041         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3042         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3043             && (!base->set || (val->value != base->value))))
3044     {
3045         if (val->inherit) {
3046             return g_snprintf(p, len, "%s:inherit;", key);
3047         } else {
3048             os << key << ":" << SP_SCALE24_TO_FLOAT(val->value) << ";";
3049             return g_strlcpy(p, os.str().c_str(), len);
3050         }
3051     }
3052     return 0;
3056 /**
3057  * Write SPIEnum object into string.
3058  */
3059 static gint
3060 sp_style_write_ienum(gchar *p, gint const len, gchar const *const key,
3061                      SPStyleEnum const *const dict,
3062                      SPIEnum const *const val, SPIEnum const *const base, guint const flags)
3064     if ((flags & SP_STYLE_FLAG_ALWAYS)
3065         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3066         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3067             && (!base->set || (val->computed != base->computed))))
3068     {
3069         if (val->inherit) {
3070             return g_snprintf(p, len, "%s:inherit;", key);
3071         }
3072         for (unsigned i = 0; dict[i].key; i++) {
3073             if (dict[i].value == static_cast< gint > (val->value) ) {
3074                 return g_snprintf(p, len, "%s:%s;", key, dict[i].key);
3075             }
3076         }
3077     }
3078     return 0;
3083 /**
3084  * Write SPIString object into string.
3085  */
3086 static gint
3087 sp_style_write_istring(gchar *p, gint const len, gchar const *const key,
3088                        SPIString const *const val, SPIString const *const base, guint const flags)
3090     if ((flags & SP_STYLE_FLAG_ALWAYS)
3091         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3092         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3093             && (!base->set || strcmp(val->value, base->value))))
3094     {
3095         if (val->inherit) {
3096             return g_snprintf(p, len, "%s:inherit;", key);
3097         } else {
3098             return g_snprintf(p, len, "%s:%s;", key, val->value);
3099         }
3100     }
3101     return 0;
3105 /**
3106  * 
3107  */
3108 static bool
3109 sp_length_differ(SPILength const *const a, SPILength const *const b)
3111     if (a->unit != b->unit) {
3112         if (a->unit == SP_CSS_UNIT_EM) return true;
3113         if (a->unit == SP_CSS_UNIT_EX) return true;
3114         if (a->unit == SP_CSS_UNIT_PERCENT) return true;
3115         if (b->unit == SP_CSS_UNIT_EM) return true;
3116         if (b->unit == SP_CSS_UNIT_EX) return true;
3117         if (b->unit == SP_CSS_UNIT_PERCENT) return true;
3118     }
3120     return (a->computed != b->computed);
3125 /**
3126  * Write SPILength object into string.
3127  */
3128 static gint
3129 sp_style_write_ilength(gchar *p, gint const len, gchar const *const key,
3130                        SPILength const *const val, SPILength const *const base, guint const flags)
3132     Inkscape::CSSOStringStream os;
3134     if ((flags & SP_STYLE_FLAG_ALWAYS)
3135         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3136         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3137             && (!base->set || sp_length_differ(val, base))))
3138     {
3139         if (val->inherit) {
3140             return g_snprintf(p, len, "%s:inherit;", key);
3141         } else {
3142             switch (val->unit) {
3143                 case SP_CSS_UNIT_NONE:
3144                     os << key << ":" << val->computed << ";";
3145                     return g_strlcpy(p, os.str().c_str(), len);
3146                     break;
3147                 case SP_CSS_UNIT_PX:
3148                     os << key << ":" << val->computed << "px;";
3149                     return g_strlcpy(p, os.str().c_str(), len);
3150                     break;
3151                 case SP_CSS_UNIT_PT:
3152                     os << key << ":" << val->computed * PT_PER_PX << "pt;";
3153                     return g_strlcpy(p, os.str().c_str(), len);
3154                     break;
3155                 case SP_CSS_UNIT_PC:
3156                     os << key << ":" << val->computed * PT_PER_PX / 12.0 << "pc;";
3157                     return g_strlcpy(p, os.str().c_str(), len);
3158                     break;
3159                 case SP_CSS_UNIT_MM:
3160                     os << key << ":" << val->computed * MM_PER_PX << "mm;";
3161                     return g_strlcpy(p, os.str().c_str(), len);
3162                     break;
3163                 case SP_CSS_UNIT_CM:
3164                     os << key << ":" << val->computed * CM_PER_PX << "cm;";
3165                     return g_strlcpy(p, os.str().c_str(), len);
3166                     break;
3167                 case SP_CSS_UNIT_IN:
3168                     os << key << ":" << val->computed * IN_PER_PX << "in;";
3169                     return g_strlcpy(p, os.str().c_str(), len);
3170                     break;
3171                 case SP_CSS_UNIT_EM:
3172                     os << key << ":" << val->value << "em;";
3173                     return g_strlcpy(p, os.str().c_str(), len);
3174                     break;
3175                 case SP_CSS_UNIT_EX:
3176                     os << key << ":" << val->value << "ex;";
3177                     return g_strlcpy(p, os.str().c_str(), len);
3178                     break;
3179                 case SP_CSS_UNIT_PERCENT:
3180                     os << key << ":" << (val->value * 100.0) << "%;";
3181                     return g_strlcpy(p, os.str().c_str(), len);
3182                     break;
3183                 default:
3184                     /* Invalid */
3185                     break;
3186             }
3187         }
3188     }
3189     return 0;
3193 /**
3194  *
3195  */
3196 static bool
3197 sp_lengthornormal_differ(SPILengthOrNormal const *const a, SPILengthOrNormal const *const b)
3199     if (a->normal != b->normal) return true;
3200     if (a->normal) return false;
3202     if (a->unit != b->unit) {
3203         if (a->unit == SP_CSS_UNIT_EM) return true;
3204         if (a->unit == SP_CSS_UNIT_EX) return true;
3205         if (a->unit == SP_CSS_UNIT_PERCENT) return true;
3206         if (b->unit == SP_CSS_UNIT_EM) return true;
3207         if (b->unit == SP_CSS_UNIT_EX) return true;
3208         if (b->unit == SP_CSS_UNIT_PERCENT) return true;
3209     }
3211     return (a->computed != b->computed);
3214 /**
3215  * Write SPILengthOrNormal object into string.
3216  */
3217 static gint
3218 sp_style_write_ilengthornormal(gchar *p, gint const len, gchar const *const key,
3219                                SPILengthOrNormal const *const val,
3220                                SPILengthOrNormal const *const base,
3221                                guint const flags)
3223     if ((flags & SP_STYLE_FLAG_ALWAYS)
3224         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3225         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3226             && (!base->set || sp_lengthornormal_differ(val, base))))
3227     {
3228         if (val->normal) {
3229             return g_snprintf(p, len, "%s:normal;", key);
3230         } else {
3231             SPILength length;
3232             length.set = val->set;
3233             length.inherit = val->inherit;
3234             length.unit = val->unit;
3235             length.value = val->value;
3236             length.computed = val->computed;
3237             return sp_style_write_ilength(p, len, key, &length, NULL, SP_STYLE_FLAG_ALWAYS);
3238         }
3239     }
3240     return 0;
3243 /**
3244  *
3245  */
3246 static bool
3247 sp_textdecoration_differ(SPITextDecoration const *const a, SPITextDecoration const *const b)
3249     return    a->underline != b->underline
3250            || a->overline != b->overline
3251            || a->line_through != b->line_through
3252            || a->blink != b->blink;
3255 /**
3256  * Write SPITextDecoration object into string.
3257  */
3258 static gint
3259 sp_style_write_itextdecoration(gchar *p, gint const len, gchar const *const key,
3260                                SPITextDecoration const *const val,
3261                                SPITextDecoration const *const base,
3262                                guint const flags)
3264     Inkscape::CSSOStringStream os;
3266     if ((flags & SP_STYLE_FLAG_ALWAYS)
3267         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3268         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3269             && (!base->set || sp_textdecoration_differ(val, base))))
3270     {
3271         if (val->inherit) {
3272             return g_snprintf(p, len, "%s:inherit;", key);
3273         } else {
3274             os << key << ":";
3275             if (val->underline || val->overline || val->line_through || val->blink) {
3276                 if (val->underline) os << " underline";
3277                 if (val->overline) os << " overline";
3278                 if (val->line_through) os << " line-through";
3279                 if (val->blink) os << " blink";
3280             } else
3281                 os << "none";
3282             os << ";";
3283             return g_strlcpy(p, os.str().c_str(), len);
3284         }
3285     }
3286     return 0;
3289 /**
3290  *
3291  */
3292 static bool
3293 sp_paint_differ(SPIPaint const *const a, SPIPaint const *const b)
3295     if (a->type != b->type)
3296         return true;
3297     if (a->type == SP_PAINT_TYPE_COLOR)
3298         return !sp_color_is_equal(&a->value.color, &b->value.color);
3299     if (a->type == SP_PAINT_TYPE_PAINTSERVER)
3300         return (a->value.paint.server != b->value.paint.server);
3301     return false;
3306 /**
3307  * Write SPIPaint object into string.
3308  */
3309 static gint
3310 sp_style_write_ipaint(gchar *b, gint const len, gchar const *const key,
3311                       SPIPaint const *const paint, SPIPaint const *const base, guint const flags)
3313     if ((flags & SP_STYLE_FLAG_ALWAYS)
3314         || ((flags & SP_STYLE_FLAG_IFSET) && paint->set)
3315         || ((flags & SP_STYLE_FLAG_IFDIFF) && paint->set
3316             && (!base->set || sp_paint_differ(paint, base))))
3317     {
3318         if (paint->inherit) {
3319             return g_snprintf(b, len, "%s:inherit;", key);
3320         } else if (paint->currentcolor) {
3321             return g_snprintf(b, len, "%s:currentColor;", key);
3322         } else {
3323             switch (paint->type) {
3324             case SP_PAINT_TYPE_COLOR:
3325                 return g_snprintf(b, len, "%s:#%06x;", key, sp_color_get_rgba32_falpha(&paint->value.color, 0.0) >> 8);
3326                 break;
3327             case SP_PAINT_TYPE_PAINTSERVER:
3328                     return g_snprintf(b, len, "%s:url(%s);", key, paint->value.paint.uri);
3329                 break;
3330             default:
3331                 break;
3332             }
3333             return g_snprintf(b, len, "%s:none;", key);
3334         }
3335     }
3336     return 0;
3340 /**
3341  *
3342  */
3343 static bool
3344 sp_fontsize_differ(SPIFontSize const *const a, SPIFontSize const *const b)
3346     if (a->type != b->type)
3347         return true;
3348     if (a->type == SP_FONT_SIZE_LENGTH) {
3349         if (a->computed != b->computed)
3350             return true;
3351     } else {
3352         if (a->value != b->value)
3353             return true;
3354     }
3355     return false;
3359 /**
3360  * Write SPIFontSize object into string.
3361  */
3362 static gint
3363 sp_style_write_ifontsize(gchar *p, gint const len, gchar const *key,
3364                          SPIFontSize const *const val, SPIFontSize const *const base,
3365                          guint const flags)
3367     if ((flags & SP_STYLE_FLAG_ALWAYS)
3368         || ((flags & SP_STYLE_FLAG_IFSET) && val->set)
3369         || ((flags & SP_STYLE_FLAG_IFDIFF) && val->set
3370             && (!base->set || sp_fontsize_differ(val, base))))
3371     {
3372         if (val->inherit) {
3373             return g_snprintf(p, len, "%s:inherit;", key);
3374         } else if (val->type == SP_FONT_SIZE_LITERAL) {
3375             for (unsigned i = 0; enum_font_size[i].key; i++) {
3376                 if (enum_font_size[i].value == static_cast< gint > (val->value) ) {
3377                     return g_snprintf(p, len, "%s:%s;", key, enum_font_size[i].key);
3378                 }
3379             }
3380         } else if (val->type == SP_FONT_SIZE_LENGTH) {
3381             Inkscape::CSSOStringStream os;
3382             os << key << ":" << val->computed << "px;";      // must specify px, see inkscape bug 1221626, mozilla bug 234789
3383             return g_strlcpy(p, os.str().c_str(), len);
3384         } else if (val->type == SP_FONT_SIZE_PERCENTAGE) {
3385             Inkscape::CSSOStringStream os;
3386             os << key << ":" << (SP_F8_16_TO_FLOAT(val->value) * 100.0) << "%;";
3387             return g_strlcpy(p, os.str().c_str(), len);
3388         }
3389     }
3390     return 0;
3394 /**
3395  * Clear paint object; conditionally disconnect style from paintserver.
3396  */
3397 static void
3398 sp_style_paint_clear(SPStyle *style, SPIPaint *paint,
3399                      unsigned hunref, unsigned unset)
3401     if (hunref && (paint->type == SP_PAINT_TYPE_PAINTSERVER) && paint->value.paint.server) {
3402         if (style->hreffed) {
3403             sp_object_hunref(SP_OBJECT(paint->value.paint.server), style);
3404         }
3405         if (style->object || style->cloned) {
3406             g_signal_handlers_disconnect_matched(G_OBJECT(paint->value.paint.server),
3407                                                  G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, style);
3408         }
3409         paint->value.paint.server = NULL;
3410         paint->value.paint.uri = NULL;
3411         paint->type = SP_PAINT_TYPE_NONE;
3412     }
3414     if (unset) {
3415         paint->set = FALSE;
3416         paint->inherit = FALSE;
3417     }
3420 /**
3421  * Clear all style property attributes in object.
3422  */
3423 void
3424 sp_style_unset_property_attrs(SPObject *o)
3426     if (!o) return;
3428     SPStyle *style = SP_OBJECT_STYLE(o);
3429     if (!style) return;
3431     Inkscape::XML::Node *repr = SP_OBJECT_REPR(o);
3432     if (!repr) return;
3434     if (style->opacity.set) {
3435         repr->setAttribute("opacity", NULL);
3436     }
3437     if (style->color.set) {
3438         repr->setAttribute("color", NULL);
3439     }
3440     if (style->fill.set) {
3441         repr->setAttribute("fill", NULL);
3442     }
3443     if (style->fill_opacity.set) {
3444         repr->setAttribute("fill-opacity", NULL);
3445     }
3446     if (style->fill_rule.set) {
3447         repr->setAttribute("fill-rule", NULL);
3448     }
3449     if (style->stroke.set) {
3450         repr->setAttribute("stroke", NULL);
3451     }
3452     if (style->stroke_width.set) {
3453         repr->setAttribute("stroke-width", NULL);
3454     }
3455     if (style->stroke_linecap.set) {
3456         repr->setAttribute("stroke-linecap", NULL);
3457     }
3458     if (style->stroke_linejoin.set) {
3459         repr->setAttribute("stroke-linejoin", NULL);
3460     }
3461     if (style->marker[SP_MARKER_LOC].set) {
3462         repr->setAttribute("marker", NULL);
3463     }
3464     if (style->marker[SP_MARKER_LOC_START].set) {
3465         repr->setAttribute("marker-start", NULL);
3466     }
3467     if (style->marker[SP_MARKER_LOC_MID].set) {
3468         repr->setAttribute("marker-mid", NULL);
3469     }
3470     if (style->marker[SP_MARKER_LOC_END].set) {
3471         repr->setAttribute("marker-end", NULL);
3472     }
3473     if (style->stroke_opacity.set) {
3474         repr->setAttribute("stroke-opacity", NULL);
3475     }
3476     if (style->stroke_dasharray_set) {
3477         repr->setAttribute("stroke-dasharray", NULL);
3478     }
3479     if (style->stroke_dashoffset_set) {
3480         repr->setAttribute("stroke-dashoffset", NULL);
3481     }
3482     if (style->text_private && style->text->font_family.set) {
3483         repr->setAttribute("font-family", NULL);
3484     }
3485     if (style->text_anchor.set) {
3486         repr->setAttribute("text-anchor", NULL);
3487     }
3488     if (style->writing_mode.set) {
3489         repr->setAttribute("writing_mode", NULL);
3490     }
3493 /**
3494  * \pre style != NULL.
3495  * \pre flags in {IFSET, ALWAYS}.
3496  */
3497 SPCSSAttr *
3498 sp_css_attr_from_style(SPStyle const *const style, guint const flags)
3500     g_return_val_if_fail(style != NULL, NULL);
3501     g_return_val_if_fail(((flags == SP_STYLE_FLAG_IFSET) ||
3502                           (flags == SP_STYLE_FLAG_ALWAYS)  ),
3503                          NULL);
3504     gchar *style_str = sp_style_write_string(style, flags);
3505     SPCSSAttr *css = sp_repr_css_attr_new();
3506     sp_repr_css_attr_add_from_string(css, style_str);
3507     g_free(style_str);
3508     return css;
3512 /**
3513  * \pre object != NULL
3514  * \pre flags in {IFSET, ALWAYS}.
3515  */
3516 SPCSSAttr *
3517 sp_css_attr_from_object(SPObject *object, guint const flags)
3519     g_return_val_if_fail(((flags == SP_STYLE_FLAG_IFSET) ||
3520                           (flags == SP_STYLE_FLAG_ALWAYS)  ),
3521                          NULL);
3522     SPStyle const *const style = SP_OBJECT_STYLE(object);
3523     if (style == NULL)
3524         return NULL;
3525     return sp_css_attr_from_style (style, flags);
3528 /**
3529  * Unset any text-related properties
3530  */
3531 SPCSSAttr *
3532 sp_css_attr_unset_text(SPCSSAttr *css)
3534     sp_repr_css_set_property(css, "font", NULL); // not implemented yet
3535     sp_repr_css_set_property(css, "font-size", NULL);
3536     sp_repr_css_set_property(css, "font-size-adjust", NULL); // not implemented yet
3537     sp_repr_css_set_property(css, "font-style", NULL);
3538     sp_repr_css_set_property(css, "font-variant", NULL);
3539     sp_repr_css_set_property(css, "font-weight", NULL);
3540     sp_repr_css_set_property(css, "font-stretch", NULL);
3541     sp_repr_css_set_property(css, "font-family", NULL);
3542     sp_repr_css_set_property(css, "text-indent", NULL);
3543     sp_repr_css_set_property(css, "text-align", NULL);
3544     sp_repr_css_set_property(css, "text-decoration", NULL);
3545     sp_repr_css_set_property(css, "line-height", NULL);
3546     sp_repr_css_set_property(css, "letter-spacing", NULL);
3547     sp_repr_css_set_property(css, "word-spacing", NULL);
3548     sp_repr_css_set_property(css, "text-transform", NULL);
3549     sp_repr_css_set_property(css, "direction", NULL);
3550     sp_repr_css_set_property(css, "block-progression", NULL);
3551     sp_repr_css_set_property(css, "writing-mode", NULL);
3552     sp_repr_css_set_property(css, "text-anchor", NULL);
3553     sp_repr_css_set_property(css, "kerning", NULL); // not implemented yet
3554     sp_repr_css_set_property(css, "dominant-baseline", NULL); // not implemented yet
3555     sp_repr_css_set_property(css, "alignment-baseline", NULL); // not implemented yet
3556     sp_repr_css_set_property(css, "baseline-shift", NULL); // not implemented yet
3558     return css;
3561 bool
3562 is_url(char const *p)
3564     if (p == NULL)
3565         return false;
3566 /** \todo
3567  * FIXME: I'm not sure if this applies to SVG as well, but CSS2 says any URIs 
3568  * in property values must start with 'url('.
3569  */
3570     return (g_ascii_strncasecmp(p, "url(", 4) == 0);
3573 /**
3574  * Unset any properties that contain URI values. 
3575  *
3576  * Used for storing style that will be reused across documents when carrying 
3577  * the referenced defs is impractical.
3578  */
3579 SPCSSAttr *
3580 sp_css_attr_unset_uris(SPCSSAttr *css)
3582 // All properties that may hold <uri> or <paint> according to SVG 1.1
3583     if (is_url(sp_repr_css_property(css, "clip-path", NULL))) sp_repr_css_set_property(css, "clip-path", NULL);
3584     if (is_url(sp_repr_css_property(css, "color-profile", NULL))) sp_repr_css_set_property(css, "color-profile", NULL);
3585     if (is_url(sp_repr_css_property(css, "cursor", NULL))) sp_repr_css_set_property(css, "cursor", NULL);
3586     if (is_url(sp_repr_css_property(css, "filter", NULL))) sp_repr_css_set_property(css, "filter", NULL);
3587     if (is_url(sp_repr_css_property(css, "marker-start", NULL))) sp_repr_css_set_property(css, "marker-start", NULL);
3588     if (is_url(sp_repr_css_property(css, "marker-mid", NULL))) sp_repr_css_set_property(css, "marker-mid", NULL);
3589     if (is_url(sp_repr_css_property(css, "marker-end", NULL))) sp_repr_css_set_property(css, "marker-end", NULL);
3590     if (is_url(sp_repr_css_property(css, "mask", NULL))) sp_repr_css_set_property(css, "mask", NULL);
3591     if (is_url(sp_repr_css_property(css, "fill", NULL))) sp_repr_css_set_property(css, "fill", NULL);
3592     if (is_url(sp_repr_css_property(css, "stroke", NULL))) sp_repr_css_set_property(css, "stroke", NULL);
3594     return css;
3597 /**
3598  * Scale a single-value property.
3599  */
3600 void
3601 sp_css_attr_scale_property_single(SPCSSAttr *css, gchar const *property,
3602                                   double ex, bool only_with_units = false)
3604     gchar const *w = sp_repr_css_property(css, property, NULL);
3605     if (w) {
3606         gchar *units = NULL;
3607         double wd = g_ascii_strtod(w, &units) * ex;
3608         if (w == units) {// nothing converted, non-numeric value
3609             return;
3610         }
3611         if (only_with_units && (units == NULL || *units == '\0' || *units == '%')) {
3612             // only_with_units, but no units found, so do nothing.
3613             return;
3614         }
3615         Inkscape::CSSOStringStream os;
3616         os << wd << units; // reattach units
3617         sp_repr_css_set_property(css, property, os.str().c_str());
3618     }
3621 /**
3622  * Scale a list-of-values property.
3623  */
3624 void
3625 sp_css_attr_scale_property_list(SPCSSAttr *css, gchar const *property, double ex)
3627     gchar const *string = sp_repr_css_property(css, property, NULL);
3628     if (string) {
3629         Inkscape::CSSOStringStream os;
3630         gchar **a = g_strsplit(string, ",", 10000);
3631         bool first = true;
3632         for (gchar **i = a; i != NULL; i++) {
3633             gchar *w = *i;
3634             if (w == NULL)
3635                 break;
3636             gchar *units = NULL;
3637             double wd = g_ascii_strtod(w, &units) * ex;
3638             if (w == units) {// nothing converted, non-numeric value ("none" or "inherit"); do nothing
3639                 g_strfreev(a);
3640                 return;
3641             }
3642             if (!first) {
3643                 os << ",";
3644             }
3645             os << wd << units; // reattach units
3646             first = false;
3647         }
3648         sp_repr_css_set_property(css, property, os.str().c_str());
3649         g_strfreev(a);
3650     }
3653 /**
3654  * Scale any properties that may hold <length> by ex.
3655  */
3656 SPCSSAttr *
3657 sp_css_attr_scale(SPCSSAttr *css, double ex)
3659     sp_css_attr_scale_property_single(css, "baseline-shift", ex);
3660     sp_css_attr_scale_property_single(css, "stroke-width", ex);
3661     sp_css_attr_scale_property_list   (css, "stroke-dasharray", ex);
3662     sp_css_attr_scale_property_single(css, "stroke-dashoffset", ex);
3663     sp_css_attr_scale_property_single(css, "font-size", ex);
3664     sp_css_attr_scale_property_single(css, "kerning", ex);
3665     sp_css_attr_scale_property_single(css, "letter-spacing", ex);
3666     sp_css_attr_scale_property_single(css, "word-spacing", ex);
3667     sp_css_attr_scale_property_single(css, "line-height", ex, true);
3669     return css;
3673 /**
3674  * Remove quotes from SPIString object value.
3675  * 
3676  * \todo FIXME: now used for font family, but perhaps this should apply to 
3677  * ALL strings (check CSS spec), in which case this should be part of 
3678  * read_istring.
3679  */
3680 void
3681 css2_unescape_unquote (SPIString *val)
3683     if (val->set && val->value && strlen(val->value) >= 2) {
3685        /// \todo unescape all \-escaped chars
3687         int l = strlen(val->value);
3688         if ((val->value[0] == '"' && val->value[l - 1] == '"') || 
3689             (val->value[0] == '\'' && val->value[l - 1] == '\'')) {
3690                 memcpy (val->value, val->value+1, l - 2);
3691                 val->value[l - 2] = '\0';
3692         }
3693     }
3697 /*
3698   Local Variables:
3699   mode:c++
3700   c-file-style:"stroustrup"
3701   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
3702   indent-tabs-mode:nil
3703   fill-column:99
3704   End:
3705 */
3706 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :