From ede0e766d899d2f0aafd36915e5b599972f549c7 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 12 Jun 2018 21:28:02 -0400 Subject: add output extension --- lib/extensions/__init__.py | 1 + lib/extensions/output.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 lib/extensions/output.py (limited to 'lib') diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index ebdd2fc9..a4654d2c 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -4,3 +4,4 @@ from params import Params from print_pdf import Print from simulate import Simulate from input import Input +from output import Output diff --git a/lib/extensions/output.py b/lib/extensions/output.py new file mode 100644 index 00000000..72bbe37d --- /dev/null +++ b/lib/extensions/output.py @@ -0,0 +1,49 @@ +import sys +import traceback +import os +import inkex +import tempfile + +from .base import InkstitchExtension +from ..i18n import _ +from ..output import write_embroidery_file +from ..stitch_plan import patches_to_stitch_plan +from ..svg import render_stitch_plan, PIXELS_PER_MM + + +class Output(InkstitchExtension): + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self) + self.OptionParser.add_option("-c", "--collapse_len_mm", + action="store", type="float", + dest="collapse_length_mm", default=3.0, + help="max collapse length (mm)") + self.OptionParser.add_option("-f", "--format", + dest="file_extension", + help="file extension to output (example: DST)") + + def effect(self): + if not self.get_elements(): + return + + patches = self.elements_to_patches(self.elements) + stitch_plan = patches_to_stitch_plan(patches, self.options.collapse_length_mm * PIXELS_PER_MM) + + # libembroidery wants to write to an actual file rather than stdout + temp_file = tempfile.NamedTemporaryFile(suffix=".%s" % self.options.file_extension, delete=False) + + # in windows, failure to close here will keep the file locked + temp_file.close() + + write_embroidery_file(temp_file.name, stitch_plan, self.document.getroot()) + + # inkscape will read the file contents from stdout and copy + # to the destination file that the user chose + with open(temp_file.name) as output_file: + sys.stdout.write(output_file.read()) + + # clean up the temp file + os.remove(temp_file.name) + + # don't let inkex output the SVG! + sys.exit(0) -- cgit v1.2.3 From ea1135c1451ab2db54fd52fbf48a8eee9c5a43e0 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Tue, 12 Jun 2018 22:15:32 -0400 Subject: add ZIP batch export extension --- lib/extensions/__init__.py | 1 + lib/extensions/base.py | 9 +++++ lib/extensions/embroider.py | 3 +- lib/extensions/zip.py | 80 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 lib/extensions/zip.py (limited to 'lib') diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index a4654d2c..6d3e00d8 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -5,3 +5,4 @@ from print_pdf import Print from simulate import Simulate from input import Input from output import Output +from zip import Zip diff --git a/lib/extensions/base.py b/lib/extensions/base.py index 4589132f..831b6dc6 100644 --- a/lib/extensions/base.py +++ b/lib/extensions/base.py @@ -200,6 +200,15 @@ class InkstitchExtension(inkex.Effect): def get_inkstitch_metadata(self): return InkStitchMetadata(self.document) + def get_base_file_name(self): + svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg") + + if svg_filename.endswith('.svg'): + svg_filename = svg_filename[:-4] + + return svg_filename + + def parse(self): """Override inkex.Effect to add Ink/Stitch xml namespace""" diff --git a/lib/extensions/embroider.py b/lib/extensions/embroider.py index a213be64..1e994e27 100644 --- a/lib/extensions/embroider.py +++ b/lib/extensions/embroider.py @@ -44,8 +44,7 @@ class Embroider(InkstitchExtension): if self.options.output_file: output_path = os.path.join(self.options.path, self.options.output_file) else: - svg_filename = self.document.getroot().get(inkex.addNS('docname', 'sodipodi'), "embroidery.svg") - csv_filename = svg_filename.replace('.svg', '.%s' % self.options.output_format) + csv_filename = '%s.%s' % (self.get_base_file_name(), self.options.output_format) output_path = os.path.join(self.options.path, csv_filename) def add_suffix(path, suffix): diff --git a/lib/extensions/zip.py b/lib/extensions/zip.py new file mode 100644 index 00000000..4720ad1e --- /dev/null +++ b/lib/extensions/zip.py @@ -0,0 +1,80 @@ +import sys +import traceback +import os +import inkex +import tempfile +from zipfile import ZipFile +from libembroidery import * + +from .base import InkstitchExtension +from ..i18n import _ +from ..output import write_embroidery_file +from ..stitch_plan import patches_to_stitch_plan +from ..svg import render_stitch_plan, PIXELS_PER_MM + + +class Zip(InkstitchExtension): + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self) + self.OptionParser.add_option("-c", "--collapse_len_mm", + action="store", type="float", + dest="collapse_length_mm", default=3.0, + help="max collapse length (mm)") + + # it's kind of obnoxious that I have to do this... + self.formats = [] + formatList = embFormatList_create() + curFormat = formatList + while(curFormat): + # extension includes the dot, so we'll remove it + extension = embFormat_extension(curFormat)[1:] + description = embFormat_description(curFormat) + writer_state = embFormat_writerState(curFormat) + + if writer_state.strip() and embFormat_type(curFormat) != EMBFORMAT_OBJECTONLY: + self.OptionParser.add_option('--format-%s' % extension, type="inkbool", dest=extension) + self.formats.append(extension) + curFormat = curFormat.next + + def effect(self): + if not self.get_elements(): + return + + patches = self.elements_to_patches(self.elements) + stitch_plan = patches_to_stitch_plan(patches, self.options.collapse_length_mm * PIXELS_PER_MM) + + base_file_name = self.get_base_file_name() + path = tempfile.mkdtemp() + + files = [] + + for format in self.formats: + if getattr(self.options, format): + output_file = os.path.join(path, "%s.%s" % (base_file_name, format)) + write_embroidery_file(output_file, stitch_plan, self.document.getroot()) + files.append(output_file) + + if not files: + self.errormsg(_("No embroidery file formats selected.")) + + temp_file = tempfile.NamedTemporaryFile(suffix=".zip", delete=False) + + # in windows, failure to close here will keep the file locked + temp_file.close() + + with ZipFile(temp_file.name, "w") as zip_file: + for file in files: + zip_file.write(file) + + # inkscape will read the file contents from stdout and copy + # to the destination file that the user chose + with open(temp_file.name) as output_file: + sys.stdout.write(output_file.read()) + + os.remove(temp_file.name) + for file in files: + os.remove(file) + os.rmdir(path) + + # don't let inkex output the SVG! + sys.exit(0) -- cgit v1.2.3 From f9a5e4c03a073d403222f0d5b7810cdaab90145a Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 13 Jun 2018 12:53:05 -0400 Subject: remove tmp directory from zip file paths --- lib/extensions/zip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/extensions/zip.py b/lib/extensions/zip.py index 4720ad1e..a7616536 100644 --- a/lib/extensions/zip.py +++ b/lib/extensions/zip.py @@ -64,7 +64,7 @@ class Zip(InkstitchExtension): with ZipFile(temp_file.name, "w") as zip_file: for file in files: - zip_file.write(file) + zip_file.write(file, os.path.basename(file)) # inkscape will read the file contents from stdout and copy # to the destination file that the user chose -- cgit v1.2.3 From 4c46c2eec1fb7cf9e85617030214bcb170b8b533 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 13 Jun 2018 20:10:22 -0400 Subject: fix zip file corruption --- lib/extensions/output.py | 4 +++- lib/extensions/zip.py | 10 +++++++++- lib/utils/io.py | 21 ++++++++++++++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) (limited to 'lib') diff --git a/lib/extensions/output.py b/lib/extensions/output.py index 72bbe37d..924c2d3a 100644 --- a/lib/extensions/output.py +++ b/lib/extensions/output.py @@ -35,12 +35,14 @@ class Output(InkstitchExtension): # in windows, failure to close here will keep the file locked temp_file.close() + # libembroidery likes to debug log things to stdout. No way to disable it. + save_stdout() write_embroidery_file(temp_file.name, stitch_plan, self.document.getroot()) # inkscape will read the file contents from stdout and copy # to the destination file that the user chose with open(temp_file.name) as output_file: - sys.stdout.write(output_file.read()) + sys.real_stdout.write(output_file.read()) # clean up the temp file os.remove(temp_file.name) diff --git a/lib/extensions/zip.py b/lib/extensions/zip.py index a7616536..ca12efdd 100644 --- a/lib/extensions/zip.py +++ b/lib/extensions/zip.py @@ -11,6 +11,7 @@ from ..i18n import _ from ..output import write_embroidery_file from ..stitch_plan import patches_to_stitch_plan from ..svg import render_stitch_plan, PIXELS_PER_MM +from ..utils.io import save_stdout class Zip(InkstitchExtension): @@ -48,12 +49,19 @@ class Zip(InkstitchExtension): files = [] + # libembroidery likes to debug log things to stdout. No way to disable it. + save_stdout() for format in self.formats: if getattr(self.options, format): output_file = os.path.join(path, "%s.%s" % (base_file_name, format)) write_embroidery_file(output_file, stitch_plan, self.document.getroot()) files.append(output_file) + # I'd love to do restore_stderr() here, but if I do, libembroidery's + # stuff still prints out and corrupts the zip! That's because it uses + # C's buffered stdout, so it hasn't actually written anything to the + # real standard output yet. + if not files: self.errormsg(_("No embroidery file formats selected.")) @@ -69,7 +77,7 @@ class Zip(InkstitchExtension): # inkscape will read the file contents from stdout and copy # to the destination file that the user chose with open(temp_file.name) as output_file: - sys.stdout.write(output_file.read()) + sys.real_stdout.write(output_file.read()) os.remove(temp_file.name) for file in files: diff --git a/lib/utils/io.py b/lib/utils/io.py index be1fdf24..44d48c2a 100644 --- a/lib/utils/io.py +++ b/lib/utils/io.py @@ -7,12 +7,31 @@ def save_stderr(): # GTK likes to spam stderr, which inkscape will show in a dialog. null = open(os.devnull, 'w') sys.stderr_dup = os.dup(sys.stderr.fileno()) + sys.real_stderr = os.fdopen(sys.stderr_dup, 'w') os.dup2(null.fileno(), 2) sys.stderr_backup = sys.stderr sys.stderr = StringIO() def restore_stderr(): + sys.real_stderr.close() os.dup2(sys.stderr_dup, 2) - sys.stderr_backup.write(sys.stderr.getvalue()) + sys.real_stderr.write(sys.stderr.getvalue()) sys.stderr = sys.stderr_backup + +# It's probably possible to generalize this code, but when I tried, +# the result was incredibly unreadable. +def save_stdout(): + null = open(os.devnull, 'w') + sys.stdout_dup = os.dup(sys.stdout.fileno()) + sys.real_stdout = os.fdopen(sys.stdout_dup, 'w') + os.dup2(null.fileno(), 1) + sys.stdout_backup = sys.stdout + sys.stdout = StringIO() + + +def restore_stdout(): + sys.real_stdout.close() + os.dup2(sys.stdout_dup, 1) + sys.real_stdout.write(sys.stdout.getvalue()) + sys.stdout = sys.stdout_backup -- cgit v1.2.3 From b674c192ee5ff7b3bbc48837379d1cea5f61b3bc Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Wed, 13 Jun 2018 20:45:51 -0400 Subject: fix issue with input plugin --- lib/extensions/input.py | 6 +++++- lib/utils/io.py | 8 ++------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'lib') diff --git a/lib/extensions/input.py b/lib/extensions/input.py index 251859c5..21248dd9 100644 --- a/lib/extensions/input.py +++ b/lib/extensions/input.py @@ -14,6 +14,7 @@ from ..svg import PIXELS_PER_MM, render_stitch_plan from ..svg.tags import INKSCAPE_LABEL from ..i18n import _ from ..stitch_plan import StitchPlan +from ..utils.io import save_stdout class Input(object): @@ -25,6 +26,9 @@ class Input(object): def affect(self, args): + # libembroidery likes to dump a bunch of debugging stuff to stdout + save_stdout() + embroidery_file = args[0] pattern = embPattern_create() embPattern_read(pattern, embroidery_file) @@ -65,4 +69,4 @@ class Input(object): # Note: this is NOT the same as centering the design in the canvas! layer.set('transform', 'translate(%s,%s)' % (extents[0], extents[1])) - print etree.tostring(svg) + print >> sys.real_stdout, etree.tostring(svg) diff --git a/lib/utils/io.py b/lib/utils/io.py index 44d48c2a..e5a246f3 100644 --- a/lib/utils/io.py +++ b/lib/utils/io.py @@ -9,15 +9,13 @@ def save_stderr(): sys.stderr_dup = os.dup(sys.stderr.fileno()) sys.real_stderr = os.fdopen(sys.stderr_dup, 'w') os.dup2(null.fileno(), 2) - sys.stderr_backup = sys.stderr sys.stderr = StringIO() def restore_stderr(): - sys.real_stderr.close() os.dup2(sys.stderr_dup, 2) sys.real_stderr.write(sys.stderr.getvalue()) - sys.stderr = sys.stderr_backup + sys.stderr = sys.real_stderr # It's probably possible to generalize this code, but when I tried, # the result was incredibly unreadable. @@ -26,12 +24,10 @@ def save_stdout(): sys.stdout_dup = os.dup(sys.stdout.fileno()) sys.real_stdout = os.fdopen(sys.stdout_dup, 'w') os.dup2(null.fileno(), 1) - sys.stdout_backup = sys.stdout sys.stdout = StringIO() def restore_stdout(): - sys.real_stdout.close() os.dup2(sys.stdout_dup, 1) sys.real_stdout.write(sys.stdout.getvalue()) - sys.stdout = sys.stdout_backup + sys.stdout = sys.real_stdout -- cgit v1.2.3 From 0659bc294e943bcaa10f63966e667003623e6da4 Mon Sep 17 00:00:00 2001 From: Lex Neva Date: Sat, 16 Jun 2018 22:33:02 -0400 Subject: fix output regression --- lib/extensions/output.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/extensions/output.py b/lib/extensions/output.py index 924c2d3a..f4b153e6 100644 --- a/lib/extensions/output.py +++ b/lib/extensions/output.py @@ -9,7 +9,7 @@ from ..i18n import _ from ..output import write_embroidery_file from ..stitch_plan import patches_to_stitch_plan from ..svg import render_stitch_plan, PIXELS_PER_MM - +from ..utils.io import save_stdout class Output(InkstitchExtension): def __init__(self, *args, **kwargs): -- cgit v1.2.3