diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2018-01-30 08:33:01 +0000 |
|---|---|---|
| committer | Tavmjong Bah <tavmjong@free.fr> | 2018-01-30 08:33:01 +0000 |
| commit | 267299811df952d08324a39008f52c19641de9e0 (patch) | |
| tree | 28fef736a52cb7a72119d119be8eb663ad20a77f /src/object/uri-references.cpp | |
| parent | Translations: update inkscape.pot (diff) | |
| download | inkscape-267299811df952d08324a39008f52c19641de9e0.tar.gz inkscape-267299811df952d08324a39008f52c19641de9e0.zip | |
Move classes derived from SPObject to own directory.
A lot of header clean-up.
Diffstat (limited to 'src/object/uri-references.cpp')
| -rw-r--r-- | src/object/uri-references.cpp | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/object/uri-references.cpp b/src/object/uri-references.cpp new file mode 100644 index 000000000..7d6abd10a --- /dev/null +++ b/src/object/uri-references.cpp @@ -0,0 +1,268 @@ +/** + * Helper methods for resolving URI References + * + * Authors: + * Lauris Kaplinski <lauris@kaplinski.com> + * Marc Jeanmougin + * + * Copyright (C) 2001-2002 Lauris Kaplinski + * Copyright (C) 2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "uri-references.h" + +#include <iostream> +#include <cstring> + +#include <glibmm/miscutils.h> + +#include "bad-uri-exception.h" +#include "document.h" +#include "sp-object.h" +#include "uri.h" +#include "extract-uri.h" +#include "sp-tag-use.h" + +namespace Inkscape { + +URIReference::URIReference(SPObject *owner) + : _owner(owner) + , _owner_document(NULL) + , _obj(NULL) + , _uri(NULL) +{ + g_assert(_owner != NULL); + /* FIXME !!! attach to owner's destroy signal to clean up in case */ +} + +URIReference::URIReference(SPDocument *owner_document) + : _owner(NULL) + , _owner_document(owner_document) + , _obj(NULL) + , _uri(NULL) +{ + g_assert(_owner_document != NULL); +} + +URIReference::~URIReference() { detach(); } + +/* + * The main ideas here are: + * (1) "If we are inside a clone, then we can accept if and only if our "original thing" can accept the reference" + * (this caused problems when there are clones because a change in ids triggers signals for the object hrefing this id, + * but also its cloned reprs(descendants of <use> referencing an ancestor of the href'ing object)). + * + * (2) Once we have an (potential owner) object, it can accept a href to obj, iff the graph of objects where directed + * edges are + * either parent->child relations , *** or href'ing to href'ed *** relations, stays acyclic. + * 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 + *linear in the number of objects. + * There are no easy objects allowing to do the second proposition, while "hrefList" is a "list of objects href'ing us", + *so we'll take this. + * Then we keep a set of already visited elements, and do a DFS on this graph. if we find obj, then BOOM. + */ + +bool URIReference::_acceptObject(SPObject *obj) const +{ + // we go back following hrefList and parent to find if the object already references ourselves indirectly + std::set<SPObject *> done; + SPObject *owner = getOwner(); + if (!owner) + return true; + + while (owner->cloned) { + if(!owner->clone_original)//happens when the clone is existing and linking to something, even before the original objects exists. + //for instance, it can happen when you paste a filtered object in a already cloned group: The construction of the + //clone representation of the filtered object will finish before the original object, so the cloned repr will + //have to _accept the filter even though the original does not exist yet. In that case, we'll accept iff the parent of the + //original can accept it: loops caused by other relations than parent-child would be prevented when created on their base object. + //Fixes bug 1636533. + owner = owner->parent; + else + owner = owner->clone_original; + } + // once we have the "original" object (hopefully) we look at who is referencing it + if (obj == owner) + return false; + std::list<SPObject *> todo(owner->hrefList); + todo.push_front(owner->parent); + while (!todo.empty()) { + SPObject *e = todo.front(); + todo.pop_front(); + if (!dynamic_cast<SPObject *>(e)) + continue; + if (done.insert(e).second) { + if (e == obj) { + return false; + } + todo.push_front(e->parent); + todo.insert(todo.begin(), e->hrefList.begin(), e->hrefList.end()); + } + } + return true; +} + + + +void URIReference::attach(const URI &uri) +{ + SPDocument *document = NULL; + + // Attempt to get the document that contains the URI + if (_owner) { + document = _owner->document; + } else if (_owner_document) { + document = _owner_document; + } + + // createChildDoc() assumes that the referenced file is an SVG. + // PNG and JPG files are allowed (in the case of feImage). + gchar *filename = uri.toString(); + bool skip = false; + if (g_str_has_suffix(filename, ".jpg") || g_str_has_suffix(filename, ".JPG") || + g_str_has_suffix(filename, ".png") || g_str_has_suffix(filename, ".PNG")) { + skip = true; + } + + // The path contains references to separate document files to load. + if (document && uri.getPath() && !skip) { + std::string base = document->getBase() ? document->getBase() : ""; + std::string path = uri.getFullPath(base); + if (!path.empty()) { + document = document->createChildDoc(path); + } else { + document = NULL; + } + } + if (!document) { + g_warning("Can't get document for referenced URI: %s", filename); + g_free(filename); + return; + } + g_free(filename); + + gchar const *fragment = uri.getFragment(); + if (!uri.isRelative() || uri.getQuery() || !fragment) { + throw UnsupportedURIException(); + } + + /* FIXME !!! real xpointer support should be delegated to document */ + /* for now this handles the minimal xpointer form that SVG 1.0 + * requires of us + */ + gchar *id = NULL; + if (!strncmp(fragment, "xpointer(", 9)) { + /* FIXME !!! this is wasteful */ + /* FIXME: It looks as though this is including "))" in the id. I suggest moving + the strlen calculation and validity testing to before strdup, and copying just + the id without the "))". -- pjrm */ + if (!strncmp(fragment, "xpointer(id(", 12)) { + id = g_strdup(fragment + 12); + size_t const len = strlen(id); + if (len < 3 || strcmp(id + len - 2, "))")) { + g_free(id); + throw MalformedURIException(); + } + } else { + throw UnsupportedURIException(); + } + } else { + id = g_strdup(fragment); + } + + /* FIXME !!! validate id as an NCName somewhere */ + + _connection.disconnect(); + delete _uri; + _uri = new URI(uri); + + _setObject(document->getObjectById(id)); + _connection = document->connectIdChanged(id, sigc::mem_fun(*this, &URIReference::_setObject)); + + g_free(id); +} + +void URIReference::detach() +{ + _connection.disconnect(); + delete _uri; + _uri = NULL; + _setObject(NULL); +} + +void URIReference::_setObject(SPObject *obj) +{ + if (obj && !_acceptObject(obj)) { + obj = NULL; + } + + if (obj == _obj) + return; + + SPObject *old_obj = _obj; + _obj = obj; + + _release_connection.disconnect(); + if (_obj) { + sp_object_href(_obj, _owner); + _release_connection = _obj->connectRelease(sigc::mem_fun(*this, &URIReference::_release)); + } + _changed_signal.emit(old_obj, _obj); + if (old_obj) { + /* release the old object _after_ the signal emission */ + sp_object_hunref(old_obj, _owner); + } +} + +/* If an object is deleted, current semantics require that we release + * it on its "release" signal, rather than later, when its ID is actually + * unregistered from the document. + */ +void URIReference::_release(SPObject *obj) +{ + g_assert(_obj == obj); + _setObject(NULL); +} + +} /* namespace Inkscape */ + + + +SPObject *sp_css_uri_reference_resolve(SPDocument *document, const gchar *uri) +{ + SPObject *ref = NULL; + + if (document && uri && (strncmp(uri, "url(", 4) == 0)) { + gchar *trimmed = extract_uri(uri); + if (trimmed) { + ref = sp_uri_reference_resolve(document, trimmed); + g_free(trimmed); + } + } + + return ref; +} + +SPObject *sp_uri_reference_resolve(SPDocument *document, const gchar *uri) +{ + SPObject *ref = NULL; + + if (uri && (*uri == '#')) { + ref = document->getObjectById(uri + 1); + } + + return ref; +} + +/* + Local Variables: + mode:c++ + c-file-style:"stroustrup" + c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) + indent-tabs-mode:nil + fill-column:99 + End: +*/ +// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 : |
