This repository has been archived on 2023-03-25. You can view files and clone it, but cannot push or open issues or pull requests.
mightyscape-1.1-deprecated/extensions/fablabchemnitz/export_selection/export_selection.py

181 lines
8.5 KiB
Python
Raw Normal View History

2021-04-13 20:39:22 +02:00
#!/usr/bin/env python3
from copy import deepcopy
from pathlib import Path
import logging
import math
import os
import sys
2021-04-13 20:39:22 +02:00
import subprocess
from subprocess import Popen, PIPE
import warnings
import inkex
import inkex.command
2021-04-13 20:39:22 +02:00
from inkex.command import inkscape, inkscape_command
from lxml import etree
from scour.scour import scourString
2021-05-21 12:22:41 +02:00
'''
ToDo: work with temporary SVG file in temp dir instead handling deletion stuff at the end / valide that the file exists when spawning new instance
'''
logger = logging.getLogger(__name__)
DETACHED_PROCESS = 0x00000008
GROUP_ID = 'export_selection_transform'
class ExportObject(inkex.EffectExtension):
2021-05-08 22:22:29 +02:00
def add_arguments(self, pars):
pars.add_argument("--wrap_transform", type=inkex.Boolean, default=False, help="Wrap final document in transform")
pars.add_argument("--border_offset", type=float, default=1.000, help="Add border offset around selection")
pars.add_argument("--export_dir", default="~/inkscape_export/", help="Location to save exported documents")
pars.add_argument("--opendir", type=inkex.Boolean, default=False, help="Open containing output directory after export")
pars.add_argument("--dxf_exporter_path", default="/usr/share/inkscape/extensions/dxf_outlines.py", help="Location of dxf_outlines.py")
pars.add_argument("--export_svg", type=inkex.Boolean, default=False, help="Create a svg file")
2021-05-08 22:22:29 +02:00
pars.add_argument("--export_dxf", type=inkex.Boolean, default=False, help="Create a dxf file")
pars.add_argument("--export_pdf", type=inkex.Boolean, default=False, help="Create a pdf file")
pars.add_argument("--newwindow", type=inkex.Boolean, default=False, help="Open file in new Inkscape window")
2021-05-08 22:22:29 +02:00
def openExplorer(self, dir):
if os.name == 'nt':
Popen(["explorer", dir], close_fds=True, creationflags=DETACHED_PROCESS).wait()
else:
Popen(["xdg-open", dir], close_fds=True, start_new_session=True).wait()
def spawnIndependentInkscape(self, file): #function to spawn non-blocking inkscape instance. the inkscape command is available because it is added to ENVIRONMENT when Inkscape main instance is started
warnings.simplefilter('ignore', ResourceWarning) #suppress "enable tracemalloc to get the object allocation traceback"
if os.name == 'nt':
Popen(["inkscape", file], close_fds=True, creationflags=DETACHED_PROCESS)
else:
subprocess.Popen(["inkscape", file], start_new_session=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
2021-05-08 22:22:29 +02:00
warnings.simplefilter("default", ResourceWarning)
def effect(self):
svg_export = self.options.export_svg
if self.options.export_svg is False and \
self.options.export_dxf is False and \
self.options.export_pdf is False and \
self.options.newwindow is False:
inkex.utils.debug("You must select at least one option to continue!")
return
else:
self.options.export_svg = True #required for all other options!
2021-05-08 22:22:29 +02:00
if not self.svg.selected:
inkex.errormsg("Selection is empty. Please select some objects first!")
return
#preflight check for DXF input dir
if not os.path.exists(self.options.dxf_exporter_path):
inkex.utils.debug("Location of dxf_outlines.py does not exist. Please select a proper file and try again.")
exit(1)
export_dir = Path(self.absolute_href(self.options.export_dir))
os.makedirs(export_dir, exist_ok=True)
offset = self.options.border_offset
bbox = inkex.BoundingBox()
for elem in self.svg.selected.values():
transform = inkex.Transform()
parent = elem.getparent()
if parent is not None and isinstance(parent, inkex.ShapeElement):
transform = parent.composed_transform()
try:
bbox += elem.bounding_box(transform)
except Exception:
logger.exception("Bounding box not computed")
logger.info("Skipping bounding box")
transform = elem.composed_transform()
x1, y1 = transform.apply_to_point([0, 0])
x2, y2 = transform.apply_to_point([1, 1])
bbox += inkex.BoundingBox((x1, x2), (y1, y2))
template = self.create_document()
svg_filename = None
2021-05-08 22:22:29 +02:00
group = etree.SubElement(template, '{http://www.w3.org/2000/svg}g')
group.attrib['id'] = GROUP_ID
group.attrib['transform'] = str(inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top))))
for elem in self.svg.selected.values():
elem_copy = deepcopy(elem)
elem_copy.attrib['transform'] = str(elem.composed_transform())
elem_copy.attrib['style'] = str(elem.composed_style())
group.append(elem_copy)
template.attrib['viewBox'] = f'{-offset} {-offset} {bbox.width + offset * 2} {bbox.height + offset * 2}'
template.attrib['width'] = f'{bbox.width + offset * 2}' + self.svg.unit
template.attrib['height'] = f'{bbox.height + offset * 2}' + self.svg.unit
if svg_filename is None:
filename_base = elem.attrib.get('id', None).replace(os.sep, '_')
if filename_base:
svg_filename = filename_base + '.svg'
if not filename_base: #should never be the case. Inkscape might crash if the id attribute is empty or not existent due to invalid SVG
filename_base = self.svg.get_unique_id("selection")
svg_filename = filename_base + '.svg'
2021-05-08 22:22:29 +02:00
template.append(group)
if not self.options.wrap_transform:
self.load(inkscape_command(template.tostring(), select=GROUP_ID, verbs=['SelectionUnGroup']))
template = self.svg
for child in template.getchildren():
if child.tag == '{http://www.w3.org/2000/svg}metadata':
template.remove(child)
if self.options.export_svg is True:
self.save_document(template, export_dir / svg_filename)
2021-05-08 22:22:29 +02:00
if self.options.opendir is True:
self.openExplorer(export_dir)
if self.options.newwindow is True:
#inkscape(os.path.join(export_dir, svg_filename)) #blocking cmd
self.spawnIndependentInkscape(os.path.join(export_dir, svg_filename)) #non-blocking
2021-05-08 22:22:29 +02:00
if self.options.export_dxf is True:
#ensure that python command is available #we pass 25.4/96 which stands for unit mm. See inkex.units.UNITS and dxf_outlines.inx
cmd = [
sys.executable, #the path of the python interpreter which is used for this script
self.options.dxf_exporter_path,
'--output=' + os.path.join(export_dir, filename_base + '.dxf'),
2021-05-08 22:22:29 +02:00
r'--units=25.4/96',
os.path.join(export_dir, svg_filename)
2021-05-08 22:22:29 +02:00
]
proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE)
stdout, stderr = proc.communicate()
if proc.returncode != 0:
inkex.utils.debug("%d %s %s" % (proc.returncode, stdout, stderr))
if self.options.export_pdf is True:
cli_output = inkscape(os.path.join(export_dir, svg_filename), actions='export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose'.format(file_name=os.path.join(export_dir, filename_base + '.pdf')))
2021-05-08 22:22:29 +02: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)
2021-05-21 12:22:41 +02:00
if svg_export is False and self.options.newwindow is False: #we need the SVG file in case we open in new window because we spawn a new instance
os.remove(os.path.join(export_dir, svg_filename)) #remove SVG if not enabled to export. Might delete existing SVG accidently!
2021-05-08 22:22:29 +02:00
def create_document(self):
document = self.svg.copy()
for child in document.getchildren():
if child.tag == '{http://www.w3.org/2000/svg}defs':
continue
document.remove(child)
return document
def save_document(self, document, filename):
with open(filename, 'wb') as fp:
document = document.tostring()
fp.write(scourString(document).encode('utf8'))
if __name__ == '__main__':
2021-05-08 22:22:29 +02:00
ExportObject().run()