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
|
|
|
|
Last patch: 21.08.2020
|
|
|
|
License: GNU GPL v3
|
|
|
|
|
|
|
|
Used version of Primitive: https://github.com/fogleman/primitive/commit/0373c216458be1c4b40655b796a3aefedf8b7d23
|
|
|
|
"""
|
|
|
|
|
|
|
|
class Primitive (inkex.Effect):
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
inkex.Effect.__init__(self)
|
|
|
|
self.arg_parser.add_argument("--keeporiginal", type=inkex.Boolean, default=False, help="Keep original image on canvas")
|
2020-08-23 00:14:21 +02:00
|
|
|
self.arg_parser.add_argument("--cliprect", type=inkex.Boolean, default=True, help="Draw clipping rectangle")
|
2020-08-21 16:05:51 +02:00
|
|
|
self.arg_parser.add_argument("--n", type=int, default=100, help="Number of shapes")
|
|
|
|
self.arg_parser.add_argument("--m", default=1, help="Mode")
|
|
|
|
self.arg_parser.add_argument("--rep", type=int, default=0,help="Extra shapes/iteration")
|
|
|
|
self.arg_parser.add_argument("--r", type=int, default=256, help="Resize to size before processing (px)")
|
|
|
|
self.arg_parser.add_argument("--s", type=int, default=1024, help="Output image size (px)")
|
|
|
|
self.arg_parser.add_argument("--a", type=int, default=128, help="Color alpha")
|
|
|
|
self.arg_parser.add_argument("--bg_enabled", type=inkex.Boolean, default=True, help="Use average starting background color")
|
|
|
|
self.arg_parser.add_argument("--bg", type=Color, default=255, help="Starting background color")
|
|
|
|
self.arg_parser.add_argument("--j", type=int, default=0, help="Number of parallel workers")
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
# Write the embedded or linked image to temporary directory
|
|
|
|
exportfile = "Primitive.png"
|
|
|
|
image.save(exportfile, "png")
|
|
|
|
|
|
|
|
## Build up Primitive command according to your settings from extension GUI
|
|
|
|
command = "primitive"
|
|
|
|
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
|
|
|
|
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)
|
|
|
|
|
|
|
|
# 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(" + \
|
|
|
|
str(float(node.get('width')) / float(doc.get('width'))) + \
|
|
|
|
", 0, 0 , " + \
|
|
|
|
str(float(node.get('height')) / float(doc.get('height'))) + \
|
|
|
|
"," + node.get('x') + \
|
|
|
|
"," + node.get('y') + ")"
|
|
|
|
newGroup.append(doc)
|
|
|
|
|
|
|
|
# Delet the temporary svg file
|
|
|
|
if os.path.exists(exportfile + ".svg"):
|
|
|
|
os.remove(exportfile + ".svg")
|
|
|
|
|
|
|
|
#remove the old image or not
|
|
|
|
if self.options.keeporiginal is not True:
|
2020-08-23 00:14:21 +02:00
|
|
|
node.getparent().remove(node)
|
|
|
|
|
|
|
|
# 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.")
|
|
|
|
|
|
|
|
Primitive().run()
|