From faf43f2ffa88561acb95909861bb28fcdfaeb858 Mon Sep 17 00:00:00 2001 From: Tavmjong Bah Date: Sat, 13 Oct 2018 09:37:26 +0200 Subject: Move some files to 'io' directory. --- src/CMakeLists.txt | 4 - src/desktop.cpp | 3 +- src/dir-util.cpp | 254 ------ src/dir-util.h | 66 -- src/document.cpp | 2 +- src/extension/implementation/script.cpp | 2 +- src/extension/internal/gdkpixbuf-input.cpp | 2 +- src/file-update.cpp | 2 +- src/file.cpp | 2 +- src/inkscape.cpp | 20 +- src/io/CMakeLists.txt | 56 +- src/io/command_line_export.h | 59 ++ src/io/command_line_output.h | 58 ++ src/io/commandline_io.h | 58 ++ src/io/dir-util.cpp | 254 ++++++ src/io/dir-util.h | 66 ++ src/io/file.h | 25 + src/io/open_file.h | 12 + src/io/registrytool.cpp | 226 +++++ src/io/registrytool.h | 69 ++ src/io/resource-manager.cpp | 445 ++++++++++ src/io/resource-manager.h | 49 + src/main-cmdlinexact.cpp | 2 +- src/main.cpp | 2 +- src/object/sp-text.cpp.orig | 1324 ++++++++++++++++++++++++++++ src/registrytool.cpp | 226 ----- src/registrytool.h | 69 -- src/resource-manager.cpp | 445 ---------- src/resource-manager.h | 49 - src/xml/rebase-hrefs.cpp | 2 +- 30 files changed, 2702 insertions(+), 1151 deletions(-) delete mode 100644 src/dir-util.cpp delete mode 100644 src/dir-util.h create mode 100644 src/io/command_line_export.h create mode 100644 src/io/command_line_output.h create mode 100644 src/io/commandline_io.h create mode 100644 src/io/dir-util.cpp create mode 100644 src/io/dir-util.h create mode 100644 src/io/file.h create mode 100644 src/io/open_file.h create mode 100644 src/io/registrytool.cpp create mode 100644 src/io/registrytool.h create mode 100644 src/io/resource-manager.cpp create mode 100644 src/io/resource-manager.h create mode 100644 src/object/sp-text.cpp.orig delete mode 100644 src/registrytool.cpp delete mode 100644 src/registrytool.h delete mode 100644 src/resource-manager.cpp delete mode 100644 src/resource-manager.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 26551e99e..2912d40e6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,7 +19,6 @@ set(inkscape_SRC desktop-style.cpp desktop.cpp device-manager.cpp - dir-util.cpp document-subset.cpp document-undo.cpp document.cpp @@ -67,7 +66,6 @@ set(inkscape_SRC pure-transform.cpp rdf.cpp removeoverlap.cpp - resource-manager.cpp rubberband.cpp satisfied-guide-cns.cpp selcue.cpp @@ -124,7 +122,6 @@ set(inkscape_SRC desktop-style.h desktop.h device-manager.h - dir-util.h document-private.h document-subset.h document-undo.h @@ -183,7 +180,6 @@ set(inkscape_SRC rdf.h remove-last.h removeoverlap.h - resource-manager.h rubberband.h satisfied-guide-cns.h selcue.h diff --git a/src/desktop.cpp b/src/desktop.cpp index 79fec2927..46589ca04 100644 --- a/src/desktop.cpp +++ b/src/desktop.cpp @@ -38,7 +38,6 @@ #include "layer-manager.h" #include "message-context.h" #include "message-stack.h" -#include "resource-manager.h" #include "display/canvas-arena.h" #include "display/canvas-debug.h" @@ -55,6 +54,8 @@ #include "helper/action-context.h" #include "helper/action.h" //sp_action_perform +#include "io/resource-manager.h" + #include "object/sp-namedview.h" #include "object/sp-root.h" diff --git a/src/dir-util.cpp b/src/dir-util.cpp deleted file mode 100644 index 1a215fa29..000000000 --- a/src/dir-util.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/** - * @file - * Utility functions for filenames. - */ - -#include -#include -#include -#include -#include "dir-util.h" - -std::string sp_relative_path_from_path( std::string const &path, std::string const &base) -{ - 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; - } - - 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 ( result.empty() ) { - result = path; - } - return result; -} - -char const *sp_extension_from_path(char const *const path) -{ - if (path == nullptr) { - return nullptr; - } - - char const *p = path; - while (*p != '\0') p++; - - while ((p >= path) && (*p != G_DIR_SEPARATOR) && (*p != '.')) p--; - if (* p != '.') return nullptr; - p++; - - return p; -} - - -/* current == "./", parent == "../" */ -static char const dots[] = {'.', '.', G_DIR_SEPARATOR, '\0'}; -static char const *const parent = dots; -static char const *const current = dots + 1; - -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. */ - const char *endp = result + size - 1; - char *rp; - int length; - if (*path == G_DIR_SEPARATOR) - { - if (strlen (path) >= size) - goto erange; - strcpy (result, path); - goto finish; - } - else if (*base != G_DIR_SEPARATOR || !size) - { - errno = EINVAL; - return (nullptr); - } - else if (size == 1) - goto erange; - if (!strcmp (path, ".") || !strcmp (path, current)) - { - if (strlen (base) >= size) - goto erange; - strcpy (result, base); - /* rp points the last char. */ - rp = result + strlen (base) - 1; - if (*rp == G_DIR_SEPARATOR) - *rp = 0; - else - rp++; - /* rp point NULL char */ - if (*++path == G_DIR_SEPARATOR) - { - /* Append G_DIR_SEPARATOR to the tail of path name. */ - *rp++ = G_DIR_SEPARATOR; - if (rp > endp) - goto erange; - *rp = 0; - } - goto finish; - } - bp = base + strlen (base); - if (*(bp - 1) == G_DIR_SEPARATOR) - --bp; - /* up to root. */ - for (pp = path; *pp && *pp == '.';) - { - if (!strncmp (pp, parent, 3)) - { - pp += 3; - while (bp > base && *--bp != G_DIR_SEPARATOR) - ; - } - else if (!strncmp (pp, current, 2)) - { - pp += 2; - } - else if (!strncmp (pp, "..\0", 3)) - { - pp += 2; - while (bp > base && *--bp != G_DIR_SEPARATOR) - ; - } - else - break; - } - /* down to leaf. */ - length = bp - base; - if (length >= static_cast(size)) - goto erange; - strncpy (result, base, length); - rp = result + length; - if (*pp || *(pp - 1) == G_DIR_SEPARATOR || length == 0) - *rp++ = G_DIR_SEPARATOR; - if (rp + strlen (pp) > endp) - goto erange; - strcpy (rp, pp); -finish: - return result; -erange: - errno = ERANGE; - return (nullptr); -} - -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; - - if (*path != G_DIR_SEPARATOR) - { - if (strlen (path) >= size) - goto erange; - strcpy (result, path); - goto finish; - } - else if (*base != G_DIR_SEPARATOR || !size) - { - errno = EINVAL; - return (nullptr); - } - 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; - } - 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); - } - else - *--rp = 0; -finish: - return result; -erange: - errno = ERANGE; - return (nullptr); -} - -char *prepend_current_dir_if_relative(gchar const *uri) -{ - if (!uri) { - return nullptr; - } - - gchar *full_path = (gchar *) g_malloc (1001); - gchar *cwd = g_get_current_dir(); - - gsize bytesRead = 0; - gsize bytesWritten = 0; - GError* error = nullptr; - gchar* cwd_utf8 = g_filename_to_utf8 ( cwd, - -1, - &bytesRead, - &bytesWritten, - &error); - - inkscape_rel2abs (uri, cwd_utf8, full_path, 1000); - gchar *ret = g_strdup (full_path); - g_free (full_path); - g_free (cwd); - 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 deleted file mode 100644 index 327e1ad5f..000000000 --- a/src/dir-util.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef SEEN_DIR_UTIL_H -#define SEEN_DIR_UTIL_H - -/* - * path-util.h - * - * here are functions sp_relative_path & cousins - * maybe they are already implemented in standard libs - * - */ - -#include -#include - -/** - * 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); - -char *prepend_current_dir_if_relative(char const *filename); - - -#endif // !SEEN_DIR_UTIL_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/document.cpp b/src/document.cpp index 54b7071c0..5b8fe305c 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -42,7 +42,7 @@ #include <2geom/transforms.h> #include "desktop.h" -#include "dir-util.h" +#include "io/dir-util.h" #include "document-private.h" #include "document-undo.h" #include "file.h" diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp index 9e0dd31c8..fe3b14093 100644 --- a/src/extension/implementation/script.cpp +++ b/src/extension/implementation/script.cpp @@ -54,7 +54,7 @@ #ifdef _WIN32 #include #include -#include "registrytool.h" +#include "io/registrytool.h" #endif /** This is the command buffer that gets allocated from the stack */ diff --git a/src/extension/internal/gdkpixbuf-input.cpp b/src/extension/internal/gdkpixbuf-input.cpp index 02ca5af1a..41683d3b4 100644 --- a/src/extension/internal/gdkpixbuf-input.cpp +++ b/src/extension/internal/gdkpixbuf-input.cpp @@ -4,7 +4,7 @@ #include #include -#include "dir-util.h" +#include "io/dir-util.h" #include "display/cairo-utils.h" #include "document-private.h" #include "document-undo.h" diff --git a/src/file-update.cpp b/src/file-update.cpp index 68337b806..de09047bd 100644 --- a/src/file-update.cpp +++ b/src/file-update.cpp @@ -14,7 +14,6 @@ #include #include "desktop.h" -#include "dir-util.h" #include "document-undo.h" #include "document.h" #include "file.h" @@ -36,6 +35,7 @@ #include "extension/output.h" #include "extension/system.h" +#include "io/dir-util.h" #include "io/sys.h" #include "object/persp3d.h" diff --git a/src/file.cpp b/src/file.cpp index ac4d0ab62..5e93fb9ce 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -46,7 +46,6 @@ #include "path-prefix.h" #include "print.h" #include "rdf.h" -#include "resource-manager.h" #include "selection-chemistry.h" #include "verbs.h" @@ -58,6 +57,7 @@ #include "helper/png-write.h" #include "io/resource.h" +#include "io/resource-manager.h" #include "io/sys.h" #include "object/sp-defs.h" diff --git a/src/inkscape.cpp b/src/inkscape.cpp index 516c14f4e..6a6310b3d 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -25,8 +25,6 @@ #include #include #include -#include "debug/simple-event.h" -#include "debug/event-tracker.h" #include #include @@ -34,22 +32,30 @@ #include #include "desktop.h" - #include "device-manager.h" #include "document.h" +#include "inkscape.h" +#include "message-stack.h" +#include "path-prefix.h" + +#include "debug/simple-event.h" +#include "debug/event-tracker.h" + #include "extension/db.h" #include "extension/init.h" #include "extension/output.h" #include "extension/system.h" + #include "helper/action-context.h" -#include "inkscape.h" + #include "io/resource.h" +#include "io/resource-manager.h" #include "io/sys.h" + #include "libnrtype/FontFactory.h" -#include "message-stack.h" -#include "path-prefix.h" -#include "resource-manager.h" + #include "svg/svg-color.h" + #include "ui/dialog/debug.h" #include "ui/tools/tool-base.h" diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt index 380b983db..c816082b0 100644 --- a/src/io/CMakeLists.txt +++ b/src/io/CMakeLists.txt @@ -1,29 +1,41 @@ set(io_SRC - base64stream.cpp - bufferstream.cpp - gzipstream.cpp - inkscapestream.cpp - resource.cpp - stringstream.cpp - sys.cpp - http.cpp - uristream.cpp - xsltstream.cpp + base64stream.cpp + bufferstream.cpp + dir-util.cpp + gzipstream.cpp + inkscapestream.cpp + resource.cpp + resource-manager.cpp + stringstream.cpp + sys.cpp + http.cpp + uristream.cpp + xsltstream.cpp - # ------- - # Headers - base64stream.h - bufferstream.h - gzipstream.h - inkscapestream.h - resource.h - stringstream.h - sys.h - http.h - uristream.h - xsltstream.h + # ------- + # Headers + base64stream.h + bufferstream.h + dir-util.h + gzipstream.h + inkscapestream.h + resource.h + resource-manager.h + stringstream.h + sys.h + http.h + uristream.h + xsltstream.h ) +if(WIN32) + # Sources for the inkscape executable on Windows. + list(APPEND io_SRC + registrytool.h + registrytool.cpp + ) +endif() + # add_inkscape_lib(io_LIB "${io_SRC}") add_inkscape_source("${io_SRC}") diff --git a/src/io/command_line_export.h b/src/io/command_line_export.h new file mode 100644 index 000000000..dad0d573c --- /dev/null +++ b/src/io/command_line_export.h @@ -0,0 +1,59 @@ + +// Command line export... should be using normal export. + + +class InkCommandLineExport { + +public: + enum { + EXPORT_PLAIN_SVG, + EXPORT_INKSCAPE_SVG, + EXPORT_PNG, + EXPORT_PS, + EXPORT_EPS, + EXPORT_PDF, + EXPORT_LATEX, + EXPORT_EMF, + EXPORT_WMF, + EXPORT_XAML, + EXPORT_PRINT + } ExportType; + + InkCommandLineExport(ExportType export_type, + Glib::ustring file_name); + ~InkCommandLineExport() {}; + do_export(); + + double export_dpi; + bool export_area; + bool export_area_drawing; + bool export_area_page; + double export_margin; + bool export_snap; + int export_width; // In pixels + int export_hight; // In pixels + Glib::ustring export_id; + bool export_id_only; + bool export_id_hints; + Glib::ustring export_background; + double export_background_opacity; + int export_ps_level; + double export_pdf_level; + bool export_text_to_path; + bool export_ignore_filters; + +private: + ExportType export_type; + Glib::ustring file_name; +} + +/* + 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/io/command_line_output.h b/src/io/command_line_output.h new file mode 100644 index 000000000..31ce2cd47 --- /dev/null +++ b/src/io/command_line_output.h @@ -0,0 +1,58 @@ + +// Command line export... should be using normal export. + +class InkCommandLineExport { + +public: + enum { + EXPORT_PLAIN_SVG, + EXPORT_INKSCAPE_SVG, + EXPORT_PNG, + EXPORT_PS, + EXPORT_EPS, + EXPORT_PDF, + EXPORT_LATEX, + EXPORT_EMF, + EXPORT_WMF, + EXPORT_XAML, + EXPORT_PRINT + } ExportType; + + InkCommandLineExport(ExportType export_type, + Glib::ustring file_name); + ~InkCommandLineExport() {}; + do_export(); + + double export_dpi; + bool export_area; + bool export_area_drawing; + bool export_area_page; + double export_margin; + bool export_snap; + int export_width; // In pixels + int export_hight; // In pixels + Glib::ustring export_id; + bool export_id_only; + bool export_id_hints; + Glib::ustring export_background; + double export_background_opacity; + int export_ps_level; + double export_pdf_level; + bool export_text_to_path; + bool export_ignore_filters; + +private: + ExportType export_type; + Glib::ustring file_name; +} + +/* + 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/io/commandline_io.h b/src/io/commandline_io.h new file mode 100644 index 000000000..31ce2cd47 --- /dev/null +++ b/src/io/commandline_io.h @@ -0,0 +1,58 @@ + +// Command line export... should be using normal export. + +class InkCommandLineExport { + +public: + enum { + EXPORT_PLAIN_SVG, + EXPORT_INKSCAPE_SVG, + EXPORT_PNG, + EXPORT_PS, + EXPORT_EPS, + EXPORT_PDF, + EXPORT_LATEX, + EXPORT_EMF, + EXPORT_WMF, + EXPORT_XAML, + EXPORT_PRINT + } ExportType; + + InkCommandLineExport(ExportType export_type, + Glib::ustring file_name); + ~InkCommandLineExport() {}; + do_export(); + + double export_dpi; + bool export_area; + bool export_area_drawing; + bool export_area_page; + double export_margin; + bool export_snap; + int export_width; // In pixels + int export_hight; // In pixels + Glib::ustring export_id; + bool export_id_only; + bool export_id_hints; + Glib::ustring export_background; + double export_background_opacity; + int export_ps_level; + double export_pdf_level; + bool export_text_to_path; + bool export_ignore_filters; + +private: + ExportType export_type; + Glib::ustring file_name; +} + +/* + 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/io/dir-util.cpp b/src/io/dir-util.cpp new file mode 100644 index 000000000..1a215fa29 --- /dev/null +++ b/src/io/dir-util.cpp @@ -0,0 +1,254 @@ +/** + * @file + * Utility functions for filenames. + */ + +#include +#include +#include +#include +#include "dir-util.h" + +std::string sp_relative_path_from_path( std::string const &path, std::string const &base) +{ + 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; + } + + 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 ( result.empty() ) { + result = path; + } + return result; +} + +char const *sp_extension_from_path(char const *const path) +{ + if (path == nullptr) { + return nullptr; + } + + char const *p = path; + while (*p != '\0') p++; + + while ((p >= path) && (*p != G_DIR_SEPARATOR) && (*p != '.')) p--; + if (* p != '.') return nullptr; + p++; + + return p; +} + + +/* current == "./", parent == "../" */ +static char const dots[] = {'.', '.', G_DIR_SEPARATOR, '\0'}; +static char const *const parent = dots; +static char const *const current = dots + 1; + +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. */ + const char *endp = result + size - 1; + char *rp; + int length; + if (*path == G_DIR_SEPARATOR) + { + if (strlen (path) >= size) + goto erange; + strcpy (result, path); + goto finish; + } + else if (*base != G_DIR_SEPARATOR || !size) + { + errno = EINVAL; + return (nullptr); + } + else if (size == 1) + goto erange; + if (!strcmp (path, ".") || !strcmp (path, current)) + { + if (strlen (base) >= size) + goto erange; + strcpy (result, base); + /* rp points the last char. */ + rp = result + strlen (base) - 1; + if (*rp == G_DIR_SEPARATOR) + *rp = 0; + else + rp++; + /* rp point NULL char */ + if (*++path == G_DIR_SEPARATOR) + { + /* Append G_DIR_SEPARATOR to the tail of path name. */ + *rp++ = G_DIR_SEPARATOR; + if (rp > endp) + goto erange; + *rp = 0; + } + goto finish; + } + bp = base + strlen (base); + if (*(bp - 1) == G_DIR_SEPARATOR) + --bp; + /* up to root. */ + for (pp = path; *pp && *pp == '.';) + { + if (!strncmp (pp, parent, 3)) + { + pp += 3; + while (bp > base && *--bp != G_DIR_SEPARATOR) + ; + } + else if (!strncmp (pp, current, 2)) + { + pp += 2; + } + else if (!strncmp (pp, "..\0", 3)) + { + pp += 2; + while (bp > base && *--bp != G_DIR_SEPARATOR) + ; + } + else + break; + } + /* down to leaf. */ + length = bp - base; + if (length >= static_cast(size)) + goto erange; + strncpy (result, base, length); + rp = result + length; + if (*pp || *(pp - 1) == G_DIR_SEPARATOR || length == 0) + *rp++ = G_DIR_SEPARATOR; + if (rp + strlen (pp) > endp) + goto erange; + strcpy (rp, pp); +finish: + return result; +erange: + errno = ERANGE; + return (nullptr); +} + +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; + + if (*path != G_DIR_SEPARATOR) + { + if (strlen (path) >= size) + goto erange; + strcpy (result, path); + goto finish; + } + else if (*base != G_DIR_SEPARATOR || !size) + { + errno = EINVAL; + return (nullptr); + } + 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; + } + 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); + } + else + *--rp = 0; +finish: + return result; +erange: + errno = ERANGE; + return (nullptr); +} + +char *prepend_current_dir_if_relative(gchar const *uri) +{ + if (!uri) { + return nullptr; + } + + gchar *full_path = (gchar *) g_malloc (1001); + gchar *cwd = g_get_current_dir(); + + gsize bytesRead = 0; + gsize bytesWritten = 0; + GError* error = nullptr; + gchar* cwd_utf8 = g_filename_to_utf8 ( cwd, + -1, + &bytesRead, + &bytesWritten, + &error); + + inkscape_rel2abs (uri, cwd_utf8, full_path, 1000); + gchar *ret = g_strdup (full_path); + g_free (full_path); + g_free (cwd); + 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/io/dir-util.h b/src/io/dir-util.h new file mode 100644 index 000000000..327e1ad5f --- /dev/null +++ b/src/io/dir-util.h @@ -0,0 +1,66 @@ +#ifndef SEEN_DIR_UTIL_H +#define SEEN_DIR_UTIL_H + +/* + * path-util.h + * + * here are functions sp_relative_path & cousins + * maybe they are already implemented in standard libs + * + */ + +#include +#include + +/** + * 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); + +char *prepend_current_dir_if_relative(char const *filename); + + +#endif // !SEEN_DIR_UTIL_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/io/file.h b/src/io/file.h new file mode 100644 index 000000000..c0c44f6ab --- /dev/null +++ b/src/io/file.h @@ -0,0 +1,25 @@ +/* + * File operations (independent of GUI) + * + * Copyright (C) 2018 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#ifndef INK_FILE_IO_H +#define INK_FILE_IO_H + +class SPDocument; + +SPDocument* ink_file_new(const std::string &template = nullptr); +SPDocument* ink_file_open(const Glib::RefPtr& file = Glib::RefPtr()); + +// To do: +// ink_file_save() +// ink_file_export() +// ink_file_import() + + + +#endif // INK_FILE_IO_H diff --git a/src/io/open_file.h b/src/io/open_file.h new file mode 100644 index 000000000..5da25f88e --- /dev/null +++ b/src/io/open_file.h @@ -0,0 +1,12 @@ +/* + * File operations (independent of GUI) + * + * Copyright (C) 2018 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +SPDocument* open_file(const Glib::RefPtr& file = Glib::RefPtr()); + + diff --git a/src/io/registrytool.cpp b/src/io/registrytool.cpp new file mode 100644 index 000000000..89442bc36 --- /dev/null +++ b/src/io/registrytool.cpp @@ -0,0 +1,226 @@ +/* + * Inkscape Registry Tool + * + * Authors: + * Bob Jamison + * + * Copyright (C) 2005-2008 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "registrytool.h" + +#include +#include +#include + +#include + +struct KeyTableEntry +{ + HKEY key; + int strlen; + const char *str; +}; + + + +KeyTableEntry keyTable[] = +{ + { HKEY_CLASSES_ROOT, 18, "HKEY_CLASSES_ROOT\\" }, + { HKEY_CURRENT_CONFIG, 20, "HKEY_CURRENT_CONFIG\\" }, + { HKEY_CURRENT_USER, 18, "HKEY_CURRENT_USER\\" }, + { HKEY_LOCAL_MACHINE, 19, "HKEY_LOCAL_MACHINE\\" }, + { HKEY_USERS, 11, "HKEY_USERS\\" }, + { NULL, 0, NULL } +}; + + +bool RegistryTool::setStringValue(const Glib::ustring &keyNameArg, + const Glib::ustring &valueName, + const Glib::ustring &value) +{ + Glib::ustring keyName = keyNameArg; + bool ret = false; + + HKEY rootKey = HKEY_LOCAL_MACHINE; //default root + //Trim out the root key if necessary + for (KeyTableEntry *entry = keyTable; entry->key; entry++) + { + if (keyName.compare(0, entry->strlen, entry->str)==0) + { + rootKey = entry->key; + keyName = keyName.substr(entry->strlen); + } + } + //printf("trimmed string: '%s'\n", keyName.c_str()); + + //Get or create the key + gunichar2 *keyw = g_utf8_to_utf16(keyName.data(), -1, 0,0,0); + gunichar2 *valuenamew = g_utf8_to_utf16(valueName.data(), -1, 0,0,0); + gunichar2 *valuew = g_utf8_to_utf16(value.data(), -1, 0,0,0); + + HKEY key; + if (RegCreateKeyExW(rootKey, (WCHAR*) keyw, + 0, NULL, REG_OPTION_NON_VOLATILE, + KEY_WRITE, NULL, &key, NULL)) + { + fprintf(stderr, "RegistryTool: Could not create the registry key '%s'\n", keyName.c_str()); + goto fail; + } + + // Set the value + if (RegSetValueExW(key, (WCHAR*) valuenamew, + 0, REG_SZ, (LPBYTE) valuew, (DWORD) (2*value.size() + 2))) + { + fprintf(stderr, "RegistryTool: Could not set the value '%s'\n", value.c_str()); + goto failkey; + } + + ret = true; + + failkey: + RegCloseKey(key); + + fail: + g_free(keyw); + g_free(valuenamew); + return ret; +} + + + +bool RegistryTool::getExeInfo(Glib::ustring &fullPath, + Glib::ustring &path, + Glib::ustring &exeName) +{ + const int pathbuf = 2048; + gunichar2 pathw[pathbuf]; + GetModuleFileNameW(NULL, (WCHAR*) pathw, pathbuf); + + gchar *utf8path = g_utf16_to_utf8(pathw, -1, 0,0,0); + fullPath = utf8path; + g_free(utf8path); + + path = ""; + exeName = ""; + Glib::ustring::size_type pos = fullPath.rfind('\\'); + if (pos != fullPath.npos) + { + path = fullPath.substr(0, pos); + exeName = fullPath.substr(pos+1); + } + + return true; +} + + + +bool RegistryTool::setPathInfo() +{ + Glib::ustring fullPath; + Glib::ustring path; + Glib::ustring exeName; + + if (!getExeInfo(fullPath, path, exeName)) + return false; + + //printf("full:'%s' path:'%s' exe:'%s'\n", + // fullPath.c_str(), path.c_str(), exeName.c_str()); + + Glib::ustring keyName = + "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"; + keyName.append(exeName); + + Glib::ustring valueName = ""; + Glib::ustring value = fullPath; + + if (!setStringValue(keyName, valueName, value)) + return false; + + //add our subdirectories + Glib::ustring appPath = path; + appPath.append("\\python;"); + appPath.append(path); + appPath.append("\\perl"); + valueName = "Path"; + value = appPath; + + if (!setStringValue(keyName, valueName, value)) + return false; + + return true; +} + + +#ifdef TESTREG + + +/* + * Compile this file with + * g++ -DTESTREG registrytool.cpp -o registrytool + * to run these tests. + */ + + + +void testReg() +{ + RegistryTool rt; + char *key = + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\inkscape.exe"; + char const *name = ""; + char const *value = "c:\\inkscape\\inkscape.exe"; + if (!rt.setStringValue(key, name, value)) + { + printf("Test failed\n"); + } + else + { + printf("Test succeeded\n"); + } + name = "Path"; + value = "c:\\inkscape\\python"; + if (!rt.setStringValue(key, name, value)) + { + printf("Test failed\n"); + } + else + { + printf("Test succeeded\n"); + } +} + + +void testPath() +{ + RegistryTool rt; + rt.setPathInfo(); +} + + +int main(int argc, char **argv) +{ + //testReg(); + testPath(); + return 0; +} + +#endif /* TESTREG */ + +//######################################################################## +//# E N D O F F I L E +//######################################################################## diff --git a/src/io/registrytool.h b/src/io/registrytool.h new file mode 100644 index 000000000..335a8bd52 --- /dev/null +++ b/src/io/registrytool.h @@ -0,0 +1,69 @@ +#ifndef SEEN_REGISTRYTOOL_H +#define SEEN_REGISTRYTOOL_H +/* + * Authors: + * Bob Jamison + * + * Copyright (C) 2005 Bob Jamison + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +namespace Glib { + class ustring; +} + +/** + * Inkscape Registry Tool + * + * This simple tool is intended for allowing Inkscape to append subdirectories + * to its path. This will allow extensions and other files to be accesses + * without explicit user intervention. + */ +class RegistryTool +{ +public: + + RegistryTool() + {} + + virtual ~RegistryTool() + {} + + /** + * Set the string value of a key/name registry entry. + */ + bool setStringValue(const Glib::ustring &key, + const Glib::ustring &valueName, + const Glib::ustring &value); + + /** + * Get the full path, directory, and base file name of this running executable. + */ + bool getExeInfo(Glib::ustring &fullPath, + Glib::ustring &path, + Glib::ustring &exeName); + + /** + * Append our subdirectories to the Application Path for this + * application. + */ + bool setPathInfo(); + + +}; + +#endif // SEEN_REGISTRYTOOL_H + diff --git a/src/io/resource-manager.cpp b/src/io/resource-manager.cpp new file mode 100644 index 000000000..6a455a8cc --- /dev/null +++ b/src/io/resource-manager.cpp @@ -0,0 +1,445 @@ +/* + * 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 +#include + +#include "resource-manager.h" + +#include "document.h" +#include "document-undo.h" +#include "verbs.h" + +#include "object/sp-object.h" + +#include "xml/node.h" + +namespace Inkscape { + +static std::vector splitPath( std::string const &path ) +{ + std::vector parts; + + std::string prior; + std::string tmp = path; + while ( !tmp.empty() && (tmp != prior) ) { + prior = tmp; + + parts.push_back( Glib::path_get_basename(tmp) ); + tmp = Glib::path_get_dirname(tmp); + } + if ( !parts.empty() ) { + std::reverse(parts.begin(), parts.end()); + if ( (parts[0] == ".") && (path[0] != '.') ) { + parts.erase(parts.begin()); + } + } + + return parts; +} + +static std::string convertPathToRelative( std::string const &path, std::string const &docbase ) +{ + std::string result = path; + + if ( !path.empty() && Glib::path_is_absolute(path) ) { + // Whack the parts into pieces + + std::vector parts = splitPath(path); + std::vector baseParts = splitPath(docbase); + + // TODO debug g_message("+++++++++++++++++++++++++"); + for ( std::vector::iterator it = parts.begin(); it != parts.end(); ++it ) { + // TODO debug g_message(" [%s]", it->c_str()); + } + // TODO debug g_message(" - - - - - - - - - - - - - - - "); + for ( std::vector::iterator it = baseParts.begin(); it != baseParts.end(); ++it ) { + // TODO debug g_message(" [%s]", it->c_str()); + } + // TODO debug g_message("+++++++++++++++++++++++++"); + + if ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) { + // Both paths have the same root. We can proceed. + while ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) { + parts.erase( parts.begin() ); + baseParts.erase( baseParts.begin() ); + } + + // TODO debug g_message("+++++++++++++++++++++++++"); + for ( std::vector::iterator it = parts.begin(); it != parts.end(); ++it ) { + // TODO debug g_message(" [%s]", it->c_str()); + } + // TODO debug g_message(" - - - - - - - - - - - - - - - "); + for ( std::vector::iterator it = baseParts.begin(); it != baseParts.end(); ++it ) { + // TODO debug g_message(" [%s]", it->c_str()); + } + // TODO debug g_message("+++++++++++++++++++++++++"); + + if ( !parts.empty() ) { + result.clear(); + + for ( size_t i = 0; i < baseParts.size(); ++i ) { + parts.insert(parts.begin(), ".."); + } + result = Glib::build_filename( parts ); + // TODO debug g_message("----> [%s]", result.c_str()); + } + } + } + + return result; +} + + +class ResourceManagerImpl : public ResourceManager { +public: + ResourceManagerImpl(); + ~ResourceManagerImpl() override; + + bool fixupBrokenLinks(SPDocument *doc) override; + + + /** + * 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); + + + /** + * Try to parse href into a local filename using standard methods. + * + * @return true if successful. + */ + bool extractFilepath( Glib::ustring const &href, std::string &uri ); + + /** + * Try to parse href into a local filename using some non-standard methods. + * This means the href is likely invalid and should be rewritten. + * + * @return true if successful. + */ + bool reconstructFilepath( Glib::ustring const &href, std::string &uri ); + + bool searchUpwards( std::string const &base, std::string const &subpath, std::string &dest ); + +protected: +}; + + +ResourceManagerImpl::ResourceManagerImpl() + : ResourceManager() +{ +} + +ResourceManagerImpl::~ResourceManagerImpl() += default; + +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: + try { + uri = Glib::filename_from_uri(href); + // TODO debug g_message(" [%s]", uri.c_str()); + isFile = true; + } catch(Glib::ConvertError e) { + g_warning("%s", e.what().c_str()); + } + } + } 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; +} + +bool ResourceManagerImpl::reconstructFilepath( Glib::ustring const &href, std::string &uri ) +{ + bool isFile = false; + + uri.clear(); + + std::string scheme = Glib::uri_parse_scheme(href); + if ( !scheme.empty() ) { + if ( scheme == "file" ) { + // try to build a relative filename for URIs like "file:image.png" + // they're not standard conformant but not uncommon + Glib::ustring href_new = Glib::ustring(href, 5); + uri = Glib::filename_from_utf8(href_new); + // TODO debug g_message("reconstructed path for '%s' into '%s'", href.c_str(), uri.c_str()); + isFile = true; + } + } + return isFile; +} + + +std::vector ResourceManagerImpl::findBrokenLinks( SPDocument *doc ) +{ + std::vector result; + std::set uniques; + + if ( doc ) { + std::vector images = doc->getResourceList("image"); + for (std::vector::const_iterator it = images.begin(); it != images.end(); ++it) { + Inkscape::XML::Node *ir = (*it)->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.emplace_back(href); + uniques.insert(href); + } + } else { + std::string combined = Glib::build_filename(doc->getBase(), uri); + if ( !Glib::file_test(combined, Glib::FILE_TEST_EXISTS) ) { + result.emplace_back(href); + uniques.insert(href); + } + } + } else if ( reconstructFilepath( href, uri ) ) { + result.emplace_back(href); + uniques.insert(href); + } + } + } + } + + return result; +} + + +std::map ResourceManagerImpl::locateLinks(Glib::ustring const & docbase, std::vector const & brokenLinks) +{ + std::map result; + + + // Note: we use a vector because we want them to stay in order: + std::vector priorLocations; + + Glib::RefPtr recentMgr = Gtk::RecentManager::get_default(); + std::vector< Glib::RefPtr > recentItems = recentMgr->get_items(); + for ( std::vector< Glib::RefPtr >::iterator it = recentItems.begin(); it != recentItems.end(); ++it ) { + Glib::ustring uri = (*it)->get_uri(); + std::string scheme = Glib::uri_parse_scheme(uri); + if ( scheme == "file" ) { + try { + std::string path = Glib::filename_from_uri(uri); + path = Glib::path_get_dirname(path); + if ( std::find(priorLocations.begin(), priorLocations.end(), path) == priorLocations.end() ) { + // TODO debug g_message(" ==>[%s]", path.c_str()); + priorLocations.push_back(path); + } + } catch (Glib::ConvertError e) { + g_warning("%s", e.what().c_str()); + } + } + } + + // 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 ) || reconstructFilepath( *it, uri ) ) { + // We were able to get some path. Check it + std::string origPath = uri; + + 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()); + } + + bool exists = Glib::file_test(uri, Glib::FILE_TEST_EXISTS); + + // search in parent folders + if (!exists) { + exists = searchUpwards( docbase, origPath, uri ); + } + + // Check if the MRU bases point us to it. + if ( !exists ) { + if ( !Glib::path_is_absolute(origPath) ) { + for ( std::vector::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) { + exists = searchUpwards( *it, origPath, uri ); + } + } + } + + if ( exists ) { + if ( Glib::path_is_absolute( uri ) ) { + // TODO debug g_message("Need to convert to relative if possible [%s]", uri.c_str()); + uri = convertPathToRelative( uri, docbase ); + } + + bool isAbsolute = Glib::path_is_absolute( uri ); + Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( uri ) : Glib::filename_to_utf8( uri ); + 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", static_cast(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); + + std::vector images = doc->getResourceList("image"); + for (std::vector::const_iterator it = images.begin(); it != images.end(); ++it) { + Inkscape::XML::Node *ir = (*it)->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", nullptr ); // Remove this attribute + } + + SPObject *updated = doc->getObjectByRepr(ir); + if (updated) { + // force immediate update of dependent attributes + updated->updateRepr(); + } + + changed = true; + } + } + } + if ( changed ) { + DocumentUndo::done( doc, SP_VERB_DIALOG_XML_EDITOR, _("Fixup broken links") ); + } + DocumentUndo::setUndoSensitive(doc, savedUndoState); + } + + return changed; +} + + +bool ResourceManagerImpl::searchUpwards( std::string const &base, std::string const &subpath, std::string &dest ) +{ + bool exists = false; + // TODO debug g_message("............"); + + std::vector parts = splitPath(subpath); + std::vector baseParts = splitPath(base); + + while ( !exists && !baseParts.empty() ) { + std::vector current; + current.insert(current.begin(), parts.begin(), parts.end()); + // TODO debug g_message(" ---{%s}", Glib::build_filename( baseParts ).c_str()); + while ( !exists && !current.empty() ) { + std::vector combined; + combined.insert( combined.end(), baseParts.begin(), baseParts.end() ); + combined.insert( combined.end(), current.begin(), current.end() ); + std::string filepath = Glib::build_filename( combined ); + exists = Glib::file_test(filepath, Glib::FILE_TEST_EXISTS); + // TODO debug g_message(" ...[%s] %s", filepath.c_str(), (exists ? "XXX" : "")); + if ( exists ) { + dest = filepath; + } + current.erase( current.begin() ); + } + baseParts.pop_back(); + } + + return exists; +} + + +static ResourceManagerImpl* theInstance = nullptr; + +ResourceManager::ResourceManager() + : Glib::Object() +{ +} + +ResourceManager::~ResourceManager() = default; + +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/io/resource-manager.h b/src/io/resource-manager.h new file mode 100644 index 000000000..b4d88c7e6 --- /dev/null +++ b/src/io/resource-manager.h @@ -0,0 +1,49 @@ +/* + * Inkscape::ResourceManager - Manages external resources such as image and css files. + * + * Copyright 2011 Jon A. Cruz + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#ifndef SEEN_INKSCAPE_RESOURCE_MANAGER_H +#define SEEN_INKSCAPE_RESOURCE_MANAGER_H + +#include + +class SPDocument; + +namespace Inkscape { + +class ResourceManager : public Glib::Object { + +public: + static ResourceManager& getManager(); + + virtual bool fixupBrokenLinks(SPDocument *doc) = 0; + +protected: + ResourceManager(); + ~ResourceManager() override; + +private: + ResourceManager(ResourceManager const &) = delete; // no copy + void operator=(ResourceManager const &) = delete; // 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/main-cmdlinexact.cpp b/src/main-cmdlinexact.cpp index a30251275..3d3eb98db 100644 --- a/src/main-cmdlinexact.cpp +++ b/src/main-cmdlinexact.cpp @@ -31,7 +31,6 @@ #include "file.h" #include "inkscape.h" #include "preferences.h" -#include "resource-manager.h" #include "selection.h" #include "verbs.h" #include "yaml.h" @@ -43,6 +42,7 @@ #include "helper/action.h" #include "helper/png-write.h" +#include "io/resource-manager.h" #include "io/sys.h" #include "object/sp-namedview.h" diff --git a/src/main.cpp b/src/main.cpp index 1bbb01efb..5de51a60a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,7 @@ #ifdef _WIN32 #include -#include "registrytool.h" +#include "io/registrytool.h" #endif // _WIN32 #include "inkscape.h" diff --git a/src/object/sp-text.cpp.orig b/src/object/sp-text.cpp.orig new file mode 100644 index 000000000..4739f6b12 --- /dev/null +++ b/src/object/sp-text.cpp.orig @@ -0,0 +1,1324 @@ +/* + * SVG and implementation + * + * Author: + * Lauris Kaplinski + * bulia byak + * Jon A. Cruz + * Abhishek Sharma + * + * Copyright (C) 1999-2002 Lauris Kaplinski + * Copyright (C) 2000-2001 Ximian, Inc. + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +/* + * fixme: + * + * These subcomponents should not be items, or alternately + * we have to invent set of flags to mark, whether standard + * attributes are applicable to given item (I even like this + * idea somewhat - Lauris) + * + */ + +#include <2geom/affine.h> +#include +#include + +#include +#include +#include "svg/svg.h" +#include "display/drawing-text.h" +#include "attributes.h" +#include "document.h" +#include "preferences.h" +#include "desktop.h" +#include "sp-namedview.h" +#include "inkscape.h" +#include "xml/quote.h" +#include "mod360.h" + +#include "sp-title.h" +#include "sp-desc.h" +#include "sp-text.h" + +#include "sp-shape.h" +#include "sp-textpath.h" +#include "sp-tref.h" +#include "sp-tspan.h" + +#include "text-editing.h" + +// For SVG 2 text flow +#include "livarot/Shape.h" +#include "display/curve.h" + +/*##################################################### +# SPTEXT +#####################################################*/ +SPText::SPText() : SPItem() { +} + +SPText::~SPText() = default; + +void SPText::build(SPDocument *doc, Inkscape::XML::Node *repr) { + this->readAttr( "x" ); + this->readAttr( "y" ); + this->readAttr( "dx" ); + this->readAttr( "dy" ); + this->readAttr( "rotate" ); + + // textLength and friends + this->readAttr( "textLength" ); + this->readAttr( "lengthAdjust" ); + + SPItem::build(doc, repr); + + this->readAttr( "sodipodi:linespacing" ); // has to happen after the styles are read +} + +void SPText::release() { + SPItem::release(); +} + +void SPText::set(unsigned int key, const gchar* value) { + //std::cout << "SPText::set: " << sp_attribute_name( key ) << ": " << (value?value:"Null") << std::endl; + + if (this->attributes.readSingleAttribute(key, value, style, &viewport)) { + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); + } else { + switch (key) { + case SP_ATTR_SODIPODI_LINESPACING: + // convert deprecated tag to css... but only if 'line-height' missing. + if (value && !this->style->line_height.set) { + this->style->line_height.set = TRUE; + this->style->line_height.inherit = FALSE; + this->style->line_height.normal = FALSE; + this->style->line_height.unit = SP_CSS_UNIT_PERCENT; + this->style->line_height.value = this->style->line_height.computed = sp_svg_read_percentage (value, 1.0); + } + // Remove deprecated attribute + this->getRepr()->setAttribute("sodipodi:linespacing", nullptr); + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); + break; + + default: + SPItem::set(key, value); + break; + } + } +} + +void SPText::child_added(Inkscape::XML::Node *rch, Inkscape::XML::Node *ref) { + SPItem::child_added(rch, ref); + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_CONTENT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); +} + +void SPText::remove_child(Inkscape::XML::Node *rch) { + SPItem::remove_child(rch); + + this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_CONTENT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG); +} + + +void SPText::update(SPCtx *ctx, guint flags) { + + unsigned childflags = (flags & SP_OBJECT_MODIFIED_CASCADE); + if (flags & SP_OBJECT_MODIFIED_FLAG) { + childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + // Create temporary list of children + std::vector l; + for (auto& child: children) { + sp_object_ref(&child, this); + l.push_back(&child); + } + + for (auto child:l) { + if (childflags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + /* fixme: Do we need transform? */ + child->updateDisplay(ctx, childflags); + } + sp_object_unref(child, this); + } + + // update ourselves after updating children + SPItem::update(ctx, flags); + + if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG | + SP_OBJECT_CHILD_MODIFIED_FLAG | + SP_TEXT_LAYOUT_MODIFIED_FLAG ) ) + { + + SPItemCtx const *ictx = reinterpret_cast(ctx); + + double const w = ictx->viewport.width(); + double const h = ictx->viewport.height(); + double const em = style->font_size.computed; + double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype. + + attributes.update( em, ex, w, h ); + + // Set inline_size computed value if necessary (i.e. if unit is %). + if (style->inline_size.set) { + if (style->inline_size.unit == SP_CSS_UNIT_PERCENT) { + if (style->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB || + style->writing_mode.computed == SP_CSS_WRITING_MODE_RL_TB) { + style->inline_size.computed = style->inline_size.value * ictx->viewport.width(); + } else { + style->inline_size.computed = style->inline_size.value * ictx->viewport.height(); + } + } + } + + /* fixme: It is not nice to have it here, but otherwise children content changes does not work */ + /* fixme: Even now it may not work, as we are delayed */ + /* fixme: So check modification flag everywhere immediate state is used */ + this->rebuildLayout(); + + Geom::OptRect paintbox = this->geometricBounds(); + + for (SPItemView* v = this->display; v != nullptr; v = v->next) { + Inkscape::DrawingGroup *g = dynamic_cast(v->arenaitem); + this->_clearFlow(g); + g->setStyle(this->style, this->parent->style); + // pass the bbox of this as paintbox (used for paintserver fills) + this->layout.show(g, paintbox); + } + } +} + +void SPText::modified(guint flags) { +// SPItem::onModified(flags); + + guint cflags = (flags & SP_OBJECT_MODIFIED_CASCADE); + + if (flags & SP_OBJECT_MODIFIED_FLAG) { + cflags |= SP_OBJECT_PARENT_MODIFIED_FLAG; + } + + // FIXME: all that we need to do here is to call setStyle, to set the changed + // style, but there's no easy way to access the drawing glyphs or texts corresponding to a + // text this. Therefore we do here the same as in _update, that is, destroy all items + // and create new ones. This is probably quite wasteful. + if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG )) { + Geom::OptRect paintbox = this->geometricBounds(); + + for (SPItemView* v = this->display; v != nullptr; v = v->next) { + Inkscape::DrawingGroup *g = dynamic_cast(v->arenaitem); + this->_clearFlow(g); + g->setStyle(this->style, this->parent->style); + this->layout.show(g, paintbox); + } + } + + // Create temporary list of children + std::vector l; + for (auto& child: children) { + sp_object_ref(&child, this); + l.push_back(&child); + } + + for (auto child:l) { + if (cflags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) { + child->emitModified(cflags); + } + sp_object_unref(child, this); + } +} + +Inkscape::XML::Node *SPText::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) { + if (flags & SP_OBJECT_WRITE_BUILD) { + if (!repr) { + repr = xml_doc->createElement("svg:text"); + } + + std::vector l; + + for (auto& child: children) { + if (SP_IS_TITLE(&child) || SP_IS_DESC(&child)) { + continue; + } + + Inkscape::XML::Node *crepr = nullptr; + + if (SP_IS_STRING(&child)) { + crepr = xml_doc->createTextNode(SP_STRING(&child)->string.c_str()); + } else { + crepr = child.updateRepr(xml_doc, nullptr, flags); + } + + if (crepr) { + l.push_back(crepr); + } + } + + for (auto i=l.rbegin();i!=l.rend();++i) { + repr->addChild(*i, nullptr); + Inkscape::GC::release(*i); + } + } else { + for (auto& child: children) { + if (SP_IS_TITLE(&child) || SP_IS_DESC(&child)) { + continue; + } + + if (SP_IS_STRING(&child)) { + child.getRepr()->setContent(SP_STRING(&child)->string.c_str()); + } else { + child.updateRepr(flags); + } + } + } + + this->attributes.writeTo(repr); + this->rebuildLayout(); // copied from update(), see LP Bug 1339305 + + SPItem::write(xml_doc, repr, flags); + + return repr; +} + +Geom::OptRect SPText::bbox(Geom::Affine const &transform, SPItem::BBoxType type) const { + Geom::OptRect bbox = SP_TEXT(this)->layout.bounds(transform); + + // FIXME this code is incorrect + if (bbox && type == SPItem::VISUAL_BBOX && !this->style->stroke.isNone()) { + double scale = transform.descrim(); + bbox->expandBy(0.5 * this->style->stroke_width.computed * scale); + } + + return bbox; +} + +Inkscape::DrawingItem* SPText::show(Inkscape::Drawing &drawing, unsigned /*key*/, unsigned /*flags*/) { + Inkscape::DrawingGroup *flowed = new Inkscape::DrawingGroup(drawing); + flowed->setPickChildren(false); + flowed->setStyle(this->style, this->parent->style); + + // pass the bbox of the text object as paintbox (used for paintserver fills) + this->layout.show(flowed, this->geometricBounds()); + + return flowed; +} + + +void SPText::hide(unsigned int key) { + for (SPItemView* v = this->display; v != nullptr; v = v->next) { + if (v->key == key) { + Inkscape::DrawingGroup *g = dynamic_cast(v->arenaitem); + this->_clearFlow(g); + } + } +} + +const char* SPText::displayName() const { + return _("Text"); +} + +gchar* SPText::description() const { + + SPStyle *style = this->style; + + char *n = xml_quote_strdup( style->font_family.value ); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + int unit = prefs->getInt("/options/font/unitType", SP_CSS_UNIT_PT); + Inkscape::Util::Quantity q = Inkscape::Util::Quantity(style->font_size.computed, "px"); + q.quantity *= this->i2doc_affine().descrim(); + Glib::ustring xs = q.string(sp_style_get_css_unit_string(unit)); + + char const *trunc = ""; + Inkscape::Text::Layout const *layout = te_get_layout((SPItem *) this); + + if (layout && layout->inputTruncated()) { + trunc = _(" [truncated]"); + } + + char *ret = ( SP_IS_TEXT_TEXTPATH(this) + ? g_strdup_printf(_("on path%s (%s, %s)"), trunc, n, xs.c_str()) + : g_strdup_printf(_("%s (%s, %s)"), trunc, n, xs.c_str()) ); + return ret; +} + +void SPText::snappoints(std::vector &p, Inkscape::SnapPreferences const *snapprefs) const { + if (snapprefs->isTargetSnappable(Inkscape::SNAPTARGET_TEXT_BASELINE)) { + // Choose a point on the baseline for snapping from or to, with the horizontal position + // of this point depending on the text alignment (left vs. right) + Inkscape::Text::Layout const *layout = te_get_layout(this); + + if (layout != nullptr && layout->outputExists()) { + boost::optional pt = layout->baselineAnchorPoint(); + + if (pt) { + p.emplace_back((*pt) * this->i2dt_affine(), Inkscape::SNAPSOURCE_TEXT_ANCHOR, Inkscape::SNAPTARGET_TEXT_ANCHOR); + } + } + } +} + +Geom::Affine SPText::set_transform(Geom::Affine const &xform) { + // we cannot optimize textpath because changing its fontsize will break its match to the path + if (SP_IS_TEXT_TEXTPATH (this)) { + if (!this->_optimizeTextpathText) { + return xform; + } else { + this->_optimizeTextpathText = false; + } + } + + // we cannot optimize text with textLength because it may show different size than specified + if (this->attributes.getTextLength()->_set) + return xform; + + /* This function takes care of scaling & translation only, we return whatever parts we can't + handle. */ + +// TODO: pjrm tried to use fontsize_expansion(xform) here and it works for text in that font size +// is scaled more intuitively when scaling non-uniformly; however this necessitated using +// fontsize_expansion instead of expansion in other places too, where it was not appropriate +// (e.g. it broke stroke width on copy/pasting of style from horizontally stretched to vertically +// stretched shape). Using fontsize_expansion only here broke setting the style via font +// dialog. This needs to be investigated further. + double const ex = xform.descrim(); + if (ex == 0) { + return xform; + } + + Geom::Affine ret(Geom::Affine(xform).withoutTranslation()); + ret[0] /= ex; + ret[1] /= ex; + ret[2] /= ex; + ret[3] /= ex; + + // Adjust x/y, dx/dy + this->_adjustCoordsRecursive (this, xform * ret.inverse(), ex); + + // Adjust font size + this->_adjustFontsizeRecursive (this, ex); + + // Adjust stroke width + this->adjust_stroke_width_recursive (ex); + + // Adjust pattern fill + this->adjust_pattern(xform * ret.inverse()); + + // Adjust gradient fill + this->adjust_gradient(xform * ret.inverse()); + + return ret; +} + +void SPText::print(SPPrintContext *ctx) { + Geom::OptRect pbox, bbox, dbox; + pbox = this->geometricBounds(); + bbox = this->desktopVisualBounds(); + dbox = Geom::Rect::from_xywh(Geom::Point(0,0), this->document->getDimensions()); + + Geom::Affine const ctm (this->i2dt_affine()); + + this->layout.print(ctx,pbox,dbox,bbox,ctm); +} + +/* + * Member functions + */ + +void SPText::_buildLayoutInit() +{ + + layout.strut.reset(); + layout.wrap_mode = Inkscape::Text::Layout::WRAP_NONE; // Default to SVG 1.1 + + if (style) { + + // Strut + font_instance *font = font_factory::Default()->FaceFromStyle( style ); + if (font) { + font->FontMetrics(layout.strut.ascent, layout.strut.descent, layout.strut.xheight); + font->Unref(); + } + layout.strut *= style->font_size.computed; + if (style->line_height.normal ) { + layout.strut.computeEffective( Inkscape::Text::Layout::LINE_HEIGHT_NORMAL ); + } else if (style->line_height.unit == SP_CSS_UNIT_NONE) { + layout.strut.computeEffective( style->line_height.computed ); + } else { + if( style->font_size.computed > 0.0 ) { + layout.strut.computeEffective( style->line_height.computed/style->font_size.computed ); + } + } + + + // To do: follow SPItem clip_ref/mask_ref code + if (style->shape_inside.set ) { + + layout.wrap_mode = Inkscape::Text::Layout::WRAP_SHAPE_INSIDE; + + // Find union of all exclusion shapes + Shape *exclusion_shape = nullptr; + if(style->shape_subtract.set) { + exclusion_shape = _buildExclusionShape(); + } + + // Extract out shapes (a comma separated list of urls) + Glib::ustring shapeInside_value = style->shape_inside.value; + std::vector shapes_url = Glib::Regex::split_simple(" ", shapeInside_value); + for (unsigned int i=0; i(document->getObjectById( shape_url )); + if ( shape ) { + + // This code adapted from sp-flowregion.cpp: GetDest() + if (!(shape->_curve)) { + shape->set_shape(); + } + SPCurve *curve = shape->getCurve(); + + if ( curve ) { + Path *temp = new Path; + Path *padded = new Path; + temp->LoadPathVector( curve->get_pathvector(), shape->transform, true ); + if( style->shape_padding.set ) { + // std::cout << " padding: " << style->shape_padding.computed << std::endl; + temp->OutsideOutline ( padded, style->shape_padding.computed, join_round, butt_straight, 20.0 ); + } else { + // std::cout << " no padding" << std::endl; + padded->Copy( temp ); + } + padded->Convert( 0.25 ); // Convert to polyline + Shape* sh = new Shape; + padded->Fill( sh, 0 ); + // for( unsigned i = 0; i < temp->pts.size(); ++i ) { + // std::cout << " ........ " << temp->pts[i].p << std::endl; + // } + // std::cout << " ...... shape: " << sh->numberOfPoints() << std::endl; + Shape *uncross = new Shape; + uncross->ConvertToShape( sh ); + + // Subtract exclusion shape + if(style->shape_subtract.set) { + Shape *copy = new Shape; + if (exclusion_shape && exclusion_shape->hasEdges()) { + copy->Booleen(uncross, const_cast(exclusion_shape), bool_op_diff); + } else { + copy->Copy(uncross); + } + layout.appendWrapShape( copy ); + //delete exclusion_shape; + continue; + } + + layout.appendWrapShape( uncross ); + + delete temp; + delete padded; + delete sh; + // delete uncross; + } else { + std::cerr << "SPText::_buildLayoutInit(): Failed to get curve." << std::endl; + } + } else { + std::cerr << "SPText::_buildLayoutInit(): Failed to find shape." << std::endl; + } + } + } + + } else if (style->inline_size.set) { + + layout.wrap_mode = Inkscape::Text::Layout::WRAP_INLINE_SIZE; + + // If both shape_inside and inline_size are set, shape_inside wins out. + + // We construct a rectangle with one dimension set by the computed value of 'inline-size' + // and the other dimension set to infinity. Text is laid out starting at the 'x' and 'y' + // attribute values. This is handled elsewhere. + + double inline_size = style->inline_size.computed; + unsigned mode = style->writing_mode.computed; + unsigned anchor = style->text_anchor.computed; + unsigned direction = style->direction.computed; + + Geom::Rect frame; + if (mode == SP_CSS_WRITING_MODE_LR_TB || + mode == SP_CSS_WRITING_MODE_RL_TB) { + // horizontal + frame = Geom::Rect::from_xywh(attributes.firstXY()[Geom::X], -100000, inline_size, 200000); + if (anchor == SP_CSS_TEXT_ANCHOR_MIDDLE) { + frame *= Geom::Translate (-inline_size/2.0, 0 ); + } else if ( (direction == SP_CSS_DIRECTION_LTR && anchor == SP_CSS_TEXT_ANCHOR_END ) || + (direction == SP_CSS_DIRECTION_RTL && anchor == SP_CSS_TEXT_ANCHOR_START) ) { + frame *= Geom::Translate (-inline_size, 0); + } + } else { + // vertical + frame = Geom::Rect::from_xywh(-100000, attributes.firstXY()[Geom::Y], 200000, inline_size); + if (anchor == SP_CSS_TEXT_ANCHOR_MIDDLE) { + frame *= Geom::Translate (0, -inline_size/2.0); + } else if (anchor == SP_CSS_TEXT_ANCHOR_END) { + frame *= Geom::Translate (0, -inline_size); + } + } + // std::cout << " inline_size frame: " << frame << std::endl; + + Shape *shape = new Shape; + shape->Reset(); + int v0 = shape->AddPoint(frame.corner(0)); + int v1 = shape->AddPoint(frame.corner(1)); + int v2 = shape->AddPoint(frame.corner(2)); + int v3 = shape->AddPoint(frame.corner(3)); + shape->AddEdge(v0, v1); + shape->AddEdge(v1, v2); + shape->AddEdge(v2, v3); + shape->AddEdge(v3, v0); + Shape *uncross = new Shape; + uncross->ConvertToShape( shape ); + + layout.appendWrapShape( uncross ); + + delete shape; + } + + } // if (style) +} + +unsigned SPText::_buildLayoutInput(SPObject *object, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_optional_attrs, unsigned parent_attrs_offset, bool in_textpath) +{ + unsigned length = 0; + int child_attrs_offset = 0; + Inkscape::Text::Layout::OptionalTextTagAttrs optional_attrs; + + // Per SVG spec, an object with 'display:none' doesn't contribute to text layout. + if (object->style->display.computed == SP_CSS_DISPLAY_NONE) { + return 0; + } + + if (SP_IS_TEXT(object)) { + SP_TEXT(object)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, true, true); + + // SVG 2 Text wrapping + if (layout.wrap_mode == Inkscape::Text::Layout::WRAP_SHAPE_INSIDE) { + + // 'x' and 'y' attributes are always ignored. + optional_attrs.x.clear(); + optional_attrs.y.clear(); + + } else if (layout.wrap_mode == Inkscape::Text::Layout::WRAP_INLINE_SIZE) { + + // For horizontal text: + // 'x' is used to calculate the left/right edges of the rectangle but is not + // needed later. If not deleted here, it will cause an incorrect positioning + // of the first line. + // 'y' is used to determine where the first line box is located and is needed + // during the output stage. + // For vertical text: + // Follow above but exchange 'x' and 'y'. + // The SVG 2 spec currently says use the 'x' and 'y' from the element, + // if not defined in the element, use the 'x' and 'y' from the first child. + // We only look at the element. (Doing otherwise means tracking if + // we've found 'x' and 'y' and then creating the Shape at the end.) + if (style->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB || + style->writing_mode.computed == SP_CSS_WRITING_MODE_RL_TB) { + // Horizontal text + optional_attrs.x.clear(); + if (optional_attrs.y.size() > 0) { + optional_attrs.y.resize(1); // Keep only first + } + } else { + // Vertical text + if (optional_attrs.x.size() > 0) { + optional_attrs.x.resize(1); // Keep only first + } + optional_attrs.y.clear(); + } + } + + // set textLength on the entire layout, see note in TNG-Layout.h + if (SP_TEXT(object)->attributes.getTextLength()->_set) { + layout.textLength._set = true; + layout.textLength.value = SP_TEXT(object)->attributes.getTextLength()->value; + layout.textLength.computed = SP_TEXT(object)->attributes.getTextLength()->computed; + layout.textLength.unit = SP_TEXT(object)->attributes.getTextLength()->unit; + layout.lengthAdjust = (Inkscape::Text::Layout::LengthAdjust) SP_TEXT(object)->attributes.getLengthAdjust(); + } + } + + else if (SP_IS_TSPAN(object)) { + SPTSpan *tspan = SP_TSPAN(object); + // x, y attributes are stripped from some tspans marked with role="line" as we do our own line layout. + // This should be checked carefully, as it can undo line layout in imported SVG files. + bool use_xy = !in_textpath && (tspan->role == SP_TSPAN_ROLE_UNSPECIFIED || !tspan->attributes.singleXYCoordinates()); + tspan->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, true); + + // SVG 2 Text wrapping: see comment above. + if (layout.wrap_mode == Inkscape::Text::Layout::WRAP_SHAPE_INSIDE || + layout.wrap_mode == Inkscape::Text::Layout::WRAP_INLINE_SIZE) { + + // 'x' and 'y' attributes are always ignored. + optional_attrs.x.clear(); + optional_attrs.y.clear(); + } + } + + else if (SP_IS_TREF(object)) { + SP_TREF(object)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, true, true); + } + + else if (SP_IS_TEXTPATH(object)) { + in_textpath = true; + SP_TEXTPATH(object)->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, false, true); + optional_attrs.x.clear(); + optional_attrs.y.clear(); + } + + else { + optional_attrs = parent_optional_attrs; + child_attrs_offset = parent_attrs_offset; + } + + + if (SP_IS_TSPAN(object)) { + if (SP_TSPAN(object)->role != SP_TSPAN_ROLE_UNSPECIFIED) { + // we need to allow the first line not to have role=line, but still set the source_cookie to the right value + SPObject *prev_object = object->getPrev(); + if (prev_object && SP_IS_TSPAN(prev_object)) { + if (!layout.inputExists()) { + layout.appendText("", prev_object->style, prev_object, &optional_attrs); + } + layout.appendControlCode(Inkscape::Text::Layout::PARAGRAPH_BREAK, prev_object); + } + if (!object->hasChildren()) { + layout.appendText("", object->style, object, &optional_attrs); + } + length++; // interpreting line breaks as a character for the purposes of x/y/etc attributes + // is a liberal interpretation of the svg spec, but a strict reading would mean + // that if the first line is empty the second line would take its place at the + // start position. Very confusing. + child_attrs_offset--; + } + } + + for (auto& child: object->children) { + SPString *str = dynamic_cast(&child); + if (str) { + Glib::ustring const &string = str->string; + // std::cout << " Appending: >" << string << "<" << std::endl; + layout.appendText(string, object->style, &child, &optional_attrs, child_attrs_offset + length); + length += string.length(); + } else if (!sp_repr_is_meta_element(child.getRepr())) { + /* ^^^^ XML Tree being directly used here while it shouldn't be.*/ + length += _buildLayoutInput(&child, optional_attrs, child_attrs_offset + length, in_textpath); + } + } + + return length; +} + +Shape* SPText::_buildExclusionShape() const +{ + Shape *result = new Shape(); // Union of all exlusion shapes + Shape *shape_temp = new Shape(); + + Glib::ustring shapeSubtract_value = style->shape_subtract.value; + + // Extract out shapes (a comma separated list of urls) + std::vector shapes_url = Glib::Regex::split_simple(" ", shapeSubtract_value); + + for(unsigned int i=0; i(document->getObjectById( shape_url )); + if ( shape ) { + // This code adapted from sp-flowregion.cpp: GetDest() + if (!(shape->_curve)) { + shape->set_shape(); + } + SPCurve *curve = shape->getCurve(); + + if ( curve ) { + Path *temp = new Path; + Path *margin = new Path; + temp->LoadPathVector( curve->get_pathvector(), shape->transform, true ); + + if( shape->style->shape_margin.set ) { + temp->OutsideOutline ( margin, -shape->style->shape_margin.computed, join_round, butt_straight, 20.0 ); + } else { + margin->Copy( temp ); + } + + margin->Convert( 0.25 ); // Convert to polyline + Shape* sh = new Shape; + margin->Fill( sh, 0 ); + + Shape *uncross = new Shape; + uncross->ConvertToShape( sh ); + + if (result->hasEdges()) { + shape_temp->Booleen(result, uncross, bool_op_union); + std::swap(result, shape_temp); + } else { + result->Copy(uncross); + } + } + } + } + } + return result; +} + +void SPText::rebuildLayout() +{ + layout.clear(); + _buildLayoutInit(); + + Inkscape::Text::Layout::OptionalTextTagAttrs optional_attrs; + _buildLayoutInput(this, optional_attrs, 0, false); + + layout.calculateFlow(); + + for (auto& child: children) { + if (SP_IS_TEXTPATH(&child)) { + SPTextPath const *textpath = SP_TEXTPATH(&child); + if (textpath->originalPath != nullptr) { +#if DEBUG_TEXTLAYOUT_DUMPASTEXT + g_print("%s", layout.dumpAsText().c_str()); +#endif + layout.fitToPathAlign(textpath->startOffset, *textpath->originalPath); + } + } + } +#if DEBUG_TEXTLAYOUT_DUMPASTEXT + g_print("%s", layout.dumpAsText().c_str()); +#endif + + // set the x,y attributes on role:line spans + for (auto& child: children) { + if (SP_IS_TSPAN(&child)) { + SPTSpan *tspan = SP_TSPAN(&child); + if ( (tspan->role != SP_TSPAN_ROLE_UNSPECIFIED) + && tspan->attributes.singleXYCoordinates() ) { + Inkscape::Text::Layout::iterator iter = layout.sourceToIterator(tspan); + Geom::Point anchor_point = layout.chunkAnchorPoint(iter); + tspan->attributes.setFirstXY(anchor_point); + } + } + } +} + + +void SPText::_adjustFontsizeRecursive(SPItem *item, double ex, bool is_root) +{ + SPStyle *style = item->style; + + if (style && !Geom::are_near(ex, 1.0)) { + if (!style->font_size.set && is_root) { + style->font_size.set = 1; + } + style->font_size.type = SP_FONT_SIZE_LENGTH; + style->font_size.computed *= ex; + style->letter_spacing.computed *= ex; + style->word_spacing.computed *= ex; + if (style->line_height.unit != SP_CSS_UNIT_NONE && + style->line_height.unit != SP_CSS_UNIT_PERCENT && + style->line_height.unit != SP_CSS_UNIT_EM && + style->line_height.unit != SP_CSS_UNIT_EX) { + // No unit on 'line-height' property has special behavior. + style->line_height.computed *= ex; + } + item->updateRepr(); + } + + for(auto& o: item->children) { + if (SP_IS_ITEM(&o)) + _adjustFontsizeRecursive(SP_ITEM(&o), ex, false); + } +} + +void SPText::_adjustCoordsRecursive(SPItem *item, Geom::Affine const &m, double ex, bool is_root) +{ + if (SP_IS_TSPAN(item)) + SP_TSPAN(item)->attributes.transform(m, ex, ex, is_root); + // it doesn't matter if we change the x,y for role=line spans because we'll just overwrite them anyway + else if (SP_IS_TEXT(item)) + SP_TEXT(item)->attributes.transform(m, ex, ex, is_root); + else if (SP_IS_TEXTPATH(item)) + SP_TEXTPATH(item)->attributes.transform(m, ex, ex, is_root); + else if (SP_IS_TREF(item)) { + SP_TREF(item)->attributes.transform(m, ex, ex, is_root); + } + + for(auto& o: item->children) { + if (SP_IS_ITEM(&o)) + _adjustCoordsRecursive(SP_ITEM(&o), m, ex, false); + } +} + + +void SPText::_clearFlow(Inkscape::DrawingGroup *in_arena) +{ + in_arena->clearChildren(); +} + + +/* + * TextTagAttributes implementation + */ + +// Not used. +// void TextTagAttributes::readFrom(Inkscape::XML::Node const *node) +// { +// readSingleAttribute(SP_ATTR_X, node->attribute("x")); +// readSingleAttribute(SP_ATTR_Y, node->attribute("y")); +// readSingleAttribute(SP_ATTR_DX, node->attribute("dx")); +// readSingleAttribute(SP_ATTR_DY, node->attribute("dy")); +// readSingleAttribute(SP_ATTR_ROTATE, node->attribute("rotate")); +// readSingleAttribute(SP_ATTR_TEXTLENGTH, node->attribute("textLength")); +// readSingleAttribute(SP_ATTR_LENGTHADJUST, node->attribute("lengthAdjust")); +// } + +bool TextTagAttributes::readSingleAttribute(unsigned key, gchar const *value, SPStyle const *style, Geom::Rect const *viewport) +{ + // std::cout << "TextTagAttributes::readSingleAttribute: key: " << key + // << " value: " << (value?value:"Null") << std::endl; + std::vector *attr_vector; + bool update_x = false; + bool update_y = false; + switch (key) { + case SP_ATTR_X: attr_vector = &attributes.x; update_x = true; break; + case SP_ATTR_Y: attr_vector = &attributes.y; update_y = true; break; + case SP_ATTR_DX: attr_vector = &attributes.dx; update_x = true; break; + case SP_ATTR_DY: attr_vector = &attributes.dy; update_y = true; break; + case SP_ATTR_ROTATE: attr_vector = &attributes.rotate; break; + case SP_ATTR_TEXTLENGTH: + attributes.textLength.readOrUnset(value); + return true; + break; + case SP_ATTR_LENGTHADJUST: + attributes.lengthAdjust = (value && !strcmp(value, "spacingAndGlyphs")? + Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS : + Inkscape::Text::Layout::LENGTHADJUST_SPACING); // default is "spacing" + return true; + break; + default: return false; + } + + // FIXME: sp_svg_length_list_read() amalgamates repeated separators. This prevents unset values. + *attr_vector = sp_svg_length_list_read(value); + + if( (update_x || update_y) && style != nullptr && viewport != nullptr ) { + double const w = viewport->width(); + double const h = viewport->height(); + double const em = style->font_size.computed; + double const ex = em * 0.5; + for(std::vector::iterator it = attr_vector->begin(); it != attr_vector->end(); ++it) { + if( update_x ) + it->update( em, ex, w ); + if( update_y ) + it->update( em, ex, h ); + } + } + return true; +} + +void TextTagAttributes::writeTo(Inkscape::XML::Node *node) const +{ + writeSingleAttributeVector(node, "x", attributes.x); + writeSingleAttributeVector(node, "y", attributes.y); + writeSingleAttributeVector(node, "dx", attributes.dx); + writeSingleAttributeVector(node, "dy", attributes.dy); + writeSingleAttributeVector(node, "rotate", attributes.rotate); + + writeSingleAttributeLength(node, "textLength", attributes.textLength); + + if (attributes.textLength._set) { + if (attributes.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACING) { + node->setAttribute("lengthAdjust", "spacing"); + } else if (attributes.lengthAdjust == Inkscape::Text::Layout::LENGTHADJUST_SPACINGANDGLYPHS) { + node->setAttribute("lengthAdjust", "spacingAndGlyphs"); + } + } +} + +void TextTagAttributes::update( double em, double ex, double w, double h ) +{ + for(std::vector::iterator it = attributes.x.begin(); it != attributes.x.end(); ++it) { + it->update( em, ex, w ); + } + for(std::vector::iterator it = attributes.y.begin(); it != attributes.y.end(); ++it) { + it->update( em, ex, h ); + } + for(std::vector::iterator it = attributes.dx.begin(); it != attributes.dx.end(); ++it) { + it->update( em, ex, w ); + } + for(std::vector::iterator it = attributes.dy.begin(); it != attributes.dy.end(); ++it) { + it->update( em, ex, h ); + } +} + +void TextTagAttributes::writeSingleAttributeLength(Inkscape::XML::Node *node, gchar const *key, const SVGLength &length) +{ + if (length._set) { + node->setAttribute(key, length.write().c_str()); + } else + node->setAttribute(key, nullptr); +} + +void TextTagAttributes::writeSingleAttributeVector(Inkscape::XML::Node *node, gchar const *key, std::vector const &attr_vector) +{ + if (attr_vector.empty()) + node->setAttribute(key, nullptr); + else { + Glib::ustring string; + + // FIXME: this has no concept of unset values because sp_svg_length_list_read() can't read them back in + for (std::vector::const_iterator it = attr_vector.begin() ; it != attr_vector.end() ; ++it) { + if (!string.empty()) string += ' '; + string += it->write(); + } + node->setAttribute(key, string.c_str()); + } +} + +bool TextTagAttributes::singleXYCoordinates() const +{ + return attributes.x.size() <= 1 && attributes.y.size() <= 1; +} + +bool TextTagAttributes::anyAttributesSet() const +{ + return !attributes.x.empty() || !attributes.y.empty() || !attributes.dx.empty() || !attributes.dy.empty() || !attributes.rotate.empty(); +} + +Geom::Point TextTagAttributes::firstXY() const +{ + Geom::Point point; + if (attributes.x.empty()) point[Geom::X] = 0.0; + else point[Geom::X] = attributes.x[0].computed; + if (attributes.y.empty()) point[Geom::Y] = 0.0; + else point[Geom::Y] = attributes.y[0].computed; + return point; +} + +void TextTagAttributes::setFirstXY(Geom::Point &point) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.x.empty()) + attributes.x.resize(1, zero_length); + if (attributes.y.empty()) + attributes.y.resize(1, zero_length); + attributes.x[0] = point[Geom::X]; + attributes.y[0] = point[Geom::Y]; +} + +void TextTagAttributes::mergeInto(Inkscape::Text::Layout::OptionalTextTagAttrs *output, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_attrs, unsigned parent_attrs_offset, bool copy_xy, bool copy_dxdyrotate) const +{ + mergeSingleAttribute(&output->x, parent_attrs.x, parent_attrs_offset, copy_xy ? &attributes.x : nullptr); + mergeSingleAttribute(&output->y, parent_attrs.y, parent_attrs_offset, copy_xy ? &attributes.y : nullptr); + mergeSingleAttribute(&output->dx, parent_attrs.dx, parent_attrs_offset, copy_dxdyrotate ? &attributes.dx : nullptr); + mergeSingleAttribute(&output->dy, parent_attrs.dy, parent_attrs_offset, copy_dxdyrotate ? &attributes.dy : nullptr); + mergeSingleAttribute(&output->rotate, parent_attrs.rotate, parent_attrs_offset, copy_dxdyrotate ? &attributes.rotate : nullptr); + if (attributes.textLength._set) { // only from current node, this is not inherited from parent + output->textLength.value = attributes.textLength.value; + output->textLength.computed = attributes.textLength.computed; + output->textLength.unit = attributes.textLength.unit; + output->textLength._set = attributes.textLength._set; + output->lengthAdjust = attributes.lengthAdjust; + } +} + +void TextTagAttributes::mergeSingleAttribute(std::vector *output_list, std::vector const &parent_list, unsigned parent_offset, std::vector const *overlay_list) +{ + output_list->clear(); + if (overlay_list == nullptr) { + if (parent_list.size() > parent_offset) + { + output_list->reserve(parent_list.size() - parent_offset); + std::copy(parent_list.begin() + parent_offset, parent_list.end(), std::back_inserter(*output_list)); + } + } else { + output_list->reserve(std::max((int)parent_list.size() - (int)parent_offset, (int)overlay_list->size())); + unsigned overlay_offset = 0; + while (parent_offset < parent_list.size() || overlay_offset < overlay_list->size()) { + SVGLength const *this_item; + if (overlay_offset < overlay_list->size()) { + this_item = &(*overlay_list)[overlay_offset]; + overlay_offset++; + parent_offset++; + } else { + this_item = &parent_list[parent_offset]; + parent_offset++; + } + output_list->push_back(*this_item); + } + } +} + +void TextTagAttributes::erase(unsigned start_index, unsigned n) +{ + if (n == 0) return; + if (!singleXYCoordinates()) { + eraseSingleAttribute(&attributes.x, start_index, n); + eraseSingleAttribute(&attributes.y, start_index, n); + } + eraseSingleAttribute(&attributes.dx, start_index, n); + eraseSingleAttribute(&attributes.dy, start_index, n); + eraseSingleAttribute(&attributes.rotate, start_index, n); +} + +void TextTagAttributes::eraseSingleAttribute(std::vector *attr_vector, unsigned start_index, unsigned n) +{ + if (attr_vector->size() <= start_index) return; + if (attr_vector->size() <= start_index + n) + attr_vector->erase(attr_vector->begin() + start_index, attr_vector->end()); + else + attr_vector->erase(attr_vector->begin() + start_index, attr_vector->begin() + start_index + n); +} + +void TextTagAttributes::insert(unsigned start_index, unsigned n) +{ + if (n == 0) return; + if (!singleXYCoordinates()) { + insertSingleAttribute(&attributes.x, start_index, n, true); + insertSingleAttribute(&attributes.y, start_index, n, true); + } + insertSingleAttribute(&attributes.dx, start_index, n, false); + insertSingleAttribute(&attributes.dy, start_index, n, false); + insertSingleAttribute(&attributes.rotate, start_index, n, false); +} + +void TextTagAttributes::insertSingleAttribute(std::vector *attr_vector, unsigned start_index, unsigned n, bool is_xy) +{ + if (attr_vector->size() <= start_index) return; + SVGLength zero_length; + zero_length = 0.0; + attr_vector->insert(attr_vector->begin() + start_index, n, zero_length); + if (is_xy) { + double begin = start_index == 0 ? (*attr_vector)[start_index + n].computed : (*attr_vector)[start_index - 1].computed; + double diff = ((*attr_vector)[start_index + n].computed - begin) / n; // n tested for nonzero in insert() + for (unsigned i = 0 ; i < n ; i++) + (*attr_vector)[start_index + i] = begin + diff * i; + } +} + +void TextTagAttributes::split(unsigned index, TextTagAttributes *second) +{ + if (!singleXYCoordinates()) { + splitSingleAttribute(&attributes.x, index, &second->attributes.x, false); + splitSingleAttribute(&attributes.y, index, &second->attributes.y, false); + } + splitSingleAttribute(&attributes.dx, index, &second->attributes.dx, true); + splitSingleAttribute(&attributes.dy, index, &second->attributes.dy, true); + splitSingleAttribute(&attributes.rotate, index, &second->attributes.rotate, true); +} + +void TextTagAttributes::splitSingleAttribute(std::vector *first_vector, unsigned index, std::vector *second_vector, bool trimZeros) +{ + second_vector->clear(); + if (first_vector->size() <= index) return; + second_vector->resize(first_vector->size() - index); + std::copy(first_vector->begin() + index, first_vector->end(), second_vector->begin()); + first_vector->resize(index); + if (trimZeros) + while (!first_vector->empty() && (!first_vector->back()._set || first_vector->back().value == 0.0)) + first_vector->resize(first_vector->size() - 1); +} + +void TextTagAttributes::join(TextTagAttributes const &first, TextTagAttributes const &second, unsigned second_index) +{ + if (second.singleXYCoordinates()) { + attributes.x = first.attributes.x; + attributes.y = first.attributes.y; + } else { + joinSingleAttribute(&attributes.x, first.attributes.x, second.attributes.x, second_index); + joinSingleAttribute(&attributes.y, first.attributes.y, second.attributes.y, second_index); + } + joinSingleAttribute(&attributes.dx, first.attributes.dx, second.attributes.dx, second_index); + joinSingleAttribute(&attributes.dy, first.attributes.dy, second.attributes.dy, second_index); + joinSingleAttribute(&attributes.rotate, first.attributes.rotate, second.attributes.rotate, second_index); +} + +void TextTagAttributes::joinSingleAttribute(std::vector *dest_vector, std::vector const &first_vector, std::vector const &second_vector, unsigned second_index) +{ + if (second_vector.empty()) + *dest_vector = first_vector; + else { + dest_vector->resize(second_index + second_vector.size()); + if (first_vector.size() < second_index) { + std::copy(first_vector.begin(), first_vector.end(), dest_vector->begin()); + SVGLength zero_length; + zero_length = 0.0; + std::fill(dest_vector->begin() + first_vector.size(), dest_vector->begin() + second_index, zero_length); + } else + std::copy(first_vector.begin(), first_vector.begin() + second_index, dest_vector->begin()); + std::copy(second_vector.begin(), second_vector.end(), dest_vector->begin() + second_index); + } +} + +void TextTagAttributes::transform(Geom::Affine const &matrix, double scale_x, double scale_y, bool extend_zero_length) +{ + SVGLength zero_length; + zero_length = 0.0; + + /* edge testcases for this code: + 1) moving text elements whose position is done entirely with transform="...", no x,y attributes + 2) unflowing multi-line flowtext then moving it (it has x but not y) + */ + unsigned points_count = std::max(attributes.x.size(), attributes.y.size()); + if (extend_zero_length && points_count < 1) + points_count = 1; + for (unsigned i = 0 ; i < points_count ; i++) { + Geom::Point point; + if (i < attributes.x.size()) point[Geom::X] = attributes.x[i].computed; + else point[Geom::X] = 0.0; + if (i < attributes.y.size()) point[Geom::Y] = attributes.y[i].computed; + else point[Geom::Y] = 0.0; + point *= matrix; + if (i < attributes.x.size()) + attributes.x[i] = point[Geom::X]; + else if (point[Geom::X] != 0.0 && extend_zero_length) { + attributes.x.resize(i + 1, zero_length); + attributes.x[i] = point[Geom::X]; + } + if (i < attributes.y.size()) + attributes.y[i] = point[Geom::Y]; + else if (point[Geom::Y] != 0.0 && extend_zero_length) { + attributes.y.resize(i + 1, zero_length); + attributes.y[i] = point[Geom::Y]; + } + } + for (std::vector::iterator it = attributes.dx.begin() ; it != attributes.dx.end() ; ++it) + *it = it->computed * scale_x; + for (std::vector::iterator it = attributes.dy.begin() ; it != attributes.dy.end() ; ++it) + *it = it->computed * scale_y; +} + +double TextTagAttributes::getDx(unsigned index) +{ + if( attributes.dx.empty()) { + return 0.0; + } + if( index < attributes.dx.size() ) { + return attributes.dx[index].computed; + } else { + return 0.0; // attributes.dx.back().computed; + } +} + + +double TextTagAttributes::getDy(unsigned index) +{ + if( attributes.dy.empty() ) { + return 0.0; + } + if( index < attributes.dy.size() ) { + return attributes.dy[index].computed; + } else { + return 0.0; // attributes.dy.back().computed; + } +} + + +void TextTagAttributes::addToDx(unsigned index, double delta) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.dx.size() < index + 1) attributes.dx.resize(index + 1, zero_length); + attributes.dx[index] = attributes.dx[index].computed + delta; +} + +void TextTagAttributes::addToDy(unsigned index, double delta) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.dy.size() < index + 1) attributes.dy.resize(index + 1, zero_length); + attributes.dy[index] = attributes.dy[index].computed + delta; +} + +void TextTagAttributes::addToDxDy(unsigned index, Geom::Point const &adjust) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (adjust[Geom::X] != 0.0) { + if (attributes.dx.size() < index + 1) attributes.dx.resize(index + 1, zero_length); + attributes.dx[index] = attributes.dx[index].computed + adjust[Geom::X]; + } + if (adjust[Geom::Y] != 0.0) { + if (attributes.dy.size() < index + 1) attributes.dy.resize(index + 1, zero_length); + attributes.dy[index] = attributes.dy[index].computed + adjust[Geom::Y]; + } +} + +double TextTagAttributes::getRotate(unsigned index) +{ + if( attributes.rotate.empty() ) { + return 0.0; + } + if( index < attributes.rotate.size() ) { + return attributes.rotate[index].computed; + } else { + return attributes.rotate.back().computed; + } +} + + +void TextTagAttributes::addToRotate(unsigned index, double delta) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.rotate.size() < index + 2) { + if (attributes.rotate.empty()) + attributes.rotate.resize(index + 2, zero_length); + else + attributes.rotate.resize(index + 2, attributes.rotate.back()); + } + attributes.rotate[index] = mod360(attributes.rotate[index].computed + delta); +} + + +void TextTagAttributes::setRotate(unsigned index, double angle) +{ + SVGLength zero_length; + zero_length = 0.0; + + if (attributes.rotate.size() < index + 2) { + if (attributes.rotate.empty()) + attributes.rotate.resize(index + 2, zero_length); + else + attributes.rotate.resize(index + 2, attributes.rotate.back()); + } + attributes.rotate[index] = mod360(angle); +} + + +/* + 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/registrytool.cpp b/src/registrytool.cpp deleted file mode 100644 index 89442bc36..000000000 --- a/src/registrytool.cpp +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Inkscape Registry Tool - * - * Authors: - * Bob Jamison - * - * Copyright (C) 2005-2008 Bob Jamison - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "registrytool.h" - -#include -#include -#include - -#include - -struct KeyTableEntry -{ - HKEY key; - int strlen; - const char *str; -}; - - - -KeyTableEntry keyTable[] = -{ - { HKEY_CLASSES_ROOT, 18, "HKEY_CLASSES_ROOT\\" }, - { HKEY_CURRENT_CONFIG, 20, "HKEY_CURRENT_CONFIG\\" }, - { HKEY_CURRENT_USER, 18, "HKEY_CURRENT_USER\\" }, - { HKEY_LOCAL_MACHINE, 19, "HKEY_LOCAL_MACHINE\\" }, - { HKEY_USERS, 11, "HKEY_USERS\\" }, - { NULL, 0, NULL } -}; - - -bool RegistryTool::setStringValue(const Glib::ustring &keyNameArg, - const Glib::ustring &valueName, - const Glib::ustring &value) -{ - Glib::ustring keyName = keyNameArg; - bool ret = false; - - HKEY rootKey = HKEY_LOCAL_MACHINE; //default root - //Trim out the root key if necessary - for (KeyTableEntry *entry = keyTable; entry->key; entry++) - { - if (keyName.compare(0, entry->strlen, entry->str)==0) - { - rootKey = entry->key; - keyName = keyName.substr(entry->strlen); - } - } - //printf("trimmed string: '%s'\n", keyName.c_str()); - - //Get or create the key - gunichar2 *keyw = g_utf8_to_utf16(keyName.data(), -1, 0,0,0); - gunichar2 *valuenamew = g_utf8_to_utf16(valueName.data(), -1, 0,0,0); - gunichar2 *valuew = g_utf8_to_utf16(value.data(), -1, 0,0,0); - - HKEY key; - if (RegCreateKeyExW(rootKey, (WCHAR*) keyw, - 0, NULL, REG_OPTION_NON_VOLATILE, - KEY_WRITE, NULL, &key, NULL)) - { - fprintf(stderr, "RegistryTool: Could not create the registry key '%s'\n", keyName.c_str()); - goto fail; - } - - // Set the value - if (RegSetValueExW(key, (WCHAR*) valuenamew, - 0, REG_SZ, (LPBYTE) valuew, (DWORD) (2*value.size() + 2))) - { - fprintf(stderr, "RegistryTool: Could not set the value '%s'\n", value.c_str()); - goto failkey; - } - - ret = true; - - failkey: - RegCloseKey(key); - - fail: - g_free(keyw); - g_free(valuenamew); - return ret; -} - - - -bool RegistryTool::getExeInfo(Glib::ustring &fullPath, - Glib::ustring &path, - Glib::ustring &exeName) -{ - const int pathbuf = 2048; - gunichar2 pathw[pathbuf]; - GetModuleFileNameW(NULL, (WCHAR*) pathw, pathbuf); - - gchar *utf8path = g_utf16_to_utf8(pathw, -1, 0,0,0); - fullPath = utf8path; - g_free(utf8path); - - path = ""; - exeName = ""; - Glib::ustring::size_type pos = fullPath.rfind('\\'); - if (pos != fullPath.npos) - { - path = fullPath.substr(0, pos); - exeName = fullPath.substr(pos+1); - } - - return true; -} - - - -bool RegistryTool::setPathInfo() -{ - Glib::ustring fullPath; - Glib::ustring path; - Glib::ustring exeName; - - if (!getExeInfo(fullPath, path, exeName)) - return false; - - //printf("full:'%s' path:'%s' exe:'%s'\n", - // fullPath.c_str(), path.c_str(), exeName.c_str()); - - Glib::ustring keyName = - "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"; - keyName.append(exeName); - - Glib::ustring valueName = ""; - Glib::ustring value = fullPath; - - if (!setStringValue(keyName, valueName, value)) - return false; - - //add our subdirectories - Glib::ustring appPath = path; - appPath.append("\\python;"); - appPath.append(path); - appPath.append("\\perl"); - valueName = "Path"; - value = appPath; - - if (!setStringValue(keyName, valueName, value)) - return false; - - return true; -} - - -#ifdef TESTREG - - -/* - * Compile this file with - * g++ -DTESTREG registrytool.cpp -o registrytool - * to run these tests. - */ - - - -void testReg() -{ - RegistryTool rt; - char *key = - "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\inkscape.exe"; - char const *name = ""; - char const *value = "c:\\inkscape\\inkscape.exe"; - if (!rt.setStringValue(key, name, value)) - { - printf("Test failed\n"); - } - else - { - printf("Test succeeded\n"); - } - name = "Path"; - value = "c:\\inkscape\\python"; - if (!rt.setStringValue(key, name, value)) - { - printf("Test failed\n"); - } - else - { - printf("Test succeeded\n"); - } -} - - -void testPath() -{ - RegistryTool rt; - rt.setPathInfo(); -} - - -int main(int argc, char **argv) -{ - //testReg(); - testPath(); - return 0; -} - -#endif /* TESTREG */ - -//######################################################################## -//# E N D O F F I L E -//######################################################################## diff --git a/src/registrytool.h b/src/registrytool.h deleted file mode 100644 index 335a8bd52..000000000 --- a/src/registrytool.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef SEEN_REGISTRYTOOL_H -#define SEEN_REGISTRYTOOL_H -/* - * Authors: - * Bob Jamison - * - * Copyright (C) 2005 Bob Jamison - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -namespace Glib { - class ustring; -} - -/** - * Inkscape Registry Tool - * - * This simple tool is intended for allowing Inkscape to append subdirectories - * to its path. This will allow extensions and other files to be accesses - * without explicit user intervention. - */ -class RegistryTool -{ -public: - - RegistryTool() - {} - - virtual ~RegistryTool() - {} - - /** - * Set the string value of a key/name registry entry. - */ - bool setStringValue(const Glib::ustring &key, - const Glib::ustring &valueName, - const Glib::ustring &value); - - /** - * Get the full path, directory, and base file name of this running executable. - */ - bool getExeInfo(Glib::ustring &fullPath, - Glib::ustring &path, - Glib::ustring &exeName); - - /** - * Append our subdirectories to the Application Path for this - * application. - */ - bool setPathInfo(); - - -}; - -#endif // SEEN_REGISTRYTOOL_H - diff --git a/src/resource-manager.cpp b/src/resource-manager.cpp deleted file mode 100644 index 6a455a8cc..000000000 --- a/src/resource-manager.cpp +++ /dev/null @@ -1,445 +0,0 @@ -/* - * 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 -#include - -#include "resource-manager.h" - -#include "document.h" -#include "document-undo.h" -#include "verbs.h" - -#include "object/sp-object.h" - -#include "xml/node.h" - -namespace Inkscape { - -static std::vector splitPath( std::string const &path ) -{ - std::vector parts; - - std::string prior; - std::string tmp = path; - while ( !tmp.empty() && (tmp != prior) ) { - prior = tmp; - - parts.push_back( Glib::path_get_basename(tmp) ); - tmp = Glib::path_get_dirname(tmp); - } - if ( !parts.empty() ) { - std::reverse(parts.begin(), parts.end()); - if ( (parts[0] == ".") && (path[0] != '.') ) { - parts.erase(parts.begin()); - } - } - - return parts; -} - -static std::string convertPathToRelative( std::string const &path, std::string const &docbase ) -{ - std::string result = path; - - if ( !path.empty() && Glib::path_is_absolute(path) ) { - // Whack the parts into pieces - - std::vector parts = splitPath(path); - std::vector baseParts = splitPath(docbase); - - // TODO debug g_message("+++++++++++++++++++++++++"); - for ( std::vector::iterator it = parts.begin(); it != parts.end(); ++it ) { - // TODO debug g_message(" [%s]", it->c_str()); - } - // TODO debug g_message(" - - - - - - - - - - - - - - - "); - for ( std::vector::iterator it = baseParts.begin(); it != baseParts.end(); ++it ) { - // TODO debug g_message(" [%s]", it->c_str()); - } - // TODO debug g_message("+++++++++++++++++++++++++"); - - if ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) { - // Both paths have the same root. We can proceed. - while ( !parts.empty() && !baseParts.empty() && (parts[0] == baseParts[0]) ) { - parts.erase( parts.begin() ); - baseParts.erase( baseParts.begin() ); - } - - // TODO debug g_message("+++++++++++++++++++++++++"); - for ( std::vector::iterator it = parts.begin(); it != parts.end(); ++it ) { - // TODO debug g_message(" [%s]", it->c_str()); - } - // TODO debug g_message(" - - - - - - - - - - - - - - - "); - for ( std::vector::iterator it = baseParts.begin(); it != baseParts.end(); ++it ) { - // TODO debug g_message(" [%s]", it->c_str()); - } - // TODO debug g_message("+++++++++++++++++++++++++"); - - if ( !parts.empty() ) { - result.clear(); - - for ( size_t i = 0; i < baseParts.size(); ++i ) { - parts.insert(parts.begin(), ".."); - } - result = Glib::build_filename( parts ); - // TODO debug g_message("----> [%s]", result.c_str()); - } - } - } - - return result; -} - - -class ResourceManagerImpl : public ResourceManager { -public: - ResourceManagerImpl(); - ~ResourceManagerImpl() override; - - bool fixupBrokenLinks(SPDocument *doc) override; - - - /** - * 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); - - - /** - * Try to parse href into a local filename using standard methods. - * - * @return true if successful. - */ - bool extractFilepath( Glib::ustring const &href, std::string &uri ); - - /** - * Try to parse href into a local filename using some non-standard methods. - * This means the href is likely invalid and should be rewritten. - * - * @return true if successful. - */ - bool reconstructFilepath( Glib::ustring const &href, std::string &uri ); - - bool searchUpwards( std::string const &base, std::string const &subpath, std::string &dest ); - -protected: -}; - - -ResourceManagerImpl::ResourceManagerImpl() - : ResourceManager() -{ -} - -ResourceManagerImpl::~ResourceManagerImpl() -= default; - -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: - try { - uri = Glib::filename_from_uri(href); - // TODO debug g_message(" [%s]", uri.c_str()); - isFile = true; - } catch(Glib::ConvertError e) { - g_warning("%s", e.what().c_str()); - } - } - } 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; -} - -bool ResourceManagerImpl::reconstructFilepath( Glib::ustring const &href, std::string &uri ) -{ - bool isFile = false; - - uri.clear(); - - std::string scheme = Glib::uri_parse_scheme(href); - if ( !scheme.empty() ) { - if ( scheme == "file" ) { - // try to build a relative filename for URIs like "file:image.png" - // they're not standard conformant but not uncommon - Glib::ustring href_new = Glib::ustring(href, 5); - uri = Glib::filename_from_utf8(href_new); - // TODO debug g_message("reconstructed path for '%s' into '%s'", href.c_str(), uri.c_str()); - isFile = true; - } - } - return isFile; -} - - -std::vector ResourceManagerImpl::findBrokenLinks( SPDocument *doc ) -{ - std::vector result; - std::set uniques; - - if ( doc ) { - std::vector images = doc->getResourceList("image"); - for (std::vector::const_iterator it = images.begin(); it != images.end(); ++it) { - Inkscape::XML::Node *ir = (*it)->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.emplace_back(href); - uniques.insert(href); - } - } else { - std::string combined = Glib::build_filename(doc->getBase(), uri); - if ( !Glib::file_test(combined, Glib::FILE_TEST_EXISTS) ) { - result.emplace_back(href); - uniques.insert(href); - } - } - } else if ( reconstructFilepath( href, uri ) ) { - result.emplace_back(href); - uniques.insert(href); - } - } - } - } - - return result; -} - - -std::map ResourceManagerImpl::locateLinks(Glib::ustring const & docbase, std::vector const & brokenLinks) -{ - std::map result; - - - // Note: we use a vector because we want them to stay in order: - std::vector priorLocations; - - Glib::RefPtr recentMgr = Gtk::RecentManager::get_default(); - std::vector< Glib::RefPtr > recentItems = recentMgr->get_items(); - for ( std::vector< Glib::RefPtr >::iterator it = recentItems.begin(); it != recentItems.end(); ++it ) { - Glib::ustring uri = (*it)->get_uri(); - std::string scheme = Glib::uri_parse_scheme(uri); - if ( scheme == "file" ) { - try { - std::string path = Glib::filename_from_uri(uri); - path = Glib::path_get_dirname(path); - if ( std::find(priorLocations.begin(), priorLocations.end(), path) == priorLocations.end() ) { - // TODO debug g_message(" ==>[%s]", path.c_str()); - priorLocations.push_back(path); - } - } catch (Glib::ConvertError e) { - g_warning("%s", e.what().c_str()); - } - } - } - - // 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 ) || reconstructFilepath( *it, uri ) ) { - // We were able to get some path. Check it - std::string origPath = uri; - - 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()); - } - - bool exists = Glib::file_test(uri, Glib::FILE_TEST_EXISTS); - - // search in parent folders - if (!exists) { - exists = searchUpwards( docbase, origPath, uri ); - } - - // Check if the MRU bases point us to it. - if ( !exists ) { - if ( !Glib::path_is_absolute(origPath) ) { - for ( std::vector::iterator it = priorLocations.begin(); !exists && (it != priorLocations.end()); ++it ) { - exists = searchUpwards( *it, origPath, uri ); - } - } - } - - if ( exists ) { - if ( Glib::path_is_absolute( uri ) ) { - // TODO debug g_message("Need to convert to relative if possible [%s]", uri.c_str()); - uri = convertPathToRelative( uri, docbase ); - } - - bool isAbsolute = Glib::path_is_absolute( uri ); - Glib::ustring replacement = isAbsolute ? Glib::filename_to_uri( uri ) : Glib::filename_to_utf8( uri ); - 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", static_cast(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); - - std::vector images = doc->getResourceList("image"); - for (std::vector::const_iterator it = images.begin(); it != images.end(); ++it) { - Inkscape::XML::Node *ir = (*it)->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", nullptr ); // Remove this attribute - } - - SPObject *updated = doc->getObjectByRepr(ir); - if (updated) { - // force immediate update of dependent attributes - updated->updateRepr(); - } - - changed = true; - } - } - } - if ( changed ) { - DocumentUndo::done( doc, SP_VERB_DIALOG_XML_EDITOR, _("Fixup broken links") ); - } - DocumentUndo::setUndoSensitive(doc, savedUndoState); - } - - return changed; -} - - -bool ResourceManagerImpl::searchUpwards( std::string const &base, std::string const &subpath, std::string &dest ) -{ - bool exists = false; - // TODO debug g_message("............"); - - std::vector parts = splitPath(subpath); - std::vector baseParts = splitPath(base); - - while ( !exists && !baseParts.empty() ) { - std::vector current; - current.insert(current.begin(), parts.begin(), parts.end()); - // TODO debug g_message(" ---{%s}", Glib::build_filename( baseParts ).c_str()); - while ( !exists && !current.empty() ) { - std::vector combined; - combined.insert( combined.end(), baseParts.begin(), baseParts.end() ); - combined.insert( combined.end(), current.begin(), current.end() ); - std::string filepath = Glib::build_filename( combined ); - exists = Glib::file_test(filepath, Glib::FILE_TEST_EXISTS); - // TODO debug g_message(" ...[%s] %s", filepath.c_str(), (exists ? "XXX" : "")); - if ( exists ) { - dest = filepath; - } - current.erase( current.begin() ); - } - baseParts.pop_back(); - } - - return exists; -} - - -static ResourceManagerImpl* theInstance = nullptr; - -ResourceManager::ResourceManager() - : Glib::Object() -{ -} - -ResourceManager::~ResourceManager() = default; - -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 deleted file mode 100644 index b4d88c7e6..000000000 --- a/src/resource-manager.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Inkscape::ResourceManager - Manages external resources such as image and css files. - * - * Copyright 2011 Jon A. Cruz - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#ifndef SEEN_INKSCAPE_RESOURCE_MANAGER_H -#define SEEN_INKSCAPE_RESOURCE_MANAGER_H - -#include - -class SPDocument; - -namespace Inkscape { - -class ResourceManager : public Glib::Object { - -public: - static ResourceManager& getManager(); - - virtual bool fixupBrokenLinks(SPDocument *doc) = 0; - -protected: - ResourceManager(); - ~ResourceManager() override; - -private: - ResourceManager(ResourceManager const &) = delete; // no copy - void operator=(ResourceManager const &) = delete; // 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/xml/rebase-hrefs.cpp b/src/xml/rebase-hrefs.cpp index 5d8f270b0..c4b5a4117 100644 --- a/src/xml/rebase-hrefs.cpp +++ b/src/xml/rebase-hrefs.cpp @@ -4,9 +4,9 @@ #include #include "../document.h" /* Unfortunately there's a separate xml/document.h. */ -#include "dir-util.h" #include "streq.h" +#include "io/dir-util.h" #include "io/sys.h" #include "object/sp-object.h" -- cgit v1.2.3