From 13aebd6ec18d5ed131bdfe936639f598a75e1e62 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Tue, 26 Oct 2021 18:55:45 +0200 Subject: [PATCH] several updates to bbox adjust and export selection --- README.md | 2 +- extensions/fablabchemnitz/000_validate.sh | 36 +++++++- .../epilog_dashboard_bbox_adjust.inx | 4 +- .../epilog_dashboard_bbox_adjust.py | 90 +++++++++++++++++-- .../export_selection_as.inx | 9 ++ .../export_selection_as.py | 45 +++++++--- 6 files changed, 160 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 91c2006a..7f1d09e4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MightyScape for Inkscape 1.0+ -In short: A maintained extension collection for Inkscape 1.0+, working on Windows and Linux. There are **233 extension folders** with **406 .inx files** inside. We also take part at https://inkscape.org/gallery/=extension/ (with single extension uploads). +In short: A maintained extension collection for Inkscape 1.0+, working on Windows and Linux. There are **234 extension folders** with **407 .inx files** inside. We also take part at https://inkscape.org/gallery/=extension/ (with single extension uploads). # About MightyScape diff --git a/extensions/fablabchemnitz/000_validate.sh b/extensions/fablabchemnitz/000_validate.sh index f6353e1d..cb8e0d6e 100755 --- a/extensions/fablabchemnitz/000_validate.sh +++ b/extensions/fablabchemnitz/000_validate.sh @@ -1,12 +1,14 @@ #!/bin/bash clear + echo "--> Validating inx files with xmllint. Only errors are printed to console" for folder in */ ; do xmllint --noout --relaxng ./inkscape.extension.rng ${folder}*.inx > /dev/null 2>> 000_xmllint.out done grep -v "validates\|warning: failed to load external entity" 000_xmllint.out; rm 000_xmllint.out + #complete set of meta information AGGLOMERATED_JSON="" for folder in */ ; do @@ -23,35 +25,65 @@ done #print overall json #echo $AGGLOMERATED_JSON | jq + echo "--> Show unique license kinds used:" echo $AGGLOMERATED_JSON | jq -r '.[]|{license}|.[]' | sort | uniq -c + echo "--> show unique list of involved contributors (thanks/credits):" #echo $AGGLOMERATED_JSON | jq -r '.[]|{main_authors}|.[]|.[]' | sort | uniq -c echo $AGGLOMERATED_JSON | jq -r '.[]|{main_authors}|.[]|.[]' | sort | uniq + #show extensions which are in gallery GALLERY_EXTENSIONS=$(echo $AGGLOMERATED_JSON | jq -r '.[]|{inkscape_gallery_url}|.[]' | sort | grep -v "null") for GALLERY_EXTENSION in ${GALLERY_EXTENSIONS}; do EXTENSION=$(echo ${AGGLOMERATED_JSON} | jq -r '.[]|select(.inkscape_gallery_url=="'$GALLERY_EXTENSION'")|{name}|.[]') done + echo "--> Count of inx files:" INX=$(find ./ -type f -name "*.inx" | wc -l) echo INX: $INX + echo "--> Count of extension folders:" FOLDERS=$(ls -d */ | wc -l) echo FOLDERS: $FOLDERS + README="../../README.md" #replace values in README.md sed -i 's/\*\*.* extension folders\*\*/\*\*'${FOLDERS}' extension folders\*\*/g' ${README} sed -i 's/\*\* with .* \.inx files\*\*/\*\* with \*\*'${INX}' \.inx files\*\*/g' ${README} + echo "Removing unrequired pyc cache files" find . -type d -name "__pycache__" -exec rm -rf {} \; + +read -p "Build local gallery extension zip files?" -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Building extension zip files for zipmirror" + TARGETDIR="../../../mightyscape-1.X-zipmirror" + mkdir -p $TARGETDIR > /dev/null 2>&1 + + for EXTENSION in */; do + EXTENSION="${EXTENSION%/}" #strip trailing slash + EXTRA="" + if [[ $EXTENSION == "styles_to_layers" ]] || [[ $EXTENSION == "ungrouper_and_element_migrator_filter" ]] || [[ $EXTENSION == "epilog_dashboard_bbox_adjust" ]]; then + EXTRA="${EXTRA} apply_transformations/" + elif [[ $EXTENSION == "styles_to_layers" ]] || [[ $EXTENSION == "ungrouper_and_element_migrator_filter" ]]; then + EXTRA="${EXTRA} remove_empty_groups/" + fi + ZIPFILE=$TARGETDIR/$EXTENSION.zip + zip -ru $ZIPFILE $EXTENSION/ 000_about_fablabchemnitz.svg $EXTRA > /dev/null 2>&1 + echo "--> creating/updating $ZIPFILE" + done +fi + + read -p "Build local gallery extension zip files?" -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then @@ -64,7 +96,7 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then for GALLERY_EXTENSION in ${GALLERY_EXTENSIONS}; do EXTENSION="$(echo ${AGGLOMERATED_JSON} | jq -r '.[]|select(.inkscape_gallery_url=="'$GALLERY_EXTENSION'")|{path}|.[]')" EXTRA="" - if [[ $EXTENSION == "styles_to_layers" ]] || [[ $EXTENSION == "ungrouper_and_element_migrator_filter" ]]; then + if [[ $EXTENSION == "styles_to_layers" ]] || [[ $EXTENSION == "ungrouper_and_element_migrator_filter" ]] || [[ $EXTENSION == "epilog_dashboard_bbox_adjust" ]]; then EXTRA="${EXTRA} apply_transformations/" elif [[ $EXTENSION == "styles_to_layers" ]] || [[ $EXTENSION == "ungrouper_and_element_migrator_filter" ]]; then EXTRA="${EXTRA} remove_empty_groups/" @@ -72,6 +104,6 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then ZIPFILE=$TARGETDIR/$EXTENSION.zip rm $ZIPFILE > /dev/null 2>&1 echo "--> creating $ZIPFILE" - zip -r $ZIPFILE $EXTENSION/ 000_about_fablabchemnitz.svg $EXTRA + zip -ru $ZIPFILE $EXTENSION/ 000_about_fablabchemnitz.svg $EXTRA done fi diff --git a/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.inx b/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.inx index bb13a277..0e0a14d8 100644 --- a/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.inx +++ b/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.inx @@ -4,8 +4,9 @@ fablabchemnitz.de.epilog_dashboard_bbox_adjust + false 1.0 - + @@ -19,6 +20,7 @@ false + false diff --git a/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.py b/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.py index 0f70f858..4b44e3cd 100644 --- a/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.py +++ b/extensions/fablabchemnitz/epilog_dashboard_bbox_adjust/epilog_dashboard_bbox_adjust.py @@ -10,7 +10,7 @@ So we add a default (small) amount of 1.0 doc units to expand the document's can Author: Mario Voigt / FabLab Chemnitz Mail: mario.voigt@stadtfabrikanten.org Date: 21.04.2021 -Last patch: 27.05.2021 +Last patch: 26.0510.2021 License: GNU GPL v3 #known bugs: @@ -23,8 +23,10 @@ License: GNU GPL v3 ''' import math +import sys import inkex from inkex import Transform +sys.path.append("../apply_transformations") class EpilogDashboardBboxAdjust(inkex.EffectExtension): @@ -39,33 +41,105 @@ class EpilogDashboardBboxAdjust(inkex.EffectExtension): def add_arguments(self, pars): pars.add_argument("--tab") + pars.add_argument("--apply_transformations", type=inkex.Boolean, default=False, help="Run 'Apply Transformations' extension before running vpype. Helps avoiding geometry shifting") pars.add_argument("--offset", type=float, default="1.0", help="XY Offset (mm) from top left corner") pars.add_argument("--removal", default="none", help="Remove all elements outside the bounding box or selection") pars.add_argument("--use_machine_size", type=inkex.Boolean, default=False, help="Use machine size") pars.add_argument("--machine_size", default="812x508", help="Machine/Size") pars.add_argument("--debug", type=inkex.Boolean, default=False, help="Debug output") - + pars.add_argument("--skip_errors", type=inkex.Boolean, default=False, help="Skip on errors") + def effect(self): + + applyTransformationsAvailable = False # at first we apply external extension + try: + import apply_transformations + applyTransformationsAvailable = True + except Exception as e: + # self.msg(e) + self.msg("Calling 'Apply Transformations' extension failed. Maybe the extension is not installed. You can download it from official InkScape Gallery. Skipping ...") + + if self.options.apply_transformations is True and applyTransformationsAvailable is True: + apply_transformations.ApplyTransformations().recursiveFuseTransform(self.document.getroot()) + offset = self.options.offset - #units = self.svg.unit units = "mm" #force millimeters + scale_factor = self.svg.unittouu("1px") + #namedView = self.document.getroot().find(inkex.addNS('namedview', 'sodipodi')) + #doc_units = namedView.get(inkex.addNS('document-units', 'inkscape')) + #doc_units = self.svg.unit + #https://wiki.inkscape.org/wiki/Units_In_Inkscape + #remove sodipodi units. Some actions add units to namedview, but we already have "inkscape:document-units". + #namedView.pop('units') + #del namedView.attrib["units"] #does the same like namedView.pop('units') # create a new bounding box and get the bbox size of all elements of the document (we cannot use the page's bbox) bbox = inkex.BoundingBox() if len(self.svg.selected) > 0: - bbox = self.svg.selection.bounding_box() - #for element in self.svg.selected.values(): - # bbox += element.bounding_box() + #bbox = self.svg.selection.bounding_box() #it could be so easy! But ... + for element in self.svg.selected.values(): + ''' + ...rectangles cause some strangle scaling issue, offendingly caused by namedview units. + The rectangle attributes are set in px. They ignore the real units from namedview. + Strange fact: ellipses, spirals and other primitives work flawlessly. + ''' + if isinstance (element, inkex.Rectangle): + bbox += element.bounding_box() * scale_factor + elif isinstance (element, inkex.TextElement): + if self.options.skip_errors is False: + self.msg("Text elements are not supported!") + return + else: + continue + else: + bbox += element.bounding_box() else: #for element in self.svg.root.getchildren(): for element in self.document.getroot().iter("*"): if isinstance (element, inkex.ShapeElement) and element.tag != inkex.addNS('use','svg') and element.get('inkscape:groupmode') != 'layer': #bbox fails for svg:use elements and layers: - bbox += element.bounding_box() + if isinstance (element, inkex.Rectangle): + bbox += element.bounding_box() * scale_factor + elif isinstance (element, inkex.TextElement): + if self.options.skip_errors is False: + self.msg("Text elements are not supported!") + return + else: + continue + else: + bbox += element.bounding_box() if abs(bbox.width) == math.inf or abs(bbox.height) == math.inf: - inkex.utils.debug("Error calculating bounding box! Impossible to continue!") + inkex.utils.debug("Error while calculating overall bounding box! Check your element types. Things like svg:text or svg:use are not supported. Impossible to continue!") return + #if len(self.svg.selected) > 0: + # selected = self.svg.selected + #else: + # selected = self.svg.root.getchildren() + #for element in selected: + # transform = inkex.Transform() + # parent = element.getparent() + # if parent is not None and isinstance(parent, inkex.ShapeElement): + # transform = parent.composed_transform() + # try: + # ''' + # ...rectangles cause some strangle scaling issue, offendingly caused by namedview units. + # The rectangle attributes are set in px. They ignore the real units from namedview. + # Strange fact: ellipses, spirals and other primitives work flawlessly. + # ''' + # if isinstance (element, inkex.Rectangle) or isinstance (element, inkex.TextElement): + # bbox += element.bounding_box(transform) * scale_factor + # else: + # bbox += element.bounding_box(transform) + # except Exception: + # logger.exception("Bounding box not computed") + # logger.info("Skipping bounding box") + # transform = element.composed_transform() + # x1, y1 = transform.apply_to_point([0, 0]) + # x2, y2 = transform.apply_to_point([1, 1]) + # bbox += inkex.BoundingBox((x1, x2), (y1, y2)) + + # adjust the viewBox to the bbox size and add the desired offset if self.options.use_machine_size is True: machineWidth = float(self.options.machine_size.split('x')[0]) diff --git a/extensions/fablabchemnitz/export_selection_as/export_selection_as.inx b/extensions/fablabchemnitz/export_selection_as/export_selection_as.inx index be0f65e2..29c517f9 100644 --- a/extensions/fablabchemnitz/export_selection_as/export_selection_as.inx +++ b/extensions/fablabchemnitz/export_selection_as/export_selection_as.inx @@ -6,6 +6,14 @@ false 1.000 + + + + + + + + ./inkscape_export/ false /usr/share/inkscape/extensions/dxf_outlines.py @@ -16,6 +24,7 @@ 96 false false + false diff --git a/extensions/fablabchemnitz/export_selection_as/export_selection_as.py b/extensions/fablabchemnitz/export_selection_as/export_selection_as.py index 92cb76bc..d95947f8 100644 --- a/extensions/fablabchemnitz/export_selection_as/export_selection_as.py +++ b/extensions/fablabchemnitz/export_selection_as/export_selection_as.py @@ -32,6 +32,7 @@ class ExportObject(inkex.EffectExtension): pars.add_argument("--tab") pars.add_argument("--wrap_transform", type=inkex.Boolean, default=False, help="Wrap final document in transform") pars.add_argument("--border_offset", type=float, default=1.000, help="Add border offset around selection") + pars.add_argument("--border_offset_unit", default="mm", help="Offset unit") pars.add_argument("--export_dir", default="~/inkscape_export/", help="Location to save exported documents") pars.add_argument("--opendir", type=inkex.Boolean, default=False, help="Open containing output directory after export") pars.add_argument("--dxf_exporter_path", default="/usr/share/inkscape/extensions/dxf_outlines.py", help="Location of dxf_outlines.py") @@ -42,6 +43,7 @@ class ExportObject(inkex.EffectExtension): pars.add_argument("--png_dpi", type=float, default=96, help="PNG DPI (applies for export and replace)") pars.add_argument("--replace_by_png", type=inkex.Boolean, default=False, help="Replace selection by png export") pars.add_argument("--newwindow", type=inkex.Boolean, default=False, help="Open file in new Inkscape window") + pars.add_argument("--skip_errors", type=inkex.Boolean, default=False, help="Skip on errors") def openExplorer(self, dir): if os.name == 'nt': @@ -61,6 +63,7 @@ class ExportObject(inkex.EffectExtension): warnings.simplefilter("default", ResourceWarning) def effect(self): + scale_factor = self.svg.unittouu("1px") svg_export = self.options.export_svg extra_param = "--batch-process" @@ -90,7 +93,7 @@ class ExportObject(inkex.EffectExtension): export_dir = Path(self.absolute_href(self.options.export_dir)) os.makedirs(export_dir, exist_ok=True) - offset = self.options.border_offset + offset = self.svg.unittouu(str(self.options.border_offset) + self.options.border_offset_unit) bbox = inkex.BoundingBox() @@ -98,21 +101,35 @@ class ExportObject(inkex.EffectExtension): firstId = selected[0].get('id') parent = self.svg.getElementById(firstId).getparent() - for elem in selected.values(): + for element in selected.values(): transform = inkex.Transform() - parent = elem.getparent() + parent = element.getparent() if parent is not None and isinstance(parent, inkex.ShapeElement): transform = parent.composed_transform() try: - bbox += elem.bounding_box(transform) + ''' + ...rectangles cause some strangle scaling issue, offendingly caused by namedview units. + The rectangle attributes are set in px. They ignore the real units from namedview. + Strange fact: ellipses, spirals and other primitives work flawlessly. + ''' + if isinstance (element, inkex.Rectangle): + bbox += element.bounding_box(transform) * scale_factor + elif isinstance (element, inkex.TextElement): + if self.options.skip_errors is False: + self.msg("Text elements are not supported!") + return + else: + continue + else: + bbox += element.bounding_box(transform) except Exception: logger.exception("Bounding box not computed") logger.info("Skipping bounding box") - transform = elem.composed_transform() + transform = element.composed_transform() x1, y1 = transform.apply_to_point([0, 0]) x2, y2 = transform.apply_to_point([1, 1]) bbox += inkex.BoundingBox((x1, x2), (y1, y2)) - + template = self.create_document() svg_filename = None @@ -120,12 +137,12 @@ class ExportObject(inkex.EffectExtension): group.attrib['id'] = GROUP_ID group.attrib['transform'] = str(inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top)))) - for elem in self.svg.selected.values(): - if elem.tag == inkex.addNS('image', 'svg'): + for element in self.svg.selected.values(): + if element.tag == inkex.addNS('image', 'svg'): continue #skip images - elem_copy = deepcopy(elem) - elem_copy.attrib['transform'] = str(elem.composed_transform()) - elem_copy.attrib['style'] = str(elem.composed_style()) + elem_copy = deepcopy(element) + elem_copy.attrib['transform'] = str(element.composed_transform()) + elem_copy.attrib['style'] = str(element.composed_style()) group.append(elem_copy) template.attrib['viewBox'] = f'{-offset} {-offset} {bbox.width + offset * 2} {bbox.height + offset * 2}' @@ -133,7 +150,7 @@ class ExportObject(inkex.EffectExtension): template.attrib['height'] = f'{bbox.height + offset * 2}' + self.svg.unit if svg_filename is None: - filename_base = elem.attrib.get('id', None).replace(os.sep, '_') + filename_base = element.attrib.get('id', None).replace(os.sep, '_') if filename_base: svg_filename = filename_base + '.svg' if not filename_base: #should never be the case. Inkscape might crash if the id attribute is empty or not existent due to invalid SVG @@ -241,8 +258,8 @@ class ExportObject(inkex.EffectExtension): self.msg(cli_output) #then remove the selection and replace it by png #self.msg(parent.get('id')) - for elem in selected.values(): - elem.delete() + for element in selected.values(): + element.delete() #read png file and get base64 string from it try: img = Image.open(png_export)