13#include <gtkmm/recentmanager.h>
14#include <glibmm/i18n.h>
15#include <glibmm/miscutils.h>
16#include <glibmm/fileutils.h>
17#include <glibmm/uriutils.h>
18#include <glibmm/convert.h>
34std::vector<std::string>
splitPath( std::string
const &path )
36 std::vector<std::string> parts;
39 std::string tmp = path;
40 while ( !tmp.empty() && (tmp != prior) ) {
43 parts.push_back( Glib::path_get_basename(tmp) );
44 tmp = Glib::path_get_dirname(tmp);
46 if ( !parts.empty() ) {
47 std::reverse(parts.begin(), parts.end());
48 if ( (parts[0] ==
".") && (path[0] !=
'.') ) {
49 parts.erase(parts.begin());
67std::string
optimizePath(std::string
const &path, std::string
const &base,
unsigned int parents)
71 if (!path.empty() && Glib::path_is_absolute(path)) {
74 std::vector<std::string> parts =
splitPath(path);
75 std::vector<std::string> baseParts =
splitPath(base);
77 if ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) {
79 while ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) {
80 parts.erase( parts.begin() );
81 baseParts.erase( baseParts.begin() );
84 if (!parts.empty() && baseParts.size() <= parents) {
87 for (
size_t i = 0; i < baseParts.size(); ++i ) {
88 parts.insert(parts.begin(),
"..");
90 result = Glib::build_filename( parts );
117static std::map<Glib::ustring, Glib::ustring>
locateLinks(Glib::ustring
const & docbase, std::vector<Glib::ustring>
const & brokenLinks);
125static bool extractFilepath(Glib::ustring
const &href, std::string &filename);
135static bool searchUpwards( std::string
const &base, std::string
const &subpath, std::string &dest );
145 auto scheme = Glib::uri_parse_scheme(href.raw());
146 if ( !scheme.empty() ) {
148 if ( scheme ==
"file" ) {
153 filename = Glib::filename_from_uri(href);
155 }
catch(Glib::ConvertError e) {
156 g_warning(
"%s", e.what());
162 filename = Glib::filename_from_utf8(href);
175 auto scheme = Glib::uri_parse_scheme(href.raw());
176 if ( !scheme.empty() ) {
177 if ( scheme ==
"file" ) {
180 Glib::ustring href_new = Glib::ustring(href, 5);
181 filename = Glib::filename_from_utf8(href_new);
191 std::vector<Glib::ustring>
result;
192 std::set<Glib::ustring> uniques;
196 for (
auto image : images) {
200 if ( href && ( uniques.find(href) == uniques.end() ) ) {
201 std::string filename;
203 if (Glib::path_is_absolute(filename)) {
204 if (!Glib::file_test(filename, Glib::FileTest::EXISTS)) {
205 result.emplace_back(href);
206 uniques.insert(href);
209 std::string combined = Glib::build_filename(doc->
getDocumentBase(), filename);
210 if ( !Glib::file_test(combined, Glib::FileTest::EXISTS) ) {
211 result.emplace_back(href);
212 uniques.insert(href);
216 result.emplace_back(href);
217 uniques.insert(href);
227static std::map<Glib::ustring, Glib::ustring>
locateLinks(Glib::ustring
const & docbase, std::vector<Glib::ustring>
const & brokenLinks)
229 std::map<Glib::ustring, Glib::ustring>
result;
233 std::vector<std::string> priorLocations;
235 Glib::RefPtr<Gtk::RecentManager> recentMgr = Gtk::RecentManager::get_default();
236 std::vector< Glib::RefPtr<Gtk::RecentInfo> > recentItems = recentMgr->get_items();
237 for (
auto & recentItem : recentItems) {
238 Glib::ustring uri = recentItem->get_uri();
239 auto scheme = Glib::uri_parse_scheme(uri.raw());
240 if ( scheme ==
"file" ) {
242 std::string path = Glib::filename_from_uri(uri);
243 path = Glib::path_get_dirname(path);
244 if ( std::find(priorLocations.begin(), priorLocations.end(), path) == priorLocations.end() ) {
246 priorLocations.push_back(path);
248 }
catch (Glib::ConvertError e) {
249 g_warning(
"%s", e.what());
255 for (
const auto & brokenLink : brokenLinks) {
258 std::string filename;
260 auto const docbase_native = Glib::filename_from_utf8(docbase);
263 std::string origPath = filename;
265 if (!Glib::path_is_absolute(filename)) {
266 filename = Glib::build_filename(docbase_native, filename);
269 bool exists = Glib::file_test(filename, Glib::FileTest::EXISTS);
278 if ( !Glib::path_is_absolute(origPath) ) {
279 for ( std::vector<std::string>::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) {
286 if (Glib::path_is_absolute(filename)) {
290 bool isAbsolute = Glib::path_is_absolute(filename);
291 Glib::ustring replacement =
292 isAbsolute ? Glib::filename_to_uri(filename) : Glib::filename_to_utf8(filename);
293 result[brokenLink] = replacement;
303 bool changed =
false;
309 if ( !brokenHrefs.empty() ) {
311 for ( std::vector<Glib::ustring>::iterator it = brokenHrefs.begin(); it != brokenHrefs.end(); ++it ) {
321 std::map<Glib::ustring, Glib::ustring> mapping =
locateLinks(base, brokenHrefs);
322 for ( std::map<Glib::ustring, Glib::ustring>::iterator it = mapping.begin(); it != mapping.end(); ++it )
330 for (
auto image : images) {
337 if ( mapping.find(href) != mapping.end() ) {
341 if ( ir->
attribute(
"sodipodi:absref" ) ) {
356 DocumentUndo::done( doc, _(
"Fixup broken links"), INKSCAPE_ICON(
"dialog-xml-editor"));
363static bool searchUpwards( std::string
const &base, std::string
const &subpath, std::string &dest )
368 std::vector<std::string> parts =
splitPath(subpath);
369 std::vector<std::string> baseParts =
splitPath(base);
371 while ( !exists && !baseParts.empty() ) {
372 std::vector<std::string>
current;
375 while ( !exists && !
current.empty() ) {
376 std::vector<std::string> combined;
377 combined.insert( combined.end(), baseParts.begin(), baseParts.end() );
378 combined.insert( combined.end(),
current.begin(),
current.end() );
379 std::string filepath = Glib::build_filename( combined );
380 exists = Glib::file_test(filepath, Glib::FileTest::EXISTS);
387 baseParts.pop_back();
RAII-style mechanism for creating a temporary undo-insensitive context.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Interface for refcounted XML nodes.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Typed SVG document implementation.
char const * getDocumentBase() const
std::vector< SPObject * > const getResourceList(char const *key)
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Inkscape::XML::Node * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
static char const *const current
TODO: insert short description here.
Macro for icon names used in Inkscape.
std::unique_ptr< Magick::Image > image
Helper class to stream background task notifications as a series of messages.
static bool searchUpwards(std::string const &base, std::string const &subpath, std::string &dest)
static bool extractFilepath(Glib::ustring const &href, std::string &filename)
Try to parse href into a local filename using standard methods.
std::vector< std::string > splitPath(std::string const &path)
bool fixBrokenLinks(SPDocument *doc)
static bool reconstructFilepath(Glib::ustring const &href, std::string &filename)
Try to parse href into a local filename using some non-standard methods.
static std::map< Glib::ustring, Glib::ustring > locateLinks(Glib::ustring const &docbase, std::vector< Glib::ustring > const &brokenLinks)
Resolve broken links as a whole and return a map for those that can be found.
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
std::string optimizePath(std::string const &path, std::string const &base, unsigned int parents)
Convert an absolute path into a relative one if possible to do in the given number of parent steps.
static std::vector< Glib::ustring > findBrokenLinks(SPDocument *doc)
Walk all links in a document and create a listing of unique broken links.