diff options
| author | Jon A. Cruz <jon@joncruz.org> | 2011-05-06 06:21:51 +0000 |
|---|---|---|
| committer | Jon A. Cruz <jon@joncruz.org> | 2011-05-06 06:21:51 +0000 |
| commit | a4d0a358424440128cd4c4fb2915ccc4b86f4587 (patch) | |
| tree | 818fc169950e0076d262bd8e633976afaafd7783 | |
| parent | symbol rendering fix bug:705345 (diff) | |
| download | inkscape-a4d0a358424440128cd4c4fb2915ccc4b86f4587.tar.gz inkscape-a4d0a358424440128cd4c4fb2915ccc4b86f4587.zip | |
Adding initial cut of resource manager.
(bzr r10198)
| -rw-r--r-- | src/Makefile_insert | 1 | ||||
| -rw-r--r-- | src/desktop.cpp | 7 | ||||
| -rw-r--r-- | src/desktop.h | 1 | ||||
| -rw-r--r-- | src/dir-util.cpp | 218 | ||||
| -rw-r--r-- | src/dir-util.h | 37 | ||||
| -rw-r--r-- | src/display/curve-test.h | 2 | ||||
| -rw-r--r-- | src/document-undo.h | 6 | ||||
| -rw-r--r-- | src/file.cpp | 28 | ||||
| -rw-r--r-- | src/inkscape.cpp | 2 | ||||
| -rw-r--r-- | src/resource-manager.cpp | 272 | ||||
| -rw-r--r-- | src/resource-manager.h | 49 | ||||
| -rw-r--r-- | src/sp-image.cpp | 71 | ||||
| -rw-r--r-- | src/ui/view/edit-widget-interface.h | 4 | ||||
| -rw-r--r-- | src/widgets/desktop-widget.cpp | 19 | ||||
| -rw-r--r-- | src/widgets/desktop-widget.h | 7 | ||||
| -rw-r--r-- | src/xml/Makefile_insert | 1 | ||||
| -rw-r--r-- | src/xml/rebase-hrefs-test.h | 126 | ||||
| -rw-r--r-- | src/xml/rebase-hrefs.cpp | 231 | ||||
| -rw-r--r-- | src/xml/rebase-hrefs.h | 27 | ||||
| -rw-r--r-- | src/xml/repr-action-test.h | 1 | ||||
| -rw-r--r-- | src/xml/repr-io.cpp | 47 | ||||
| -rw-r--r-- | src/xml/repr.h | 3 |
22 files changed, 840 insertions, 320 deletions
diff --git a/src/Makefile_insert b/src/Makefile_insert index 36c9de34f..3a3862437 100644 --- a/src/Makefile_insert +++ b/src/Makefile_insert @@ -123,6 +123,7 @@ ink_common_sources += \ removeoverlap.cpp removeoverlap.h \ rdf.cpp rdf.h \ rect-context.cpp rect-context.h \ + resource-manager.cpp resource-manager.h \ require-config.h \ round.h \ rubberband.cpp rubberband.h \ diff --git a/src/desktop.cpp b/src/desktop.cpp index a6224a71c..361ed7fea 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -87,6 +87,7 @@ #include "device-manager.h" #include "layer-fns.h" #include "layer-manager.h" +#include "resource-manager.h" #include "event-log.h" #include "display/canvas-grid.h" #include "widgets/desktop-widget.h" @@ -177,6 +178,7 @@ SPDesktop::init (SPNamedView *nv, SPCanvas *aCanvas, Inkscape::UI::View::EditWid // Temporary workaround for link order issues: Inkscape::DeviceManager::getManager().getDevices(); + Inkscape::ResourceManager::getManager(); Inkscape::Preferences *prefs = Inkscape::Preferences::get(); _guides_message_context = new Inkscape::MessageContext(const_cast<Inkscape::MessageStack*>(messageStack())); @@ -1320,6 +1322,11 @@ SPDesktop::presentWindow() _widget->present(); } +bool SPDesktop::showInfoDialog( Glib::ustring const & message ) +{ + return _widget->showInfoDialog( message ); +} + bool SPDesktop::warnDialog (gchar *text) { diff --git a/src/desktop.h b/src/desktop.h index 947e92fe7..6d1bcd194 100644 --- a/src/desktop.h +++ b/src/desktop.h @@ -288,6 +288,7 @@ public: void setWindowTransient (void* p, int transient_policy=1); Gtk::Window* getToplevel(); void presentWindow(); + bool showInfoDialog( Glib::ustring const &message ); bool warnDialog (gchar *text); void toggleRulers(); void toggleScrollbars(); diff --git a/src/dir-util.cpp b/src/dir-util.cpp index 67db03628..acec39953 100644 --- a/src/dir-util.cpp +++ b/src/dir-util.cpp @@ -1,9 +1,8 @@ -/** @file - * @brief Utility functions for filenames +/** + * @file + * Utility functions for filenames. */ -#define DIR_UTIL_C - #include <errno.h> #include <string> #include <cstring> @@ -13,49 +12,37 @@ #include <glib/gconvert.h> #include <glib/gstrfuncs.h> -/** Returns a form of \a path relative to \a base if that is easy to construct (e.g. if \a path - appears to be in the directory specified by \a base), otherwise returns \a path. - - N.B. The return value is a pointer into the \a path string. - - \a base is expected to be either NULL or the absolute path of a directory. - - \a path is expected to be an absolute path. - - \see inkscape_abs2rel for a more sophisticated version. - \see prepend_current_dir_if_relative. -*/ -char const * -sp_relative_path_from_path(char const *const path, char const *const base) +std::string sp_relative_path_from_path( std::string const &path, std::string const &base) { - if (base == NULL || path == NULL) { - return path; - } + std::string result; + if ( !base.empty() && !path.empty() ) { + size_t base_len = base.length(); + while (base_len != 0 + && (base[base_len - 1] == G_DIR_SEPARATOR)) + { + --base_len; + } - size_t base_len = strlen(base); - while (base_len != 0 - && (base[base_len - 1] == G_DIR_SEPARATOR)) - { - --base_len; - } + if ( (path.substr(0, base_len) == base.substr(0, base_len)) + && (path[base_len] == G_DIR_SEPARATOR)) + { + size_t retPos = base_len + 1; + while ( (retPos < path.length()) && (path[retPos] == G_DIR_SEPARATOR) ) { + retPos++; + } + if ( (retPos + 1) < path.length() ) { + result = path.substr(retPos); + } + } - if ((memcmp(path, base, base_len) == 0) - && (path[base_len] == G_DIR_SEPARATOR)) - { - char const *ret = path + base_len + 1; - while (*ret == G_DIR_SEPARATOR) { - ++ret; - } - if (*ret != '\0') { - return ret; - } - } - - return path; + } + if ( result.empty() ) { + result = path; + } + return result; } -char const * -sp_extension_from_path(char const *const path) +char const *sp_extension_from_path(char const *const path) { if (path == NULL) { return NULL; @@ -77,25 +64,7 @@ static char const dots[] = {'.', '.', G_DIR_SEPARATOR, '\0'}; static char const *const parent = dots; static char const *const current = dots + 1; -/** - * \brief Convert a relative path name into absolute. If path is already absolute, does nothing except copying path to result. - * - * \param path relative path - * \param base base directory (must be absolute path) - * \param result result buffer - * \param size size of result buffer - * \return != NULL: absolute path - * == NULL: error - -\comment - based on functions by Shigio Yamaguchi. - FIXME:TODO: force it to also do path normalization of the entire resulting path, - i.e. get rid of any .. and . in any place, even if 'path' is already absolute - (now it returns it unchanged in this case) - - */ -char * -inkscape_rel2abs (const char *path, const char *base, char *result, const size_t size) +char *inkscape_rel2abs(const char *path, const char *base, char *result, const size_t size) { const char *pp, *bp; /* endp points the last position which is safe in the result buffer. */ @@ -181,79 +150,77 @@ erange: return (NULL); } -char * -inkscape_abs2rel (const char *path, const char *base, char *result, const size_t size) +char *inkscape_abs2rel(const char *path, const char *base, char *result, const size_t size) { - const char *pp, *bp, *branch; - /* endp points the last position which is safe in the result buffer. */ - const char *endp = result + size - 1; - char *rp; + const char *pp, *bp, *branch; + // endp points the last position which is safe in the result buffer. + const char *endp = result + size - 1; + char *rp; - if (*path != G_DIR_SEPARATOR) + if (*path != G_DIR_SEPARATOR) { - if (strlen (path) >= size) - goto erange; - strcpy (result, path); - goto finish; + if (strlen (path) >= size) + goto erange; + strcpy (result, path); + goto finish; } - else if (*base != G_DIR_SEPARATOR || !size) + else if (*base != G_DIR_SEPARATOR || !size) { - errno = EINVAL; - return (NULL); + errno = EINVAL; + return (NULL); } - else if (size == 1) - goto erange; - /* seek to branched point. */ - branch = path; - for (pp = path, bp = base; *pp && *bp && *pp == *bp; pp++, bp++) - if (*pp == G_DIR_SEPARATOR) - branch = pp; - if (((*pp == 0) || ((*pp == G_DIR_SEPARATOR) && (*(pp + 1) == 0))) && - ((*bp == 0) || ((*bp == G_DIR_SEPARATOR) && (*(bp + 1) == 0)))) + else if (size == 1) + goto erange; + /* seek to branched point. */ + branch = path; + for (pp = path, bp = base; *pp && *bp && *pp == *bp; pp++, bp++) + if (*pp == G_DIR_SEPARATOR) + branch = pp; + if (((*pp == 0) || ((*pp == G_DIR_SEPARATOR) && (*(pp + 1) == 0))) && + ((*bp == 0) || ((*bp == G_DIR_SEPARATOR) && (*(bp + 1) == 0)))) { - rp = result; - *rp++ = '.'; - if (*pp == G_DIR_SEPARATOR || *(pp - 1) == G_DIR_SEPARATOR) - *rp++ = G_DIR_SEPARATOR; - if (rp > endp) - goto erange; - *rp = 0; - goto finish; + rp = result; + *rp++ = '.'; + if (*pp == G_DIR_SEPARATOR || *(pp - 1) == G_DIR_SEPARATOR) + *rp++ = G_DIR_SEPARATOR; + if (rp > endp) + goto erange; + *rp = 0; + goto finish; } - if (((*pp == 0) && (*bp == G_DIR_SEPARATOR)) || ((*pp == G_DIR_SEPARATOR) && (*bp == 0))) - branch = pp; - /* up to root. */ - rp = result; - for (bp = base + (branch - path); *bp; bp++) - if (*bp == G_DIR_SEPARATOR && *(bp + 1) != 0) - { - if (rp + 3 > endp) - goto erange; - *rp++ = '.'; - *rp++ = '.'; - *rp++ = G_DIR_SEPARATOR; - } - if (rp > endp) - goto erange; - *rp = 0; - /* down to leaf. */ - if (*branch) + if (((*pp == 0) && (*bp == G_DIR_SEPARATOR)) || ((*pp == G_DIR_SEPARATOR) && (*bp == 0))) + branch = pp; + /* up to root. */ + rp = result; + for (bp = base + (branch - path); *bp; bp++) + if (*bp == G_DIR_SEPARATOR && *(bp + 1) != 0) + { + if (rp + 3 > endp) + goto erange; + *rp++ = '.'; + *rp++ = '.'; + *rp++ = G_DIR_SEPARATOR; + } + if (rp > endp) + goto erange; + *rp = 0; + /* down to leaf. */ + if (*branch) { - if (rp + strlen (branch + 1) > endp) - goto erange; - strcpy (rp, branch + 1); + if (rp + strlen (branch + 1) > endp) + goto erange; + strcpy (rp, branch + 1); } - else - *--rp = 0; + else + *--rp = 0; finish: - return result; + return result; erange: - errno = ERANGE; - return (NULL); + errno = ERANGE; + return (NULL); } -gchar * -prepend_current_dir_if_relative(gchar const *uri) +gchar *prepend_current_dir_if_relative(gchar const *uri) { if (!uri) { return NULL; @@ -278,4 +245,13 @@ prepend_current_dir_if_relative(gchar const *uri) return ret; } - +/* + 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 encoding=utf-8 textwidth=99 : diff --git a/src/dir-util.h b/src/dir-util.h index 7d04b3007..f7700cfa3 100644 --- a/src/dir-util.h +++ b/src/dir-util.h @@ -12,14 +12,47 @@ #include <stdlib.h> #include <glib/gtypes.h> -char const *sp_relative_path_from_path(char const *path, char const *base); +/** + * Returns a form of \a path relative to \a base if that is easy to construct (eg if \a path + * appears to be in the directory specified by \a base), otherwise returns \a path. + * + * @param path is expected to be an absolute path. + * @param base is expected to be either empty or the absolute path of a directory. + * + * @return a relative version of the path, if reasonable. + * + * @see inkscape_abs2rel for a more sophisticated version. + * @see prepend_current_dir_if_relative. +*/ +std::string sp_relative_path_from_path(std::string const &path, std::string const &base); + char const *sp_extension_from_path(char const *path); + +/** + * Convert a relative path name into absolute. If path is already absolute, does nothing except copying path to result. + * + * @param path relative path. + * @param base base directory (must be absolute path). + * @param result result buffer. + * @param size size of result buffer. + * + * @return != NULL: absolute path + * == NULL: error + * + * based on functions by Shigio Yamaguchi. + * FIXME:TODO: force it to also do path normalization of the entire resulting path, + * i.e. get rid of any .. and . in any place, even if 'path' is already absolute + * (now it returns it unchanged in this case) + * + */ char *inkscape_rel2abs(char const *path, char const *base, char *result, size_t const size); + char *inkscape_abs2rel(char const *path, char const *base, char *result, size_t const size); + gchar *prepend_current_dir_if_relative(gchar const *filename); -#endif /* !SEEN_DIR_UTIL_H */ +#endif // !SEEN_DIR_UTIL_H /* Local Variables: diff --git a/src/display/curve-test.h b/src/display/curve-test.h index d89cb4c99..3d698ca07 100644 --- a/src/display/curve-test.h +++ b/src/display/curve-test.h @@ -21,7 +21,7 @@ public: path1.close(); // Closed path (ClosingSegment is zero length) path2.append(Geom::LineSegment(Geom::Point(2,0),Geom::Point(3,0))); - path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0))); + // TODO fix path2.append(Geom::BezierCurve<3>(Geom::Point(3,0),Geom::Point(2,1),Geom::Point(1,1),Geom::Point(2,0))); path2.close(); // Open path path3.append(Geom::SVGEllipticalArc(Geom::Point(4,0),1,2,M_PI,false,false,Geom::Point(5,1))); diff --git a/src/document-undo.h b/src/document-undo.h index 9be260fa2..e4f0d15a8 100644 --- a/src/document-undo.h +++ b/src/document-undo.h @@ -15,10 +15,10 @@ public: * Since undo sensitivity needs to be nested, setting undo sensitivity * should be done like this: *\verbatim - bool saved = sp_document_get_undo_sensitive(document); - sp_document_set_undo_sensitive(document, false); + bool saved = DocumentUndo::getUndoSensitive(document); + DocumentUndo::setUndoSensitive(document, false); ... do stuff ... - sp_document_set_undo_sensitive(document, saved); \endverbatim + DocumentUndo::setUndoSensitive(document, saved); \endverbatim */ static void setUndoSensitive(SPDocument *doc, bool sensitive); diff --git a/src/file.cpp b/src/file.cpp index c93188358..a1fc23117 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -54,6 +54,7 @@ #include "path-prefix.h" #include "preferences.h" #include "print.h" +#include "resource-manager.h" #include "rdf.h" #include "selection-chemistry.h" #include "selection.h" @@ -209,14 +210,14 @@ sp_file_exit() * \param replace_empty if true, and the current desktop is empty, this document * will replace the empty one. */ -bool -sp_file_open(const Glib::ustring &uri, - Inkscape::Extension::Extension *key, - bool add_to_recent, bool replace_empty) +bool sp_file_open(const Glib::ustring &uri, + Inkscape::Extension::Extension *key, + bool add_to_recent, bool replace_empty) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; - if (desktop) + if (desktop) { desktop->setWaitingCursor(); + } SPDocument *doc = NULL; try { @@ -227,27 +228,30 @@ sp_file_open(const Glib::ustring &uri, doc = NULL; } - if (desktop) + if (desktop) { desktop->clearWaitingCursor(); + } if (doc) { SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL; if (existing && existing->virgin && replace_empty) { // If the current desktop is empty, open the document there - doc->ensureUpToDate(); + doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc. desktop->change_document(doc); doc->emitResizedSignal(doc->getWidth(), doc->getHeight()); } else { // create a whole new desktop and window - SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); + SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc. sp_create_window(dtw, TRUE); desktop = static_cast<SPDesktop*>(dtw->view); } doc->virgin = FALSE; + // everyone who cares now has a reference, get rid of ours doc->doUnref(); + // resize the window to match the document properties sp_namedview_window_from_document(desktop); sp_namedview_update_layers_from_document(desktop); @@ -256,6 +260,14 @@ sp_file_open(const Glib::ustring &uri, sp_file_add_recent( doc->getURI() ); } + if ( inkscape_use_gui() ) { + // Perform a fixup pass for hrefs. + if ( Inkscape::ResourceManager::getManager().fixupBrokenLinks(doc) ) { + Glib::ustring msg = _("Broken links have been changed to point to existing files."); + desktop->showInfoDialog(msg); + } + } + return TRUE; } else { gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); diff --git a/src/inkscape.cpp b/src/inkscape.cpp index 1007c315a..91e3b0c5f 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -59,6 +59,7 @@ using Inkscape::Extension::Internal::PrintWin32; #include "io/sys.h" #include "message-stack.h" #include "preferences.h" +#include "resource-manager.h" #include "selection.h" #include "ui/dialog/debug.h" #include "xml/repr.h" @@ -820,6 +821,7 @@ inkscape_application_init (const gchar *argv0, gboolean use_gui) inkscape_load_menus(inkscape); Inkscape::DeviceManager::getManager().loadConfig(); } + Inkscape::ResourceManager::getManager(); /* set language for user interface according setting in preferences */ Glib::ustring ui_language = prefs->getString("/ui/language"); diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp new file mode 100644 index 000000000..a68b2c7ae --- /dev/null +++ b/src/resource-manager.cpp @@ -0,0 +1,272 @@ +/* + * Inkscape::ResourceManager - tracks external resources such as image and css files. + * + * Copyright 2011 Jon A. Cruz <jon@joncruz.org> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include <string> +#include <vector> +#include <glibmm/i18n.h> +#include <glibmm/convert.h> +#include <glibmm/fileutils.h> +#include <glibmm/miscutils.h> +#include <glibmm/uriutils.h> + +#include "resource-manager.h" + +#include "document.h" +#include "sp-object.h" +#include "xml/node.h" +#include "document-undo.h" + +namespace Inkscape { + + + +class ResourceManagerImpl : public ResourceManager { +public: + ResourceManagerImpl(); + virtual ~ResourceManagerImpl(); + + virtual bool fixupBrokenLinks(SPDocument *doc); + + + /** + * Walk all links in a document and create a listing of unique broken links. + * + * @return a list of all broken links. + */ + std::vector<Glib::ustring> findBrokenLinks(SPDocument *doc); + + /** + * Resolve broken links as a whole and return a map for those that can be found. + * + * Note: this will allow for future enhancements including relinking to new locations + * with the most broken files found, etc. + * + * @return a map of found links. + */ + std::map<Glib::ustring, Glib::ustring> locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks); + + bool extractFilepath( Glib::ustring const &href, std::string &uri ); + +protected: +}; + + +ResourceManagerImpl::ResourceManagerImpl() + : ResourceManager() +{ +} + +ResourceManagerImpl::~ResourceManagerImpl() +{ +} + +bool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::string &uri ) +{ + bool isFile = false; + + uri.clear(); + + std::string scheme = Glib::uri_parse_scheme(href); + if ( !scheme.empty() ) { + // TODO debug g_message("Scheme is now [%s]", scheme.c_str()); + if ( scheme == "file" ) { + // TODO debug g_message("--- is a file URI [%s]", href.c_str()); + + // throws Glib::ConvertError: + uri = Glib::filename_from_uri(href); // TODO see if we can get this to throw + // TODO debug g_message(" [%s]", uri.c_str()); + isFile = true; + } + } else { + // No scheme. Assuming it is a file path (absolute or relative). + // throws Glib::ConvertError: + uri = Glib::filename_from_utf8( href ); + isFile = true; + } + + return isFile; +} + + +std::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc ) +{ + std::vector<Glib::ustring> result; + std::set<Glib::ustring> uniques; + + if ( doc ) { + GSList const *images = doc->getResourceList("image"); + for (GSList const *it = images; it; it = it->next) { + Inkscape::XML::Node *ir = static_cast<SPObject *>(it->data)->getRepr(); + + gchar const *href = ir->attribute("xlink:href"); + if ( href && ( uniques.find(href) == uniques.end() ) ) { + std::string uri; + if ( extractFilepath( href, uri ) ) { + if ( Glib::path_is_absolute(uri) ) { + if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { + result.push_back(href); + uniques.insert(href); + } + } else { + std::string combined = Glib::build_filename(doc->getBase(), uri); + if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { + result.push_back(href); + uniques.insert(href); + } + } + } + } + } + } + + return result; +} + + +std::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks) +{ + std::map<Glib::ustring, Glib::ustring> result; + + // At the moment we expect this list to contain file:// references, or simple relative or absolute paths. + for ( std::vector<Glib::ustring>::const_iterator it = brokenLinks.begin(); it != brokenLinks.end(); ++it ) { + // TODO debug g_message("========{%s}", it->c_str()); + + std::string uri; + if ( extractFilepath( *it, uri ) ) { + // We were able to get some path. Check it + + if ( !Glib::path_is_absolute(uri) ) { + uri = Glib::build_filename(docbase, uri); + // TODO debug g_message(" not absolute. Fixing up as [%s]", uri.c_str()); + } + + if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) { + // TODO debug g_message(" DOES NOT EXIST."); + std::string tmp = uri; + std::string prior; + std::string remainder; + bool exists = false; + while ( (tmp != prior) && !exists) { + prior = tmp; + std::string basename = Glib::path_get_basename(tmp); + tmp = Glib::path_get_dirname(tmp); + if ( remainder.empty() ) { + remainder = basename; + } else { + remainder = Glib::build_filename(basename, remainder); + } + + std::string rebuild = Glib::build_filename(docbase, remainder); + exists = Glib::file_test(rebuild, Glib::FILE_TEST_EXISTS); + + // TODO debug g_message(" [%s] [%s]%s", tmp.c_str(), remainder.c_str(), exists ? " XXXX" : ""); + if ( exists ) { + Glib::ustring replacement = Glib::filename_to_utf8( remainder ); + result[*it] = replacement; + } + } + } + } + } + + return result; +} + +bool ResourceManagerImpl::fixupBrokenLinks(SPDocument *doc) +{ + bool changed = false; + if ( doc ) { + // TODO debug g_message("FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP"); + // TODO debug g_message(" base is [%s]", doc->getBase()); + + std::vector<Glib::ustring> brokenHrefs = findBrokenLinks(doc); + if ( !brokenHrefs.empty() ) { + // TODO debug g_message(" FOUND SOME LINKS %d", brokenHrefs.size()); + for ( std::vector<Glib::ustring>::iterator it = brokenHrefs.begin(); it != brokenHrefs.end(); ++it ) { + // TODO debug g_message(" [%s]", it->c_str()); + } + } + + std::map<Glib::ustring, Glib::ustring> mapping = locateLinks(doc->getBase(), brokenHrefs); + for ( std::map<Glib::ustring, Glib::ustring>::iterator it = mapping.begin(); it != mapping.end(); ++it ) + { + // TODO debug g_message(" [%s] ==> {%s}", it->first.c_str(), it->second.c_str()); + } + + bool savedUndoState = DocumentUndo::getUndoSensitive(doc); + DocumentUndo::setUndoSensitive(doc, true); + + GSList const *images = doc->getResourceList("image"); + for (GSList const *it = images; it; it = it->next) { + Inkscape::XML::Node *ir = static_cast<SPObject *>(it->data)->getRepr(); + + gchar const *href = ir->attribute("xlink:href"); + if ( href ) { + // TODO debug g_message(" consider [%s]", href); + + if ( mapping.find(href) != mapping.end() ) { + // TODO debug g_message(" Found a replacement"); + + ir->setAttribute( "xlink:href", mapping[href].c_str() ); + if ( ir->attribute( "sodipodi:absref" ) ) { + ir->setAttribute( "sodipodi:absref", 0 ); // Remove this attribute + } + + SPObject *updated = doc->getObjectByRepr(ir); + if (updated) { + // force immediate update of dependant attributes + updated->updateRepr(); + } + + changed = true; + } + } + } + if ( changed ) { + DocumentUndo::done( doc, SP_VERB_DIALOG_XML_EDITOR, _("Fixup broken links") ); + } + DocumentUndo::setUndoSensitive(doc, savedUndoState); + } + + return changed; +} + + + + +static ResourceManagerImpl* theInstance = 0; + +ResourceManager::ResourceManager() + : Glib::Object() +{ +} + +ResourceManager::~ResourceManager() { +} + +ResourceManager& ResourceManager::getManager() { + if ( !theInstance ) { + theInstance = new ResourceManagerImpl(); + } + + return *theInstance; +} + + +} // namespace Inkscape + +/* + 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 : diff --git a/src/resource-manager.h b/src/resource-manager.h new file mode 100644 index 000000000..8f01c23a0 --- /dev/null +++ b/src/resource-manager.h @@ -0,0 +1,49 @@ +/* + * Inkscape::ResourceManager - Manages external resources such as image and css files. + * + * Copyright 2011 Jon A. Cruz <jon@joncruz.org> + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_RESOURCE_MANAGER_H +#define SEEN_INKSCAPE_RESOURCE_MANAGER_H + +#include <glibmm/object.h> + +class SPDocument; + +namespace Inkscape { + +class ResourceManager : public Glib::Object { + +public: + static ResourceManager& getManager(); + + virtual bool fixupBrokenLinks(SPDocument *doc) = 0; + +protected: + ResourceManager(); + virtual ~ResourceManager(); + +private: + ResourceManager(ResourceManager const &); // no copy + void operator=(ResourceManager const &); // no assign +}; + + + +} // namespace Inkscape + +#endif // SEEN_INKSCAPE_RESOURCE_MANAGER_H + +/* + 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:textwidth=99 : diff --git a/src/sp-image.cpp b/src/sp-image.cpp index 8bd1bfadd..746cd97d9 100644 --- a/src/sp-image.cpp +++ b/src/sp-image.cpp @@ -5,6 +5,7 @@ * Lauris Kaplinski <lauris@kaplinski.com> * Edward Flick (EAF) * Abhishek Sharma + * Jon A. Cruz <jon@joncruz.org> * * Copyright (C) 1999-2005 Authors * Copyright (C) 2000-2001 Ximian, Inc. @@ -107,7 +108,6 @@ extern "C" void user_read_data( png_structp png_ptr, png_bytep data, png_size_t length ); void user_write_data( png_structp png_ptr, png_bytep data, png_size_t length ); void user_flush_data( png_structp png_ptr ); - } @@ -557,8 +557,7 @@ GdkPixbuf* pixbuf_new_from_file( const char *filename, GError **error ) } } -GType -sp_image_get_type (void) +GType sp_image_get_type(void) { static GType image_type = 0; if (!image_type) { @@ -579,8 +578,7 @@ sp_image_get_type (void) return image_type; } -static void -sp_image_class_init (SPImageClass * klass) +static void sp_image_class_init( SPImageClass * klass ) { GObjectClass * gobject_class; SPObjectClass * sp_object_class; @@ -635,8 +633,7 @@ static void sp_image_init( SPImage *image ) image->lastMod = 0; } -static void -sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) +static void sp_image_build( SPObject *object, SPDocument *document, Inkscape::XML::Node *repr ) { if (((SPObjectClass *) parent_class)->build) { ((SPObjectClass *) parent_class)->build (object, document, repr); @@ -654,8 +651,7 @@ sp_image_build (SPObject *object, SPDocument *document, Inkscape::XML::Node *rep document->addResource("image", object); } -static void -sp_image_release (SPObject *object) +static void sp_image_release( SPObject *object ) { SPImage *image = SP_IMAGE(object); @@ -695,8 +691,7 @@ sp_image_release (SPObject *object) } } -static void -sp_image_set (SPObject *object, unsigned int key, const gchar *value) +static void sp_image_set( SPObject *object, unsigned int key, const gchar *value ) { SPImage *image = SP_IMAGE (object); @@ -818,8 +813,7 @@ sp_image_set (SPObject *object, unsigned int key, const gchar *value) sp_image_set_curve(image); //creates a curve at the image's boundary for snapping } -static void -sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags) +static void sp_image_update( SPObject *object, SPCtx *ctx, unsigned int flags ) { SPImage *image = SP_IMAGE(object); SPDocument *doc = object->document; @@ -1026,8 +1020,7 @@ sp_image_update (SPObject *object, SPCtx *ctx, unsigned int flags) sp_image_update_canvas_image ((SPImage *) object); } -static void -sp_image_modified (SPObject *object, unsigned int flags) +static void sp_image_modified( SPObject *object, unsigned int flags ) { SPImage *image = SP_IMAGE (object); @@ -1042,8 +1035,7 @@ sp_image_modified (SPObject *object, unsigned int flags) } } -static Inkscape::XML::Node * -sp_image_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) +static Inkscape::XML::Node *sp_image_write( SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags ) { SPImage *image = SP_IMAGE (object); @@ -1081,8 +1073,7 @@ sp_image_write (SPObject *object, Inkscape::XML::Document *xml_doc, Inkscape::XM return repr; } -static void -sp_image_bbox(SPItem const *item, NRRect *bbox, Geom::Affine const &transform, unsigned const /*flags*/) +static void sp_image_bbox( SPItem const *item, NRRect *bbox, Geom::Affine const &transform, unsigned const /*flags*/ ) { SPImage const &image = *SP_IMAGE(item); @@ -1099,8 +1090,7 @@ sp_image_bbox(SPItem const *item, NRRect *bbox, Geom::Affine const &transform, u } } -static void -sp_image_print (SPItem *item, SPPrintContext *ctx) +static void sp_image_print( SPItem *item, SPPrintContext *ctx ) { SPImage *image = SP_IMAGE(item); @@ -1135,8 +1125,7 @@ sp_image_print (SPItem *item, SPPrintContext *ctx) } } -static gchar * -sp_image_description(SPItem *item) +static gchar *sp_image_description( SPItem *item ) { SPImage *image = SP_IMAGE(item); char *href_desc; @@ -1159,8 +1148,7 @@ sp_image_description(SPItem *item) return ret; } -static NRArenaItem * -sp_image_show (SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/) +static NRArenaItem *sp_image_show( SPItem *item, NRArena *arena, unsigned int /*key*/, unsigned int /*flags*/ ) { SPImage * image = SP_IMAGE(item); NRArenaItem *ai = NRArenaImage::create(arena); @@ -1286,8 +1274,7 @@ GdkPixbuf *sp_image_repr_read_image( time_t& modTime, char*& pixPath, const gcha return pixbuf; } -static GdkPixbuf * -sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf) +static GdkPixbuf *sp_image_pixbuf_force_rgba( GdkPixbuf * pixbuf ) { GdkPixbuf* result; if (gdk_pixbuf_get_has_alpha(pixbuf)) { @@ -1301,8 +1288,7 @@ sp_image_pixbuf_force_rgba (GdkPixbuf * pixbuf) /* We assert that realpixbuf is either NULL or identical size to pixbuf */ -static void -sp_image_update_canvas_image (SPImage *image) +static void sp_image_update_canvas_image( SPImage *image ) { SPItem *item = SP_ITEM(image); @@ -1342,7 +1328,7 @@ sp_image_update_canvas_image (SPImage *image) } } -static void sp_image_snappoints(SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/) +static void sp_image_snappoints( SPItem const *item, std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const */*snapprefs*/ ) { /* An image doesn't have any nodes to snap, but still we want to be able snap one image to another. Therefore we will create some snappoints at the corner, similar to a rect. If @@ -1376,8 +1362,7 @@ static void sp_image_snappoints(SPItem const *item, std::vector<Inkscape::SnapCa * Transform x, y, set x, y, clear translation */ -static Geom::Affine -sp_image_set_transform(SPItem *item, Geom::Affine const &xform) +static Geom::Affine sp_image_set_transform( SPItem *item, Geom::Affine const &xform ) { SPImage *image = SP_IMAGE(item); @@ -1417,8 +1402,7 @@ sp_image_set_transform(SPItem *item, Geom::Affine const &xform) return ret; } -static GdkPixbuf * -sp_image_repr_read_dataURI (const gchar * uri_data) +static GdkPixbuf *sp_image_repr_read_dataURI( const gchar * uri_data ) { GdkPixbuf * pixbuf = NULL; @@ -1474,8 +1458,7 @@ sp_image_repr_read_dataURI (const gchar * uri_data) return pixbuf; } -static GdkPixbuf * -sp_image_repr_read_b64 (const gchar * uri_data) +static GdkPixbuf *sp_image_repr_read_b64( const gchar * uri_data ) { GdkPixbuf * pixbuf = NULL; @@ -1551,8 +1534,7 @@ sp_image_repr_read_b64 (const gchar * uri_data) return pixbuf; } -static void -sp_image_set_curve(SPImage *image) +static void sp_image_set_curve( SPImage *image ) { //create a curve at the image's boundary for snapping if ((image->height.computed < MAGIC_EPSILON_TOO) || (image->width.computed < MAGIC_EPSILON_TOO) || (image->clip_ref->getObject())) { @@ -1580,8 +1562,7 @@ sp_image_set_curve(SPImage *image) /** * Return duplicate of curve (if any exists) or NULL if there is no curve */ -SPCurve * -sp_image_get_curve (SPImage *image) +SPCurve *sp_image_get_curve( SPImage *image ) { SPCurve *result = 0; if (image->curve) { @@ -1590,8 +1571,7 @@ sp_image_get_curve (SPImage *image) return result; } -void -sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring const &mime_in) +void sp_embed_image( Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring const &mime_in ) { Glib::ustring format, mime; if (mime_in == "image/jpeg") { @@ -1602,8 +1582,8 @@ sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring con format = "png"; } - gchar *data; - gsize length; + gchar *data = 0; + gsize length = 0; gdk_pixbuf_save_to_buffer(pb, &data, &length, format.data(), NULL, NULL); // Save base64 encoded data in image node @@ -1614,7 +1594,8 @@ sp_embed_image(Inkscape::XML::Node *image_node, GdkPixbuf *pb, Glib::ustring con gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer; buf_work += g_sprintf(buffer, "data:%s;base64,", mime.data()); - gint state = 0, save = 0; + gint state = 0; + gint save = 0; gsize written = 0; written += g_base64_encode_step((guchar*) data, length, TRUE, buf_work, &state, &save); written += g_base64_encode_close(TRUE, buf_work + written, &state, &save); diff --git a/src/ui/view/edit-widget-interface.h b/src/ui/view/edit-widget-interface.h index 919b570dd..4ff4f92f9 100644 --- a/src/ui/view/edit-widget-interface.h +++ b/src/ui/view/edit-widget-interface.h @@ -137,6 +137,10 @@ struct EditWidgetInterface /// Message widget will get no content virtual void setMessage (Inkscape::MessageType type, gchar const* msg) = 0; + + /** Show an info dialog with the given message */ + virtual bool showInfoDialog( Glib::ustring const &message ) = 0; + /// Open yes/no dialog with warning text and confirmation question. virtual bool warnDialog (gchar*) = 0; diff --git a/src/widgets/desktop-widget.cpp b/src/widgets/desktop-widget.cpp index 63fdc5930..87ce9053f 100644 --- a/src/widgets/desktop-widget.cpp +++ b/src/widgets/desktop-widget.cpp @@ -1150,6 +1150,25 @@ SPDesktopWidget::presentWindow() gtk_window_present (w); } +bool SPDesktopWidget::showInfoDialog( Glib::ustring const &message ) +{ + bool result = false; + GtkWindow *window = GTK_WINDOW( gtk_widget_get_toplevel( GTK_WIDGET(this) ) ); + if (window) + { + GtkWidget *dialog = gtk_message_dialog_new( + window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_INFO, + GTK_BUTTONS_OK, + "%s", message.c_str()); + gtk_window_set_title( GTK_WINDOW(dialog), _("Note:")); // probably want to take this as a parameter. + gint response = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + } + return result; +} + bool SPDesktopWidget::warnDialog (gchar* text) { diff --git a/src/widgets/desktop-widget.h b/src/widgets/desktop-widget.h index 0102897e5..53d9dd1bc 100644 --- a/src/widgets/desktop-widget.h +++ b/src/widgets/desktop-widget.h @@ -200,8 +200,14 @@ struct SPDesktopWidget { { _dtw->setCoordinateStatus (p); } virtual void setMessage (Inkscape::MessageType type, gchar const* msg) { _dtw->setMessage (type, msg); } + + virtual bool showInfoDialog( Glib::ustring const &message ) { + return _dtw->showInfoDialog( message ); + } + virtual bool warnDialog (gchar* text) { return _dtw->warnDialog (text); } + virtual Inkscape::UI::Widget::Dock* getDock () { return _dtw->getDock(); } }; @@ -218,6 +224,7 @@ struct SPDesktopWidget { void setWindowSize (gint w, gint h); void setWindowTransient (void *p, int transient_policy); void presentWindow(); + bool showInfoDialog( Glib::ustring const &message ); bool warnDialog (gchar *text); void setToolboxFocusTo (gchar const *); void setToolboxAdjustmentValue (gchar const * id, double value); diff --git a/src/xml/Makefile_insert b/src/xml/Makefile_insert index 7190b7948..b10f2448b 100644 --- a/src/xml/Makefile_insert +++ b/src/xml/Makefile_insert @@ -47,5 +47,6 @@ ink_common_sources += \ # ### CxxTest stuff #### # ###################### CXXTEST_TESTSUITES += \ + $(srcdir)/xml/rebase-hrefs-test.h \ $(srcdir)/xml/repr-action-test.h \ $(srcdir)/xml/quote-test.h diff --git a/src/xml/rebase-hrefs-test.h b/src/xml/rebase-hrefs-test.h new file mode 100644 index 000000000..e00337836 --- /dev/null +++ b/src/xml/rebase-hrefs-test.h @@ -0,0 +1,126 @@ +#include <cxxtest/TestSuite.h> + +#include <cstdlib> +#include <glib.h> + +#include "uri.h" + + +class RebaseHrefsTest : public CxxTest::TestSuite +{ + Inkscape::XML::Document *document; + Inkscape::XML::Node *a, *b, *c, *root; + +public: + + RebaseHrefsTest() + { + Inkscape::GC::init(); + + document = sp_repr_document_new("test"); + root = document->root(); + + a = document->createElement("a"); + b = document->createElement("b"); + c = document->createElement("c"); + } + virtual ~RebaseHrefsTest() {} + +// createSuite and destroySuite get us per-suite setup and teardown +// without us having to worry about static initialization order, etc. + static RebaseHrefsTest *createSuite() { return new RebaseHrefsTest(); } + static void destroySuite( RebaseHrefsTest *suite ) { delete suite; } + + + void dump_str(gchar const *str, gchar const *prefix) + { + Glib::ustring tmp; + tmp = prefix; + tmp += " ["; + size_t const total = strlen(str); + for (unsigned i = 0; i < total; i++) { + gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i])); + tmp += tmp2; + g_free(tmp2); + } + + tmp += "]"; + g_message("%s", tmp.c_str()); + } + + void testFlipples() + { + using Inkscape::URI; + using Inkscape::MalformedURIException; + + gchar const* things[] = { + "data:foo,bar", + "http://www.google.com/image.png", + "ftp://ssd.com/doo", + "/foo/dee/bar.svg", + "foo.svg", + "file:/foo/dee/bar.svg", + "file:///foo/dee/bar.svg", + "file:foo.svg", + "/foo/bar\xe1\x84\x92.svg", + "file:///foo/bar\xe1\x84\x92.svg", + "file:///foo/bar%e1%84%92.svg", + "/foo/bar%e1%84%92.svg", + "bar\xe1\x84\x92.svg", + "bar%e1%84%92.svg", + NULL + }; + g_message("+------"); + for ( int i = 0; things[i]; i++ ) + { + try + { + URI uri(things[i]); + gboolean isAbs = g_path_is_absolute( things[i] ); + gchar *str = uri.toString(); + g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs, + (int)uri.isRelative(), + uri.getScheme(), + uri.getPath(), + uri.getOpaque(), + things[i], + str ); + g_free(str); + } + catch ( MalformedURIException err ) + { + dump_str( things[i], "MalformedURIException" ); + xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]); + g_message(" gone from [%s] to [%s]", things[i], redo ); + if ( redo == NULL ) + { + URI again = URI::fromUtf8( things[i] ); + g_message(" uri from [%s] to [%s]", things[i], again.toString() ); + gboolean isAbs = g_path_is_absolute( things[i] ); + gchar *str = again.toString(); + g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs, + (int)again.isRelative(), + again.getScheme(), + again.getPath(), + again.getOpaque(), + things[i], + str ); + g_free(str); + g_message(" ----"); + } + } + } + g_message("+------"); + } +}; + +/* + 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:textwidth=99 : diff --git a/src/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp index 33b31685d..71e1cfb87 100644 --- a/src/xml/rebase-hrefs.cpp +++ b/src/xml/rebase-hrefs.cpp @@ -10,68 +10,64 @@ #include <glib/gmem.h> #include <glib/gurifuncs.h> #include <glib/gutils.h> -using Inkscape::XML::AttributeRecord; +#include <glibmm/miscutils.h> +#include <glibmm/convert.h> +#include <glibmm/uriutils.h> +using Inkscape::XML::AttributeRecord; /** - * \pre href. + * Determine if a href needs rebasing. */ -static bool -href_needs_rebasing(char const *const href) +static bool href_needs_rebasing(std::string const &href) { - g_return_val_if_fail(href, false); + bool ret = true; - if (!*href || *href == '#') { - return false; + if ( href.empty() || (href[0] == '#') ) { + ret = false; /* False (no change) is the right behaviour even when the base URI differs from the * document URI: RFC 3986 defines empty string relative URL as referring to the containing * document, rather than referring to the base URI. */ - } - - /* Don't change data or http hrefs. */ - { - char *const scheme = g_uri_parse_scheme(href); - if (scheme) { + } else { + /* Don't change data or http hrefs. */ + std::string scheme = Glib::uri_parse_scheme(href); + if ( !scheme.empty() ) { /* Assume it shouldn't be changed. This is probably wrong if the scheme is `file' * (or if the scheme of the new base is non-file, though I believe that never * happens at the time of writing), but that's rare, and we won't try too hard to * handle this now: wait until after the freeze, then add liburiparser (or similar) * as a dependency and do it properly. For now we'll just try to be simple (while * at least still correctly handling data hrefs). */ - free(scheme); - return false; + ret = false; + } else if (Glib::path_is_absolute(href)) { + /* If absolute then keep it as is. + * + * Even in the following borderline cases: + * + * - We keep it absolute even if it is in new_base (directly or indirectly). + * + * - We assume that if xlink:href is absolute then we honour it in preference to + * sodipodi:absref even if sodipodi:absref points to an existing file while xlink:href + * doesn't. This is because we aren't aware of any bugs in xlink:href handling when + * it's absolute, so we assume that it's the best value to use even in this case.) + */ + /* No strong preference on what we do for sodipodi:absref. Once we're + * confident of our handling of xlink:href and xlink:base, we should clear it. + * Though for the moment we do the simple thing: neither clear nor set it. */ + ret = false; } } - /* If absolute then keep it as is. - * - * Even in the following borderline cases: - * - * - We keep it absolute even if it is in new_base (directly or indirectly). - * - * - We assume that if xlink:href is absolute then we honour it in preference to - * sodipodi:absref even if sodipodi:absref points to an existing file while xlink:href - * doesn't. This is because we aren't aware of any bugs in xlink:href handling when - * it's absolute, so we assume that it's the best value to use even in this case.) - */ - if (g_path_is_absolute(href)) { - /* No strong preference on what we do for sodipodi:absref. Once we're - * confident of our handling of xlink:href and xlink:base, we should clear it. - * Though for the moment we do the simple thing: neither clear nor set it. */ - return false; - } - - return true; + return ret; } -static gchar * -calc_abs_href(gchar const *const abs_base_dir, gchar const *const href, - gchar const *const sp_absref) +static std::string calc_abs_href(std::string const &abs_base_dir, std::string const &href, + gchar const *const sp_absref) { - gchar *ret = g_build_filename(abs_base_dir, href, NULL); + std::string ret = Glib::build_filename(abs_base_dir, href); if ( sp_absref - && !Inkscape::IO::file_test(ret, G_FILE_TEST_EXISTS) + && !Inkscape::IO::file_test(ret.c_str(), G_FILE_TEST_EXISTS) && Inkscape::IO::file_test(sp_absref, G_FILE_TEST_EXISTS) ) { /* sodipodi:absref points to an existing file while xlink:href doesn't. @@ -93,18 +89,12 @@ calc_abs_href(gchar const *const abs_base_dir, gchar const *const href, * effic: Once we no longer consult sodipodi:absref, we can do * `if (base unchanged) { return; }' at the start of rebase_hrefs. */ - g_free(ret); - ret = g_strdup(sp_absref); + ret = sp_absref; } return ret; } -/** - * Change relative xlink:href attributes to be relative to \a new_abs_base instead of old_abs_base. - * - * Note that old_abs_base and new_abs_base must each be non-NULL, absolute directory paths. - */ Inkscape::Util::List<AttributeRecord const> Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base, gchar const *const new_abs_base, @@ -115,6 +105,7 @@ Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base, using Inkscape::Util::ptr_shared; using Inkscape::Util::share_string; + if (old_abs_base == new_abs_base) { return attributes; } @@ -133,7 +124,7 @@ Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base, for (List<AttributeRecord const> ai(attributes); ai; ++ai) { if (ai->key == href_key) { old_href = ai->value; - if (!href_needs_rebasing(old_href)) { + if (!href_needs_rebasing(static_cast<char const *>(old_href))) { return attributes; } } else if (ai->key == absref_key) { @@ -153,23 +144,33 @@ Inkscape::XML::rebase_href_attrs(gchar const *const old_abs_base, * reversed.) */ } - gchar *const abs_href(calc_abs_href(old_abs_base, old_href, sp_absref)); - gchar const *const new_href = sp_relative_path_from_path(abs_href, new_abs_base); - ret = cons(AttributeRecord(href_key, share_string(new_href)), ret); + std::string abs_href = calc_abs_href(old_abs_base, static_cast<char const *>(old_href), sp_absref); + std::string new_href = sp_relative_path_from_path(abs_href, new_abs_base); + ret = cons(AttributeRecord(href_key, share_string(new_href.c_str())), ret); // Check if this is safe/copied or if it is only held. if (sp_absref) { /* We assume that if there wasn't previously a sodipodi:absref attribute * then we shouldn't create one. */ - ret = cons(AttributeRecord(absref_key, ( streq(abs_href, sp_absref) + ret = cons(AttributeRecord(absref_key, ( streq(abs_href.c_str(), sp_absref) ? sp_absref - : share_string(abs_href) )), + : share_string(abs_href.c_str()) )), ret); } - g_free(abs_href); + return ret; } -gchar * -Inkscape::XML::calc_abs_doc_base(gchar const *const doc_base) +// std::string Inkscape::XML::rebase_href_attrs( std::string const &oldAbsBase, std::string const &newAbsBase, gchar const * /*href*/, gchar const */*absref*/ ) +// { +// std::string ret; +// //g_message( "XX need to flip from [%s] to [%s]", oldAbsBase.c_str(), newAbsBase.c_str() ); + +// if ( oldAbsBase != newAbsBase ) { +// } + +// return ret; +// } + +std::string Inkscape::XML::calc_abs_doc_base(gchar const *doc_base) { /* Note that we don't currently try to handle the case of doc_base containing * `..' or `.' path components. This non-handling means that sometimes @@ -179,34 +180,27 @@ Inkscape::XML::calc_abs_doc_base(gchar const *const doc_base) * relative URL/IRI href processing (with liburiparser). * * (Note that one possibile difficulty with `..' is symlinks.) */ + std::string ret; if (!doc_base) { - return g_get_current_dir(); - } else if (g_path_is_absolute(doc_base)) { - return g_strdup(doc_base); + ret = Glib::get_current_dir(); + } else if (Glib::path_is_absolute(doc_base)) { + ret = doc_base; } else { - gchar *const cwd = g_get_current_dir(); - gchar *const ret = g_build_filename(cwd, doc_base, NULL); - g_free(cwd); - return ret; + ret = Glib::build_filename( Glib::get_current_dir(), doc_base ); } + + return ret; } -/** - * Change relative hrefs in doc to be relative to \a new_base instead of doc.base. - * - * (NULL doc base or new_base is interpreted as current working directory.) - * - * \param spns True iff doc should contain sodipodi:absref attributes. - */ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_base, bool const spns) { if (!doc->getBase()) { return; } - gchar *const old_abs_base = calc_abs_doc_base(doc->getBase()); - gchar *const new_abs_base = calc_abs_doc_base(new_base); + std::string old_abs_base = calc_abs_doc_base(doc->getBase()); + std::string new_abs_base = calc_abs_doc_base(new_base); /* TODO: Should handle not just image but also: * @@ -232,22 +226,26 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b for (GSList const *l = images; l != NULL; l = l->next) { Inkscape::XML::Node *ir = static_cast<SPObject *>(l->data)->getRepr(); - gchar * uri = g_strdup(ir->attribute("xlink:href")); - if (!uri) { - continue; + std::string uri; + { + gchar const *tmp = ir->attribute("xlink:href"); + if ( !tmp ) { + continue; + } + uri = tmp; } - if (!strncmp(uri, "file://", 7)) { - uri = g_strdup(g_filename_from_uri(ir->attribute("xlink:href"), NULL, NULL)); + if ( uri.substr(0, 7) == "file://" ) { + uri = Glib::filename_from_uri(uri); } // The following two cases are for absolute hrefs that can be converted to relative. // Imported images, first time rebased, need an old base. - gchar * href = uri; - if (g_path_is_absolute(href)) { - href = (gchar *) sp_relative_path_from_path(uri, old_abs_base); + std::string href = uri; + if ( Glib::path_is_absolute(href) ) { + href = sp_relative_path_from_path(uri, old_abs_base); } // Files moved from a absolute path need a new one. - if (g_path_is_absolute(href)) { - href = (gchar *) sp_relative_path_from_path(uri, new_abs_base); + if ( Glib::path_is_absolute(href) ) { + href = sp_relative_path_from_path(uri, new_abs_base); } // Other bitmaps are either really absolute, or already relative. @@ -264,52 +262,41 @@ void Inkscape::XML::rebase_hrefs(SPDocument *const doc, gchar const *const new_b * changing non-file hrefs), which breaks if href starts with a scheme or if href contains * any escaping. */ - if (!href || !href_needs_rebasing(href)) { - g_free(uri); - continue; - } - - gchar *const abs_href(calc_abs_href(old_abs_base, href, ir->attribute("sodipodi:absref"))); - - /* todo: One difficult case once we support writing to non-file locations is where - * existing hrefs in the document point to local files. In this case, we should - * probably copy those referenced files to the new location at the same time. It's - * less clear what to do when copying from one non-file location to another. We may - * need to ask the user in some way (even if it's as a checkbox), but we'd like to - * bother the user as little as possible yet also want to warn the user about the case - * of file hrefs. */ - - gchar const *const new_href = sp_relative_path_from_path(abs_href, new_abs_base); - ir->setAttribute("sodipodi:absref", ( spns - ? abs_href - : NULL )); - if (!g_path_is_absolute(new_href)) { + if ( href_needs_rebasing(href) ) { + std::string abs_href = calc_abs_href(old_abs_base, href, ir->attribute("sodipodi:absref")); + + /* todo: One difficult case once we support writing to non-file locations is where + * existing hrefs in the document point to local files. In this case, we should + * probably copy those referenced files to the new location at the same time. It's + * less clear what to do when copying from one non-file location to another. We may + * need to ask the user in some way (even if it's as a checkbox), but we'd like to + * bother the user as little as possible yet also want to warn the user about the case + * of file hrefs. */ + + std::string new_href = sp_relative_path_from_path(abs_href, new_abs_base); + ir->setAttribute("sodipodi:absref", ( spns + ? abs_href.c_str() + : NULL )); + if (!Glib::path_is_absolute(new_href)) { #ifdef WIN32 - /* Native Windows path separators are replaced with / so that the href - * also works on Gnu/Linux and OSX */ - ir->setAttribute("xlink:href", g_strdelimit((gchar *) new_href, "\\", '/')); + /* Native Windows path separators are replaced with / so that the href + * also works on Gnu/Linux and OSX */ + ir->setAttribute("xlink:href", g_strdelimit(new_href.c_str(), "\\", '/')); #else - ir->setAttribute("xlink:href", new_href); + ir->setAttribute("xlink:href", new_href.c_str()); #endif - } else { - ir->setAttribute("xlink:href", g_filename_to_uri((gchar *) new_href, NULL, NULL)); - } + } else { + ir->setAttribute("xlink:href", g_filename_to_uri(new_href.c_str(), NULL, NULL)); + } - /* impl: I assume that if !spns then any existing sodipodi:absref is about to get - * cleared (or is already cleared) anyway, in which case it doesn't matter whether we - * clear or leave any existing sodipodi:absref value. If that assumption turns out to - * be wrong, then leaving it means risking leaving the wrong value (if xlink:href - * referred to a different file than sodipodi:absref) while clearing it means risking - * losing information. */ - - g_free(uri); - // (No need to free href, it's guaranteed to point into uri.) - g_free(abs_href); - // (No need to free new_href, it's guaranteed to point into abs_href.) + /* impl: I assume that if !spns then any existing sodipodi:absref is about to get + * cleared (or is already cleared) anyway, in which case it doesn't matter whether we + * clear or leave any existing sodipodi:absref value. If that assumption turns out to + * be wrong, then leaving it means risking leaving the wrong value (if xlink:href + * referred to a different file than sodipodi:absref) while clearing it means risking + * losing information. */ + } } - - g_free(new_abs_base); - g_free(old_abs_base); } diff --git a/src/xml/rebase-hrefs.h b/src/xml/rebase-hrefs.h index b4f288c4d..4cbdec9a5 100644 --- a/src/xml/rebase-hrefs.h +++ b/src/xml/rebase-hrefs.h @@ -9,17 +9,36 @@ struct SPDocument; namespace Inkscape { namespace XML { -gchar *calc_abs_doc_base(gchar const *doc_base); - +std::string calc_abs_doc_base(gchar const *doc_base); + +/** + * Change relative hrefs in doc to be relative to \a new_base instead of doc.base. + * + * (NULL doc base or new_base is interpreted as current working directory.) + * + * @param spns True if doc should contain sodipodi:absref attributes. + */ void rebase_hrefs(SPDocument *doc, gchar const *new_base, bool spns); +/** + * Change relative xlink:href attributes to be relative to \a new_abs_base instead of old_abs_base. + * + * Note that old_abs_base and new_abs_base must each be non-NULL, absolute directory paths. + */ Inkscape::Util::List<AttributeRecord const> rebase_href_attrs( gchar const *old_abs_base, gchar const *new_abs_base, Inkscape::Util::List<AttributeRecord const> attributes); -} -} + +// /** +// * . +// * @return a non-empty replacement href if needed, empty otherwise. +// */ +// std::string rebase_href_attrs( std::string const &oldAbsBase, std::string const &newAbsBase, gchar const *href, gchar const *absref = 0 ); + +} // namespace XML +} // namespace Inkscape #endif /* !REBASE_HREFS_H_SEEN */ diff --git a/src/xml/repr-action-test.h b/src/xml/repr-action-test.h index afc9b2c46..ae4291397 100644 --- a/src/xml/repr-action-test.h +++ b/src/xml/repr-action-test.h @@ -88,7 +88,6 @@ public: } /* lots more tests needed ... */ - }; /* diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index 5f7654ba8..2a0bb6ce8 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -52,6 +52,7 @@ static void sp_repr_write_stream_root_element(Node *repr, Writer &out, int inlineattrs, int indent, gchar const *old_href_abs_base, gchar const *new_href_abs_base); + static void sp_repr_write_stream_element(Node *repr, Writer &out, gint indent_level, bool add_whitespace, Glib::QueryQuark elide_prefix, @@ -644,7 +645,7 @@ sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar cons return false; } - gchar *old_href_abs_base = NULL; + std::string old_href_abs_base; gchar *new_href_abs_base = NULL; if (for_filename) { old_href_abs_base = calc_abs_doc_base(old_base); @@ -662,9 +663,8 @@ sp_repr_save_rebased_file(Document *doc, gchar const *const filename, gchar cons * to using sodipodi:absref instead of the xlink:href value, * then we should do `if streq() { free them and set both to NULL; }'. */ } - sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base, new_href_abs_base); + sp_repr_save_stream(doc, file, default_ns, compress, old_href_abs_base.c_str(), new_href_abs_base); - g_free(old_href_abs_base); g_free(new_href_abs_base); if (fclose (file) != 0) { @@ -879,17 +879,16 @@ void sp_repr_write_stream( Node *repr, Writer &out, gint indent_level, } -static void -sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level, - bool add_whitespace, - Glib::QueryQuark elide_prefix, - List<AttributeRecord const> attributes, - int inlineattrs, int indent, - gchar const *const old_href_base, - gchar const *const new_href_base) +void sp_repr_write_stream_element( Node * repr, Writer & out, + gint indent_level, bool add_whitespace, + Glib::QueryQuark elide_prefix, + List<AttributeRecord const> attributes, + int inlineattrs, int indent, + gchar const *old_href_base, + gchar const *new_href_base ) { - Node *child; - bool loose; + Node *child = 0; + bool loose = false; g_return_if_fail (repr != NULL); @@ -921,6 +920,28 @@ sp_repr_write_stream_element (Node * repr, Writer & out, gint indent_level, add_whitespace = false; } + + { + GQuark const href_key = g_quark_from_static_string("xlink:href"); + GQuark const absref_key = g_quark_from_static_string("sodipodi:absref"); + + gchar const *xxHref = 0; + gchar const *xxAbsref = 0; + for ( List<AttributeRecord const> ai(attributes); ai; ++ai ) { + if ( ai->key == href_key ) { + xxHref = ai->value; + } else if ( ai->key == absref_key ) { + xxAbsref = ai->value; + } + } + + // Might add a special case for absref but no href. + if ( old_href_base && new_href_base && xxHref ) { + //g_message("href rebase test with [%s] and [%s]", xxHref, xxAbsref); + //std::string newOne = rebase_href_attrs( old_href_base, new_href_base, xxHref, xxAbsref ); + } + } + for ( List<AttributeRecord const> iter = rebase_href_attrs(old_href_base, new_href_base, attributes); iter ; ++iter ) diff --git a/src/xml/repr.h b/src/xml/repr.h index bde3e533f..5fa9387c7 100644 --- a/src/xml/repr.h +++ b/src/xml/repr.h @@ -79,10 +79,13 @@ void sp_repr_write_stream(Inkscape::XML::Node *repr, Inkscape::IO::Writer &out, gchar const *new_href_base = NULL); Inkscape::XML::Document *sp_repr_read_buf (const Glib::ustring &buf, const gchar *default_ns); Glib::ustring sp_repr_save_buf(Inkscape::XML::Document *doc); + +// TODO convert to std::string void sp_repr_save_stream(Inkscape::XML::Document *doc, FILE *to_file, gchar const *default_ns = NULL, bool compress = false, gchar const *old_href_base = NULL, gchar const *new_href_base = NULL); + bool sp_repr_save_file(Inkscape::XML::Document *doc, gchar const *filename, gchar const *default_ns=NULL); bool sp_repr_save_rebased_file(Inkscape::XML::Document *doc, gchar const *filename_utf8, gchar const *default_ns, |
