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-document.h"
12 #include "xml/simple-node.h"
13 #include "style.h"
14 #include "libcroco/cr-sel-eng.h"
16 using Inkscape::Util::List;
17 using Inkscape::XML::AttributeRecord;
18 using Inkscape::XML::SimpleNode;
19 using Inkscape::XML::Node;
20 using Inkscape::XML::NodeType;
21 using Inkscape::XML::Document;
23 struct SPCSSAttrImpl : public SimpleNode, public SPCSSAttr {
24 public:
25 SPCSSAttrImpl(Document *doc)
26 : SimpleNode(g_quark_from_static_string("css"), doc) {}
27 SPCSSAttrImpl(SPCSSAttrImpl const &other, Document *doc)
28 : SimpleNode(other, doc) {}
30 NodeType type() const { return Inkscape::XML::ELEMENT_NODE; }
32 protected:
33 SimpleNode *_duplicate(Document* doc) const { return new SPCSSAttrImpl(*this, doc); }
34 };
36 static void sp_repr_css_add_components(SPCSSAttr *css, Node *repr, gchar const *attr);
39 SPCSSAttr *
40 sp_repr_css_attr_new()
41 {
42 static Inkscape::XML::Document *attr_doc=NULL;
43 if (!attr_doc) {
44 attr_doc = new Inkscape::XML::SimpleDocument();
45 }
46 return new SPCSSAttrImpl(attr_doc);
47 }
49 void
50 sp_repr_css_attr_unref(SPCSSAttr *css)
51 {
52 g_assert(css != NULL);
53 Inkscape::GC::release((Node *) css);
54 }
56 SPCSSAttr *sp_repr_css_attr(Node *repr, gchar const *attr)
57 {
58 g_assert(repr != NULL);
59 g_assert(attr != NULL);
61 SPCSSAttr *css = sp_repr_css_attr_new();
62 sp_repr_css_add_components(css, repr, attr);
63 return css;
64 }
66 static void
67 sp_repr_css_attr_inherited_recursive(SPCSSAttr *css, Node *repr, gchar const *attr)
68 {
69 Node *parent = sp_repr_parent(repr);
71 // read the ancestors from root down, using head recursion, so that children override parents
72 if (parent) {
73 sp_repr_css_attr_inherited_recursive(css, parent, attr);
74 }
76 sp_repr_css_add_components(css, repr, attr);
77 }
80 SPCSSAttr *sp_repr_css_attr_inherited(Node *repr, gchar const *attr)
81 {
82 g_assert(repr != NULL);
83 g_assert(attr != NULL);
85 SPCSSAttr *css = sp_repr_css_attr_new();
87 sp_repr_css_attr_inherited_recursive(css, repr, attr);
89 return css;
90 }
92 static void
93 sp_repr_css_add_components(SPCSSAttr *css, Node *repr, gchar const *attr)
94 {
95 g_assert(css != NULL);
96 g_assert(repr != NULL);
97 g_assert(attr != NULL);
99 char const *data = repr->attribute(attr);
100 sp_repr_css_attr_add_from_string(css, data);
101 }
103 char const *
104 sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
105 {
106 g_assert(css != NULL);
107 g_assert(name != NULL);
109 char const *attr = ((Node *)css)->attribute(name);
110 return ( attr == NULL
111 ? defval
112 : attr );
113 }
115 bool
116 sp_repr_css_property_is_unset(SPCSSAttr *css, gchar const *name)
117 {
118 g_assert(css != NULL);
119 g_assert(name != NULL);
121 char const *attr = ((Node *)css)->attribute(name);
122 return (attr && !strcmp(attr, "inkscape:unset"));
123 }
126 void
127 sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
128 {
129 g_assert(css != NULL);
130 g_assert(name != NULL);
132 ((Node *) css)->setAttribute(name, value, false);
133 }
135 void
136 sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
137 {
138 g_assert(css != NULL);
139 g_assert(name != NULL);
141 ((Node *) css)->setAttribute(name, "inkscape:unset", false);
142 }
144 double
145 sp_repr_css_double_property(SPCSSAttr *css, gchar const *name, double defval)
146 {
147 g_assert(css != NULL);
148 g_assert(name != NULL);
150 return sp_repr_get_double_attribute((Node *) css, name, defval);
151 }
153 gchar *
154 sp_repr_css_write_string(SPCSSAttr *css)
155 {
156 Glib::ustring buffer;
158 for ( List<AttributeRecord const> iter = css->attributeList() ;
159 iter ; ++iter )
160 {
161 if (iter->value && !strcmp(iter->value, "inkscape:unset")) {
162 continue;
163 }
165 buffer.append(g_quark_to_string(iter->key));
166 buffer.push_back(':');
167 if (!strcmp(g_quark_to_string(iter->key), "font-family")
168 || !strcmp(g_quark_to_string(iter->key), "-inkscape-font-specification")) {
169 // we only quote font-family/font-specification, as SPStyle does
170 gchar *t = g_strdup (iter->value);
171 g_free (t);
172 gchar *val_quoted = css2_escape_quote (iter->value);
173 if (val_quoted) {
174 buffer.append(val_quoted);
175 g_free (val_quoted);
176 }
177 } else {
178 buffer.append(iter->value); // unquoted
179 }
181 if (rest(iter)) {
182 buffer.push_back(';');
183 }
184 }
186 return (buffer.empty() ? NULL : g_strdup (buffer.c_str()));
187 }
189 void
190 sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
191 {
192 g_assert(repr != NULL);
193 g_assert(css != NULL);
194 g_assert(attr != NULL);
196 gchar *value = sp_repr_css_write_string(css);
198 repr->setAttribute(attr, value);
200 if (value) g_free (value);
201 }
203 void
204 sp_repr_css_print(SPCSSAttr *css)
205 {
206 for ( List<AttributeRecord const> iter = css->attributeList() ;
207 iter ; ++iter )
208 {
209 gchar const * key = g_quark_to_string(iter->key);
210 gchar const * val = iter->value;
211 g_print("%s:\t%s\n",key,val);
212 }
213 }
215 void
216 sp_repr_css_merge(SPCSSAttr *dst, SPCSSAttr *src)
217 {
218 g_assert(dst != NULL);
219 g_assert(src != NULL);
221 dst->mergeFrom(src, "");
222 }
225 static void
226 sp_repr_css_merge_from_decl(SPCSSAttr *css, CRDeclaration const *const decl)
227 {
228 guchar *const str_value_unsigned = cr_term_to_string(decl->value);
229 gchar *const str_value = reinterpret_cast<gchar *>(str_value_unsigned);
230 gchar *value_unquoted = attribute_unquote (str_value); // libcroco returns strings quoted in ""
231 ((Node *) css)->setAttribute(decl->property->stryng->str, value_unquoted, false);
232 g_free(value_unquoted);
233 g_free(str_value);
234 }
236 /**
237 * \pre decl_list != NULL
238 */
239 static void
240 sp_repr_css_merge_from_decl_list(SPCSSAttr *css, CRDeclaration const *const decl_list)
241 {
242 // read the decls from start to end, using tail recursion, so that latter declarations override
243 // (Ref: http://www.w3.org/TR/REC-CSS2/cascade.html#cascading-order point 4.)
244 // because sp_repr_css_merge_from_decl sets properties unconditionally
245 sp_repr_css_merge_from_decl(css, decl_list);
246 if (decl_list->next) {
247 sp_repr_css_merge_from_decl_list(css, decl_list->next);
248 }
249 }
251 void
252 sp_repr_css_attr_add_from_string(SPCSSAttr *css, gchar const *p)
253 {
254 if (p != NULL) {
255 CRDeclaration *const decl_list
256 = cr_declaration_parse_list_from_buf(reinterpret_cast<guchar const *>(p), CR_UTF_8);
257 if (decl_list) {
258 sp_repr_css_merge_from_decl_list(css, decl_list);
259 cr_declaration_destroy(decl_list);
260 }
261 }
262 }
264 void
265 sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
266 {
267 g_assert(repr != NULL);
268 g_assert(css != NULL);
269 g_assert(attr != NULL);
271 SPCSSAttr *current = sp_repr_css_attr(repr, attr);
272 sp_repr_css_merge(current, css);
273 sp_repr_css_set(repr, current, attr);
275 sp_repr_css_attr_unref(current);
276 }
278 void
279 sp_repr_css_change_recursive(Node *repr, SPCSSAttr *css, gchar const *attr)
280 {
281 g_assert(repr != NULL);
282 g_assert(css != NULL);
283 g_assert(attr != NULL);
285 sp_repr_css_change(repr, css, attr);
287 for (Node *child = repr->firstChild(); child != NULL; child = child->next()) {
288 sp_repr_css_change_recursive(child, css, attr);
289 }
290 }
293 /*
294 Local Variables:
295 mode:c++
296 c-file-style:"stroustrup"
297 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
298 indent-tabs-mode:nil
299 fill-column:99
300 End:
301 */
302 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :