diff options
| author | Tavmjong Bah <tavmjong@free.fr> | 2018-11-12 20:22:44 +0000 |
|---|---|---|
| committer | Tavmjong Bah <tavmjong@free.fr> | 2018-11-12 20:22:44 +0000 |
| commit | db05b842cba28f01b431eee890537959aa2d8fe3 (patch) | |
| tree | 04cad3abf64f9cda14a457ef61bd0d5bf6fa762c /src | |
| parent | Support multiple style sheets or style elements in an svg document (diff) | |
| download | inkscape-db05b842cba28f01b431eee890537959aa2d8fe3.tar.gz inkscape-db05b842cba28f01b431eee890537959aa2d8fe3.zip | |
Rewrite of main.cpp using InkscapeApplication (Gtk::Application)
Use Gio::File for accessing files.
Use Gio options to handle command line arguments.
Use Gio::Actions for some command line arguments.
Move file export code to src/io/file-export-cmd.h/.cpp. Make into class.
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 9 | ||||
| -rw-r--r-- | src/actions/CMakeLists.txt | 10 | ||||
| -rw-r--r-- | src/actions/actions-base.cpp | 277 | ||||
| -rw-r--r-- | src/actions/actions-base.h | 18 | ||||
| -rw-r--r-- | src/inkscape-application.cpp | 575 | ||||
| -rw-r--r-- | src/inkscape-application.h | 80 | ||||
| -rw-r--r-- | src/inkscape-main.cpp | 36 | ||||
| -rw-r--r-- | src/inkscape.cpp | 7 | ||||
| -rw-r--r-- | src/io/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | src/io/file-export-cmd.cpp | 678 | ||||
| -rw-r--r-- | src/io/file-export-cmd.h | 79 | ||||
| -rw-r--r-- | src/io/file.cpp | 64 | ||||
| -rw-r--r-- | src/io/file.h | 26 | ||||
| -rw-r--r-- | src/main.cpp | 2251 |
14 files changed, 1859 insertions, 2255 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ba3fc37d1..e2ccea569 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -245,6 +245,7 @@ list(APPEND inkscape_SRC # All folders for internal inkscape # these call add_inkscape_source +add_subdirectory(actions) add_subdirectory(debug) add_subdirectory(display) add_subdirectory(extension) @@ -278,7 +279,13 @@ set(inkscape_SRC # ----------------------------------------------------------------------------- # Setup the executable # ----------------------------------------------------------------------------- -set(main_SRC main.cpp) +set(main_SRC + inkscape-main.cpp + inkscape-application.h + inkscape-application.cpp + actions/actions-base.h + actions/actions-base.cpp +) set(view_SRC inkview-main.cpp inkview-application.h diff --git a/src/actions/CMakeLists.txt b/src/actions/CMakeLists.txt new file mode 100644 index 000000000..e16dbc505 --- /dev/null +++ b/src/actions/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +set(actions_SRC + # actions-base.cpp + + # HEADERS + # actions-base.h + ) + +add_inkscape_source("${actions_SRC}") diff --git a/src/actions/actions-base.cpp b/src/actions/actions-base.cpp new file mode 100644 index 000000000..a3ff363a7 --- /dev/null +++ b/src/actions/actions-base.cpp @@ -0,0 +1,277 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Gio::Actions tied to the application and 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. + * + */ + +#include <iostream> + +#include <giomm.h> // Not <gtkmm.h>! To eventually allow a headless version! + +#include "actions-base.h" + +#include "inkscape-application.h" + +#include "inkscape.h" // Inkscape::Application +#include "inkscape-version.h" // Inkscape version +#include "path-prefix.h" // Extension directory +#include "extension/init.h" // List verbs +#include "verbs.h" // List verbs +#include "selection.h" // Selection +#include "object/sp-root.h" // query_all() +#include "file.h" // dpi convert method + +void +print_inkscape_version() +{ + std::cout << "Inkscape " << Inkscape::version_string << std::endl; +} + +void +print_extension_directory() +{ + std::cout << INKSCAPE_EXTENSIONDIR << std::endl; +} + +void +print_verb_list() +{ + // This really shouldn't go here, we should init the app. + // But, since we're just exiting in this path, there is + // no harm, and this is really a better place to put + // everything else. + Inkscape::Extension::init(); // extension/init.h + Inkscape::Verb::list(); // verbs.h +} + +// Helper function: returns true if both document and selection found. +bool +get_document_and_selection(InkscapeApplication* app, SPDocument** document, Inkscape::Selection** selection) +{ + *document = app->get_active_document(); + if (!(*document)) { + std::cerr << "get_document_and_selection: No document!" << std::endl; + return false; + } + + Inkscape::ActionContext context = INKSCAPE.action_context_for_document(*document); + *selection = context.getSelection(); + if (!*selection) { + std::cerr << "get_document_and_selection: No selection!" << std::endl; + return false; + } + + return true; +} + +void +select_via_id(Glib::ustring ids, InkscapeApplication* app) +{ + SPDocument* document = nullptr; + Inkscape::Selection* selection = nullptr; + if (!get_document_and_selection(app, &document, &selection)) { + return; + } + + auto tokens = Glib::Regex::split_simple("\\s*,\\s*", ids); + for (auto id : tokens) { + SPObject* object = document->getObjectById(id); + if (object) { + selection->add(object); + } else { + std::cerr << "select: did not find object with id: " << id << std::endl; + } + } +} + +// Helper function for query_x(), query_y(), query_width(), and query_height(). +void +query_dimension(InkscapeApplication* app, bool extent, Geom::Dim2 const axis) +{ + SPDocument* document = nullptr; + Inkscape::Selection* selection = nullptr; + if (!get_document_and_selection(app, &document, &selection)) { + return; + } + + if (selection->isEmpty()) { + selection->add(document->getRoot()); + } + + bool first = true; + auto items = selection->items(); + for (auto item : items) { + if (!first) { + std::cout << ","; + } + first = false; + Geom::OptRect area = item->documentVisualBounds(); + if (area) { + if (extent) { + std::cout << area->dimensions()[axis]; + } else { + std::cout << area->min()[axis]; + } + } else { + std::cout << "0"; + } + } + std::cout << std::endl; +} + +void +query_x(InkscapeApplication* app) +{ + query_dimension(app, false, Geom::X); +} + +void +query_y(InkscapeApplication* app) +{ + query_dimension(app, false, Geom::Y); +} + +void +query_width(InkscapeApplication* app) +{ + query_dimension(app, true, Geom::X); +} + +void +query_height(InkscapeApplication* app) +{ + query_dimension(app, true, Geom::Y); +} + +// Helper for query_all() +void +query_all_recurse (SPObject *o) +{ + SPItem *item = dynamic_cast<SPItem*>(o); + if (item && item->getId()) { + Geom::OptRect area = item->documentVisualBounds(); + if (area) { + std::cout << item->getId() << "," + << area->min()[Geom::X] << "," + << area->min()[Geom::Y] << "," + << area->dimensions()[Geom::X] << "," + << area->dimensions()[Geom::Y] << std::endl; + } + + for (auto& child: o->children) { + query_all_recurse (&child); + } + } +} + +void +query_all(InkscapeApplication* app) +{ + SPDocument* doc = app->get_active_document(); + if (!doc) { + std::cerr << "query_all: no document!" << std::endl; + return; + } + + SPObject *o = doc->getRoot(); + if (o) { + query_all_recurse(o); + } +} + +void +pdf_page(int page) +{ + INKSCAPE.set_pdf_page(page); +} + +void +convert_dpi_method(Glib::ustring method) +{ + if (method == "none") { + sp_file_convert_dpi_method_commandline = FILE_DPI_UNCHANGED; + } else if (method == "scale-viewbox") { + sp_file_convert_dpi_method_commandline = FILE_DPI_VIEWBOX_SCALED; + } else if (method == "scale-document") { + sp_file_convert_dpi_method_commandline = FILE_DPI_DOCUMENT_SCALED; + } else { + std::cerr << "dpi_convert_method: invalid option" << std::endl; + } +} + +void +no_convert_baseline() +{ + sp_no_convert_text_baseline_spacing = true; +} + +// Temporary: Verbs are to be replaced by Gio::Actions! +void +verbs(Glib::ustring verblist, InkscapeApplication* app) +{ + auto tokens = Glib::Regex::split_simple("\\s*,\\s*", verblist); + for (auto token : tokens) { + std::vector<Glib::ustring> parts = Glib::Regex::split_simple("\\s*:\\s*", token); // Second part is always ignored... we could implement it but better to switch to Gio::Actions + if (!parts[0].empty()) { + Inkscape::Verb* verb = Inkscape::Verb::getbyid(parts[0].c_str()); + if (verb == nullptr) { + std::cerr << "verbs_action: Invalid verb: " << parts[0] << std::endl; + break; + } + // Inkscape::ActionContext context = INKSCAPE.action_context_for_document(*document); + SPAction* action = verb->get_action(INKSCAPE.active_action_context()); + sp_action_perform(action, nullptr); // Data is ignored! + } + } +} + +void +vacuum_defs(InkscapeApplication* app) +{ + SPDocument* document = nullptr; + Inkscape::Selection* selection = nullptr; + if (!get_document_and_selection(app, &document, &selection)) { + return; + } + document->vacuumDocument(); +} + +void +add_actions_base(InkscapeApplication* app) +{ + // Note: "radio" actions are just an easy way to set type without using templating. + app->add_action("inkscape-version", sigc::ptr_fun(&print_inkscape_version )); + app->add_action("extension-directory", sigc::ptr_fun(&print_extension_directory)); + app->add_action("verb-list", sigc::ptr_fun(&print_verb_list )); + + app->add_action_radio_integer( "open-page", sigc::ptr_fun(&pdf_page), 0); + app->add_action_radio_string( "convert-dpi-method", sigc::ptr_fun(&convert_dpi_method), "none"); + app->add_action( "no-convert-baseline", sigc::ptr_fun(&no_convert_baseline) ); + + app->add_action( "vacuum-defs", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&vacuum_defs), app) ); + app->add_action_radio_string( "select", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&select_via_id), app), "null"); + app->add_action_radio_string( "verb", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&verbs), app), "null"); + + app->add_action_radio_string( "query-id", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&select_via_id), app), "null"); // For backwards compatibility. + app->add_action( "query-x", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&query_x), app) ); + app->add_action( "query-y", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&query_y), app) ); + app->add_action( "query-width", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&query_width), app) ); + app->add_action( "query-height", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&query_height), app) ); + app->add_action( "query-all", sigc::bind<InkscapeApplication*>(sigc::ptr_fun(&query_all), app) ); + +} + +/* + 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/actions/actions-base.h b/src/actions/actions-base.h new file mode 100644 index 000000000..a52078881 --- /dev/null +++ b/src/actions/actions-base.h @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Gio::Actions tied to the application and without 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_ACTIONS_BASE_H +#define INK_ACTIONS_BASE_H + +class InkscapeApplication; + +void add_actions_base(InkscapeApplication* app); + +#endif // INK_ACTIONS_BASE_H diff --git a/src/inkscape-application.cpp b/src/inkscape-application.cpp new file mode 100644 index 000000000..3a4721963 --- /dev/null +++ b/src/inkscape-application.cpp @@ -0,0 +1,575 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The main Inkscape application. + * + * Copyright (C) 2018 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#include <iostream> + +#include <glibmm/i18n.h> // Internationalization + +#include "inkscape-application.h" + +#include "inkscape.h" // Inkscape::Application +#include "inkgc/gc-core.h" // Garbage Collecting init +#include "ui/widget/panel.h" // Panel prep +#include "file.h" // File open and window creation. +#include "io/file.h" // File open (command line). +#include "desktop.h" // Access to window +#include "actions/actions-base.h" // Actions + +#ifdef ENABLE_NLS +// Native Language Support - shouldn't this always be used? +#include "helper/gettext.h" // gettext init +#endif // ENABLE_NLS + +// This is a bit confusing as there are two ways to handle command line arguments and files +// depending on if the Gio::APPLICATION_HANDLES_OPEN and/or Gio::APPLICATION_HANDLES_COMMAND_LINE +// flags are set. If the open flag is set and the command line not, the all the remainng arguments +// after calling on_handle_local_options() are assumed to be filenames. + +InkscapeApplication::InkscapeApplication() + : Gtk::Application("org.inkscape.application.with_gui", + Gio::APPLICATION_HANDLES_OPEN | // Use default file opening. + Gio::APPLICATION_NON_UNIQUE ) // Allows different instances of Inkscape to run at same time. + , _with_gui(true) +{ + + // ==================== Initializations ===================== + // Garbage Collector + Inkscape::GC::init(); + +#ifdef ENABLE_NLS + // Native Language Support (shouldn't this always be used?). + Inkscape::initialize_gettext(); +#endif + + Glib::set_application_name(N_("Inkscape - A Vector Drawing Program")); // After gettext() init. + + // ======================== Actions ========================= + add_actions_base(this); // actions that are GUI independent + + // ====================== Command Line ====================== + + // Will automatically handle character conversions. + // Note: OPTION_TYPE_FILENAME => std::string, OPTION_TYPE_STRING => Glib::ustring. + + // Actions + add_main_option_entry(OPTION_TYPE_STRING, "actions", 'a', N_("Actions (with optional arguments), semi-colon separated."), N_("ACTION(:ARGUMENT)")); + add_main_option_entry(OPTION_TYPE_BOOL, "action-list", '\0', N_("Actions: List available actions."), ""); + + // Query + add_main_option_entry(OPTION_TYPE_BOOL, "version", 'v', N_("Print: Inkscape version."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "extensions-directory",'x', N_("Print: Extensions directory."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "verb-list", '\0', N_("Print: List verbs."), ""); + + // Interface + add_main_option_entry(OPTION_TYPE_BOOL, "with-gui", 'g', N_("GUI: With graphical interface."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "without-gui", 'G', N_("GUI: Console only."), ""); + + // Open/Import + add_main_option_entry(OPTION_TYPE_INT, "pdf-page", '\0', N_("Open: PDF page to import"), N_("PAGE")); + add_main_option_entry(OPTION_TYPE_STRING, "convert-dpi-method", '\0', N_("Open: Method used to convert pre-0.92 document dpi, if needed: [none|scale-viewbox|scale-document]."), "[...]"); + add_main_option_entry(OPTION_TYPE_BOOL, "no-convert-text-baseline-spacing", 0, N_("Open: Do not fix pre-0.92 document's text baseline spacing on opening."), ""); + + // Query - Geometry + add_main_option_entry(OPTION_TYPE_STRING, "query-id", 'I', N_("Query: ID of object to be queried."), N_("ID")); + add_main_option_entry(OPTION_TYPE_BOOL, "query-all", 'S', N_("Query: Print bounding boxes of all objects."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "query-x", 'X', N_("Query: X coordinate of drawing or object (if specified by --query-id)."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "query-y", 'Y', N_("Query: Y coordinate of drawing or object (if specified by --query-id)."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "query-width", 'W', N_("Query: Width of drawing or object (if specified by --query-id)."), ""); + add_main_option_entry(OPTION_TYPE_BOOL, "query-height", 'H', N_("Query: Heightof drawing or object (if specified by --query-id)."), ""); + + // Processing + add_main_option_entry(OPTION_TYPE_BOOL, "vacuum-defs", '\0', N_("Process: Remove unused definitions from the <defs> section(s) of document."), ""); + add_main_option_entry(OPTION_TYPE_STRING, "select", '\0', N_("Process: Select objects: comma separated list of IDs."), N_("OBJECT-ID[,OBJECT-ID]*")); + add_main_option_entry(OPTION_TYPE_STRING, "verb", '\0', N_("Process: Verb(s) to call when Inkscape opens."), N_("VERB-ID[,VERB-ID]*")); + //add_main_option_entry(OPTION_TYPE_BOOL, "shell", '\0', N_("Process: Start Inkscape in interative shell mode."), ""); + + // Export - File and File Type + add_main_option_entry(OPTION_TYPE_STRING, "export-type", '\0', N_("Export: File type:[svg,png,ps,psf,tex,emf,wmf,xaml]"), "[...]"); + add_main_option_entry(OPTION_TYPE_FILENAME, "export-file", 'o', N_("Export: File name"), N_("EXPORT-FILENAME")); + add_main_option_entry(OPTION_TYPE_BOOL, "export-overwrite", '\0', N_("Export: Overwrite input file."), ""); // BSP + + // B = PNG, S = SVG, P = PS/EPS/PDF + // Export - Geometry + add_main_option_entry(OPTION_TYPE_STRING, "export-area", 'a', N_("Export: Area to export in SVG user units."), N_("x0:y0:x1:y1")); // BSP + add_main_option_entry(OPTION_TYPE_BOOL, "export-drawing", 'D', N_("Export: Area to export is drawing (not page)."), ""); // BSP + add_main_option_entry(OPTION_TYPE_BOOL, "export-page", 'C', N_("Export: Area to export is page."), ""); // BSP + add_main_option_entry(OPTION_TYPE_INT, "export-margin", '\0', N_("Export: Margin around export area: units of page size for SVG, mm for PS/EPS/PDF."), ""); // xSP + add_main_option_entry(OPTION_TYPE_BOOL, "export-area-snap", '\0', N_("Export: Snap the bitmap export area outwards to the nearest integer values."), ""); // Bxx + add_main_option_entry(OPTION_TYPE_INT, "export-width", 'w', N_("Export: Bitmap width in pixels (overrides --export-dpi)."), N_("WIDTH")); // Bxx + add_main_option_entry(OPTION_TYPE_INT, "export-height", 'h', N_("Export: Bitmap height in pixels (overrides --export-dpi)."), N_("HEIGHT")); // Bxx + + // Export - Options + add_main_option_entry(OPTION_TYPE_STRING, "export-id", 'i', N_("Export: ID of object to export."), N_("OBJECT-ID")); // BSP + add_main_option_entry(OPTION_TYPE_BOOL, "export-id-only", 'j', N_("Export: Hide all objects except object with ID selected by export-id."), ""); // BSx + add_main_option_entry(OPTION_TYPE_BOOL, "export-plain-svg", 'l', N_("Export: Remove items in the Inkscape namespace."), ""); // xSx + add_main_option_entry(OPTION_TYPE_INT, "export-dpi", 'd', N_("Export: Resolution for rasterization bitmaps and filters (default is 96)."), N_("DPI")); // BxP + add_main_option_entry(OPTION_TYPE_BOOL, "export-ignore-filters", '\0', N_("Export: Render objects without filters instead of rasterizing. (PS/EPS/PDF)"), ""); // xxP + add_main_option_entry(OPTION_TYPE_BOOL, "export-text-to-path", 'T', N_("Export: Convert text to paths. (PS/EPS/PDF/SVG)."), ""); // xxP + add_main_option_entry(OPTION_TYPE_INT, "export-ps-level", '\0', N_("Export: Postscript level (2 or 3). Default is 3."), N_("PS-Level")); // xxP + add_main_option_entry(OPTION_TYPE_STRING, "export-pdf-level", '\0', N_("Export: PDF level (1.4 or 1.5)"), N_("PDF-Level")); // xxP + add_main_option_entry(OPTION_TYPE_BOOL, "export-latex", '\0', N_("Export: Export text separately to LaTeX file (PS/EPS/PDF). Include via \\input{file.tex}"), ""); // xxP + add_main_option_entry(OPTION_TYPE_BOOL, "export-use-hints", 't', N_("Export: Use stored filename and DPI hints when exporting object selected by --export-id."), ""); // Bxx + add_main_option_entry(OPTION_TYPE_STRING, "export-background", 'b', N_("Export: Background color for exported bitmaps (any SVG color string)."), N_("COLOR")); // Bxx + add_main_option_entry(OPTION_TYPE_DOUBLE, "export-background-opacity", 'y', N_("Export: Background opacity for exported bitmaps (either 0.0 to 1.0 or 1 to 255)."), N_("VALUE")); // Bxx + +#ifdef WITH_YAML + add_main_option_entry(OPTION_TYPE_FILENAME, "xverbs", '\0', N_("Process: xverb command file."), N_("XVERBS-FILENAME")); +#endif // WITH_YAML + +#ifdef WITH_DBUS + add_main_option_entry(OPTION_TYPE_BOOL, "dbus-listen", '\0', N_("D-Bus: Enter a listening loop for D-Bus messages in console mode."), ""); + add_main_option_entry(OPTION_TYPE_STRING, "dbus-name", '\0', N_("D-Bus: Specify the D-Bus name (default is 'org.inkscape')."), N_("BUS-NAME")); +#endif // WITH_DBUS + + signal_handle_local_options().connect(sigc::mem_fun(*this, &InkscapeApplication::on_handle_local_options)); + + // This is normally called for us... but after the "handle_local_options" signal is emitted. If + // we want to rely on actions for handling options, we need to call it here. This appears to + // have no unwanted side-effect. It will also trigger the call to on_startup(). + register_application(); +} + +Glib::RefPtr<InkscapeApplication> InkscapeApplication::create() +{ + return Glib::RefPtr<InkscapeApplication>(new InkscapeApplication()); +} + +SPDocument* +InkscapeApplication::get_active_document() +{ + // This should change based on last document window in focus if with GUI. But for now we're + // only using it for command line mode so return last document (the one currently be read in). + return _documents.back(); +} + +void +InkscapeApplication::on_startup() +{ + Gtk::Application::on_startup(); +} + +// Here are things that should be in on_startup() but cannot be as we don't set _with_gui until +// on_handle_local_options() is called. +void +InkscapeApplication::on_startup2() +{ + // This should be completely rewritten. + Inkscape::Application::create(nullptr, _with_gui); // argv appears to not be used. + + if (!_with_gui) { + return; + } + + // ======================= Actions (GUI) ====================== + add_action("new", sigc::mem_fun(*this, &InkscapeApplication::on_new )); + add_action("quit", sigc::mem_fun(*this, &InkscapeApplication::on_quit )); + + // ========================= GUI Init ========================= + Gtk::Window::set_default_icon_name("inkscape"); + Inkscape::UI::Widget::Panel::prep(); + + // ========================= Builder ========================== + _builder = Gtk::Builder::create(); + + try + { + _builder->add_from_file("ink-application.xml"); + } + catch (const Glib::Error& ex) + { + std::cerr << "InkscapeApplication: ink_application.xml file not read! " << ex.what() << std::endl; + } + + auto object = _builder->get_object("menu-application"); + auto menu = Glib::RefPtr<Gio::Menu>::cast_dynamic(object); + if (!menu) { + std::cerr << "InkscapeApplication: failed to load application menu!" << std::endl; + } else { + set_app_menu(menu); + } +} + +// Open document window with default document. Either this or on_open() is called. +void +InkscapeApplication::on_activate() +{ + on_startup2(); + + if (_with_gui) { + create_window(); + } else { + std::cerr << "InkscapeApplication::on_activate: Without GUI" << std::endl; + // Create blank document? + } +} + +// Open document window for each file. Either this or on_activate() is called. +// type_vec_files == std::vector<Glib::RefPtr<Gio::File> > +void +InkscapeApplication::on_open(const Gio::Application::type_vec_files& files, const Glib::ustring& hint) +{ + on_startup2(); + + for (auto file : files) { + if (_with_gui) { + // Create a window for each file. + + create_window(file); + + // Process each file. + for (auto action: _command_line_actions) { + activate_action( action.first, action.second ); + } + + } else { + + // Open file + SPDocument *doc = ink_file_open(file); + if (!doc) continue; + + // Add to Inkscape::Application... + INKSCAPE.add_document(doc); + + doc->ensureUpToDate(); // Or queries don't work! + + // Add to our application + _documents.push_back(doc); + + // process_file(file); + for (auto action: _command_line_actions) { + activate_action( action.first, action.second ); + } + + // Save... can't use action yet. + _file_export.do_export(doc, file->get_path()); + + // Remove from our application... we only have one in command-line mode. + _documents.pop_back(); + + // Close file + INKSCAPE.remove_document(doc); + delete doc; + } + } + + //Call the base class's implementation: + // Gtk::Application::on_open(files, hint); +} + +void +InkscapeApplication::create_window(const Glib::RefPtr<Gio::File>& file) +{ + SPDesktop* desktop = nullptr; + if (file) { + desktop = sp_file_new_default(); + sp_file_open(file->get_parse_name(), nullptr, false, true); + } else { + desktop = sp_file_new_default(); + } + + _documents.push_back(desktop->getDocument()); + + // Add to Gtk::Window to app window list. + add_window(*desktop->getToplevel()); +} + +// ========================= Callbacks ========================== + +/* + * Handle command line options. + * + * Options are processed in the order they appear in this function. + * We process in order: Print -> GUI -> Open -> Query -> Process -> Export. + * For each file without GUI: Open -> Query -> Process -> Export + * More flexible processing can be done via actions or xverbs. + */ +int +InkscapeApplication::on_handle_local_options(const Glib::RefPtr<Glib::VariantDict>& options) +{ + if (!options) { + std::cerr << "InkscapeApplication::on_handle_local_options: options is null!" << std::endl; + return -1; // Keep going + } + + // ===================== QUERY ===================== + // These are processed first as they result in immediate program termination. + if (options->contains("version")) { + activate_action("inkscape-version"); + return EXIT_SUCCESS; + } + + if (options->contains("extensions-directory")) { + activate_action("extensions-directory"); + return EXIT_SUCCESS; + } + + if (options->contains("verb-list")) { + activate_action("verb-list"); + return EXIT_SUCCESS; + } + + if (options->contains("action-list")) { + std::cerr << "action-list: not implemented" << std::endl; + list_actions(); // Doesn't work... member of Gio::ActionGroup, not Gio::ActionMap. + return EXIT_SUCCESS; + } + + // For options without arguments. + auto base = Glib::VariantBase(); + + // ====================== GUI ===================== + if (options->contains("without-gui")) _with_gui = false; + if (options->contains("with-gui")) _with_gui = true; + + // Some options should preclude using gui! + if (options->contains("query-id") || + options->contains("query-x") || + options->contains("query-all") || + options->contains("query-y") || + options->contains("query-width") || + options->contains("query-height") || + options->contains("export-type") || + options->contains("export-file") || + options->contains("export-overwrite") + ) { + _with_gui = false; + } + + // ==================== ACTIONS ==================== + // Actions as an argument string: e.g.: --actions="query-id:rect1;query-x". + // Actions will be processed in order that they are given in argument. + Glib::ustring actions; + if (options->contains("actions")) { + options->lookup_value("actions", actions); + + // Split action list + std::vector<Glib::ustring> tokens = Glib::Regex::split_simple("\\s*;\\s*", actions); + for (auto token : tokens) { + std::cout << token << std::endl; + std::vector<Glib::ustring> tokens2 = Glib::Regex::split_simple("\\s*:\\s*", token); + std::string action; + std::string value; + if (tokens2.size() > 0) { + action = tokens2[0]; + } + if (tokens2.size() > 1) { + value = tokens2[1]; + } + + Glib::RefPtr<Gio::Action> action_ptr = lookup_action(action); + if (action_ptr) { + // Doesn't seem to be a way to test this using the C++ binding without Glib-CRITICAL errors. + const GVariantType* gtype = g_action_get_parameter_type(action_ptr->gobj()); + if (gtype) { + // With value. + Glib::VariantType type = action_ptr->get_parameter_type(); + if (type.get_string() == "s") { + _command_line_actions.push_back( + std::make_pair( action, Glib::Variant<Glib::ustring>::create(value) )); + } else if (type.get_string() == "i") { + _command_line_actions.push_back( + std::make_pair( action, Glib::Variant<int>::create(std::stoi(value)))); + } else if (type.get_string() == "d") { + _command_line_actions.push_back( + std::make_pair( action, Glib::Variant<double>::create(std::stod(value)))); + } else { + std::cerr << "InkscapeApplication::on_handle_local_options: unhandled action value: " + << action << ": " << type.get_string() << std::endl; + } + } else { + // Stateless (i.e. no value). + _command_line_actions.push_back( std::make_pair( action, Glib::VariantBase() ) ); + } + } else { + std::cerr << "InkscapeApplication::on_handle_local_options: '" + << action << "' is not a valid action!" << std::endl; + } + } + } + + + // ================= OPEN/IMPORT =================== + + if (options->contains("pdf-page")) { // Maybe useful for other file types? + int page = 0; + options->lookup_value("pdf-page", page); + _command_line_actions.push_back( + std::make_pair("open-page", Glib::Variant<int>::create(page))); + } + + if (options->contains("convert-dpi-method")) { + Glib::ustring method; + options->lookup_value("convert-dpi-method", method); + if (!method.empty()) { + _command_line_actions.push_back( + std::make_pair("convert-dpi-method", Glib::Variant<Glib::ustring>::create(method))); + } + } + + if (options->contains("no-convert-text-baseline-spacing")) _command_line_actions.push_back(std::make_pair("no-convert-baseline", base)); + + + // ===================== QUERY ===================== + + // 'query-id' should be processed first! Idea: We could turn this into a comma separated list. + if (options->contains("query-id")) { + Glib::ustring query_id; + options->lookup_value("query-id", query_id); + if (!query_id.empty()) { + _command_line_actions.push_back( + std::make_pair("query-id", Glib::Variant<Glib::ustring>::create(query_id))); + } + } + + if (options->contains("query-all")) _command_line_actions.push_back(std::make_pair("query-all", base)); + if (options->contains("query-x")) _command_line_actions.push_back(std::make_pair("query-x", base)); + if (options->contains("query-y")) _command_line_actions.push_back(std::make_pair("query-y", base)); + if (options->contains("query-width")) _command_line_actions.push_back(std::make_pair("query-width", base)); + if (options->contains("query-height")) _command_line_actions.push_back(std::make_pair("query-height",base)); + + + // =================== PROCESS ===================== + + // Note: this won't work with --verb="FileSave,FileClose" unless some additional verb changes the file. FIXME + // One can use --verb="FileVacuum,FileSave,FileClose". + if (options->contains("vacuum-defs")) _command_line_actions.push_back(std::make_pair("vacuum-defs", base)); + + if (options->contains("select")) { + Glib::ustring select; + options->lookup_value("select", select); + if (!select.empty()) { + _command_line_actions.push_back( + std::make_pair("select", Glib::Variant<Glib::ustring>::create(select))); + } + } + + if (options->contains("verb")) { + Glib::ustring verb; + options->lookup_value("verb", verb); + if (!verb.empty()) { + _command_line_actions.push_back( + std::make_pair("verb", Glib::Variant<Glib::ustring>::create(verb))); + } + } + + // ==================== EXPORT ===================== + if (options->contains("export-file")) { + options->lookup_value("export-file", _file_export.export_filename); + } + + if (options->contains("export-type")) { + options->lookup_value("export-type", _file_export.export_type); + } + + if (options->contains("export-overwrite")) _file_export.export_overwrite = true; + + // Export - Geometry + if (options->contains("export-area")) { + options->lookup_value("export-area", _file_export.export_area); + } + + if (options->contains("export-area-drawing")) _file_export.export_area_drawing = true; + if (options->contains("export-area-page")) _file_export.export_area_page = true; + + if (options->contains("export-margin")) { + options->lookup_value("export-margin", _file_export.export_margin); + } + + if (options->contains("export-area-snap")) _file_export.export_area_snap = true; + + if (options->contains("export-width")) { + options->lookup_value("export-width", _file_export.export_width); + } + + if (options->contains("export-height")) { + options->lookup_value("export-height", _file_export.export_height); + } + + // Export - Options + if (options->contains("export-id")) { + options->lookup_value("export-id", _file_export.export_id); + } + + if (options->contains("export-id-only")) _file_export.export_id_only = true; + if (options->contains("export-plain-svg")) _file_export.export_plain_svg = true; + + if (options->contains("export-dpi")) { + options->lookup_value("export-dpi", _file_export.export_dpi); + } + + if (options->contains("export-ignore-filters")) _file_export.export_ignore_filters = true; + if (options->contains("export-text-to-path")) _file_export.export_text_to_path = true; + + if (options->contains("export-ps-level")) { + options->lookup_value("export-ps-level", _file_export.export_ps_level); + } + + if (options->contains("export-pdf-level")) { + options->lookup_value("export-pdf-level", _file_export.export_pdf_level); + } + + if (options->contains("export-latex")) _file_export.export_latex = true; + if (options->contains("export-use-hints")) _file_export.export_use_hints = true; + + if (options->contains("export-background")) { + options->lookup_value("export-background",_file_export.export_background); + } + + if (options->contains("export-background-opacity")) { + options->lookup_value("export-background-opacity", _file_export.export_background_opacity); + } + + + // ==================== D-BUS ====================== + +#ifdef WITH_DBUS + // Before initializing extensions, we must set the DBus bus name if required + if (options->contains("dbus-listen")) { + std::string dbus_name; + options->lookup_value("dbus-name", dbus_name); + if (!dbus_name.empty()) { + Inkscape::Extension::Dbus::dbus_set_bus_name(dbus_name.c_str()); + } + } +#endif + + return -1; // Keep going +} + +// ======================== Actions ========================= + +void +InkscapeApplication::on_new() +{ + create_window(); +} + +void +InkscapeApplication::on_quit() +{ + // Delete all windows (quit() doesn't do this). + std::vector<Gtk::Window*> windows = get_windows(); + for (auto window: windows) { + // Do something + } + + quit(); +} + +/* + 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/inkscape-application.h b/src/inkscape-application.h new file mode 100644 index 000000000..b176c2921 --- /dev/null +++ b/src/inkscape-application.h @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The main Inkscape application. + * + * Copyright (C) 2018 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#ifndef INKSCAPE_APPLICATION_H +#define INKSCAPE_APPLICATION_H + +/* + * The main Inkscape application. + * + * Copyright (C) 2018 Tavmjong Bah + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#include <gtkmm.h> + +#include "document.h" +#include "helper/action.h" +#include "io/file-export-cmd.h" // File export (non-verb) + +class InkscapeApplication : public Gtk::Application +{ +protected: + InkscapeApplication(); + +public: + static Glib::RefPtr<InkscapeApplication> create(); + + SPDocument* get_active_document(); + +protected: + void on_startup() override; + void on_startup2(); + void on_activate() override; + void on_open(const Gio::Application::type_vec_files& files, const Glib::ustring& hint) override; + +private: + void create_window(const Glib::RefPtr<Gio::File>& file = Glib::RefPtr<Gio::File>()); + +private: + // Callbacks + int on_handle_local_options(const Glib::RefPtr<Glib::VariantDict>& options); + + // Actions + void on_new(); + void on_quit(); + void on_about(); + + Glib::RefPtr<Gtk::Builder> _builder; + + bool _with_gui; + InkFileExportCmd _file_export; + + // Documents are owned by the application which is responsible for opening/saving/exporting. WIP + std::vector<SPDocument*> _documents; + + // Actions from the command line or file. + std::vector<std::pair<std::string, Glib::VariantBase> > _command_line_actions; +}; + +#endif // INKSCAPE_APPLICATION_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 : diff --git a/src/inkscape-main.cpp b/src/inkscape-main.cpp new file mode 100644 index 000000000..52d3e7873 --- /dev/null +++ b/src/inkscape-main.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Inkscape - an ambitious vector drawing program + * + * Authors: + * Tavmjong Bah + * + * (C) 2018 Tavmjong Bah + * + * Released under GNU GPL v2+, read the file 'COPYING' for more information. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "inkscape-application.h" + + +int main(int argc, char *argv[]) +{ + auto application = InkscapeApplication::create(); + + return application->run(argc, argv); +} + +/* + 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/inkscape.cpp b/src/inkscape.cpp index d01b53e57..859f23ba9 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -67,7 +67,7 @@ Inkscape::Application * Inkscape::Application::_S_inst = nullptr; bool Inkscape::Application::_crashIsHappening = false; -#define DESKTOP_IS_ACTIVE(d) (!INKSCAPE._desktops->empty() && ((d) == INKSCAPE._desktops->front())) +#define DESKTOP_IS_ACTIVE(d) (INKSCAPE._desktops != nullptr && !INKSCAPE._desktops->empty() && ((d) == INKSCAPE._desktops->front())) static void (* segv_handler) (int) = SIG_DFL; static void (* abrt_handler) (int) = SIG_DFL; @@ -612,7 +612,7 @@ Application::~Application() _S_inst = nullptr; // this will probably break things refCount = 0; - gtk_main_quit (); + // gtk_main_quit (); } /** Sets the keyboard modifer to map to Alt. @@ -1282,6 +1282,7 @@ Application::action_context_for_document(SPDocument *doc) // Document is not associated with any desktops - maybe we're in command-line mode std::map<SPDocument *, AppSelectionModel *>::iterator sel_iter = _selection_models.find(doc); if (sel_iter == _selection_models.end()) { + std::cout << "Application::action_context_for_document: no selection model" << std::endl; return Inkscape::ActionContext(); } return Inkscape::ActionContext(sel_iter->second->getSelection()); @@ -1312,7 +1313,7 @@ Application::exit () signal_shut_down.emit(); Inkscape::Preferences::unload(); - gtk_main_quit (); + //gtk_main_quit (); } diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt index f31788b22..4315c8cbc 100644 --- a/src/io/CMakeLists.txt +++ b/src/io/CMakeLists.txt @@ -3,6 +3,8 @@ set(io_SRC bufferstream.cpp dir-util.cpp + file.cpp + file-export-cmd.cpp gzipstream.cpp inkscapestream.cpp resource.cpp @@ -17,6 +19,8 @@ set(io_SRC # Headers bufferstream.h dir-util.h + file.h + file-export-cmd.h gzipstream.h inkscapestream.h resource.h diff --git a/src/io/file-export-cmd.cpp b/src/io/file-export-cmd.cpp new file mode 100644 index 000000000..46847634b --- /dev/null +++ b/src/io/file-export-cmd.cpp @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * File export from the command line. This code, greatly modified, use to be in main.cpp. It should + * be replaced by code shared code shared with the file dialog (Gio::Actions?). + * + * Copyright (C) 2018 Tavmjong Bah + * + * Git blame shows that bulia byak is the main author of the orignal export code from + * main.cpp. Other authors of note include Nicolas Dufour, Vinicius dos Santos Oliveira, and Bob + * Jamison; none of whom bothered to add their names to the copyright of main.cc. + * + * The contents of this file may be used under the GNU General Public License Version 2 or later. + * + */ + +#include "file-export-cmd.h" + +#include <png.h> // PNG export + +#include "document.h" +#include "object/sp-item.h" +#include "object/sp-root.h" +#include "object/sp-text.h" +#include "object/sp-flowtext.h" +#include "object/sp-namedview.h" +#include "object/sp-object-group.h" +#include "path-chemistry.h" // sp_item_list_to_curves +#include "text-editing.h" // te_update_layout_now_recursive +#include "selection-chemistry.h" // fit_canvas_to_drawing +#include "svg/svg-color.h" // Background color +#include "helper/png-write.h" // PNG Export + +#include "extension/extension.h" +#include "extension/system.h" +#include "extension/db.h" +#include "extension/output.h" +#include "extension/init.h" + +InkFileExportCmd::InkFileExportCmd() + : over_write(false) + , export_type("svg") + , export_overwrite(false) + , export_area_drawing(false) + , export_area_page(false) + , export_margin(0) + , export_area_snap(false) + , export_use_hints(false) + , export_width(0) + , export_height(0) + , export_dpi(0) + , export_ignore_filters(false) + , export_text_to_path(false) + , export_ps_level(3) + , export_pdf_level("1.5") + , export_latex(false) + , export_id_only(false) + , export_background_opacity(1.0) + , export_plain_svg(false) +{ +} + +void +InkFileExportCmd::do_export(SPDocument* doc, std::string filename_in) +{ + + std::string filename_out = get_filename_out(filename_in); + + // We need a valid file name to write to unless we're using PNG export hints. + if (!(export_type == "png" && export_use_hints)) { + + // Check for file name. + if (filename_out.empty()) { + std::cerr << "InkFileExportCmd::do_export: Could not determine ouput file name!" << std::endl; + return; + } + + // Check if directory exists. + std::string directory = Glib::path_get_dirname(filename_out); + if (!Glib::file_test(directory, Glib::FILE_TEST_IS_DIR)) { + std::cerr << "InkFileExportCmd::do_export: File path includes directory that does not exist! " << directory << std::endl; + return; + } + } + + if (export_type == "svg") { + + do_export_svg(doc, filename_out); + + } else if (export_type == "png") { + + do_export_png(doc, filename_out); + + } else if (export_type == "ps") { + + do_export_ps_pdf(doc, filename_out, "image/x-postscript"); + + } else if (export_type == "eps") { + + do_export_ps_pdf(doc, filename_out, "image/x-e-postscript"); + + } else if (export_type == "pdf") { + + do_export_ps_pdf(doc, filename_out, "application/pdf"); + + } else if (export_type == "emf") { + + do_export_win_metafile(doc, filename_out, "image/x-emf"); + + } else if (export_type == "wmf") { + + do_export_win_metafile(doc, filename_out, "image/x-wmf"); + + } else if (export_type == "xaml") { + + do_export_win_metafile(doc, filename_out, "text/xml+xaml"); + + } else { + + std::cerr << "InkFileExportCmd::export: Unknown export type: " << export_type << std::endl; + } +} + +std::string +InkFileExportCmd::get_filename_out(std::string filename_in) +{ + + if (export_type != "svg" && + export_type != "png" && + export_type != "ps" && + export_type != "eps" && + export_type != "pdf" && + export_type != "emf" && + export_type != "wmf" && + export_type != "xaml" && + export_type != "svg" ) { + std::cerr << "InkFileExportCmd::get_filename_out: Invalid export-type: " << export_type + << " Allowed values: [svg,png,ps,eps,pdf,emf,wmf,xaml]." << std::endl; + return (std::string()); + } + + // Writing to pipe. + if (export_filename == "-") { + return export_filename; + } + + // Use export_filename if given. + if (!export_filename.empty()) { + // Check file extension matches file_type. + auto extension_pos = export_filename.find_last_of('.'); + + if (extension_pos == std::string::npos) { + std::cerr << "InkFileExportCmd::get_filename_out: no export_filename extension: " << export_filename << std::endl; + return (std::string()); + } + + std::string extension = export_filename.substr(extension_pos+1); + if (export_type != extension) { + std::cerr << "InkFileExportCmd::get_filename_out: mismatch between export_filename extension and export_type: " + << extension << ", " << export_type << std::endl; + return (std::string()); + } + + return export_filename; + } + + // Construct output filename from input filename and export_type. + auto extension_pos = filename_in.find_last_of('.'); + if (extension_pos == std::string::npos) { + std::cerr << "InkFileExportCmd::get_filename_out: cannot determine input file type from filename extension: " << filename_in << std::endl; + return (std::string()); + } + + std::string extension = filename_in.substr(extension_pos+1); + if (export_type == extension) { + if (export_overwrite) { + return filename_in; + } else { + // Add extra characters to filename. + filename_in.insert(extension_pos, "_out"); + return filename_in; + } + } else { + return (filename_in.substr(0,extension_pos+1) + export_type); + } +} + +/** + * Perform an SVG export + * + * \param doc Document to export. + */ +int +InkFileExportCmd::do_export_svg(SPDocument* doc, std::string filename_out) +{ + if (export_text_to_path) { + std::vector<SPItem*> items; + SPRoot *root = doc->getRoot(); + doc->ensureUpToDate(); + for (auto& iter: root->children) { + SPItem* item = (SPItem*) &iter; + if (! (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item) || SP_IS_GROUP(item))) { + continue; + } + + te_update_layout_now_recursive(item); + items.push_back(item); + } + + std::vector<SPItem*> selected; // Not used + std::vector<Inkscape::XML::Node*> to_select; // Not used + + sp_item_list_to_curves(items, selected, to_select); + } + + if (export_margin != 0) { + gdouble margin = export_margin; + doc->ensureUpToDate(); + SPNamedView *nv; + Inkscape::XML::Node *nv_repr; + if ((nv = sp_document_namedview(doc, nullptr)) && (nv_repr = nv->getRepr())) { + sp_repr_set_svg_double(nv_repr, "fit-margin-top", margin); + sp_repr_set_svg_double(nv_repr, "fit-margin-left", margin); + sp_repr_set_svg_double(nv_repr, "fit-margin-right", margin); + sp_repr_set_svg_double(nv_repr, "fit-margin-bottom", margin); + } + } + + if(export_area_drawing) { + fit_canvas_to_drawing(doc, export_margin != 0 ? true : false); + } + + if(!export_id.empty()) { + doc->ensureUpToDate(); + + // "crop" the document to the specified object, cleaning as we go. + SPObject *obj = doc->getObjectById(export_id); + if (export_id_only) { + // If -j then remove all other objects to complete the "crop" + doc->getRoot()->cropToObject(obj); + } + Inkscape::ObjectSet s(doc); + s.set(obj); + if (!export_area_page) { + s.fitCanvas(export_margin ? true : false); + } + } + + int ret = 0; + if (export_plain_svg) { + + try { + Inkscape::Extension::save(Inkscape::Extension::db.get("org.inkscape.output.svg.plain"), doc, filename_out.c_str(), false, + false, false, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY); + } catch (Inkscape::Extension::Output::save_failed &e) { + std::cerr << "InkFileExportCmd::do_export_svg: Failed to save SVG to: " << filename_out << std::endl; + ret = 1; + } + + } else { + + // Export as inkscape SVG. + try { + Inkscape::Extension::save(Inkscape::Extension::db.get("org.inkscape.output.svg.inkscape"), doc, filename_out.c_str(), false, + false, false, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG); + } catch (Inkscape::Extension::Output::save_failed &e) { + std::cerr << "InkFileExportCmd::do_export_svg: Failed to save Inkscape SVG to: " << filename_out << std::endl; + ret = 1; + } + } + return ret; +} + + +/** + * Perform a PNG export + * + * \param doc Document to export. + */ +int +InkFileExportCmd::do_export_png(SPDocument *doc, std::string filename_in) +{ + std::string filename = filename_in; + + bool filename_from_hint = false; + gdouble dpi = 0.0; + + if (export_use_hints && (export_id.empty() && !export_area_drawing)) { + g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored."); + } + + std::vector<SPItem*> items; + + // Find export area. + Geom::Rect area; + if (!export_id.empty() || export_area_drawing) { + + SPObject *o = nullptr; + SPObject *o_area = nullptr; + if (!export_id.empty() && export_area_drawing) { + o = doc->getObjectById(export_id); + o_area = doc->getRoot(); + } else if (!export_id.empty()) { + o = doc->getObjectById(export_id); + o_area = o; + } else if (export_area_drawing) { + o = doc->getRoot(); + o_area = o; + } + + if (o) { + if (!SP_IS_ITEM (o)) { + g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", export_id); + return 1; + } + + items.push_back(SP_ITEM(o)); + + if (export_id_only) { +// do_print_message("Exporting only object with id=\"%s\"; all other objects hidden\n", export_id); + } + + if (export_use_hints) { + + // retrieve export filename hint + const gchar *fn_hint = o->getRepr()->attribute("inkscape:export-filename"); + if (fn_hint) { + if (!filename.empty()) { + g_warning ("Using export filename from the command line. Filename hint %s is ignored.", fn_hint); + } else { + filename = fn_hint; + filename_from_hint = true; + } + } else { + g_warning ("Export filename hint not found for the object."); + } + + // retrieve export dpi hints + const gchar *dpi_hint = o->getRepr()->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now + if (dpi_hint) { + if (export_dpi || export_width || export_height) { + g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint); + } else { + dpi = atof(dpi_hint); + } + } else { + g_warning ("Export DPI hint not found for the object."); + } + } + + // write object bbox to area + doc->ensureUpToDate(); + Geom::OptRect areaMaybe = static_cast<SPItem *>(o_area)->desktopVisualBounds(); + if (areaMaybe) { + area = *areaMaybe; + } else { + g_warning("Unable to determine a valid bounding box. Nothing exported."); + return 1; + } + } else { + g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", export_id); + return 1; + } + } + + if (!export_area.empty()) { + /* Try to parse area (given in SVG pixels) */ + gdouble x0,y0,x1,y1; + if (sscanf(export_area.c_str(), "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) != 4) { + g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", export_area); + return 1; + } + area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1)); + } else if (export_area_page || !(!export_id.empty() || export_area_drawing)) { + /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */ + doc->ensureUpToDate(); + Geom::Point origin(doc->getRoot()->x.computed, doc->getRoot()->y.computed); + area = Geom::Rect(origin, origin + doc->getDimensions()); + } + // End finding area. + + // Check we have a filename. + if (filename.empty()) { + g_warning ("No export filename given and no filename hint. Nothing exported."); + return 1; + } + + if (export_dpi != 0.0 && dpi == 0.0) { + dpi = export_dpi; + if ((dpi < 0.1) || (dpi > 10000.0)) { + g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", export_dpi); + return 1; + } +// do_print_message("DPI: %g\n", dpi); + } + + if (export_area_snap) { + area = area.roundOutwards(); + } + + // default dpi + if (dpi == 0.0) { + dpi = Inkscape::Util::Quantity::convert(1, "in", "px"); + } + + unsigned long int width = 0; + unsigned long int height = 0; + + if (export_width != 0) { + errno=0; + width = export_width; + if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) { + g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX); + return 1; + } + dpi = (gdouble) Inkscape::Util::Quantity::convert(width, "in", "px") / area.width(); + } else { + width = (unsigned long int) (Inkscape::Util::Quantity::convert(area.width(), "px", "in") * dpi + 0.5); + } + + if (export_height != 0) { + errno=0; + height = export_height; + if ((height < 1) || (height > PNG_UINT_31_MAX)) { + g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX); + return 1; + } + dpi = (gdouble) Inkscape::Util::Quantity::convert(height, "in", "px") / area.height(); + } else { + height = (unsigned long int) (Inkscape::Util::Quantity::convert(area.height(), "px", "in") * dpi + 0.5); + } + + guint32 bgcolor = 0x00000000; + if (!export_background.empty()) { + // override the page color + bgcolor = sp_svg_read_color(export_background.c_str(), 0xffffff00); + bgcolor |= 0xff; // default is no opacity + } else { + // read from namedview + Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview"); + if (nv && nv->attribute("pagecolor")){ + bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00); + } + if (nv && nv->attribute("inkscape:pageopacity")){ + double opacity = 1.0; + sp_repr_get_double (nv, "inkscape:pageopacity", &opacity); + bgcolor |= SP_COLOR_F_TO_U(opacity); + } + } + bgcolor &= (guint32) 0xffffff00; + + if (export_background_opacity > 1.0) { + float value = CLAMP (export_background_opacity, 1.0f, 255.0f); + bgcolor |= (guint32) floor(value); + } else { + float value = CLAMP (export_background_opacity, 0.0f, 1.0f); + bgcolor |= SP_COLOR_F_TO_U(value); + } + + std::string path = filename; // File names are std::string, not Glib::ustring! + if (filename_from_hint) { + //Make relative paths go from the document location, if possible: + if (!Glib::path_is_absolute(filename) && doc->getURI()) { + std::string dirname = Glib::path_get_dirname(doc->getURI()); + if (!dirname.empty()) { + path = Glib::build_filename(dirname, filename); + } + } + } + + // Check if directory exists + std::string directory = Glib::path_get_dirname(path); + if (!Glib::file_test(directory, Glib::FILE_TEST_IS_DIR)) { + std::cerr << "File path " << path << " includes directory that doesn't exist." << std::endl; + return 1; + } + + // Do we really need to print this? + std::cerr << "Background RRGGBBAA: " << std::hex << bgcolor << std::dec << std::endl; + std::cerr << "Area " + << area[Geom::X][0] << ":" << area[Geom::Y][0] << ":" + << area[Geom::X][1] << ":" << area[Geom::Y][1] << " exported to " + << width << " x " << height << " pixels (" << dpi << " dpi)" << std::endl; + + reverse(items.begin(),items.end()); + + if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) { + if( sp_export_png_file(doc, path.c_str(), area, width, height, dpi, + dpi, bgcolor, nullptr, nullptr, true, export_id_only ? items : std::vector<SPItem*>()) == 1 ) { +// do_print_message("Bitmap saved as: %s\n", filename.c_str()); + } else { + g_warning("Bitmap failed to save to: %s", filename.c_str()); + return 1; + } + } else { + g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX); + return 1; + } + + return 0; +} + + +/** + * Perform a PDF/PS/EPS export + * + * \param doc Document to export. + * \param filename File to write to. + * \param mime MIME type to export as. + */ +int +InkFileExportCmd::do_export_ps_pdf(SPDocument* doc, std::string filename, std::string mime_type) +{ + // Check if we support mime type. + Inkscape::Extension::DB::OutputList o; + Inkscape::Extension::db.get_output_list(o); + Inkscape::Extension::DB::OutputList::const_iterator i = o.begin(); + while (i != o.end() && strcmp( (*i)->get_mimetype(), mime_type.c_str() ) != 0) { + i++; + } + + if (i == o.end()) { + std::cerr << "InkFileExportCmd::do_export_ps_pdf: Could not find an extension to export to MIME type: " << mime_type << std::endl; + return 1; + } + + // Export only object with given id. + if (!export_id.empty()) { + SPObject *o = doc->getObjectById(export_id); + if (o == nullptr) { + g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", export_id); + return 1; + } + (*i)->set_param_string ("exportId", export_id.c_str()); + } else { + (*i)->set_param_string ("exportId", ""); + } + + // Set export area. + if (export_area_page && export_area_drawing) { + g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect."); + export_area_drawing = false; + } + + if (export_area_drawing) { + (*i)->set_param_optiongroup ("area", "drawing"); + } + + if (export_area_page) { + if (export_type == "eps") { + g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the page, --export-area-page will clip it to drawing."); + } + (*i)->set_param_optiongroup ("area", "page"); + } + + if (!export_area_drawing && !export_area_page && export_id.empty()) { + // neither is set, set page as default for ps/pdf and drawing for eps + if (export_type == "eps") { + try { + (*i)->set_param_optiongroup("area", "drawing"); + } catch (...) {} + } + } + + // Set export options. + if (export_text_to_path) { + (*i)->set_param_optiongroup("textToPath", "paths"); + } else if (export_latex) { + (*i)->set_param_optiongroup("textToPath", "LaTeX"); + } else { + (*i)->set_param_optiongroup("textToPath", "embed"); + } + + if (export_ignore_filters) { + (*i)->set_param_bool("blurToBitmap", false); + } else { + (*i)->set_param_bool("blurToBitmap", true); + + gdouble dpi = 96.0; + if (export_dpi) { + dpi = export_dpi; + if ((dpi < 1) || (dpi > 10000.0)) { + g_warning("DPI value %d out of range [1 - 10000]. Using 96 dpi instead.", export_dpi); + dpi = 96; + } + } + + (*i)->set_param_int("resolution", (int) dpi); + } + + (*i)->set_param_float("bleed", export_margin); + + // handle --export-pdf-version + if (mime_type == "application/pdf") { + bool set_export_pdf_version_fail = true; + const gchar *pdfver_param_name = "PDFversion"; + if (!export_pdf_level.empty()) { + // combine "PDF " and the given command line + std::string version_gui_string = std::string("PDF ") + export_pdf_level; + try{ + // first, check if the given pdf version is selectable in the ComboBox + if((*i)->get_param_enum_contains("PDFversion", version_gui_string.c_str())) { + (*i)->set_param_enum(pdfver_param_name, version_gui_string.c_str()); + set_export_pdf_version_fail=false; + } else { + g_warning("Desired PDF export version \"%s\" not supported! Hint: input one of the versions found in the pdf export dialog e.g. \"1.4\".", + export_pdf_level); + } + } catch (...) { + // can be thrown along the way: + // throw Extension::param_not_exist(); + // throw Extension::param_not_enum_param(); + g_warning("Parameter or Enum \"%s\" might not exist", pdfver_param_name); + } + } + + // set default pdf export version to 1.4, also if something went wrong + if(set_export_pdf_version_fail) { + (*i)->set_param_enum(pdfver_param_name, "PDF 1.4"); + } + } + + if (mime_type == "image/x-postscript" || mime_type == "image/x-e-postscript") { + if ( export_ps_level < 2 || export_ps_level > 3 ) { + g_warning("Only supported PostScript levels are 2 and 3." + " Defaulting to 2."); + export_ps_level = 2; + } + + (*i)->set_param_enum("PSlevel", (export_ps_level == 3) + ? "PostScript level 3" : "PostScript level 2"); + } + + try { + (*i)->save(doc, filename.c_str()); + } catch(...) { + std::cerr << "Failed to save PS/EPS/PDF to: " << filename << std::endl; + } + return 0; +} + +/** + * Export a document to EMF or WMF + * + * \param doc Document to export. + * \param filename to export to. + * \param mime MIME type to export as (should be "image/x-emf" or "image/x-wmf") + */ +int +InkFileExportCmd::do_export_win_metafile(SPDocument* doc, std::string filename, std::string mime_type) +{ + // Check if we support mime type. + Inkscape::Extension::DB::OutputList o; + Inkscape::Extension::db.get_output_list(o); + Inkscape::Extension::DB::OutputList::const_iterator i = o.begin(); + while (i != o.end() && strcmp( (*i)->get_mimetype(), mime_type.c_str() ) != 0) { + i++; + } + + if (i == o.end()) + { + std::cerr << "InkFileExportCmd::do_export_win_metafile_common: Could not find an extension to export to MIME type: " << mime_type << std::endl; + return 1; + } + + (*i)->save(doc, filename.c_str()); + return 0; +} + +/* + 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/file-export-cmd.h b/src/io/file-export-cmd.h new file mode 100644 index 000000000..0f7dd994b --- /dev/null +++ b/src/io/file-export-cmd.h @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * File export from the command line. This code use to be in main.cpp. It should be + * replaced by shared code (Gio::Actions?) for export from the file dialog. + * + * 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_EXPORT_CMD_H +#define INK_FILE_EXPORT_CMD_H + +#include <iostream> +#include <gtkmm.h> + +class SPDocument; + +class InkFileExportCmd { + +public: + InkFileExportCmd(); + + void do_export(SPDocument* doc, std::string filename_in=""); + +private: + + std::string get_filename_out(std::string filename_in=""); + int do_export_svg( SPDocument* doc, std::string filename_out); + int do_export_png( SPDocument* doc, std::string filename_out); + int do_export_ps_pdf(SPDocument* doc, std::string filename_out, std::string mime_type); + int do_export_win_metafile(SPDocument* doc, std::string filename_out, std::string mime_type); + +public: + // Should be private, but this is just temporary code (I hope!). + + bool over_write; + + // One-to-one correspondence with command line options + std::string export_filename; // Only if one file is processed! + + Glib::ustring export_type; + bool export_overwrite; + + Glib::ustring export_area; + bool export_area_drawing; + bool export_area_page; + int export_margin; + bool export_area_snap; + int export_width; + int export_height; + + int export_dpi; + bool export_ignore_filters; + bool export_text_to_path; + int export_ps_level; + Glib::ustring export_pdf_level; + bool export_latex; + Glib::ustring export_id; + bool export_id_only; + bool export_use_hints; + Glib::ustring export_background; + double export_background_opacity; + bool export_plain_svg; +}; + +#endif // INK_FILE_EXPORT_CMD_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 : diff --git a/src/io/file.cpp b/src/io/file.cpp new file mode 100644 index 000000000..3505f018d --- /dev/null +++ b/src/io/file.cpp @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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. + * + */ + +#include <iostream> +#include <gtkmm.h> + +#include "file.h" + +#include "document.h" + +#include "extension/system.h" // Extension::open() +#include "extension/extension.h" +#include "extension/db.h" +#include "extension/output.h" +#include "extension/input.h" + +// SPDocument* +// ink_file_new(const std::string &Template) +// { +// SPDocument *doc = SPDocument::createNewDoc( Template, true, true ); +// return doc; +// } + + +SPDocument* +ink_file_open(const Glib::RefPtr<Gio::File>& file) +{ + + SPDocument *doc = nullptr; + + std::string path = file->get_path(); + + try { + doc = Inkscape::Extension::open(nullptr, path.c_str()); + } catch (Inkscape::Extension::Input::no_extension_found &e) { + doc = nullptr; + } catch (Inkscape::Extension::Input::open_failed &e) { + doc = nullptr; + } + + // Try to open explicitly as SVG. + if (doc == nullptr) { + try { + doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), path.c_str()); + } catch (Inkscape::Extension::Input::no_extension_found &e) { + doc = nullptr; + } catch (Inkscape::Extension::Input::open_failed &e) { + doc = nullptr; + } + } + if (doc == nullptr) { + std::cerr << "ink_file_open: '" << path << "' cannot be opened!" << std::endl; + } + + return doc; +} + diff --git a/src/io/file.h b/src/io/file.h new file mode 100644 index 000000000..e1b91adcd --- /dev/null +++ b/src/io/file.h @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * 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<Gio::File>& file = Glib::RefPtr<Gio::File>()); + +// To do: +// ink_file_save() +// ink_file_export() +// ink_file_import() + + + +#endif // INK_FILE_IO_H diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 0f6269611..000000000 --- a/src/main.cpp +++ /dev/null @@ -1,2251 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Inkscape - an ambitious vector drawing program - * - * Authors: - * Lauris Kaplinski <lauris@kaplinski.com> - * Frank Felfe <innerspace@iname.com> - * Davide Puricelli <evo@debian.org> - * Mitsuru Oka <oka326@parkcity.ne.jp> - * Masatake YAMATO <jet@gyve.org> - * F.J.Franklin <F.J.Franklin@sheffield.ac.uk> - * Michael Meeks <michael@helixcode.com> - * Chema Celorio <chema@celorio.com> - * Pawel Palucha - * Bryce Harrington <bryce@bryceharrington.org> - * ... and various people who have worked with various projects - * Jon A. Cruz <jon@oncruz.org> - * Abhishek Sharma - * Marc Jeanmougin - * - * Copyright (C) 1999-2004 authors - * Copyright (C) 2001-2002 Ximian, Inc. - * - * Released under GNU GPL v2+, read the file 'COPYING' for more information. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -// This has to be included prior to anything that includes setjmp.h, it croaks otherwise -#include <png.h> - -#include <cstring> - -#include <popt.h> -#ifndef POPT_TABLEEND -#define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL } -#endif /* Not def: POPT_TABLEEND */ - -#include <libxml/tree.h> - -#include "ui/widget/panel.h" // This has to be the first to include <glib.h> because of Glibmm's dependence on a deprecated feature... - -#include <glibmm/i18n.h> -#include <glibmm/convert.h> -#include <glibmm/fileutils.h> -#include <glibmm/miscutils.h> -#include <glibmm/main.h> -#include <gtkmm/main.h> -#include <gtkmm/window.h> -#include <gdkmm/screen.h> - -#include <cerrno> - -#ifdef _WIN32 -#include <windows.h> -#include "io/registrytool.h" -#endif // _WIN32 - -#include "inkscape.h" -#include "inkscape-version.h" - -#include "color.h" -#include "document.h" -#include "file.h" -#include "layer-model.h" -#include "path-chemistry.h" -#include "path-prefix.h" -#include "print.h" -#include "selection-chemistry.h" -#include "selection.h" -#include "text-editing.h" -#include "verbs.h" - -#include "debug/logger.h" -#include "debug/log-display-config.h" - -#include "extension/extension.h" -#include "extension/db.h" -#include "extension/output.h" -#include "extension/input.h" -#include "extension/init.h" -// Not ideal, but there doesn't appear to be a nicer system in place for -// passing command-line parameters to extensions before initialization... -#ifdef WITH_DBUS -#include "extension/dbus/dbus-init.h" -#endif // WITH_DBUS - -#include "helper/action-context.h" -#include "helper/png-write.h" -#ifdef ENABLE_NLS -#include "helper/gettext.h" -#endif - -#include "inkgc/gc-core.h" - -#include "io/resource.h" -#include "io/sys.h" - -#include "object/object-set.h" -#include "object/sp-guide.h" -#include "object/sp-root.h" -#include "object/sp-namedview.h" -#include "object/sp-text.h" -#include "object/sp-flowtext.h" - -#include "ui/interface.h" - -#include "svg/svg.h" -#include "svg/svg-color.h" -#include "svg/stringstream.h" - -#include "xml/repr.h" - -#include "main-cmdlineact.h" -#include "main-cmdlinexact.h" - - -enum { - SP_ARG_NONE, - SP_ARG_NOGUI, - SP_ARG_GUI, - SP_ARG_FILE, - SP_ARG_XVERBS, - SP_ARG_PRINT, - SP_ARG_EXPORT_PNG, - SP_ARG_EXPORT_DPI, - SP_ARG_EXPORT_AREA, - SP_ARG_EXPORT_AREA_DRAWING, - SP_ARG_EXPORT_AREA_PAGE, - SP_ARG_EXPORT_MARGIN, - SP_ARG_EXPORT_AREA_SNAP, - SP_ARG_EXPORT_WIDTH, - SP_ARG_EXPORT_HEIGHT, - SP_ARG_EXPORT_ID, - SP_ARG_EXPORT_ID_ONLY, - SP_ARG_EXPORT_USE_HINTS, - SP_ARG_EXPORT_BACKGROUND, - SP_ARG_EXPORT_BACKGROUND_OPACITY, - SP_ARG_EXPORT_SVG, - SP_ARG_EXPORT_INKSCAPE_SVG, - SP_ARG_EXPORT_PS, - SP_ARG_EXPORT_EPS, - SP_ARG_EXPORT_PS_LEVEL, - SP_ARG_EXPORT_PDF, - SP_ARG_EXPORT_PDF_VERSION, - SP_ARG_EXPORT_LATEX, - SP_ARG_EXPORT_EMF, - SP_ARG_EXPORT_WMF, - SP_ARG_EXPORT_XAML, - SP_ARG_EXPORT_TEXT_TO_PATH, - SP_ARG_EXPORT_IGNORE_FILTERS, - SP_ARG_PDF_PAGE, - SP_ARG_EXTENSIONDIR, - SP_ARG_QUERY_X, - SP_ARG_QUERY_Y, - SP_ARG_QUERY_WIDTH, - SP_ARG_QUERY_HEIGHT, - SP_ARG_QUERY_ALL, - SP_ARG_QUERY_ID, - SP_ARG_SHELL, - SP_ARG_VERSION, - SP_ARG_VACUUM_DEFS, - SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING, - SP_ARG_CONVERT_DPI_METHOD, -#ifdef WITH_DBUS - SP_ARG_DBUS_LISTEN, - SP_ARG_DBUS_NAME, -#endif // WITH_DBUS - SP_ARG_VERB_LIST, - SP_ARG_VERB, - SP_ARG_SELECT, - SP_ARG_LAST -}; - -int sp_main_gui(int argc, char const **argv); -int sp_main_console(int argc, char const **argv); -static int do_export_png(SPDocument *doc); -static int do_export_svg(SPDocument* doc); -static int do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime); -static int do_export_emf(SPDocument* doc, gchar const* uri, char const *mime); -static int do_export_wmf(SPDocument* doc, gchar const* uri, char const *mime); -static int do_export_xaml(SPDocument* doc, gchar const* uri, char const *mime); -static int do_export_win_metafile_common(SPDocument* doc, gchar const* uri, char const *mime); -static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id); -static void do_query_all (SPDocument *doc); -static void do_query_all_recurse (SPObject *o); -static void do_print_message(const char *message, ...); -static void detect_pipe_in_filename(const char *filename); - -static gchar *sp_global_printer = nullptr; -static gchar *sp_export_png = nullptr; -static gchar *sp_export_dpi = nullptr; -static gchar *sp_export_area = nullptr; -static gboolean sp_export_area_drawing = FALSE; -static gboolean sp_export_area_page = FALSE; -static gchar *sp_export_margin = nullptr; -static gboolean sp_export_latex = FALSE; -static gchar *sp_export_width = nullptr; -static gchar *sp_export_height = nullptr; -static gchar *sp_export_id = nullptr; -static gchar *sp_export_background = nullptr; -static gchar *sp_export_background_opacity = nullptr; -static gboolean sp_export_area_snap = FALSE; -static gboolean sp_export_use_hints = FALSE; -static gboolean sp_export_id_only = FALSE; -static gchar *sp_export_svg = nullptr; -static gchar *sp_export_inkscape_svg = nullptr; -static gchar *sp_export_ps = nullptr; -static gchar *sp_export_eps = nullptr; -static gint sp_export_ps_level = 3; -static gchar *sp_export_pdf = nullptr; -static gchar *sp_export_pdf_version = nullptr; -static gchar *sp_export_emf = nullptr; -static gchar *sp_export_wmf = nullptr; -static gchar *sp_export_xaml = nullptr; -static gboolean sp_export_text_to_path = FALSE; -static gboolean sp_export_ignore_filters = FALSE; -static gboolean sp_export_font = FALSE; -static gint sp_pdf_page = 1; -static gboolean sp_query_x = FALSE; -static gboolean sp_query_y = FALSE; -static gboolean sp_query_width = FALSE; -static gboolean sp_query_height = FALSE; -static gboolean sp_query_all = FALSE; -static gchar *sp_query_id = nullptr; -static gboolean sp_shell = FALSE; -static gboolean sp_vacuum_defs = FALSE; -#ifdef WITH_DBUS -static gboolean sp_dbus_listen = FALSE; -static gchar *sp_dbus_name = NULL; -#endif // WITH_DBUS -static gchar *sp_export_png_utf8 = nullptr; -static gchar *sp_export_svg_utf8 = nullptr; -static gchar *sp_export_inkscape_svg_utf8 = nullptr; -static gchar *sp_global_printer_utf8 = nullptr; -static gboolean sp_writingToPipe = FALSE; - -#ifdef WITH_YAML -static gchar *sp_xverbs_yaml_utf8 = nullptr; -static gchar *sp_xverbs_yaml = nullptr; -#endif // WITH_YAML - -/** - * Reset variables to default values. - */ -static void resetCommandlineGlobals() { - sp_global_printer = nullptr; - sp_export_png = nullptr; - sp_export_dpi = nullptr; - sp_export_area = nullptr; - sp_export_area_drawing = FALSE; - sp_export_area_page = FALSE; - sp_export_margin = nullptr; - sp_export_latex = FALSE; - sp_export_width = nullptr; - sp_export_height = nullptr; - sp_export_id = nullptr; - sp_export_background = nullptr; - sp_export_background_opacity = nullptr; - sp_export_area_snap = FALSE; - sp_export_use_hints = FALSE; - sp_export_id_only = FALSE; - sp_export_svg = nullptr; - sp_export_inkscape_svg = nullptr; - sp_export_ps = nullptr; - sp_export_eps = nullptr; - sp_export_ps_level = 3; - sp_export_pdf = nullptr; - sp_export_pdf_version = nullptr; - sp_export_emf = nullptr; - sp_export_wmf = nullptr; - sp_export_xaml = nullptr; - sp_export_text_to_path = FALSE; - sp_export_ignore_filters = FALSE; - sp_export_font = FALSE; - sp_pdf_page = 1; - sp_query_x = FALSE; - sp_query_y = FALSE; - sp_query_width = FALSE; - sp_query_height = FALSE; - sp_query_all = FALSE; - sp_query_id = nullptr; - sp_vacuum_defs = FALSE; - sp_no_convert_text_baseline_spacing = FALSE; - sp_file_convert_dpi_method_commandline = -1; -#ifdef WITH_DBUS - sp_dbus_listen = FALSE; - sp_dbus_name = NULL; -#endif // WITH_DBUS - - sp_export_png_utf8 = nullptr; - sp_export_svg_utf8 = nullptr; - sp_export_inkscape_svg_utf8 = nullptr; - sp_global_printer_utf8 = nullptr; -} - -#ifdef _WIN32 -static bool replaceArgs( int& argc, char**& argv ); -#endif -static std::vector<gchar *> sp_process_args(poptContext ctx); -struct poptOption options[] = { - {"version", 'V', - POPT_ARG_NONE, nullptr, SP_ARG_VERSION, - N_("Print the Inkscape version number"), - nullptr}, - - {"without-gui", 'z', - POPT_ARG_NONE, nullptr, SP_ARG_NOGUI, - N_("Do not use X server (only process files from console)"), - nullptr}, - - {"with-gui", 'g', - POPT_ARG_NONE, nullptr, SP_ARG_GUI, - N_("Try to use X server (even if $DISPLAY is not set)"), - nullptr}, - - {"file", 'f', - POPT_ARG_STRING, nullptr, SP_ARG_FILE, - N_("Open specified document(s) (option string may be excluded)"), - N_("FILENAME")}, - -#ifdef WITH_YAML - {"xverbs", 0, - POPT_ARG_STRING, &sp_xverbs_yaml, SP_ARG_XVERBS, - N_("xverbs command"), - N_("XVERBS_FILENAME")}, -#endif // WITH_YAML - - {"print", 'p', - POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT, - N_("Print document(s) to specified output file (use '| program' for pipe)"), - N_("FILENAME")}, - - {"export-png", 'e', - POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG, - N_("Export document to a PNG file"), - N_("FILENAME")}, - - {"export-dpi", 'd', - POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI, - N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 96)"), - N_("DPI")}, - - {"export-area", 'a', - POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA, - N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"), - N_("x0:y0:x1:y1")}, - - {"export-area-drawing", 'D', - POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING, - N_("Exported area is the entire drawing (not page)"), - nullptr}, - - {"export-area-page", 'C', - POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE, - N_("Exported area is the entire page"), - nullptr}, - - {"export-margin", 0, - POPT_ARG_STRING, &sp_export_margin, SP_ARG_EXPORT_MARGIN, - N_("Sets margin around exported area (default 0) in units of page size for SVG and mm for PS/EPS/PDF"), - N_("VALUE")}, - - {"export-area-snap", 0, - POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP, - N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"), - nullptr}, - - {"export-width", 'w', - POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH, - N_("The width of exported bitmap in pixels (overrides export-dpi)"), - N_("WIDTH")}, - - {"export-height", 'h', - POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT, - N_("The height of exported bitmap in pixels (overrides export-dpi)"), - N_("HEIGHT")}, - - {"export-id", 'i', - POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID, - N_("The ID of the object to export"), - N_("ID")}, - - {"export-id-only", 'j', - POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY, - // TRANSLATORS: this means: "Only export the object whose id is given in --export-id". - // See "man inkscape" for details. - N_("Export just the object with export-id, hide all others (only with export-id)"), - nullptr}, - - {"export-use-hints", 't', - POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS, - N_("Use stored filename and DPI hints when exporting (only with export-id)"), - nullptr}, - - {"export-background", 'b', - POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND, - N_("Background color of exported bitmap (any SVG-supported color string)"), - N_("COLOR")}, - - {"export-background-opacity", 'y', - POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY, - N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"), - N_("VALUE")}, - - {"export-inkscape-svg", 0, - POPT_ARG_STRING, &sp_export_inkscape_svg, SP_ARG_EXPORT_INKSCAPE_SVG, - N_("Export document to an inkscape SVG file (similar to save as.)"), - N_("FILENAME")}, - {"export-plain-svg", 'l', - POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG, - N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"), - N_("FILENAME")}, - - {"export-ps", 'P', - POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS, - N_("Export document to a PS file"), - N_("FILENAME")}, - - {"export-eps", 'E', - POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS, - N_("Export document to an EPS file"), - N_("FILENAME")}, - - {"export-ps-level", 0, - POPT_ARG_INT, &sp_export_ps_level, SP_ARG_EXPORT_PS_LEVEL, - N_("Choose the PostScript Level used to export. Possible choices are" - " 2 and 3 (the default)"), - N_("PS Level")}, - - {"export-pdf", 'A', - POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF, - N_("Export document to a PDF file"), - N_("FILENAME")}, - - {"export-pdf-version", 0, - POPT_ARG_STRING, &sp_export_pdf_version, SP_ARG_EXPORT_PDF_VERSION, - // TRANSLATORS: "--export-pdf-version" is an Inkscape command line option; see "inkscape --help" - N_("Export PDF to given version. (hint: make sure to input a version found in the PDF export dialog, e.g. \"1.4\" which is PDF-a conformant)"), - N_("PDF_VERSION")}, - - {"export-latex", 0, - POPT_ARG_NONE, &sp_export_latex, SP_ARG_EXPORT_LATEX, - N_("Export PDF/PS/EPS without text. Besides the PDF/PS/EPS, a LaTeX file is exported, putting the text on top of the PDF/PS/EPS file. Include the result in LaTeX like: \\input{latexfile.tex}"), - nullptr}, - - {"export-emf", 'M', - POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF, - N_("Export document to an Enhanced Metafile (EMF) File"), - N_("FILENAME")}, - - {"export-wmf", 'm', - POPT_ARG_STRING, &sp_export_wmf, SP_ARG_EXPORT_WMF, - N_("Export document to a Windows Metafile (WMF) File"), - N_("FILENAME")}, - - {"export-xaml", 0, - POPT_ARG_STRING, &sp_export_xaml, SP_ARG_EXPORT_XAML, - N_("Export document to a eXtensible Application Markup Language (XAML) File"), - N_("FILENAME")}, - - {"export-text-to-path", 'T', - POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH, - N_("Convert text object to paths on export (PS, EPS, PDF, SVG)"), - nullptr}, - - {"export-ignore-filters", 0, - POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS, - N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"), - nullptr}, - - {"pdf-page", 0, - POPT_ARG_INT, &sp_pdf_page, SP_ARG_PDF_PAGE, - N_("PDF page to import"), - N_("PAGE")}, - - {"query-x", 'X', - POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X, - // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help" - N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"), - nullptr}, - - {"query-y", 'Y', - POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y, - // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help" - N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"), - nullptr}, - - {"query-width", 'W', - POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH, - // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help" - N_("Query the width of the drawing or, if specified, of the object with --query-id"), - nullptr}, - - {"query-height", 'H', - POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT, - // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help" - N_("Query the height of the drawing or, if specified, of the object with --query-id"), - nullptr}, - - {"query-all", 'S', - POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL, - N_("List id,x,y,w,h for all objects"), - nullptr}, - - {"query-id", 'I', - POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID, - N_("The ID of the object whose dimensions are queried"), - N_("ID")}, - - {"extension-directory", 'x', - POPT_ARG_NONE, nullptr, SP_ARG_EXTENSIONDIR, - // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory - N_("Print out the extension directory and exit"), - nullptr}, - - {"vacuum-defs", 0, - POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS, - N_("Remove unused definitions from the defs section(s) of the document"), - nullptr}, - -#ifdef WITH_DBUS - {"dbus-listen", 0, - POPT_ARG_NONE, &sp_dbus_listen, SP_ARG_DBUS_LISTEN, - N_("Enter a listening loop for D-Bus messages in console mode"), - NULL}, - - {"dbus-name", 0, - POPT_ARG_STRING, &sp_dbus_name, SP_ARG_DBUS_NAME, - N_("Specify the D-Bus bus name to listen for messages on (default is org.inkscape)"), - N_("BUS-NAME")}, -#endif // WITH_DBUS - - {"verb-list", 0, - POPT_ARG_NONE, nullptr, SP_ARG_VERB_LIST, - N_("List the IDs of all the verbs in Inkscape"), - nullptr}, - - {"verb", 0, - POPT_ARG_STRING, nullptr, SP_ARG_VERB, - N_("Verb to call when Inkscape opens."), - N_("VERB-ID")}, - - {"select", 0, - POPT_ARG_STRING, nullptr, SP_ARG_SELECT, - N_("Object ID to select when Inkscape opens."), - N_("OBJECT-ID")}, - - {"shell", 0, - POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL, - N_("Start Inkscape in interactive shell mode."), - nullptr}, - - {"no-convert-text-baseline-spacing", 0, - POPT_ARG_NONE, &sp_no_convert_text_baseline_spacing, SP_ARG_NO_CONVERT_TEXT_BASELINE_SPACING, - N_("Do not fix legacy (pre-0.92) files' text baseline spacing on opening."), - nullptr}, - - {"convert-dpi-method", 0, - POPT_ARG_STRING, nullptr, SP_ARG_CONVERT_DPI_METHOD, - N_("Method used to convert pre-.92 document dpi, if needed. ([none|scale-viewbox|scale-document])"), - "[...]"}, - - POPT_AUTOHELP POPT_TABLEEND -}; - -static bool needToRecodeParams = true; -gchar * blankParam = g_strdup(""); - - - -#ifdef _WIN32 - -/** - * Set up the PATH, INKSCAPE_LOCALEDIR and PYTHONPATH environment - * variables on Windows - * @param exe Inkscape executable directory in UTF-8 - */ -static void _win32_set_inkscape_env(gchar const *exe) -{ - // add inkscape directory to DLL search path so dynamically linked extension modules find their libraries - wchar_t *exe_w = (wchar_t *)g_utf8_to_utf16(exe, -1, NULL, NULL, NULL); - SetDllDirectoryW(exe_w); - g_free(exe_w); - - // add inkscape directory to PATH - gchar const *path = g_getenv("PATH"); - gchar *new_path; - if (path) { - new_path = g_strdup_printf("%s;%s", exe, path); - } else { - new_path = g_strdup(exe); - } - g_setenv("PATH", new_path, TRUE); - g_free(new_path); - - // INKSCAPE_LOCALEDIR is needed by Python/Gettext - gchar *localepath = g_build_filename(exe, PACKAGE_LOCALE_DIR, NULL); - g_setenv("INKSCAPE_LOCALEDIR", localepath, TRUE); - g_free(localepath); - - // prevent "please insert disk" messages. fixes bug #950781 - SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); -} -#endif - -static void set_extensions_env() -{ - gchar *pythonpath = get_extensions_path(); - g_setenv("PYTHONPATH", pythonpath, TRUE); - g_free(pythonpath); - // printf("PYTHONPATH = %s\n", g_getenv("PYTHONPATH")); -} - - -/** - * This is the classic main() entry point of the program, though on some - * architectures it might be called by something else. - */ -int -main(int argc, char **argv) -{ -#ifdef ENABLE_NLS - // Native Language Support - Inkscape::initialize_gettext(); -#endif - -#ifdef _WIN32 - gchar *datadir = g_win32_get_package_installation_directory_of_module(NULL); - _win32_set_inkscape_env(datadir); - g_free(datadir); - - // Don't touch the registry (works fine without it) for Inkscape Portable - gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR"); - if (!val) { - RegistryTool rt; - rt.setPathInfo(); - } -#endif - - set_extensions_env(); - - // Checks libxml version is compatible with version software is compiled against - LIBXML_TEST_VERSION - - // Garbage Collector - Inkscape::GC::init(); - - Inkscape::Debug::Logger::init(); - - bool use_gui; - -#if !defined(_WIN32) && !defined(GDK_WINDOWING_QUARTZ) - use_gui = (g_getenv("DISPLAY") != nullptr); -#else - use_gui = true; -#endif - /* Test whether with/without GUI is forced */ - for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "-z") - || !strcmp(argv[i], "--without-gui") - || !strcmp(argv[i], "-p") - || !strncmp(argv[i], "--print", 7) - || !strcmp(argv[i], "-e") - || !strncmp(argv[i], "--export-png", 12) - || !strcmp(argv[i], "-l") - || !strncmp(argv[i], "--export-plain-svg", 18) - || !strncmp(argv[i], "--export-inkscape-svg", 21) - || !strcmp(argv[i], "-i") - || !strncmp(argv[i], "--export-area-drawing", 21) - || !strcmp(argv[i], "-D") - || !strncmp(argv[i], "--export-area-page", 18) - || !strcmp(argv[i], "-C") - || !strncmp(argv[i], "--export-id", 11) - || !strcmp(argv[i], "-P") - || !strncmp(argv[i], "--export-ps", 11) - || !strcmp(argv[i], "-E") - || !strncmp(argv[i], "--export-eps", 12) - || !strcmp(argv[i], "-A") - || !strncmp(argv[i], "--export-pdf", 12) - || !strncmp(argv[i], "--export-latex", 14) - || !strcmp(argv[i], "-M") - || !strncmp(argv[i], "--export-emf", 12) - || !strcmp(argv[i], "-m") - || !strncmp(argv[i], "--export-wmf", 12) - || !strncmp(argv[i], "--export-xaml", 13) - || !strcmp(argv[i], "-W") - || !strncmp(argv[i], "--query-width", 13) - || !strcmp(argv[i], "-H") - || !strncmp(argv[i], "--query-height", 14) - || !strcmp(argv[i], "-S") - || !strncmp(argv[i], "--query-all", 11) - || !strcmp(argv[i], "-X") - || !strncmp(argv[i], "--query-x", 9) - || !strcmp(argv[i], "-Y") - || !strncmp(argv[i], "--query-y", 9) - || !strcmp(argv[i], "--vacuum-defs") -#ifdef WITH_DBUS - || !strcmp(argv[i], "--dbus-listen") -#endif // WITH_DBUS - || !strcmp(argv[i], "--shell") - ) - { - /* main_console handles any exports -- not the gui */ - use_gui = false; - break; - } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) { - use_gui = true; - break; - } - } - -#ifdef _WIN32 - { - // If the call fails, we'll need to convert charsets - needToRecodeParams = !replaceArgs( argc, argv ); - } -#endif // _WIN32 - - int retcode; - - if (use_gui) { - retcode = sp_main_gui(argc, (const char **) argv); - } else { - retcode = sp_main_console(argc, (const char **) argv); - } - - return retcode; -} - - - - -static void fixupSingleFilename( gchar **orig, gchar **spare ) -{ - if ( orig && *orig && **orig ) { - GError *error = nullptr; - gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, nullptr, nullptr, &error); - if ( newFileName ) - { - *orig = newFileName; - if ( spare ) { - *spare = newFileName; - } -// g_message("Set a replacement fixup"); - } - } -} - - - -static void fixupFilenameEncoding( std::vector<gchar*> &filenames) -{ - for (unsigned int i=0; i<filenames.size(); ++i) { - gchar *fn = filenames[i]; - gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, nullptr, nullptr, nullptr); - if ( newFileName ) { - g_free( fn ); - filenames[i] = newFileName; - } - } -} - -static int sp_common_main( int argc, char const **argv, std::vector<gchar*> *flDest ) -{ -#ifdef ENABLE_NLS - // Temporarily switch gettext encoding to locale, so that help messages can be output properly. - Inkscape::bind_textdomain_codeset_console(); -#endif - - poptContext ctx = poptGetContext(nullptr, argc, argv, options, 0); - poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:")); - g_return_val_if_fail(ctx != nullptr, 1); - - /* Collect own arguments */ - std::vector<gchar*> fl = sp_process_args(ctx); - poptFreeContext(ctx); - -#ifdef ENABLE_NLS - // Now switch gettext back to UTF-8 (for GUI) - Inkscape::bind_textdomain_codeset_utf8(); -#endif - - // Now let's see if the file list still holds up - if ( needToRecodeParams ) - { - fixupFilenameEncoding( fl ); - } - - // Check the globals for filename-fixup - if ( needToRecodeParams ) - { - fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 ); - fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 ); - fixupSingleFilename( &sp_export_inkscape_svg, &sp_export_inkscape_svg_utf8 ); - fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 ); -#ifdef WITH_YAML - fixupSingleFilename( &sp_xverbs_yaml, &sp_xverbs_yaml_utf8 ); -#endif // WITH_YAML - } - else - { - if ( sp_export_png ) - sp_export_png_utf8 = g_strdup( sp_export_png ); - if ( sp_export_svg ) - sp_export_svg_utf8 = g_strdup( sp_export_svg ); - if ( sp_export_inkscape_svg ) - sp_export_inkscape_svg_utf8 = g_strdup( sp_export_inkscape_svg ); - if ( sp_global_printer ) - sp_global_printer_utf8 = g_strdup( sp_global_printer ); -#ifdef WITH_YAML - if ( sp_xverbs_yaml ) - sp_xverbs_yaml_utf8 = g_strdup( sp_xverbs_yaml ); -#endif // WITH_YAML - } - - detect_pipe_in_filename( sp_export_png ); - detect_pipe_in_filename( sp_export_svg ); - detect_pipe_in_filename( sp_export_pdf ); - detect_pipe_in_filename( sp_export_ps ); - detect_pipe_in_filename( sp_export_eps ); - -#ifdef WITH_DBUS - // Before initializing extensions, we must set the DBus bus name if required - if (sp_dbus_name != NULL) { - Inkscape::Extension::Dbus::dbus_set_bus_name(sp_dbus_name); - } -#endif - - // Return the list if wanted, else free it up. - if ( flDest ) { - flDest->insert(flDest->end(),fl.begin(),fl.end()); - } else { - for (auto i:fl) { - g_free(i); - } - } - return 0; -} - -namespace Inkscape { -namespace UI { -namespace Tools { - -guint get_latin_keyval(GdkEventKey const* event, guint *consumed_modifiers = nullptr); - -} -} -} - -static void -snooper(GdkEvent *event, gpointer /*data*/) { - if (INKSCAPE.mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */ - { - GdkModifierType mapping=(GdkModifierType)INKSCAPE.mapalt(); - switch (event->type) { - case GDK_MOTION_NOTIFY: - if(event->motion.state & mapping) { - event->motion.state|=GDK_MOD1_MASK; - } - break; - case GDK_BUTTON_PRESS: - if(event->button.state & mapping) { - event->button.state|=GDK_MOD1_MASK; - } - break; - case GDK_KEY_PRESS: - if(event->key.state & mapping) { - event->key.state|=GDK_MOD1_MASK; - } - break; - default: - break; - } - } - - if (INKSCAPE.trackalt()) { - // MacOS X with X11 has some problem with the default - // xmodmapping. A ~/.xmodmap solution does not work reliably due - // to the way we package our executable in a .app that can launch - // X11 or use an already-running X11. The same problem has been - // reported on Linux but there is no .app/X11 to get in the way - // of ~/.xmodmap fixes. So we make this a preference. - // - // For some reason, Gdk senses changes in Alt (Mod1) state for - // many message types, but not for keystrokes! So this ugly hack - // tracks what the state of Alt-pressing is, and ensures - // GDK_MOD1_MASK is in the event->key.state as appropriate. - // - static gboolean altL_pressed = FALSE; - static gboolean altR_pressed = FALSE; - static gboolean alt_pressed = FALSE; - - guint keyval = 0; - switch (event->type) { - case GDK_MOTION_NOTIFY: - alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK); - break; - case GDK_BUTTON_PRESS: - alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK); - break; - case GDK_KEY_PRESS: - keyval = Inkscape::UI::Tools::get_latin_keyval(&event->key); - if (keyval == GDK_KEY_Alt_L) altL_pressed = TRUE; - if (keyval == GDK_KEY_Alt_R) altR_pressed = TRUE; - alt_pressed = alt_pressed || altL_pressed || altR_pressed; - alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK); - if (alt_pressed) - event->key.state |= GDK_MOD1_MASK; - else - event->key.state &= ~GDK_MOD1_MASK; - break; - case GDK_KEY_RELEASE: - keyval = Inkscape::UI::Tools::get_latin_keyval(&event->key); - if (keyval == GDK_KEY_Alt_L) altL_pressed = FALSE; - if (keyval == GDK_KEY_Alt_R) altR_pressed = FALSE; - if (!altL_pressed && !altR_pressed) - alt_pressed = FALSE; - break; - default: - break; - } - //printf("alt_pressed: %s\n", alt_pressed? "+" : "-"); - } - - gtk_main_do_event (event); -} - -int -sp_main_gui(int argc, char const **argv) -{ - Gtk::Main main_instance (&argc, const_cast<char ***>(&argv)); - - std::vector<gchar *> fl; - int retVal = sp_common_main( argc, argv, &fl ); - g_return_val_if_fail(retVal == 0, 1); - - gdk_event_handler_set((GdkEventFunc)snooper, nullptr, nullptr); - Inkscape::Debug::log_display_config(); - - // Set default window icon. Obeys the theme. - Gtk::Window::set_default_icon_name("inkscape"); - Inkscape::UI::Widget::Panel::prep(); - - bool create_new = true; - - /// \todo FIXME BROKEN - non-UTF-8 sneaks in here. - Inkscape::Application::create(argv[0], true); - INKSCAPE.set_pdf_page(sp_pdf_page); - - for (auto i:fl) { - if (sp_file_open(i,nullptr)) { - create_new=false; - } - } - if (create_new) { - sp_file_new_default(); - } - - Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle)); - main_instance.run(); - -#ifdef _WIN32 - //We might not need anything here - //sp_win32_finish(); <-- this is a NOP func -#endif - - return 0; -} - -/** - * Process file list - */ -static int sp_process_file_list(std::vector<gchar*> fl) -{ - int retVal = 0; -#ifdef WITH_DBUS - if (fl.empty()) { - // If we've been asked to listen for D-Bus messages, enter a main loop here - // The main loop may be exited by calling "exit" on the D-Bus application interface. - if (sp_dbus_listen) { - Gtk::Main main_dbus_loop(0, NULL); - main_dbus_loop.run(); - } - } -#endif // WITH_DBUS - - for (auto filename:fl) { - - SPDocument *doc = nullptr; - try { - doc = Inkscape::Extension::open(nullptr, filename); - } catch (Inkscape::Extension::Input::no_extension_found &e) { - doc = nullptr; - } catch (Inkscape::Extension::Input::open_failed &e) { - doc = nullptr; - } - - if (doc == nullptr) { - try { - doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename); - } catch (Inkscape::Extension::Input::no_extension_found &e) { - doc = nullptr; - } catch (Inkscape::Extension::Input::open_failed &e) { - doc = nullptr; - } - } - if (doc == nullptr) { - g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename); - retVal++; - } else { - - INKSCAPE.add_document(doc); - - if (sp_vacuum_defs) { - doc->vacuumDocument(); - } - - // Execute command-line actions (selections and verbs) using our local models - bool has_performed_actions = Inkscape::CmdLineAction::doList(INKSCAPE.active_action_context()); - -#ifdef WITH_DBUS - // If we've been asked to listen for D-Bus messages, enter a main loop here - // The main loop may be exited by calling "exit" on the D-Bus application interface. - if (sp_dbus_listen) { - Gtk::Main main_dbus_loop(0, NULL); - main_dbus_loop.run(); - } -#endif // WITH_DBUS - - if (!sp_export_svg && !sp_export_inkscape_svg && (sp_vacuum_defs || has_performed_actions)) { - // save under the name given in the command line - Inkscape::Extension::save(Inkscape::Extension::db.get("org.inkscape.output.svg.inkscape"), doc, filename, false, - false, false, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG); - } - - if (sp_global_printer) { - sp_print_document_to_file(doc, sp_global_printer); - } - if (sp_export_png || (sp_export_id && sp_export_use_hints)) { - retVal |= do_export_png(doc); - } - if (sp_export_svg || sp_export_inkscape_svg) { - retVal |= do_export_svg(doc); - } - if (sp_export_ps) { - retVal |= do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript"); - } - if (sp_export_eps) { - retVal |= do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript"); - } - if (sp_export_pdf) { - retVal |= do_export_ps_pdf(doc, sp_export_pdf, "application/pdf"); - } - if (sp_export_emf) { - retVal |= do_export_emf(doc, sp_export_emf, "image/x-emf"); - } - if (sp_export_wmf) { - retVal |= do_export_wmf(doc, sp_export_wmf, "image/x-wmf"); - } - if (sp_export_xaml) { - retVal |= do_export_xaml(doc, sp_export_xaml, "text/xml+xaml"); - } - if (sp_query_all) { - do_query_all (doc); - } else if (sp_query_width || sp_query_height) { - do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id); - } else if (sp_query_x || sp_query_y) { - do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id); - } - - INKSCAPE.remove_document(doc); - - delete doc; - } - } - return retVal; -} - -/** - * Run the application as an interactive shell, parsing command lines from stdin - * Returns -1 on error. - */ -static int sp_main_shell(char const* command_name) -{ - int retval = 0; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - const unsigned int buffer_size = 4096; - gchar *command_line = g_strnfill(buffer_size, 0); - g_strlcpy(command_line, command_name, buffer_size); - gsize offset = g_strlcat(command_line, " ", buffer_size); - gsize sizeLeft = buffer_size - offset; - gchar *useme = command_line + offset; - - fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string); - fflush(stdout); - char* linedata = nullptr; - do { - fprintf(stdout, ">"); - fflush(stdout); - if ((linedata = fgets(useme, sizeLeft, stdin))) { - size_t len = strlen(useme); - if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) { - fprintf(stdout, "ERROR: Command line too long\n"); - // Consume rest of line - retval = -1; // If the while loop completes, this remains -1 - while (fgets(useme, sizeLeft, stdin) && retval) { - len = strlen(command_line); - if ( (len < buffer_size) && (command_line[len-1] == '\n') ) { - retval = 0; - } - } - } else { - useme[--len] = '\0'; // Strip newline - if (useme[len - 1] == '\r') { - useme[--len] = '\0'; - } - if ( strcmp(useme, "quit") == 0 ) { - // Time to quit - fflush(stdout); - linedata = nullptr; // mark for exit - } else if ( len < 1 ) { - // blank string. Do nothing. - } else { - GError* parseError = nullptr; - gchar** argv = nullptr; - gint argc = 0; - if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) { - poptContext ctx = poptGetContext(nullptr, argc, const_cast<const gchar**>(argv), options, 0); - poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:")); - if ( ctx ) { - std::vector<gchar *> fl = sp_process_args(ctx); - INKSCAPE.set_pdf_page(sp_pdf_page); - if (sp_process_file_list(fl)) { - retval = -1; - } - poptFreeContext(ctx); - } else { - retval = 1; // not sure why. But this was the previous return value - } - resetCommandlineGlobals(); - g_strfreev(argv); - } else { - g_warning("Cannot parse commandline: %s", useme); - retval = -1; - } - } - } - } // if (linedata... - } while (linedata && (retval == 0)); - - g_free(command_line); - return retval; -} - -int sp_main_console(int argc, char const **argv) -{ - /* We are started in text mode */ -Inkscape::Preferences *prefs = Inkscape::Preferences::get(); -#if !GLIB_CHECK_VERSION(2,36,0) - /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango) - * in a non-Gtk environment. Used in libnrtype's - * FontInstance.cpp and FontFactory.cpp. - * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html - */ - g_type_init(); -#endif - - char **argv2 = const_cast<char **>(argv); - gtk_init_check( &argc, &argv2 ); - - std::vector<gchar*> fl; - int retVal = sp_common_main( argc, argv, &fl ); - g_return_val_if_fail(retVal == 0, 1); - - if (fl.empty() && !sp_shell -#ifdef WITH_DBUS - && !sp_dbus_listen -#endif // WITH_DBUS - ) { - do_print_message("Nothing to do!\n"); - exit(0); - } - - Inkscape::Application::create(argv[0], false); - INKSCAPE.set_pdf_page(sp_pdf_page); - - if (sp_shell) { - int retVal = sp_main_shell(argv[0]); // Run as interactive shell - exit((retVal < 0) ? 1 : 0); - } else { - int retVal = sp_process_file_list(fl); // Normal command line invocation - if (retVal){ - exit(1); - } - } - return 0; -} - -static void -do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id) -{ - SPObject *o = nullptr; - - if (id) { - o = doc->getObjectById(id); - if (o) { - if (!SP_IS_ITEM (o)) { - g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id); - return; - } - } else { - g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id); - return; - } - } else { - o = doc->getRoot(); - } - - if (o) { - doc->ensureUpToDate(); - SPItem *item = ((SPItem *) o); - - // visual bbox in document coords for scripting - Geom::OptRect area = item->documentVisualBounds(); - if (area) { - Inkscape::SVGOStringStream os; - if (extent) { - os << area->dimensions()[axis]; - } else { - os << area->min()[axis]; - } - do_print_message ("%s", os.str().c_str()); - } else { - do_print_message("0"); - } - } -} - -static void do_query_all(SPDocument *doc) -{ - SPObject *o = doc->getRoot(); - - if (o) { - doc->ensureUpToDate(); - do_query_all_recurse(o); - } -} - -static void -do_query_all_recurse (SPObject *o) -{ - SPItem *item = ((SPItem *) o); - if (o->getId() && SP_IS_ITEM(item)) { - Geom::OptRect area = item->documentVisualBounds(); - if (area) { - Inkscape::SVGOStringStream os; - os << o->getId(); - os << "," << area->min()[Geom::X]; - os << "," << area->min()[Geom::Y]; - os << "," << area->dimensions()[Geom::X]; - os << "," << area->dimensions()[Geom::Y]; - do_print_message ("%s\n", os.str().c_str()); - } - } - - for(auto& child: o->children) { - do_query_all_recurse (&child); - } -} - -/** - * Perform a PNG export - * - * \param doc Document to export. - */ - -static int do_export_png(SPDocument *doc) -{ - Glib::ustring filename; - bool filename_from_hint = false; - gdouble dpi = 0.0; - - if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) { - g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored."); - } - - std::vector<SPItem*> items; - - Geom::Rect area; - if (sp_export_id || sp_export_area_drawing) { - - SPObject *o = nullptr; - SPObject *o_area = nullptr; - if (sp_export_id && sp_export_area_drawing) { - o = doc->getObjectById(sp_export_id); - o_area = doc->getRoot(); - } else if (sp_export_id) { - o = doc->getObjectById(sp_export_id); - o_area = o; - } else if (sp_export_area_drawing) { - o = doc->getRoot(); - o_area = o; - } - - if (o) { - if (!SP_IS_ITEM (o)) { - g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id); - return 1; - } - - items.push_back(SP_ITEM(o)); - - if (sp_export_id_only) { - do_print_message("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id); - } - - if (sp_export_use_hints) { - - // retrieve export filename hint - const gchar *fn_hint = o->getRepr()->attribute("inkscape:export-filename"); - if (fn_hint) { - if (sp_export_png) { - g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint); - filename = sp_export_png; - } else { - filename = fn_hint; - filename_from_hint = true; - } - } else { - g_warning ("Export filename hint not found for the object."); - filename = sp_export_png; - } - - // retrieve export dpi hints - const gchar *dpi_hint = o->getRepr()->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now - if (dpi_hint) { - if (sp_export_dpi || sp_export_width || sp_export_height) { - g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint); - } else { - dpi = atof(dpi_hint); - } - } else { - g_warning ("Export DPI hint not found for the object."); - } - - } - - // write object bbox to area - doc->ensureUpToDate(); - Geom::OptRect areaMaybe = static_cast<SPItem *>(o_area)->desktopVisualBounds(); - if (areaMaybe) { - area = *areaMaybe; - } else { - g_warning("Unable to determine a valid bounding box. Nothing exported."); - return 1; - } - } else { - g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id); - return 1; - } - } - - if (sp_export_area) { - /* Try to parse area (given in SVG pixels) */ - gdouble x0,y0,x1,y1; - if (sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) != 4) { - g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area); - return 1; - } - area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1)); - } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) { - /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */ - doc->ensureUpToDate(); - Geom::Point origin(doc->getRoot()->x.computed, doc->getRoot()->y.computed); - area = Geom::Rect(origin, origin + doc->getDimensions()); - } - - // set filename and dpi from options, if not yet set from the hints - if (filename.empty()) { - if (!sp_export_png || sp_export_png[0] == '\0') { - g_warning ("No export filename given and no filename hint. Nothing exported."); - return 1; - } - filename = sp_export_png; - } - - if (sp_export_dpi && dpi == 0.0) { - dpi = atof(sp_export_dpi); - if ((dpi < 0.1) || (dpi > 10000.0)) { - g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi); - return 1; - } - do_print_message("DPI: %g\n", dpi); - } - - if (sp_export_area_snap) { - area = area.roundOutwards(); - } - - // default dpi - if (dpi == 0.0) { - dpi = Inkscape::Util::Quantity::convert(1, "in", "px"); - } - - unsigned long int width = 0; - unsigned long int height = 0; - - if (sp_export_width) { - errno=0; - width = strtoul(sp_export_width, nullptr, 0); - if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) { - g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX); - return 1; - } - dpi = (gdouble) Inkscape::Util::Quantity::convert(width, "in", "px") / area.width(); - } - - if (sp_export_height) { - errno=0; - height = strtoul(sp_export_height, nullptr, 0); - if ((height < 1) || (height > PNG_UINT_31_MAX)) { - g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX); - return 1; - } - dpi = (gdouble) Inkscape::Util::Quantity::convert(height, "in", "px") / area.height(); - } - - if (!sp_export_width) { - width = (unsigned long int) (Inkscape::Util::Quantity::convert(area.width(), "px", "in") * dpi + 0.5); - } - - if (!sp_export_height) { - height = (unsigned long int) (Inkscape::Util::Quantity::convert(area.height(), "px", "in") * dpi + 0.5); - } - - guint32 bgcolor = 0x00000000; - if (sp_export_background) { - // override the page color - bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00); - bgcolor |= 0xff; // default is no opacity - } else { - // read from namedview - Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview"); - if (nv && nv->attribute("pagecolor")){ - bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00); - } - if (nv && nv->attribute("inkscape:pageopacity")){ - double opacity = 1.0; - sp_repr_get_double (nv, "inkscape:pageopacity", &opacity); - bgcolor |= SP_COLOR_F_TO_U(opacity); - } - } - - if (sp_export_background_opacity) { - // override opacity - gfloat value; - if (sp_svg_number_read_f (sp_export_background_opacity, &value)) { - if (value > 1.0) { - value = CLAMP (value, 1.0f, 255.0f); - bgcolor &= (guint32) 0xffffff00; - bgcolor |= (guint32) floor(value); - } else { - value = CLAMP (value, 0.0f, 1.0f); - bgcolor &= (guint32) 0xffffff00; - bgcolor |= SP_COLOR_F_TO_U(value); - } - } - } - - Glib::ustring path; - if (filename_from_hint) { - //Make relative paths go from the document location, if possible: - if (!Glib::path_is_absolute(filename) && doc->getURI()) { - Glib::ustring dirname = Glib::path_get_dirname(doc->getURI()); - if (!dirname.empty()) { - path = Glib::build_filename(dirname, filename); - } - } - if (path.empty()) { - path = filename; - } - } else { - path = filename; - } - - //check if specified directory exists - if (!Inkscape::IO::file_directory_exists(filename.c_str())) { - g_warning("File path \"%s\" includes directory that doesn't exist.\n", filename.c_str()); - return 1; - } else { - do_print_message("Background RRGGBBAA: %08x\n", bgcolor); - - do_print_message("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area[Geom::X][0], area[Geom::Y][0], area[Geom::X][1], area[Geom::Y][1], width, height, dpi); - - reverse(items.begin(),items.end()); - - if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) { - if( sp_export_png_file(doc, path.c_str(), area, width, height, dpi, - dpi, bgcolor, nullptr, nullptr, true, sp_export_id_only ? items : std::vector<SPItem*>()) == 1 ) { - do_print_message("Bitmap saved as: %s\n", filename.c_str()); - } else { - g_warning("Bitmap failed to save to: %s", filename.c_str()); - return 1; - } - } else { - g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX); - return 1; - } - } - - return 0; -} - -/** - * Perform an SVG export - * - * \param doc Document to export. - */ - -static int do_export_svg(SPDocument* doc) -{ - if (sp_export_text_to_path) { - std::vector<SPItem*> items; - SPRoot *root = doc->getRoot(); - doc->ensureUpToDate(); - for (auto& iter: root->children) { - SPItem* item = (SPItem*) &iter; - if (! (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item) || SP_IS_GROUP(item))) { - continue; - } - - te_update_layout_now_recursive(item); - items.push_back(item); - } - - std::vector<SPItem*> selected; - std::vector<Inkscape::XML::Node*> to_select; - - sp_item_list_to_curves(items, selected, to_select); - - } - if (sp_export_margin) { - gdouble margin = g_ascii_strtod(sp_export_margin, nullptr); - doc->ensureUpToDate(); - SPNamedView *nv; - Inkscape::XML::Node *nv_repr; - if ((nv = sp_document_namedview(doc, nullptr)) && (nv_repr = nv->getRepr())) { - sp_repr_set_svg_double(nv_repr, "fit-margin-top", margin); - sp_repr_set_svg_double(nv_repr, "fit-margin-left", margin); - sp_repr_set_svg_double(nv_repr, "fit-margin-right", margin); - sp_repr_set_svg_double(nv_repr, "fit-margin-bottom", margin); - } - } - if(sp_export_area_drawing) { - fit_canvas_to_drawing(doc, sp_export_margin ? true : false); - } - if(sp_export_id) { - doc->ensureUpToDate(); - - // "crop" the document to the specified object, cleaning as we go. - SPObject *obj = doc->getObjectById(sp_export_id); - if (sp_export_id_only) { - // If -j then remove all other objects to complete the "crop" - doc->getRoot()->cropToObject(obj); - } - Inkscape::ObjectSet s(doc); - s.set(obj); - if (!sp_export_area_page) { - s.fitCanvas(sp_export_margin ? true : false); - } - } - - int ret = 0; - if (sp_export_svg) { - try { - Inkscape::Extension::save(Inkscape::Extension::db.get("org.inkscape.output.svg.plain"), doc, sp_export_svg, false, - false, false, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY); - } catch (Inkscape::Extension::Output::save_failed &e) { - g_warning("Failed to save plain SVG to: %s", sp_export_svg); - ret = 1; - } - } - if (sp_export_inkscape_svg) { - // Export as inkscape SVG. - try { - Inkscape::Extension::save(Inkscape::Extension::db.get("org.inkscape.output.svg.inkscape"), doc, sp_export_inkscape_svg, false, - false, false, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG); - } catch (Inkscape::Extension::Output::save_failed &e) { - g_warning("Failed to save Inkscape SVG to: %s", sp_export_inkscape_svg); - ret = 1; - } - } - return ret; -} - -/** - * Perform a PDF/PS/EPS export - * - * \param doc Document to export. - * \param uri URI to export to. - * \param mime MIME type to export as. - */ - -static int do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime) -{ - Inkscape::Extension::DB::OutputList o; - Inkscape::Extension::db.get_output_list(o); - Inkscape::Extension::DB::OutputList::const_iterator i = o.begin(); - while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) { - i++; - } - - if (i == o.end()) - { - g_warning ("Could not find an extension to export to MIME type %s.", mime); - return 1; - } - - if (sp_export_id) { - SPObject *o = doc->getObjectById(sp_export_id); - if (o == nullptr) { - g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id); - return 1; - } - (*i)->set_param_string ("exportId", sp_export_id); - } else { - (*i)->set_param_string ("exportId", ""); - } - - if (sp_export_area_page && sp_export_area_drawing) { - g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect."); - sp_export_area_drawing = false; - } - - if (sp_export_area_drawing) { - (*i)->set_param_optiongroup ("area", "drawing"); - } - - if (sp_export_area_page) { - if (sp_export_eps) { - g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the page, --export-area-page will clip it to drawing."); - } - (*i)->set_param_optiongroup ("area", "page"); - } - - if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) { - // neither is set, set page as default for ps/pdf and drawing for eps - if (sp_export_eps) { - try { - (*i)->set_param_optiongroup("area", "drawing"); - } catch (...) {} - } - } - - if (sp_export_text_to_path) { - (*i)->set_param_optiongroup("textToPath", "paths"); - } else if (sp_export_latex) { - (*i)->set_param_optiongroup("textToPath", "LaTeX"); - } else { - (*i)->set_param_optiongroup("textToPath", "embed"); - } - - if (sp_export_ignore_filters) { - (*i)->set_param_bool("blurToBitmap", FALSE); - } else { - (*i)->set_param_bool("blurToBitmap", TRUE); - - gdouble dpi = 96.0; - if (sp_export_dpi) { - dpi = atof(sp_export_dpi); - if ((dpi < 1) || (dpi > 10000.0)) { - g_warning("DPI value %s out of range [1 - 10000]. Using 96 dpi instead.", sp_export_dpi); - dpi = 96; - } - } - - (*i)->set_param_int("resolution", (int) dpi); - } - - // if no bleed/margin is given, set to 0 (otherwise it takes the value last used from the UI) - float margin = 0.; - if (sp_export_margin) { - margin = g_ascii_strtod(sp_export_margin, nullptr); - } - (*i)->set_param_float("bleed", margin); - - // handle --export-pdf-version - if (g_strcmp0(mime, "application/pdf") == 0) { - bool set_export_pdf_version_fail=true; - const gchar *pdfver_param_name="PDFversion"; - if(sp_export_pdf_version) { - // combine "PDF " and the given command line - std::string version_gui_string=std::string("PDF ")+sp_export_pdf_version; - try{ - // first, check if the given pdf version is selectable in the ComboBox - if((*i)->get_param_enum_contains("PDFversion", version_gui_string.c_str())) { - (*i)->set_param_enum(pdfver_param_name, version_gui_string.c_str()); - set_export_pdf_version_fail=false; - } else { - g_warning("Desired PDF export version \"%s\" not supported! Hint: input one of the versions found in the pdf export dialog e.g. \"1.4\".", - sp_export_pdf_version); - } - } catch (...) { - // can be thrown along the way: - // throw Extension::param_not_exist(); - // throw Extension::param_not_enum_param(); - g_warning("Parameter or Enum \"%s\" might not exist",pdfver_param_name); - } - } - - // set default pdf export version to 1.4, also if something went wrong - if(set_export_pdf_version_fail) { - (*i)->set_param_enum(pdfver_param_name, "PDF 1.4"); - } - } - - if(!uri || uri[0] == '\0') { - g_warning ("No export filename given. Nothing exported."); - return 0; - } - - //check if specified directory exists - if (!Inkscape::IO::file_directory_exists(uri)) { - g_warning("File path \"%s\" includes directory that doesn't exist.\n", uri); - return 1; - } - - if ( g_strcmp0(mime, "image/x-postscript") == 0 - || g_strcmp0(mime, "image/x-e-postscript") == 0 ) { - if ( sp_export_ps_level < 2 || sp_export_ps_level > 3 ) { - g_warning("Only supported PostScript levels are 2 and 3." - " Defaulting to 2."); - sp_export_ps_level = 2; - } - - (*i)->set_param_enum("PSlevel", (sp_export_ps_level == 3) - ? "PostScript level 3" : "PostScript level 2"); - } - - try { - (*i)->save(doc, uri); - } catch(...) { - g_warning("Failed to save pdf to: %s", uri); - } - return 0; -} - -/** - * Export a document to EMF or WMF - * - * \param doc Document to export. - * \param uri URI to export to. - * \param mime MIME type to export as (should be "image/x-emf" or "image/x-wmf") - */ - -static int do_export_win_metafile_common(SPDocument* doc, gchar const* uri, char const* mime) -{ - Inkscape::Extension::DB::OutputList o; - Inkscape::Extension::db.get_output_list(o); - Inkscape::Extension::DB::OutputList::const_iterator i = o.begin(); - while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) { - i++; - } - - if (i == o.end()) - { - g_warning ("Could not find an extension to export to MIME type %s.", mime); - return 1; - } - - //check if specified directory exists - if (!Inkscape::IO::file_directory_exists(uri)){ - g_warning("File path \"%s\" includes directory that doesn't exist.\n", - uri); - return 1; - } - - (*i)->save(doc, uri); - return 0; -} - -/** - * Export a document to EMF - * - * \param doc Document to export. - * \param uri URI to export to. - * \param mime MIME type to export as (should be "image/x-emf") - */ - -static int do_export_emf(SPDocument* doc, gchar const* uri, char const* mime) -{ - if(!uri || uri[0] == '\0') { - g_warning("No filename provided for emf export."); - return 0; - } - return do_export_win_metafile_common(doc, uri, mime); -} - -/** - * Export a document to WMF - * - * \param doc Document to export. - * \param uri URI to export to. - * \param mime MIME type to export as (should be "image/x-wmf") - */ - -static int do_export_wmf(SPDocument* doc, gchar const* uri, char const* mime) -{ - if(!uri || uri[0] == '\0') { - g_warning("No filename provided for wmf export."); - return 0; - } - return do_export_win_metafile_common(doc, uri, mime); -} - -/** - * Export a document to XAML - * - * \param doc Document to export. - * \param uri URI to export to. - * \param mime MIME type to export as (should be "text/xml+xaml) - */ - -static int do_export_xaml(SPDocument* doc, gchar const* uri, char const* mime) -{ - if(!uri || uri[0] == '\0') { - g_warning("No filename provided for xaml export."); - return 0; - } - return do_export_win_metafile_common(doc, uri, mime); -} - -#ifdef _WIN32 -bool replaceArgs( int& argc, char**& argv ) -{ - bool worked = false; - -#ifdef REPLACEARGS_DEBUG - MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION ); -#endif // REPLACEARGS_DEBUG - - wchar_t* line = GetCommandLineW(); - if ( line ) - { -#ifdef REPLACEARGS_DEBUG - { - gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL ); - if ( utf8Line ) - { - gchar *safe = Inkscape::IO::sanitizeString(utf8Line); - { - char tmp[strlen(safe) + 32]; - snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe ); - MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION ); - } - } - } -#endif // REPLACEARGS_DEBUG - - int numArgs = 0; - wchar_t** parsed = CommandLineToArgvW( line, &numArgs ); - -#ifdef REPLACEARGS_ANSI -// test code for trying things on Win95/98/ME - if ( !parsed ) - { -#ifdef REPLACEARGS_DEBUG - MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION ); -#endif // REPLACEARGS_DEBUG - int lineLen = wcslen(line) + 1; - wchar_t* lineDup = new wchar_t[lineLen]; - wcsncpy( lineDup, line, lineLen ); - - int pos = 0; - bool inQuotes = false; - bool inWhitespace = true; - std::vector<int> places; - while ( lineDup[pos] ) - { - if ( inQuotes ) - { - if ( lineDup[pos] == L'"' ) - { - inQuotes = false; - } - } - else if ( lineDup[pos] == L'"' ) - { - inQuotes = true; - inWhitespace = false; - places.push_back(pos); - } - else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' ) - { - if ( !inWhitespace ) - { - inWhitespace = true; - lineDup[pos] = 0; - } - } - else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') ) - { - inWhitespace = false; - places.push_back(pos); - } - else - { - // consume - } - pos++; - } -#ifdef REPLACEARGS_DEBUG - { - char tmp[256]; - snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() ); - MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION ); - } -#endif // REPLACEARGS_DEBUG - - wchar_t** block = new wchar_t*[places.size()]; - int i = 0; - for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ ) - { - block[i++] = &lineDup[*it]; - } - parsed = block; - numArgs = places.size(); - } -#endif // REPLACEARGS_ANSI - - if ( parsed ) - { - std::vector<wchar_t*>expandedArgs; - if ( numArgs > 0 ) - { - expandedArgs.push_back( parsed[0] ); - } - - for ( int i1 = 1; i1 < numArgs; i1++ ) - { - bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL); - wildcarded &= parsed[i1][0] != L'"'; - wildcarded &= parsed[i1][0] != L'-'; - if ( wildcarded ) - { -#ifdef REPLACEARGS_ANSI - WIN32_FIND_DATAA data; -#else - WIN32_FIND_DATAW data; -#endif // REPLACEARGS_ANSI - - memset((void *)&data, 0, sizeof(data)); - - int baseLen = wcslen(parsed[i1]) + 2; - wchar_t* base = new wchar_t[baseLen]; - wcsncpy( base, parsed[i1], baseLen ); - wchar_t* last = wcsrchr( base, L'\\' ); - if ( last ) - { - last[1] = 0; - } - else - { - base[0] = 0; - } - baseLen = wcslen( base ); - -#ifdef REPLACEARGS_ANSI - char target[MAX_PATH]; - if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) ) - { - HANDLE hf = FindFirstFileA( target, &data ); -#else - HANDLE hf = FindFirstFileW( parsed[i1], &data ); -#endif // REPLACEARGS_ANSI - if ( hf != INVALID_HANDLE_VALUE ) - { - BOOL found = TRUE; - do - { -#ifdef REPLACEARGS_ANSI - int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 ); - if ( howMany > 0 ) - { - howMany += baseLen; - wchar_t* tmp = new wchar_t[howMany + 1]; - wcsncpy( tmp, base, howMany + 1 ); - MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen ); - expandedArgs.push_back( tmp ); - found = FindNextFileA( hf, &data ); - } -#else - int howMany = wcslen(data.cFileName) + baseLen; - wchar_t* tmp = new wchar_t[howMany + 1]; - wcsncpy( tmp, base, howMany + 1 ); - wcsncat( tmp, data.cFileName, howMany + 1 ); - expandedArgs.push_back( tmp ); - found = FindNextFileW( hf, &data ); -#endif // REPLACEARGS_ANSI - } while ( found ); - - FindClose( hf ); - } - else - { - expandedArgs.push_back( parsed[i1] ); - } -#ifdef REPLACEARGS_ANSI - } -#endif // REPLACEARGS_ANSI - - delete[] base; - } - else - { - expandedArgs.push_back( parsed[i1] ); - } - } - - { - wchar_t** block = new wchar_t*[expandedArgs.size()]; - int iz = 0; - for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ ) - { - block[iz++] = *it; - } - parsed = block; - numArgs = expandedArgs.size(); - } - - std::vector<gchar*> newArgs; - for ( int i = 0; i < numArgs; i++ ) - { - gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL ); - if ( replacement ) - { -#ifdef REPLACEARGS_DEBUG - gchar *safe2 = Inkscape::IO::sanitizeString(replacement); - - if ( safe2 ) - { - { - char tmp[1024]; - snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 ); - MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION ); - } - g_free( safe2 ); - } -#endif // REPLACEARGS_DEBUG - - newArgs.push_back( replacement ); - } - else - { - newArgs.push_back( blankParam ); - } - } - - // Now push our munged params to be the new argv and argc - { - char** block = new char*[newArgs.size()]; - int iz = 0; - for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ ) - { - block[iz++] = *it; - } - argv = block; - argc = newArgs.size(); - worked = true; - } - } -#ifdef REPLACEARGS_DEBUG - else - { - MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION ); - } -#endif // REPLACEARGS_DEBUG - } -#ifdef REPLACEARGS_DEBUG - else - { - { - MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION ); - } - - char* line2 = GetCommandLineA(); - if ( line2 ) - { - gchar *safe = Inkscape::IO::sanitizeString(line2); - { - { - char tmp[strlen(safe) + 32]; - snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe ); - MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION ); - } - } - } - else - { - MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION ); - } - } -#endif // REPLACEARGS_DEBUG - - return worked; -} -#endif // _WIN32 - -static std::vector<gchar *> -sp_process_args(poptContext ctx) -{ - std::vector<gchar *> fl; - - gint a; - while ((a = poptGetNextOpt(ctx)) != -1) { - switch (a) { - case SP_ARG_FILE: { - gchar const *fn = poptGetOptArg(ctx); - if (fn != nullptr) { - fl.push_back(g_strdup(fn)); - } - break; - } -#ifdef WITH_YAML - case SP_ARG_XVERBS: { - gchar const *fn = poptGetOptArg(ctx); - if (fn != nullptr) { - sp_xverbs_yaml = g_strdup(fn); - Inkscape::CmdLineXAction::createActionsFromYAML((const char *)sp_xverbs_yaml); - } - break; - } -#endif // WITH_YAML - case SP_ARG_VERSION: { - printf("Inkscape %s\n", Inkscape::version_string); - exit(0); - break; - } - case SP_ARG_EXTENSIONDIR: { - printf("%s\n", INKSCAPE_EXTENSIONDIR); - exit(0); - break; - } - case SP_ARG_VERB_LIST: { - // This really shouldn't go here, we should init the app. - // But, since we're just exiting in this path, there is - // no harm, and this is really a better place to put - // everything else. - Inkscape::Extension::init(); - Inkscape::Verb::list(); - exit(0); - break; - } - case SP_ARG_VERB: - case SP_ARG_SELECT: { - gchar const *arg = poptGetOptArg(ctx); - if (arg != nullptr) { - // printf("Adding in: %s\n", arg); - new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg); - } - break; - } - case SP_ARG_CONVERT_DPI_METHOD: { - gchar const *arg = poptGetOptArg(ctx); - if (arg != nullptr) { - if (!strcmp(arg,"none")) { - sp_file_convert_dpi_method_commandline = FILE_DPI_UNCHANGED; - } else if (!strcmp(arg,"scale-viewbox")) { - sp_file_convert_dpi_method_commandline = FILE_DPI_VIEWBOX_SCALED; - } else if (!strcmp(arg,"scale-document")) { - sp_file_convert_dpi_method_commandline = FILE_DPI_DOCUMENT_SCALED; - } else { - g_warning("Invalid update option"); - } - } - break; - } - case POPT_ERROR_BADOPT: { - g_warning ("Invalid option %s", poptBadOption(ctx, 0)); - exit(1); - break; - } - default: { - break; - } - } - } - - gchar const ** const args = poptGetArgs(ctx); - if (args != nullptr) { - for (unsigned i = 0; args[i] != nullptr; i++) { - fl.push_back(g_strdup(args[i])); - } - } - - return fl; -} - -/** - * Print a message for the user to the terminal - * - * If inkscape is run as part of a pipe all messages to standard output - * are redirected to standard error. - * - * \param message Message to print. - */ - -static void do_print_message(const char *message, ...) -{ - va_list args; - - va_start(args, message); - gchar *msg = g_strdup_vprintf(message, args); - - if (sp_writingToPipe) - g_printerr("%s", msg); - else - g_print("%s", msg); - - g_free(msg); - va_end(args); -} - -/** - * Checks if a given filename is meant to be a pipe - * - * In case it is a pipe the variable sp_writingToPipe is set. - * - * \param filename Filename to check for a pipe. - */ - -static void detect_pipe_in_filename(const char *filename) -{ - if (g_strcmp0(filename, "-") == 0) { - sp_writingToPipe = TRUE; - } -} - -/* - 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 : |
