kopia lustrzana https://github.com/inkstitch/inkstitch
Testing
rodzic
40536de8a6
commit
922307a68d
|
@ -311,7 +311,7 @@ def with_time(extension, remaining_args, profile_file_path: Path):
|
|||
import resource
|
||||
usage = resource.getrusage(resource.RUSAGE_SELF)
|
||||
log(f"Max RSS: {usage.ru_maxrss}KB")
|
||||
except: # Resource isn't supported on all platforms
|
||||
except Exception: # Resource isn't supported on all platforms
|
||||
pass
|
||||
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ from ..update import update_inkstitch_document
|
|||
|
||||
class InkstitchExtension(inkex.EffectExtension):
|
||||
"""Base class for Inkstitch extensions. Not intended for direct use."""
|
||||
document: inkex.SvgDocumentElement
|
||||
|
||||
# Set to True to hide this extension from release builds of Ink/Stitch. It will
|
||||
# only be available in development installations.
|
||||
|
|
|
@ -4,22 +4,21 @@
|
|||
# Licensed under the GNU GPL version 3.0 or later. See the file LICENSE for details.
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from copy import deepcopy
|
||||
import io
|
||||
from zipfile import ZipFile
|
||||
|
||||
from inkex import Boolean, Group, errormsg
|
||||
from lxml import etree
|
||||
from sanitize_filename import sanitize
|
||||
|
||||
import pyembroidery
|
||||
|
||||
from ..extensions.lettering_along_path import TextAlongPath
|
||||
from ..i18n import _
|
||||
from ..lettering import get_font_by_name
|
||||
from ..output import write_embroidery_file
|
||||
from ..stitch_plan import stitch_groups_to_stitch_plan
|
||||
from ..output import write_embroidery_file_stream
|
||||
from ..stitch_plan import stitch_groups_to_stitch_plan, StitchPlan
|
||||
from ..svg import get_correction_transform
|
||||
from ..threads import ThreadCatalog
|
||||
from ..utils import DotDict
|
||||
|
@ -129,33 +128,15 @@ class BatchLettering(InkstitchExtension):
|
|||
# The path should be labeled as "batch lettering"
|
||||
text_positioning_path = self.svg.findone(".//*[@inkscape:label='batch lettering']")
|
||||
|
||||
path = tempfile.mkdtemp()
|
||||
files = []
|
||||
for text in texts:
|
||||
stitch_plan, lettering_group = self.generate_stitch_plan(text, text_positioning_path)
|
||||
for file_format in file_formats:
|
||||
files.append(self.generate_output_file(file_format, path, text, stitch_plan))
|
||||
with ZipFile(sys.stdout.buffer, "w") as zip_file:
|
||||
for text in texts:
|
||||
stitch_plan, lettering_group = self.generate_stitch_plan(text, text_positioning_path)
|
||||
for file_format in file_formats:
|
||||
with zip_file.open(sanitize(f"{text}.{file_format}"), "w") as output_file:
|
||||
self.generate_output_file(output_file, file_format, stitch_plan)
|
||||
|
||||
self.reset_document(lettering_group, text_positioning_path)
|
||||
|
||||
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 output in files:
|
||||
zip_file.write(output, os.path.basename(output))
|
||||
|
||||
# inkscape will read the file contents from stdout and copy
|
||||
# to the destination file that the user chose
|
||||
with open(temp_file.name, 'rb') as output_file:
|
||||
sys.stdout.buffer.write(output_file.read())
|
||||
|
||||
os.remove(temp_file.name)
|
||||
for output in files:
|
||||
os.remove(output)
|
||||
os.rmdir(path)
|
||||
# Reset document for next text
|
||||
self.reset_document(lettering_group, text_positioning_path)
|
||||
|
||||
def reset_document(self, lettering_group, text_positioning_path):
|
||||
# reset document for the next iteration
|
||||
|
@ -163,20 +144,18 @@ class BatchLettering(InkstitchExtension):
|
|||
index = parent.index(lettering_group)
|
||||
if text_positioning_path is not None:
|
||||
parent.insert(index, text_positioning_path)
|
||||
parent.remove(lettering_group)
|
||||
|
||||
def generate_output_file(self, file_format, path, text, stitch_plan):
|
||||
text = text.replace('\n', '')
|
||||
output_file = os.path.join(path, f"{text}.{file_format}")
|
||||
lettering_group.delete()
|
||||
|
||||
def generate_output_file(self, output_file: io.IOBase, file_format: str, stitch_plan: StitchPlan) -> None:
|
||||
if file_format == 'svg':
|
||||
document = deepcopy(self.document.getroot())
|
||||
with open(output_file, 'w', encoding='utf-8') as svg:
|
||||
svg.write(etree.tostring(document).decode('utf-8'))
|
||||
output_file.write(etree.tostring(self.document.getroot()))
|
||||
else:
|
||||
write_embroidery_file(output_file, stitch_plan, self.document.getroot())
|
||||
|
||||
return output_file
|
||||
# Unfortunately some of pyembroidery's writers need `seek` and `tell`, which stdout doesn't support.
|
||||
# Until that changes, we need to write this to an in-memory buffer that does support these, then
|
||||
# write out the result to the stdout-backed zipfile
|
||||
with io.BytesIO() as buf:
|
||||
write_embroidery_file_stream(buf, file_format, stitch_plan, self.document.getroot())
|
||||
output_file.write(buf.getvalue())
|
||||
|
||||
def generate_stitch_plan(self, text, text_positioning_path):
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import Tuple, Optional, NoReturn
|
||||
import io
|
||||
|
||||
import inkex
|
||||
from pyembroidery.exceptions import TooManyColorChangesError
|
||||
|
@ -14,7 +16,7 @@ import pyembroidery
|
|||
|
||||
from .commands import global_command
|
||||
from .i18n import _
|
||||
from .stitch_plan import Stitch
|
||||
from .stitch_plan import Stitch, StitchPlan
|
||||
from .svg import PIXELS_PER_MM
|
||||
from .utils import Point
|
||||
|
||||
|
@ -50,7 +52,15 @@ def jump_to_stop_point(pattern, svg):
|
|||
pattern.add_stitch_absolute(pyembroidery.JUMP, stop_position.point.x, stop_position.point.y)
|
||||
|
||||
|
||||
def write_embroidery_file(file_path, stitch_plan, svg, settings={}):
|
||||
def _compute_pattern_settings(
|
||||
extension: str,
|
||||
stitch_plan: StitchPlan,
|
||||
svg: inkex.SvgDocumentElement,
|
||||
settings: Optional[dict]) -> Tuple[pyembroidery.EmbPattern, dict]:
|
||||
# Return an embroidery pattern and settings to pass to pyembroidery
|
||||
if settings is None:
|
||||
settings = {}
|
||||
|
||||
# convert from pixels to millimeters
|
||||
# also multiply by 10 to get tenths of a millimeter as required by pyembroidery
|
||||
scale = 10 / PIXELS_PER_MM
|
||||
|
@ -89,10 +99,10 @@ def write_embroidery_file(file_path, stitch_plan, svg, settings={}):
|
|||
"full_jump": True,
|
||||
})
|
||||
|
||||
if not file_path.endswith(('.col', '.edr', '.inf')):
|
||||
if extension not in ('col', 'edr', 'inf'):
|
||||
settings['encode'] = True
|
||||
|
||||
if file_path.endswith('.csv'):
|
||||
if extension == 'csv':
|
||||
# Special treatment for CSV: instruct pyembroidery not to do any post-
|
||||
# processing. This will allow the user to match up stitch numbers seen
|
||||
# in the simulator with commands in the CSV.
|
||||
|
@ -100,6 +110,36 @@ def write_embroidery_file(file_path, stitch_plan, svg, settings={}):
|
|||
settings['max_jump'] = float('inf')
|
||||
settings['explicit_trim'] = False
|
||||
|
||||
return pattern, settings
|
||||
|
||||
|
||||
def _too_many_color_changes(e: TooManyColorChangesError) -> NoReturn:
|
||||
match = re.search("d+", str(e))
|
||||
if match:
|
||||
num_color_changes = match.group()
|
||||
else:
|
||||
# Should never get here, the number of color changes should have been in the error's message
|
||||
num_color_changes = "???"
|
||||
msg = _("Couldn't save embroidery file.")
|
||||
msg += '\n\n'
|
||||
msg += _("There are {num_color_changes} color changes in your design. This is way too many.").format(num_color_changes=num_color_changes)
|
||||
msg += '\n'
|
||||
msg += _("Please reduce color changes. Find more information on our website:")
|
||||
msg += '\n\n'
|
||||
msg += _("https://inkstitch.org/docs/faq/#too-many-color-changes")
|
||||
inkex.errormsg(msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def write_embroidery_file(
|
||||
file_path: str,
|
||||
stitch_plan: StitchPlan,
|
||||
svg: inkex.SvgDocumentElement,
|
||||
settings: Optional[dict] = None) -> None:
|
||||
""" Write embroidery file to a given path """
|
||||
|
||||
pattern, settings = _compute_pattern_settings(os.path.splitext(svg.name)[1], stitch_plan, svg, settings)
|
||||
|
||||
try:
|
||||
pyembroidery.write(pattern, file_path, settings)
|
||||
except IOError as e:
|
||||
|
@ -109,13 +149,34 @@ def write_embroidery_file(file_path, stitch_plan, svg, settings={}):
|
|||
inkex.errormsg(msg)
|
||||
sys.exit(1)
|
||||
except TooManyColorChangesError as e:
|
||||
num_color_changes = re.search("d+", str(e)).group()
|
||||
msg = _("Couldn't save embrodiery file.")
|
||||
msg += '\n\n'
|
||||
msg += _("There are {num_color_changes} color changes in your design. This is way too many.").format(num_color_changes=num_color_changes)
|
||||
msg += '\n'
|
||||
msg += _("Please reduce color changes. Find more information on our website:")
|
||||
msg += '\n\n'
|
||||
msg += _("https://inkstitch.org/docs/faq/#too-many-color-changes")
|
||||
_too_many_color_changes(e)
|
||||
|
||||
|
||||
def write_embroidery_file_stream(
|
||||
stream: io.IOBase,
|
||||
extension: str,
|
||||
stitch_plan: StitchPlan,
|
||||
svg: inkex.SvgDocumentElement,
|
||||
settings: Optional[dict] = None) -> None:
|
||||
""" Write embroidery file to a stream """
|
||||
|
||||
pattern, settings = _compute_pattern_settings(extension, stitch_plan, svg, settings)
|
||||
|
||||
try:
|
||||
for file_type in pyembroidery.EmbPattern.supported_formats():
|
||||
if file_type["extension"] != extension:
|
||||
continue
|
||||
writer = file_type.get("writer", None)
|
||||
if writer is None:
|
||||
continue
|
||||
|
||||
pyembroidery.EmbPattern.write_embroidery(writer, pattern, stream, settings)
|
||||
break
|
||||
except IOError as e:
|
||||
# L10N low-level file error. %(error)s is (hopefully?) translated by
|
||||
# the user's system automatically.
|
||||
msg = _("Error writing: %(error)s") % dict(error=e.strerror)
|
||||
inkex.errormsg(msg)
|
||||
sys.exit(1)
|
||||
except TooManyColorChangesError as e:
|
||||
_too_many_color_changes(e)
|
||||
|
|
3
mypy.ini
3
mypy.ini
|
@ -35,6 +35,9 @@ ignore_missing_imports = True
|
|||
[mypy-winutils.*]
|
||||
ignore_missing_imports = True
|
||||
|
||||
[mypy-sanitize_filename.*]
|
||||
ignore_missing_imports = True
|
||||
|
||||
# ... And this one is ours but is missing type information for now anyway...
|
||||
[mypy-pyembroidery.*]
|
||||
ignore_missing_imports = True
|
||||
|
|
|
@ -32,6 +32,7 @@ flask-cors
|
|||
pywinutils ; sys_platform == 'win32'
|
||||
pywin32 ; sys_platform == 'win32'
|
||||
types-pywin32; sys_platform == 'win32'
|
||||
sanitize_filename
|
||||
|
||||
# Test dependencies.
|
||||
# It should be okay to include these here because this list isn't the one used for bundling dependencies.
|
||||
|
|
Ładowanie…
Reference in New Issue