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/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 +++++ 12 files changed, 1355 insertions(+), 22 deletions(-) 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 (limited to 'src/io') 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 : -- cgit v1.2.3