summaryrefslogtreecommitdiffstats
path: root/src/xml/rebase-hrefs.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:50:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 11:50:49 +0000
commitc853ffb5b2f75f5a889ed2e3ef89b818a736e87a (patch)
tree7d13a0883bb7936b84d6ecdd7bc332b41ed04bee /src/xml/rebase-hrefs.cpp
parentInitial commit. (diff)
downloadinkscape-c853ffb5b2f75f5a889ed2e3ef89b818a736e87a.tar.xz
inkscape-c853ffb5b2f75f5a889ed2e3ef89b818a736e87a.zip
Adding upstream version 1.3+ds.upstream/1.3+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/xml/rebase-hrefs.cpp')
-rw-r--r--src/xml/rebase-hrefs.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp
new file mode 100644
index 0000000..b74e9e6
--- /dev/null
+++ b/src/xml/rebase-hrefs.cpp
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/** @file
+ * TODO: insert short description here
+ *//*
+ * Authors: see git history
+ *
+ * Copyright (C) 2018 Authors
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include <glibmm/convert.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/uriutils.h>
+#include <glibmm/utility.h>
+
+#include "../document.h" /* Unfortunately there's a separate xml/document.h. */
+#include "streq.h"
+
+#include "io/dir-util.h"
+#include "io/sys.h"
+
+#include "object/sp-object.h"
+#include "object/uri.h"
+
+#include "xml/node.h"
+#include "xml/repr.h"
+#include "xml/rebase-hrefs.h"
+#include "xml/href-attribute-helper.h"
+
+using Inkscape::XML::AttributeRecord;
+using Inkscape::XML::AttributeVector;
+
+/**
+ * Determine if a href needs rebasing.
+ */
+static bool href_needs_rebasing(char const *href)
+{
+ // RFC 3986 defines empty string relative URL as referring to the
+ // containing document, rather than referring to the base URI.
+ if (!href[0] || href[0] == '#') {
+ return false;
+ }
+
+ // skip document-local queries
+ if (href[0] == '?') {
+ return false;
+ }
+
+ // skip absolute-path and network-path references
+ if (href[0] == '/') {
+ return false;
+ }
+
+ // Don't change non-file URIs (like data or http)
+ auto scheme = Glib::make_unique_ptr_gfree(g_uri_parse_scheme(href));
+ return !scheme || g_str_equal(scheme.get(), "file");
+}
+
+AttributeVector
+Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base,
+ gchar const *const new_abs_base,
+ const AttributeVector &attributes)
+{
+ using Inkscape::Util::share_string;
+
+ auto ret = attributes; // copy
+
+ if (old_abs_base == new_abs_base) {
+ return ret;
+ }
+
+ static GQuark const href_key = g_quark_from_static_string("href");
+ static GQuark const xlink_href_key = g_quark_from_static_string("xlink:href");
+ static GQuark const absref_key = g_quark_from_static_string("sodipodi:absref");
+
+ auto const find_record = [&ret](GQuark const key) {
+ return find_if(ret.begin(), ret.end(), [key](auto const &attr) { return attr.key == key; });
+ };
+
+ auto href_it = find_record(href_key);
+ if (href_it == ret.end()) {
+ href_it = find_record(xlink_href_key);
+ }
+ if (href_it == ret.end() || !href_needs_rebasing(href_it->value.pointer())) {
+ return ret;
+ }
+
+ auto uri = URI::from_href_and_basedir(href_it->value.pointer(), old_abs_base);
+ auto abs_href = uri.toNativeFilename();
+
+ auto absref_it = find_record(absref_key);
+ if (absref_it != ret.end()) {
+ if (g_file_test(abs_href.c_str(), G_FILE_TEST_EXISTS)) {
+ if (!streq(abs_href.c_str(), absref_it->value.pointer())) {
+ absref_it->value = share_string(abs_href.c_str());
+ }
+ } else if (g_file_test(absref_it->value.pointer(), G_FILE_TEST_EXISTS)) {
+ uri = URI::from_native_filename(absref_it->value.pointer());
+ }
+ }
+
+ std::string baseuri;
+ if (new_abs_base && new_abs_base[0]) {
+ baseuri = URI::from_dirname(new_abs_base).str();
+ }
+
+ auto new_href = uri.str(baseuri.c_str());
+ href_it->value = share_string(new_href.c_str());
+
+ return ret;
+}
+
+static void rebase_image_href(Inkscape::XML::Node *ir, std::string const &old_base_url_str, std::string const &new_base_url_str, bool const spns) {
+
+ using Inkscape::URI;
+
+ auto [href_key, href_cstr] = Inkscape::getHrefAttribute(*ir);
+ if (!href_cstr) {
+ return;
+ }
+
+ if (!href_needs_rebasing(href_cstr)) {
+ return;
+ }
+
+ // make absolute
+ URI url;
+ try {
+ url = URI(href_cstr, old_base_url_str.c_str());
+ } catch (...) {
+ return;
+ }
+
+ // skip non-file URLs
+ if (!url.hasScheme("file")) {
+ return;
+ }
+
+ // if path doesn't exist, use sodipodi:absref
+ if (!g_file_test(url.toNativeFilename().c_str(), G_FILE_TEST_EXISTS)) {
+ auto spabsref = ir->attribute("sodipodi:absref");
+ if (spabsref && g_file_test(spabsref, G_FILE_TEST_EXISTS)) {
+ url = URI::from_native_filename(spabsref);
+ }
+ } else if (spns) {
+ ir->setAttributeOrRemoveIfEmpty("sodipodi:absref", url.toNativeFilename());
+ }
+
+ if (!spns) {
+ ir->removeAttribute("sodipodi:absref");
+ }
+
+ auto href_str = url.str(new_base_url_str.c_str());
+ href_str = Inkscape::uri_to_iri(href_str.c_str());
+
+ ir->setAttribute(href_key, href_str);
+}
+
+void Inkscape::XML::rebase_hrefs(Inkscape::XML::Node *rootxml, gchar const *const old_base, gchar const *const new_base, bool const spns)
+{
+ using Inkscape::URI;
+
+ std::string old_base_url_str = URI::from_dirname(old_base).str();
+ std::string new_base_url_str;
+
+ if (new_base) {
+ new_base_url_str = URI::from_dirname(new_base).str();
+ }
+ sp_repr_visit_descendants(rootxml, [&](Inkscape::XML::Node *ir) {
+ if (!strcmp("svg:image", ir->name())) {
+ rebase_image_href(ir, old_base_url_str, new_base_url_str, spns);
+ }
+ return true;
+ });
+}
+
+void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_base, bool const spns)
+{
+ rebase_hrefs(doc->getReprRoot(), doc->getDocumentBase(), new_base, spns);
+ doc->setDocumentBase(new_base);
+}
+
+
+/*
+ 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:
+*/
+// vi: set autoindent shiftwidth=4 tabstop=8 filetype=cpp expandtab softtabstop=4 fileencoding=utf-8 textwidth=99 :