This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/fablabchemnitz/primitive/primitive.py

195 lines
9.1 KiB
Python
Raw Normal View History

2020-08-21 16:05:51 +02:00
#!/usr/bin/env python3
import sys
import inkex
import os
import base64
import urllib.request as urllib
from PIL import Image
from io import BytesIO
from lxml import etree
from inkex import Color
"""
Extension for InkScape 1.X
Features
- Primitive - Reproducing images with geometric primitives written in Go.
Author: Mario Voigt / FabLab Chemnitz
Mail: mario.voigt@stadtfabrikanten.org
Date: 21.08.2020
2020-08-23 00:16:14 +02:00
Last patch: 23.08.2020
2020-08-21 16:05:51 +02:00
License: GNU GPL v3
Used version of Primitive: https://github.com/fogleman/primitive/commit/0373c216458be1c4b40655b796a3aefedf8b7d23
"""
2021-04-16 14:41:12 +02:00
class Primitive (inkex.EffectExtension):
2020-08-21 16:05:51 +02:00
def rgbToHex(self, pickerColor):
longcolor = int(pickerColor)
if longcolor < 0:
longcolor = longcolor & 0xFFFFFFFF
return '#' + format(longcolor >> 8, '06X')
def checkImagePath(self, node):
xlink = node.get('xlink:href')
if xlink and xlink[:5] == 'data:':
# No need, data alread embedded
return
url = urllib.urlparse(xlink)
href = urllib.url2pathname(url.path)
# Primary location always the filename itself.
path = self.absolute_href(href or '')
# Backup directory where we can find the image
if not os.path.isfile(path):
path = node.get('sodipodi:absref', path)
if not os.path.isfile(path):
inkex.errormsg('File not found "{}". Unable to embed image.').format(path)
return
if (os.path.isfile(path)):
return path
2021-04-16 14:41:12 +02:00
def add_arguments(self, pars):
2021-04-19 20:54:38 +02:00
pars.add_argument("--tab")
2021-04-16 14:41:12 +02:00
pars.add_argument("--keeporiginal", type=inkex.Boolean, default=False, help="Keep original image on canvas")
pars.add_argument("--cliprect", type=inkex.Boolean, default=True, help="Draw clipping rectangle")
pars.add_argument("--n", type=int, default=100, help="Number of shapes")
pars.add_argument("--m", default=1, help="Mode")
pars.add_argument("--rep", type=int, default=0,help="Extra shapes/iteration")
pars.add_argument("--r", type=int, default=256, help="Resize to size before processing (px)")
pars.add_argument("--s", type=int, default=1024, help="Output image size (px)")
pars.add_argument("--a", type=int, default=128, help="Color alpha")
pars.add_argument("--bg_enabled", type=inkex.Boolean, default=True, help="Use average starting background color")
pars.add_argument("--bg", type=Color, default=255, help="Starting background color")
pars.add_argument("--j", type=int, default=0, help="Number of parallel workers")
2020-08-21 16:05:51 +02:00
def effect(self):
# internal overwrite for scale:
self.options.scale = 1.0
if (self.options.ids):
for node in self.svg.selected.values():
if node.tag == inkex.addNS('image', 'svg'):
self.path = self.checkImagePath(node) # This also ensures the file exists
if self.path is None: # check if image is embedded or linked
image_string = node.get('{http://www.w3.org/1999/xlink}href')
# find comma position
i = 0
while i < 40:
if image_string[i] == ',':
break
i = i + 1
image = Image.open(BytesIO(base64.b64decode(image_string[i + 1:len(image_string)])))
else:
image = Image.open(self.path)
if node.get('width')[-1].isdigit() is False or node.get('height')[-1].isdigit() is False:
inkex.utils.debug("Image seems to have some weird dimensions in XML structure. Please remove units from width and height attributes at <svg:image>")
return
parent = node.getparent()
if parent is not None and parent != self.document.getroot():
tpc = parent.composed_transform()
x_offset = tpc.e
y_offset = tpc.f
else:
x_offset = 0.0
y_offset = 0.0
2020-08-21 16:05:51 +02:00
# Write the embedded or linked image to temporary directory
2020-08-30 12:29:46 +02:00
if os.name == "nt":
exportfile = "Primitive.png"
else:
exportfile = "/tmp/Primitive.png"
2021-05-15 20:13:31 +02:00
if image.mode != 'RGB':
image = image.convert('RGB')
2020-08-21 16:05:51 +02:00
image.save(exportfile, "png")
## Build up Primitive command according to your settings from extension GUI
2020-08-30 12:29:46 +02:00
if os.name == "nt":
command = "primitive"
else:
command = "./primitive"
2020-08-21 16:05:51 +02:00
command += " -m " + str(self.options.m)
command += " -rep " + str(self.options.rep)
command += " -r " + str(self.options.r)
command += " -s " + str(self.options.s)
command += " -a " + str(self.options.a)
if not self.options.bg_enabled:
command += " -bg " + self.rgbToHex(self.options.bg)
command += " -j " + str(self.options.j)
command += " -i " + exportfile
command += " -o " + exportfile + ".svg"
command += " -n " + str(self.options.n)
2020-08-21 16:10:22 +02:00
#inkex.utils.debug(command)
2020-08-21 16:05:51 +02:00
# Create the vector new SVG file
with os.popen(command, "r") as proc:
result = proc.read()
#inkex.utils.debug(result)
# proceed if new SVG file was successfully created
2020-08-30 12:29:46 +02:00
doc = None
2020-08-21 16:05:51 +02:00
if os.path.exists(exportfile + ".svg"):
# Delete the temporary png file again because we do not need it anymore
if os.path.exists(exportfile):
os.remove(exportfile)
2020-08-21 16:05:51 +02:00
# new parse the SVG file and insert it as new group into the current document tree
doc = etree.parse(exportfile + ".svg").getroot()
newGroup = self.document.getroot().add(inkex.Group())
newGroup.attrib['transform'] = "matrix({:0.6f}, 0, 0, {:0.6f}, {:0.6f}, {:0.6f})".format(
float(node.get('width')) / float(doc.get('width')),
float(node.get('height')) / float(doc.get('height')),
float(node.get('x')) + x_offset,
float(node.get('y')) + y_offset
)
2020-08-21 16:05:51 +02:00
newGroup.append(doc)
# Delete the temporary svg file
2020-08-21 16:05:51 +02:00
if os.path.exists(exportfile + ".svg"):
try:
os.remove(exportfile + ".svg")
except:
pass
2020-08-21 16:05:51 +02:00
2020-08-30 12:29:46 +02:00
else:
inkex.utils.debug("Error while creating output file! :-( The \"primitive\" executable seems to be missing, has no exec permissions or platform is imcompatible.")
2020-08-30 12:29:46 +02:00
exit(1)
2020-08-21 16:05:51 +02:00
#remove the old image or not
if self.options.keeporiginal is not True:
2021-04-18 18:21:23 +02:00
node.delete()
# create clip path to remove the stuffy surroundings
if self.options.cliprect:
path = '//svg:defs'
defslist = self.document.getroot().xpath(path, namespaces=inkex.NSS)
if len(defslist) > 0:
defs = defslist[0]
clipPathData = {inkex.addNS('label', 'inkscape'):'imagetracerClipPath', 'clipPathUnits':'userSpaceOnUse', 'id':'imagetracerClipPath'}
clipPath = etree.SubElement(defs, 'clipPath', clipPathData)
#inkex.utils.debug(image.width)
clipBox = {
'x':str(0),
'y':str(0),
'width':str(doc.get('width')),
'height':str(doc.get('height')),
'style':'fill:#000000; stroke:none; fill-opacity:1;'
}
etree.SubElement(clipPath, 'rect', clipBox)
#etree.SubElement(newGroup, 'g', {inkex.addNS('label','inkscape'):'imagetracerjs', 'clip-path':"url(#imagetracerClipPath)"})
newGroup.getchildren()[0].set('clip-path','url(#imagetracerClipPath)')
2020-08-21 16:05:51 +02:00
else:
inkex.utils.debug("No image found for tracing. Please select an image first.")
if __name__ == '__main__':
Primitive().run()