afa0ca2ef36a8b439443cc36a8f58886eca162f0
1 /*
2 * bulia byak <buliabyak@users.sf.net>
3 */
5 #define SP_REPR_CSS_C
7 #include <cstring>
8 #include <glibmm/ustring.h>
10 #include "xml/repr.h"
11 #include "xml/simple-node.h"
12 #include "style.h"
13 #include "libcroco/cr-sel-eng.h"
15 using Inkscape::Util::List;
16 using Inkscape::XML::AttributeRecord;
17 using Inkscape::XML::SimpleNode;
18 using Inkscape::XML::Node;
19 using Inkscape::XML::NodeType;
20 using Inkscape::XML::Document;
22 struct SPCSSAttrImpl : public SimpleNode, public SPCSSAttr {
23 public:
24 SPCSSAttrImpl() : SimpleNode(g_quark_from_static_string("css")) {}
26 NodeType type() const { return Inkscape::XML::ELEMENT_NODE; }
28 protected:
29 SimpleNode *_duplicate(Document* /*doc*/) const { return new SPCSSAttrImpl(*this); }
30 };
32 static void sp_repr_css_add_components(SPCSSAttr *css, Node *repr, gchar const *attr);
34 SPCSSAttr *
35 sp_repr_css_attr_new()
36 {
37 return new SPCSSAttrImpl();
38 }
40 void
41 sp_repr_css_attr_unref(SPCSSAttr *css)
42 {
43 g_assert(css != NULL);
44 Inkscape::GC::release((Node *) css);
45 }
47 SPCSSAttr *sp_repr_css_attr(Node *repr, gchar const *attr)
48 {
49 g_assert(repr != NULL);
50 g_assert(attr != NULL);
52 SPCSSAttr *css = sp_repr_css_attr_new();
53 sp_repr_css_add_components(css, repr, attr);
54 return css;
55 }
57 static void
58 sp_repr_css_attr_inherited_recursive(SPCSSAttr *css, Node *repr, gchar const *attr)
59 {
60 Node *parent = sp_repr_parent(repr);
62 // read the ancestors from root down, using head recursion, so that children override parents
63 if (parent) {
64 sp_repr_css_attr_inherited_recursive(css, parent, attr);
65 }
67 sp_repr_css_add_components(css, repr, attr);
68 }
71 SPCSSAttr *sp_repr_css_attr_inherited(Node *repr, gchar const *attr)
72 {
73 g_assert(repr != NULL);
74 g_assert(attr != NULL);
76 SPCSSAttr *css = sp_repr_css_attr_new();
78 sp_repr_css_attr_inherited_recursive(css, repr, attr);
80 return css;
81 }
83 static void
84 sp_repr_css_add_components(SPCSSAttr *css, Node *repr, gchar const *attr)
85 {
86 g_assert(css != NULL);
87 g_assert(repr != NULL);
88 g_assert(attr != NULL);
90 char const *data = repr->attribute(attr);
91 sp_repr_css_attr_add_from_string(css, data);
92 }
94 char const *
95 sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
96 {
97 g_assert(css != NULL);
98 g_assert(name != NULL);
100 char const *attr = ((Node *)css)->attribute(name);
101 return ( attr == NULL
102 ? defval
103 : attr );
104 }
106 bool
107 sp_repr_css_property_is_unset(SPCSSAttr *css, gchar const *name)
108 {
109 g_assert(css != NULL);
110 g_assert(name != NULL);
112 char const *attr = ((Node *)css)->attribute(name);
113 return (attr && !strcmp(attr, "inkscape:unset"));
114 }
117 void
118 sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
119 {
120 g_assert(css != NULL);
121 g_assert(name != NULL);
123 sp_repr_set_attr((Node *) css, name, value);
124 }
126 void
127 sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
128 {
129 g_assert(css != NULL);
130 g_assert(name != NULL);
132 sp_repr_set_attr((Node *) css, name, "inkscape:unset");
133 }
135 double
136 sp_repr_css_double_property(SPCSSAttr *css, gchar const *name, double defval)
137 {
138 g_assert(css != NULL);
139 g_assert(name != NULL);
141 return sp_repr_get_double_attribute((Node *) css, name, defval);
142 }
144 gchar *
145 sp_repr_css_write_string(SPCSSAttr *css)
146 {
147 Glib::ustring buffer;
149 for ( List<AttributeRecord const> iter = css->attributeList() ;
150 iter ; ++iter )
151 {
152 if (iter->value && !strcmp(iter->value, "inkscape:unset")) {
153 continue;
154 }
156 buffer.append(g_quark_to_string(iter->key));
157 buffer.push_back(':');
158 if (!strcmp(g_quark_to_string(iter->key), "font-family")
159 || !strcmp(g_quark_to_string(iter->key), "-inkscape-font-specification")) {
160 // we only quote font-family/font-specification, as SPStyle does
161 gchar *t = g_strdup (iter->value);
162 g_free (t);
163 gchar *val_quoted = css2_escape_quote (iter->value);
164 if (val_quoted) {
165 buffer.append(val_quoted);
166 g_free (val_quoted);
167 }
168 } else {
169 buffer.append(iter->value); // unquoted
170 }
172 if (rest(iter)) {
173 buffer.push_back(';');
174 }
175 }
177 return (buffer.empty() ? NULL : g_strdup (buffer.c_str()));
178 }
180 void
181 sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
182 {
183 g_assert(repr != NULL);
184 g_assert(css != NULL);
185 g_assert(attr != NULL);
187 gchar *value = sp_repr_css_write_string(css);
189 repr->setAttribute(attr, value);
191 if (value) g_free (value);
192 }
194 void
195 sp_repr_css_print(SPCSSAttr *css)
196 {
197 for ( List<AttributeRecord const> iter = css->attributeList() ;
198 iter ; ++iter )
199 {
200 gchar const * key = g_quark_to_string(iter->key);
201 gchar const * val = iter->value;
202 g_print("%s:\t%s\n",key,val);
203 }
204 }
206 void
207 sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
208 {
209 g_assert(dst != NULL);
210 g_assert(src != NULL);
212 dst->mergeFrom(src, "");
213 }
216 static void
217 sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *const decl)
218 {
219 guchar *const str_value_unsigned = cr_term_to_string(decl->value);
220 gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned);
221 gchar *value_unquoted = attribute_unquote (str_value); // libcroco returns strings quoted in ""
222 sp_repr_set_attr((Node *) css, decl->property->stryng->str, value_unquoted);
223 g_free(value_unquoted);
224 g_free(str_value);
225 }
227 /**
228 * \pre decl_list != NULL
229 */
230 static void
231 sp_repr_css_merge_from_decl_list(SPCSSAttr *css, CRDeclaration const *const decl_list)
232 {
233 // read the decls from start to end, using tail recursion, so that latter declarations override
234 // (Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.)
235 // because sp_repr_css_merge_from_decl sets properties unconditionally
236 sp_repr_css_merge_from_decl(css, decl_list);
237 if (decl_list->next) {
238 sp_repr_css_merge_from_decl_list(css, decl_list->next);
239 }
240 }
242 void
243 sp_repr_css_attr_add_from_string(SPCSSAttr *css, gchar const *p)
244 {
245 if (p != NULL) {
246 CRDeclaration *const decl_list
247 = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8);
248 if (decl_list) {
249 sp_repr_css_merge_from_decl_list(css, decl_list);
250 cr_declaration_destroy(decl_list);
251 }
252 }
253 }
255 void
256 sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
257 {
258 g_assert(repr != NULL);
259 g_assert(css != NULL);
260 g_assert(attr != NULL);
262 SPCSSAttr *current = sp_repr_css_attr(repr, attr);
263 sp_repr_css_merge(current, css);
264 sp_repr_css_set(repr, current, attr);
266 sp_repr_css_attr_unref(current);
267 }
269 void
270 sp_repr_css_change_recursive(Node *repr, SPCSSAttr *css, gchar const *attr)
271 {
272 g_assert(repr != NULL);
273 g_assert(css != NULL);
274 g_assert(attr != NULL);
276 sp_repr_css_change(repr, css, attr);
278 for (Node *child = repr->firstChild(); child != NULL; child = child->next()) {
279 sp_repr_css_change_recursive(child, css, attr);
280 }
281 }
284 /*
285 Local Variables:
286 mode:c++
287 c-file-style:"stroustrup"
288 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
289 indent-tabs-mode:nil
290 fill-column:99
291 End:
292 */
293 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :