242 lines
14 KiB
Python
242 lines
14 KiB
Python
#!/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
|
|
|
|
class KVEC (inkex.EffectExtension):
|
|
|
|
def checkImagePath(self, element):
|
|
xlink = element.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 = element.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 add_arguments(self, pars):
|
|
pars.add_argument("--tab")
|
|
|
|
#General Settings
|
|
pars.add_argument("--keeporiginal", type=inkex.Boolean, default=False, help="Keep original image on canvas")
|
|
pars.add_argument("--fittooriginal", type=inkex.Boolean, default=False, help="Fit to original dimensions")
|
|
pars.add_argument("--debug", type=inkex.Boolean, default=False, help="Enable debug output")
|
|
pars.add_argument("--sysmalloc", type=inkex.Boolean, default=True, help="Use system-malloc routines")
|
|
pars.add_argument("--text", type=inkex.Boolean, default=True, help="Text output")
|
|
pars.add_argument("--font", type=inkex.Boolean, default=True, help="Generate optimized set of parameters")
|
|
pars.add_argument("--sort", default="max", help="Type of sort order for vectors")
|
|
|
|
#Geometry/Quality
|
|
pars.add_argument("--grit", type=int, default=0, help="Filter out details smaller than x pixels")
|
|
pars.add_argument("--gapfill", type=int, default=0, help="Gap fill (jumping)")
|
|
pars.add_argument("--centerline", default="off", help="Generates single lines if linewidth small enough")
|
|
pars.add_argument("--bezier", type=inkex.Boolean, default=False, help="Generate Bezier-curves")
|
|
pars.add_argument("--errbez", type=int, default=3, help="Error-Parameter for Bezier-curves")
|
|
pars.add_argument("--reduce", default="orthogonal", help="Type of line reducing")
|
|
pars.add_argument("--overlapp", type=inkex.Boolean, default=False, help="Polygons overlap (1px)")
|
|
pars.add_argument("--smooth", type=inkex.Boolean, default=False, help="Smoothing of polylines")
|
|
pars.add_argument("--winding", default="original", help="Winding")
|
|
pars.add_argument("--high_resolution", type=inkex.Boolean, default=False, help="High vectorization resolution")
|
|
pars.add_argument("--subsampling", type=inkex.Boolean, default=False, help="If enabled, the output vectors are subsampled by a factor of 2. This will reduce the size of the output file and will also result in smoothing the vectors")
|
|
pars.add_argument("--lossless", type=inkex.Boolean, default=False, help="Generate lossless image")
|
|
pars.add_argument("--progressive", type=inkex.Boolean, default=False, help="image is build up from two successive layers (one 'rough' picture without details and one refined picture which contains only details).")
|
|
pars.add_argument("--progressive_gritfactor", type=int, default=2, help="The first layer has a grit-value multiplied by this")
|
|
pars.add_argument("--progressive_colorfactor", type=int, default=2, help="The first layer has a quantize-value divided by this")
|
|
|
|
#Colors/Styles
|
|
pars.add_argument("--quantize", type=int, default=32, help="Color quantization")
|
|
pars.add_argument("--delta", type=int, default=0, help="Delta")
|
|
pars.add_argument("--fill", default="solid", help="Fill")
|
|
pars.add_argument("--lwidth", type=int, default=0, help="Line width")
|
|
pars.add_argument("--black", type=inkex.Boolean, default=False, help="Output-color is always black")
|
|
pars.add_argument("--palette", default="optimize", help="Palette")
|
|
pars.add_argument("--color_vectorization", default="normal", help="Color vectorization")
|
|
pars.add_argument("--colspace", default="rgb", help="Colorspace conversion parameters")
|
|
pars.add_argument("--colsep", default="rgb", help="Color separation parameters")
|
|
pars.add_argument("--tcolor_mode", default="none", help="Transparency color")
|
|
pars.add_argument("--tcolor_custom", type=Color, default=255, help="User-defined transparency color (RGB values)")
|
|
pars.add_argument("--vcolor_mode", default="none", help="Pick out regions with color")
|
|
pars.add_argument("--vcolor", type=Color, default=255, help="Region color")
|
|
def effect(self):
|
|
|
|
so = self.options
|
|
|
|
if (so.ids):
|
|
for element in self.svg.selected.values():
|
|
if element.tag == inkex.addNS('image', 'svg'):
|
|
self.path = self.checkImagePath(element) # This also ensures the file exists
|
|
if self.path is None: # check if image is embedded or linked
|
|
image_string = element.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 element.get('width')[-1].isdigit() is False or element.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
|
|
|
|
# Write the embedded or linked image to temporary directory
|
|
if os.name == "nt":
|
|
exportfile = "kvec.png"
|
|
else:
|
|
exportfile = "/tmp/kvec.png"
|
|
|
|
if image.mode != 'RGB':
|
|
image = image.convert('RGB')
|
|
image.save(exportfile, "png")
|
|
|
|
#some crash avoidings
|
|
if so.lossless is True:
|
|
so.color_vectorization = "normal"
|
|
so.bezier = False
|
|
so.font = False
|
|
so.black = False
|
|
if so.high_resolution is True:
|
|
so.overlapp = False
|
|
|
|
## Build up command according to your settings from extension GUI
|
|
if os.name == "nt":
|
|
command = "kvec.exe"
|
|
else:
|
|
command = "./kvec"
|
|
command += " " + exportfile #input
|
|
command += " " + exportfile + ".svg" #output
|
|
command += " -format svg"
|
|
|
|
#General Settings
|
|
if so.sysmalloc is False: command += " -sysmalloc off"
|
|
if so.font is True: command += " -font"
|
|
if so.text is False: command += " -text off"
|
|
command += " -sort " + so.sort
|
|
|
|
#Geometry/Quality
|
|
if so.grit > 0: command += " -grit " + str(so.grit)
|
|
command += " -gapfill " + str(so.gapfill)
|
|
command += " -centerline " + so.centerline
|
|
command += " -winding " + so.winding
|
|
if so.bezier is True: command += " -bezier"
|
|
command += " -errbez " + str(so.errbez)
|
|
if so.overlapp is True: command += " -overlapp"
|
|
if so.smooth is True: command += " -smooth on"
|
|
command += " -reduce " + str(so.reduce)
|
|
if so.high_resolution is True: command += " -resolution high"
|
|
if so.subsampling is True: command += " -subsampling"
|
|
if so.lossless is True: command += " -lossless"
|
|
if so.progressive is True:
|
|
command += " -progressive gritfactor " + str(so.progressive_gritfactor)
|
|
command += " -progressive colorfactor " + str(so.progressive_colorfactor)
|
|
|
|
#Colors/Styles
|
|
command += " -quantize " + str(so.quantize)
|
|
command += " -delta " + str(so.delta)
|
|
command += " -fill " + so.fill
|
|
command += " -lwidth " + str(so.lwidth)
|
|
command += " -palette " + so.palette
|
|
if so.black is True: command += " -black"
|
|
if so.color_vectorization != "normal": command += " -" + so.color_vectorization
|
|
command += " -colspace " + so.colspace
|
|
command += " -colsep " + so.colsep
|
|
if so.tcolor_mode == "auto":
|
|
command += " -tcolor auto"
|
|
elif so.tcolor_mode == "custom":
|
|
command += " -tcolor color {} {} {}".format(so.tcolor_custom.red, so.tcolor_custom.green, so.tcolor_custom.blue)
|
|
if so.vcolor_mode == "matching":
|
|
command += " -vcolor {} {} {}".format(so.vcolor.red, so.vcolor.green, so.vcolor.blue)
|
|
elif so.vcolor_mode == "not_matching":
|
|
command += " -vcolor {} {} {}".format(-so.vcolor.red, -so.vcolor.green, -so.vcolor.blue)
|
|
|
|
#some debug stuff
|
|
if so.debug is True:
|
|
command += " -debug all"
|
|
inkex.utils.debug(command)
|
|
|
|
# Create the vector new SVG file
|
|
with os.popen(command, "r") as proc:
|
|
result = proc.read()
|
|
if so.debug is True: inkex.utils.debug(result)
|
|
|
|
# proceed if new SVG file was successfully created
|
|
doc = None
|
|
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()
|
|
|
|
parent = element.getparent()
|
|
idx = parent.index(element)
|
|
#newGroup = self.document.getroot().add(inkex.Group())
|
|
newGroup = inkex.Group()
|
|
parent.insert(idx + 1,newGroup)
|
|
for child in doc:
|
|
if child.tag != etree.Comment and "desc" not in child.tag:
|
|
if child.text is not None:
|
|
child.text = child.text.strip()
|
|
if child.tail is not None:
|
|
child.tail = child.tail.strip()
|
|
newGroup.append(child)
|
|
|
|
#doc.get('height')
|
|
#doc.get('width')
|
|
#doc.get('viewBox')
|
|
if so.fittooriginal is True: #fitting does not work in all cases so we make it available as option
|
|
bbox = newGroup.bounding_box()
|
|
if bbox is not None:
|
|
newGroup.attrib['transform'] = "matrix({:0.6f}, 0, 0, {:0.6f}, {:0.6f}, {:0.6f})".format(
|
|
#float(element.get('width')) / float(doc.get('width')),
|
|
#float(element.get('height')) / float(doc.get('height')),
|
|
float(element.get('width')) / bbox.width,
|
|
float(element.get('height')) / bbox.height,
|
|
float(element.get('x')) - (float(element.get('width')) / bbox.width) * bbox.left,
|
|
float(element.get('y')) - (float(element.get('height')) / bbox.height) * bbox.top
|
|
)
|
|
else:
|
|
inkex.utils.debug("Fit to original dimensions failed. Please scale to size manually.")
|
|
|
|
# Delete the temporary svg file
|
|
if os.path.exists(exportfile + ".svg"):
|
|
try:
|
|
os.remove(exportfile + ".svg")
|
|
except:
|
|
pass
|
|
|
|
else:
|
|
inkex.utils.debug("Error while creating output file! :-( The \"kvec\" executable seems to be missing, has no exec permissions or platform is imcompatible.")
|
|
exit(1)
|
|
#remove the old image or not
|
|
if so.keeporiginal is not True:
|
|
element.delete()
|
|
else:
|
|
inkex.utils.debug("No image found for tracing. Please select an image first.")
|
|
|
|
if __name__ == '__main__':
|
|
KVEC().run() |