resource-manager.cpp revision 2c312e5f8adf7306ce4b41fb6639475df9c3ea87
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * Inkscape::ResourceManager - tracks external resources such as image and css files.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * Copyright 2011 Jon A. Cruz <jon@joncruz.org>
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * Released under GNU GPL, read the file 'COPYING' for more information
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruzstd::vector<std::string> splitPath( std::string const &path )
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz parts.push_back( Glib::path_get_basename(tmp) );
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruzstd::string convertPathToRelative( std::string const &path, std::string const &docbase )
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz if ( !path.empty() && Glib::path_is_absolute(path) ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // Whack the parts into pieces
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz std::vector<std::string> parts = splitPath(path);
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz std::vector<std::string> baseParts = splitPath(docbase);
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message("+++++++++++++++++++++++++");
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz for ( std::vector<std::string>::iterator it = parts.begin(); it != parts.end(); ++it ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message(" [%s]", it->c_str());
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message(" - - - - - - - - - - - - - - - ");
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz for ( std::vector<std::string>::iterator it = baseParts.begin(); it != baseParts.end(); ++it ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message(" [%s]", it->c_str());
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message("+++++++++++++++++++++++++");
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz if ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // Both paths have the same root. We can proceed.
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz while ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message("+++++++++++++++++++++++++");
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz for ( std::vector<std::string>::iterator it = parts.begin(); it != parts.end(); ++it ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message(" [%s]", it->c_str());
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message(" - - - - - - - - - - - - - - - ");
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz for ( std::vector<std::string>::iterator it = baseParts.begin(); it != baseParts.end(); ++it ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message(" [%s]", it->c_str());
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message("+++++++++++++++++++++++++");
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz for ( size_t i = 0; i < baseParts.size(); ++i ) {
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message("----> [%s]", result.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruzclass ResourceManagerImpl : public ResourceManager {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * Walk all links in a document and create a listing of unique broken links.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * @return a list of all broken links.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz std::vector<Glib::ustring> findBrokenLinks(SPDocument *doc);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * Resolve broken links as a whole and return a map for those that can be found.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * Note: this will allow for future enhancements including relinking to new locations
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * with the most broken files found, etc.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz * @return a map of found links.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz std::map<Glib::ustring, Glib::ustring> locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz bool extractFilepath( Glib::ustring const &href, std::string &uri );
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz bool searchUpwards( std::string const &base, std::string const &subpath, std::string &dest );
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruzbool ResourceManagerImpl::extractFilepath( Glib::ustring const &href, std::string &uri )
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz bool isFile = false;
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz std::string scheme = Glib::uri_parse_scheme(href);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message("Scheme is now [%s]", scheme.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message("--- is a file URI [%s]", href.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // throws Glib::ConvertError:
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz uri = Glib::filename_from_uri(href); // TODO see if we can get this to throw
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" [%s]", uri.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // No scheme. Assuming it is a file path (absolute or relative).
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // throws Glib::ConvertError:
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruzstd::vector<Glib::ustring> ResourceManagerImpl::findBrokenLinks( SPDocument *doc )
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz GSList const *images = doc->getResourceList("image");
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz for (GSList const *it = images; it; it = it->next) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz Inkscape::XML::Node *ir = static_cast<SPObject *>(it->data)->getRepr();
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz gchar const *href = ir->attribute("xlink:href");
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz if ( href && ( uniques.find(href) == uniques.end() ) ) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz std::string combined = Glib::build_filename(doc->getBase(), uri);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruzstd::map<Glib::ustring, Glib::ustring> ResourceManagerImpl::locateLinks(Glib::ustring const & docbase, std::vector<Glib::ustring> const & brokenLinks)
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz // Note: we use a vector because we want them to stay in order:
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz Glib::RefPtr<Gtk::RecentManager> recentMgr = Gtk::RecentManager::get_default();
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz std::vector< Glib::RefPtr<Gtk::RecentInfo> > recentItems = recentMgr->get_items();
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz for ( std::vector< Glib::RefPtr<Gtk::RecentInfo> >::iterator it = recentItems.begin(); it != recentItems.end(); ++it ) {
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz std::string scheme = Glib::uri_parse_scheme(uri);
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz std::string path = Glib::filename_from_uri(uri);
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz if ( std::find(priorLocations.begin(), priorLocations.end(), path) == priorLocations.end() ) {
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz // TODO debug g_message(" ==>[%s]", path.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // At the moment we expect this list to contain file:// references, or simple relative or absolute paths.
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz for ( std::vector<Glib::ustring>::const_iterator it = brokenLinks.begin(); it != brokenLinks.end(); ++it ) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message("========{%s}", it->c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // We were able to get some path. Check it
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" not absolute. Fixing up as [%s]", uri.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz if ( !Glib::file_test(uri, Glib::FILE_TEST_EXISTS) ) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" DOES NOT EXIST.");
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz bool exists = searchUpwards( docbase, origPath, remainder );
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz // TODO debug g_message("Expanding the search...");
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz // Check if the MRU bases point us to it.
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) {
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz exists = searchUpwards( *it, origPath, remainder );
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz // TODO debug g_message("Need to convert to relative if possible [%s]", remainder.c_str());
c864652e7fca4ed7605835d5cb6661dc422cabaaJon A. Cruz remainder = convertPathToRelative( remainder, docbase );
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz bool isAbsolute = Glib::path_is_absolute( remainder );
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( remainder ) : Glib::filename_to_utf8( remainder );
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruzbool ResourceManagerImpl::fixupBrokenLinks(SPDocument *doc)
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz bool changed = false;
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message("FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP FIXUP");
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" base is [%s]", doc->getBase());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz std::vector<Glib::ustring> brokenHrefs = findBrokenLinks(doc);
00339f176618a0eebcbb2261306a9cd841ad43ecJon A. Cruz // TODO debug g_message(" FOUND SOME LINKS %d", static_cast<int>(brokenHrefs.size()));
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz for ( std::vector<Glib::ustring>::iterator it = brokenHrefs.begin(); it != brokenHrefs.end(); ++it ) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" [%s]", it->c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz std::map<Glib::ustring, Glib::ustring> mapping = locateLinks(doc->getBase(), brokenHrefs);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz for ( std::map<Glib::ustring, Glib::ustring>::iterator it = mapping.begin(); it != mapping.end(); ++it )
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" [%s] ==> {%s}", it->first.c_str(), it->second.c_str());
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz bool savedUndoState = DocumentUndo::getUndoSensitive(doc);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz GSList const *images = doc->getResourceList("image");
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz for (GSList const *it = images; it; it = it->next) {
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz Inkscape::XML::Node *ir = static_cast<SPObject *>(it->data)->getRepr();
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz gchar const *href = ir->attribute("xlink:href");
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" consider [%s]", href);
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // TODO debug g_message(" Found a replacement");
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz ir->setAttribute( "xlink:href", mapping[href].c_str() );
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz ir->setAttribute( "sodipodi:absref", 0 ); // Remove this attribute
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz // force immediate update of dependant attributes
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz DocumentUndo::done( doc, SP_VERB_DIALOG_XML_EDITOR, _("Fixup broken links") );
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz DocumentUndo::setUndoSensitive(doc, savedUndoState);
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruzbool ResourceManagerImpl::searchUpwards( std::string const &base, std::string const &subpath, std::string &dest )
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz bool exists = false;
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz // TODO debug g_message("............");
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz std::vector<std::string> parts = splitPath(subpath);
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz std::vector<std::string> baseParts = splitPath(base);
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz current.insert(current.begin(), parts.begin(), parts.end());
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz // TODO debug g_message(" ---{%s}", Glib::build_filename( baseParts ).c_str());
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz combined.insert( combined.end(), baseParts.begin(), baseParts.end() );
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz combined.insert( combined.end(), current.begin(), current.end() );
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz std::string filepath = Glib::build_filename( combined );
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz exists = Glib::file_test(filepath, Glib::FILE_TEST_EXISTS);
2c312e5f8adf7306ce4b41fb6639475df9c3ea87Jon A. Cruz // TODO debug g_message(" ...[%s] %s", filepath.c_str(), (exists ? "XXX" : ""));
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz} // namespace Inkscape
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz Local Variables:
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz c-file-style:"stroustrup"
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz indent-tabs-mode:nil
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz fill-column:99
a63dbee6633e3ed991bb2b34cbed7c9f02c1a839Jon A. Cruz// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :