From 196337a7bc37de2dba0c721ff9edfd6a521e7691 Mon Sep 17 00:00:00 2001 From: Mario Voigt Date: Mon, 3 Oct 2022 13:42:30 +0200 Subject: [PATCH] adding back several more extensions --- .../meta.json | 6 +- .../dots_to_path_points/meta.json | 4 +- .../fablabchemnitz/edit_attributes/meta.json | 4 +- .../filter_by_length_area/meta.json | 6 +- .../filter_to_layer/filter_to_layer.inx | 21 + .../filter_to_layer/filter_to_layer.py | 82 + .../fablabchemnitz/filter_to_layer/meta.json | 21 + .../fablabchemnitz/gpx_import/gpx_import.inx | 28 + .../fablabchemnitz/gpx_import/gpx_import.py | 379 +++++ .../fablabchemnitz/gpx_import/meta.json | 21 + .../fablabchemnitz/gradient_saver/GUI.glade | 667 ++++++++ .../gradient_saver/gradient_saver.inx | 16 + .../gradient_saver/gradient_saver.py | 386 +++++ .../fablabchemnitz/gradient_saver/icon.svg | 678 ++++++++ .../fablabchemnitz/gradient_saver/meta.json | 21 + .../grey_to_monoalpha/grey_to_monoalpha.inx | 47 + .../grey_to_monoalpha/grey_to_monoalpha.py | 99 ++ .../grey_to_monoalpha/meta.json | 21 + .../image_triangulation/meta.json | 4 +- .../fablabchemnitz/inset_alignment/meta.json | 4 +- extensions/fablabchemnitz/my-gradients.svg | 17 + .../fablabchemnitz/output_pro/meta.json | 21 + .../fablabchemnitz/output_pro/output_pro.inx | 16 + .../fablabchemnitz/output_pro/output_pro.py | 1091 ++++++++++++ .../output_pro/outputpro/__init__.py | 0 .../output_pro/outputpro/alpha.png | Bin 0 -> 170 bytes .../output_pro/outputpro/cmyk.py | 118 ++ .../output_pro/outputpro/cutmarks.py | 84 + .../output_pro/outputpro/preview_mask.png | Bin 0 -> 2029 bytes .../output_pro/outputpro/top.png | Bin 0 -> 70828 bytes .../fablabchemnitz/output_pro/result-imp.jpeg | Bin 0 -> 324815 bytes .../paths_to_openscad/meta.json | 22 + .../paths_to_openscad/paths_to_openscad.inx | 95 ++ .../paths_to_openscad/paths_to_openscad.py | 1487 +++++++++++++++++ .../fablabchemnitz/pixels2objects/meta.json | 4 +- extensions/fablabchemnitz/primitive/meta.json | 6 +- .../printing_marks_dotted/meta.json | 21 + .../printing_marks_dotted.inx | 46 + .../printing_marks_dotted.py | 475 ++++++ .../render_silhouette_regmarks/meta.json | 21 + .../render_silhouette_regmarks.inx | 21 + .../render_silhouette_regmarks.py | 101 ++ .../fablabchemnitz/scale_to_size/meta.json | 6 +- .../snap_object_points/meta.json | 4 +- .../stroke_color_as_fill/meta.json | 4 +- .../table_support/base_transform.py | 266 +++ .../fablabchemnitz/table_support/meta.json | 21 + .../fablabchemnitz/table_support/table.py | 850 ++++++++++ .../table_support/table_add_columns.inx | 23 + .../table_support/table_add_columns.py | 34 + .../table_support/table_add_guides.inx | 18 + .../table_support/table_add_guides.py | 34 + .../table_support/table_add_rows.inx | 23 + .../table_support/table_add_rows.py | 34 + .../table_support/table_create.inx | 28 + .../table_support/table_create.py | 38 + .../table_support/table_edit_columns.inx | 20 + .../table_support/table_edit_columns.py | 34 + .../table_support/table_edit_rows.inx | 20 + .../table_support/table_edit_rows.py | 34 + .../table_support/table_edit_table.inx | 21 + .../table_support/table_edit_table.py | 35 + .../table_support/table_edit_text.inx | 19 + .../table_support/table_edit_text.py | 34 + .../table_support/table_fit_height.inx | 18 + .../table_support/table_fit_height.py | 32 + .../table_support/table_fit_page.inx | 18 + .../table_support/table_fit_page.py | 31 + .../table_support/table_fit_width.inx | 18 + .../table_support/table_fit_width.py | 32 + .../table_support/table_merge_merge.inx | 18 + .../table_support/table_merge_merge.py | 32 + .../table_support/table_merge_split.inx | 18 + .../table_support/table_merge_split.py | 32 + .../table_support/table_remove_columns.inx | 18 + .../table_support/table_remove_columns.py | 32 + .../table_support/table_remove_rows.inx | 18 + .../table_support/table_remove_rows.py | 32 + 78 files changed, 8084 insertions(+), 26 deletions(-) create mode 100644 extensions/fablabchemnitz/filter_to_layer/filter_to_layer.inx create mode 100644 extensions/fablabchemnitz/filter_to_layer/filter_to_layer.py create mode 100644 extensions/fablabchemnitz/filter_to_layer/meta.json create mode 100644 extensions/fablabchemnitz/gpx_import/gpx_import.inx create mode 100644 extensions/fablabchemnitz/gpx_import/gpx_import.py create mode 100644 extensions/fablabchemnitz/gpx_import/meta.json create mode 100644 extensions/fablabchemnitz/gradient_saver/GUI.glade create mode 100644 extensions/fablabchemnitz/gradient_saver/gradient_saver.inx create mode 100644 extensions/fablabchemnitz/gradient_saver/gradient_saver.py create mode 100644 extensions/fablabchemnitz/gradient_saver/icon.svg create mode 100644 extensions/fablabchemnitz/gradient_saver/meta.json create mode 100644 extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx create mode 100644 extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py create mode 100644 extensions/fablabchemnitz/grey_to_monoalpha/meta.json create mode 100644 extensions/fablabchemnitz/my-gradients.svg create mode 100644 extensions/fablabchemnitz/output_pro/meta.json create mode 100644 extensions/fablabchemnitz/output_pro/output_pro.inx create mode 100644 extensions/fablabchemnitz/output_pro/output_pro.py create mode 100644 extensions/fablabchemnitz/output_pro/outputpro/__init__.py create mode 100644 extensions/fablabchemnitz/output_pro/outputpro/alpha.png create mode 100644 extensions/fablabchemnitz/output_pro/outputpro/cmyk.py create mode 100644 extensions/fablabchemnitz/output_pro/outputpro/cutmarks.py create mode 100644 extensions/fablabchemnitz/output_pro/outputpro/preview_mask.png create mode 100644 extensions/fablabchemnitz/output_pro/outputpro/top.png create mode 100644 extensions/fablabchemnitz/output_pro/result-imp.jpeg create mode 100644 extensions/fablabchemnitz/paths_to_openscad/meta.json create mode 100644 extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.inx create mode 100644 extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.py create mode 100644 extensions/fablabchemnitz/printing_marks_dotted/meta.json create mode 100644 extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.inx create mode 100644 extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.py create mode 100644 extensions/fablabchemnitz/render_silhouette_regmarks/meta.json create mode 100644 extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.inx create mode 100644 extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.py create mode 100644 extensions/fablabchemnitz/table_support/base_transform.py create mode 100644 extensions/fablabchemnitz/table_support/meta.json create mode 100644 extensions/fablabchemnitz/table_support/table.py create mode 100644 extensions/fablabchemnitz/table_support/table_add_columns.inx create mode 100755 extensions/fablabchemnitz/table_support/table_add_columns.py create mode 100644 extensions/fablabchemnitz/table_support/table_add_guides.inx create mode 100755 extensions/fablabchemnitz/table_support/table_add_guides.py create mode 100644 extensions/fablabchemnitz/table_support/table_add_rows.inx create mode 100755 extensions/fablabchemnitz/table_support/table_add_rows.py create mode 100644 extensions/fablabchemnitz/table_support/table_create.inx create mode 100755 extensions/fablabchemnitz/table_support/table_create.py create mode 100644 extensions/fablabchemnitz/table_support/table_edit_columns.inx create mode 100755 extensions/fablabchemnitz/table_support/table_edit_columns.py create mode 100644 extensions/fablabchemnitz/table_support/table_edit_rows.inx create mode 100755 extensions/fablabchemnitz/table_support/table_edit_rows.py create mode 100644 extensions/fablabchemnitz/table_support/table_edit_table.inx create mode 100755 extensions/fablabchemnitz/table_support/table_edit_table.py create mode 100644 extensions/fablabchemnitz/table_support/table_edit_text.inx create mode 100755 extensions/fablabchemnitz/table_support/table_edit_text.py create mode 100644 extensions/fablabchemnitz/table_support/table_fit_height.inx create mode 100644 extensions/fablabchemnitz/table_support/table_fit_height.py create mode 100644 extensions/fablabchemnitz/table_support/table_fit_page.inx create mode 100644 extensions/fablabchemnitz/table_support/table_fit_page.py create mode 100644 extensions/fablabchemnitz/table_support/table_fit_width.inx create mode 100644 extensions/fablabchemnitz/table_support/table_fit_width.py create mode 100644 extensions/fablabchemnitz/table_support/table_merge_merge.inx create mode 100644 extensions/fablabchemnitz/table_support/table_merge_merge.py create mode 100644 extensions/fablabchemnitz/table_support/table_merge_split.inx create mode 100755 extensions/fablabchemnitz/table_support/table_merge_split.py create mode 100644 extensions/fablabchemnitz/table_support/table_remove_columns.inx create mode 100755 extensions/fablabchemnitz/table_support/table_remove_columns.py create mode 100644 extensions/fablabchemnitz/table_support/table_remove_rows.inx create mode 100755 extensions/fablabchemnitz/table_support/table_remove_rows.py diff --git a/extensions/fablabchemnitz/convert_vertical_horizontal_to_line/meta.json b/extensions/fablabchemnitz/convert_vertical_horizontal_to_line/meta.json index f7e2d23..ca40d1a 100644 --- a/extensions/fablabchemnitz/convert_vertical_horizontal_to_line/meta.json +++ b/extensions/fablabchemnitz/convert_vertical_horizontal_to_line/meta.json @@ -7,14 +7,14 @@ "original_name": "Convert Vertical/Horizontal To Line", "original_id": "fablabchemnitz.de.convert_vertical_horizontal_to_line", "license": "GNU GPL v3", - "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE", "comment": "Written by Mario Voigt", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/convert_vertical_horizontal_to_line", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/convert_vertical_horizontal_to_line", "fork_url": null, "documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=79626259", "inkscape_gallery_url": null, "main_authors": [ - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/dots_to_path_points/meta.json b/extensions/fablabchemnitz/dots_to_path_points/meta.json index bf945e1..9726991 100644 --- a/extensions/fablabchemnitz/dots_to_path_points/meta.json +++ b/extensions/fablabchemnitz/dots_to_path_points/meta.json @@ -9,13 +9,13 @@ "license": "GNU GPL v3", "license_url": "https://github.com/chrille69/Inkscape-Extension-pathpoints2dots/blob/master/LICENSE", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/dots_to_path_points", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/dots_to_path_points", "fork_url": "https://github.com/chrille69/Inkscape-Extension-pathpoints2dots", "documentation_url": "https://stadtfabrikanten.org/display/IFM/Dots+To+Path+Points", "inkscape_gallery_url": null, "main_authors": [ "github.com/chrille69", - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/edit_attributes/meta.json b/extensions/fablabchemnitz/edit_attributes/meta.json index a2f52ae..29fa2e5 100644 --- a/extensions/fablabchemnitz/edit_attributes/meta.json +++ b/extensions/fablabchemnitz/edit_attributes/meta.json @@ -9,13 +9,13 @@ "license": "GNU LGPL v2", "license_url": "https://inkscape.org/~MatheM/%E2%98%85simple-attribute-editor+1", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/edit_attributes", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/edit_attributes", "fork_url": "https://inkscape.org/~MatheM/%E2%98%85simple-attribute-editor+1", "documentation_url": "https://stadtfabrikanten.org/display/IFM/Edit+Attributes", "inkscape_gallery_url": null, "main_authors": [ "inkscape.org/MatheM", - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/filter_by_length_area/meta.json b/extensions/fablabchemnitz/filter_by_length_area/meta.json index f93f4d8..a48223e 100644 --- a/extensions/fablabchemnitz/filter_by_length_area/meta.json +++ b/extensions/fablabchemnitz/filter_by_length_area/meta.json @@ -10,14 +10,14 @@ "original_name": "Filter By Length/Area", "original_id": "com.filter_by_length_area", "license": "GNU GPL v3", - "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/filter_by_length_area", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/filter_by_length_area", "fork_url": null, "documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=74645969", "inkscape_gallery_url": null, "main_authors": [ - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/filter_to_layer/filter_to_layer.inx b/extensions/fablabchemnitz/filter_to_layer/filter_to_layer.inx new file mode 100644 index 0000000..96cab34 --- /dev/null +++ b/extensions/fablabchemnitz/filter_to_layer/filter_to_layer.inx @@ -0,0 +1,21 @@ + + + Filter To Layer + fablabchemnitz.de.filter_to_layer + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/filter_to_layer/filter_to_layer.py b/extensions/fablabchemnitz/filter_to_layer/filter_to_layer.py new file mode 100644 index 0000000..05b5269 --- /dev/null +++ b/extensions/fablabchemnitz/filter_to_layer/filter_to_layer.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +''' +This extension adds filters to current layer or removes filters from current layer + +Copyright (C) 2012 Jabiertxo Arraiza, jabier.arraiza@marker.es + +Version 0.3 + +TODO: +Comment Better!!! + +CHANGE LOG +0.1 Start 30/07/2012 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +import inkex +import sys +import re + +class FilterToLayer(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument('--type', default = 'Add', help = 'Add or remove filters to current layer') + + def selectTop(self): + selectedSorted = None + if self.svg.selected is not None: + for element in self.document.getroot().iter(): + if element.get("id") in self.svg.selection: + selectedSorted = element + return selectedSorted + + def effect(self): + svg = self.document.getroot() + typeOperation = self.options.type + xpathStr = '//sodipodi:namedview' + namedview = svg.xpath(xpathStr, namespaces=inkex.NSS) + idLayer = namedview[0].get('{http://www.inkscape.org/namespaces/inkscape}current-layer'); + xpathStr = '//svg:g[@id="'+idLayer+'"]' + layer = svg.xpath(xpathStr, namespaces=inkex.NSS) + + if typeOperation == "Add": #Add action + element = self.selectTop() + if element is not None: + if element.get('style'): + matchObj = re.search( r'filter:url\(#.*?[^\)]\)', element.get('style'), re.M|re.I) + if matchObj: + filter = matchObj.group() + element.set('style',element.get('style').replace(filter,"").replace(";;",";")) + if layer[0].get('style'): + matchObj = re.search( r'filter:url\(#.*?[^\)]\)', layer[0].get('style'), re.M|re.I) + if matchObj: + element.set('style',element.get('style').replace(matchObj.group(),"").replace(";;",";")) + style = layer[0].get('style')+ ";" + filter; + layer[0].set('style',style.replace(";;",";")) + else: + layer[0].set('style',filter) + else: + inkex.utils.debug("Nothing selected") + + else: #Remove action + if layer[0].get('style'): + matchObj = re.search( r'filter:url\(#.*?[^\)]\)', layer[0].get('style'), re.M|re.I) + if matchObj: + layer[0].set('style',layer[0].get('style').replace(matchObj.group(),"").replace(";;",";")) + +if __name__ == '__main__': + FilterToLayer().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/filter_to_layer/meta.json b/extensions/fablabchemnitz/filter_to_layer/meta.json new file mode 100644 index 0000000..9b46c7a --- /dev/null +++ b/extensions/fablabchemnitz/filter_to_layer/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Filter To Layer", + "id": "fablabchemnitz.de.filter_to_layer", + "path": "filter_to_layer", + "dependent_extensions": null, + "original_name": "Filter to layer", + "original_id": "org.inkscape.filter.layer", + "license": "GNU GPL v2", + "license_url": "https://inkscape.org/~jabiertxof/%E2%98%85filter-layer", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/filter_to_layer", + "fork_url": "https://inkscape.org/~jabiertxof/%E2%98%85filter-layer", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Filter+To+Layer", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/jabiertxof", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/gpx_import/gpx_import.inx b/extensions/fablabchemnitz/gpx_import/gpx_import.inx new file mode 100644 index 0000000..b6bfd44 --- /dev/null +++ b/extensions/fablabchemnitz/gpx_import/gpx_import.inx @@ -0,0 +1,28 @@ + + + GPX Import + fablabchemnitz.de.gpx_import + + + 3000 + true + true + true + + + + + + + + + + .gpx + application/gpx+xml + GPS eXchange Format (*.gpx) + Import GPX Format + + + diff --git a/extensions/fablabchemnitz/gpx_import/gpx_import.py b/extensions/fablabchemnitz/gpx_import/gpx_import.py new file mode 100644 index 0000000..51f0f96 --- /dev/null +++ b/extensions/fablabchemnitz/gpx_import/gpx_import.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2012-2018 Tobias Leupold +# +# gpx2svg - Convert GPX formatted geodata to Scalable Vector Graphics (SVG) +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the Free +# Software Foundation in version 2 of the License. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +# +# modified by monomono@disroot.org at 2019-08-05 for use on inkscape extension, +# 3to2 python converter and adjust parser_options for inkscape compatibility. + +from __future__ import division +from __future__ import absolute_import +from io import open +__version__ = u'@VERSION@' + +import argparse +import sys +import math +from xml.dom.minidom import parse as parseXml +from os.path import abspath + +def parseGpx(gpxFile): + u"""Get the latitude and longitude data of all track segments in a GPX file""" + + if gpxFile == u'/dev/stdin': + gpxFile = sys.stdin + + # Get the XML information + try: + gpx = parseXml(gpxFile) + except IOError as error: + print (sys.stderr, u'Error while reading file: {}. Terminating.'.format(error)) + sys.exit(1) + except: + print (sys.stderr, u'Error while parsing XML data:') + print (sys.stderr, sys.exc_info()) + print (sys.stderr, u'Terminating.') + sys.exit(1) + + # Iterate over all tracks, track segments and points + gpsData = [] + for track in gpx.getElementsByTagName(u'trk'): + for trackseg in track.getElementsByTagName(u'trkseg'): + trackSegData = [] + for point in trackseg.getElementsByTagName(u'trkpt'): + trackSegData.append( + (float(point.attributes[u'lon'].value), float(point.attributes[u'lat'].value)) + ) + # Leave out empty segments + if(trackSegData != []): + gpsData.append(trackSegData) + + return gpsData + +def calcProjection(gpsData): + u"""Calculate a plane projection for a GPS dataset""" + + projectedData = [] + for segment in gpsData: + projectedSegment = [] + for coord in segment: + # At the moment, we only have the Mercator projection + projectedSegment.append(mercatorProjection(coord)) + projectedData.append(projectedSegment) + + return projectedData + +def mercatorProjection(coord): + u"""Calculate the Mercator projection of a coordinate pair""" + + # Assuming we're on earth, we have (according to GRS 80): + r = 6378137.0 + + # As long as meridian = 0 and can't be changed, we don't need: + # meridian = meridian * math.pi / 180.0 + # x = r * ((coord[0] * math.pi / 180.0) - meridian) + + # Instead, we use this simplified version: + x = r * coord[0] * math.pi / 180.0 + y = r * math.log(math.tan((math.pi / 4.0) + ((coord[1] * math.pi / 180.0) / 2.0))) + return x, y + +def moveProjectedData(gpsData): + u"""Move a dataset to 0,0 and return it with the resulting width and height""" + + # Find the minimum and maximum x and y coordinates + minX = maxX = gpsData[0][0][0] + minY = maxY = gpsData[0][0][1] + for segment in gpsData: + for coord in segment: + if coord[0] < minX: + minX = coord[0] + if coord[0] > maxX: + maxX = coord[0] + if coord[1] < minY: + minY = coord[1] + if coord[1] > maxY: + maxY = coord[1] + + # Move the GPS data to 0,0 + movedGpsData = [] + for segment in gpsData: + movedSegment = [] + for coord in segment: + movedSegment.append((coord[0] - minX, coord[1] - minY)) + movedGpsData.append(movedSegment) + + # Return the moved data and it's width and height + return movedGpsData, maxX - minX, maxY - minY + +def searchCircularSegments(gpsData): + u"""Splits a GPS dataset to tracks that are circular and other tracks""" + + circularSegments = [] + straightSegments = [] + + for segment in gpsData: + if segment[0] == segment[len(segment) - 1]: + circularSegments.append(segment) + else: + straightSegments.append(segment) + + return circularSegments, straightSegments + +def combineSegmentPairs(gpsData): + u"""Combine segment pairs to one bigger segment""" + + combinedData = [] + + # Walk through the GPS data and search for segment pairs + # that end with the starting point of another track + while len(gpsData) > 0: + # Get one segment from the source GPS data + firstTrackData = gpsData.pop() + foundMatch = False + + # Try to find a matching segment + for i in xrange(len(gpsData)): + if firstTrackData[len(firstTrackData) - 1] == gpsData[i][0]: + # There is a matching segment, so break here + foundMatch = True + break + + if foundMatch == True: + # We found a pair of segments with one shared point, so pop the data of the second + # segment from the source GPS data and create a new segment containing all data, but + # without the overlapping point + firstTrackData.pop() + combinedData.append(firstTrackData + gpsData[i]) + gpsData.pop(i) + else: + # No segment with a shared point was found, so just append the data to the output + combinedData.append(firstTrackData) + + return searchCircularSegments(combinedData) + +def combineSegments(gpsData): + u"""Combine all segments of a GPS dataset that can be combined""" + + # Search for circular segments. We can't combine them with any other segment. + circularSegments, remainingSegments = searchCircularSegments(gpsData) + + # Search for segments that can be combined + while True: + # Look how many tracks we have now + segmentsBefore = len(remainingSegments) + + # Search for segments that can be combined + newCircularSegments, remainingSegments = combineSegmentPairs(remainingSegments) + + # Add newly found circular segments to processedSegments -- they can't be used anymore + circularSegments = circularSegments + newCircularSegments + + if segmentsBefore == len(remainingSegments): + # combineSegmentPairs() did not reduce the number of tracks anymore, + # so we can't combine more tracks and can stop here + break + + return circularSegments + remainingSegments + +def chronologyJoinSegments(gpsData): + u"""Join all segments to a big one in the order defined by the GPX file.""" + joinedSegment = [] + for segment in gpsData: + joinedSegment += segment + return [joinedSegment] + +def scaleCoords(coord, height, scale): + u"""Return a scaled pair of coordinates""" + return coord[0] * scale, (coord[1] * -1 + height) * scale + +def generateScaledSegment(segment, height, scale): + u"""Create the coordinate part of an SVG path string from a GPS data segment""" + for coord in segment: + yield scaleCoords(coord, height, scale) + +def writeSvgData(gpsData, width, height, maxPixels, dropSinglePoints, outfile): + u"""Output the SVG data -- quick 'n' dirty, without messing around with dom stuff ;-)""" + + # Calculate the scale factor we need to fit the requested maximal output size + if width <= maxPixels and height <= maxPixels: + scale = 1 + elif width > height: + scale = maxPixels / width + else: + scale = maxPixels / height + + # Open the requested output file or map to /dev/stdout + if outfile != u'/dev/stdout': + try: + fp = open(outfile, u'w') + except IOError as error: + print (sys.stderr, u"Can't open output file: {}. Terminating.".format(error)) + sys.exit(1) + else: + fp = sys.stdout + + # Header data + fp.write( u'\n') + fp.write((u'\n')) + fp.write( u'\n'.format(__version__)) + fp.write((u'\n').format(width * scale, height * scale)) + + # Process all track segments and generate ids and path drawing commands for them + + # First, we split the data to circular and straight segments + circularSegments, straightSegments = searchCircularSegments(gpsData) + realCircularSegments = [] + singlePoints = [] + for segment in circularSegments: + # We can leave out the last point, because it's equal to the first one + segment.pop() + if len(segment) == 1: + # It's a single point + if dropSinglePoints == False: + # We want to keep single points, so add it to singlePoints + singlePoints.append(segment) + else: + realCircularSegments.append(segment) + + circularSegments = realCircularSegments + + # Draw single points if requested + if len(singlePoints) > 0: + fp.write(u'\n') + for segment in singlePoints: + x, y = scaleCoords(segment[0], height, scale) + fp.write( + u'\n'.format(x, y) + ) + fp.write(u'\n') + + # Draw all circular segments + if len(circularSegments) > 0: + fp.write(u'\n') + for segment in circularSegments: + fp.write(u'\n') + fp.write(u'\n') + + # Draw all un-closed paths + if len(straightSegments) > 0: + fp.write(u'\n') + for segment in straightSegments: + fp.write(u'\n') + fp.write(u'\n') + + # Close the XML + fp.write(u'\n') + + # Close the file if necessary + if fp != sys.stdout: + fp.close() + +def main(): + # Setup the command line argument parser + cmdArgParser = argparse.ArgumentParser( + description = u'Convert GPX formatted geodata to Scalable Vector Graphics (SVG)', + epilog = u'gpx2svg {} - http://nasauber.de/opensource/gpx2svg/'.format(__version__) + ) + cmdArgParser.add_argument( + u'i', metavar = u'FILE', nargs = u'?', default = u'/dev/stdin', + help = u'GPX input file (default: read from STDIN)' + ) + cmdArgParser.add_argument( + u'--o', metavar = u'FILE', nargs = u'?', default = u'/dev/stdout', + help = u'SVG output file (default: write to STDOUT)' + ) + cmdArgParser.add_argument( + u'--m', metavar = u'PIXELS', nargs = u'?', type = int, default = 3000, + help = u'Maximum width or height of the SVG output in pixels (default: 3000)' + ) + cmdArgParser.add_argument( + u'--d', + help = u'Drop single points (default: draw a circle with 1px diameter)' + ) + cmdArgParser.add_argument( + u'--r', + help = (u'"Raw" conversion: Create one SVG path per track segment, don\'t try to combine ' + u'paths that end with the starting point of another path') + ) + cmdArgParser.add_argument( + u'--j', + help = (u'Join all segments to a big one in the order of the GPX file. This can create an ' + u'un-scattered path if the default combining algorithm does not work because there ' + u'are no matching points across segments (implies -r)') + ) + cmdArgParser.add_argument( + u'--tab', + help = (u'inkscape option') + ) + + + # Get the given arguments + cmdArgs = cmdArgParser.parse_args() + + # Map "-" to STDIN or STDOUT + if cmdArgs.i == u'-': + cmdArgs.i = u'/dev/stdin' + if cmdArgs.o == u'-': + cmdArgs.o = u'/dev/stdout' + + # Check if a given input or output file name is a relative representation of STDIN or STDOUT + if cmdArgs.i != u'/dev/stdin': + if abspath(cmdArgs.i) == u'/dev/stdin': + cmdArgs.i = u'/dev/stdin' + if cmdArgs.o != u'/dev/stdout': + if abspath(cmdArgs.o) == u'/dev/stdout': + cmdArgs.o = u'/dev/stdout' + + # Get the latitude and longitude data from the given GPX file or STDIN + gpsData = parseGpx(cmdArgs.i) + + # Check if we actually _have_ data + if gpsData == []: + print (sys.stderr, u'No data to convert. Terminating.') + sys.exit(1) + + # Join all segments if requested by "-j" + if bool(cmdArgs.j): + gpsData = chronologyJoinSegments(gpsData) + + # Try to combine all track segments that can be combined if not requested otherwise + # Don't execute if all segments are already joined with "-j" + if not bool(cmdArgs.r) and not bool(cmdArgs.j): + gpsData = combineSegments(gpsData) + + # Calculate a plane projection for a GPS dataset + # At the moment, we only have the Mercator projection + gpsData = calcProjection(gpsData) + + # Move the projected data to the 0,0 origin of a cartesial coordinate system + # and get the raw width and height of the resulting vector data + gpsData, width, height = moveProjectedData(gpsData) + + # Write the resulting SVG data to the requested output file or STDOUT + writeSvgData(gpsData, width, height, cmdArgs.m, bool(cmdArgs.d), cmdArgs.o) + +if __name__ == u'__main__': + main() diff --git a/extensions/fablabchemnitz/gpx_import/meta.json b/extensions/fablabchemnitz/gpx_import/meta.json new file mode 100644 index 0000000..0d42f3a --- /dev/null +++ b/extensions/fablabchemnitz/gpx_import/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "GPX Import", + "id": "fablabchemnitz.de.gpx_import", + "path": "gpx_import", + "dependent_extensions": null, + "original_name": "GPX Import", + "original_id": "mono.inkscape.pgx_input", + "license": "GNU GPL v2", + "license_url": "https://inkscape.org/de/~mono/%E2%98%85inkgpx2svg", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/gpx_import", + "fork_url": "https://git.disroot.org/monomono/inkgpx2svg", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/GPX+Import", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/mono", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/gradient_saver/GUI.glade b/extensions/fablabchemnitz/gradient_saver/GUI.glade new file mode 100644 index 0000000..31f26b0 --- /dev/null +++ b/extensions/fablabchemnitz/gradient_saver/GUI.glade @@ -0,0 +1,667 @@ + + + + + + False + Gradient Saver + False + center + 600 + 400 + icon.svg + north + + + + + + + True + True + + + + True + False + 400 + 250 + + + 600 + 180 + True + True + + + True + False + none + + + True + False + 30 + 10 + 2 + vertical + 20 + + + True + False + 10 + 18 + + + 150 + 42 + True + True + False + + + False + True + 0 + + + + + 325 + 42 + True + True + 25 + e.g Beautiful Color + + + False + True + 1 + + + + + False + False + 1 + + + + + + + + + 130 + + + + + 580 + 32 + True + False + 20 + 10 + end + + + Cancel + True + True + True + + + + True + True + 0 + + + + + Save + True + True + True + + + + True + True + 1 + + + + + 318 + + + + + 600 + 100 + True + False + 10 + top + + + 55 + True + False + 30 + 21 + gtk-info + 6 + + + False + True + 0 + + + + + 250 + True + False + 30 + 14 + <b>Hint:</b> +Select at least one object that use gradient color in order to add those gradient to your library. Selected gradient object will appear below. You can give a name for your gradients to help you recognize it later. + True + True + + + False + True + 1 + + + + + + + 550 + 20 + True + False + 45 + 50 + 10 + 100 + + + True + False + <b>Preview</b> + True + + + False + True + 0 + + + + + True + False + <b>Gradient Name</b> + True + + + False + True + 1 + + + + + 100 + + + + + + + True + False + Add New + + + False + + + + + True + False + + + 580 + 32 + True + False + 20 + 6 + bottom + end + + + Remove + True + True + True + True + + + + True + True + 0 + + + + + Load + True + True + True + True + + + + True + True + 1 + + + + + 318 + + + + + 600 + 100 + True + False + 10 + top + + + 55 + True + False + 30 + 21 + gtk-info + 6 + + + False + True + 0 + + + + + 250 + True + False + 30 + 14 + <b>Hint:</b> +Select at least one object that use gradient color in order to add those gradient to your library. Selected gradient object will appear below. You can give a name for your gradients to help you recognize it later. + True + True + + + False + True + 1 + + + + + + + 550 + 20 + True + False + 45 + 50 + 10 + 100 + + + True + False + <b>Your Current Gradient</b> + True + + + False + True + 0 + + + + + 100 + + + + + 600 + 180 + True + True + + + True + False + none + + + True + False + start + 10 + 10 + 30 + 10 + 2 + 2 + none + False + + + 60 + 42 + True + True + start + start + + + True + False + 5 + top + + + 42 + True + True + False + True + + + False + True + 0 + + + + + 100 + 42 + True + True + False + + + False + True + 1 + + + + + 42 + True + False + ijo trans + True + char + 25 + + + False + True + 2 + + + + + + + + + + + + + 130 + + + + + 1 + + + + + True + False + Load or Remove + + + 1 + False + + + + + True + False + + + 317 + 300 + True + False + <span font="12"><b>Version $VERSION</b></span> + True + True + + + 137 + -84 + + + + + 100 + 80 + True + False + icon.svg + + + 10 + 32 + + + + + 100 + 80 + True + False + <span font="12"><b>Gradient Saver</b></span> + True + + + 69 + 225 + + + + + 307 + 80 + True + False + <span>Gradient Saver is an extension for Inkscape that will help you to organize your favorite gradients. This extension created with love by <a href="#">Sofyan</a> &amp; <a href="https://raniaamina.id">Rania Amina</a> and fully supported by Gimpscape ID Community. + +Project Repository: +<a href="#"><b>https://github.com/artemtech/inkscape-gradient-saver</b></a></span> + True + True + + + 243 + 82 + + + + + 580 + 32 + True + False + 20 + 10 + end + + + Close + True + True + True + + + + True + True + 1 + + + + + 318 + + + + + 2 + + + + + True + False + About + + + 2 + False + + + + + + + False + Information + dialog-information + dialog + window + + + + + + False + 10 + 10 + 10 + 10 + vertical + 2 + + + False + 10 + 10 + center + + + Close + True + True + True + + + + True + True + 0 + + + + + False + False + 0 + + + + + True + False + + + 55 + True + False + gtk-info + 6 + + + False + True + 0 + + + + + True + False + label + + + False + True + 1 + + + + + False + True + 1 + + + + + + diff --git a/extensions/fablabchemnitz/gradient_saver/gradient_saver.inx b/extensions/fablabchemnitz/gradient_saver/gradient_saver.inx new file mode 100644 index 0000000..320813a --- /dev/null +++ b/extensions/fablabchemnitz/gradient_saver/gradient_saver.inx @@ -0,0 +1,16 @@ + + + Gradient Saver + fablabchemnitz.de.gradient_saver + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/gradient_saver/gradient_saver.py b/extensions/fablabchemnitz/gradient_saver/gradient_saver.py new file mode 100644 index 0000000..08589d5 --- /dev/null +++ b/extensions/fablabchemnitz/gradient_saver/gradient_saver.py @@ -0,0 +1,386 @@ +#! /usr/bin/env python3 + +# OS modules +import os +import sys + +import gi +gi.require_version("Gtk", "3.0") +from gi.repository import Gtk + +# inkscape extension files +import cairo +from lxml import etree +import inkex # required +from inkex.elements import NSS +from inkex.utils import errormsg as show_errormsg +from inkex.styles import Style +from inkex.colors import Color + +__version__ = '1.0.0' + +saved_gradient_path = "../my-gradients.svg" + +def create_new_file(gradient_data): + root = etree.Element("svg", nsmap=NSS) + def_tree = etree.SubElement(root, "defs") + for i, item in enumerate(gradient_data): + gradient = etree.SubElement(def_tree, item.tag, attrib=item.attrib) + for j, gradient_stop in enumerate(item): + etree.SubElement(gradient, gradient_stop.tag, attrib=gradient_stop.attrib, id="stop%d%d" % (i, j)) + with open(saved_gradient_path, "w") as f: + f.write(etree.tostring( + root, encoding="utf-8", xml_declaration=True, pretty_print=True).decode("utf-8")) + +def save_to_file(data): + """ Wrapper for saving gradients to file. """ + if len(data) == 0: + return 1 + else: + try: + # read previous data then append it with current data + if os.path.exists(saved_gradient_path): + previous_data = load_gradients_from_file() + data = previous_data + data + create_new_file(data) + return 0 + except Exception as e: + import traceback + show_errormsg(e) + show_errormsg(traceback.print_exc()) + return -1 + +def load_gradients_from_file(): + """ Load gradients from saved gradient, returned as List """ + if os.path.exists(saved_gradient_path) and os.stat(saved_gradient_path).st_size != 0: + root = etree.parse(saved_gradient_path) + mygradients = root.xpath("//linearGradient", namespaces=NSS) + else: + mygradients = [] + return mygradients + +def read_stop_gradient(gradient): + stop_data = { + "id": gradient.attrib.get("id"), + "stops": [] + } + for stop in gradient: + offset = stop.attrib.get("offset") + style = Style(Style.parse_str(stop.attrib['style'])) + color = Color.parse_str(style.get("stop-color"))[1] + opacity = style.get("stop-opacity") + stop_data.get("stops").append( + tuple([float(offset)] + [x/256.0 for x in color] + [float(opacity)])) + return stop_data + +class MainWindow(Gtk.Builder): + def __init__(self, gradientSaver): + Gtk.Builder.__init__(self) + self.gradientSaver = gradientSaver + self.add_from_file("GUI.glade") + self.window = self.get_object("window") + self.app_version = self.get_object("extension_version") + self.app_version.set_label(self.app_version.get_label().replace("$VERSION",__version__)) + self.information_dialog = self.get_object("information_dialog") + # parsing components + # save gradient components + self.save_container = self.get_object("save_gradients_container") + save_template = self.get_object("save_gradient1") + self.save_container.remove(save_template) + for idx, item in enumerate(self.gradientSaver.doc_selected_gradients): + new_save_template = self.SaveGradientTemplate(item) + new_save_template.set_name("gradient%d" % idx) + self.save_container.add(new_save_template) + self.save_container.show_all() + # - end save gradient components + # load gradient components + self.load_container = self.get_object("load_gradients_container") + load_template = self.get_object("load_gradient1") + self.load_container.remove(load_template) + # - end load gradient components + # show the GUI + self.connect_signals(self.Handler(self)) + self.window.show() + + class SaveGradientTemplate(Gtk.HBox): + """ + Template for generating gradient name + and preview of selected object in the save page. + """ + + def __init__(self, gradient_data): + Gtk.HBox.__init__(self) + self.gradient_data = gradient_data + self.set_spacing(20) + preview = Gtk.DrawingArea() + preview.set_size_request(150, 42) + preview.set_app_paintable(True) + preview.connect("draw", self.on_draw, gradient_data) + self.pack_start(preview, False, True, 0) + self.input_entry = Gtk.Entry() + self.input_entry.set_placeholder_text("e.g Beautiful Color") + self.input_entry.set_size_request(250, 42) + self.input_entry.set_text(gradient_data.get("id")) + self.input_entry.set_max_length(25) + self.pack_start(self.input_entry, False, True, 1) + + def on_draw(self, wid, cr, data): + """ + Calllback for draw signal for rendering gradient. + params: + - wid :GtkWidget + - cr :Cairo + - data :list -> gradient data + """ + lg = cairo.LinearGradient(0.0, 20.0, 150.0, 20.0) + for stop in data["stops"]: + lg.add_color_stop_rgba( + stop[0], stop[1], stop[2], stop[3], stop[4]) + cr.rectangle(10.0, 0.0, 150.0, 42.0) + cr.set_source(lg) + cr.fill() + + def get_save_gradient_text(self): + return self.input_entry.get_text() + + def get_compiled_gradient(self, new_id): + # compiling gradient stops + root = etree.Element("linearGradient", id=new_id) + for idx, stop in enumerate(self.gradient_data["stops"]): + stop_id = self.get_name() + str(idx) + offset = stop[0] + color = Color([stop[1], stop[2], stop[3]],"rgb") + opacity = stop[4] + tmp_stops = { + "id": stop_id, + "offset": str(offset), + "style": Style({ + "stop-color": str(color), + "stop-opacity": str(opacity) + }).to_str() + } + current_stop = etree.SubElement(root, "stop", attrib=tmp_stops) + return root + + class LoadGradientTemplate(Gtk.FlowBoxChild): + """ + Template for generating gradient name + and preview of saved gradient in the load page. + """ + def __init__(self, gradient_data): + Gtk.FlowBoxChild.__init__(self) + self.gradient_data = gradient_data + self.set_size_request(60,32) + self.set_halign(Gtk.Align.START) + self.set_valign(Gtk.Align.START) + container = Gtk.HBox() + container.set_spacing(5) + container.set_baseline_position(Gtk.BaselinePosition.TOP) + self.checkbox = Gtk.CheckButton() + self.checkbox.draw_indicator = True + container.pack_start(self.checkbox,False,True,0) + preview = Gtk.DrawingArea() + preview.set_size_request(100, 32) + preview.set_app_paintable(True) + preview.connect("draw", self.on_draw, gradient_data) + container.pack_start(preview,False,True,0) + self.text_gradient = Gtk.Label() + self.text_gradient.set_text(gradient_data.get("id")) + self.text_gradient.set_line_wrap(True) + self.text_gradient.set_line_wrap_mode(1) + self.text_gradient.set_max_width_chars(25) + container.pack_start(self.text_gradient,False,True,0) + self.add(container) + + def on_draw(self, wid, cr, data): + """ + Calllback for draw signal for rendering gradient. + params: + - wid :GtkWidget + - cr :Cairo + - data :list -> gradient data + """ + lg = cairo.LinearGradient(0.0, 20.0, 100.0, 20.0) + for stop in data["stops"]: + lg.add_color_stop_rgba( + stop[0], stop[1], stop[2], stop[3], stop[4]) + cr.rectangle(10.0, 0.0, 110.0, 32.0) + cr.set_source(lg) + cr.fill() + + class Handler: + """ Signal Handler for GUI """ + + def __init__(self, main_window): + self.main_window = main_window + + def onDestroy(self, *args): + Gtk.main_quit() + + def onSwitchPage(self, notebook, page, page_num): + if page_num == 0: # save tab + pass + elif page_num == 1: # load/remove tab + self.main_window.gradients_to_load = [] + for children in self.main_window.load_container.get_children(): + self.main_window.load_container.remove(children) + loaded_gradients = load_gradients_from_file() + # TODO render with disabled checkbox if it already exists in current project doc + for idx,gradient in enumerate(loaded_gradients): + # parse gradient stops + stop_data = read_stop_gradient(gradient) + gradient_info = self.main_window.LoadGradientTemplate(stop_data) + gradient_info.checkbox.connect("toggled",self.onLoadGradientToggled, gradient) + gradient_info.set_name("gradient%d" % idx) + self.main_window.load_container.add(gradient_info) + self.main_window.load_container.show_all() + else: + pass + + def onSaveGradientClicked(self, button): + text = "" + gradient_to_save = [] + # get all gradient data in save_container + for item in self.main_window.save_container.get_children(): + # get new gradient name + new_name_gradient = item.get_save_gradient_text() + # strip all special chars + if not new_name_gradient.isalnum(): + new_name_gradient = ''.join(e for e in new_name_gradient if e.isalnum()) + # get gradient data + gradient_data = item.get_compiled_gradient(new_name_gradient) + text += "{0}\n-----\n".format(etree.tostring(gradient_data)) + gradient_to_save.append(gradient_data) + # save to file + status = save_to_file(gradient_to_save) + if status == 0: + info = "%d gradients saved successfully!" % len(gradient_to_save) + # reload current document info with saved gradients + self.main_window.gradientSaver.reload_current_gradients(gradient_to_save) + elif status == 1: + info = "Nothing to save, there is no object with gradient selected. Exiting..." + elif status == -1: + info = "Internal Error (-1)! " + # showing popup information + self.main_window.get_object("information_text").set_text(info) + self.main_window.information_dialog.set_title("Save Gradient Information") + self.main_window.information_dialog.show_all() + + def onLoadGradientToggled(self, togglebutton, gradient): + # if active, queue gradient, otherwise pop it + if togglebutton.get_active(): + self.main_window.gradients_to_load.append(gradient) + else: + self.main_window.gradients_to_load.remove(gradient) + + def onLoadGradientClicked(self, button): + if len(self.main_window.gradients_to_load) > 0: + self.main_window.gradientSaver.insert_new_gradients_to_current_doc(self.main_window.gradients_to_load) + teks = "Successfully loading these gradients:\n" + teks += "".join(["- "+gradient.attrib["id"]+"\n" for gradient in self.main_window.gradients_to_load]) + else: + teks = "No gradient(s) selected to load. Exiting..." + self.main_window.get_object("information_text").set_text(teks) + self.main_window.information_dialog.set_title("Load Gradient Information") + self.main_window.information_dialog.show_all() + + def onRemoveGradientClicked(self, button): + loaded_gradients = load_gradients_from_file() + if len(self.main_window.gradients_to_load) > 0: + gradient_to_remove = [gradient.attrib["id"] for gradient in self.main_window.gradients_to_load] + new_gradient_after = [gradient for gradient in loaded_gradients if gradient.attrib["id"] not in gradient_to_remove] + create_new_file(new_gradient_after) + teks = "Successfully removing these gradients:\n" + teks += "".join(["- "+gradient+"\n" for gradient in gradient_to_remove]) + else: + teks = "No gradient(s) selected to load. Exiting..." + self.main_window.get_object("information_text").set_text(teks) + self.main_window.information_dialog.set_title("Remove Gradient Information") + self.main_window.information_dialog.show_all() + + +class GradientSaver(inkex.Effect): + def __init__(self): + " define how the options are mapped from the inx file " + inkex.Effect.__init__(self) # initialize the super class + try: + self.tty = open("/dev/tty", 'w') + except: + self.tty = open(os.devnull, 'w') + self.doc_selected_gradients = [] + + def insert_new_gradients_to_current_doc(self, gradients): + defs_node = self.svg.getElement("//svg:defs") + for item in gradients: + gradient = etree.SubElement(defs_node,item.tag,attrib=item.attrib) + for stop in item: + etree.SubElement(gradient,stop.tag,attrib=stop.attrib) + + def reload_current_gradients(self, new_data): + " reload gradients information in current project with stored gradient " + for idx,gradient in enumerate(self.doc_selected_gradients): + # set old gradient id to new id + real_node = self.svg.getElement("//*[@id='%s']" % gradient["id"]) + # remove inkscape collect first + real_node.attrib.pop("{"+NSS["inkscape"]+"}collect", None) + real_node.attrib["id"] = new_data[idx].attrib["id"] + # set old xlink:href to new id + node_href = self.svg.getElement("//*[@xlink:href='#%s']" % gradient["id"]) + node_href.attrib["{"+NSS["xlink"]+"}href"] = "#"+new_data[idx].attrib["id"] + # last set up inkscape collect again + real_node.attrib["{"+NSS["inkscape"]+"}collect"] = "always" + + def get_all_doc_gradients(self): + """TODO + retrieve all gradient sources of current project document + """ + pass + + def get_selected_gradients_data(self): + selected_objects = self.svg.selected + gradient_list = [] + if len(selected_objects) > 0: + for item in selected_objects.values(): + style = Style(Style.parse_str(item.get('style'))) + fill = stroke = "None" + if style.get("fill"): + fill = style.get("fill")[5:-1] if "url" in style.get("fill") else "None" + if style.get("stroke"): + stroke = style("stroke")[5:-1] if "url" in style.get("stroke") else "None" + if fill == "None" and stroke == "None": + continue + # read fill data + if "radialGradient" in fill or "linearGradient" in fill: + real_fill = self.svg.getElementById(fill).attrib["{"+NSS["xlink"]+"}href"][1:] + real_fill_node = self.svg.getElementById(real_fill) + if real_fill_node not in gradient_list: + gradient_list.append(real_fill_node) + # read stroke data + if "radialGradient" in stroke or "linearGradient" in stroke: + real_stroke = self.svg.getElementById(stroke).attrib["{"+NSS["xlink"]+"}href"][1:] + real_stroke_node = self.svg.getElementById(real_stroke) + if real_stroke_node not in gradient_list: + gradient_list.append(real_stroke_node) + data = [] + # read gradients data + for gradient in gradient_list: + # parse gradient stops + stop_data = read_stop_gradient(gradient) + inkex.utils.debug(gradient) + data.append(stop_data) + return data + + # called when the extension is running. + def effect(self): + self.doc_selected_gradients = self.get_selected_gradients_data() + try: + app = MainWindow(self) + Gtk.main() + except Exception as e: + import traceback + show_errormsg(e) + show_errormsg(traceback.print_exc()) + + +if __name__ == '__main__': + GradientSaver().run() diff --git a/extensions/fablabchemnitz/gradient_saver/icon.svg b/extensions/fablabchemnitz/gradient_saver/icon.svg new file mode 100644 index 0000000..1ef4270 --- /dev/null +++ b/extensions/fablabchemnitz/gradient_saver/icon.svg @@ -0,0 +1,678 @@ + + + + + Gradient Saver Icon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Gradient Saver Icon + + + Hadiid Pratama + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/gradient_saver/meta.json b/extensions/fablabchemnitz/gradient_saver/meta.json new file mode 100644 index 0000000..2d17495 --- /dev/null +++ b/extensions/fablabchemnitz/gradient_saver/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Gradient Saver", + "id": "fablabchemnitz.de.gradient_saver", + "path": "gradient_saver", + "dependent_extensions": null, + "original_name": "Gradient Saver", + "original_id": "id.artemtech.gradient_saver", + "license": "GNU GPL v3", + "license_url": "https://github.com/artemtech/inkscape-gradient-saver/blob/inkscape-1.0/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/gradient_saver", + "fork_url": "https://github.com/artemtech/inkscape-gradient-saver/", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Gradient+Saver", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/artemtech", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx new file mode 100644 index 0000000..50bf83a --- /dev/null +++ b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.inx @@ -0,0 +1,47 @@ + + + Grey to MonoAlpha + fablabchemnitz.de.grey_to_monoalpha + + + + + 0x000000ff + + + + + + + + + + + 0 + 1 + + + + + + + + + + + + path + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py new file mode 100644 index 0000000..1bbe1ae --- /dev/null +++ b/extensions/fablabchemnitz/grey_to_monoalpha/grey_to_monoalpha.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# +# Copyright (C) [2021] [Matt Cottam], [mpcottam@raincloud.co.uk] +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +############################################################################## +# Grey To Mono Alpha *** Convert Greys to Monochrome with varying Opacity +############################################################################## + +import math + +import inkex +from inkex import Color + +# Python Standard Libary + +from statistics import mean + + +def get_attributes(self): + for att in dir(self): + inkex.errormsg((att, getattr(self, att))) + + +def rgba_to_bw_rgba(self, my_objects): + apply_to = self.options.apply_to_type_radio + mono_color = self.options.color_picker_mono.to_rgba() + opacity_lower_threshold = self.options.opacity_lower_threshold + opacity_upper_threshold = self.options.opacity_upper_threshold + opacity_range = opacity_upper_threshold - opacity_lower_threshold + + for my_object in my_objects: + + if 'fill' in apply_to and ('fill:none' not in str(my_object.style)): + my_fill_color = my_object.style.get_color(name='fill').to_rgba() + my_fill_color_red = my_fill_color[0] + my_fill_color_green = my_fill_color[1] + my_fill_color_blue = my_fill_color[2] + mean_fill_component_value = mean([my_fill_color_red, my_fill_color_blue, my_fill_color_green]) + + if mean_fill_component_value > 0: + mono_opacity = (1 - (mean_fill_component_value / 256)) * opacity_range + mono_opacity = mono_opacity + opacity_lower_threshold + else: + mono_opacity = opacity_upper_threshold + + my_object.style['fill'] = str(mono_color) + my_object.style['fill-opacity'] = str(mono_opacity) + + if 'stroke' in apply_to and (';stroke:none' not in str(my_object.style)) and ('stroke:' in str(my_object.style)): + my_stroke_color = my_object.style.get_color(name='stroke').to_rgba() + my_stroke_color_red = my_stroke_color[0] + my_stroke_color_green = my_stroke_color[1] + my_stroke_color_blue = my_stroke_color[2] + mean_stroke_component_value = mean([my_stroke_color_red, my_stroke_color_blue, my_stroke_color_green]) + + if mean_stroke_component_value > 0: + mono_opacity = (1 - (mean_stroke_component_value / 256)) * opacity_range + mono_opacity = mono_opacity + opacity_lower_threshold + else: + mono_opacity = opacity_upper_threshold + + my_object.style['stroke'] = str(mono_color) + my_object.style['stroke-opacity'] = str(mono_opacity) + + +class GreyToMonoAlpha(inkex.EffectExtension): + + def add_arguments(self, pars): + pars.add_argument("--tab") + pars.add_argument("--color_picker_mono", type=inkex.colors.Color, default=0) + pars.add_argument("--apply_to_type_radio", default=None) + pars.add_argument("--opacity_lower_threshold", type=float, default=0) + pars.add_argument("--opacity_upper_threshold", type=float, default=1) + + def effect(self): + my_objects = self.svg.selected + if len(my_objects) < 1: + self.msg('Please select some paths first.') + return + rgba_to_bw_rgba(self, my_objects) + + +if __name__ == '__main__': + GreyToMonoAlpha().run() diff --git a/extensions/fablabchemnitz/grey_to_monoalpha/meta.json b/extensions/fablabchemnitz/grey_to_monoalpha/meta.json new file mode 100644 index 0000000..cba48c9 --- /dev/null +++ b/extensions/fablabchemnitz/grey_to_monoalpha/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Grey To MonoAlpha", + "id": "fablabchemnitz.de.grey_to_monoalpha", + "path": "grey_to_monoalpha", + "dependent_extensions": null, + "original_name": "Grey to MonoAlpha", + "original_id": "org.inkscape.grey_to_monoalpha", + "license": "GNU GPL v3", + "license_url": "https://gitlab.com/inklinea/grey-to-mono-alpha/-/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/grey_to_monoalpha", + "fork_url": "https://gitlab.com/inklinea/grey-to-mono-alpha", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Grey+to+MonoAlpha", + "inkscape_gallery_url": null, + "main_authors": [ + "gitlab.com/inklinea", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/image_triangulation/meta.json b/extensions/fablabchemnitz/image_triangulation/meta.json index e6537e9..c367e09 100644 --- a/extensions/fablabchemnitz/image_triangulation/meta.json +++ b/extensions/fablabchemnitz/image_triangulation/meta.json @@ -9,13 +9,13 @@ "license": "GNU GPL v2", "license_url": "https://github.com/nicolaromano/triangulate/blob/master/triangulation.py", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/image_triangulation", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/image_triangulation", "fork_url": "https://github.com/nicolaromano/triangulate", "documentation_url": "https://stadtfabrikanten.org/display/IFM/Image+Triangulation", "inkscape_gallery_url": null, "main_authors": [ "github.com/nicolaromano", - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/inset_alignment/meta.json b/extensions/fablabchemnitz/inset_alignment/meta.json index 2aa3326..fa2c239 100644 --- a/extensions/fablabchemnitz/inset_alignment/meta.json +++ b/extensions/fablabchemnitz/inset_alignment/meta.json @@ -9,13 +9,13 @@ "license": "GNU GPL v2", "license_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/InsetAlignmentExtension/InsetAlignment.py", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/inset_alignment", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/inset_alignment", "fork_url": "https://sourceforge.net/p/razorfoss/svn/HEAD/tree/trunk/Inkscape/InsetAlignmentExtension/", "documentation_url": "https://stadtfabrikanten.org/display/IFM/Inset+Alignment", "inkscape_gallery_url": null, "main_authors": [ "Luke Phillips:lukerazor@hotmail.com", - "github.com/vmario89" + "github.com/eridur-de" ] } ] diff --git a/extensions/fablabchemnitz/my-gradients.svg b/extensions/fablabchemnitz/my-gradients.svg new file mode 100644 index 0000000..d49e971 --- /dev/null +++ b/extensions/fablabchemnitz/my-gradients.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/extensions/fablabchemnitz/output_pro/meta.json b/extensions/fablabchemnitz/output_pro/meta.json new file mode 100644 index 0000000..aa60977 --- /dev/null +++ b/extensions/fablabchemnitz/output_pro/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Inkscape Output Pro", + "id": "fablabchemnitz.de.output_pro", + "path": "output_pro", + "dependent_extensions": null, + "original_name": "Inkscape Output Pro", + "original_id": "org.inkscape.outputpro", + "license": "GNU GPL v2", + "license_url": "https://github.com/jonata/Inkscape-OUTPUT-PRO/blob/master/LICENSE.txt", + "comment": "ported to Inkscape v1 by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/output_pro", + "fork_url": "https://github.com/jonata/Inkscape-OUTPUT-PRO", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Output+Pro+for+Inkscape", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/jonata", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/output_pro/output_pro.inx b/extensions/fablabchemnitz/output_pro/output_pro.inx new file mode 100644 index 0000000..c0d7ce0 --- /dev/null +++ b/extensions/fablabchemnitz/output_pro/output_pro.inx @@ -0,0 +1,16 @@ + + + Inkscape Output Pro + fablabchemnitz.de.output_pro + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/output_pro/output_pro.py b/extensions/fablabchemnitz/output_pro/output_pro.py new file mode 100644 index 0000000..9c4c5b5 --- /dev/null +++ b/extensions/fablabchemnitz/output_pro/output_pro.py @@ -0,0 +1,1091 @@ +#!/usr/bin/env python3 + +import inkex, re, os, random, sys, subprocess, shutil +from inkex.command import inkscape + +from outputpro import cmyk, cutmarks + +from PyQt5 import QtGui, QtCore, uic, QtWidgets +from PyQt5.QtWidgets import QMainWindow, QApplication +from PyQt5.QtCore import * + +import gettext +_ = gettext.gettext + +import tempfile + +dirpathTempFolder = tempfile.TemporaryDirectory(suffix=str(random.randint(0,9)), prefix="output-") +dirpathSoftware = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'outputpro') +uuconv = { + "in": 96.0, + "pt": 1.33333333333, + "px": 1.0, + "mm": 3.77952755913, + "cm": 37.7952755913, + "pc": 16.0} + + +def unittouu(string): + '''Returns userunits given a string representation of units in another system''' + unit = re.compile('(%s)$' % '|'.join(uuconv.keys())) + param = re.compile(r'(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)') + + p = param.match(string) + u = unit.search(string) + if p: + retval = float(p.string[p.start():p.end()]) + else: + retval = 0.0 + if u: + try: + return retval * uuconv[u.string[u.start():u.end()]] + except KeyError: + pass + return retval + +class OutputPro(inkex.EffectExtension): + + def effect(self): + if "nt" in os.name: + icc_dir = 'C:\\Windows\\System32\\spool\\drivers\\color' + else: + icc_dir = '/usr/share/color/icc/colord/' + #might be also ~/.local/share/icc/ + + try: + with open(os.path.join(os.path.abspath(inkex.utils.get_user_directory() + "/../"), "preferences.xml"), 'r') as f: + inkscape_config = f.read() + + list_of_export_formats = ['JPEG'] + list_of_format_tips = {'JPEG':'The JPEG format always has some loss of quality. Although it supports CMYK, it is not recommended for use in printed graphics.'} + list_of_color_modes_jpeg = ['CMYK','RGB','Gray','CMY','HSB','HSL','HWB','Lab','Log', 'OHTA','Rec601Luma','Rec601YCbCr','Rec709Luma','Rec709YCbCr','sRGB','XYZ','YCbCr','YCC','YIQ','YPbPr','YUV'] + list_of_interlacing_jpeg = {u'None':'none', u'Line':'line', u'Plane':'plane', u'Partition':'partition'} + list_of_noise_jpeg = {u'Gaussian':'Gaussian-noise', u'Impulse':'Impulse-noise', u'Laplacian':'Laplacian-noise', u'Multiplicative':'Multiplicative-noise', u'Poisson':'Poisson-noise', u'Uniform':'Uniform-noise'} + list_of_subsampling_jpeg = ['1x1, 1x1, 1x1', '2x1, 1x1, 1x1', '1x2, 1x1, 1x1', '2x2, 1x1, 1x1'] + list_of_dct_jpeg = {u'Integer':'int', u'Integer (fast)':'fast', u'Floating point':'float'} + list_of_area_to_export = [_(u"Page"), _(u"Drawing"), _(u"Object")]#, _(u"Área definida")] + + if "nt" in os.name: + shell = True + else: + shell = False + + selected_screen_profile = inkscape_config.split('id="displayprofile"')[1].split('uri="')[1].split('" />')[0].split('/')[-1] + #if selected_screen_profile == '': + # inkex.utils.debug("Configured icc color profile (Inkscape) is not set. Configure it in preferences and restart Inkscape to apply changes.") + selected_print_profile = inkscape_config.split('id="softproof"')[1].split('uri="')[1].split('" />')[0].split('/')[-1] + + list_of_selected_objects = [] + for id, node in self.svg.selected.items(): + list_of_selected_objects.append(node.get('id')) + if len(list_of_selected_objects) >= 1: + selected_object = list_of_selected_objects[0] + else: + selected_object = 'layer1' + + resolution = '96' + + shutil.copy2(self.options.input_file, os.path.join(dirpathTempFolder.name, "original.svg")) # complete target filename given + + svg = self.document.getroot() + page_width = unittouu(svg.get('width')) + page_height = unittouu(svg.get('height')) + + class mainWindow(QtWidgets.QWidget): + + def __init__(self, parent=None): + QtWidgets.QWidget.__init__(self, parent) + self.resize(950, 600) + self.setMaximumSize(QtCore.QSize(950, 600)) + self.setMinimumSize(QtCore.QSize(950, 600)) + self.setWindowTitle(_(u'Inkscape Output Pro Bitmap')) + self.move(int((QtWidgets.QDesktopWidget().screenGeometry().width()-self.geometry().width())/2), int((QtWidgets.QDesktopWidget().screenGeometry().height()-self.geometry().height())/2)) + + self.preview_zoom = 1.0 + + self.top_title_bitmap = QtWidgets.QLabel(parent=self) + self.top_title_bitmap.setGeometry(0, 0, 950, 60) + self.top_title_bitmap.setPixmap(QtGui.QPixmap(os.path.join(dirpathSoftware, 'top.png'))) + + self.preview_panel = QtWidgets.QWidget(parent=self) + self.preview_panel.setGeometry(0, 0, 320, 600) + + self.preview_bitmap = QtWidgets.QLabel(parent=self.preview_panel) + self.preview_bitmap.setGeometry(10, 70, 300, 300) + self.preview_bitmap.setPixmap(QtGui.QPixmap(os.path.join(dirpathTempFolder.name, 'preview.png'))) + #self.preview_bitmap.setStyleSheet("QWidget { background: url(alpha.png)}") + #self.preview_bitmap.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignCenter) + + self.preview_original_title = QtWidgets.QLabel(parent=self.preview_panel) + self.preview_original_title.setText(_(u"Original").upper()) + self.preview_original_title.setGeometry(255, 355, 50, 10) + self.preview_original_title.setAlignment(QtCore.Qt.AlignCenter) + self.preview_original_title.setStyleSheet('QFrame{font:6pt;border-radius: 2px;padding: 2px;background-color:rgba(0,0,0,128);color:white}') + + self.preview_result_title = QtWidgets.QLabel(parent=self.preview_panel) + self.preview_result_title.setText(_(u"Result").upper()) + self.preview_result_title.setGeometry(15, 75, 50, 10) + self.preview_result_title.setAlignment(QtCore.Qt.AlignCenter) + self.preview_result_title.setStyleSheet('QFrame{font:6pt;border-radius: 2px;padding: 2px;background-color:rgba(0,0,0,128);color:white}') + + self.zoom_out_button = QtWidgets.QPushButton(QtGui.QIcon.fromTheme("zoom-out"), '', parent=self.preview_panel) + self.zoom_out_button.setGeometry(10, 371, 16, 16) + self.zoom_out_button.setIconSize(QtCore.QSize(12,12)) + self.zoom_out_button.setFlat(True) + self.zoom_out_button.clicked.connect(self.zoom_out) + + self.zoom_in_button = QtWidgets.QPushButton(QtGui.QIcon.fromTheme("zoom-in"), '', parent=self.preview_panel) + self.zoom_in_button.setGeometry(26, 371, 16, 16) + self.zoom_in_button.setIconSize(QtCore.QSize(12,12)) + self.zoom_in_button.setFlat(True) + self.zoom_in_button.clicked.connect(self.zoom_in) + + self.preview_zoom_title = QtWidgets.QLabel(parent=self.preview_panel) + self.preview_zoom_title.setGeometry(44, 371, 256, 16) + self.preview_zoom_title.setText('100%') + self.preview_zoom_title.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft) + self.preview_zoom_title.setFont(QtGui.QFont('Ubuntu', 8)) + #self.preview_result_title.setStyleSheet('QFrame{font:7pt;border-radius: 2px;padding: 2px;background-color:rgba(0,0,0,128);color:white}') + + self.view_c_button = QtWidgets.QPushButton('C', parent=self.preview_panel) + self.view_c_button.setGeometry(246, 371, 16, 16) + self.view_c_button.setIconSize(QtCore.QSize(12,12)) + self.view_c_button.setFlat(True) + self.view_c_button.setCheckable(True) + self.view_c_button.setChecked(True) + self.view_c_button.setVisible(False) + self.view_c_button.clicked.connect(self.cmyk_advanced_manipulation_view_separations) + + self.view_m_button = QtWidgets.QPushButton('M', parent=self.preview_panel) + self.view_m_button.setGeometry(262, 371, 16, 16) + self.view_m_button.setIconSize(QtCore.QSize(12,12)) + self.view_m_button.setFlat(True) + self.view_m_button.setCheckable(True) + self.view_m_button.setChecked(True) + self.view_m_button.setVisible(False) + self.view_m_button.clicked.connect(self.cmyk_advanced_manipulation_view_separations) + + self.view_y_button = QtWidgets.QPushButton('Y', parent=self.preview_panel) + self.view_y_button.setGeometry(278, 371, 16, 16) + self.view_y_button.setIconSize(QtCore.QSize(12,12)) + self.view_y_button.setFlat(True) + self.view_y_button.setCheckable(True) + self.view_y_button.setChecked(True) + self.view_y_button.setVisible(False) + self.view_y_button.clicked.connect(self.cmyk_advanced_manipulation_view_separations) + + self.view_k_button = QtWidgets.QPushButton('K', parent=self.preview_panel) + self.view_k_button.setGeometry(294, 371, 16, 16) + self.view_k_button.setIconSize(QtCore.QSize(12,12)) + self.view_k_button.setFlat(True) + self.view_k_button.setCheckable(True) + self.view_k_button.setChecked(True) + self.view_k_button.setVisible(False) + self.view_k_button.clicked.connect(self.cmyk_advanced_manipulation_view_separations) + + self.view_image_info = QtWidgets.QLabel(parent=self.preview_panel) + self.view_image_info.setGeometry(10, 400, 300, 190) + self.view_image_info.setFont(QtGui.QFont('Ubuntu', 8)) + self.view_image_info.setWordWrap(True) + self.view_image_info.setAlignment(QtCore.Qt.AlignTop) + + self.format_title = QtWidgets.QLabel(parent=self) + self.format_title.setText(_(u"Format").upper()) + self.format_title.setGeometry(320, 70, 200, 15) + self.format_title.setFont(QtGui.QFont('Ubuntu', 8, 75)) + + self.format_choice = QtWidgets.QComboBox(parent=self) + self.format_choice.setGeometry(320, 85, 200, 25) + self.format_choice.addItems(list_of_export_formats) + self.format_choice.activated.connect(self.change_format) + + self.format_preview_check = QtWidgets.QCheckBox(parent=self) + self.format_preview_check.setGeometry(540, 85, 200, 25) + self.format_preview_check.setText(_(u"Preview")) + self.format_preview_check.setChecked(True) + self.format_preview_check.clicked.connect(self.format_preview_change) + + self.option_box = QtWidgets.QTabWidget(parent=self) + self.option_box.setGeometry(320, 120, 620, 435) + + self.general_options_panel = QtWidgets.QWidget(parent=self) + self.general_geometry_panel = QtWidgets.QWidget(parent=self) + self.general_prepress_panel = QtWidgets.QWidget(parent=self) + self.general_imposition_panel = QtWidgets.QWidget(parent=self) + self.option_box.addTab(self.general_options_panel, _(u"Options")) + self.option_box.addTab(self.general_geometry_panel, _(u"Size")) + self.option_box.addTab(self.general_prepress_panel, _(u"Prepress")) + self.option_box.addTab(self.general_imposition_panel, _(u"Imposition")) + + self.option_box.currentChanged.connect(self.generate_preview) + + self.general_options_panel_jpeg = QtWidgets.QWidget(parent=self.general_options_panel) + self.general_options_panel_jpeg.setVisible(False) + + self.icc_dir_textbox_label = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.icc_dir_textbox_label.setText(_(u"ICC profile folder")) + self.icc_dir_textbox_label.setGeometry(10, 280, 120, 25) + + self.icc_dir_textbox = QtWidgets.QLineEdit(parent=self.general_options_panel_jpeg) + self.icc_dir_textbox.setReadOnly(True) + self.icc_dir_textbox.setGeometry(130, 280, 260, 25) + self.icc_dir_textbox.setText(icc_dir) + + self.icc_dir_button = QtWidgets.QPushButton(_("Change"), parent=self.general_options_panel_jpeg) + self.icc_dir_button.setGeometry(403, 280, 70, 25) + self.icc_dir_button.clicked.connect(self.change_icc_dir) + + self.color_mode_title_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.color_mode_title_jpeg.setText(_(u"Color mode").upper()) + self.color_mode_title_jpeg.setGeometry(10, 10, 260, 15) + self.color_mode_title_jpeg.setFont(QtGui.QFont('Ubuntu', 8)) + + self.color_mode_choice_jpeg = QtWidgets.QComboBox(parent=self.general_options_panel_jpeg) + self.color_mode_choice_jpeg.setGeometry(10, 25, 260, 25) + self.color_mode_choice_jpeg.addItems(list_of_color_modes_jpeg) + self.color_mode_choice_jpeg.activated.connect(self.change_color_mode_jpeg) + + self.color_mode_title_tip_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.color_mode_title_tip_jpeg.setGeometry(10, 50, 260, 15) + self.color_mode_title_tip_jpeg.setFont(QtGui.QFont('Ubuntu', 7)) + + self.quality_title_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.quality_title_jpeg.setText(_(u"Quality").upper()) + self.quality_title_jpeg.setGeometry(285, 10, 100, 15) + self.quality_title_jpeg.setFont(QtGui.QFont('Ubuntu', 8)) + + jpeg_quality = 100 + self.quality_percent_title_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.quality_percent_title_jpeg.setText('{}%'.format(jpeg_quality)) + self.quality_percent_title_jpeg.setGeometry(505, 10, 100, 40) + self.quality_percent_title_jpeg.setFont(QtGui.QFont('Ubuntu', 12, 75)) + self.quality_percent_title_jpeg.setAlignment(QtCore.Qt.AlignRight) + + self.quality_percent_title_left_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.quality_percent_title_left_jpeg.setText('Lower quality\nSmaller file') + self.quality_percent_title_left_jpeg.setGeometry(285, 40, 160, 35) + self.quality_percent_title_left_jpeg.setFont(QtGui.QFont('Ubuntu', 7)) + self.quality_percent_title_left_jpeg.setAlignment(QtCore.Qt.AlignLeft) + + self.quality_percent_title_right_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.quality_percent_title_right_jpeg.setText('Higher quality
Larger file') + self.quality_percent_title_right_jpeg.setGeometry(445, 40, 160, 35) + self.quality_percent_title_right_jpeg.setFont(QtGui.QFont('Ubuntu', 7)) + self.quality_percent_title_right_jpeg.setAlignment(QtCore.Qt.AlignRight) + + self.quality_choice_dial_jpeg = QtWidgets.QDial(parent=self.general_options_panel_jpeg) + self.quality_choice_dial_jpeg.setRange(1,100) + self.quality_choice_dial_jpeg.setGeometry(415, 10, 60, 60) + self.quality_choice_dial_jpeg.setNotchesVisible(True) + self.quality_choice_dial_jpeg.setValue(jpeg_quality) + self.quality_choice_dial_jpeg.sliderReleased.connect(self.generate_preview) + self.quality_choice_dial_jpeg.valueChanged.connect(self.change_quality_live_jpeg) + + self.color_profile_choice_jpeg = QtWidgets.QCheckBox(_(u"Use Inkscape color profile"), parent=self.general_options_panel_jpeg) + self.color_profile_choice_jpeg.setChecked(False) + self.color_profile_choice_jpeg.setGeometry(283, 150, 325, 25) + self.color_profile_choice_jpeg.clicked.connect(self.generate_preview) + + self.document_color_profile_title_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.document_color_profile_title_jpeg.setGeometry(290, 175, 320, 30) + self.document_color_profile_title_jpeg.setWordWrap(True) + self.document_color_profile_title_jpeg.setFont(QtGui.QFont('Ubuntu', 7)) + self.document_color_profile_title_jpeg.setAlignment(QtCore.Qt.AlignLeft) + + if selected_print_profile == '': + self.document_color_profile_title_jpeg.setEnabled(False) + self.color_profile_choice_jpeg.setEnabled(False) + self.document_color_profile_title_jpeg.setText(_(u"This document is not using a color profile.")) + else: + self.document_color_profile_title_jpeg.setText(_(u"The profile used by Inkscape is") + ' ' + selected_print_profile[:-4]) + + self.jpeg_interlace_option_jpeg = QtWidgets.QCheckBox(_(u"Interlace"), parent=self.general_options_panel_jpeg) + self.jpeg_interlace_option_jpeg.setGeometry(10, 80, 120, 25) + self.jpeg_interlace_option_jpeg.toggled.connect(self.jpeg_interlace_click_jpeg) + + self.jpeg_interlace_choice_jpeg = QtWidgets.QComboBox(parent=self.general_options_panel_jpeg) + self.jpeg_interlace_choice_jpeg.setGeometry(130, 80, 140, 25) + self.jpeg_interlace_choice_jpeg.addItems(list_of_interlacing_jpeg.keys()) + self.jpeg_interlace_choice_jpeg.setCurrentIndex(1) + self.jpeg_interlace_choice_jpeg.setEnabled(False) + self.jpeg_interlace_choice_jpeg.activated.connect(self.generate_preview) + + self.jpeg_optimize_option_jpeg = QtWidgets.QCheckBox(_(u"Optimize"), parent=self.general_options_panel_jpeg) + self.jpeg_optimize_option_jpeg.setGeometry(10, 115, 260, 25) + self.jpeg_optimize_option_jpeg.setChecked(True) + + self.jpeg_noise_option_jpeg = QtWidgets.QCheckBox(_(u"Noise"), parent=self.general_options_panel_jpeg) + self.jpeg_noise_option_jpeg.setGeometry(10, 150, 120, 25) + self.jpeg_noise_option_jpeg.toggled.connect(self.jpeg_noise_click_jpeg) + + self.jpeg_noise_choice_jpeg = QtWidgets.QComboBox(parent=self.general_options_panel_jpeg) + self.jpeg_noise_choice_jpeg.setGeometry(130, 150, 140, 25) + self.jpeg_noise_choice_jpeg.addItems(list_of_noise_jpeg.keys()) + self.jpeg_noise_choice_jpeg.setCurrentIndex(1) + self.jpeg_noise_choice_jpeg.setEnabled(False) + self.jpeg_noise_choice_jpeg.activated.connect(self.generate_preview) + + self.jpeg_noise_ammount_jpeg = QtWidgets.QSlider(QtCore.Qt.Horizontal, parent=self.general_options_panel_jpeg) + self.jpeg_noise_ammount_jpeg.setGeometry(10, 170, 260, 25) + self.jpeg_noise_ammount_jpeg.setRange(0,100) + self.jpeg_noise_ammount_jpeg.setEnabled(False) + self.jpeg_noise_ammount_jpeg.setValue(0) + self.jpeg_noise_ammount_jpeg.sliderReleased.connect(self.generate_preview) + + self.jpeg_subsampling_option_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.jpeg_subsampling_option_jpeg.setText(_(u"Sub-sampling")) + self.jpeg_subsampling_option_jpeg.setGeometry(10, 210, 140, 25) + + self.jpeg_subsampling_choice_jpeg = QtWidgets.QComboBox(parent=self.general_options_panel_jpeg) + self.jpeg_subsampling_choice_jpeg.setGeometry(150, 210, 120, 25) + self.jpeg_subsampling_choice_jpeg.addItems(list_of_subsampling_jpeg) + self.jpeg_subsampling_choice_jpeg.setCurrentIndex(0) + self.jpeg_subsampling_choice_jpeg.activated.connect(self.generate_preview) + + self.jpeg_dct_option_jpeg = QtWidgets.QLabel(parent=self.general_options_panel_jpeg) + self.jpeg_dct_option_jpeg.setText(_(u"DCT Method")) + self.jpeg_dct_option_jpeg.setGeometry(10, 245, 120, 25) + + self.jpeg_dct_choice_jpeg = QtWidgets.QComboBox(parent=self.general_options_panel_jpeg) + self.jpeg_dct_choice_jpeg.setGeometry(130, 245, 140, 25) + self.jpeg_dct_choice_jpeg.addItems(list_of_dct_jpeg.keys()) + self.jpeg_dct_choice_jpeg.activated.connect(self.generate_preview) + + self.cmyk_advanced_manipulation_option_jpeg = QtWidgets.QCheckBox(_(u"Accurate color handling"), parent=self.general_options_panel_jpeg) + self.cmyk_advanced_manipulation_option_jpeg.setGeometry(283, 80, 325, 25) + self.cmyk_advanced_manipulation_option_jpeg.clicked.connect(self.cmyk_advanced_manipulation_click_jpeg) + + self.cmyk_overblack_jpeg = QtWidgets.QCheckBox(_(u"Black overlay"), parent=self.general_options_panel_jpeg) + self.cmyk_overblack_jpeg.setGeometry(283, 115, 325, 25) + self.cmyk_overblack_jpeg.setEnabled(False) + self.cmyk_overblack_jpeg.clicked.connect(self.cmyk_advanced_manipulation_click_jpeg) + + self.area_to_export_title = QtWidgets.QLabel(parent=self.general_geometry_panel) + self.area_to_export_title.setText(_(u"Area to export").upper()) + self.area_to_export_title.setGeometry(10, 20, 250, 15) + self.area_to_export_title.setFont(QtGui.QFont('Ubuntu', 8)) + + self.area_to_export_choice = QtWidgets.QComboBox(parent=self.general_geometry_panel) + self.area_to_export_choice.setGeometry(10, 35, 250, 25) + self.area_to_export_choice.addItems(list_of_area_to_export) + self.area_to_export_choice.activated.connect(self.change_area_to_export) + + self.dpi_title = QtWidgets.QLabel(parent=self.general_geometry_panel) + self.dpi_title.setText(_(u"Dots per inch").upper()) + self.dpi_title.setGeometry(270, 20, 200, 15) + self.dpi_title.setFont(QtGui.QFont('Ubuntu', 8)) + + self.dpi_choice = QtWidgets.QSpinBox(parent=self.general_geometry_panel) + self.dpi_choice.setValue(96) + self.dpi_choice.setGeometry(270, 35, 100, 25) + self.dpi_choice.setRange(1, 99999) + self.dpi_choice.editingFinished.connect(self.change_area_to_export) + + self.dpi_text_title = QtWidgets.QLabel(parent=self.general_geometry_panel) + self.dpi_text_title.setText('dpi') + self.dpi_text_title.setGeometry(380, 35, 80, 25) + self.dpi_text_title.setFont(QtGui.QFont('Ubuntu', 8)) + + self.x0_value = QtWidgets.QSpinBox(parent=self.general_geometry_panel) + self.x0_value.setGeometry(10, 100, 80, 25) + self.x0_value.setRange(1, 2147483647) + self.x0_value.editingFinished.connect(self.change_area_to_export) + + self.y0_value = QtWidgets.QSpinBox(parent=self.general_geometry_panel) + self.y0_value.setGeometry(100, 130, 80, 25) + self.y0_value.setRange(1, 2147483647) + self.y0_value.editingFinished.connect(self.change_area_to_export) + + self.x1_value = QtWidgets.QSpinBox(parent=self.general_geometry_panel) + self.x1_value.setGeometry(100, 70, 80, 25) + self.x1_value.setRange(1, 2147483647) + self.x1_value.editingFinished.connect(self.change_area_to_export) + + self.y1_value = QtWidgets.QSpinBox(parent=self.general_geometry_panel) + self.y1_value.setGeometry(190, 100, 80, 25) + self.y1_value.setRange(1, 2147483647) + self.y1_value.editingFinished.connect(self.change_area_to_export) + + self.area_to_export_id_title = QtWidgets.QLabel(parent=self.general_geometry_panel) + self.area_to_export_id_title.setText(_(u"Object to be exported").upper()) + self.area_to_export_id_title.setGeometry(10, 70, 300, 15) + self.area_to_export_id_title.setFont(QtGui.QFont('Ubuntu', 8)) + + self.area_to_export_id_name = QtWidgets.QLineEdit(parent=self.general_geometry_panel) + self.area_to_export_id_name.setGeometry(10, 85, 300, 25) + + self.area_to_export_idonly_check = QtWidgets.QCheckBox(parent=self.general_geometry_panel) + self.area_to_export_idonly_check.setGeometry(10, 120, 400, 25) + self.area_to_export_idonly_check.setText(_(u"Export only object")) + + self.prepress_paper_settings_label = QtWidgets.QLabel(parent=self.general_prepress_panel) + self.prepress_paper_settings_label.setGeometry(10, 10, 300, 15) + self.prepress_paper_settings_label.setText(_(u"Paper or film setting").upper()) + self.prepress_paper_settings_label.setFont(QtGui.QFont('Ubuntu', 8)) + + self.prepress_paper_settings_invert = QtWidgets.QCheckBox(parent=self.general_prepress_panel) + self.prepress_paper_settings_invert.setGeometry(10, 25, 300, 25) + self.prepress_paper_settings_invert.setText(_(u"Invert")) + self.prepress_paper_settings_invert.setChecked(False) + self.prepress_paper_settings_invert.clicked.connect(self.generate_preview) + + self.prepress_paper_settings_mirror = QtWidgets.QCheckBox(parent=self.general_prepress_panel) + self.prepress_paper_settings_mirror.setGeometry(10, 50, 300, 25) + self.prepress_paper_settings_mirror.setText(_(u"Mirror")) + self.prepress_paper_settings_mirror.setChecked(False) + self.prepress_paper_settings_mirror.clicked.connect(self.generate_preview) + + self.prepress_paper_cutmarks_label = QtWidgets.QLabel(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_label.setGeometry(10, 85, 300, 15) + self.prepress_paper_cutmarks_label.setText(_(u"Crop marks").upper()) + self.prepress_paper_cutmarks_label.setFont(QtGui.QFont('Ubuntu', 8)) + + self.prepress_paper_cutmarks_check = QtWidgets.QCheckBox(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_check.setGeometry(10, 100, 300, 25) + self.prepress_paper_cutmarks_check.setText(_(u"Insert crop marks")) + self.prepress_paper_cutmarks_check.setChecked(False) + self.prepress_paper_cutmarks_check.clicked.connect(self.cut_marks_insert_change) + + self.prepress_paper_cutmarks_strokewidth_label = QtWidgets.QLabel(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_strokewidth_label.setGeometry(10, 125, 200, 25) + self.prepress_paper_cutmarks_strokewidth_label.setText(_(u"Mark thickness:")) + self.prepress_paper_cutmarks_strokewidth_label.setEnabled(False) + + self.prepress_paper_cutmarks_strokewidth_value = QtWidgets.QLineEdit(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_strokewidth_value.setGeometry(210, 125, 50, 25) + self.prepress_paper_cutmarks_strokewidth_value.setText('1') + self.prepress_paper_cutmarks_strokewidth_value.setEnabled(False) + self.prepress_paper_cutmarks_strokewidth_value.editingFinished.connect(self.generate_preview) + + self.prepress_paper_cutmarks_strokewidth_choice = QtWidgets.QComboBox(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_strokewidth_choice.setGeometry(260,125,50,25) + self.prepress_paper_cutmarks_strokewidth_choice.addItems(uuconv.keys()) + self.prepress_paper_cutmarks_strokewidth_choice.setCurrentIndex(5) + self.prepress_paper_cutmarks_strokewidth_choice.activated.connect(self.generate_preview) + self.prepress_paper_cutmarks_strokewidth_choice.setEnabled(False) + + self.prepress_paper_cutmarks_bleedsize_label = QtWidgets.QLabel(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_bleedsize_label.setGeometry(10, 150, 200, 25) + self.prepress_paper_cutmarks_bleedsize_label.setText(_(u"Bleed:")) + self.prepress_paper_cutmarks_bleedsize_label.setEnabled(False) + + self.prepress_paper_cutmarks_bleedsize_value = QtWidgets.QLineEdit(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_bleedsize_value.setGeometry(210, 150, 50, 25) + self.prepress_paper_cutmarks_bleedsize_value.setText('5') + self.prepress_paper_cutmarks_bleedsize_value.setEnabled(False) + self.prepress_paper_cutmarks_bleedsize_value.editingFinished.connect(self.generate_preview) + + self.prepress_paper_cutmarks_bleedsize_choice = QtWidgets.QComboBox(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_bleedsize_choice.setGeometry(260,150,50,25) + self.prepress_paper_cutmarks_bleedsize_choice.addItems(uuconv.keys()) + self.prepress_paper_cutmarks_bleedsize_choice.setCurrentIndex(5) + self.prepress_paper_cutmarks_bleedsize_choice.activated.connect(self.generate_preview) + self.prepress_paper_cutmarks_bleedsize_choice.setEnabled(False) + + self.prepress_paper_cutmarks_marksize_label = QtWidgets.QLabel(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_marksize_label.setGeometry(10, 175, 200, 25) + self.prepress_paper_cutmarks_marksize_label.setText(_(u"Mark size:")) + self.prepress_paper_cutmarks_marksize_label.setEnabled(False) + + self.prepress_paper_cutmarks_marksize_value = QtWidgets.QLineEdit(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_marksize_value.setGeometry(210, 175, 50, 25) + self.prepress_paper_cutmarks_marksize_value.setText('5') + self.prepress_paper_cutmarks_marksize_value.setEnabled(False) + self.prepress_paper_cutmarks_marksize_value.editingFinished.connect(self.generate_preview) + + self.prepress_paper_cutmarks_marksize_choice = QtWidgets.QComboBox(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_marksize_choice.setGeometry(260,175,50,25) + self.prepress_paper_cutmarks_marksize_choice.addItems(uuconv.keys()) + self.prepress_paper_cutmarks_marksize_choice.setCurrentIndex(5) + self.prepress_paper_cutmarks_marksize_choice.activated.connect(self.generate_preview) + self.prepress_paper_cutmarks_marksize_choice.setEnabled(False) + + self.prepress_paper_cutmarks_inside_check = QtWidgets.QCheckBox(parent=self.general_prepress_panel) + self.prepress_paper_cutmarks_inside_check.setGeometry(10, 200, 300, 25) + self.prepress_paper_cutmarks_inside_check.setText(_(u"No internal marks")) + self.prepress_paper_cutmarks_inside_check.setChecked(False) + self.prepress_paper_cutmarks_inside_check.setEnabled(False) + self.prepress_paper_cutmarks_inside_check.clicked.connect(self.generate_preview) + + self.imposition_label = QtWidgets.QLabel(parent=self.general_imposition_panel) + self.imposition_label.setGeometry(10, 10, 300, 15) + self.imposition_label.setText(_(u"Amount of impositions").upper()) + self.imposition_label.setFont(QtGui.QFont('Ubuntu', 8)) + + self.imposition_vertical_number_label = QtWidgets.QLabel(parent=self.general_imposition_panel) + self.imposition_vertical_number_label.setGeometry(10, 25, 200, 25) + self.imposition_vertical_number_label.setText(_(u"Lines:")) + + self.imposition_vertical_number_value = QtWidgets.QSpinBox(parent=self.general_imposition_panel) + self.imposition_vertical_number_value.setGeometry(210, 25, 50, 25) + self.imposition_vertical_number_value.setValue(1) + self.imposition_vertical_number_value.setRange(1, 999) + self.imposition_vertical_number_value.editingFinished.connect(self.generate_preview) + + self.imposition_horizontal_number_label = QtWidgets.QLabel(parent=self.general_imposition_panel) + self.imposition_horizontal_number_label.setGeometry(10, 60, 200, 25) + self.imposition_horizontal_number_label.setText(_(u"Columns:")) + + self.imposition_horizontal_number_value = QtWidgets.QSpinBox(parent=self.general_imposition_panel) + self.imposition_horizontal_number_value.setGeometry(210, 60, 50, 25) + self.imposition_horizontal_number_value.setValue(1) + self.imposition_horizontal_number_value.setRange(1, 999) + self.imposition_horizontal_number_value.editingFinished.connect(self.generate_preview) + + self.imposition_space_label = QtWidgets.QLabel(parent=self.general_imposition_panel) + self.imposition_space_label.setGeometry(10, 90, 200, 25) + self.imposition_space_label.setText(_(u"Space between marks:")) + + self.imposition_space_value = QtWidgets.QLineEdit(parent=self.general_imposition_panel) + self.imposition_space_value.setGeometry(210, 90, 50, 25) + self.imposition_space_value.setText('5') + self.imposition_space_value.editingFinished.connect(self.generate_preview) + + self.imposition_space_choice = QtWidgets.QComboBox(parent=self.general_imposition_panel) + self.imposition_space_choice.setGeometry(260,90,50,25) + self.imposition_space_choice.addItems(uuconv.keys()) + self.imposition_space_choice.setCurrentIndex(5) + self.imposition_space_choice.activated.connect(self.generate_preview) + + self.export_button = QtWidgets.QPushButton(QtGui.QIcon.fromTheme("document-export"), _("Export"), parent=self) + self.export_button.setGeometry(740, 560, 200, 30) + self.export_button.setIconSize(QtCore.QSize(20,20)) + self.export_button.clicked.connect(self.export) + + self.change_area_to_export() + self.change_format() + + def generate_preview(self): + if self.format_preview_check.isChecked(): + self.generate_final_file() + + if self.option_box.currentIndex() == 0: + self.preview_original_title.setVisible(True) + self.preview_result_title.setVisible(True) + + final_command = ['convert'] + final_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.') + list_of_export_formats[self.format_choice.currentIndex()].lower()) + + if self.color_profile_choice_jpeg.isChecked(): + final_command.append('-profile') + final_command.append(os.path.join(self.icc_dir_textbox.text(), selected_screen_profile)) + + final_command.append(os.path.join(dirpathTempFolder.name, 'result.png')) + subprocess.Popen(final_command, shell=shell).wait() + + file_info = subprocess.Popen(['identify', os.path.join(dirpathTempFolder.name, 'source.png')], shell=shell, stdout=subprocess.PIPE).communicate()[0].decode('UTF-8') + image_width = int(file_info.split(' ')[2].split('x')[0]) + image_height = int(file_info.split(' ')[2].split('x')[1]) + + marksize = (self.dpi_choice.value() / 96) * unittouu(str(self.prepress_paper_cutmarks_marksize_value.text()) + str(self.prepress_paper_cutmarks_marksize_choice.currentText())) + imposition_space = (self.dpi_choice.value() / 96) * unittouu(str(self.imposition_space_value.text()) + str(self.imposition_space_choice.currentText())) + + file_info = subprocess.Popen(['identify', '-verbose', os.path.join(dirpathTempFolder.name, 'result-imp.') + list_of_export_formats[self.format_choice.currentIndex()].lower()], shell=shell, stdout=subprocess.PIPE).communicate()[0].decode('UTF-8') + file_info_final = '' + for line in file_info.split('\n'): + if ' Format: ' in line: + file_info_final += 'Image Format: ' + line.replace(' Format: ', '') + '
' + if ' Geometry: ' in line: + file_info_final += 'Width and height: ' + line.replace(' Geometry: ', '').split('+')[0] + '
' + if ' Resolution: ' in line: + file_info_final += 'Resolution: ' + line.replace(' Resolution: ', '') + if ' Units: ' in line: + file_info_final += ' ' + line.replace(' Units: ', '').replace('Per', ' per ').replace('Pixels', 'pixels').replace('Centimeter', 'centimeter').replace('Inch', 'inch') + '
' + if ' Colorspace: ' in line: + file_info_final += 'Colorspace: ' + line.replace(' Colorspace: ', '') + '
' + if ' Depth: ' in line: + file_info_final += 'Depth: ' + line.replace(' Depth: ', '') + '
' + if ' Quality: ' in line: + file_info_final += 'Quality: ' + line.replace(' Quality: ', '') + '%
' + if ' Filesize: ' in line: + file_info_final += 'Filesize: ' + line.replace(' Filesize: ', '') + '
' + if ' jpeg:sampling-factor: ' in line: + file_info_final += 'Sampling: ' + line.replace(' jpeg:sampling-factor: ', '') + '
' + + if self.prepress_paper_cutmarks_check.isChecked(): + margin = marksize + else: + margin = imposition_space + + if image_width < 300 or image_height < 300: + what_show = '-extent ' + str(int(300 * self.preview_zoom)) + 'x' + str(int(300 * self.preview_zoom)) + '-' + str(int(150 * self.preview_zoom) - int(image_width / 2)) + '-' + str(int(150 * self.preview_zoom) - int(image_height / 2)) + else: + what_show = '-crop ' + str(int(300 * self.preview_zoom)) + 'x' + str(int(300 * self.preview_zoom)) + '+' + str(int(image_width / 2) - int(150 * self.preview_zoom)) + '+' + str(int(image_height / 2) - int(150 * self.preview_zoom)) + os.system('convert "' + os.path.join(dirpathTempFolder.name, 'source.png') + '" ' + what_show + ' "' + os.path.join(dirpathTempFolder.name, 'original.png') + '"' ) + + if image_width < 300 or image_height < 300: + what_show = '-extent ' + str(int(300 * self.preview_zoom)) + 'x' + str(int(300 * self.preview_zoom)) + '-' + str(int(150 * self.preview_zoom) - int(image_width / 2) - margin) + '-' + str(int(150 * self.preview_zoom) - int(image_height / 2) - margin) + else: + what_show = '-crop ' + str(int(300 * self.preview_zoom)) + 'x' + str(int(300 * self.preview_zoom)) + '+' + str(int(image_width / 2) - int(150 * self.preview_zoom) + margin) + '+' + str(int(image_height / 2) - int(150 * self.preview_zoom) + margin) + + os.system('convert "' + os.path.join(dirpathTempFolder.name, 'result.png') + '" ' + what_show + ' "' + os.path.join(dirpathTempFolder.name, 'result.png') + '"' ) + + if not self.preview_zoom == 1: + os.system('convert "' + os.path.join(dirpathTempFolder.name, 'original.png') + '" -filter box -resize 300x300 "' + os.path.join(dirpathTempFolder.name, 'original.png') + '"' ) + os.system('convert "' + os.path.join(dirpathTempFolder.name, 'result.png') + '" -filter box -resize 300x300 "' + os.path.join(dirpathTempFolder.name, 'result.png') + '"' ) + + os.system( + 'convert "' + + os.path.join(dirpathTempFolder.name, 'original.png') + + '" "' + + os.path.join(dirpathTempFolder.name, 'result.png') + + '" "' + + os.path.join(dirpathSoftware, 'preview_mask.png') + #static file from extension directory + '" -composite "' + + os.path.join(dirpathTempFolder.name, 'preview.png') + + '"' + ) + + self.view_image_info.setText(file_info_final + '
' + list_of_format_tips[list_of_export_formats[self.format_choice.currentIndex()]] + '') + + elif self.option_box.currentIndex() == 1: + self.preview_original_title.setVisible(False) + self.preview_result_title.setVisible(False) + + subprocess.Popen(['convert', os.path.join(dirpathTempFolder.name, 'result-imp.') + list_of_export_formats[self.format_choice.currentIndex()].lower(), '-resize', '300x300', os.path.join(dirpathTempFolder.name, 'preview.png')], shell=shell).wait() + + elif self.option_box.currentIndex() == 2: + None + + elif self.option_box.currentIndex() == 3: + None + + self.preview_bitmap.setPixmap(QtGui.QPixmap(os.path.join(dirpathTempFolder.name, 'preview.png'))) + + def generate_final_file(self): + if list_of_export_formats[self.format_choice.currentIndex()] == 'JPEG': + jpeg_command = ['convert'] + + if not self.cmyk_advanced_manipulation_option_jpeg.isChecked(): + pre_command = ['convert'] + pre_command.append(os.path.join(dirpathTempFolder.name, 'source.tiff')) + + if list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'CMYK' or list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'RGB': + if self.color_profile_choice_jpeg.isChecked(): + pre_command.append('-profile') + pre_command.append(os.path.join(self.icc_dir_textbox.text(), selected_screen_profile)) + + if list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'RGB': + pre_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + subprocess.Popen(pre_command, shell=shell).wait() + del pre_command[:] + pre_command.append('convert') + pre_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + pre_command.append('-profile') + pre_command.append(os.path.join(self.icc_dir_textbox.text(), selected_screen_profile)) + + pre_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + subprocess.Popen(pre_command, shell=shell).wait() + if not os.path.isfile(os.path.join(dirpathTempFolder.name, 'result.tiff')): + inkex.utils.debug("Error. Missing result.tiff") + + else: + if self.color_profile_choice_jpeg.isChecked(): + pre_command = ['convert'] + pre_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + pre_command.append('-profile') + pre_command.append(os.path.join(self.icc_dir_textbox.text(), selected_screen_profile)) + pre_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + subprocess.Popen(pre_command, shell=shell).wait() + + file_info = subprocess.Popen(['identify', os.path.join(dirpathTempFolder.name, 'source.png')], shell=shell, stdout=subprocess.PIPE).communicate()[0].decode('UTF-8') + if self.prepress_paper_cutmarks_check.isChecked(): + bleedsize = (self.dpi_choice.value() / 96) * unittouu(str(self.prepress_paper_cutmarks_bleedsize_value.text()) + str(self.prepress_paper_cutmarks_bleedsize_choice.currentText())) + marksize = (self.dpi_choice.value() / 96) * unittouu(str(self.prepress_paper_cutmarks_marksize_value.text()) + str(self.prepress_paper_cutmarks_marksize_choice.currentText())) + else: + bleedsize = 0 + marksize = 0 + + imposition_space = (self.dpi_choice.value() / 96) *unittouu(str(self.imposition_space_value.text()) + str(self.imposition_space_choice.currentText())) + + image_width = [] + for i in range(self.imposition_vertical_number_value.value()): + image_width.append(int(file_info.split(' ')[2].split('x')[0])) + + image_height = [] + for i in range(self.imposition_horizontal_number_value.value()): + image_height.append(int(file_info.split(' ')[2].split('x')[1])) + + imposition_command = ['convert'] + imposition_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + imposition_command.append('-extent') + imposition_command.append(str(sum(image_width) + (marksize*2) + (imposition_space * (len(image_width) -1))) + 'x' + str(sum(image_height) + (marksize*2) + (imposition_space * (len(image_height) -1))) + '-' + str(marksize) + '-' + str(marksize)) + imposition_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')) + subprocess.Popen(imposition_command, shell=shell).wait() + + last_width = 0 + last_height = 0 + last_marksize = marksize + for width in image_width: + for height in image_height: + if not (last_width == 0 and last_height == 0): + imposition_command = ['composite'] + imposition_command.append('-geometry') + imposition_command.append('+' + str(last_width + marksize) + '+' + str(last_height + marksize)) + imposition_command.append(os.path.join(dirpathTempFolder.name, 'result.tiff')) + imposition_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')) + imposition_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')) + subprocess.Popen(imposition_command, shell=shell).wait() + + last_height += height + imposition_space + last_marksize = 0 + last_width += width + imposition_space + last_height = 0 + + if self.prepress_paper_cutmarks_check.isChecked(): + cutmarks.generate_final_file(False, + self.prepress_paper_cutmarks_inside_check.isChecked(), + list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()], + image_width, + image_height, + imposition_space, + unittouu(str(self.prepress_paper_cutmarks_strokewidth_value.text()) + str(self.prepress_paper_cutmarks_strokewidth_choice.currentText())), + bleedsize, + marksize, + dirpathTempFolder.name) + + cut_marks_command = ['composite'] + cut_marks_command.append('-compose') + cut_marks_command.append('Multiply') + cut_marks_command.append('-gravity') + cut_marks_command.append('center') + cut_marks_command.append(os.path.join(dirpathTempFolder.name, 'cut_mark.tiff')) + cut_marks_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')) + cut_marks_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')) + subprocess.Popen(cut_marks_command, shell=shell).wait() + + if not os.path.isfile(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')): + inkex.utils.debug("Error. Missing result-imp.tiff") + + jpeg_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.tiff')) + + if self.prepress_paper_settings_invert.isChecked(): + jpeg_command.append('-negate') + + if self.prepress_paper_settings_mirror.isChecked(): + jpeg_command.append('-flop') + + jpeg_command.append('-quality') + jpeg_command.append(str(self.quality_choice_dial_jpeg.value())) + + jpeg_command.append('-colorspace') + jpeg_command.append(list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()]) + + if self.jpeg_interlace_option_jpeg.isChecked(): + jpeg_command.append('-interlace') + jpeg_command.append(list_of_interlacing_jpeg[self.jpeg_interlace_choice_jpeg.currentText()]) + + if self.jpeg_optimize_option_jpeg.isChecked(): + jpeg_command.append('-type') + jpeg_command.append('optimize') + + if self.jpeg_noise_option_jpeg.isChecked(): + jpeg_command.append('-evaluate') + jpeg_command.append(list_of_noise_jpeg[self.jpeg_noise_choice_jpeg.currentText()]) + jpeg_command.append(str(self.jpeg_noise_ammount_jpeg.value())) + + jpeg_command.append('-sampling-factor') + jpeg_command.append(self.jpeg_subsampling_choice_jpeg.currentText()) + + jpeg_command.append('-define') + jpeg_command.append('jpeg:dct-method=' + list_of_dct_jpeg[self.jpeg_dct_choice_jpeg.currentText()]) + + jpeg_command.append(os.path.join(dirpathTempFolder.name, 'result-imp.jpeg')) + + subprocess.Popen(jpeg_command, shell=shell).wait() + + def change_format(self): + self.general_options_panel_jpeg.setVisible(False) + + if list_of_export_formats[self.format_choice.currentIndex()] == 'JPEG': + self.general_options_panel_jpeg.setVisible(True) + + self.generate_preview() + + def change_color_mode_jpeg(self): + if list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'CMYK': + self.color_mode_title_tip_jpeg.setText(u'Recommended for graphic printing') + self.cmyk_advanced_manipulation_option_jpeg.setChecked(False) + self.cmyk_advanced_manipulation_option_jpeg.setEnabled(True) + self.cmyk_overblack_jpeg.setEnabled(False) + self.cmyk_overblack_jpeg.setChecked(False) + self.color_profile_choice_jpeg.setEnabled(True) + self.color_profile_choice_jpeg.setChecked(False) + self.document_color_profile_title_jpeg.setEnabled(True) + self.general_prepress_panel.setEnabled(True) + else: + self.cmyk_advanced_manipulation_option_jpeg.setEnabled(False) + self.cmyk_overblack_jpeg.setEnabled(False) + self.cmyk_overblack_jpeg.setChecked(False) + #self.color_profile_choice_jpeg.setEnabled(False) + self.color_profile_choice_jpeg.setChecked(False) + self.document_color_profile_title_jpeg.setEnabled(False) + self.general_prepress_panel.setEnabled(False) + if list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'CMY': + self.color_mode_title_tip_jpeg.setText(u'Recommended for specific print cases') + elif list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'RGB': + self.color_mode_title_tip_jpeg.setText(u'Recommended for use on screens') + elif list_of_color_modes_jpeg[self.color_mode_choice_jpeg.currentIndex()] == 'Gray': + self.color_mode_title_tip_jpeg.setText(u'Grayscale image') + + self.generate_preview() + + def change_quality_live_jpeg(self): + self.quality_percent_title_jpeg.setText(str(self.quality_choice_dial_jpeg.value()) + '%') + + def jpeg_interlace_click_jpeg(self): + if self.jpeg_interlace_option_jpeg.isChecked(): + self.jpeg_interlace_choice_jpeg.setEnabled(True) + else: + self.jpeg_interlace_choice_jpeg.setEnabled(False) + self.generate_preview() + + def jpeg_noise_click_jpeg(self): + if self.jpeg_noise_option_jpeg.isChecked(): + self.jpeg_noise_choice_jpeg.setEnabled(True) + self.jpeg_noise_ammount_jpeg.setEnabled(True) + else: + self.jpeg_noise_choice_jpeg.setEnabled(False) + self.jpeg_noise_ammount_jpeg.setEnabled(False) + self.generate_preview() + + def cmyk_advanced_manipulation_click_jpeg(self): + if self.cmyk_advanced_manipulation_option_jpeg.isChecked(): + self.cmyk_overblack_jpeg.setEnabled(True) + self.view_c_button.setVisible(True) + self.view_m_button.setVisible(True) + self.view_y_button.setVisible(True) + self.view_k_button.setVisible(True) + self.cmyk_overprint_black() + self.cmyk_advanced_manipulation() + + else: + self.cmyk_overblack_jpeg.setEnabled(False) + self.cmyk_overblack_jpeg.setChecked(False) + self.view_c_button.setVisible(False) + self.view_m_button.setVisible(False) + self.view_y_button.setVisible(False) + self.view_k_button.setVisible(False) + self.generate_preview() + + def cmyk_overprint_black(self): + with open(os.path.join(dirpathTempFolder.name, 'original.svg'), 'r') as f: + if self.cmyk_overblack_jpeg.isChecked(): + cmyk.generate_svg_separations(dirpathTempFolder.name, f.read(), True) + else: + cmyk.generate_svg_separations(dirpathTempFolder.name, f.read(), False) + + def cmyk_advanced_manipulation(self): + area_to_export = self.area_to_export() + cmyk.generate_png_separations(dirpathTempFolder.name, self.area_to_export(), self.dpi_choice.value(), False) + + for color in ['C', 'M', 'Y', 'K']: + cmd = ['convert', + os.path.join(dirpathTempFolder.name, 'separated' + area_to_export.replace(' ', '') + color + ".png"), + '-colorspace', + 'CMYK', + '-channel', + color, + '-separate', + os.path.join(dirpathTempFolder.name, 'separated' + area_to_export.replace(' ', '') + color + ".png")] + #inkex.utils.debug(cmd) + p = subprocess.Popen(cmd, shell=shell).wait() + + self.cmyk_advanced_manipulation_view_separations() + + def cmyk_advanced_manipulation_view_separations(self): + area_to_export = self.area_to_export() + + file_info = subprocess.Popen(['identify', os.path.join(dirpathTempFolder.name, 'source.png')], shell=shell, stdout=subprocess.PIPE).communicate()[0].decode('UTF-8') + + image_size = file_info.split(' ')[2] + + subprocess.Popen(['convert', '-size', image_size, 'xc:black', os.path.join(dirpathTempFolder.name, 'empty.png')], shell=shell).wait() + + final_command = ['convert'] + + if self.view_c_button.isChecked(): + final_command.append(os.path.join(dirpathTempFolder.name, 'separated') + area_to_export.replace(' ', '') + 'C' + ".png") + else: + final_command.append(os.path.join(dirpathTempFolder.name, 'empty.png')) + + if self.view_m_button.isChecked(): + final_command.append(os.path.join(dirpathTempFolder.name, 'separated') + area_to_export.replace(' ', '') + 'M' + ".png") + else: + final_command.append(os.path.join(dirpathTempFolder.name, 'empty.png')) + + if self.view_y_button.isChecked(): + final_command.append(os.path.join(dirpathTempFolder.name, 'separated') + area_to_export.replace(' ', '') + 'Y' + ".png") + else: + final_command.append(os.path.join(dirpathTempFolder.name, 'empty.png')) + + if self.view_k_button.isChecked(): + final_command.append(os.path.join(dirpathTempFolder.name, 'separated') + area_to_export.replace(' ', '') + 'K' + ".png") + else: + final_command.append(os.path.join(dirpathTempFolder.name, 'empty.png')) + + if not os.path.isfile(os.path.join(dirpathTempFolder.name, 'empty.png')): + inkex.utils.debug("Error. Missing empty.png") + + final_command.extend(['-set', 'colorspace', 'cmyk']) + final_command.extend(['-combine', os.path.join(dirpathTempFolder.name, 'result.tiff')]) + subprocess.Popen(final_command, shell=shell).wait() + + self.generate_preview() + + def area_to_export(self): + if self.area_to_export_choice.currentIndex() == 1: + return 'export-area-drawing' + + elif self.area_to_export_choice.currentIndex() == 2: + if self.area_to_export_idonly_check.isChecked(): + return 'export-id:' + str(self.area_to_export_id_name.text()) + 'export-id-only' + else: + return 'export-id:' + str(self.area_to_export_id_name.text()) + + elif self.area_to_export_choice.currentIndex() == 3: + return 'export-area:' + str(self.x0_value.value()) + ':' + str(self.y0_value.value()) + ':' + str(self.x1_value.value()) + ':' + str(self.y1_value.value()) + + else: + return 'export-area-page' + + def change_area_to_export(self): + self.x0_value.setVisible(False) + self.y0_value.setVisible(False) + self.x1_value.setVisible(False) + self.y1_value.setVisible(False) + self.area_to_export_id_title.setVisible(False) + self.area_to_export_id_name.setVisible(False) + self.area_to_export_idonly_check.setVisible(False) + + if self.area_to_export_choice.currentIndex() == 2: + self.area_to_export_id_name.setText(selected_object) + self.area_to_export_id_title.setVisible(True) + self.area_to_export_id_name.setVisible(True) + self.area_to_export_idonly_check.setVisible(True) + + elif self.area_to_export_choice.currentIndex() == 3: + self.x0_value.setVisible(True) + self.y0_value.setVisible(True) + self.x1_value.setVisible(True) + self.y1_value.setVisible(True) + + cmd = self.area_to_export() + ';export-dpi:' + str(self.dpi_choice.value()) + ';export-background-opacity:1;export-filename:' + os.path.join(dirpathTempFolder.name, 'source.png') + ';export-do' + cli_output = inkscape(os.path.join(dirpathTempFolder.name, 'original.svg'), actions=cmd) + #inkex.utils.debug(cmd) + if len(cli_output) > 0: + self.debug(_("Inkscape returned the following output when trying to run the file export; the file export may still have worked:")) + self.debug(cli_output) + + subprocess.Popen(['convert', os.path.join(dirpathTempFolder.name, 'source.png'), os.path.join(dirpathTempFolder.name, 'source.tiff')], shell=shell).wait() + + if not os.path.isfile(os.path.join(dirpathTempFolder.name, 'source.tiff')): + inkex.utils.debug("Error. Missing source.tiff") + + self.generate_preview() + + def zoom_out(self): + self.preview_zoom += 0.1 + self.generate_preview() + + if int(self.preview_zoom * 100) == 200: + self.zoom_out_button.setEnabled(False) + self.zoom_in_button.setEnabled(True) + + self.preview_zoom_title.setText(str(int(self.preview_zoom * 100)) + '%') + + def zoom_in(self): + self.preview_zoom -= 0.1 + self.generate_preview() + + if int(self.preview_zoom * 100) == 10: + self.zoom_in_button.setEnabled(False) + self.zoom_out_button.setEnabled(True) + + self.preview_zoom_title.setText(str(int(self.preview_zoom * 100)) + '%') + + def cut_marks_insert_change(self): + if self.prepress_paper_cutmarks_check.isChecked(): + self.prepress_paper_cutmarks_strokewidth_label.setEnabled(True) + self.prepress_paper_cutmarks_strokewidth_value.setEnabled(True) + self.prepress_paper_cutmarks_strokewidth_choice.setEnabled(True) + self.prepress_paper_cutmarks_bleedsize_label.setEnabled(True) + self.prepress_paper_cutmarks_bleedsize_value.setEnabled(True) + self.prepress_paper_cutmarks_bleedsize_choice.setEnabled(True) + self.prepress_paper_cutmarks_marksize_label.setEnabled(True) + self.prepress_paper_cutmarks_marksize_value.setEnabled(True) + self.prepress_paper_cutmarks_marksize_choice.setEnabled(True) + self.prepress_paper_cutmarks_inside_check.setEnabled(True) + + else: + self.prepress_paper_cutmarks_strokewidth_label.setEnabled(False) + self.prepress_paper_cutmarks_strokewidth_value.setEnabled(False) + self.prepress_paper_cutmarks_strokewidth_choice.setEnabled(False) + self.prepress_paper_cutmarks_bleedsize_label.setEnabled(False) + self.prepress_paper_cutmarks_bleedsize_value.setEnabled(False) + self.prepress_paper_cutmarks_bleedsize_choice.setEnabled(False) + self.prepress_paper_cutmarks_marksize_label.setEnabled(False) + self.prepress_paper_cutmarks_marksize_value.setEnabled(False) + self.prepress_paper_cutmarks_marksize_choice.setEnabled(False) + self.prepress_paper_cutmarks_inside_check.setEnabled(False) + + self.generate_preview() + + def format_preview_change(self): + if self.format_preview_check.isChecked(): + self.resize(950, 600) + self.setMaximumSize(QtCore.QSize(950, 600)) + self.setMinimumSize(QtCore.QSize(950, 600)) + self.preview_panel.setVisible(True) + self.option_box.setGeometry(320, 120, 620, 435) + self.format_title.setGeometry(320, 70, 200, 15) + self.format_choice.setGeometry(320, 85, 200, 25) + self.export_button.setGeometry(740, 560, 200, 30) + self.format_preview_check.setGeometry(540, 85, 200, 25) + else: + self.resize(640, 600) + self.setMaximumSize(QtCore.QSize(640, 600)) + self.setMinimumSize(QtCore.QSize(640, 600)) + self.preview_panel.setVisible(False) + self.option_box.setGeometry(10, 120, 620, 435) + self.format_title.setGeometry(10, 70, 200, 15) + self.format_choice.setGeometry(10, 85, 200, 25) + self.export_button.setGeometry(430, 560, 200, 30) + self.format_preview_check.setGeometry(230, 85, 200, 25) + + self.move(int((QtWidgets.QDesktopWidget().screenGeometry().width()-self.geometry().width())/2), int((QtWidgets.QDesktopWidget().screenGeometry().height()-self.geometry().height())/2)) + + def export(self): + self.location_path = QtWidgets.QFileDialog.getSaveFileName(self, _(u"Save image"), os.environ.get('HOME', None), list_of_export_formats[self.format_choice.currentIndex()], options=QtWidgets.QFileDialog.DontConfirmOverwrite) + + if not self.format_preview_check.isChecked(): + self.generate_final_file() + + if not str(self.location_path) == '': + result_imp = os.path.join(dirpathTempFolder.name, 'result-imp.' + list_of_export_formats[self.format_choice.currentIndex()].lower()) + target_imp = os.path.abspath(self.location_path[0] + "." + self.location_path[1].lower()) + if not os.path.isfile(result_imp): + inkex.utils.debug("Error. No result generated to export. The following files were created in temp dir:") + inkex.utils.debug(os.listdir(dirpathTempFolder.name)) + else: + shutil.copy2(result_imp, target_imp) + + def change_icc_dir(self): + self.icc_dir_textbox.setText(QtWidgets.QFileDialog.getExistingDirectory(self, 'Select Folder')) + + app = QtWidgets.QApplication(sys.argv) + app.main = mainWindow() + getattr(app.main, "raise")() + app.main.show() + app.main.activateWindow() #bring to front (required for Windows; but not for Linux) + sys.exit(app.exec_()) + + + except Exception as e: + self.msg(e) + finally: + #inkex.utils.debug(os.listdir(dirpathTempFolder.name)) + dirpathTempFolder.cleanup() #close temp dir + +if __name__ == '__main__': + OutputPro().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/output_pro/outputpro/__init__.py b/extensions/fablabchemnitz/output_pro/outputpro/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/extensions/fablabchemnitz/output_pro/outputpro/alpha.png b/extensions/fablabchemnitz/output_pro/outputpro/alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..c30c1c2a24256937a7a9de7bff6724c2f9f5aa41 GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4u@pObhHwBu4M$1`kk47*5m^jW ze;tGwoit`w00kvWTq8 0: + inkex.utils.debug(cli_output) + #inkex.utils.debug(os.listdir(temp_dir)) + \ No newline at end of file diff --git a/extensions/fablabchemnitz/output_pro/outputpro/cutmarks.py b/extensions/fablabchemnitz/output_pro/outputpro/cutmarks.py new file mode 100644 index 0000000..5ddbf16 --- /dev/null +++ b/extensions/fablabchemnitz/output_pro/outputpro/cutmarks.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +import subprocess +import os + +def generate_final_file(isvector, hide_inside_marks, colormode, width, height, space, strokewidth, bleedsize, marksize, temp_dir): + if not isvector: + + if "nt" in os.name: + shell = True + else: + shell = False + + command = [] + final_command = ['convert'] + + for color in colormode: + command.append('convert') + command.append('-size') + command.append(str(sum(width) + (marksize*2) + (space * (len(width) -1))) + 'x' + str(sum(height) + (marksize*2) + (space * (len(height) -1)))) + command.append('xc:white') + command.append('-stroke') + command.append('black') + command.append('-strokewidth') + command.append(str(strokewidth)) + + width_value = 0 + number_of_column = 1 + + for column in width: + height_value = 0 + number_of_line = 1 + + for line in height: + with open(os.path.join(temp_dir, 'str.txt'), 'a') as f: + f.write(str(width.index(column))) + + if not hide_inside_marks or (hide_inside_marks and number_of_column == 1): + command.append('-draw') + command.append('line ' + str(width_value + marksize) + ',' + str(height_value + marksize + bleedsize) + ', ' + str(width_value) + ',' + str(height_value + marksize + bleedsize)) + command.append('-draw') + command.append('line ' + str(width_value + marksize) + ',' + str(height_value + line + marksize - bleedsize) + ', ' + str(width_value) + ',' + str(height_value + line + marksize - bleedsize)) + + if not hide_inside_marks or (hide_inside_marks and number_of_line == 1): + command.append('-draw') + command.append('line ' + str(width_value + marksize + bleedsize) + ',' + str(height_value + marksize) + ', ' + str(width_value + marksize + bleedsize) + ',' + str(height_value)) + command.append('-draw') + command.append('line ' + str(width_value + column + marksize - bleedsize) + ',' + str(height_value + marksize) + ', ' + str(width_value + column + marksize - bleedsize) + ',' + str(height_value)) + + if not hide_inside_marks or (hide_inside_marks and number_of_column == len(width)): + command.append('-draw') + command.append('line ' + str(width_value + marksize + column) + ',' + str(height_value + marksize + bleedsize) + ', ' + str(width_value + (marksize*2) + column) + ',' + str(height_value + marksize + bleedsize)) + command.append('-draw') + command.append('line ' + str(width_value + marksize + column) + ',' + str(height_value + line + marksize - bleedsize) + ', ' + str(width_value + (marksize*2) + column) + ',' + str(height_value + marksize + line - bleedsize)) + + if not hide_inside_marks or (hide_inside_marks and number_of_line == len(height)): + command.append('-draw') + command.append('line ' + str(width_value + marksize + bleedsize) + ',' + str(height_value + line + marksize) + ', ' + str(width_value + marksize + bleedsize) + ',' + str(height_value + line + (marksize*2))) + command.append('-draw') + command.append('line ' + str(width_value + column + marksize - bleedsize) + ',' + str(height_value + line + marksize) + ', ' + str(width_value + marksize + column - bleedsize) + ',' + str(height_value + line + (marksize*2))) + + height_value += line + space + number_of_line += 1 + width_value += column + space + number_of_column += 1 + command.append(os.path.join(temp_dir, 'cut_mark_' + color + '.png')) + subprocess.Popen(command, shell=shell).wait() + del command[:] + + command.append('convert') + command.append(os.path.join(temp_dir, 'cut_mark_' + color + '.png')) + command.append('-colorspace') + command.append(str(colormode).lower()) + command.append('-channel') + command.append('K') + command.append('-separate') + command.append(os.path.join(temp_dir, 'cut_mark_' + color + '.png')) + subprocess.Popen(command, shell=shell).wait() + del command[:] + + final_command.append(os.path.join(temp_dir, 'cut_mark_' + color + '.png')) + + final_command.extend(['-set', 'colorspace', colormode, '-combine', os.path.join(temp_dir, 'cut_mark.tiff')]) + subprocess.Popen(final_command, shell=shell).wait() diff --git a/extensions/fablabchemnitz/output_pro/outputpro/preview_mask.png b/extensions/fablabchemnitz/output_pro/outputpro/preview_mask.png new file mode 100644 index 0000000000000000000000000000000000000000..9932f773c7fede6c8a2b59cdd4db732acf1c3724 GIT binary patch literal 2029 zcmW-i4Nw&48OIOJ5yeH5G!`#nk{)H%gF2GvhuU! z^YZGQVh! zJY1Q#^_7x_i;1Zn&GZg~G5CXh`#xE{;US0l+L7XGPZs>t`}O^WFLoHV|7T%g;hVCr zmNnJq=H^~|?!Py*rpw=Sm$sWtR&>+9i&|lxx-CBAHd1pF=&{g}ec;c{VGv)~lmo`f0lm8Fg{oq19IEu+=~ePBz4~<#RQ>&b zfolD_&9IeHsaM)ws51KRK$Y?fy;`yts@W?%RD+lQ3RPyMUadHxS3f%dRrck-LA7#T zua<6C;X%LGryHlwyWTOI?hRLf*kB)q^PObp!Ou-=g!8@BmJ82;Swj1pe%Ok|cZ2@h ztQq1pZ-{0?mE#|WY7FZY`#-%xR;U`iuqDKILDl&|9I9u0`qm~dYzbpydUbVCufEi` zP7J}88ZUtA$_M(r)+O|<7lvR<9UIrHtNOjxsnb8ERwN(hKT0!E7(FN#tQr2r3sSHN z&$FTCpgLXSHZEnow}~Y2UlA#QJS;=LA`i#Rji7OohtW960W`oe6&MY0OvMPgOY$(f zOL72>S($!}##p9*1dZ94{$Vu6G5rBFZ)GwtnrE4e5j1aOGKSGS$7BRh+{z4&qBzS8 z22tF`433~U#|#G0qLs-WMT;zx9Yl*ZCVK=ea!hsrUAGJO++tfun>G5=6)E@~SJv84 z^TW7ux7&D7ogSoCJ}wocM$EQl;tY@k=gVr zXUXLlx065V6sS?uFszw9;zFy$KJNdV4mr%OaE?3t2$r9sl)G5&qm%(GbChxn%YI79 z#Il7_?qJzVDOa$ZLn$sS4^hgqSbmIB=CNEvDZj*WJEc@&`Hz&c9?R*J@;R3GQp)>S z?xqw6mgAK2B$iE-GJ|EFQp&L0NhuL5-=dU!EU%@MNh}|rlwvIRP)Z2PpHa%ISbo%O zs_LTqtjqzMWxGRo(aE=7*BVBU`Mk97UB9Em9ex7K=hIBza+d9fg{N7|9lM~?lU5Y% zqB$0|bcTFYJ*@ z$^48&lleJ`Ci8bCn#_A8+RlIMmOd@#KlVtUlH4inC%H%3Px2Y*63OSJOC-N5ts%Kr zT4U#}$)OV=WMA$6ce5B2sJ}1wH!qlPEp-_W+0gSi<+@YoRq58K_Lj|J?v=vGZkkyY zLH4y}z|5JIgt=wFf|*pp17*NtGZf)lWx&)3m}~NZ8zNwC&Ij&^fVn>(_<97)Q~AJE zlVCnk419hP%$JIRKb!>fU@@?2l9)~F$Z!5-TTsI(_nmW67k%$fTF7RZ^?v`1lMg%< zB4X2FcY3Fo^ec*E{7XJda|kZu>WNdp={mxT6R!iOorL=)P6DTE2|t;54LDs+*ii_6 zM-liPCE$1P;7=8TKUD<&R0;S~Jovw_{I^!e^s9V9*5pn=)`_IV%>>F3X0nR>vNAFua1k#t^LjC)y7BQ*j{?31Fb{Tii9!&WWw`t?X Tibu&`vmtL=!PbE-_U8Ws)lrPV literal 0 HcmV?d00001 diff --git a/extensions/fablabchemnitz/output_pro/outputpro/top.png b/extensions/fablabchemnitz/output_pro/outputpro/top.png new file mode 100644 index 0000000000000000000000000000000000000000..25dce7449645643d2d83af4d51350a1cfce9ba4b GIT binary patch literal 70828 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�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 + + Paths To OpenSCAD + fablabchemnitz.de.paths_to_openscad + + + {NAME}.scad + 5.0 + true + false + false + false + false + + + 0.2 + 0 + + + + + + + + + 1.0 + 100.0 + + + + + + + false + + + + + openscad "{NAME}.scad" + openscad "{NAME}.scad" -o "{NAME}.stl" + cura "{NAME}.stl" & + + + + + + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.py b/extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.py new file mode 100644 index 0000000..94fc69e --- /dev/null +++ b/extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.py @@ -0,0 +1,1487 @@ +#!/usr/bin/env python3 +# +# paths2openscad.py +# +# This is an Inkscape extension to output paths to extruded OpenSCAD polygons +# The Inkscape objects must first be converted to paths (Path > Object to +# Path). Some paths may not work well -- the paths have to be polygons. As +# such, paths derived from text may meet with mixed results. + +# Written by Daniel C. Newman ( dan dot newman at mtbaldy dot us ) +# +# 2020-06-18 +# Updated by Sillyfrog (https://github.com/sillyfrog) to support +# Inkscape v1.0 (exclusively, prior versions) are no longer supported). +# Updated to run under python3 now python2 is end of life. +# +# 10 June 2012 +# +# 15 June 2012 +# Updated by Dan Newman to handle a single level of polygon nesting. +# This is sufficient to handle most fonts. +# If you want to nest two polygons, combine them into a single path +# within Inkscape with "Path > Combine Path". +# +# 15 August 2014 +# Updated by Josef Skladanka to automatically set extruded heights +# +# 2017-03-11, juergen@fabmail.org +# 0.12 parse svg width="400mm" correctly. Came out downscaled by 3... +# +# 2017-04-08, juergen@fabmail.org +# 0.13 allow letter 'a' prefix on zsize values for anti-matter. +# All anti-matter objects are subtracted from all normal objects. +# raise: Offset along Z axis, to make cut-outs and balconies. +# Refactored object_merge_extrusion_values() from convertPath(). +# Inheriting extrusion values from enclosing groups. +# +# 2017-04-10, juergen@fabmail.org +# 0.14 Started merging V7 outline mode by Neon22. +# (http://www.thingiverse.com/thing:1065500) +# Toplevel object from http://www.thingiverse.com/thing:1286041 +# is already included. +# +# 2017-04-16, juergen@fabmail.org +# 0.15 Fixed https://github.com/fablabnbg/inkscape-paths2openscad/ +# issues/1#issuecomment-294257592 +# Line width of V7 code became a minimum line width, +# rendering is now based on stroke-width +# Refactored LengthWithUnit() from getLength() +# Finished merge with v7 code. +# Subpath in subpath are now handled very nicely. +# Added msg_extrude_by_hull_and_paths() outline mode with nested paths. +# +# 2017-06-12, juergen@fabmail.org +# 0.16 Feature added: scale: XXX to taper the object while extruding. + +# 2017-06-15, juergen@fabmail.org +# 0.17 scale is now centered on each path. and supports an optional second +# value for explicit Y scaling. Renamed the autoheight command line +# option to 'parsedesc' with default true. Renamed dict auto to +# extrusion. Rephrased all prose to refer to extrusion syntax rather +# than auto zsize. +# 2017-06-18, juergen@fabmail.org +# 0.18 pep8 relaxed. all hard 80 cols line breaks removed. +# Refactored the commands into a separate tab in the inx. +# Added 'View in OpenSCAD' feature with pidfile for single instance. +# +# 2017-08-10, juergen@fabmail.org +# 0.19 fix style="" elements. +# +# 2017-11-14, juergen@fabmail.org +# 0.20 do not traverse into objects with style="display:none" +# some precondition checks had 'pass' but should have 'continue'. +# +# 2018-01-21, juergen@fabmail.org +# 0.21 start a new openscad instance if the command has changed. +# +# 2018-01-27, juergen@fabmail.org +# 0.22 command comparison fixed. do not use 0.21 ! +# +# 2018-02-18, juergen@fabmail.org +# 0.23 fixed rect with x=0 not rendered. +# FIXME: should really use inksvg.py here too! +# +# 2018.09-09, juergen@fabmail.org +# 0.24 merged module feature, renamed Height,Raise to Zsize,Zoffset +# +# 2019-01-18, juergen@fabmail.org +# 0.25 Allow Depth,Offset instead of Zsize,Zoffset +# Simplify the syntax description page. +# Added parameter line_width_scale. +# Added parameter chamfer, and module chamfer_sphere for doing minkowski +# +# 2020-03-12, juergen@fabmail.org +# 0.26 DEB: relax dependency on 'openscad' to 'openscad | bash' +# +# 2020-04-05, juergen@fabmail.org +# 0.27 Make pep8 happy again. Give proper error message, when file was not saved. +# +# CAUTION: keep the version number in sync with paths2openscad.inx about page + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import os +import sys +import os.path +import inkex +import inkex.paths +import inkex.bezier +from inkex.transforms import Transform +import re +import time +import string +import tempfile +import gettext +import subprocess + +VERSION = "0.27" # CAUTION: Keep in sync with all *.inx files +DEFAULT_WIDTH = 100 +DEFAULT_HEIGHT = 100 +# Parse all these as 56.7 mm zsize: +# "path1234_56_7_mm", "pat1234____57.7mm", "path1234_57.7__mm" +# +# The verbs Height and Raise are deprecated. Use Zsize and Zoffset, (or Depth and Offset) instead. +RE_AUTO_ZSIZE_ID = re.compile(r".*?_+([aA]?\d+(?:[_\.]\d+)?)_*mm$") +RE_AUTO_ZSIZE_DESC = re.compile( + r"^(?:[Hh]eight|[Dd]epth|[Zz]-?size):\s*([aA]?\d+(?:\.\d+)?) ?mm$", re.MULTILINE +) +RE_AUTO_SCALE_DESC = re.compile( + r"^(?:sc|[Ss]cale|[Tt]aper):\s*(\d+(?:\.\d+)?(?: ?, ?\d+(?:\.\d+)?)?) ?%$", + re.MULTILINE, +) +RE_AUTO_ZOFFSET_DESC = re.compile( + r"^(?:[Rr]aise|[Zz]-?offset|[Oo]ffset):\s*(\d+(?:\.\d+)?) ?mm$", re.MULTILINE +) +DESC_TAGS = ["desc", inkex.addNS("desc", "svg")] + +# CAUTION: keep these defaults in sync with paths2openscad.inx +INX_SCADVIEW = os.getenv("INX_SCADVIEW", "openscad \"{NAME}.scad\"") +INX_SCAD2STL = os.getenv("INX_SCAD2STL", "openscad \"{NAME}.scad\" -o \"{NAME}.stl\"") +INX_STL_POSTPROCESSING = os.getenv("INX_STL_POSTPROCESSING", "cura \"{NAME}.stl\" &") + + +def IsProcessRunning(pid): + """ + Windows code from https://stackoverflow.com/questions/7647167/check-if-a-process-is-running-in-python-in-linux-unix + """ + sys_platform = sys.platform.lower() + if sys_platform.startswith("win"): + with subprocess.Popen(r'tasklist.exe /NH /FI "PID eq %d"' % (pid), shell=True, stdout=subprocess.PIPE) as ps: + output = ps.stdout.read() + ps.wait() + if str(pid) in output: + return True + return False + else: + # OSX sys_platform.startswith('darwin'): + # and Linux + try: + os.kill(pid, 0) + return True + except OSError: + return False + + +def parseLengthWithUnits(str, default_unit="px"): + """ + Parse an SVG value which may or may not have units attached + This version is greatly simplified in that it only allows: no units, + units of px, and units of %. Everything else, it returns None for. + There is a more general routine to consider in scour.py if more + generality is ever needed. + With inkscape 0.91 we need other units too: e.g. svg:width="400mm" + """ + + u = default_unit + s = str.strip() + if s[-2:] in ("px", "pt", "pc", "mm", "cm", "in", "ft"): + u = s[-2:] + s = s[:-2] + elif s[-1:] in ("m", "%"): + u = s[-1:] + s = s[:-1] + + try: + v = float(s) + except Exception: + return None, None + + return v, u + + +def pointInBBox(pt, bbox): + """ + Determine if the point pt=[x, y] lies on or within the bounding + box bbox=[xmin, xmax, ymin, ymax]. + """ + + # if ( x < xmin ) or ( x > xmax ) or ( y < ymin ) or ( y > ymax ) + if (pt[0] < bbox[0]) or (pt[0] > bbox[1]) or (pt[1] < bbox[2]) or (pt[1] > bbox[3]): + return False + else: + return True + + +def bboxInBBox(bbox1, bbox2): + """ + Determine if the bounding box bbox1 lies on or within the + bounding box bbox2. NOTE: we do not test for strict enclosure. + + Structure of the bounding boxes is + + bbox1 = [ xmin1, xmax1, ymin1, ymax1 ] + bbox2 = [ xmin2, xmax2, ymin2, ymax2 ] + """ + + # if ( xmin1 < xmin2 ) or ( xmax1 > xmax2 ) or + # ( ymin1 < ymin2 ) or ( ymax1 > ymax2 ) + + if ( + (bbox1[0] < bbox2[0]) + or (bbox1[1] > bbox2[1]) + or (bbox1[2] < bbox2[2]) + or (bbox1[3] > bbox2[3]) + ): + return False + else: + return True + + +def pointInPoly(p, poly, bbox=None): + """ + Use a ray casting algorithm to see if the point p = [x, y] lies within + the polygon poly = [[x1,y1],[x2,y2],...]. Returns True if the point + is within poly, lies on an edge of poly, or is a vertex of poly. + """ + + if (p is None) or (poly is None): + return False + + # Check to see if the point lies outside the polygon's bounding box + if bbox is not None: + if not pointInBBox(p, bbox): + return False + + # Check to see if the point is a vertex + if p in poly: + return True + + # Handle a boundary case associated with the point + # lying on a horizontal edge of the polygon + x = p[0] + y = p[1] + p1 = poly[0] + p2 = poly[1] + for i in range(len(poly)): + if i != 0: + p1 = poly[i - 1] + p2 = poly[i] + if ( + (y == p1[1]) + and (p1[1] == p2[1]) + and (x > min(p1[0], p2[0])) + and (x < max(p1[0], p2[0])) + ): + return True + + n = len(poly) + inside = False + + p1_x, p1_y = poly[0] + for i in range(n + 1): + p2_x, p2_y = poly[i % n] + if y > min(p1_y, p2_y): + if y <= max(p1_y, p2_y): + if x <= max(p1_x, p2_x): + if p1_y != p2_y: + intersect = p1_x + (y - p1_y) * (p2_x - p1_x) / (p2_y - p1_y) + if x <= intersect: + inside = not inside + else: + inside = not inside + p1_x, p1_y = p2_x, p2_y + + return inside + + +def polyInPoly(poly1, bbox1, poly2, bbox2): + """ + Determine if polygon poly2 = [[x1,y1],[x2,y2],...] + contains polygon poly1. + + The bounding box information, bbox=[xmin, xmax, ymin, ymax] + is optional. When supplied it can be used to perform rejections. + Note that one bounding box containing another is not sufficient + to imply that one polygon contains another. It's necessary, but + not sufficient. + """ + + # See if poly1's bboundin box is NOT contained by poly2's bounding box + # if it isn't, then poly1 cannot be contained by poly2. + + if (bbox1 is not None) and (bbox2 is not None): + if not bboxInBBox(bbox1, bbox2): + return False + + # To see if poly1 is contained by poly2, we need to ensure that each + # vertex of poly1 lies on or within poly2 + + for p in poly1: + if not pointInPoly(p, poly2, bbox2): + return False + + # Looks like poly1 is contained on or in Poly2 + + return True + + +def subdivideCubicPath(sp, flat, i=1): + """ + [ Lifted from eggbot.py with impunity ] + + Break up a bezier curve into smaller curves, each of which + is approximately a straight line within a given tolerance + (the "smoothness" defined by [flat]). + + This is a modified version of cspsubdiv.cspsubdiv(): rewritten + because recursion-depth errors on complicated line segments + could occur with cspsubdiv.cspsubdiv(). + """ + + while True: + while True: + if i >= len(sp): + return + + p0 = sp[i - 1][1] + p1 = sp[i - 1][2] + p2 = sp[i][0] + p3 = sp[i][1] + + b = (p0, p1, p2, p3) + + if inkex.bezier.maxdist(b) > flat: + break + + i += 1 + + one, two = inkex.bezier.beziersplitatt(b, 0.5) + sp[i - 1][2] = one[1] + sp[i][0] = two[2] + p = [one[2], one[3], two[1]] + sp[i:1] = [p] + + +def msg_linear_extrude(id, prefix): + msg = ( + " translate (%s_%d_center) linear_extrude(height=h, convexity=10, scale=0.01*s)\n" + + " translate (-%s_%d_center) polygon(%s_%d_points);\n" + ) + return msg % (id, prefix, id, prefix, id, prefix) + + +def msg_linear_extrude_by_paths(id, prefix): + msg = ( + " translate (%s_%d_center) linear_extrude(height=h, convexity=10, scale=0.01*s)\n" + + " translate (-%s_%d_center) polygon(%s_%d_points, %s_%d_paths);\n" + ) + return msg % (id, prefix, id, prefix, id, prefix, id, prefix) + + +def msg_extrude_by_hull(id, prefix): + msg = ( + " for (t = [0: len(%s_%d_points)-2]) {\n" % (id, prefix) + + " hull() {\n" + + " translate(%s_%d_points[t]) \n" % (id, prefix) + + " cylinder(h=h, r=w/2, $fn=res);\n" + + " translate(%s_%d_points[t + 1]) \n" % (id, prefix) + + " cylinder(h=h, r=w/2, $fn=res);\n" + + " }\n" + + " }\n" + ) + return msg + + +def msg_extrude_by_hull_and_paths(id, prefix): + msg = ( + " for (p = [0: len(%s_%d_paths)-1]) {\n" % (id, prefix) + + " pp = %s_%d_paths[p];\n" % (id, prefix) + + " for (t = [0: len(pp)-2]) {\n" + + " hull() {\n" + + " translate(%s_%d_points[pp[t]])\n" % (id, prefix) + + " cylinder(h=h, r=w/2, $fn=res);\n" + + " translate(%s_%d_points[pp[t+1]])\n" % (id, prefix) + + " cylinder(h=h, r=w/2, $fn=res);\n" + + " }\n" + + " }\n" + + " }\n" + ) + return msg + + +def remove_umlaut(string): + """ + Removes umlauts from strings and replaces them with the letter+e convention + :param string: string to remove umlauts from + :return: unumlauted string + """ + u = 'ü'.encode() + U = 'Ü'.encode() + a = 'ä'.encode() + A = 'Ä'.encode() + o = 'ö'.encode() + O = 'Ö'.encode() + ss = 'ß'.encode() + + string = string.encode() + string = string.replace(u, b'ue') + string = string.replace(U, b'Ue') + string = string.replace(a, b'ae') + string = string.replace(A, b'Ae') + string = string.replace(o, b'oe') + string = string.replace(O, b'Oe') + string = string.replace(ss, b'ss') + + string = string.decode('utf-8') + return string + +class PathsToOpenSCAD(inkex.EffectExtension): + + def add_arguments(self, pars): + inkex.localization.localize() # does not help for localizing my *.inx file + + pars.add_argument( "--tab", default="splash", help="The active tab when Apply was pressed", ) + pars.add_argument( "--smoothness", type=float, default=float(0.2), help="Curve smoothing (less for more)", ) + pars.add_argument( "--chamfer", type=float, default=float(1.), help="Add a chamfer radius, displacing all object walls outwards [mm]", ) + pars.add_argument( "--chamfer_fn", type=int, default=int(4), help="Chamfer precision ($fn when generating the minkowski sphere)", ) + pars.add_argument( "--zsize", default="5", help="Depth (Z-size) [mm]", ) + pars.add_argument( "--min_line_width", type=float, default=float(1), help="Line width for non closed curves [mm]", ) + pars.add_argument( "--line_width_scale_perc", type=float, default=float(1), help="Percentage of SVG line width. Use e.g. 26.46 to compensate a px/mm confusion. Default: 100 [%]", ) + pars.add_argument( "--line_fn", type=int, default=int(4), help="Line width precision ($fn when constructing hull)", ) + pars.add_argument( "--force_line", type=inkex.utils.Boolean, default=False, help="Force outline mode.", ) + pars.add_argument( "--fname", default="{NAME}.scad", help="openSCAD output file derived from the svg file name.", ) + pars.add_argument( "--parsedesc", type=inkex.utils.Boolean, default=True, help="Parse zsize and other parameters from object descriptions", ) + pars.add_argument( "--scadview", type=inkex.utils.Boolean, default=False, help="Open the file with openscad ( details see --scadviewcmd option )", ) + pars.add_argument( "--scadviewcmd", default=INX_SCADVIEW, help="Command used start an openscad viewer. Use {SCAD} for the openSCAD input.", ) + pars.add_argument( "--scad2stl", type=inkex.utils.Boolean, default=False, help="Also convert to STL ( details see --scad2stlcmd option )", ) + pars.add_argument( "--scad2stlcmd", default=INX_SCAD2STL, help="Command used to convert to STL. You can use {NAME}.scad for the openSCAD file to read and " + + "{NAME}.stl for the STL file to write.", ) + pars.add_argument( "--stlpost", type=inkex.utils.Boolean, default=False, help="Start e.g. a slicer. This implies the --scad2stl option. ( see --stlpostcmd )", ) + pars.add_argument( "--stlpostcmd", default=INX_STL_POSTPROCESSING, help="Command used for post processing an STL file (typically a slicer). You can use {NAME}.stl for the STL file.", ) + pars.add_argument( "--stlmodule", type=inkex.utils.Boolean, default=False, help="Output configured to comment out final rendering line, to create a module file for import.", ) + + self.userunitsx = 1.0 # Move to pure userunits per mm for v1.0 + self.userunitsy = 1.0 + self.px_used = False # raw px unit depends on correct dpi. + self.cx = float(DEFAULT_WIDTH) / 2.0 + self.cy = float(DEFAULT_HEIGHT) / 2.0 + self.xmin, self.xmax = (1.0E70, -1.0E70) + self.ymin, self.ymax = (1.0E70, -1.0E70) + + # Dictionary of paths we will construct. It's keyed by the SVG node + # it came from. Such keying isn't too useful in this specific case, + # but it can be useful in other applications when you actually want + # to go back and update the SVG document + self.paths = {} + + # Output file handling + self.call_list = [] + self.call_list_neg = [] # anti-matter (holes via difference) + self.pathid = int(0) + + # Output file + outfile = None + + # For handling an SVG viewbox attribute, we will need to know the + # values of the document's width and height attributes as well + # as establishing a transform from the viewbox to the display. + + self.docWidth = float(DEFAULT_WIDTH) + self.docHeight = float(DEFAULT_HEIGHT) + self.docTransform = Transform(None) + + # Dictionary of warnings issued. This to prevent from warning + # multiple times about the same problem + self.warnings = {} + + def getLength(self, name, default): + + """ + Get the attribute with name "name" and default value "default" + Parse the attribute into a value and associated units. Then, accept + units of cm, ft, in, m, mm, pc, or pt. Convert to pixels. + + Note that SVG defines 90 px = 1 in = 25.4 mm. + Note: Since inkscape 0.92 we use the CSS standard of 96 px = 1 in. + """ + str = self.document.getroot().get(name) + if str: + return self.LengthWithUnit(str) + else: + # No width specified; assume the default value + return float(default) + + def LengthWithUnit(self, strn, default_unit="px"): + v, u = parseLengthWithUnits(strn, default_unit) + if v is None: + # Couldn't parse the value + return None + elif u == "mm": + return float(v) * (self.userunitsx) + elif u == "cm": + return float(v) * (self.userunitsx * 10.0) + elif u == "m": + return float(v) * (self.userunitsx * 1000.0) + elif u == "in": + return float(v) * self.userunitsx * 25.4 + elif u == "ft": + return float(v) * 12.0 * self.userunitsx * 25.4 + elif u == "pt": + # Use modern "Postscript" points of 72 pt = 1 in instead + # of the traditional 72.27 pt = 1 in + return float(v) * (self.userunitsx * 25.4 / 72.0) + elif u == "pc": + return float(v) * (self.userunitsx * 25.4 / 6.0) + elif u == "px": + self.px_used = True + return float(v) + else: + # Unsupported units + return None + + def getDocProps(self): + """ + Get the document's height and width attributes from the tag. + Use a default value in case the property is not present or is + expressed in units of percentages. + """ + self.inkscape_version = self.document.getroot().get( + "{http://www.inkscape.org/namespaces/inkscape}version" + ) + sodipodi_docname = self.document.getroot().get( + "{http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd}docname" + ) + if sodipodi_docname is None: + sodipodi_docname = "inkscape" + # the document was not saved. We can assume it is v1 inkscape + self.basename = re.sub(r"\.SVG", "", sodipodi_docname, flags=re.I).rsplit('/', 1)[-1] + self.docHeight = self.getLength("height", DEFAULT_HEIGHT) + self.docWidth = self.getLength("width", DEFAULT_WIDTH) + + if (self.docHeight is None) or (self.docWidth is None): + return False + else: + return True + + def handleViewBox(self): + """ + Set up the document-wide transform in the event that the document has + an SVG viewbox, which it should as of v1.0 + For details, see https://wiki.inkscape.org/wiki/index.php/Units_In_Inkscape + """ + + if self.getDocProps(): + viewbox = self.document.getroot().get("viewBox") + if viewbox: + vinfo = viewbox.strip().replace(",", " ").split() + vinfo = [float(i) for i in vinfo] + unitsx = abs(vinfo[0] - vinfo[2]) + # unitsy = abs(vinfo[1] - vinfo[3]) + self.userunitsx = self.docWidth / unitsx + # The above wiki page suggests that x and y scaling maybe different + # however in practice they are not + self.userunitsy = self.userunitsx + self.docTransform = Transform( + "scale(%f,%f)" % (self.userunitsx, self.userunitsy) + ) + + def getPathVertices(self, path, node=None, transform=None): + """ + Decompose the path data from an SVG element into individual + subpaths, each subpath consisting of absolute move to and line + to coordinates. Place these coordinates into a list of polygon + vertices. + """ + if not path: + # Path must have been devoid of any real content + return None + + # Get a cubic super path + p = inkex.paths.CubicSuperPath(path) + if (not p) or (len(p) == 0): + # Probably never happens, but... + return None + + if transform: + p = p.transform(transform) + + # Now traverse the cubic super path + subpath_list = [] + subpath_vertices = [] + + sp_xmin = None + sp_xmax = None + sp_ymin = None + sp_ymax = None + for sp in p: + + # We've started a new subpath + # See if there is a prior subpath and whether we should keep it + if len(subpath_vertices): + subpath_list.append( + [subpath_vertices, [sp_xmin, sp_xmax, sp_ymin, sp_ymax]] + ) + + subpath_vertices = [] + subdivideCubicPath(sp, float(self.options.smoothness)) + + # Note the first point of the subpath + first_point = sp[0][1] + subpath_vertices.append(first_point) + sp_xmin = first_point[0] + sp_xmax = first_point[0] + sp_ymin = first_point[1] + sp_ymax = first_point[1] + + n = len(sp) + + # Traverse each point of the subpath + for csp in sp[1:n]: + + # Append the vertex to our list of vertices + pt = csp[1] + subpath_vertices.append(pt) + + # Track the bounding box of this subpath + if pt[0] < sp_xmin: + sp_xmin = pt[0] + elif pt[0] > sp_xmax: + sp_xmax = pt[0] + if pt[1] < sp_ymin: + sp_ymin = pt[1] + elif pt[1] > sp_ymax: + sp_ymax = pt[1] + + # Track the bounding box of the overall drawing + # This is used for centering the polygons in OpenSCAD around the + # (x,y) origin + if sp_xmin < self.xmin: + self.xmin = sp_xmin + if sp_xmax > self.xmax: + self.xmax = sp_xmax + if sp_ymin < self.ymin: + self.ymin = sp_ymin + if sp_ymax > self.ymax: + self.ymax = sp_ymax + + # Handle the final subpath + if len(subpath_vertices): + subpath_list.append( + [subpath_vertices, [sp_xmin, sp_xmax, sp_ymin, sp_ymax]] + ) + + if len(subpath_list) > 0: + self.paths[node] = subpath_list + + def getPathStyle(self, node): + style = node.get("style", "") + # .get default is not reliable, ensure value is a string + if not style: + style = "" + ret = {} + # fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1 + for elem in style.split(";"): + if len(elem): + try: + (key, val) = elem.strip().split(":") + except Exception: + inkex.errormsg( + "unparsable element '{1}' in style '{0}'".format(elem, style) + ) + ret[key] = val + return ret + + def convertPath(self, node, outfile): + def object_merge_extrusion_values(extrusion, node): + + """ Parser for description and ID fields for extrusion parameters. + This recurse into parents, to inherit values from enclosing + groups. + """ + p = node.getparent() + if p is not None and p.tag in (inkex.addNS("g", "svg"), "g"): + object_merge_extrusion_values(extrusion, p) + + # let the node override inherited values + rawid = node.get("id", "") + if rawid is not None: + zsize = RE_AUTO_ZSIZE_ID.findall(rawid) + if zsize: + extrusion["zsize"] = zsize[-1].replace("_", ".") + # let description contents override id contents. + for tagname in DESC_TAGS: + desc_node = node.find("./%s" % tagname) + if desc_node is not None: + zsize = RE_AUTO_ZSIZE_DESC.findall(desc_node.text) + if zsize: + extrusion["zsize"] = zsize[-1] + zscale = RE_AUTO_SCALE_DESC.findall(desc_node.text) + if zscale: + if "," in zscale[-1]: + extrusion["scale"] = "[" + zscale[-1] + "]" + else: + extrusion["scale"] = zscale[-1] + zoffset = RE_AUTO_ZOFFSET_DESC.findall(desc_node.text) + if zoffset: + extrusion["zoffset"] = zoffset[-1] + if extrusion["zsize"][0] in ("a", "A"): + extrusion["neg"] = True + extrusion["zsize"] = extrusion["zsize"][1:] + # END object_merge_extrusion_values + + path = self.paths[node] + if (path is None) or (len(path) == 0): + return + + # Determine which polys contain which + + contains = [[] for i in range(len(path))] + contained_by = [[] for i in range(len(path))] + + for i in range(0, len(path)): + for j in range(i + 1, len(path)): + if polyInPoly(path[j][0], path[j][1], path[i][0], path[i][1]): + # subpath i contains subpath j + contains[i].append(j) + # subpath j is contained in subpath i + contained_by[j].append(i) + elif polyInPoly(path[i][0], path[i][1], path[j][0], path[j][1]): + # subpath j contains subpath i + contains[j].append(i) + # subpath i is contained in subpath j + contained_by[i].append(j) + + # Generate an OpenSCAD module for this path + rawid = node.get("id", "") + if (rawid is None) or (rawid == ""): + id = str(self.pathid) + "x" + rawid = id + self.pathid += 1 + else: + id = re.sub("[^A-Za-z0-9_]+", "", rawid) + + style = self.getPathStyle(node) + stroke_width = style.get("stroke-width", "1") + + # FIXME: works with document units == 'mm', but otherwise untested.. + stroke_width_mm = self.LengthWithUnit(stroke_width, default_unit="mm") + stroke_width_mm = str(stroke_width_mm * self.userunitsx) # px to mm + fill_color = style.get("fill", "#FFF") + if fill_color == "none": + filled = False + else: + filled = True + if filled is False and style.get("stroke", "none") == "none": + inkex.errormsg( + "WARNING: " + rawid + " has fill:none and stroke:none, object ignored." + ) + return + + # #### global data for msg_*() functions. #### + # fold subpaths into a single list of points and index paths. + prefix = 0 + for i in range(0, len(path)): + # Skip this subpath if it is contained by another one + if len(contained_by[i]) != 0: + continue + subpath = path[i][0] + bbox = path[i][1] # [xmin, xmax, ymin, ymax] + + # + polycenter = ( + id + + "_" + + str(prefix) + + "_center = [%f,%f]" + % ( + (bbox[0] + bbox[1]) * .5 - self.cx, + (bbox[2] + bbox[3]) * .5 - self.cy, + ) + ) + polypoints = id + "_" + str(prefix) + "_points = [" + # polypaths = [[0,1,2], [3,4,5]] # this path is two triangle + polypaths = id + "_" + str(prefix) + "_paths = [[" + if len(contains[i]) == 0: + # This subpath does not contain any subpaths + for point in subpath: + polypoints += "[%f,%f]," % ( + (point[0] - self.cx), + (point[1] - self.cy), + ) + polypoints = polypoints[:-1] + polypoints += "];\n" + outfile.write(polycenter + ";\n") + outfile.write(polypoints) + prefix += 1 + else: + # This subpath contains other subpaths + # collect all points into polypoints + # also collect the indices into polypaths + for point in subpath: + polypoints += "[%f,%f]," % ( + (point[0] - self.cx), + (point[1] - self.cy), + ) + count = len(subpath) + for k in range(0, count): + polypaths += "%d," % (k) + polypaths = polypaths[:-1] + "],\n\t\t\t\t[" + # The nested paths + for j in contains[i]: + for point in path[j][0]: + polypoints += "[%f,%f]," % ( + (point[0] - self.cx), + (point[1] - self.cy), + ) + for k in range(count, count + len(path[j][0])): + polypaths += "%d," % k + count += len(path[j][0]) + polypaths = polypaths[:-1] + "],\n\t\t\t\t[" + polypoints = polypoints[:-1] + polypoints += "];\n" + polypaths = polypaths[:-7] + "];\n" + # write the polys and paths + outfile.write(polycenter + ";\n") + outfile.write(polypoints) + outfile.write(polypaths) + prefix += 1 + # #### end global data for msg_*() functions. #### + + outfile.write("module poly_" + id + "(h, w, s, res=line_fn)\n{\n") + # Element is transformed to correct size, so scale is now just for the user to + # tweak after the fact + outfile.write(" scale([custom_scale_x, -custom_scale_y, 1]) union()\n {\n") + + # And add the call to the call list + # Z-size is set by the overall module parameter + # unless an extrusion zsize is parsed from the description or ID. + extrusion = {"zsize": "h", "zoffset": "0", "scale": 100.0, "neg": False} + if self.options.parsedesc is True: + object_merge_extrusion_values(extrusion, node) + + call_item = "translate ([0,0,%s]) poly_%s(%s, min_line_mm(%s), %s);\n" % ( + extrusion["zoffset"], + id, + extrusion["zsize"], + stroke_width_mm, + extrusion["scale"], + ) + + if extrusion["neg"]: + self.call_list_neg.append(call_item) + else: + self.call_list.append(call_item) + + prefix = 0 + for i in range(0, len(path)): + + # Skip this subpath if it is contained by another one + if len(contained_by[i]) != 0: + continue + + subpath = path[i][0] + bbox = path[i][1] + + if filled and not self.options.force_line: + + if len(contains[i]) == 0: + # This subpath does not contain any subpaths + poly = msg_linear_extrude(id, prefix) + else: + # This subpath contains other subpaths + poly = msg_linear_extrude_by_paths(id, prefix) + + else: # filled == False -> outline mode + + if len(contains[i]) == 0: + # This subpath does not contain any subpaths + poly = msg_extrude_by_hull(id, prefix) + else: + # This subpath contains other subpaths + poly = msg_extrude_by_hull_and_paths(id, prefix) + + outfile.write(poly) + prefix += 1 + + # End the module + outfile.write(" }\n}\n") + + def recursivelyTraverseSvg( + self, aNodeList, matCurrent=Transform(None), parent_visibility="visible" + ): + + """ + [ This too is largely lifted from eggbot.py ] + + Recursively walk the SVG document, building polygon vertex lists + for each graphical element we support. + + Rendered SVG elements: + , , , , , , + + Supported SVG elements: + , + + Ignored SVG elements: + , , , , , + processing directives + + All other SVG elements trigger an error (including ) + """ + + for node in aNodeList: + + # Ignore invisible nodes + v = node.get("visibility", parent_visibility) + if v == "inherit": + v = parent_visibility + if v == "hidden" or v == "collapse": + continue + + s = node.get("style", "") + if s == "display:none": + continue + + # First apply the current matrix transform to this node's transform + matNew = matCurrent @ Transform(node.get("transform")) + + if node.tag == inkex.addNS("g", "svg") or node.tag == "g": + + self.recursivelyTraverseSvg(node, matNew, v) + + elif node.tag == inkex.addNS("use", "svg") or node.tag == "use": + + # A element refers to another SVG element via an + # xlink:href="#blah" attribute. We will handle the element by + # doing an XPath search through the document, looking for the + # element with the matching id="blah" attribute. We then + # recursively process that element after applying any necessary + # (x,y) translation. + # + # Notes: + # 1. We ignore the height and width attributes as they do not + # apply to path-like elements, and + # 2. Even if the use element has visibility="hidden", SVG + # still calls for processing the referenced element. The + # referenced element is hidden only if its visibility is + # "inherit" or "hidden". + + refid = node.get(inkex.addNS("href", "xlink")) + if not refid: + continue + + # [1:] to ignore leading '#' in reference + path = '//*[@id="%s"]' % refid[1:] + refnode = node.xpath(path) + if refnode: + x = float(node.get("x", "0")) + y = float(node.get("y", "0")) + # Note: the transform has already been applied + if (x != 0) or (y != 0): + matNew2 = matNew * Transform("translate(%f,%f)" % (x, y)) + else: + matNew2 = matNew + v = node.get("visibility", v) + self.recursivelyTraverseSvg(refnode, matNew2, v) + + elif node.tag == inkex.addNS("path", "svg"): + + path_data = node.get("d") + if path_data: + self.getPathVertices(path_data, node, matNew) + + elif node.tag == inkex.addNS("rect", "svg") or node.tag == "rect": + + # Manually transform + # + # + # + # into + # + # + # + # I.e., explicitly draw three sides of the rectangle and the + # fourth side implicitly + + # Create a path with the outline of the rectangle + x = float(node.get("x")) + y = float(node.get("y")) + w = float(node.get("width", "0")) + h = float(node.get("height", "0")) + a = [] + a.append(["M", [x, y]]) + a.append(["l", [w, 0]]) + a.append(["l", [0, h]]) + a.append(["l", [-w, 0]]) + a.append(["Z", []]) + self.getPathVertices(a, node, matNew) + + elif node.tag == inkex.addNS("line", "svg") or node.tag == "line": + + # Convert + # + # + + x1 = float(node.get("x1")) + y1 = float(node.get("y1")) + x2 = float(node.get("x2")) + y2 = float(node.get("y2")) + if (not x1) or (not y1) or (not x2) or (not y2): + continue + a = [] + a.append(["M", [x1, y1]]) + a.append(["L", [x2, y2]]) + self.getPathVertices(a, node, matNew) + + elif node.TAG in ["polygon", "polyline"]: + + # Convert + # + # + # + # to + # + # + # + # Note: we ignore polylines with no points + + pl = node.get("points", "").strip() + if not pl: + continue + + pa = pl.split() + d = "".join( + [ + "M " + pa[i] if i == 0 else " L " + pa[i] + for i in range(0, len(pa)) + ] + ) + d = [] + first = True + for part in pl.split(): + x, y = part.split(",") + coords = [float(x), float(y)] + if first: + d.append(["M", coords]) + first = False + else: + d.append(["L", coords]) + if node.TAG == "polygon": + d.append(["Z", []]) + self.getPathVertices(d, node, matNew) + + elif ( + node.tag == inkex.addNS("ellipse", "svg") + or node.tag == "ellipse" + or node.tag == inkex.addNS("circle", "svg") + or node.tag == "circle" + ): + + # Convert circles and ellipses to a path with two 180 degree + # arcs. In general (an ellipse), we convert + # + # + # + # to + # + # + # + # where + # + # X1 = CX - RX + # X2 = CX + RX + # + # Note: ellipses or circles with a radius attribute of value 0 + # are ignored + + if node.tag == inkex.addNS("ellipse", "svg") or node.tag == "ellipse": + rx = float(node.get("rx", "0")) + ry = float(node.get("ry", "0")) + else: + rx = float(node.get("r", "0")) + ry = rx + if rx == 0 or ry == 0: + continue + + cx = float(node.get("cx", "0")) + cy = float(node.get("cy", "0")) + x1 = cx - rx + x2 = cx + rx + d = [ + ["M", (x1, cy)], + ["A", (rx, ry, 0, 1, 0, x2, cy)], + ["A", (rx, ry, 0, 1, 0, x1, cy)], + ] + self.getPathVertices(d, node, matNew) + + elif node.tag == inkex.addNS("pattern", "svg") or node.tag == "pattern": + pass + + elif node.tag == inkex.addNS("metadata", "svg") or node.tag == "metadata": + pass + + elif node.tag == inkex.addNS("defs", "svg") or node.tag == "defs": + pass + + elif node.tag == inkex.addNS("desc", "svg") or node.tag == "desc": + pass + + elif ( + node.tag == inkex.addNS("namedview", "sodipodi") + or node.tag == "namedview" + ): + pass + + elif node.tag == inkex.addNS("eggbot", "svg") or node.tag == "eggbot": + pass + + elif node.tag == inkex.addNS("text", "svg") or node.tag == "text": + texts = [] + plaintext = "" + for tnode in node.iterfind(".//"): # all subtree + if tnode is not None and tnode.text is not None: + texts.append(tnode.text) + if len(texts): + plaintext = "', '".join(texts).encode("latin-1") + inkex.errormsg('Warning: text "%s"' % plaintext) + inkex.errormsg( + "Warning: unable to draw text, please convert it to a path first." + ) + + elif node.tag == inkex.addNS("title", "svg") or node.tag == "title": + pass + + elif node.tag == inkex.addNS("image", "svg") or node.tag == "image": + if "image" not in self.warnings: + inkex.errormsg( + gettext.gettext( + "Warning: unable to draw bitmap images; please convert them to line art first. " + 'Consider using the "Trace bitmap..." tool of the "Path" menu. Mac users please ' + "note that some X11 settings may cause cut-and-paste operations to paste in bitmap copies." + ) + ) + self.warnings["image"] = 1 + + elif node.tag == inkex.addNS("pattern", "svg") or node.tag == "pattern": + pass + + elif ( + node.tag == inkex.addNS("radialGradient", "svg") + or node.tag == "radialGradient" + ): + # Similar to pattern + pass + + elif ( + node.tag == inkex.addNS("linearGradient", "svg") + or node.tag == "linearGradient" + ): + # Similar in pattern + pass + + elif node.tag == inkex.addNS("style", "svg") or node.tag == "style": + # This is a reference to an external style sheet and not the + # value of a style attribute to be inherited by child elements + pass + + elif node.tag == inkex.addNS("cursor", "svg") or node.tag == "cursor": + pass + + elif ( + node.tag == inkex.addNS("color-profile", "svg") + or node.tag == "color-profile" + ): + # Gamma curves, color temp, etc. are not relevant to single + # color output + pass + + elif not isinstance(node.tag, (str, bytes)): + # This is likely an XML processing instruction such as an XML + # comment. lxml uses a function reference for such node tags + # and as such the node tag is likely not a printable string. + # Further, converting it to a printable string likely won't + # be very useful. + pass + + else: + inkex.errormsg( + "Warning: unable to draw object <%s>, please convert it to a path first." + % node.tag + ) + pass + + def recursivelyGetEnclosingTransform(self, node): + # Determine the cumulative transform which node inherits from its chain of ancestors. + node = node.getparent() + if node is not None: + parent_transform = self.recursivelyGetEnclosingTransform(node) + node_transform = node.get("transform", None) + if node_transform is None: + return parent_transform + else: + tr = Transform(node_transform) + if parent_transform is None: + return tr + else: + return parent_transform * tr + else: + return self.docTransform + + def effect(self): + # Viewbox handling + self.handleViewBox() + + # First traverse the document (or selected items), reducing + # everything to line segments. If working on a selection, + # then determine the selection's bounding box in the process. + # (Actually, we just need to know its extrema on the x-axis.) + + if self.options.ids: + # Traverse the selected objects + for id in self.options.ids: + transform = self.recursivelyGetEnclosingTransform(self.svg.selected[id]) + self.recursivelyTraverseSvg([self.svg.selected[id]], transform) + else: + # Traverse the entire document building new, transformed paths + self.recursivelyTraverseSvg(self.document.getroot(), self.docTransform) + + # Determine the center of the drawing's bounding box + self.cx = self.xmin + (self.xmax - self.xmin) / 2.0 + self.cy = self.ymin + (self.ymax - self.ymin) / 2.0 + + # Determine which polygons lie entirely within other polygons + try: + self.options.fname = self.options.fname.format(**{"NAME": self.basename}) + if os.sep not in self.options.fname and "PWD" in os.environ: + # current working directory of an extension seems to be the extension dir. + # Workaround using PWD, if available... + self.options.fname = os.environ["PWD"] + "/" + self.options.fname + scad_fname = os.path.expanduser(self.options.fname) + if "/" != os.sep: + scad_fname = scad_fname.replace("/", os.sep) + + with open(scad_fname, 'w') as outfile: + outfile = open(scad_fname, "w") + outfile.write( + "// Generated by inkscape %s + inkscape-paths2openscad %s\n" + % (self.inkscape_version, VERSION) + ) + outfile.write('// %s from "%s.svg"\n' % (time.ctime(), self.basename)) + # for use in options.fname basename is derived from the sodipodi_docname by + # stripping the svg extension - or if there is no sodipodi_docname basename is 'inkscape'. + # for use in scadviewcmd, scad2stlcmd and stlpostcmd basename is rederived from + # options.fname by stripping an scad extension. + self.basename = re.sub(r"\.scad", "", scad_fname, flags=re.I) + + outfile.write( + """ +// Module names are of the form poly_(). As a result, +// you can associate a polygon in this OpenSCAD program with the corresponding +// SVG element in the Inkscape document by looking for the XML element with +// the attribute id=\"inkscape-path-id\". + +// fudge value is used to ensure that subtracted solids are a tad taller +// in the z dimension than the polygon being subtracted from. This helps +// keep the resulting .stl file manifold. +fudge = 0.1; +""" + ) + if self.options.chamfer < 0.001: + self.options.chamfer = None + + outfile.write("user_unit_scale_x = %s;\n" % (self.userunitsx)) + outfile.write("user_unit_scale_y = %s;\n" % (self.userunitsy)) + outfile.write("custom_scale_x = 1;\n") + outfile.write("custom_scale_y = 1;\n") + + # writeout users parameters + outfile.write("zsize = %s;\n" % (self.options.zsize)) + outfile.write("line_fn = %d;\n" % (self.options.line_fn)) + if self.options.chamfer: + outfile.write("chamfer = %s;\n" % (self.options.chamfer)) + outfile.write("chamfer_fn = %d;\n" % (self.options.chamfer_fn)) + outfile.write("min_line_width = %s;\n" % (self.options.min_line_width)) + outfile.write( + "line_width_scale = %s;\n" % (self.options.line_width_scale_perc * 0.01) + ) + outfile.write( + "function min_line_mm(w) = max(min_line_width, w * line_width_scale) * %g;\n\n" + % self.userunitsx + ) + + for key in self.paths: + outfile.write("\n") + self.convertPath(key, outfile) + + if self.options.chamfer: + outfile.write( + """ +module chamfer_sphere(rad=chamfer, res=chamfer_fn) +{ + if(res <= 4) + { + // octaeder: 3 sided faces = 8 + polyhedron( + points = [ [.0, .0, rad], [.0, .0, -rad], [ rad, .0, .0], [-rad, .0, .0], [.0, rad, .0], [.0, -rad, .0] ], + faces = [ [4, 2, 0], [3, 4, 0], [5, 3, 0], [2, 5, 0], [5, 2, 1], [3, 5, 1], [4, 3, 1], [2 , 4, 1] ]); + } + else + { + sphere(r=rad, center=true, $fn=res); + } +} +""" + ) + + # Come up with a name for the module based on the file name. + name = os.path.splitext(os.path.basename(self.options.fname))[0] + # Remove all punctuation except underscore. + badchars = string.punctuation.replace("_", "") + " " + name = re.sub("[" + badchars + "]", "_", name) + name = remove_umlaut(name) + + outfile.write("\nmodule %s(h)\n{\n" % name) + mi = "" + if self.options.chamfer: + mi = " " + outfile.write(" minkowski()\n {\n") + + # Now output the list of modules to call + outfile.write( + "%s difference()\n%s {\n%s union()\n%s {\n" % (mi, mi, mi, mi) + ) + for call in self.call_list: + outfile.write("%s %s" % (mi, call)) + outfile.write("%s }\n%s union()\n%s {\n" % (mi, mi, mi)) + for call in self.call_list_neg: + outfile.write("%s %s" % (mi, call)) + outfile.write("%s }\n%s }\n" % (mi, mi)) + if self.options.chamfer: + outfile.write(" chamfer_sphere();\n }\n") + + # The module that calls all the other ones. + if self.options.stlmodule is True: + self.options.scad2stl = False #otherwise program will fail because modules are not renderable + outfile.write("}\n\n//%s(zsize);\n" % (name)) + else: + outfile.write("}\n\n%s(zsize);\n" % (name)) + + outfile.close() + except IOError as e: + inkex.errormsg("Unable to write file " + self.options.fname) + inkex.errormsg("ERROR: " + str(e)) + + ################################################################ + # Call OpenSCAD + ################################################################ + if self.options.scadview is True: + #inkex.utils.debug("Calling OpenSCAD ...") + pidfile = os.path.join(tempfile.gettempdir(), "paths2openscad.pid") + running = False + cmd = self.options.scadviewcmd.format(**{"SCAD": scad_fname, "NAME": self.basename}) + try: + with open(pidfile) as pfile: + m = re.match(r"(\d+)\s+(.*)", pfile.read()) + oldpid = int(m.group(1)) + oldcmd = m.group(2) + # print >> sys.stderr, "pid {1} seen in {0}".format(pidfile, oldpid) + # print >> sys.stderr, "cmd {0}, oldcmd {1}".format(cmd, oldcmd) + if cmd == oldcmd: + # we found a pidfile and the cmd in there is still identical. + # If we change the filename in the inkscape extension gui, the cmd differs, and + # the still running openscad would not pick up our changes. + # If the command is identical, we check if the pid in the pidfile is alive. + # If so, we assume, the still running openscad will pick up the changes. + # + # WARNING: too much magic here. We cannot really test, if the last assumption holds. + # Comment out the next line to always start a new instance of openscad. + running = IsProcessRunning(oldpid) + # print >> sys.stderr, "running {0}".format(running) + except Exception: + pass + if not running: + try: + tty = open("/dev/tty", "w") + except Exception: + tty = subprocess.PIPE + try: + with subprocess.Popen(cmd, shell=True, stdin=tty, stdout=tty, stderr=tty) as proc: + proc.wait() + tty.close() + except OSError as e: + raise OSError("%s failed: errno=%d %s" % (cmd, e.errno, e.strerror)) + try: + with open(pidfile, "w") as pfile: + pfile.write(str(proc.pid) + "\n" + cmd + "\n") + except Exception: + pass + else: + # BUG alert: + # If user changes the file viewed in openscad (save with different name, re-open that name + # without closing openscad, again, the still running openscad does not + # pick up the changes. and we have no way to tell the difference if it did. + pass + + ################################################################ + # Call OpenSCAD to STL conversion + ################################################################ + if self.options.scad2stl is True or self.options.stlpost is True: + #inkex.utils.debug("Calling OpenSCAD to STL conversion...") + stl_fname = self.basename + ".stl" + scad2stlcmd = self.options.scad2stlcmd.format(**{"SCAD": scad_fname, "STL": stl_fname, "NAME": self.basename}) + try: + os.unlink(stl_fname) + except Exception: + pass + + with subprocess.Popen(scad2stlcmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: + proc.wait() + stdout, stderr = proc.communicate() + len = -1 + try: + len = os.path.getsize(stl_fname) + except Exception: + pass + if len < 1000: + inkex.errormsg("CMD: {} WARNING: {} is very small: {} bytes.".format(scad2stlcmd, stl_fname, len)) + inkex.errormsg("= " * 24) + inkex.errormsg("STDOUT:\n{}".format(stdout.decode('UTF-8'))) + inkex.errormsg("= " * 24) + inkex.errormsg("STDERR:\n{}".format(stderr.decode('UTF-8'))) + inkex.errormsg("= " * 24) + if len <= 0: # something is wrong. better stop here + self.options.stlpost = False + + ################################################################ + # Call OpenSCAD post processing + ################################################################ + if self.options.stlpost is True: + #inkex.utils.debug("Calling OpenSCAD post processing...") + stlpostcmd = self.options.stlpostcmd.format( + **{"STL": self.basename + ".stl", "NAME": self.basename} + ) + try: + with subprocess.Popen(stlpostcmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as proc: + proc.wait() + stdout, stderr = proc.communicate() + if stdout or stderr: + inkex.errormsg("CMD: {}".format(stlpostcmd)) + inkex.errormsg("= " * 24) + if stdout: + inkex.errormsg("STDOUT: {}".format(stdout.decode('UTF-8'))) + inkex.errormsg("= " * 24) + if stderr: + inkex.errormsg("STDERR: {}".format(stderr.decode('UTF-8'))) + inkex.errormsg("= " * 24) + except OSError as e: + raise OSError("%s failed: errno=%d %s" % (stlpostcmd, e.errno.decode('UTF-8'), e.strerror.decode('UTF-8'))) + +if __name__ == '__main__': + PathsToOpenSCAD().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/pixels2objects/meta.json b/extensions/fablabchemnitz/pixels2objects/meta.json index 69c61f6..833f9b5 100644 --- a/extensions/fablabchemnitz/pixels2objects/meta.json +++ b/extensions/fablabchemnitz/pixels2objects/meta.json @@ -9,13 +9,13 @@ "license": "GNU GPL v3", "license_url": "https://inkscape.org/~pakin/%E2%98%85pixels-to-objects", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/pixels2objects", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/pixels2objects", "fork_url": "https://inkscape.org/~pakin/%E2%98%85pixels-to-objects", "documentation_url": "https://stadtfabrikanten.org/display/IFM/Pixels+To+Objects", "inkscape_gallery_url": null, "main_authors": [ "inkscape.org/pakin", - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/primitive/meta.json b/extensions/fablabchemnitz/primitive/meta.json index 91aa406..70f3f09 100644 --- a/extensions/fablabchemnitz/primitive/meta.json +++ b/extensions/fablabchemnitz/primitive/meta.json @@ -7,14 +7,14 @@ "original_name": "Primitive (Michael Fogleman)", "original_id": "fablabchemnitz.de.primitive", "license": "GNU GPL v3", - "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/primitive", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/primitive", "fork_url": "https://github.com/fogleman/primitive", "documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=78807580", "inkscape_gallery_url": "https://inkscape.org/~MarioVoigt/%E2%98%85primitive-for-inkscape", "main_authors": [ - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/printing_marks_dotted/meta.json b/extensions/fablabchemnitz/printing_marks_dotted/meta.json new file mode 100644 index 0000000..bf47ab1 --- /dev/null +++ b/extensions/fablabchemnitz/printing_marks_dotted/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Printing Marks Dotted", + "id": "fablabchemnitz.de.printing_marks_dotted", + "path": "printing_marks_dotted", + "dependent_extensions": null, + "original_name": "Printing Marks Dotted", + "original_id": "org.inkscape.printing.marks.dotted", + "license": "GNU GPL v2", + "license_url": "https://inkscape.org/~jabiertxof/%E2%98%85printing-marks-dotted", + "comment": "ported to Inkscape v1 manually by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/printing_marks_dotted", + "fork_url": "https://inkscape.org/~jabiertxof/%E2%98%85printing-marks-dotted", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Printing+Marks+Dotted", + "inkscape_gallery_url": null, + "main_authors": [ + "inkscape.org/jabiertxof", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.inx b/extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.inx new file mode 100644 index 0000000..4223c77 --- /dev/null +++ b/extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.inx @@ -0,0 +1,46 @@ + + + Printing Marks Dotted + fablabchemnitz.de.printing_marks_dotted + + + true + false + false + true + false + true + false + + + + + + + + + + + + + + 5 + + 5 + 5 + 5 + 5 + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.py b/extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.py new file mode 100644 index 0000000..06b62d8 --- /dev/null +++ b/extensions/fablabchemnitz/printing_marks_dotted/printing_marks_dotted.py @@ -0,0 +1,475 @@ +#!/usr/bin/env python3 +''' +This extension allows you to draw crop, registration and other +printing marks in Inkscape. + +Authors: + Nicolas Dufour - Association Inkscape-fr + Aurelio A. Heckert + +Copyright (C) 2008 Authors + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +''' + +from subprocess import Popen, PIPE, STDOUT +import math +import inkex +from lxml import etree + +class PrintingMarksDotted(inkex.EffectExtension): + + # Default parameters + stroke_width = 0.25 + + def add_arguments(self, pars): + pars.add_argument("--where_to_crop", default=True, help="Apply crop marks to...") + pars.add_argument("--crop_marks", type=inkex.Boolean, default=True, help="Draw crop Marks?") + pars.add_argument("--dotted_crop_marks", type=inkex.Boolean, default=True, help="Draw dotted crop Marks?") + pars.add_argument("--bleed_marks", type=inkex.Boolean, default=False, help="Draw Bleed Marks?") + pars.add_argument("--registration_marks", type=inkex.Boolean, default=False, help="Draw Registration Marks?") + pars.add_argument("--star_target", type=inkex.Boolean, default=False, help="Draw Star Target?") + pars.add_argument("--colour_bars", type=inkex.Boolean, default=False, help="Draw Colour Bars?") + pars.add_argument("--page_info", type=inkex.Boolean, default=False, help="Draw Page Information?") + pars.add_argument("--unit",default="px", help="Draw measurment") + pars.add_argument("--crop_offset", type=float, default=0, help="Offset") + pars.add_argument("--bleed_top", type=float, default=0, help="Bleed Top Size") + pars.add_argument("--bleed_bottom", type=float, default=0, help="Bleed Bottom Size") + pars.add_argument("--bleed_left", type=float, default=0, help="Bleed Left Size") + pars.add_argument("--bleed_right",type=float, default=0, help="Bleed Right Size") + pars.add_argument("--tab", help="The selected UI-tab when OK was pressed") + + def addMarker(self): + svg = self.document.getroot() + xpathStr = '//marker[@id="scissorsCroper"]' + maskElement = svg.xpath(xpathStr, namespaces=inkex.NSS) + if maskElement == []: + xpathStr = '//svg:defs' + defs = svg.xpath(xpathStr, namespaces=inkex.NSS) + line_attribs = {'markerWidth': "8.2212915", + 'markerHeight': "4.8983894", + 'orient': "auto", + 'id':"scissorsCroper"} + markerElement = etree.SubElement(defs[0],inkex.addNS('marker','svg'), line_attribs) + line_attribs = {'style': "fill:#000000;stroke:#ffffff;stroke-width:0.2;stroke-miterlimit:4;stroke-opacity:1", + 'id': "scissorsCroperPath", + 'd': "m -3.09375,-2.59375 c -0.2875213,-0.019086 -0.5530997,0.080418 -0.78125,0.25 -0.2281503,0.1695818 -0.4212781,0.4427198 -0.4375,0.75 -0.014236,0.2696628 0.032949,0.4281517 0.09375,0.53125 0.011692,0.019827 0.022314,0.017924 0.03125,0.03125 -0.074992,0.019409 -0.1886388,0.0360237 -0.34375,0.0625 -0.3217609,0.0549221 -0.7596575,0.13825127 -1.21875,0.375 l -3.03125,-1.125 c -0.2710413,-0.1042898 -0.5662791,-0.1829987 -0.875,-0.15625 -0.3087209,0.026749 -0.621076,0.1687088 -0.84375,0.4375 a 0.20792008,0.20792008 0 0 0 -0.03125,0.03125 0.20792008,0.20792008 0 0 0 -0.03125,0.0625 0.20792008,0.20792008 0 0 0 0,0.0625 0.20792008,0.20792008 0 0 0 0.03125,0.0625 0.20792008,0.20792008 0 0 0 0.03125,0.03125 0.20792008,0.20792008 0 0 0 0.09375,0.0625 l 2.9375,1.15625 -2.96875,1.125 a 0.20792008,0.20792008 0 0 0 -0.09375,0.0625 0.20792008,0.20792008 0 0 0 -0.03125,0.03125 0.20792008,0.20792008 0 0 0 -0.03125,0.0625 0.20792008,0.20792008 0 0 0 0,0.0625 0.20792008,0.20792008 0 0 0 0.03125,0.0625 0.20792008,0.20792008 0 0 0 0.03125,0.03125 C -10.094168,1.9539272 -9.4699318,1.9749423 -9,1.84375 L -5.71875,0.6875 c 0.481754,0.20541523 0.912658,0.3186677 1.1875,0.375 0.1483249,0.030401 0.2392409,0.045912 0.3125,0.0625 0.03663,0.00829 0.024599,-0.00324 0.03125,0 -0.0079,0.02335 -0.010635,0.041757 -0.03125,0.09375 -0.053917,0.1359822 -0.1506131,0.3500538 -0.09375,0.625 0.074929,0.3622982 0.3561361,0.6217769 0.65625,0.75 0.3001139,0.1282231 0.6300895,0.1440646 0.9375,0.03125 0.6444683,-0.175589 0.9014775,-0.9349259 0.625,-1.5 C -2.2324842,0.83910622 -2.4880622,0.66240891 -2.75,0.5625 -3.0119378,0.46259109 -3.2717529,0.42256233 -3.53125,0.4375 c -0.2805605,0.0161501 -0.5796777,0.0351178 -0.8125,-0.03125 -0.1944918,-0.0554414 -0.3308104,-0.18103045 -0.46875,-0.375 0.1925418,-0.25215792 0.4169804,-0.350782 0.71875,-0.375 0.3394341,-0.0272407 0.7247815,0.0434012 1.0625,0 0.010025,-6.5986e-4 0.021283,9.2632e-4 0.03125,0 0.5937358,-0.0551819 1.1050788,-0.57908524 1.0625,-1.1875 -0.00523,-0.6217326 -0.5853909,-1.0659264 -1.15625,-1.0625 z M -2.9375,-1.875 c 0.1401777,0.04894 0.2268596,0.139783 0.25,0.25 a 0.20792008,0.20792008 0 0 0 0.03125,0.03125 c 0.046997,0.1597651 -0.018243,0.2935457 -0.15625,0.40625 -0.1380068,0.1127043 -0.3531142,0.176154 -0.5,0.125 -0.1652738,-0.046651 -0.2408416,-0.1796945 -0.25,-0.34375 -0.00916,-0.1640555 0.046643,-0.3414062 0.21875,-0.4375 0.104863,-0.058549 0.2664752,-0.08005 0.40625,-0.03125 z m -0.21875,3.03125 c 0.2392165,0.047351 0.4697735,0.2941069 0.4375,0.53125 -0.010405,0.1211995 -0.066062,0.2235316 -0.1875,0.28125 C -3.0276883,2.0264684 -3.2009829,2.0387215 -3.3125,2 A 0.20792008,0.20792008 0 0 0 -3.34375,2 C -3.6474031,1.9320987 -3.710744,1.2999504 -3.40625,1.1875 a 0.20792008,0.20792008 0 0 0 0.03125,0 c 0.072689,-0.036572 0.1390112,-0.047034 0.21875,-0.03125 z"} + pathElement = etree.SubElement(markerElement, inkex.addNS('path','svg'), line_attribs) + + def draw_crop_line(self, x1, y1, x2, y2, name, parent): + if self.options.dotted_crop_marks == True: + self.addMarker() + style = { 'stroke': '#FFFFFF', 'stroke-width': str(self.stroke_width), + 'fill': 'none'} + else: + style = { 'stroke': '#000000', 'stroke-width': str(self.stroke_width), + 'fill': 'none'} + line_attribs = {'style': str(inkex.Style(style)), + 'id': name, + 'd': 'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)} + etree.SubElement(parent, 'path', line_attribs) + if self.options.dotted_crop_marks == True: + style = { 'stroke': '#000000', 'stroke-width': str(self.stroke_width), + 'fill': 'none' , 'marker-end':'url(#scissorsCroper)', + 'stroke-dasharray' :'0.5,0.25', 'stroke-miterlimit':"4"} + + line_attribs = {'style': str(inkex.Style(style)), + 'id': name + "_dotted", + 'd': 'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)} + etree.SubElement(parent, 'path', line_attribs) + + def draw_bleed_line(self, x1, y1, x2, y2, name, parent): + style = { 'stroke': '#000000', 'stroke-width': str(self.stroke_width), + 'fill': 'none', + 'stroke-miterlimit': '4', 'stroke-dasharray': '4, 2, 1, 2', + 'stroke-dashoffset': '0' } + line_attribs = {'style': str(inkex.Style(style)), + 'id': name, + 'd': 'M '+str(x1)+','+str(y1)+' L '+str(x2)+','+str(y2)} + etree.SubElement(parent, 'path', line_attribs) + + def draw_reg_circles(self, cx, cy, r, name, colours, parent): + for i in range(len(colours)): + style = {'stroke':colours[i], 'stroke-width':str(r / len(colours)), + 'fill':'none'} + circle_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'cx':str(cx), 'cy':str(cy), + 'r':str((r / len(colours)) * (i + 0.5))} + etree.SubElement(parent, inkex.addNS('circle','svg'), + circle_attribs) + + def draw_registration_marks(self, cx, cy, rotate, name, parent): + colours = ['#000000','#00ffff','#ff00ff','#ffff00','#000000'] + g = etree.SubElement(parent, 'g', { 'id': name }) + for i in range(len(colours)): + style = {'fill':colours[i], 'fill-opacity':'1', 'stroke':'none'} + r = (self.mark_size/2) + step = r + stroke = r / len(colours) + regoffset = stroke * i + regmark_attribs = {'style': str(inkex.Style(style)), + 'd': 'm' +\ + ' '+str(-regoffset)+','+str(r) +\ + ' '+str(-stroke) +',0' +\ + ' '+str(step) +','+str(-r) +\ + ' '+str(-step) +','+str(-r) +\ + ' '+str(stroke) +',0' +\ + ' '+str(step) +','+str(r) +\ + ' '+str(-step) +','+str(r) +\ + ' z', + 'transform': 'translate('+str(cx)+','+str(cy)+ \ + ') rotate('+str(rotate)+')'} + etree.SubElement(g, 'path', regmark_attribs) + + def draw_star_target(self, cx, cy, name, parent): + r = (self.mark_size/2) + style = {'fill':'#000 device-cmyk(1,1,1,1)', 'fill-opacity':'1', 'stroke':'none'} + d = ' M 0,0' + i = 0 + while i < ( 2 * math.pi ): + i += math.pi / 16 + d += ' L 0,0 ' +\ + ' L '+ str(math.sin(i)*r) +','+ str(math.cos(i)*r) +\ + ' L '+ str(math.sin(i+0.09)*r) +','+ str(math.cos(i+0.09)*r) + regmark_attribs = {'style':str(inkex.Style(style)), + inkex.addNS('label','inkscape'):name, + 'transform':'translate('+str(cx)+','+str(cy)+')', + 'd':d} + etree.SubElement(parent, inkex.addNS('path','svg'), + regmark_attribs) + + def draw_coluor_bars(self, cx, cy, rotate, name, parent): + g = etree.SubElement(parent, 'g', { + 'id':name, + 'transform':'translate('+str(cx)+','+str(cy)+\ + ') rotate('+str(rotate)+')' }) + l = min( self.mark_size / 3, max(self.area_w,self.area_h) / 45 ) + for bar in [{'c':'*', 'stroke':'#000', 'x':0, 'y':-(l+1)}, + {'c':'r', 'stroke':'#0FF', 'x':0, 'y':0}, + {'c':'g', 'stroke':'#F0F', 'x':(l*11)+1, 'y':-(l+1)}, + {'c':'b', 'stroke':'#FF0', 'x':(l*11)+1, 'y':0} + ]: + i = 0 + while i <= 1: + cr = '255' + cg = '255' + cb = '255' + if bar['c'] == 'r' or bar['c'] == '*' : cr = str(255*i) + if bar['c'] == 'g' or bar['c'] == '*' : cg = str(255*i) + if bar['c'] == 'b' or bar['c'] == '*' : cb = str(255*i) + r_att = {'fill':'rgb('+cr+','+cg+','+cb+')', + 'stroke':bar['stroke'], + 'stroke-width':'0.5', + 'x':str((l*i*10)+bar['x']), 'y':str(bar['y']), + 'width':str(l), 'height':str(l)} + r = etree.SubElement(g, 'rect', r_att) + i += 0.1 + + def get_selection_area(self): + scale = self.svg.unittouu('1px') # convert to document units + sel_area = {} + min_x, min_y, max_x, max_y = False, False, False, False + for id in self.options.ids: + sel_area[id] = {} + for att in [ "x", "y", "width", "height" ]: + args = [ "inkscape", "-I", id, "--query-"+att, self.options.input_file ] + sel_area[id][att] = scale* \ + float(Popen(args, stdout=PIPE, stderr=PIPE).communicate()[0]) + current_min_x = sel_area[id]["x"] + current_min_y = sel_area[id]["y"] + current_max_x = sel_area[id]["x"] + \ + sel_area[id]["width"] + current_max_y = sel_area[id]["y"] + \ + sel_area[id]["height"] + if not min_x: min_x = current_min_x + if not min_y: min_y = current_min_y + if not max_x: max_x = current_max_x + if not max_y: max_y = current_max_y + if current_min_x < min_x: min_x = current_min_x + if current_min_y < min_y: min_y = current_min_y + if current_max_x > max_x: max_x = current_max_x + if current_max_y > max_y: max_y = current_max_y + #inkex.errormsg( '>> '+ id + + # ' min_x:'+ str(min_x) + + # ' min_y:'+ str(min_y) + + # ' max_x:'+ str(max_x) + + # ' max_y:'+ str(max_y) ) + self.area_x1 = min_x + self.area_y1 = min_y + self.area_x2 = max_x + self.area_y2 = max_y + self.area_w = max_x - min_x + self.area_h = max_y - min_y + + def effect(self): + self.mark_size = self.svg.unittouu('1cm') + self.min_mark_margin = self.svg.unittouu('3mm') + + if self.options.where_to_crop == 'selection' : + self.get_selection_area() + #inkex.errormsg('Sory, the crop to selection is a TODO feature') + #exit(1) + else : + svg = self.document.getroot() + self.area_w = self.svg.unittouu(svg.get('width')) + self.area_h = self.svg.unittouu(svg.attrib['height']) + self.area_x1 = 0 + self.area_y1 = 0 + self.area_x2 = self.area_w + self.area_y2 = self.area_h + + # Get SVG document dimensions + # self.width must be replaced by self.area_x2. same to others. + svg = self.document.getroot() + #self.width = width = self.svg.unittouu(svg.get('width')) + #self.height = height = self.svg.unittouu(svg.attrib['height']) + + # Convert parameters to user unit + offset = self.svg.unittouu(str(self.options.crop_offset) + \ + self.options.unit) + bt = self.svg.unittouu(str(self.options.bleed_top) + self.options.unit) + bb = self.svg.unittouu(str(self.options.bleed_bottom) + self.options.unit) + bl = self.svg.unittouu(str(self.options.bleed_left) + self.options.unit) + br = self.svg.unittouu(str(self.options.bleed_right) + self.options.unit) + # Bleed margin + if bt < offset : bmt = 0 + else : bmt = bt - offset + if bb < offset : bmb = 0 + else : bmb = bb - offset + if bl < offset : bml = 0 + else : bml = bl - offset + if br < offset : bmr = 0 + else : bmr = br - offset + + # Define the new document limits + offset_left = self.area_x1 - offset + offset_right = self.area_x2 + offset + offset_top = self.area_y1 - offset + offset_bottom = self.area_y2 + offset + + # Get middle positions + middle_vertical = self.area_y1 + ( self.area_h / 2 ) + middle_horizontal = self.area_x1 + ( self.area_w / 2 ) + + # Test if printing-marks layer existis + layer = self.document.xpath( + '//*[@id="printing-marks" and @inkscape:groupmode="layer"]', + namespaces=inkex.NSS) + if layer: svg.remove(layer[0]) # remove if it existis + # Create a new layer + layer = etree.SubElement(svg, 'g') + layer.set('id', 'printing-marks') + layer.set(inkex.addNS('label', 'inkscape'), 'Printing Marks') + layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') + layer.set(inkex.addNS('insensitive', 'sodipodi'), 'true') + + # Crop Mark + if self.options.crop_marks == True: + # Create a group for Crop Mark + g_attribs = {inkex.addNS('label','inkscape'):'CropMarks', + 'id':'CropMarks'} + g_crops = etree.SubElement(layer, 'g', g_attribs) + + # Top left Mark + self.draw_crop_line(self.area_x1, offset_top, + self.area_x1, offset_top - self.mark_size, + 'cropTL1', g_crops) + self.draw_crop_line(offset_left, self.area_y1, + offset_left - self.mark_size, self.area_y1, + 'cropTL2', g_crops) + + # Top right Mark + self.draw_crop_line(self.area_x2, offset_top, + self.area_x2, offset_top - self.mark_size, + 'cropTR1', g_crops) + self.draw_crop_line(offset_right, self.area_y1, + offset_right + self.mark_size, self.area_y1, + 'cropTR2', g_crops) + + # Bottom left Mark + self.draw_crop_line(self.area_x1, offset_bottom, + self.area_x1, offset_bottom + self.mark_size, + 'cropBL1', g_crops) + self.draw_crop_line(offset_left, self.area_y2, + offset_left - self.mark_size, self.area_y2, + 'cropBL2', g_crops) + + # Bottom right Mark + self.draw_crop_line(self.area_x2, offset_bottom, + self.area_x2, offset_bottom + self.mark_size, + 'cropBR1', g_crops) + self.draw_crop_line(offset_right, self.area_y2, + offset_right + self.mark_size, self.area_y2, + 'cropBR2', g_crops) + + # Bleed Mark + if self.options.bleed_marks == True: + # Create a group for Bleed Mark + g_attribs = {inkex.addNS('label','inkscape'):'BleedMarks', + 'id':'BleedMarks'} + g_bleed = etree.SubElement(layer, 'g', g_attribs) + + # Top left Mark + self.draw_bleed_line(self.area_x1 - bl, offset_top - bmt, + self.area_x1 - bl, offset_top - bmt - self.mark_size, + 'bleedTL1', g_bleed) + self.draw_bleed_line(offset_left - bml, self.area_y1 - bt, + offset_left - bml - self.mark_size, self.area_y1 - bt, + 'bleedTL2', g_bleed) + + # Top right Mark + self.draw_bleed_line(self.area_x2 + br, offset_top - bmt, + self.area_x2 + br, offset_top - bmt - self.mark_size, + 'bleedTR1', g_bleed) + self.draw_bleed_line(offset_right + bmr, self.area_y1 - bt, + offset_right + bmr + self.mark_size, self.area_y1 - bt, + 'bleedTR2', g_bleed) + + # Bottom left Mark + self.draw_bleed_line(self.area_x1 - bl, offset_bottom + bmb, + self.area_x1 - bl, offset_bottom + bmb + self.mark_size, + 'bleedBL1', g_bleed) + self.draw_bleed_line(offset_left - bml, self.area_y2 + bb, + offset_left - bml - self.mark_size, self.area_y2 + bb, + 'bleedBL2', g_bleed) + + # Bottom right Mark + self.draw_bleed_line(self.area_x2 + br, offset_bottom + bmb, + self.area_x2 + br, offset_bottom + bmb + self.mark_size, + 'bleedBR1', g_bleed) + self.draw_bleed_line(offset_right + bmr, self.area_y2 + bb, + offset_right + bmr + self.mark_size, self.area_y2 + bb, + 'bleedBR2', g_bleed) + + # Registration Mark + if self.options.registration_marks == True: + # Create a group for Registration Mark + g_attribs = {inkex.addNS('label','inkscape'):'RegistrationMarks', + 'id':'RegistrationMarks'} + g_center = etree.SubElement(layer, 'g', g_attribs) + + # Left Mark + cx = max( bml + offset, self.min_mark_margin ) + self.draw_registration_marks(self.area_x1 - cx - (self.mark_size/2), + middle_vertical - self.mark_size*1.5, + '0', 'regMarkL', g_center) + + # Right Mark + cx = max( bmr + offset, self.min_mark_margin ) + self.draw_registration_marks(self.area_x2 + cx + (self.mark_size/2), + middle_vertical - self.mark_size*1.5, + '180', 'regMarkR', g_center) + + # Top Mark + cy = max( bmt + offset, self.min_mark_margin ) + self.draw_registration_marks(middle_horizontal, + self.area_y1 - cy - (self.mark_size/2), + '90', 'regMarkT', g_center) + + # Bottom Mark + cy = max( bmb + offset, self.min_mark_margin ) + self.draw_registration_marks(middle_horizontal, + self.area_y2 + cy + (self.mark_size/2), + '-90', 'regMarkB', g_center) + + # Star Target + if self.options.star_target == True: + # Create a group for Star Target + g_attribs = {inkex.addNS('label','inkscape'):'StarTarget', + 'id':'StarTarget'} + g_center = etree.SubElement(layer, 'g', g_attribs) + + if self.area_h < self.area_w : + # Left Star + cx = max( bml + offset, self.min_mark_margin ) + self.draw_star_target(self.area_x1 - cx - (self.mark_size/2), + middle_vertical, + 'starTargetL', g_center) + # Right Star + cx = max( bmr + offset, self.min_mark_margin ) + self.draw_star_target(self.area_x2 + cx + (self.mark_size/2), + middle_vertical, + 'starTargetR', g_center) + else : + # Top Star + cy = max( bmt + offset, self.min_mark_margin ) + self.draw_star_target(middle_horizontal - self.mark_size*1.5, + self.area_y1 - cy - (self.mark_size/2), + 'starTargetT', g_center) + # Bottom Star + cy = max( bmb + offset, self.min_mark_margin ) + self.draw_star_target(middle_horizontal - self.mark_size*1.5, + self.area_y2 + cy + (self.mark_size/2), + 'starTargetB', g_center) + + + # Colour Bars + if self.options.colour_bars == True: + # Create a group for Colour Bars + g_attribs = {inkex.addNS('label','inkscape'):'ColourBars', + 'id':'PrintingColourBars'} + g_center = etree.SubElement(layer, 'g', g_attribs) + + if self.area_h > self.area_w : + # Left Bars + cx = max( bml + offset, self.min_mark_margin ) + self.draw_coluor_bars(self.area_x1 - cx - (self.mark_size/2), + middle_vertical + self.mark_size, + 90, + 'PrintingColourBarsL', g_center) + # Right Bars + cx = max( bmr + offset, self.min_mark_margin ) + self.draw_coluor_bars(self.area_x2 + cx + (self.mark_size/2), + middle_vertical + self.mark_size, + 90, + 'PrintingColourBarsR', g_center) + else : + # Top Bars + cy = max( bmt + offset, self.min_mark_margin ) + self.draw_coluor_bars(middle_horizontal + self.mark_size, + self.area_y1 - cy - (self.mark_size/2), + 0, + 'PrintingColourBarsT', g_center) + # Bottom Bars + cy = max( bmb + offset, self.min_mark_margin ) + self.draw_coluor_bars(middle_horizontal + self.mark_size, + self.area_y2 + cy + (self.mark_size/2), + 0, + 'PrintingColourBarsB', g_center) + + + # Page Information + if self.options.page_info == True: + # Create a group for Page Information + g_attribs = {inkex.addNS('label','inkscape'):'PageInformation', + 'id':'PageInformation'} + g_pag_info = etree.SubElement(layer, 'g', g_attribs) + y_margin = max( bmb + offset, self.min_mark_margin ) + txt_attribs = { + 'style': 'font-size:12px;font-style:normal;font-weight:normal;fill:#000000;font-family:Bitstream Vera Sans,sans-serif;text-anchor:middle;text-align:center', + 'x': str(middle_horizontal), + 'y': str(self.area_y2+y_margin+self.mark_size+20) + } + txt = etree.SubElement(g_pag_info, 'text', txt_attribs) + txt.text = 'Page size: ' +\ + str(round(self.svg.uutounit(self.area_w,self.options.unit),2)) +\ + 'x' +\ + str(round(self.svg.uutounit(self.area_h,self.options.unit),2)) +\ + ' ' + self.options.unit + + +if __name__ == '__main__': + PrintingMarksDotted().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/render_silhouette_regmarks/meta.json b/extensions/fablabchemnitz/render_silhouette_regmarks/meta.json new file mode 100644 index 0000000..2b25ac4 --- /dev/null +++ b/extensions/fablabchemnitz/render_silhouette_regmarks/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "Silhouette Cameo Registration Marks", + "id": "fablabchemnitz.de.render_silhouette_regmarks", + "path": "render_silhouette_regmarks", + "dependent_extensions": null, + "original_name": "Classic", + "original_id": "org.inkscape.render.silhouette-regmarks", + "license": "GNU GPL v3", + "license_url": "https://github.com/miLORD1337/silhouette-regmarks/blob/main/LICENSE", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/render_silhouette_regmarks", + "fork_url": "https://github.com/miLORD1337/silhouette-regmarks", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Silhouette+Cameo+Registration+Marks", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/miLORD1337", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.inx b/extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.inx new file mode 100644 index 0000000..d6a7fa5 --- /dev/null +++ b/extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.inx @@ -0,0 +1,21 @@ + + + Silhouette Cameo Registration Marks + fablabchemnitz.de.silhouette_cameo_registration_marks + 180 + 230 + 15 + 20 + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.py b/extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.py new file mode 100644 index 0000000..6ce6153 --- /dev/null +++ b/extensions/fablabchemnitz/render_silhouette_regmarks/render_silhouette_regmarks.py @@ -0,0 +1,101 @@ +# +# Copyright (C) 2021 miLORD1337 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA. +# +""" +Base module for rendering regmarks for Silhouette CAMEO products in Inkscape. +""" + +import inkex +from lxml import etree + +SVG_URI = u'http://www.w3.org/2000/svg' + +class SilhouetteCameoRegistrationMarks(inkex.EffectExtension): + + def add_arguments(self, pars): + # Define string option "--what" with "-w" shortcut and default value "World". + # Layer name static, since self.document.getroot() not available on initialization + self.layername = 'silhouette-regmark' + + # Parse arguments + pars.add_argument("-X", "--reg-x", "--regwidth", type = float, dest = "regwidth", default = 180.0, help="X mark distance [mm]") + pars.add_argument("-Y", "--reg-y", "--reglength", type = float, dest = "reglength", default = 230.0, help="Y mark distance [mm]") + pars.add_argument("--rego-x", "--regoriginx", type = float, dest = "regoriginx", default = 15.0, help="X mark origin from left [mm]") + pars.add_argument("--rego-y", "--regoriginy", type = float, dest = "regoriginy", default = 20.0, help="X mark origin from top [mm]") + + #SVG rect element generation routine + def drawRect(self, size, pos, name): + x, y = pos + w, h = size + rect = etree.Element('{%s}rect' % SVG_URI) + rect.set('x', str(x)) + rect.set('y', str(y)) + rect.set('id', name) + rect.set('width', str(w)) + rect.set('height', str(h)) + rect.set('style', 'fill: black;') + return rect + + #SVG line element generation routine + def drawLine(self, posStart, posEnd, name): + x1, y1 = posStart + x2, y2, = posEnd + line = etree.Element('{%s}line' % SVG_URI) + line.set('x1', str(x1)) + line.set('y1', str(y1)) + line.set('x2', str(x2)) + line.set('y2', str(y2)) + line.set('id', name) + line.set('style', 'stroke: black; stroke-width: 0.5;') + return line + + def effect(self): + svg = self.document.getroot() + + # Create a new layer. + layer = etree.SubElement(svg, 'g') + layer.set(inkex.addNS('label', 'inkscape'), self.layername) + layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') + + # Create square in top left corner + layer.append(self.drawRect((5,5), (self.options.regoriginx,self.options.regoriginy), 'TopLeft')) + + # Create group for top right corner + topRight = etree.Element('{%s}g' % SVG_URI) + topRight.set('id', 'TopRight') + topRight.set('style', 'fill: black;') + + # Create horizontal and vertical lines in group + topRight.append(self.drawLine((self.options.regwidth-20,self.options.regoriginy), (self.options.regwidth,self.options.regoriginy), 'Horizontal')) + topRight.append(self.drawLine((self.options.regwidth,self.options.regoriginy), (self.options.regwidth,self.options.regoriginy + 20), 'Vertical')) + layer.append(topRight) + + # Create group for top right corner + bottomLeft = etree.Element('{%s}g' % SVG_URI) + bottomLeft.set('id', 'BottomLeft') + bottomLeft.set('style', 'fill: black;') + + # Create horizontal and vertical lines in group + bottomLeft.append(self.drawLine((self.options.regoriginx,self.options.reglength), (self.options.regoriginx+20,self.options.reglength), 'Horizontal')) + bottomLeft.append(self.drawLine((self.options.regoriginx,self.options.reglength), (self.options.regoriginx,self.options.reglength - 20), 'Vertical')) + layer.append(bottomLeft) + + #Lock layer + layer.set(inkex.addNS('insensitive', 'sodipodi'), 'true') + +if __name__ == '__main__': + SilhouetteCameoRegistrationMarks().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/scale_to_size/meta.json b/extensions/fablabchemnitz/scale_to_size/meta.json index a7568e0..8668518 100644 --- a/extensions/fablabchemnitz/scale_to_size/meta.json +++ b/extensions/fablabchemnitz/scale_to_size/meta.json @@ -7,14 +7,14 @@ "original_name": "Scale To Size", "original_id": "fablabchemnitz.de.scale_to_size", "license": "GNU GPL v3", - "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/LICENSE", + "license_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/LICENSE", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/scale_to_size", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/scale_to_size", "fork_url": null, "documentation_url": "https://stadtfabrikanten.org/pages/viewpage.action?pageId=76054572", "inkscape_gallery_url": null, "main_authors": [ - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/snap_object_points/meta.json b/extensions/fablabchemnitz/snap_object_points/meta.json index c9f27f2..21f2f80 100644 --- a/extensions/fablabchemnitz/snap_object_points/meta.json +++ b/extensions/fablabchemnitz/snap_object_points/meta.json @@ -9,13 +9,13 @@ "license": "GNU GPL v3", "license_url": "https://inkscape.org/de/~pakin/%E2%98%85snap-object-points", "comment": "", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/snap_object_points", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/snap_object_points", "fork_url": "https://inkscape.org/de/~pakin/%E2%98%85snap-object-points", "documentation_url": "", "inkscape_gallery_url": null, "main_authors": [ "inkscape.org/pakin", - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/stroke_color_as_fill/meta.json b/extensions/fablabchemnitz/stroke_color_as_fill/meta.json index 94c2d42..038f491 100644 --- a/extensions/fablabchemnitz/stroke_color_as_fill/meta.json +++ b/extensions/fablabchemnitz/stroke_color_as_fill/meta.json @@ -9,13 +9,13 @@ "license": "GNU GPL v3", "license_url": "https://inkscape.org/de/~jabiertxof/%E2%98%85stroke-color-as-fill", "comment": "ported to Inkscape v1 by Mario Voigt", - "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/stroke_color_as_fill", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/stroke_color_as_fill", "fork_url": "https://inkscape.org/de/~jabiertxof/%E2%98%85stroke-color-as-fill", "documentation_url": "https://stadtfabrikanten.org/display/IFM/Stroke+Color+As+Fill", "inkscape_gallery_url": null, "main_authors": [ "inkscape.org/jabiertxof", - "github.com/vmario89" + "github.com/eridur-de" ] } ] \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/base_transform.py b/extensions/fablabchemnitz/table_support/base_transform.py new file mode 100644 index 0000000..7365808 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/base_transform.py @@ -0,0 +1,266 @@ +#!/usr/bin/env python3 +""" +base_transform.py +Base matemathical operations for SVG 3x3 matrices + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import re +import inkex +import os +from math import * + +class BaseTransform(inkex.Effect): + unitMatrix = [[1, 0, 0], [0, 1, 0], [0, 0, 1]] + + def isset(self, v, i = None): + try: + if (i is None): + v + else: + v[i] + return True + except: + return False + + def __init__(self): + inkex.Effect.__init__(self) + + def sizeToPx(self, s, dim = "y"): + root = self.document.getroot() + try: + factor = float(root.attrib[inkex.addNS('export-' + dim + 'dpi', 'inkscape')]) + except: + factor = 90 + unit = '' + pattern = '[\\-\\d\\.]+([a-zA-Z][a-zA-Z])' + if (re.search(pattern, s)): + res = re.search(pattern, s) + unit = res.group(1) + pattern = '^([\\-\\d\\.]*)' + res = re.search(pattern, s) + n = float(res.group(1)) + if unit == 'cm': + return (n / 2.54) * factor + elif unit == 'ft': + return n * 12 * factor + elif unit == 'in': + return n * factor + elif unit == 'm': + return ((n * 10) / 2.54) * factor + elif unit == 'mm': + return ((n / 10) / 2.54) * factor + elif unit == 'pc': + return ((n * 2.36228956229) / 2.54) * factor + elif unit == 'pt': + return (((n / 2.83464646465) / 10) / 2.54) * factor + elif unit == 'px' or unit == '': + return n + + return 0 + + def transform(self, el): + result = self.unitMatrix + if (el.tag == inkex.addNS('svg', 'svg')): + return result + + if (not self.isset(el.attrib, 'transform')): + return self.multiply(self.transform(el.getparent()), result) + pattern = '(matrix|translate|scale|rotate|skewX|skewY)[\\s|,]*\\(([^\\)]*)\\)' + transforms = re.findall(pattern, el.attrib['transform']) + + for transform in transforms: + values = re.split('[\\s|,]+', transform[1]) + for i in range(len(values)): + values[i] = float(values[i]) + function = transform[0] + if (function == 'matrix'): + a = [[values[0], values[2], values[4]], + [values[1], values[3], values[5]], + [0, 0, 1]] + result = self.multiply(result, a) + elif (function == 'translate'): + a = [[1, 0, values[0]], + [0, 1, values[1]], + [0, 0, 1]] + result = self.multiply(result, a) + elif (function == 'scale'): + a = [[values[0], 0, 0], + [0, values[1], 0], + [0, 0, 1]] + result = self.multiply(result, a) + elif (function == 'rotate'): + if (len(values) == 1): + a = [[math.cos(values[0]), -math.sin(values[0]), 0], + [math.sin(values[0]), math.cos(values[0]), 0], + [0, 0, 1]] + result = self.multiply(result, a) + else: + a = [[1, 0, values[2]], + [0, 1, values[2]], + [0, 0, 1]] + result = self.multiply(result, a) + a = [[math.cos(values[0]), -math.sin(values[0]), 0], + [math.sin(values[0]), math.cos(values[0]), 0], + [0, 0, 1]] + result = self.multiply(result, a) + a = [[1, 0, -values[2]], + [0, 1, -values[2]], + [0, 0, 1]] + result = self.multiply(result, a) + elif (function == 'skewX'): + a = [[1, math.tan(values[0]), 0], + [0, 1, 0], + [0, 0, 1]] + result = self.multiply(result, a) + elif (function == 'skewY'): + a = [[1, 0, 0], + [math.tan(values[0]), 1, 0], + [0, 0, 1]] + result = self.multiply(result, a) + + return self.multiply(self.transform(el.getparent()), result) + + + def getPosition(self, el): + if not self.isset(el.attrib, 'x'): + return False + + x = self.sizeToPx(el.attrib['x'], 'x') + y = self.sizeToPx(el.attrib['y'], 'y') + v = [x, y, 1] + t = self.transform(el) + v = self.multiply(t, v) + + return {'coordinates': v, 'matrix': t} + + def setPosition(self, el, position): + c = position['coordinates'] + a = position['matrix'] + if (not self.isUnitMatrix(a)): + c = self.multiply(self.inverse(a), c) + el.set('x', str(c[0])) + el.set('y', str(c[1])) + + + def determinant(self, a): + if len(a) != 3: + return False + if (len(a[0]) != 3): + return False + + det = a[0][0] * (a[1][1] * a[2][2] - a[2][1] * a[1][2]) - a[0][1] * (a[1][0] * a[2][2] - a[2][0] * a[1][2]) + a[0][2] * (a[1][0] * a[2][1] - a[2][0] * a[1][1]) + + if (det == 0): + det = 0.00001 + + return det + + def minors(self, a): + if len(a) != 3: + return False + if (len(a[0]) != 3): + return False + + return [[a[1][1] * a[2][2] - a[2][1] * a[1][2], a[1][0] * a[2][2] - a[2][0] * a[1][2], a[1][0] * a[2][1] - a[2][0] * a[1][1]], + [a[0][1] * a[2][2] - a[2][1] * a[0][2], a[0][0] * a[2][2] - a[0][2] * a[2][0], a[0][0] * a[2][1] - a[2][0] * a[0][1]], + [a[0][1] * a[1][2] - a[1][1] * a[0][2], a[0][0] * a[1][2] - a[0][1] * a[0][2], a[0][0] * a[1][1] - a[1][0] * a[0][1]] + ] + + def cofactors(self, a): + if len(a) != 3: + return False + if (len(a[0]) != 3): + return False + + return [[a[0][0], -a[0][1], a[0][2]], + [-a[1][0], a[1][1], -a[1][2]], + [a[2][0], -a[2][1], a[2][2]] + ] + + def adjoint(self, a): + if len(a) != 3: + return False + if (len(a[0]) != 3): + return False + + return [[a[0][0], a[1][0], a[2][0]], + [a[0][1], a[1][1], a[2][1]], + [a[0][2], a[1][2], a[2][2]] + ] + + def inverse(self, a): + if len(a) != 3: + return False + if (len(a[0]) != 3): + return False + + det = self.determinant(a) + m = self.minors(a) + c = self.cofactors(m) + adj = self.adjoint(c) + + return [[adj[0][0] / det, adj[0][1] / det, adj[0][2] / det], + [adj[1][0] / det, adj[1][1] / det, adj[1][2] / det], + [adj[2][0] / det, adj[2][1] / det, adj[2][2] / det] + ] + + def multiply(self, a, v): + if len(a) != 3: + return False + if (len(a[0]) != 3): + return False + + if (len(v) != 3): + return False + + if (not self.isset(v[0], 0)): + return [a[0][0] * v[0] + a[0][1] * v[1] + a[0][2] * v[2], + a[1][0] * v[0] + a[1][1] * v[1] + a[1][2] * v[2], + a[2][0] * v[0] + a[2][1] * v[1] + a[2][2] * v[2] + ] + else: + return [[a[0][0] * v[0][0] + a[0][1] * v[1][0] + a[0][2] * v[2][0], a[0][0] * v[0][1] + a[0][1] * v[1][1] + a[0][2] * v[2][1], a[0][0] * v[0][2] + a[0][1] * v[1][2] + a[0][2] * v[2][2]], + [a[1][0] * v[0][0] + a[1][1] * v[1][0] + a[1][2] * v[2][0], a[1][0] * v[0][1] + a[1][1] * v[1][1] + a[1][2] * v[2][1], a[1][0] * v[0][2] + a[1][1] * v[1][2] + a[1][2] * v[2][2]], + [a[2][0] * v[0][0] + a[2][1] * v[1][0] + a[2][2] * v[2][0], a[2][0] * v[0][1] + a[2][1] * v[1][1] + a[2][2] * v[2][1], a[2][0] * v[0][2] + a[2][1] * v[1][2] + a[2][2] * v[2][2]] + ] + + def isUnitMatrix(self, a): + if (len(a) != 3): + return False + if (len(a[0]) != 3): + return False + + for i in range(3): + for j in range(3): + if (a[i][j] != self.unitMatrix[i][j]): + return False + + return True + + def reParse(self): + if os.name == 'nt': + path = os.environ['USERPROFILE'] + else: + path = os.path.expanduser("~") + text = inkex.etree.tostring(self.document.getroot()) + f = open(path + '/tmp.svg', 'w') + f.write(text) + f.close() + self.parse(path + '/tmp.svg') + + os.remove(path + '/tmp.svg') + + def matrix2string(self, a): + return 'matrix(' + str(a[0][0]) + ',' + str(a[1][0]) + ',' + str(a[0][1]) + ',' + str(a[1][1]) + ',' + str(a[0][2]) + ',' + str(a[1][2]) + ')' \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/meta.json b/extensions/fablabchemnitz/table_support/meta.json new file mode 100644 index 0000000..2f0bb6e --- /dev/null +++ b/extensions/fablabchemnitz/table_support/meta.json @@ -0,0 +1,21 @@ +[ + { + "name": "", + "id": "fablabchemnitz.de.table_", + "path": "table_support", + "dependent_extensions": null, + "original_name": "", + "original_id": "org.greygreen.inkscape.effects.table_", + "license": "GNU GPL v2", + "license_url": "https://sourceforge.net/projects/inkscape-tables/files/inkscape-table-1.0.tar.gz/download", + "comment": "ported to Inkscape v1 by Mario Voigt", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/table_support", + "fork_url": "https://sourceforge.net/projects/inkscape-tables", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Table+Support", + "inkscape_gallery_url": null, + "main_authors": [ + "sourceforge.net/lixapopescu", + "github.com/eridur-de" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table.py b/extensions/fablabchemnitz/table_support/table.py new file mode 100644 index 0000000..f87c105 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table.py @@ -0,0 +1,850 @@ +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import math + +import inkex +from inkex import Guide +from lxml import etree +import base_transform +import re + +class TableEngine(base_transform.BaseTransform): + + defaultId = 'inkscape-table' + cell_type_row = 'row' + cell_type_column = 'column' + normalizeFactor = 5 + tablesCount = 0 + tables = None + selectedTables = {} + mergedCells = {} + tablesTexts = {} + get_tables = True + auto_split = True + delimiter = ',' + + def __init__(self, get_tables = True, auto_split = True): + inkex.NSS['inkex'] = 'http://sodipodi.sourceforge.net/DTD/inkex-0.dtd' + self.get_tables = get_tables + self.auto_split = auto_split + base_transform.BaseTransform.__init__(self) + + def getTablesCount(self): + node = self.document.xpath('//inkex:tables', namespaces = inkex.NSS) + if len(node) == 0: + xml = '' + self.document.getroot().append(etree.fromstring(xml)) + node = self.document.xpath('//inkex:tables', namespaces = inkex.NSS) + else: + self.tablesCount = int(node[0].attrib['count']) + + self.tables = node[0] + + def isTableCell(self, id): + el = self.svg.getElementById(id) + if (el == None): + return False + + if (self.isset(el.attrib, inkex.addNS('table-id', 'inkex'))): + tableId = el.attrib[inkex.addNS('table-id', 'inkex')] + if (re.search('\\-text$', tableId)): + return False + else: + return True + + return False + + def effect(self): + self.getTablesCount() + if (self.get_tables): + self.getAllTables() + if (self.auto_split): + for id, table in self.selectedTables.items(): + for i in range(len(table)): + for j in range(len(table[i])): + if (table[i][j] != None): + points = self.splitCell(table[i][j]) + if (points != False): + if (self.isset(self.mergedCells, id)): + self.mergedCells[id].append(points) + else: + self.mergedCells[id] = [points] + for tableId in self.mergedCells: + self.getTable(tableId) + self.doinkex() + if (self.get_tables): + if (self.auto_split): + for tableId in self.mergedCells: + self.getTableText(tableId) + for points in self.mergedCells[tableId]: + if (not self.isset(points, 'removed')): + self.mergeTable(tableId, points) + + def newCell(self, x, y, width, height, id, i, j, transform = None): + #path = '//*[@inkex:table-id="%s"]' % id + _id = self.svg.get_unique_id(id) + etree.SubElement(self.svg.get_current_layer(), 'rect', { + 'id': _id, + 'style': 'fill:none;stroke:#000000;stroke-width:1px;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none', + 'width': str(width) + 'px', + 'height': str(height) + 'px', + 'x': str(x) + 'px', + 'y': str(y) + 'px', + inkex.addNS('table-id', 'inkex'): id, + inkex.addNS('row', 'inkex'): str(i), + inkex.addNS('column', 'inkex'): str(j) + }) + + if (transform != None): + el = self.svg.getElementById(_id) + el.set('transform', transform) + + return _id + + ''' + _id = self.svg.get_unique_id(id) + content = '' + + self.svg.get_current_layer().append(etree.XML(content)) + el = self.svg.getElementById(_id) + el.set(inkex.addNS('table-id', 'inkex'), id) + el.set(inkex.addNS('row', 'inkex'), str(i)) + el.set(inkex.addNS('column', 'inkex'), str(j)) + ''' + + def create(self, width, height, cols, rows): + tableId = self.defaultId + str(self.tablesCount) + self.tablesCount += 1 + self.tables.set('count', str(self.tablesCount)) + content = '' + self.tables.append(etree.fromstring(content)) + + width = self.sizeToPx(width, 'x') + height = self.sizeToPx(height, 'y') + + x = 0 + y = 0 + + content = '' + + for i in range(rows): + x = 0 + for j in range(cols): + self.newCell(x, y, width, height, tableId, i, j) + x += width + y += height + + def getTree(self, id): + ids = [id] + el = self.svg.getElementById(id) + for _el in list(el): + for _id in self.getTree(_el.attrib['id']): + ids.append(_id) + return ids + + + def getSubSelectedIds(self): + ids = [] + for id in self.svg.selected.ids: + for _id in self.getTree(id): + ids.append(_id) + return ids + + def getAllTables(self): + ids = self.getSubSelectedIds() + for id in ids: + el = self.svg.getElementById(id) + if (self.isTableCell(id)): + tableId = el.attrib[inkex.addNS('table-id', 'inkex')] + if (not self.isset(self.selectedTables, tableId)): + self.getTable(tableId) + self.tablesTexts[tableId] = self.getTableText(tableId) + + def getTableDimensions(self, tableId): + nodes = self.tables.xpath('//inkex:table[@table-id="' + tableId + '"]', namespaces = inkex.NSS) + if (len(nodes) > 0): + return {'rows': int(nodes[0].attrib['rows']), 'cols': int(nodes[0].attrib['columns'])} + return False + + def setTableDimensions(self, tableId, dimensions): + table_dim = self.tables.xpath('//inkex:table[@table-id="' + tableId + '"]', namespaces = inkex.NSS) + if (len(table_dim) > 0): + table_dim[0].set('rows', str(dimensions['rows'])) + table_dim[0].set('columns', str(dimensions['cols'])) + + + def getTable(self, tableId): + nodes = self.tables.xpath('//inkex:table[@table-id="' + tableId + '"]', namespaces = inkex.NSS) + if (len(nodes) > 0): + cols = int(nodes[0].attrib['columns']) + rows = int(nodes[0].attrib['rows']) + table = [[None for i in range(cols)] for j in range(rows)] + path = '//*[@inkex:table-id="' + tableId + '"]' + cells = self.document.xpath(path, namespaces = inkex.NSS) + for cell in cells: + i = int(cell.attrib[inkex.addNS('row', 'inkex')]) + j = int(cell.attrib[inkex.addNS('column', 'inkex')]) + table[i][j] = cell.attrib['id'] + self.selectedTables[tableId] = table + + def getTableText(self, tableId): + nodes = self.tables.xpath('//inkex:table[@table-id="' + tableId + '"]', namespaces = inkex.NSS) + if (len(nodes) > 0): + cols = int(nodes[0].attrib['columns']) + rows = int(nodes[0].attrib['rows']) + texts = [[None for i in range(cols)] for j in range(rows)] + path = '//*[@inkex:table-id="' + tableId + '-text"]' + cells = self.document.xpath(path, namespaces = inkex.NSS) + for cell in cells: + i = int(cell.attrib[inkex.addNS('row', 'inkex')]) + j = int(cell.attrib[inkex.addNS('column', 'inkex')]) + texts[i][j] = cell.attrib['id'] + return texts + return None + + def doAddGuide(self, el, type): + px = self.sizeToPx(str(self.svg.unittouu(self.document.getroot().attrib['height'])), 'y') + + position = self.getPosition(el) + if (position != False): + c = position['coordinates'] + a = position['matrix'] + x = c[0] + y = c[1] + angle = math.acos(a[0][0]) * 180 / math.pi + if angle < 90: + angle = 90 - angle + elif angle < 180: + angle = 180 - angle + elif angle < 270: + angle = 270 - angle + else: + angle = 360 - angle + if (type == self.cell_type_row): + angle += 90 + self.svg.namedview.add(Guide().move_to(str(x), str(px - y), angle)) + + def _addGuides(self, tableId, type): + table = self.selectedTables[tableId] + + count = len(table) + if (type == self.cell_type_column): + count = len(table[0]) + + for i in range(count): + _i = i + _j = 0 + if (type == self.cell_type_column): + _i = 0 + _j = i + el = self.svg.getElementById(table[_i][_j]) + self.doAddGuide(el, type) + + if (i == count - 1): + if (type == self.cell_type_column): + el.attrib['x'] = str(self.sizeToPx(el.attrib['x'], 'x') + self.sizeToPx(el.attrib['width'], 'x')) + else: + el.attrib['y'] = str(self.sizeToPx(el.attrib['y'], 'y') + self.sizeToPx(el.attrib['height'], 'y')) + self.doAddGuide(el, type) + if (type == self.cell_type_column): + el.attrib['x'] = str(self.sizeToPx(el.attrib['x'], 'x') - self.sizeToPx(el.attrib['width'], 'x')) + else: + el.attrib['y'] = str(self.sizeToPx(el.attrib['y'], 'y') - self.sizeToPx(el.attrib['height'], 'y')) + + def addGuides(self, type): + for tableId in self.selectedTables: + self._addGuides(tableId, type) + + def doEditText(self, id, fontSize): + el = self.svg.getElementById(id) + if (not self.isTableCell(id)): + return + + position = self.getPosition(el) + if (position != False): + a = position['matrix'] + if (not self.isUnitMatrix(a)): + transform = 'transform="' + self.matrix2string(a) + '"' + else: + transform = '' + content = 'text here' + + self.svg.get_current_layer().append(etree.fromstring(content)) + + def editText(self, fontSize): + ids = self.getSubSelectedIds() + + for id in ids: + self.doEditText(id, fontSize) + + def getColumnIndex(self, id): + el = self.svg.getElementById(id) + if (self.isset(el.attrib, inkex.addNS('column', 'inkex'))): + return int(el.attrib[inkex.addNS('column', 'inkex')]) + + return -1 + + def getRowIndex(self, id): + el = self.svg.getElementById(id) + if (self.isset(el.attrib, inkex.addNS('row', 'inkex'))): + return int(el.attrib[inkex.addNS('row', 'inkex')]) + + return -1 + + def setTextRect(self, text, c): + for child in list(text): + if (child.tag == inkex.addNS('flowRegion', 'svg')): + for subchild in list(child): + if (subchild.tag == inkex.addNS('rect', 'svg')): + for key, value in c.items(): + if value != None: + subchild.set(key, str(value)) + break + + def getTextRect(self, text): + for child in list(text): + if (child.tag == inkex.addNS('flowRegion', 'svg')): + for subchild in list(child): + if (subchild.tag == inkex.addNS('rect', 'svg')): + return subchild + + return None + + def moveCells(self, tableId, idx, delta, type): + table = self.selectedTables[tableId] + texts = self.tablesTexts[tableId] + if (type == self.cell_type_column): + starti = 0 + startj = idx + else: + starti = idx + startj = 0 + + for i in range(starti, len(table)): + for j in range(startj, len(table[i])): + el = self.svg.getElementById(table[i][j]) + position = self.getPosition(el) + if (position != False): + c = [self.sizeToPx(el.attrib['x'], 'x'), self.sizeToPx(el.attrib['y'], 'y')] + c[0] += delta[0] + c[1] += delta[1] + el.set('x', str(c[0])) + el.set('y', str(c[1])) + if (texts != None): + if (texts[i][j] != None): + el = self.svg.getElementById(texts[i][j]) + rect = self.getTextRect(el) + if (rect != None): + c[0] = self.sizeToPx(rect.attrib['x'], 'x') + delta[0] + c[1] = self.sizeToPx(rect.attrib['y'], 'y') + delta[1] + self.setTextRect(el, {'x': c[0], 'y': c[1]}) + + def setCellSize(self, tableId, idx, size, type): + table = self.selectedTables[tableId] + texts = self.tablesTexts[tableId] + if (type == self.cell_type_column): + size = self.sizeToPx(size, 'x') + old_size = self.sizeToPx(self.svg.getElementById(table[0][idx]).attrib['width'], 'x') + else: + size = self.sizeToPx(size, 'y') + old_size = self.sizeToPx(self.svg.getElementById(table[idx][0]).attrib['height'], 'y') + + if (type == self.cell_type_column): + delta = [size - old_size, 0, 1] + else: + delta = [0, size - old_size, 1] + + if ((idx + 1 < len(table) and type == self.cell_type_row) or (idx + 1 < len(table[0]) and type == self.cell_type_column)): + self.moveCells(tableId, idx + 1, delta, type) + + count = len(table[idx]) + if (type == self.cell_type_column): + count = len(table) + + for i in range(count): + _i = idx + _j = i + if type == self.cell_type_column: + _i = i + _j = idx + el = self.svg.getElementById(table[_i][_j]) + param = 'height' + if (type == self.cell_type_column): + param = 'width' + + el.set(param, str(size)) + + if texts != None: + if texts[_i][_j] != None: + el = self.svg.getElementById(texts[_i][_j]) + self.setTextRect(el, {param: size}) + + def editSize(self, size, type): + processed = {} + for node in self.svg.selected.values(): + id = node.get('id') + if (self.isTableCell(id)): + tableId = node.attrib[inkex.addNS('table-id', 'inkex')] + if (type == self.cell_type_column): + idx = self.getColumnIndex(id) + else: + idx = self.getRowIndex(id) + if (not self.isset(processed, tableId)): + processed[tableId] = [] + + if (not self.isset(processed[tableId], idx)): + self.setCellSize(tableId, idx, size, type) + processed[tableId].append(idx) + + def getTableWidth(self, tableId): + table = self.selectedTables[tableId] + width = 0 + for i in range(len(table[0])): + el = self.svg.getElementById(table[0][i]) + width += self.sizeToPx(el.attrib['width'], 'x') + + return width + + def getTableHeight(self, tableId): + table = self.selectedTables[tableId] + height = 0 + for i in range(len(table)): + el = self.svg.getElementById(table[i][0]) + height += self.sizeToPx(el.attrib['height'], 'y') + + return height + + def setTableSize(self, tableId, size, type): + if (type == self.cell_type_column): + size = self.sizeToPx(size, 'x') + old_size = self.getTableWidth(tableId) + else: + size = self.sizeToPx(size, 'y') + old_size = self.getTableHeight(tableId) + + factor = size / old_size + table = self.selectedTables[tableId] + count = len(table) + if (type == self.cell_type_column): + count = len(table[0]) + + for i in range(count): + if (type == self.cell_type_column): + el = self.svg.getElementById(table[0][i]) + new_size = self.sizeToPx(el.attrib['width'], 'x') * factor + else: + el = self.svg.getElementById(table[i][0]) + new_size = self.sizeToPx(el.attrib['height'], 'y') * factor + self.setCellSize(tableId, i, str(new_size), type) + + def editTable(self, width, height): + for id in self.selectedTables: + self.setTableSize(id, width, self.cell_type_column) + self.setTableSize(id, height, self.cell_type_row) + + def getTablePosition(self, tableId): + table = self.selectedTables[tableId] + el = self.svg.getElementById(table[0][0]) + return self.getPosition(el) + + def fitPage(self): + width = str(self.svg.unittouu(self.document.getroot().attrib['width'])) + height = str(self.svg.unittouu(self.document.getroot().attrib['height'])) + + for id in self.selectedTables: + position = self.getTablePosition(id) + if (position != False): + c = position['coordinates'] + self.moveCells(id, 0, [-c[0], -c[1], 1], type) + self.setTableSize(id, width, self.cell_type_column) + self.setTableSize(id, height, self.cell_type_row) + + def fitPageWidth(self): + width = str(self.svg.unittouu(self.document.getroot().attrib['width'])) + + for id in self.selectedTables: + position = self.getTablePosition(id) + if (position != False): + c = position['coordinates'] + self.moveCells(id, 0, [-c[0], 0, 1], type) + self.setTableSize(id, width, self.cell_type_column) + + def fitPageHeight(self): + height = str(self.svg.unittouu(self.document.getroot().attrib['height'])) + + for id in self.selectedTables: + position = self.getTablePosition(id) + if (position != False): + c = position['coordinates'] + self.moveCells(id, 0, [0, -c[1], 1], type) + self.setTableSize(id, height, self.cell_type_row) + + def getSelectedListIds(self): + idList = [] + for id in self.getSubSelectedIds(): + idList.append(id) + return idList + + def getCellText(self, tableId, i, j): + texts = self.tablesTexts[tableId] + if (texts != None): + if (texts[i][j] != None): + return self.svg.getElementById(texts[i][j]) + return None + + def getMergePoints(self, tableId): + dim = self.getTableDimensions(tableId) + table = self.selectedTables[tableId] + idList = self.getSelectedListIds() + start = [] + for i in range(dim['rows']): + for j in range(dim['cols']): + if (idList.count(table[i][j]) > 0): + start = [i, j] + break + if len(start) > 0: + break + + if (len(start) != 2): + return False + + end = [1, 1] + + for i in range(start[0] + 1, len(table)): + if (idList.count(table[i][start[1]]) > 0): + end[0] += 1 + else: + break + + for i in range(start[1] + 1, len(table[0])): + if (idList.count(table[start[0]][i]) > 0): + end[1] += 1 + else: + break + + for i in range(start[0], start[0] + end[0]): + for j in range(start[1], start[1] + end[1]): + if (idList.count(table[i][j]) == 0): + return False + + return {'i': start[0], 'j': start[1], 'rows': end[0], 'cols': end[1]} + + def mergeTable(self, tableId, points = None): + if (points == None): + points = self.getMergePoints(tableId) + if (points == False): + inkex.errormsg('You have to select the cells to form a rectangle from a single table.') + return + + table = self.selectedTables[tableId] + cell = self.svg.getElementById(table[points['i']][points['j']]) + width = 0 + height = 0 + widths = '' + heights = '' + + for i in range(points['i'], points['i'] + points['rows']): + el = self.svg.getElementById(table[i][points['j']]) + height += self.sizeToPx(el.attrib['height'], 'y') + if (heights != ''): + heights += self.delimiter + heights += el.attrib['height'] + + for j in range(points['j'], points['j'] + points['cols']): + el = self.svg.getElementById(table[points['i']][j]) + width += self.sizeToPx(el.attrib['width'], 'x') + if (widths != ''): + widths += self.delimiter + widths += el.attrib['width'] + + for i in range(points['i'], points['i'] + points['rows']): + for j in range(points['j'], points['j'] + points['cols']): + if (i != points['i'] or j != points['j']): + el = self.svg.getElementById(table[i][j]) + el.getparent().remove(el) + text = self.getCellText(tableId, i, j) + if (text != None): + text.getparent().remove(text) + + cell.set('width', str(width) + 'px') + cell.set('height', str(height) + 'px') + cell.set(inkex.addNS('merged', 'inkex'), str(points['rows']) + self.delimiter + str(points['cols'])) + cell.set(inkex.addNS('merged-columns-widths', 'inkex'), widths) + cell.set(inkex.addNS('merged-rows-heights', 'inkex'), heights) + + text = self.getCellText(tableId, points['i'], points['j']) + + if (text != None): + rect = self.getTextRect(text) + rect.set('width', str(width) + 'px') + rect.set('height', str(height) + 'px') + + + def mergeMerge(self): + for id in self.selectedTables: + self.mergeTable(id) + + def splitCell(self, cellId): + cell = self.svg.getElementById(cellId) + if (self.isset(cell.attrib, inkex.addNS('merged', 'inkex'))): + tableId = cell.attrib[inkex.addNS('table-id', 'inkex')] + row = int(cell.attrib[inkex.addNS('row', 'inkex')]) + column = int(cell.attrib[inkex.addNS('column', 'inkex')]) + position = self.getPosition(cell) + + merge_size = cell.attrib[inkex.addNS('merged', 'inkex')].split(self.delimiter) + widths = cell.attrib[inkex.addNS('merged-columns-widths', 'inkex')].split(self.delimiter) + heights = cell.attrib[inkex.addNS('merged-rows-heights', 'inkex')].split(self.delimiter) + + y = self.sizeToPx(cell.attrib['y'], 'y') + + for i in range(row, row + int(merge_size[0])): + x = self.sizeToPx(cell.attrib['x'], 'x') + for j in range(column, column + int(merge_size[1])): + if (i != row or j != column): + transform = None + if position != False: + a = position['matrix'] + if (not self.isUnitMatrix(a)): + transform = self.matrix2string(a) + self.newCell(x, y, self.sizeToPx(widths[j - column], 'x'), self.sizeToPx(heights[i - row], 'y'), tableId, i, j, transform) + x += self.sizeToPx(widths[j - column], 'x') + y += self.sizeToPx(heights[i - row], 'y') + + result = {'i': row, 'j': column, 'rows': int(merge_size[0]), 'cols': int(merge_size[1])} + cell.set('width', widths[0]) + cell.set('height', heights[0]) + text = self.getCellText(tableId, row, column) + if (text != None): + rect = self.getTextRect(text) + rect.set('width', widths[0]) + rect.set('height', heights[0]) + del cell.attrib[inkex.addNS('merged', 'inkex')] + del cell.attrib[inkex.addNS('merged-columns-widths', 'inkex')] + del cell.attrib[inkex.addNS('merged-rows-heights', 'inkex')] + + return result + return False + + def mergeSplit(self): + for id in self.svg.selected.ids: + self.splitCell(id) + + def updateMergedPoints(self, tableId, idx, delta, type): + if (self.get_tables): + if (self.auto_split): + if (self.isset(self.mergedCells, tableId)): + for points in self.mergedCells[tableId]: + cond1 = idx > points['i'] and idx < points['i'] + points['rows'] + cond2 = idx <= points['i'] + if (type == self.cell_type_column): + cond1 = idx > points['j'] and idx < points['j'] + points['cols'] + cond2 = idx <= points['j'] + + if (cond1): + if (type == self.cell_type_column): + points['cols'] += delta + if (points['cols'] <= 1): + points['removed'] = 1 + else: + points['rows'] += delta + if (points['rows'] <= 1): + points['removed'] = 1 + elif (cond2): + if (type == self.cell_type_column): + if (delta == -1 and idx == points['j']): + points['cols'] += delta + if (points['cols'] <= 1): + points['removed'] = 1 + else: + points['j'] += delta + else: + if (delta == -1 and idx == points['i']): + points['rows'] += delta + if (points['rows'] <= 1): + points['removed'] = 1 + else: + points['i'] += delta + + def incrementCoordonate(self, tableId, idx, inc, type): + table = self.selectedTables[tableId] + texts = self.getTableText(tableId) + starti = idx + startj = 0 + dim = self.getTableDimensions(tableId) + if type == self.cell_type_column: + dim['cols'] += inc + else: + dim['rows'] += inc + self.setTableDimensions(tableId, dim) + + if (type == self.cell_type_column): + starti = 0 + startj = idx + + for i in range(starti, len(table)): + for j in range(startj, len(table[i])): + cell = self.svg.getElementById(table[i][j]) + text = self.svg.getElementById(texts[i][j]) + if (type == self.cell_type_column): + cell.set(inkex.addNS('column', 'inkex'), str(int(cell.attrib[inkex.addNS('column', 'inkex')]) + inc)) + if (text != None): + text.set(inkex.addNS('column', 'inkex'), str(int(text.attrib[inkex.addNS('column', 'inkex')]) + inc)) + else: + cell.set(inkex.addNS('row', 'inkex'), str(int(cell.attrib[inkex.addNS('row', 'inkex')]) + inc)) + if (text != None): + text.set(inkex.addNS('row', 'inkex'), str(int(text.attrib[inkex.addNS('row', 'inkex')]) + inc)) + + def addCell(self, tableId, idx, type): + table = self.selectedTables[tableId] + if (type == self.cell_type_column): + if (idx != -1): + delta = [self.sizeToPx(self.svg.getElementById(table[0][idx]).attrib['width'], 'x'), 0, 1] + else: + delta = [self.sizeToPx(self.svg.getElementById(table[0][0]).attrib['width'], 'x'), 0, 1] + else: + if (idx != -1): + delta = [0, self.sizeToPx(self.svg.getElementById(table[idx][0]).attrib['height'], 'y'), 1] + else: + delta = [0, self.sizeToPx(self.svg.getElementById(table[0][0]).attrib['height'], 'y'), 1] + + count = len(table) + if type == self.cell_type_row: + if (idx != -1): + count = len(table[idx]) + else: + count = len(table[0]) + for i in range(count): + if (type == self.cell_type_column): + if (idx != -1): + cell = self.svg.getElementById(table[i][idx]) + else: + cell = self.svg.getElementById(table[i][0]) + else: + if (idx != -1): + cell = self.svg.getElementById(table[idx][i]) + else: + cell = self.svg.getElementById(table[0][i]) + + position = self.getPosition(cell) + transform = '' + if (position != False): + a = position['matrix'] + if (not self.isUnitMatrix(a)): + transform = self.matrix2string(a) + x = self.sizeToPx(cell.attrib['x'], 'x') + y = self.sizeToPx(cell.attrib['y'], 'y') + width = self.sizeToPx(cell.attrib['width'], 'x') + height = self.sizeToPx(cell.attrib['height'], 'y') + + if (type == self.cell_type_column): + if (idx != -1): + x += width + self.newCell(x, y, width, height, tableId, i, idx + 1, transform) + else: + if (idx != -1): + y += height + self.newCell(x, y, width, height, tableId, idx + 1, i, transform) + + self.moveCells(tableId, idx + 1, delta, type) + self.updateMergedPoints(tableId, idx + 1, 1, type) + self.incrementCoordonate(tableId, idx + 1, 1, type) + self.getTable(tableId) + self.tablesTexts[tableId] = self.getTableText(tableId) + + def addColumns(self, count, where): + for id in self.svg.selected.ids: + el = self.svg.getElementById(id) + if (self.isTableCell(id)): + tableId = el.attrib[inkex.addNS('table-id', 'inkex')] + idx = self.getColumnIndex(id) + if (where == 'before'): + idx -= 1 + + for i in range(count): + self.addCell(tableId, idx, self.cell_type_column) + + def addRows(self, count, where): + for id in self.svg.selected.ids: + el = self.svg.getElementById(id) + if (self.isTableCell(id)): + tableId = el.attrib[inkex.addNS('table-id', 'inkex')] + idx = self.getRowIndex(id) + if (where == 'before'): + idx -= 1 + + for i in range(count): + self.addCell(tableId, idx, self.cell_type_row) + + break + + def removeCell(self, tableId, idx, type): + table = self.selectedTables[tableId] + texts = self.tablesTexts[tableId] + if (type == self.cell_type_column): + delta = [-self.sizeToPx(self.svg.getElementById(table[0][idx]).attrib['width'], 'x'), 0, 1] + else: + delta = [0, -self.sizeToPx(self.svg.getElementById(table[idx][0]).attrib['height'], 'y'), 1] + + count = len(table) + if type == self.cell_type_row: + count = len(table[idx]) + + for i in range(count): + if (type == self.cell_type_column): + cell = self.svg.getElementById(table[i][idx]) + text = self.svg.getElementById(texts[i][idx]) + else: + cell = self.svg.getElementById(table[idx][i]) + text = self.svg.getElementById(texts[idx][i]) + + if (cell != None): + cell.getparent().remove(cell) + if (text != None): + text.getparent().remove(text) + + self.moveCells(tableId, idx + 1, delta, type) + self.updateMergedPoints(tableId, idx, -1, type) + self.incrementCoordonate(tableId, idx + 1, -1, type) + self.getTable(tableId) + self.tablesTexts[tableId] = self.getTableText(tableId) + + def removeRowsColumns(self, type): + for id in self.svg.selected.ids: + el = self.svg.getElementById(id) + if (el != None): + if (self.isTableCell(id)): + tableId = el.attrib[inkex.addNS('table-id', 'inkex')] + if (type == self.cell_type_column): + idx = self.getColumnIndex(id) + else: + idx = self.getRowIndex(id) + + self.removeCell(tableId, idx, type) diff --git a/extensions/fablabchemnitz/table_support/table_add_columns.inx b/extensions/fablabchemnitz/table_support/table_add_columns.inx new file mode 100644 index 0000000..e442c07 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_add_columns.inx @@ -0,0 +1,23 @@ + + + Columns + fablabchemnitz.de.table_add_columns + 1 + + + + + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_add_columns.py b/extensions/fablabchemnitz/table_support/table_add_columns.py new file mode 100755 index 0000000..06a458f --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_add_columns.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self, True, True) + self.arg_parser.add_argument('--number', type = int, default = 1, help = 'The number of columns') + self.arg_parser.add_argument('--where', default = "after", help = 'Where to add') + + def doinkex(self): + self.addColumns(self.options.number, self.options.where) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_add_guides.inx b/extensions/fablabchemnitz/table_support/table_add_guides.inx new file mode 100644 index 0000000..0cf1c39 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_add_guides.inx @@ -0,0 +1,18 @@ + + + Guide Lines + fablabchemnitz.de.table_add_guides + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_add_guides.py b/extensions/fablabchemnitz/table_support/table_add_guides.py new file mode 100755 index 0000000..8cdda97 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_add_guides.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self) + + def doinkex(self): + self.addGuides(self.cell_type_row) + self.addGuides(self.cell_type_column) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_add_rows.inx b/extensions/fablabchemnitz/table_support/table_add_rows.inx new file mode 100644 index 0000000..2684455 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_add_rows.inx @@ -0,0 +1,23 @@ + + + Rows + fablabchemnitz.de.table_add_rows + 1 + + + + + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_add_rows.py b/extensions/fablabchemnitz/table_support/table_add_rows.py new file mode 100755 index 0000000..b212045 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_add_rows.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self, True, True) + self.arg_parser.add_argument('--number', type = int, default = 1, help = 'The number of rows') + self.arg_parser.add_argument('--where', default = "after", help = 'Where to add') + + def doinkex(self): + self.addRows(self.options.number, self.options.where) + +if __name__ == '__main__': #pragma: no cover + Table().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_create.inx b/extensions/fablabchemnitz/table_support/table_create.inx new file mode 100644 index 0000000..0420a5c --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_create.inx @@ -0,0 +1,28 @@ + + + Create + fablabchemnitz.de.table_create + + + 2 + 2 + 100mm + 20mm + + + + + + + + all + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_create.py b/extensions/fablabchemnitz/table_support/table_create.py new file mode 100755 index 0000000..40d4f8c --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_create.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + + def add_arguments(self, pars): + pars.add_argument("--rows", type=int, default=2, help='The number of table rows') + pars.add_argument("--cols", type=int, default=2, help='The number of table columns') + pars.add_argument("--width", default='100mm', help='The width of the table') + pars.add_argument("--height", default='20mm', help='The height of the table') + pars.add_argument("--tab") + + def doinkex(self): + self.create(self.options.width, self.options.height, self.options.cols, self.options.rows) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_edit_columns.inx b/extensions/fablabchemnitz/table_support/table_edit_columns.inx new file mode 100644 index 0000000..6624b60 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_columns.inx @@ -0,0 +1,20 @@ + + + Selected columns width + fablabchemnitz.de.table_edit_columns + 10mm + + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_edit_columns.py b/extensions/fablabchemnitz/table_support/table_edit_columns.py new file mode 100755 index 0000000..a36b821 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_columns.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + + def add_arguments(self, pars): + pars.add_argument("--width", default='10mm', help='The new width') + + def doinkex(self): + self.editSize(self.options.width, self.cell_type_column) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_edit_rows.inx b/extensions/fablabchemnitz/table_support/table_edit_rows.inx new file mode 100644 index 0000000..58c179f --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_rows.inx @@ -0,0 +1,20 @@ + + + Selected rows height + fablabchemnitz.de.table_edit_rows + 10mm + + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_edit_rows.py b/extensions/fablabchemnitz/table_support/table_edit_rows.py new file mode 100755 index 0000000..e71f847 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_rows.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + + def add_arguments(self, pars): + pars.add_argument("--height", default='10mm', help='The new height') + + def doinkex(self): + self.editSize(self.options.height, self.cell_type_row) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_edit_table.inx b/extensions/fablabchemnitz/table_support/table_edit_table.inx new file mode 100644 index 0000000..4c9ac43 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_table.inx @@ -0,0 +1,21 @@ + + + Selected table width and height + fablabchemnitz.de.table_edit_table + 150mm + 60mm + + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_edit_table.py b/extensions/fablabchemnitz/table_support/table_edit_table.py new file mode 100755 index 0000000..01ccedf --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_table.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + + def add_arguments(self, pars): + pars.add_argument("--width", default='150mm', help='The new width') + pars.add_argument("--height", default='60mm', help='The new height') + + def doinkex(self): + self.editTable(self.options.width, self.options.height) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_edit_text.inx b/extensions/fablabchemnitz/table_support/table_edit_text.inx new file mode 100644 index 0000000..2b8413e --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_text.inx @@ -0,0 +1,19 @@ + + + Selected cells text + fablabchemnitz.de.table_edit_text + 12px + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_edit_text.py b/extensions/fablabchemnitz/table_support/table_edit_text.py new file mode 100755 index 0000000..b21b10f --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_edit_text.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + + def add_arguments(self, pars): + pars.add_argument("--size", default='12px', help='The font size') + + def doinkex(self): + self.editText(self.options.size) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_fit_height.inx b/extensions/fablabchemnitz/table_support/table_fit_height.inx new file mode 100644 index 0000000..b830aa1 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_fit_height.inx @@ -0,0 +1,18 @@ + + + Selected table to page height + fablabchemnitz.de.table_fit_height + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_fit_height.py b/extensions/fablabchemnitz/table_support/table_fit_height.py new file mode 100644 index 0000000..7b6d6cb --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_fit_height.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self) + + def doinkex(self): + self.fitPageHeight() + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_fit_page.inx b/extensions/fablabchemnitz/table_support/table_fit_page.inx new file mode 100644 index 0000000..f371f7b --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_fit_page.inx @@ -0,0 +1,18 @@ + + + Selected table to page + fablabchemnitz.de.table_fit_page + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_fit_page.py b/extensions/fablabchemnitz/table_support/table_fit_page.py new file mode 100644 index 0000000..2689e46 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_fit_page.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" + +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + + def doinkex(self): + self.fitPage() + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_fit_width.inx b/extensions/fablabchemnitz/table_support/table_fit_width.inx new file mode 100644 index 0000000..779f481 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_fit_width.inx @@ -0,0 +1,18 @@ + + + Selected table to page width + fablabchemnitz.de.table_fit_width + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_fit_width.py b/extensions/fablabchemnitz/table_support/table_fit_width.py new file mode 100644 index 0000000..1ac7626 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_fit_width.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self) + + def doinkex(self): + self.fitPageWidth() + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_merge_merge.inx b/extensions/fablabchemnitz/table_support/table_merge_merge.inx new file mode 100644 index 0000000..ebd9339 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_merge_merge.inx @@ -0,0 +1,18 @@ + + + Merge selected cells + fablabchemnitz.de.table_merge_merge + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_merge_merge.py b/extensions/fablabchemnitz/table_support/table_merge_merge.py new file mode 100644 index 0000000..89d0a02 --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_merge_merge.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self) + + def doinkex(self): + self.mergeMerge() + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_merge_split.inx b/extensions/fablabchemnitz/table_support/table_merge_split.inx new file mode 100644 index 0000000..1c7134c --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_merge_split.inx @@ -0,0 +1,18 @@ + + + Split selected cells + fablabchemnitz.de.table_merge_split + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_merge_split.py b/extensions/fablabchemnitz/table_support/table_merge_split.py new file mode 100755 index 0000000..c62c8cb --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_merge_split.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self, True, False) + + def doinkex(self): + self.mergeSplit() + +if __name__ == '__main__': #pragma: no cover + Table().run() \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_remove_columns.inx b/extensions/fablabchemnitz/table_support/table_remove_columns.inx new file mode 100644 index 0000000..eb840bf --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_remove_columns.inx @@ -0,0 +1,18 @@ + + + Selected columns + fablabchemnitz.de.table_remove_columns + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_remove_columns.py b/extensions/fablabchemnitz/table_support/table_remove_columns.py new file mode 100755 index 0000000..053f03e --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_remove_columns.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self, True, True) + + def doinkex(self): + self.removeRowsColumns(self.cell_type_column) + +if __name__ == '__main__': #pragma: no cover + Table().run() diff --git a/extensions/fablabchemnitz/table_support/table_remove_rows.inx b/extensions/fablabchemnitz/table_support/table_remove_rows.inx new file mode 100644 index 0000000..01f5dbc --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_remove_rows.inx @@ -0,0 +1,18 @@ + + + Selected rows + fablabchemnitz.de.table_remove_rows + + all + + + + + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/table_support/table_remove_rows.py b/extensions/fablabchemnitz/table_support/table_remove_rows.py new file mode 100755 index 0000000..11a3b3f --- /dev/null +++ b/extensions/fablabchemnitz/table_support/table_remove_rows.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +""" +table.py +Table support for Inkscape + +Copyright (C) 2011 Cosmin Popescu, cosminadrianpopescu@gmail.com + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +""" +import sys +import os +import table + +sys.path.append(os.path.dirname(sys.argv[0])) + +class Table(table.TableEngine): + def __init__(self): + table.TableEngine.__init__(self, True, True) + + def doinkex(self): + self.removeRowsColumns(self.cell_type_row) + +if __name__ == '__main__': #pragma: no cover + Table().run()