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 / 0373 c216458be1c4b40655b796a3aefedf8b7d23
"""
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
2020-08-30 12:29:46 +02:00
if os . name == " nt " :
exportfile = " Primitive.png "
else :
exportfile = " /tmp/Primitive.png "
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 )
# 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 )
2020-08-31 21:25:41 +02:00
# Delete the temporary svg file
2020-08-21 16:05:51 +02:00
if os . path . exists ( exportfile + " .svg " ) :
2020-08-31 21:25:41 +02:00
try :
os . remove ( exportfile + " .svg " )
except :
pass
2020-08-21 16:05:51 +02:00
2020-08-30 12:29:46 +02:00
else :
2020-08-31 21:25:41 +02:00
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 :
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. " )
2020-08-31 21:25:41 +02:00
if __name__ == ' __main__ ' :
Primitive ( ) . run ( )