diff --git a/CMakeLists.txt b/CMakeLists.txt index c64887a8c340073b7fdeba068e170e878a8f90bf..b94ef9c98d25879ede6ecec53a95fa23dcee7abe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,8 +160,6 @@ if(NOT WIN32) add_subdirectory(man) endif() - - # ----------------------------------------------------------------------------- # Check License Headers # ----------------------------------------------------------------------------- diff --git a/buildtools/generate-gresource-xml.py b/buildtools/generate-gresource-xml.py new file mode 100755 index 0000000000000000000000000000000000000000..2efa1678f7bbd7aa6bfe26a730a153117ac08bf6 --- /dev/null +++ b/buildtools/generate-gresource-xml.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-3.0-or-later +# Resource xml generator +# Author: Martin Owens +# Licensed under GPL version 3 or any later version. + +import glob +import os +import sys + +from collections import defaultdict + +PREFIX = '/org/inkscape/' + +SCRIPT = os.path.abspath(os.path.dirname(__file__)) +PATH = os.path.abspath(os.path.join(SCRIPT, '..', 'share')) + +if len(sys.argv) == 2: + PATH = os.path.abspath(sys.argv[-1]) + +if not os.path.isdir(PATH): + sys.stderr.write(f"Couldn't find share path: '{PATH}'") + sys.exit(1) + +WHAT = [ + # Path to the resources, fnmatch, xml attributes + ('ui', "*.ui", {"preprocess": "xml-stripblanks", "compressed": "true"}), + ('ui', "*.glade", {"preprocess": "xml-stripblanks", "compressed": "true"}), + ('extensions', '*.inx', {"preprocess": "xml-stripblanks", "compressed": "true"}), +] + +if __name__ == '__main__': + output = "\n" + for path, pattern, attrs in WHAT: + fullpath = os.path.join(PATH, path) + if not os.path.isdir(fullpath): + sys.stderr.write(f"Can't find resource path: {path}\n") + sys.exit(1) + + att = "" + for key, val in attrs.items(): + att += f" {key}=\"{val}\"" + + for root, subdirs, files in os.walk(fullpath): + for file in files: + if glob.fnmatch.fnmatch(file, pattern): + internal_name = os.path.join(path, root.replace(fullpath, '').strip('/'), file) + output += f" {internal_name}\n" + + if not output: + sys.exit(2) + + print(f""" + + {output} +""") diff --git a/share/inkscape.gresources.xml b/share/inkscape.gresources.xml new file mode 100644 index 0000000000000000000000000000000000000000..19afcfca3165d46a40862acda71cf05781c1ad12 --- /dev/null +++ b/share/inkscape.gresources.xml @@ -0,0 +1,253 @@ + + + + ui/toolbar-marker.ui + ui/toolbar-calligraphy.ui + ui/toolbar-star.ui + ui/toolbar-select.ui + ui/toolbar-text.ui + ui/toolbar-spiral.ui + ui/toolbar-box3d.ui + ui/widget-new-from-template.ui + ui/toolbar-tweak.ui + ui/toolbar-measure.ui + ui/toolbar-snap.ui + ui/toolbar-booleans.ui + ui/toolbar-spray.ui + ui/toolbar-commands.ui + ui/toolbar-node.ui + ui/inkview-controls.ui + ui/toolbar-rect.ui + ui/toolbar-objectpicker.ui + ui/toolbar-mesh.ui + ui/toolbar-gradient.ui + ui/toolbar-arc.ui + ui/document-tab-preview.ui + ui/toolbar-dropper.ui + ui/statusbar.ui + ui/toolbar-connector.ui + ui/simple-tab.ui + ui/toolbar-paintbucket.ui + ui/toolbar-eraser.ui + ui/toolbar-tool-prefs.ui + ui/toolbar-pencil.ui + ui/toolbar-page.ui + ui/toolbar-lpe.ui + ui/dialog-spellcheck.ui + ui/dialog-measure-tool-settings.ui + ui/toolbar-tool.ui + ui/align-and-distribute.ui + ui/menus.ui + ui/document-tab.ui + ui/toolbar-zoom.ui + ui/dialog-objects.glade + ui/dialog-livepatheffect.glade + ui/dialog-filter-editor.glade + ui/attribute-edit-component.glade + ui/font-list.glade + ui/page-properties.glade + ui/completion-box.glade + ui/object-attributes.glade + ui/pattern-edit.glade + ui/dialog-livepatheffect-item.glade + ui/canvas-notice.glade + ui/dialog-trace.glade + ui/display-popup.glade + ui/inkscape-start.glade + ui/dialog-css.glade + ui/dialog-text-edit.glade + ui/dialog-xml.glade + ui/command-palette-main.glade + ui/color-page.glade + ui/color-palette.glade + ui/marker-popup.glade + ui/command-palette-operation.glade + ui/dialog-save-template.glade + ui/gradient-edit.glade + ui/dialog-export.glade + ui/dialog-paint-servers.glade + ui/dialog-crash.glade + ui/dialog-document-resources.glade + ui/dialog-symbols.glade + ui/image-properties.glade + ui/dialog-swatches.glade + ui/dialog-extensions.glade + ui/inkscape-about.glade + ui/extension-pdfinput.glade + ui/dialog-export-prefs.glade + ui/dialog-font-collections.glade + extensions/export_gimp_palette.inx + extensions/typst_formula.inx + extensions/straightseg.inx + extensions/convert2dashes.inx + extensions/voronoi_fill.inx + extensions/jessyink_install.inx + extensions/ps_input.inx + extensions/frame.inx + extensions/path_to_absolute.inx + extensions/interp_att_g.inx + extensions/render_barcode.inx + extensions/guides_creator.inx + extensions/color_hsl_adjust.inx + extensions/raster_output_tiff.inx + extensions/color_replace.inx + extensions/pixelsnap.inx + extensions/color_lesssaturation.inx + extensions/color_removeblue.inx + extensions/measure.inx + extensions/tar_layers.inx + extensions/template_seamless_pattern.inx + extensions/text_randomcase.inx + extensions/jessyink_effects.inx + extensions/layers2svgfont.inx + extensions/color_morelight.inx + extensions/text_sentencecase.inx + extensions/printing_marks.inx + extensions/fractalize.inx + extensions/lorem_ipsum.inx + extensions/web_set_att.inx + extensions/image_extract_selected.inx + extensions/param_curves.inx + extensions/guillotine.inx + extensions/scribus_export_pdf.inx + extensions/output_scour.inx + extensions/hershey.inx + extensions/template_business_card.inx + extensions/text_merge.inx + extensions/voronoi_diagram.inx + extensions/svg2fxg.inx + extensions/pdflatex.inx + extensions/media_zip.inx + extensions/web_interactive_mockup.inx + extensions/color_blackandwhite.inx + extensions/web_transmit_att.inx + extensions/foldablebox.inx + extensions/flatten.inx + extensions/perspective.inx + extensions/rtree.inx + extensions/template_dvd_cover.inx + extensions/gimp_xcf.inx + extensions/markers_strokepaint.inx + extensions/eps_input.inx + extensions/layer2png.inx + extensions/jessyink_transitions.inx + extensions/dpi96to90.inx + extensions/image_attributes.inx + extensions/spirograph.inx + extensions/text_split.inx + extensions/replace_font.inx + extensions/grid_polar.inx + extensions/previous_glyph_layer.inx + extensions/dxf_outlines.inx + extensions/handles.inx + extensions/next_glyph_layer.inx + extensions/long_shadow.inx + extensions/text_titlecase.inx + extensions/color_desaturate.inx + extensions/layout_nup.inx + extensions/dxf_input.inx + extensions/twirl.inx + extensions/path_number_nodes.inx + extensions/text_flipcase.inx + extensions/polyhedron_3d.inx + extensions/svgfont2layers.inx + extensions/image_embed_selected.inx + extensions/hpgl_input.inx + extensions/render_alphabetsoup.inx + extensions/jessyink_export.inx + extensions/text_lowercase.inx + extensions/render_barcode_datamatrix.inx + extensions/merge_styles.inx + extensions/render_gears.inx + extensions/jessyink_autotexts.inx + extensions/nicechart.inx + extensions/aisvg.inx + extensions/path_mesh_m2p.inx + extensions/webslicer_create_rect.inx + extensions/interp.inx + extensions/jessyink_master_slide.inx + extensions/new_glyph_layer.inx + extensions/construct_from_triangle.inx + extensions/image_embed.inx + extensions/image_extract.inx + extensions/render_barcode_qrcode.inx + extensions/ungroup_deep.inx + extensions/color_lesshue.inx + extensions/extrude.inx + extensions/dpi90to96.inx + extensions/color_lesslight.inx + extensions/jessyink_key_bindings.inx + extensions/ink2canvas.inx + extensions/color_randomize.inx + extensions/hpgl_output.inx + extensions/rubberstretch.inx + extensions/color_negative.inx + extensions/funcplot.inx + extensions/color_rgbbarrel.inx + extensions/dimension.inx + extensions/distribute_along_path.inx + extensions/jitternodes.inx + extensions/jessyink_summary.inx + extensions/text_uppercase.inx + extensions/restack.inx + extensions/color_removered.inx + extensions/path_envelope.inx + extensions/webslicer_export.inx + extensions/inset_shadow.inx + extensions/raster_output_jpg.inx + extensions/patternalongpath.inx + extensions/color_morehue.inx + extensions/webslicer_create_group.inx + extensions/print_win32_vector.inx + extensions/wireframe_sphere.inx + extensions/cgm_input.inx + extensions/setup_typography_canvas.inx + extensions/color_custom.inx + extensions/dhw_input.inx + extensions/perfectboundcover.inx + extensions/color_darker.inx + extensions/fig_input.inx + extensions/jessyink_video.inx + extensions/color_grayscale.inx + extensions/jessyink_view.inx + extensions/color_moresaturation.inx + extensions/doc_ai_convert.inx + extensions/raster_output_webp.inx + extensions/lindenmayer.inx + extensions/triangle.inx + extensions/grid_cartesian.inx + extensions/color_removegreen.inx + extensions/jessyink_mouse_handler.inx + extensions/color_brighter.inx + extensions/dxf12_outlines.inx + extensions/addnodes.inx + extensions/text_braille.inx + extensions/docinfo.inx + extensions/text_extract.inx + extensions/synfig_output.inx + extensions/inkscape_follow_link.inx + extensions/path_mesh_p2m.inx + extensions/color_list.inx + extensions/svgcalendar.inx + extensions/jessyink_uninstall.inx + extensions/grid_isometric.inx + extensions/plotter.inx + extensions/raster_output_png.inx + extensions/render_gear_rack.inx + extensions/other/templates/template_shape_prefab.inx + extensions/other/clipart/import_web_image.inx + extensions/other/gcodetools/gcodetools_prepare_path_for_plasma.inx + extensions/other/gcodetools/gcodetools_lathe.inx + extensions/other/gcodetools/gcodetools_dxf_points.inx + extensions/other/gcodetools/gcodetools_area.inx + extensions/other/gcodetools/gcodetools_about.inx + extensions/other/gcodetools/gcodetools_engraving.inx + extensions/other/gcodetools/gcodetools_orientation_points.inx + extensions/other/gcodetools/gcodetools_tools_library.inx + extensions/other/gcodetools/gcodetools_graffiti.inx + extensions/other/gcodetools/gcodetools_path_to_gcode.inx + extensions/other/extension-xaml/inkxaml/svg2xaml.inx + extensions/other/extension-xaml/inkxaml/xaml2svg.inx + extensions/other/inkman/manage_extensions.inx + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6e620d538e88be76fb5aa0c52863e0a624ffa230..bbb644a9eccbe1272a1c28b1dabb89d438bdbe50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -288,6 +288,38 @@ list(APPEND inkscape_SRC ${CMAKE_BINARY_DIR}/src/inkscape-version.cpp ) +# ----------------------------------------------------------------------------- +# Generate resource file +# ----------------------------------------------------------------------------- + +add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/include/inkscape-gresources.h + COMMAND glib-compile-resources + ${CMAKE_SOURCE_DIR}/share/inkscape.gresources.xml + --target=${CMAKE_BINARY_DIR}/include/inkscape-gresources.h + --internal + --generate + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/share/ + COMMENT "Generating inkscape-gresources.h") + +add_custom_command( + OUTPUT ${CMAKE_BINARY_DIR}/src/inkscape-gresources.c always-run-gr-source + COMMAND glib-compile-resources + ${CMAKE_SOURCE_DIR}/share/inkscape.gresources.xml + --target=${CMAKE_BINARY_DIR}/src/inkscape-gresources.c + --internal + --generate + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/share/ + COMMENT "Generating inkscape-gresources.c") + +list(APPEND inkscape_SRC + ${CMAKE_BINARY_DIR}/include/inkscape-gresources.h + ${CMAKE_BINARY_DIR}/src/inkscape-gresources.c +) + +add_custom_target(build-gresources + DEPENDS always-run-gr-source +) # ----------------------------------------------------------------------------- # Load in subdirectories diff --git a/src/extension/init.cpp b/src/extension/init.cpp index 9226ced6f420e970522534455ab332ad55d4462b..22c9c252d7cd967c79494c8fa0001a639d17a64a 100644 --- a/src/extension/init.cpp +++ b/src/extension/init.cpp @@ -247,10 +247,15 @@ init() load_user_extensions(); load_shared_extensions(); - for(auto &filename: get_filenames(SYSTEM, EXTENSIONS, {SP_MODULE_EXTENSION})) { - build_from_file(filename.c_str()); + // We need to tell it where the inx would have been so it can match py files + auto const resource_root = get_path_string(GRESOURCE, EXTENSIONS); + auto const system_root = get_path_string(SYSTEM, EXTENSIONS); + + for(auto &resource : get_resources(EXTENSIONS, {SP_MODULE_EXTENSION})) { + build_from_resource(resource_root + resource, system_root + resource); } + /* this is at the very end because it has several catch-alls * that are possibly over-ridden by other extensions (such as * svgz) diff --git a/src/extension/system.cpp b/src/extension/system.cpp index e09a46f6dd2fb122f0641736d981df6ccf4ba82e..025d8efede2d5f44290c5eb1b0c5bcb10f91f3f5 100644 --- a/src/extension/system.cpp +++ b/src/extension/system.cpp @@ -459,6 +459,31 @@ build_from_file(gchar const *filename) Inkscape::GC::release(doc); } +/** + * \brief This function creates a module from a filename of an + * XML description. + * \param resource The file holding the XML description of the module. + * \param filename The location the inx *would have had* on the disk. + * + * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc. + */ +void +build_from_resource(std::string const &resource, std::string const &filename) +{ + auto doc = sp_repr_read_resource(resource, INKSCAPE_EXTENSION_URI); + if (!doc) { + g_critical("Inkscape::Extension::build_from_file() - XML description loaded from '%s' not valid.", resource.c_str()); + return; + } + + std::string dir = Glib::path_get_dirname(filename); + auto file_name = Glib::path_get_basename(filename); + if (!build_from_reprdoc(doc, {}, &dir, &file_name)) { + g_warning("Inkscape::Extension::build_from_file() - Could not parse extension from '%s'.", resource.c_str()); + } + Inkscape::GC::release(doc); +} + /** * \brief Create a module from a buffer holding an XML description. * \param buffer The buffer holding the XML description of the module. diff --git a/src/extension/system.h b/src/extension/system.h index 1ded8396038f1a0a57af276523ffdb4651163638..eaabfc0cf8428e1b46377f574212db8152e850ac 100644 --- a/src/extension/system.h +++ b/src/extension/system.h @@ -48,6 +48,7 @@ void save(Extension *key, SPDocument *doc, char const *filename, Inkscape::Extension::FileSaveMethod save_method); Print *get_print(char const *key); void build_from_file(char const *filename); +void build_from_resource(std::string const &resource, std::string const &filename); void build_from_mem(char const *buffer, std::unique_ptr in_imp); /** diff --git a/src/inkscape.cpp b/src/inkscape.cpp index d55d303e0aa22cb43451eb94e32cbbe940801956..d4be2a8d703d205038b99df81dab0bee82013413 100644 --- a/src/inkscape.cpp +++ b/src/inkscape.cpp @@ -240,13 +240,18 @@ Application::Application(bool use_gui) : auto display = Gdk::Display::get_default(); auto icon_theme = Gtk::IconTheme::get_for_display(display); auto search_paths = icon_theme->get_search_path(); + search_paths.clear(); // prepend search paths or else hicolor icons fallback will fail for (auto type : {USER, SHARED, SYSTEM}) { auto path = get_path_string(type, ICONS); if (!path.empty()) { + g_warning(" > '%s'", path.c_str()); search_paths.insert(search_paths.begin(), path); } } + for (auto &path : search_paths) { + g_warning(" < '%s'", path.c_str()); + } icon_theme->set_search_path(search_paths); themecontext = new Inkscape::UI::ThemeContext(); diff --git a/src/io/resource.cpp b/src/io/resource.cpp index 1241b88ac7b10e033f988624fc2db57d43aff15d..567f21c4d944eb879e3c87dc4424af1aa615dc7f 100644 --- a/src/io/resource.cpp +++ b/src/io/resource.cpp @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -40,6 +41,7 @@ using Inkscape::IO::file_test; namespace Inkscape::IO::Resource { #define INKSCAPE_PROFILE_DIR "inkscape" +#define INKSCAPE_GRESOURCE_PREFIX "/org/inkscape/" /** * @returns Path. Value is in platform-native encoding (see Glib::filename_to_utf8). @@ -80,6 +82,7 @@ gchar *_get_path(Domain domain, Type type, char const *filename, char const *ext case SYSTEM: sysdir = "inkscape"; case SHARED: + case GRESOURCE: case USER: { switch (type) { case ATTRIBUTES: name = "attributes"; break; @@ -89,7 +92,7 @@ gchar *_get_path(Domain domain, Type type, char const *filename, char const *ext case FILTERS: name = "filters"; break; case FONTS: name = "fonts"; break; case FONTCOLLECTIONS: name = "fontcollections"; break; - case ICONS: name = "icons"; break; + case ICONS: name = "others"; break; case KEYS: name = "keys"; break; case MARKERS: name = "markers"; break; case PAINT: name = "paint"; break; @@ -105,6 +108,11 @@ gchar *_get_path(Domain domain, Type type, char const *filename, char const *ext } } break; } + + if (domain == GRESOURCE) { + return g_build_path("/", INKSCAPE_GRESOURCE_PREFIX, name, filename, extra, nullptr); + } + // Look for an over-ride in the local environment if (envor && domain == USER) { std::string env_dir = Glib::getenv(envor); @@ -287,6 +295,41 @@ std::vector get_filenames(std::string path, std::vector get_resources(Type type, std::vector const &extensions) +{ + std::vector resources; + std::vector paths = {"/"}; + auto const root = get_path_string(GRESOURCE, type); + + // Recursive lookup + while(!paths.empty()) { + auto resource_path = paths.back(); + auto resource_root = root + resource_path; + paths.pop_back(); + for (auto &resource : Gio::Resource::enumerate_children_global(resource_root.c_str())) { + // If not extensions are specified, don't reject ANY files. + bool reject = !extensions.empty(); + + // Unreject any file which has one of the extensions. + for (auto &ext: extensions) { + reject ^= Glib::str_has_suffix(resource, ext); + } + if (resource.back() == '/') { + paths.emplace_back(resource_path + resource); + } else if(!reject) { + resources.emplace_back(resource_path + resource); + } + } + } + return resources; +} + /* * Gets all folders inside each type, for all domain types. * diff --git a/src/io/resource.h b/src/io/resource.h index 3cea902b158e2cdf756291f27250127bcc6b1505..fee24325dc1f9ce3375a0557b93723226fb3287d 100644 --- a/src/io/resource.h +++ b/src/io/resource.h @@ -47,6 +47,7 @@ enum Domain { CREATE, CACHE, SHARED, + GRESOURCE, USER }; @@ -82,6 +83,10 @@ std::vector get_filenames(std::string path, std::vector const &extensions={}, std::vector const &exclusions={}); +[[nodiscard]] +std::vector get_resources(Type type, + std::vector const &extensions = {}); + [[nodiscard]] std::vector get_foldernames(Type type, std::vector const &exclusions = {}); diff --git a/src/ui/builder-utils.cpp b/src/ui/builder-utils.cpp index 21c24a19bdbee347249496372e437d97a8ad5ee8..3e8beb3d40aafe94627058ed434da55257b1d0da 100644 --- a/src/ui/builder-utils.cpp +++ b/src/ui/builder-utils.cpp @@ -34,10 +34,17 @@ void throw_missing(const char* object_type, const char* id) } // namespace Detail Glib::RefPtr create_builder(const char* filename) { - auto glade = Inkscape::IO::Resource::get_filename(Inkscape::IO::Resource::UIS, filename); + auto glade = IO::Resource::get_path_string(IO::Resource::GRESOURCE, IO::Resource::UIS, filename); Glib::RefPtr builder; + + static bool loaded = false; + if (!loaded) { + g_resources_register(inkscape_get_resource()); + loaded = true; + } + try { - return Gtk::Builder::create_from_file(glade); + return Gtk::Builder::create_from_resource(glade); } catch (Glib::Error& ex) { g_error("Cannot load glade file: %s", ex.what()); diff --git a/src/ui/builder-utils.h b/src/ui/builder-utils.h index 1f6a90cd25004449cfec3f52d216211b475b33cc..c4fc82a219b74e0375b752610786c05c9b2ca7ed 100644 --- a/src/ui/builder-utils.h +++ b/src/ui/builder-utils.h @@ -18,6 +18,10 @@ #include #include +extern "C" { +#include "inkscape-gresources.h" +} + namespace Inkscape { namespace UI { diff --git a/src/ui/cursor-utils.cpp b/src/ui/cursor-utils.cpp index b3a8aa9008db25b4540419c3c357c98f9dbf6817..f87ee4572a17f3853a88dc494ffeea298980deae 100644 --- a/src/ui/cursor-utils.cpp +++ b/src/ui/cursor-utils.cpp @@ -40,9 +40,6 @@ #include "util/statics.h" #include "util/units.h" -using Inkscape::IO::Resource::SYSTEM; -using Inkscape::IO::Resource::ICONS; - namespace Inkscape { // SVG cursor unique ID/key diff --git a/src/xml/repr-io.cpp b/src/xml/repr-io.cpp index 7422d6a54aa817a78313ef4fe74acb9598089218..a81195a74e078edde1916b4d315b5551af6b412d 100644 --- a/src/xml/repr-io.cpp +++ b/src/xml/repr-io.cpp @@ -37,6 +37,7 @@ #include "preferences.h" +#include #include using Inkscape::IO::Writer; @@ -318,6 +319,19 @@ Document *sp_repr_read_file (const gchar * filename, const gchar *default_ns, bo return rdoc; } +Document *sp_repr_read_resource(std::string const &resource, const gchar *default_ns) +{ + try { + if (auto bytes = Gio::Resource::lookup_data_global(resource)) { + auto size = bytes->get_size(); + return sp_repr_read_mem(reinterpret_cast(bytes->get_data(size)), size, default_ns); + } + } catch (Glib::Error& ex) { + g_warning("Cannot load resource file: %s", ex.what()); + } + return nullptr; +} + /** * Reads and parses XML from a buffer, returning it as an Document */ diff --git a/src/xml/repr.h b/src/xml/repr.h index d1183495424ed7b76be27c327c1491b01672773a..5ed09eca23de85c4f4363ff3b1da38d1b22d6021 100644 --- a/src/xml/repr.h +++ b/src/xml/repr.h @@ -53,6 +53,7 @@ Inkscape::XML::Document *sp_repr_document_new(char const *rootname); /* IO */ Inkscape::XML::Document *sp_repr_read_file(char const *filename, char const *default_ns, bool xinclude = false); +Inkscape::XML::Document *sp_repr_read_resource(std::string const &resource, const gchar *default_ns); Inkscape::XML::Document *sp_repr_read_mem(char const *buffer, int length, char const *default_ns); void sp_repr_write_stream(Inkscape::XML::Node *repr, Inkscape::IO::Writer &out, int indent_level, bool add_whitespace, Glib::QueryQuark elide_prefix,