simplified "open in roland cutstudio"

This commit is contained in:
Mario Voigt 2021-07-15 14:19:12 +02:00
parent 06f96fe1e5
commit ab25f2ad94

View File

@ -22,49 +22,34 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
''' '''
# The source code is a horrible mess. I apologize for your inconvenience, but hope that it still helps. Feel free to improve :-) '''
# Keep everything python2 compatible as long as people out there are using Inkscape <= 0.92.4! KNOWN BUGS:
WONTFIX: clipping of paths doesnt work
WONTFIX: if there is any object with opacity != 100%, inkscape exports some objects as bitmaps. They will disappear in CutStudio! The same problem also occurs if the alpha value of stroke or fill color is not 100%.
WONTFIX: filters (e.g. blur) are not supported
'''
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import
from builtins import open
from builtins import map
from builtins import str
from builtins import range
import sys import sys
import os import os
from subprocess import Popen
import subprocess import subprocess
from subprocess import Popen
from functools import reduce
from pathlib import Path
import shutil import shutil
import numpy import numpy
from functools import reduce
import atexit import atexit
import filecmp import filecmp
try:
from pathlib import Path
except ImportError:
# Workaround for Python < 3.5
class fakepath:
def home(self):
return os.path.expanduser("~")
Path = fakepath()
try:
from functools import lru_cache
except ImportError:
def lru_cache():
def wrapper(func):
return func
return wrapper
import tempfile import tempfile
import warnings
import inkex.command
from inkex.command import inkscape
DEVNULL = open(os.devnull, 'w') DEVNULL = open(os.devnull, 'w')
atexit.register(DEVNULL.close) atexit.register(DEVNULL.close)
def message(s): def message(s):
sys.stderr.write(s+"\n") sys.stderr.write(s+"\n")
def debug(s): def debug(s):
message(s) message(s)
@ -94,17 +79,6 @@ def which(program, raiseError, extraPaths=[], subdir=None):
else: else:
return None return None
# mostly copied from https://github.com/t-oster/VisiCut/blob/0abe785a30d5d5085dd3b5953b38239b1ff83358/tools/inkscape_extension/visicut_export.py
@lru_cache()
def inkscape_version():
"""determine if Inkscape is version 0 or 1"""
version = subprocess.check_output([INKSCAPEBIN, "--version"], stderr=DEVNULL).decode('ASCII', 'ignore')
assert version.startswith("Inkscape ")
if version.startswith("Inkscape 0"):
return 0
else:
return 1
# copied from https://github.com/t-oster/VisiCut/blob/0abe785a30d5d5085dd3b5953b38239b1ff83358/tools/inkscape_extension/visicut_export.py # copied from https://github.com/t-oster/VisiCut/blob/0abe785a30d5d5085dd3b5953b38239b1ff83358/tools/inkscape_extension/visicut_export.py
# Strip SVG to only contain selected elements, convert objects to paths, unlink clones # Strip SVG to only contain selected elements, convert objects to paths, unlink clones
# Inkscape version: takes care of special cases where the selected objects depend on non-selected ones. # Inkscape version: takes care of special cases where the selected objects depend on non-selected ones.
@ -115,72 +89,34 @@ def inkscape_version():
def stripSVG_inkscape(src, dest, elements): def stripSVG_inkscape(src, dest, elements):
# create temporary file for opening with inkscape. # create temporary file for opening with inkscape.
# delete this file later so that it will disappear from the "recently opened" list. # delete this file later so that it will disappear from the "recently opened" list.
tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix='temp-visicut-', suffix='.svg') tmpfile = tempfile.NamedTemporaryFile(delete=False, prefix='temp-cutstudio-', suffix='.svg')
tmpfile.close() tmpfile.close()
tmpfile = tmpfile.name tmpfile = tmpfile.name
import shutil
shutil.copyfile(src, tmpfile) shutil.copyfile(src, tmpfile)
actions_list=[]
actions_list.append("ObjectToPath")
actions_list.append("UnlockAllInAllLayers")
if elements: # something is selected
if inkscape_version() == 0: actions_list.append("select:{}".format(",".join(elements)))
# inkscape 0.92 long-term-support release. Will be in Linux distributions until 2025 or so
# Selection commands: select items, invert selection, delete
selection = []
for el in elements:
selection += ["--select=" + el]
if len(elements) > 0:
# selection += ["--verb=FitCanvasToSelection"] # TODO add a user configuration option whether to keep the page size (and by this the position relative to the page)
selection += ["--verb=EditInvertInAllLayers", "--verb=EditDelete"]
hidegui = ["--without-gui"]
# currently this only works with gui because of a bug in inkscape: https://bugs.launchpad.net/inkscape/+bug/843260
hidegui = []
command = [INKSCAPEBIN] + hidegui + [tmpfile, "--verb=UnlockAllInAllLayers", "--verb=UnhideAllInAllLayers"] + selection + ["--verb=EditSelectAllInAllLayers", "--verb=EditUnlinkClone", "--verb=ObjectToPath", "--verb=FileSave", "--verb=FileQuit"]
else: else:
# Inkscape 1.0, to be released ca 2020 actions_list.append("EditSelectAllInAllLayers")
# inkscape --select=... --verbs=... actions_list.append("UnhideAllInAllLayers")
# (see inkscape --help, inkscape --verb-list) actions_list.append("EditInvertInAllLayers")
command = [INKSCAPEBIN, tmpfile, "--batch-process"] actions_list.append("EditDelete")
verbs = ["ObjectToPath", "UnlockAllInAllLayers"] actions_list.append("EditSelectAllInAllLayers")
if elements: # something is selected actions_list.append("EditUnlinkClone")
# --select=object1,object2,object3,... actions_list.append("ObjectToPath")
command += ["--select=" + ",".join(elements)] actions_list.append("FileSave")
else: actions = ";".join(actions_list)
verbs += ["EditSelectAllInAllLayers"] cli_output = inkscape(tmpfile, "--batch-process", actions=actions) #process recent file
verbs += ["UnhideAllInAllLayers", "EditInvertInAllLayers", "EditDelete", "EditSelectAllInAllLayers", "EditUnlinkClone", "ObjectToPath", "FileSave"] if len(cli_output) > 0:
# --verb=action1;action2;... self.msg("Inkscape returned the following output when trying to run the file export; the file export may still have worked:")
command += ["--verb=" + ";".join(verbs)] self.msg(cli_output)
DEBUG = False
if DEBUG:
# Inkscape sometimes silently ignores wrong verbs, so we need to double-check that everything's right
for verb in verbs:
verb_list = [line.split(":")[0] for line in subprocess.check_output([INKSCAPEBIN, "--verb-list"], stderr=DEVNULL).split("\n")]
if verb not in verb_list:
sys.stderr.write("Inkscape does not have the verb '{}'. Please report this as a VisiCut bug.".format(verb))
inkscape_output = "(not yet run)"
try:
#sys.stderr.write(" ".join(command))
# run inkscape, buffer output
inkscape = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
inkscape_output = inkscape.communicate()[0]
if inkscape.returncode != 0:
sys.stderr.write("Error: cleaning the document with inkscape failed. Something might still be shown in visicut, but it could be incorrect.\nInkscape's output was:\n" + inkscape_output)
except:
sys.stderr.write("Error: cleaning the document with inkscape failed. Something might still be shown in visicut, but it could be incorrect. Exception information: \n" + str(sys.exc_info()[0]) + "Inkscape's output was:\n" + inkscape_output)
# move output to the intended destination filename # move output to the intended destination filename
os.rename(tmpfile, dest) os.rename(tmpfile, dest)
# header # header
# for debugging purposes you can open the resulting EPS file in Inkscape, # for debugging purposes you can open the resulting EPS file in Inkscape,
# select all, ungroup multiple times # select all, ungroup multiple times
@ -282,6 +218,7 @@ end restore
""" """
def OpenInRolandCutStudio(src, dest, mirror=False): def OpenInRolandCutStudio(src, dest, mirror=False):
def outputFromStack(stack, n, transformCoordinates=True): def outputFromStack(stack, n, transformCoordinates=True):
arr=stack[-(n+1):-1] arr=stack[-(n+1):-1]
if transformCoordinates: if transformCoordinates:
@ -291,6 +228,7 @@ def OpenInRolandCutStudio(src, dest, mirror=False):
return output(arrTransformed+[stack[-1]]) return output(arrTransformed+[stack[-1]])
else: else:
return output(arr+[stack[-1]]) return output(arr+[stack[-1]])
def transform(x, y): def transform(x, y):
#debug("trafo from: {} {}".format(x, y)) #debug("trafo from: {} {}".format(x, y))
p=numpy.array([[float(x),float(y),1]]).transpose() p=numpy.array([[float(x),float(y),1]]).transpose()
@ -304,6 +242,7 @@ def OpenInRolandCutStudio(src, dest, mirror=False):
y=float(pnew[1]) y=float(pnew[1])
#debug("to: {} {}".format(x, y)) #debug("to: {} {}".format(x, y))
return [x, y] return [x, y]
def toString(v): def toString(v):
""" """
like str(), but gives the exact same output for floats across python2 and python3 like str(), but gives the exact same output for floats across python2 and python3
@ -312,17 +251,21 @@ def OpenInRolandCutStudio(src, dest, mirror=False):
return repr(v) return repr(v)
else: else:
return str(v) return str(v)
def outputMoveto(x, y): def outputMoveto(x, y):
[xx, yy]=transform(x, y) [xx, yy]=transform(x, y)
return output([toString(xx), toString(yy), "m"]) return output([toString(xx), toString(yy), "m"])
def outputLineto(x, y): def outputLineto(x, y):
[xx, yy]=transform(x, y) [xx, yy]=transform(x, y)
return output([toString(xx), toString(yy), "l"]) return output([toString(xx), toString(yy), "l"])
def output(array): def output(array):
array=list(map(toString, array)) array=list(map(toString, array))
output=" ".join(array) output=" ".join(array)
#debug("OUTPUT: "+output) #debug("OUTPUT: "+output)
return output + "\n" return output + "\n"
stack=[] stack=[]
scalingStack=[numpy.identity(3)] scalingStack=[numpy.identity(3)]
if mirror: if mirror:
@ -383,13 +326,6 @@ def OpenInRolandCutStudio(src, dest, mirror=False):
outputFile.close() outputFile.close()
inputFile.close() inputFile.close()
if os.name=="nt": # windows
INKSCAPEBIN = which("inkscape.exe", True, subdir="Inkscape")
else:
INKSCAPEBIN=which("inkscape", True)
assert os.path.isfile(INKSCAPEBIN), "cannot find inkscape binary " + INKSCAPEBIN
selectedElements=[] selectedElements=[]
for arg in sys.argv[1:]: for arg in sys.argv[1:]:
if arg[0] == "-": if arg[0] == "-":
@ -406,19 +342,21 @@ else:
# only take selected elements # only take selected elements
stripSVG_inkscape(src=filename, dest=filename+".filtered.svg", elements=selectedElements) stripSVG_inkscape(src=filename, dest=filename+".filtered.svg", elements=selectedElements)
if inkscape_version() == 0: actions_list=[]
# Inkscape 0.92.4 actions_list.append("export-text-to-path")
cmd = [INKSCAPEBIN,"-z",filename+".filtered.svg","-T", "--export-ignore-filters", "--export-eps="+filename+".inkscape.eps"] actions_list.append("export-ignore-filters")
else: actions_list.append("export-area-drawing")
# Inkscape 1.0 actions_list.append("export-filename:{}".format(filename + ".inkscape.eps"))
cmd = [INKSCAPEBIN, "-T", "--export-ignore-filters", "--export-area-drawing", "--export-filename="+filename+".inkscape.eps", filename+".filtered.svg"] actions_list.append("export-do")
actions = ";".join(actions_list)
cli_output = inkscape(filename + ".filtered.svg", actions=actions) #process recent file
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)
inkscape_eps_file = filename + ".inkscape.eps" inkscape_eps_file = filename + ".inkscape.eps"
#debug(" ".join(cmd))
assert 0 == subprocess.call(cmd, stderr=DEVNULL), 'EPS conversion failed: command returned error: ' + '"' + '" "'.join(cmd) + '"'
assert os.path.exists(inkscape_eps_file), 'EPS conversion failed: command did not create result file: ' + '"' + '" "'.join(cmd) + '"' assert os.path.exists(inkscape_eps_file), 'EPS conversion failed: command did not create result file: ' + '"' + '" "'.join(cmd) + '"'
if "--selftest" in sys.argv: if "--selftest" in sys.argv:
# used for unit-testing: fixed location of output file # used for unit-testing: fixed location of output file
destination = "./test-output-actual.cutstudio.eps" destination = "./test-output-actual.cutstudio.eps"
@ -437,7 +375,10 @@ if "--selftest" in sys.argv:
if os.name=="nt": if os.name=="nt":
DETACHED_PROCESS = 8 # start as "daemon" DETACHED_PROCESS = 8 # start as "daemon"
Popen([which("CutStudio\CutStudio.exe", True), "/import", destination], creationflags=DETACHED_PROCESS, close_fds=True) warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback"
proc = Popen([which("CutStudio\CutStudio.exe", True), "/import", destination], creationflags=DETACHED_PROCESS, close_fds=True)
#warnings.simplefilter("default", ResourceWarning)
else: #check if we have access to "wine" else: #check if we have access to "wine"
CUTSTUDIO_C_DRIVE = str(Path.home()) + "/.wine/drive_c/" CUTSTUDIO_C_DRIVE = str(Path.home()) + "/.wine/drive_c/"
CUTSTUDIO_PATH_LINUX_WINE = CUTSTUDIO_C_DRIVE + "Program Files (x86)/CutStudio/CutStudio.exe" CUTSTUDIO_PATH_LINUX_WINE = CUTSTUDIO_C_DRIVE + "Program Files (x86)/CutStudio/CutStudio.exe"
@ -454,9 +395,3 @@ else: #check if we have access to "wine"
"Please open that with CutStudio manually. \n\n" + \ "Please open that with CutStudio manually. \n\n" + \
"Tip: On Linux, you can use 'wine' to install CutStudio 3.10. Then, the file will be directly opened with CutStudio. \n" + \ "Tip: On Linux, you can use 'wine' to install CutStudio 3.10. Then, the file will be directly opened with CutStudio. \n" + \
" Diagnostic information: \n" + str(exc)) " Diagnostic information: \n" + str(exc))
#os.popen("/usr/bin/xdg-open " + filename) #os.popen is deprecated. Use subprocess.Popen
#Popen(["inkscape", filename+".filtered.svg"], stderr=DEVNULL)
#Popen(["inkscape", filename+".cutstudio.eps"])
#os.unlink(filename+".filtered.svg")
#os.unlink(filename)
#os.unlink(filename+".cutstudio.eps")