2022-11-03 23:39:39 +01:00
#!/usr/bin/env python3
"""
Extension for InkScape 1.2
CutOptim OS Wrapper script to make CutOptim work on Windows and Linux systems without duplicating . inx files
Author : Mario Voigt / FabLab Chemnitz
Mail : mario . voigt @stadtfabrikanten.org
Date : 31.08 .2020
Last patch : 03.11 .2022
License : GNU GPL v3
"""
import inkex
import sys
import re
import os
import subprocess
from lxml import etree
from copy import deepcopy
import tempfile
from inkex . command import inkscape , inkscape_command
class CuttingOptimizer ( inkex . EffectExtension ) :
def openDebugFile ( self , file ) :
DETACHED_PROCESS = 0x00000008
if os . name == ' nt ' :
subprocess . Popen ( [ " explorer " , file ] , close_fds = True , creationflags = DETACHED_PROCESS , stdout = subprocess . DEVNULL , stderr = subprocess . DEVNULL ) . wait ( )
else :
subprocess . Popen ( [ " xdg-open " , file ] , close_fds = True , start_new_session = True , stdout = subprocess . DEVNULL , stderr = subprocess . DEVNULL ) . wait ( )
def add_arguments ( self , pars ) :
args = sys . argv [ 1 : ]
for arg in args :
key = arg . split ( " = " ) [ 0 ]
if len ( arg . split ( " = " ) ) == 2 :
value = arg . split ( " = " ) [ 1 ]
try :
if key != " --id " :
pars . add_argument ( key , default = key )
except :
pass #ignore duplicate id arg
def effect ( self ) :
extension_dir = os . path . dirname ( os . path . realpath ( __file__ ) )
cmd = [ ]
if os . name == " nt " :
cutoptim = os . path . join ( extension_dir , " CutOptim.exe " )
else :
cutoptim = os . path . join ( extension_dir , " CutOptim " )
cmd . append ( cutoptim )
elements = self . svg . selected
if len ( elements ) > 0 : #if selection is existing, then we export only selected items to a new svg, which is then going to be processed. Otherwise we process the whole SVG document
template = self . svg . copy ( )
for child in template . getchildren ( ) :
if child . tag == ' { http://www.w3.org/2000/svg}defs ' :
continue
template . remove ( child )
group = etree . SubElement ( template , ' { http://www.w3.org/2000/svg}g ' )
group . attrib [ ' id ' ] = ' export_selection_transform '
for element in self . svg . selected . values ( ) :
elem_copy = deepcopy ( element )
elem_copy . attrib [ ' transform ' ] = str ( element . composed_transform ( ) )
elem_copy . attrib [ ' style ' ] = str ( element . specified_style ( ) )
group . append ( elem_copy )
template . attrib [ ' viewBox ' ] = self . svg . attrib [ ' viewBox ' ]
template . attrib [ ' width ' ] = self . svg . attrib [ ' width ' ]
template . attrib [ ' height ' ] = self . svg . attrib [ ' height ' ]
template . append ( group )
svg_out = os . path . join ( tempfile . gettempdir ( ) , self . svg . get_unique_id ( " selection " ) + ' .svg ' )
with open ( svg_out , ' wb ' ) as fp :
fp . write ( template . tostring ( ) )
actions_list = [ ]
actions_list . append ( " SelectionUnGroup " )
actions_list . append ( " export-type:svg " )
actions_list . append ( " export-filename: {} " . format ( svg_out ) )
actions_list . append ( " export-do " )
actions = " ; " . join ( actions_list )
2022-11-05 10:41:20 +01:00
cli_output = inkscape ( svg_out , actions = actions ) #process recent file
2022-11-03 23:39:39 +01:00
if len ( cli_output ) > 0 :
self . msg ( " Inkscape returned the following output when trying to run the file export; the file export may still have worked: " )
self . msg ( cli_output )
layerSum = 0
layer_output_0 = False
cancel_on_error = False
for arg in vars ( self . options ) :
argval = str ( getattr ( self . options , arg ) )
if arg not in ( " tab " , " output " , " ids " , " selected_nodes " , " print_cmd " ) :
#inkex.utils.debug(str(arg) + " = " + str(getattr(self.options, arg)))
#fix behaviour of "original" arg which does not correctly gets interpreted if set to false
if arg == " original " and argval == " false " :
continue
if arg == " input_file " :
cmd . append ( " --file " )
if len ( elements ) > 0 :
cmd . append ( svg_out )
else :
cmd . append ( argval )
elif arg == " layer_output_0 " :
if argval == " true " :
layerSum + = 0
layer_output_0 = True
#elif arg == "layer_output_1":
# if argval == "true": layerSum += 1
elif arg == " layer_output_2 " :
if argval == " true " : layerSum + = 2
elif arg == " layer_output_4 " :
if argval == " true " : layerSum + = 4
elif arg == " layer_output_8 " :
if argval == " true " : layerSum + = 8
elif arg == " layer_output_16 " :
if argval == " true " : layerSum + = 16
elif arg == " cancel_on_error " :
if argval == " true " : cancel_on_error = True
else :
cmd . append ( " -- {} = {} " . format ( arg , argval ) )
cmd . append ( " --layer_output " )
cmd . append ( " {} " . format ( layerSum ) )
if layerSum == 0 and layer_output_0 is False :
inkex . utils . debug ( " You need to enable at least one type of layer to continue! " )
output_file = None
if os . name == " nt " :
output_file = " cutoptim.svg "
else :
output_file = " /tmp/cutoptim.svg "
if os . path . exists ( output_file ) :
try :
os . remove ( output_file )
except OSError as e :
pass
cmd . append ( " --output " )
cmd . append ( output_file )
# run CutOptim with the parameters provided
if self . options . print_cmd == " true " :
inkex . utils . debug ( " The following command would be executed on shell: \n " )
inkex . utils . debug ( " " . join ( cmd ) )
exit ( 0 )
else :
with subprocess . Popen ( cmd , stdout = subprocess . PIPE , stderr = subprocess . PIPE , shell = False ) as cutoptim :
cutoptim . wait ( )
stdout , stderr = cutoptim . communicate ( )
#inkex.utils.debug("stdout:\n{}".format(stdout.decode('UTF-8')))
errors = stderr . decode ( ' UTF-8 ' )
if len ( errors ) > 0 :
inkex . utils . debug ( " Errors occured: \n {} " . format ( errors ) )
if len ( errors ) > 0 and cancel_on_error is True :
inkex . utils . debug ( " Maybe enlarge your document size in case not all polygons could be placed and try again! Nesting was cancelled! " )
exit ( 1 )
# check output existence
try :
stream = open ( output_file , ' r ' )
except FileNotFoundError as e :
inkex . utils . debug ( " There was no SVG output generated. Cannot continue. Command was: \n " )
inkex . utils . debug ( " " . join ( cmd ) )
exit ( 1 )
if self . options . original == " false " : #we need to use string representation of bool
for element in self . document . getroot ( ) :
if isinstance ( element , inkex . ShapeElement ) :
element . delete ( )
if self . options . debug_file == " true " : #we need to use string representation of bool
self . openDebugFile ( " Debug_CutOptim.txt " )
# write the generated SVG into Inkscape's canvas
doc = etree . parse ( stream , parser = etree . XMLParser ( huge_tree = True ) )
stream . close ( )
group = inkex . Group ( id = " CutOptim " )
'''
0 = Placed_Layer
1 = Original_Layer ( Input Layer ) < > see " Keep original layer "
2 = Polygon_Layer
4 = Large_Polygon_Layer
8 = Hull_Placed_Layer
16 = Placed_Polygon_Layer
'''
if layer_output_0 is False :
l0 = None
else :
l0 = doc . xpath ( ' //svg:g[@inkscape:label= " Placed_Layer " ] ' , namespaces = inkex . NSS )
#l1 = doc.xpath('//svg:g[@inkscape:label="Original_Layer"]', namespaces=inkex.NSS)
l2 = doc . xpath ( ' //svg:g[@inkscape:label= " Polygon_Layer " ] ' , namespaces = inkex . NSS )
l4 = doc . xpath ( ' //svg:g[@inkscape:label= " Large_Polygon_Layer " ] ' , namespaces = inkex . NSS )
l8 = doc . xpath ( ' //svg:g[@inkscape:label= " Hull_Placed_Layer " ] ' , namespaces = inkex . NSS )
l16 = doc . xpath ( ' //svg:g[@inkscape:label= " Placed_Polygon_Layer " ] ' , namespaces = inkex . NSS )
for layer in ( l0 , l2 , l4 , l8 , l16 ) : #,l1
if layer is not None and len ( layer ) > 0 :
for element in layer : #[0].getchildren():
group . append ( element )
self . document . getroot ( ) . append ( group )
if __name__ == ' __main__ ' :
CuttingOptimizer ( ) . run ( )