Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
uri-references.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
15#include "uri-references.h"
16
17#include <iostream>
18#include <cstring>
19
20#include <glibmm/miscutils.h>
22#include "bad-uri-exception.h"
23#include "document.h"
24#include "sp-object.h"
25#include "uri.h"
26#include "util/uri.h"
27#include "sp-tag-use.h"
28
29namespace Inkscape {
30
32 : _owner(owner)
33 , _owner_document(nullptr)
34 , _obj(nullptr)
35 , _uri(nullptr)
36{
37 g_assert(_owner != nullptr);
38 /* FIXME !!! attach to owner's destroy signal to clean up in case */
39}
40
42 : _owner(nullptr)
43 , _owner_document(owner_document)
44 , _obj(nullptr)
45 , _uri(nullptr)
46{
47 g_assert(_owner_document != nullptr);
48}
49
54
55/*
56 * The main ideas here are:
57 * (1) "If we are inside a clone, then we can accept if and only if our "original thing" can accept the reference"
58 * (this caused problems when there are clones because a change in ids triggers signals for the object hrefing this id,
59 * but also its cloned reprs(descendants of <use> referencing an ancestor of the href'ing object)).
60 *
61 * (2) Once we have an (potential owner) object, it can accept a href to obj, iff the graph of objects where directed
62 * edges are
63 * either parent->child relations , *** or href'ing to href'ed *** relations, stays acyclic.
64 * We can go either from owner and up in the tree, or from obj and down, in either case this will be in the worst case
65 *linear in the number of objects.
66 * There are no easy objects allowing to do the second proposition, while "hrefList" is a "list of objects href'ing us",
67 *so we'll take this.
68 * Then we keep a set of already visited elements, and do a DFS on this graph. if we find obj, then BOOM.
69 */
70
72{
73 // we go back following hrefList and parent to find if the object already references ourselves indirectly
74 std::set<SPObject *> done;
75 SPObject *owner = getOwner();
76 //allow LPE as owner has any URI attached
77 auto lpobj = cast<LivePathEffectObject>(obj);
78 if (!owner || lpobj)
79 return true;
80
81 while (owner->cloned) {
82 if(!owner->clone_original)//happens when the clone is existing and linking to something, even before the original objects exists.
83 //for instance, it can happen when you paste a filtered object in a already cloned group: The construction of the
84 //clone representation of the filtered object will finish before the original object, so the cloned repr will
85 //have to _accept the filter even though the original does not exist yet. In that case, we'll accept iff the parent of the
86 //original can accept it: loops caused by other relations than parent-child would be prevented when created on their base object.
87 //Fixes bug 1636533.
88 owner = owner->parent;
89 else
90 owner = owner->clone_original;
91 }
92 // once we have the "original" object (hopefully) we look at who is referencing it
93 if (obj == owner)
94 return false;
95 std::list<SPObject *> todo(owner->hrefList);
96 todo.push_front(owner->parent);
97 while (!todo.empty()) {
98 SPObject *e = todo.front();
99 todo.pop_front();
100 if (!e)
101 continue;
102 if (done.insert(e).second) {
103 if (e == obj) {
104 return false;
105 }
106 todo.push_front(e->parent);
107 todo.insert(todo.begin(), e->hrefList.begin(), e->hrefList.end());
108 }
109 }
110 return true;
111}
112
113void URIReference::attach(const URI &uri)
114{
115 SPDocument *document = nullptr;
116
117 // Attempt to get the document that contains the URI
118 if (_owner) {
119 document = _owner->document;
120 } else if (_owner_document) {
121 document = _owner_document;
122 }
123
124 // createChildDoc() assumes that the referenced file is an SVG.
125 // PNG and JPG files are allowed (in the case of feImage).
126 gchar const *filename = uri.getPath() ? uri.getPath() : "";
127 bool skip = false;
128 if (g_str_has_suffix(filename, ".jpg") || g_str_has_suffix(filename, ".JPG") ||
129 g_str_has_suffix(filename, ".png") || g_str_has_suffix(filename, ".PNG")) {
130 skip = true;
131 }
132
133 // The path contains references to separate document files to load.
134 if (document && uri.getPath() && !skip) {
135 char const *base = document->getDocumentBase();
136 auto absuri = URI::from_href_and_basedir(uri.str().c_str(), base);
137 std::string path;
138
139 try {
140 path = absuri.toNativeFilename();
141 } catch (const Glib::Error &e) {
142 g_warning("%s", e.what());
143 }
144
145 if (!path.empty()) {
146 document = document->createChildDoc(path);
147 } else {
148 document = nullptr;
149 }
150 }
151 if (!document) {
152 g_warning("Can't get document for referenced URI: %s", filename);
153 return;
154 }
155
156 gchar const *fragment = uri.getFragment();
157 if (uri.getQuery() || !fragment) {
159 }
160
161 /* FIXME !!! real xpointer support should be delegated to document */
162 /* for now this handles the minimal xpointer form that SVG 1.0
163 * requires of us
164 */
165 gchar *id = nullptr;
166 if (!strncmp(fragment, "xpointer(", 9)) {
167 /* FIXME !!! this is wasteful */
168 /* FIXME: It looks as though this is including "))" in the id. I suggest moving
169 the strlen calculation and validity testing to before strdup, and copying just
170 the id without the "))". -- pjrm */
171 if (!strncmp(fragment, "xpointer(id(", 12)) {
172 id = g_strdup(fragment + 12);
173 size_t const len = strlen(id);
174 if (len < 3 || strcmp(id + len - 2, "))")) {
175 g_free(id);
176 throw MalformedURIException();
177 }
178 } else {
180 }
181 } else {
182 id = g_strdup(fragment);
183 }
184
185 /* FIXME !!! validate id as an NCName somewhere */
186
187 _connection.disconnect();
188 delete _uri;
189 _uri = new URI(uri);
190
191 _setObject(document->getObjectById(id));
192 _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject));
193 g_free(id);
194}
195
196bool URIReference::try_attach(char const *uri)
197{
198 if (uri && uri[0]) {
199 try {
200 attach(Inkscape::URI(uri));
201 return true;
202 } catch (Inkscape::BadURIException &e) {
203 g_warning("%s", e.what());
204 }
205 }
206 detach();
207 return false;
208}
209
211{
212 _connection.disconnect();
213 delete _uri;
214 _uri = nullptr;
215 _setObject(nullptr);
216}
217
219{
220 if (obj && !_acceptObject(obj)) {
221 obj = nullptr;
222 }
223
224 if (obj == _obj)
225 return;
226
227 SPObject *old_obj = _obj;
228 _obj = obj;
229
230 _release_connection.disconnect();
231 if (_obj && (!_owner || !_owner->cloned)) {
234 }
235 _changed_signal.emit(old_obj, _obj);
236 if (old_obj && (!_owner || !_owner->cloned)) {
237 /* release the old object _after_ the signal emission */
238 old_obj->unhrefObject(_owner);
239 }
240}
241
242/* If an object is deleted, current semantics require that we release
243 * it on its "release" signal, rather than later, when its ID is actually
244 * unregistered from the document.
245 */
247{
248 g_assert(_obj == obj);
249 _setObject(nullptr);
250}
251
252} /* namespace Inkscape */
253
254
255
257{
258 SPObject *ref = nullptr;
259
260 if (document && uri && (strncmp(uri, "url(", 4) == 0)) {
261 auto trimmed = extract_uri(uri);
262 if (!trimmed.empty()) {
263 ref = sp_uri_reference_resolve(document, trimmed.c_str());
264 }
265 }
266
267 return ref;
268}
269
270SPObject *sp_uri_reference_resolve(SPDocument *document, const gchar *uri)
271{
272 SPObject *ref = nullptr;
273
274 if (uri && (*uri == '#')) {
275 ref = document->getObjectById(uri + 1);
276 }
277
278 return ref;
279}
280
281/*
282 Local Variables:
283 mode:c++
284 c-file-style:"stroustrup"
285 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
286 indent-tabs-mode:nil
287 fill-column:99
288 End:
289*/
290// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :
TODO: insert short description here.
Fragment fragment
Definition canvas.cpp:136
sigc::connection _release_connection
virtual ~URIReference()
Destructor.
sigc::signal< void(SPObject *, SPObject *)> _changed_signal
void detach()
Detaches from the currently attached URI target, if any; the current referrent is signaled as NULL.
bool try_attach(char const *uri)
Try to attach to a URI.
void _setObject(SPObject *object)
URIReference(SPObject *owner)
Constructor.
void _release(SPObject *object)
SPObject * getOwner() const
Returns a pointer to the URIReference's owner.
virtual bool _acceptObject(SPObject *obj) const
void attach(URI const &uri)
Attaches to a URI, relative to the specified document.
sigc::connection _connection
Represents an URI as per RFC 2396.
Definition uri.h:36
const char * getQuery() const
Return the query, which is the part between "?" and the optional fragment hash ("#")
Definition uri.cpp:159
static URI from_href_and_basedir(char const *href, char const *basedir)
Convenience function for the common use case given a xlink:href attribute and a local directory as th...
Definition uri.cpp:214
std::string str(char const *baseuri=nullptr) const
Return the string representation of this URI.
Definition uri.cpp:281
const char * getPath() const
Return the path.
Definition uri.cpp:155
const char * getFragment() const
Return the fragment, which is everything after "#".
Definition uri.cpp:163
Typed SVG document implementation.
Definition document.h:101
char const * getDocumentBase() const
Definition document.h:233
SPObject * getObjectById(std::string const &id) const
SPDocument * createChildDoc(std::string const &filename)
Fetches a document and attaches it to the current document as a child href.
Definition document.cpp:610
sigc::connection connectIdChanged(const char *id, IDChangedSignal::slot_type slot)
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPObject * clone_original
Definition sp-object.h:181
SPDocument * document
Definition sp-object.h:188
sigc::connection connectRelease(sigc::slot< void(SPObject *)> slot)
Connects to the release request signal.
Definition sp-object.h:237
SPObject * parent
Definition sp-object.h:189
void unhrefObject(SPObject *owner=nullptr)
Decrease weak refcount.
void hrefObject(SPObject *owner=nullptr)
Increase weak refcount.
unsigned int cloned
Definition sp-object.h:180
std::list< SPObject * > hrefList
Definition sp-object.h:197
Helper class to stream background task notifications as a series of messages.
Ocnode ** ref
Definition quantize.cpp:32
auto len
Definition safe-printf.h:21
SPObject * sp_css_uri_reference_resolve(SPDocument *document, const gchar *uri)
SPObject * sp_uri_reference_resolve(SPDocument *document, const gchar *uri)
std::string extract_uri(char const *s, char const **endptr)
Parse functional URI notation, as per 4.3.4 of CSS 2.1.
Definition uri.cpp:19
URI functions as per 4.3.4 of CSS 2.1 http://www.w3.org/TR/CSS21/syndata.html#uri.