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')
+
+ # 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
+ 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 @@
+
+
+
+
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 0000000..c30c1c2
Binary files /dev/null and b/extensions/fablabchemnitz/output_pro/outputpro/alpha.png differ
diff --git a/extensions/fablabchemnitz/output_pro/outputpro/cmyk.py b/extensions/fablabchemnitz/output_pro/outputpro/cmyk.py
new file mode 100644
index 0000000..d939175
--- /dev/null
+++ b/extensions/fablabchemnitz/output_pro/outputpro/cmyk.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python3
+
+import re
+import os
+import inkex
+from inkex.command import inkscape
+
+def calculateCMYK(red, green, blue):
+ C = float()
+ M = float()
+ Y = float()
+ K = float()
+
+ if 1.00 - red < 1.00 - green:
+ K = 1.00 - red
+ else:
+ K = 1.00 - green
+
+ if 1.00 - blue < K:
+ K = 1.00 - blue
+
+ if K != 1.00:
+ C = ( 1.00 - red - K ) / ( 1.00 - K )
+ M = ( 1.00 - green - K ) / ( 1.00 - K )
+ Y = ( 1.00 - blue - K ) / ( 1.00 - K )
+
+ return [C, M, Y, K]
+
+def clean_svg_color_definitions(svg):
+ def change_colors(origin, color_type):
+ for i in range(len(str(origin).split(color_type + ':'))):
+ if str(str(origin).split(color_type + ':')[i].split(';')[0]) in inkex.colors.SVG_COLOR.keys():
+ color_numbers = str(inkex.Color(inkex.Color(str(str(origin).split(color_type + ':')[i].split(';')[0])).to_rgb()))
+ origin = str(origin).replace(':' + str(str(origin).split(color_type + ':')[i].split(';')[0]) + ';', ':' + color_numbers + ';')
+ return origin
+
+ colortypes = ['fill', 'stop-color', 'flood-color', 'lighting-color', 'stroke']
+ for i in range(len(colortypes)):
+ svg = change_colors(svg, colortypes[i])
+
+ return svg
+
+def removeK(origin):
+ def reset_opacity(value):
+ return str(value.group()).split('opacity:')[0] + "opacity:0;"
+ #return re.sub("#000000;fill-opacity:[0-9.]+;", reset_opacity, re.sub("#000000;stop-opacity:[0-9.]+;", reset_opacity, re.sub("#000000;stroke-opacity:[0-9.]+;", reset_opacity, re.sub("#000000;flood-opacity:[0-9.]+;", reset_opacity, re.sub("#000000;lighting-opacity:[0-9.]+;", reset_opacity, origin)))))
+ return re.sub("#000000;fill-opacity:[0-9.?]+", reset_opacity, re.sub("#000000;stop-opacity:[0-9.?]+", reset_opacity, re.sub("#000000;stroke-opacity:[0-9.?]+", reset_opacity, re.sub("#000000;flood-opacity:[0-9.?]+", reset_opacity, re.sub("#000000;lighting-opacity:[0-9.?]+", reset_opacity, origin)))))
+
+def representC(value):
+ # returns CMS color if available
+ if (re.search("icc-color", value.group())):
+ return str(inkex.Color((float(1.00 - float(re.split(r'[,\)\s]+',value.group())[2])), float(1.00), float(1.00))))
+ else:
+ red = float(inkex.Color(str(value.group())).to_rgb()[0]/255.00)
+ green = float(inkex.Color(str(value.group())).to_rgb()[1]/255.00)
+ blue = float(inkex.Color(str(value.group())).to_rgb()[2]/255.00)
+ return str(inkex.Color((float(1.00 - calculateCMYK(red, green, blue)[0]), float(1.00), float(1.00))))
+
+def representM(value):
+ # returns CMS color if available
+ if ( re.search("icc-color", value.group()) ):
+ return str(inkex.Color((float(1.00), float(1.00 - float(re.split(r'[,\)\s]+',value.group())[3])), float(1.00))))
+ else:
+ red = float(inkex.Color(str(value.group())).to_rgb()[0]/255.00)
+ green = float(inkex.Color(str(value.group())).to_rgb()[1]/255.00)
+ blue = float(inkex.Color(str(value.group())).to_rgb()[2]/255.00)
+ return str(inkex.Color((float(1.00), float(1.00 - calculateCMYK(red, green, blue)[1]), float(1.00))))
+
+def representY(value):
+ # returns CMS color if available
+ if (re.search("icc-color", value.group()) ):
+ return str(inkex.Color((float(1.00), float(1.00), float(1.00 - float(re.split(r'[,\)\s]+',value.group())[4])))))
+ else:
+ red = float(inkex.Color(str(value.group())).to_rgb()[0]/255.00)
+ green = float(inkex.Color(str(value.group())).to_rgb()[1]/255.00)
+ blue = float(inkex.Color(str(value.group())).to_rgb()[2]/255.00)
+ return str(inkex.Color((float(1.00), float(1.00), float(1.00 - calculateCMYK(red, green, blue)[2]))))
+
+def representK(value):
+ # returns CMS color if available
+ if (re.search("icc-color", value.group()) ):
+ return str(inkex.Color((float(1.00 - float(re.split(r'[,\)\s]+',value.group())[5])), float(1.00 - float(re.split(r'[,\)\s]+',value.group())[5])), float(1.00 - float(re.split(r'[,\)\s]+',value.group())[5])))))
+ else:
+ red = float(inkex.Color(str(value.group())).to_rgb()[0]/255.00)
+ green = float(inkex.Color(str(value.group())).to_rgb()[1]/255.00)
+ blue = float(inkex.Color(str(value.group())).to_rgb()[2]/255.00)
+ return str(inkex.Color((float(1.00 - calculateCMYK(red, green, blue)[3]), float(1.00 - calculateCMYK(red, green, blue)[3]), float(1.00 - calculateCMYK(red, green, blue)[3]))))
+
+
+def generate_svg_separations(temp_dir, original_source, overblack):
+ svg_ready = clean_svg_color_definitions(original_source)
+
+ with open(os.path.join(temp_dir, "separationK.svg"), "w") as f:
+ f.write(re.sub(r"#[a-fA-F0-9]{6}( icc-color\(.*?\))?", representK, svg_ready))
+
+ if overblack:
+ svg_ready = removeK(svg_ready)
+
+ with open(os.path.join(temp_dir, "separationC.svg"), "w") as f:
+ f.write(re.sub(r"#[a-fA-F0-9]{6}( icc-color\(.*?\))?", representC, svg_ready))
+ with open(os.path.join(temp_dir, "separationM.svg"), "w") as f:
+ f.write(re.sub(r"#[a-fA-F0-9]{6}( icc-color\(.*?\))?", representM, svg_ready))
+ with open(os.path.join(temp_dir, "separationY.svg"), "w") as f:
+ f.write(re.sub(r"#[a-fA-F0-9]{6}( icc-color\(.*?\))?", representY, svg_ready))
+
+def generate_png_separations(temp_dir, area_to_export, resolution, alpha):
+ if alpha:
+ alpha_command = ""
+ else:
+ alpha_command = ";export-background:white"
+ for color in ['C', 'M', 'Y', 'K']:
+ cmd = area_to_export + alpha_command + ';export-dpi:' + str(resolution) + ';export-background-opacity:1;export-filename:' + os.path.join(temp_dir, "separated" + area_to_export.replace(' ', '') + color + ".png") + ';export-do'
+ #inkex.utils.debug(cmd)
+ cli_output = inkscape(os.path.join(temp_dir, "separation" + color + ".svg"), actions=cmd)
+ if len(cli_output) > 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 0000000..9932f77
Binary files /dev/null and b/extensions/fablabchemnitz/output_pro/outputpro/preview_mask.png differ
diff --git a/extensions/fablabchemnitz/output_pro/outputpro/top.png b/extensions/fablabchemnitz/output_pro/outputpro/top.png
new file mode 100644
index 0000000..25dce74
Binary files /dev/null and b/extensions/fablabchemnitz/output_pro/outputpro/top.png differ
diff --git a/extensions/fablabchemnitz/output_pro/result-imp.jpeg b/extensions/fablabchemnitz/output_pro/result-imp.jpeg
new file mode 100644
index 0000000..e551156
Binary files /dev/null and b/extensions/fablabchemnitz/output_pro/result-imp.jpeg differ
diff --git a/extensions/fablabchemnitz/paths_to_openscad/meta.json b/extensions/fablabchemnitz/paths_to_openscad/meta.json
new file mode 100644
index 0000000..187a392
--- /dev/null
+++ b/extensions/fablabchemnitz/paths_to_openscad/meta.json
@@ -0,0 +1,22 @@
+[
+ {
+ "name": "Paths To OpenSCAD",
+ "id": "fablabchemnitz.de.paths_to_openscad",
+ "path": "paths_to_openscad",
+ "dependent_extensions": null,
+ "original_name": "aths to OpenSCAD<",
+ "original_id": "command.extrude.openscad",
+ "license": "GNU GPL v2",
+ "license_url": "https://github.com/fablabnbg/inkscape-paths2openscad/blob/master/LICENSE",
+ "comment": "",
+ "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.2/src/branch/master/extensions/fablabchemnitz/paths_to_openscad",
+ "fork_url": "https://github.com/fablabnbg/inkscape-paths2openscad",
+ "documentation_url": "https://stadtfabrikanten.org/display/IFM/Paths+to+OpenSCAD",
+ "inkscape_gallery_url": null,
+ "main_authors": [
+ "github.com/l0b0",
+ "github.com/fablabnbg",
+ "github.com/eridur-de"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.inx b/extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.inx
new file mode 100644
index 0000000..49922eb
--- /dev/null
+++ b/extensions/fablabchemnitz/paths_to_openscad/paths_to_openscad.inx
@@ -0,0 +1,95 @@
+
+
+ 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