Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
repr-util.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
7/*
8 * Authors:
9 * Lauris Kaplinski <lauris@ximian.com>
10 * Jon A. Cruz <jon@joncruz.org>
11 *
12 * Copyright (C) 1999-2000 Lauris Kaplinski
13 * Copyright (C) 2000-2001 Ximian, Inc.
14 * g++ port Copyright (C) 2003 Nathan Hurst
15 *
16 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
17 */
18
19#include <cstring>
20
21#include <glib.h>
22#include <glibmm.h>
23
24#include "svg/svg-length.h"
25
26#include "xml/repr.h"
27#include "xml/repr-sorting.h"
28
29
30struct SPXMLNs {
31 SPXMLNs *next;
32 unsigned int uri, prefix;
33};
34
35/*#####################
36# DEFINITIONS
37#####################*/
38
39#ifndef FALSE
40# define FALSE 0
41#endif
42
43#ifndef TRUE
44# define TRUE (!FALSE)
45#endif
46
47#ifndef MAX
48# define MAX(a,b) (((a) < (b)) ? (b) : (a))
49#endif
50
51/*#####################
52# FORWARD DECLARATIONS
53#####################*/
54
55static void sp_xml_ns_register_defaults();
56static char *sp_xml_ns_auto_prefix(char const *uri);
57
58/*#####################
59# MAIN
60#####################*/
61
66static SPXMLNs *namespaces=nullptr;
67
68/*
69 * There are the prefixes to use for the XML namespaces defined
70 * in repr.h
71 */
73{
74 static SPXMLNs defaults[11];
75
76 defaults[0].uri = g_quark_from_static_string(SP_SODIPODI_NS_URI);
77 defaults[0].prefix = g_quark_from_static_string("sodipodi");
78 defaults[0].next = &defaults[1];
79
80 defaults[1].uri = g_quark_from_static_string(SP_XLINK_NS_URI);
81 defaults[1].prefix = g_quark_from_static_string("xlink");
82 defaults[1].next = &defaults[2];
83
84 defaults[2].uri = g_quark_from_static_string(SP_SVG_NS_URI);
85 defaults[2].prefix = g_quark_from_static_string("svg");
86 defaults[2].next = &defaults[3];
87
88 defaults[3].uri = g_quark_from_static_string(SP_INKSCAPE_NS_URI);
89 defaults[3].prefix = g_quark_from_static_string("inkscape");
90 defaults[3].next = &defaults[4];
91
92 defaults[4].uri = g_quark_from_static_string(SP_RDF_NS_URI);
93 defaults[4].prefix = g_quark_from_static_string("rdf");
94 defaults[4].next = &defaults[5];
95
96 defaults[5].uri = g_quark_from_static_string(SP_CC_NS_URI);
97 defaults[5].prefix = g_quark_from_static_string("cc");
98 defaults[5].next = &defaults[6];
99
100 defaults[6].uri = g_quark_from_static_string(SP_DC_NS_URI);
101 defaults[6].prefix = g_quark_from_static_string("dc");
102 defaults[6].next = &defaults[8];
103
104 //defaults[7].uri = g_quark_from_static_string("https://inkscape.org/namespaces/deprecated/osb");
105 //defaults[7].prefix = g_quark_from_static_string("osb");
106 //defaults[7].next = &defaults[8];
107
108 // Inkscape versions prior to 0.44 would write this namespace
109 // URI instead of the correct sodipodi namespace; by adding this
110 // entry to the table last (where it gets used for URI -> prefix
111 // lookups, but not prefix -> URI lookups), we effectively transfer
112 // elements in this namespace to the correct sodipodi namespace:
113
114 defaults[8].uri = g_quark_from_static_string(SP_BROKEN_SODIPODI_NS_URI);
115 defaults[8].prefix = g_quark_from_static_string("sodipodi");
116 defaults[8].next = &defaults[9];
117
118 // "Duck prion"
119 // This URL became widespread due to a bug in versions <= 0.43
120
121 defaults[9].uri = g_quark_from_static_string("http://inkscape.sourceforge.net/DTD/s odipodi-0.dtd");
122 defaults[9].prefix = g_quark_from_static_string("sodipodi");
123 defaults[9].next = &defaults[10];
124
125 // This namespace URI is being phased out by Creative Commons
126
127 defaults[10].uri = g_quark_from_static_string(SP_OLD_CC_NS_URI);
128 defaults[10].prefix = g_quark_from_static_string("cc");
129 defaults[10].next = nullptr;
130
131 namespaces = &defaults[0];
132}
133
134char *sp_xml_ns_auto_prefix(char const *uri)
135{
136 char const *start, *end;
137 char *new_prefix;
138 start = uri;
139 while ((end = strpbrk(start, ":/"))) {
140 start = end + 1;
141 }
142 end = start + strspn(start, "abcdefghijklmnopqrstuvwxyz");
143 if (end == start) {
144 start = "ns";
145 end = start + 2;
146 }
147 new_prefix = g_strndup(start, end - start);
148 if (sp_xml_ns_prefix_uri(new_prefix)) {
149 char *temp;
150 int counter=0;
151 do {
152 temp = g_strdup_printf("%s%d", new_prefix, counter++);
153 } while (sp_xml_ns_prefix_uri(temp));
154 g_free(new_prefix);
155 new_prefix = temp;
156 }
157 return new_prefix;
158}
159
160gchar const *sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
161{
162 char const *prefix;
163
164 if (!uri) return nullptr;
165
166 if (!namespaces) {
168 }
169
170 GQuark const key = g_quark_from_string(uri);
171 prefix = nullptr;
172 for ( SPXMLNs *iter=namespaces ; iter ; iter = iter->next ) {
173 if ( iter->uri == key ) {
174 prefix = g_quark_to_string(iter->prefix);
175 break;
176 }
177 }
178
179 if (!prefix) {
180 char *new_prefix;
181 SPXMLNs *ns;
182 if (suggested) {
183 GQuark const prefix_key=g_quark_from_string(suggested);
184
185 SPXMLNs *found=namespaces;
186 while (found) {
187 if (found->prefix != prefix_key) {
188 found = found->next;
189 }
190 else {
191 break;
192 }
193 }
194
195 if (found) { // prefix already used?
196 new_prefix = sp_xml_ns_auto_prefix(uri);
197 } else { // safe to use suggested
198 new_prefix = g_strdup(suggested);
199 }
200 } else {
201 new_prefix = sp_xml_ns_auto_prefix(uri);
202 }
203
204 ns = g_new(SPXMLNs, 1);
205 g_assert( ns != nullptr );
206 ns->uri = g_quark_from_string(uri);
207 ns->prefix = g_quark_from_string(new_prefix);
208
209 g_free(new_prefix);
210
211 ns->next = namespaces;
212 namespaces = ns;
213
214 prefix = g_quark_to_string(ns->prefix);
215 }
216
217 return prefix;
218}
219
220gchar const *sp_xml_ns_prefix_uri(gchar const *prefix)
221{
222 SPXMLNs *iter;
223 char const *uri;
224
225 if (!prefix) return nullptr;
226
227 if (!namespaces) {
229 }
230
231 GQuark const key = g_quark_from_string(prefix);
232 uri = nullptr;
233 for ( iter = namespaces ; iter ; iter = iter->next ) {
234 if ( iter->prefix == key ) {
235 uri = g_quark_to_string(iter->uri);
236 break;
237 }
238 }
239 return uri;
240}
241
250{
251 int p1, p2;
252 if (first->parent() == second->parent()) {
253 /* Basic case - first and second have same parent */
254 p1 = first->position();
255 p2 = second->position();
256 } else {
257 /* Special case - the two objects have different parents. They
258 could be in different groups or on different layers for
259 instance. */
260
261 // Find the lowest common ancestor(LCA)
262 Inkscape::XML::Node const *ancestor = lowest_common_ancestor(first, second);
263 g_assert(ancestor != nullptr);
264
265 if (ancestor == first) {
266 return 1;
267 } else if (ancestor == second) {
268 return -1;
269 } else {
270 Inkscape::XML::Node const *to_first = find_containing_child(first, ancestor);
271 Inkscape::XML::Node const *to_second = find_containing_child(second, ancestor);
272 g_assert(to_second->parent() == to_first->parent());
273 p1 = to_first->position();
274 p2 = to_second->position();
275 }
276 }
277
278 if (p1 > p2) return 1;
279 if (p1 < p2) return -1;
280 return 0;
281
282 /* effic: Assuming that the parent--child relationship is consistent
283 (i.e. that the parent really does contain first and second among
284 its list of children), it should be equivalent to walk along the
285 children and see which we encounter first (returning 0 iff first
286 == second).
287
288 Given that this function is used solely for sorting, we can use a
289 similar approach to do the sort: gather the things to be sorted,
290 into an STL vector (to allow random access and faster
291 traversals). Do a single pass of the parent's children; for each
292 child, do a pass on whatever items in the vector we haven't yet
293 encountered. If the child is found, then swap it to the
294 beginning of the yet-unencountered elements of the vector.
295 Continue until no more than one remains unencountered. --
296 pjrm */
297}
298
300 return sp_repr_compare_position(first, second)<0;
301}
302
303
316 gchar const *key,
317 gchar const *value)
318{
319 g_return_val_if_fail(repr != nullptr, NULL);
320 for ( Inkscape::XML::Node *child = repr->firstChild() ; child ; child = child->next() ) {
321 gchar const *child_value = child->attribute(key);
322 if ( (child_value == value) ||
323 (value && child_value && !strcmp(child_value, value)) )
324 {
325 return child;
326 }
327 }
328 return nullptr;
329}
330
335 gchar const *key,
336 gchar const *value)
337{
338 Inkscape::XML::Node const *found = nullptr;
339 g_return_val_if_fail(repr != nullptr, NULL);
340 gchar const *repr_value = repr->attribute(key);
341 if ( (repr_value == value) ||
342 (repr_value && value && strcmp(repr_value, value) == 0) ) {
343 found = repr;
344 } else {
345 for (Inkscape::XML::Node const *child = repr->firstChild() ; child && !found; child = child->next() ) {
346 found = sp_repr_lookup_descendant( child, key, value );
347 }
348 }
349 return found;
350}
351
352
354 gchar const *key,
355 gchar const *value)
356{
357 Inkscape::XML::Node const *found = sp_repr_lookup_descendant( const_cast<Inkscape::XML::Node const *>(repr), key, value );
358 return const_cast<Inkscape::XML::Node *>(found);
359}
360
361Inkscape::XML::Node const *sp_repr_lookup_name( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth )
362{
363 Inkscape::XML::Node const *found = nullptr;
364 g_return_val_if_fail(repr != nullptr, NULL);
365 g_return_val_if_fail(name != nullptr, NULL);
366
367 GQuark const quark = g_quark_from_string(name);
368
369 if ( (GQuark)repr->code() == quark ) {
370 found = repr;
371 } else if ( maxdepth != 0 ) {
372 // maxdepth == -1 means unlimited
373 if ( maxdepth == -1 ) {
374 maxdepth = 0;
375 }
376
377 for (Inkscape::XML::Node const *child = repr->firstChild() ; child && !found; child = child->next() ) {
378 found = sp_repr_lookup_name( child, name, maxdepth - 1 );
379 }
380 }
381 return found;
382}
383
384Glib::ustring sp_repr_lookup_content(Inkscape::XML::Node const *repr, gchar const *name, Glib::ustring otherwise)
385{
386 if (auto node = sp_repr_lookup_name(repr, name, 1)) {
387 if (auto ret = node->firstChild()->content()) {
388 return ret;
389 }
390 }
391 return otherwise;
392}
393
395{
396 Inkscape::XML::Node const *found = sp_repr_lookup_name( const_cast<Inkscape::XML::Node const *>(repr), name, maxdepth );
397 return const_cast<Inkscape::XML::Node *>(found);
398}
399
400std::vector<Inkscape::XML::Node const *> sp_repr_lookup_name_many( Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth )
401{
402 std::vector<Inkscape::XML::Node const *> nodes;
403 std::vector<Inkscape::XML::Node const *> found;
404 g_return_val_if_fail(repr != nullptr, nodes);
405 g_return_val_if_fail(name != nullptr, nodes);
406
407 GQuark const quark = g_quark_from_string(name);
408
409 if ( (GQuark)repr->code() == quark ) {
410 nodes.push_back(repr);
411 }
412
413 if ( maxdepth != 0 ) {
414 // maxdepth == -1 means unlimited
415 if ( maxdepth == -1 ) {
416 maxdepth = 0;
417 }
418
419 for (Inkscape::XML::Node const *child = repr->firstChild() ; child; child = child->next() ) {
420 found = sp_repr_lookup_name_many( child, name, maxdepth - 1);
421 nodes.insert(nodes.end(), found.begin(), found.end());
422 }
423 }
424
425 return nodes;
426}
427
428std::vector<Inkscape::XML::Node *>
429sp_repr_lookup_property_many( Inkscape::XML::Node *repr, Glib::ustring const& property,
430 Glib::ustring const &value, int maxdepth )
431{
432 std::vector<Inkscape::XML::Node *> nodes;
433 std::vector<Inkscape::XML::Node *> found;
434 g_return_val_if_fail(repr != nullptr, nodes);
435
436 SPCSSAttr* css = sp_repr_css_attr (repr, "style");
437 if (value == sp_repr_css_property (css, property, "")) {
438 nodes.push_back(repr);
439 }
440
441 if ( maxdepth != 0 ) {
442 // maxdepth == -1 means unlimited
443 if ( maxdepth == -1 ) {
444 maxdepth = 0;
445 }
446
447 for (Inkscape::XML::Node *child = repr->firstChild() ; child; child = child->next() ) {
448 found = sp_repr_lookup_property_many( child, property, value, maxdepth - 1);
449 nodes.insert(nodes.end(), found.begin(), found.end());
450 }
451 }
452
453 return nodes;
454}
455
460{
461 if (node == nullptr) return false;
462 if (node->type() != Inkscape::XML::NodeType::ELEMENT_NODE) return false;
463 gchar const *name = node->name();
464 if (name == nullptr) return false;
465 if (!std::strcmp(name, "svg:title")) return true;
466 if (!std::strcmp(name, "svg:desc")) return true;
467 if (!std::strcmp(name, "svg:metadata")) return true;
468 return false;
469}
470
471/*
472 Local Variables:
473 mode:c++
474 c-file-style:"stroustrup"
475 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
476 indent-tabs-mode:nil
477 fill-column:99
478 End:
479*/
480// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual Node * next()=0
Get the next sibling of this node.
virtual int code() const =0
Get the integer code corresponding to the node's name.
Inkscape::XML::Node * sp_repr_lookup_child(Inkscape::XML::Node *repr, gchar const *key, gchar const *value)
Find an element node using an unique attribute.
virtual char const * name() const =0
Get the name of the element node.
virtual Node * firstChild()=0
Get the first child of this node.
virtual unsigned position() const =0
Get the index of this node in parent's child order.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual char const * content() const =0
Get the content of a text or comment node.
virtual NodeType type() const =0
Get the type of the node.
std::shared_ptr< Css const > css
Inkscape::XML::Node * node
Geom::Point start
Geom::Point end
@ ELEMENT_NODE
Regular element node, e.g. <group />.
static cairo_user_data_key_t key
static gint counter
Definition box3d.cpp:39
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
Definition repr-css.cpp:88
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
Inkscape::XML::Node const * lowest_common_ancestor(Inkscape::XML::Node const *a, Inkscape::XML::Node const *b)
Inkscape::XML::Node const * find_containing_child(Inkscape::XML::Node const *descendant, Inkscape::XML::Node const *ancestor)
TODO: insert short description here.
Inkscape::XML::Node const * sp_repr_lookup_descendant(Inkscape::XML::Node const *repr, gchar const *key, gchar const *value)
Recursive version of sp_repr_lookup_child().
bool sp_repr_is_meta_element(const Inkscape::XML::Node *node)
Determine if the node is a 'title', 'desc' or 'metadata' element.
gchar const * sp_xml_ns_uri_prefix(gchar const *uri, gchar const *suggested)
gchar const * sp_xml_ns_prefix_uri(gchar const *prefix)
static void sp_xml_ns_register_defaults()
Definition repr-util.cpp:72
static SPXMLNs * namespaces
SPXMLNs.
Definition repr-util.cpp:66
std::vector< Inkscape::XML::Node const * > sp_repr_lookup_name_many(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
int sp_repr_compare_position(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
Works for different-parent objects, so long as they have a common ancestor.
Glib::ustring sp_repr_lookup_content(Inkscape::XML::Node const *repr, gchar const *name, Glib::ustring otherwise)
static char * sp_xml_ns_auto_prefix(char const *uri)
Inkscape::XML::Node const * sp_repr_lookup_name(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
bool sp_repr_compare_position_bool(Inkscape::XML::Node const *first, Inkscape::XML::Node const *second)
std::vector< Inkscape::XML::Node * > sp_repr_lookup_property_many(Inkscape::XML::Node *repr, Glib::ustring const &property, Glib::ustring const &value, int maxdepth)
C facade to Inkscape::XML::Node.
guint32 GQuark
Glib::ustring name
Definition toolbars.cpp:55