/* * Inkscape::ResourceManager - tracks external resources such as image and css files. * * Copyright 2011 Jon A. Cruz * * Released under GNU GPL, read the file 'COPYING' for more information */ #include #include #include #include #include #include #include #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 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 locateLinks(Glib::ustring const & docbase, std::vector 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 ResourceManagerImpl::findBrokenLinks( SPDocument *doc ) { std::vector result; std::set uniques; if ( doc ) { GSList const *images = doc->getResourceList("image"); for (GSList const *it = images; it; it = it->next) { Inkscape::XML::Node *ir = static_cast(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 ResourceManagerImpl::locateLinks(Glib::ustring const & docbase, std::vector const & brokenLinks) { std::map result; // At the moment we expect this list to contain file:// references, or simple relative or absolute paths. for ( std::vector::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 brokenHrefs = findBrokenLinks(doc); if ( !brokenHrefs.empty() ) { // TODO debug g_message(" FOUND SOME LINKS %d", brokenHrefs.size()); for ( std::vector::iterator it = brokenHrefs.begin(); it != brokenHrefs.end(); ++it ) { // TODO debug g_message(" [%s]", it->c_str()); } } std::map mapping = locateLinks(doc->getBase(), brokenHrefs); for ( std::map::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(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 :