summaryrefslogtreecommitdiff
path: root/lib/extensions/zip.py
blob: e9dcefcba8cd56006658c1361483cfb53e6c64c1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Authors: see git history
#
# Copyright (c) 2010 Authors
# Licensed under the GNU GPL version 3.0 or later.  See the file LICENSE for details.

import os
import sys
import tempfile
from copy import deepcopy
from zipfile import ZipFile

from inkex import Boolean, errormsg
from inkex.units import convert_unit
from lxml import etree

import pyembroidery

from ..i18n import _
from ..output import write_embroidery_file
from ..stitch_plan import stitch_groups_to_stitch_plan
from ..svg import PIXELS_PER_MM, render_stitch_plan
from ..threads import ThreadCatalog
from ..utils.geometry import Point
from .base import InkstitchExtension
from .png_simple import generate_png
from .thread_list import get_threadlist


class Zip(InkstitchExtension):
    def __init__(self, *args, **kwargs):
        InkstitchExtension.__init__(self)

        self.arg_parser.add_argument('--notebook')
        self.arg_parser.add_argument('--custom-file-name', type=str, default='', dest='custom_file_name')

        # it's kind of obnoxious that I have to do this...
        self.formats = []
        for format in pyembroidery.supported_formats():
            if 'writer' in format and format['category'] in ['embroidery', 'color', 'image', 'stitch']:
                extension = format['extension']
                self.arg_parser.add_argument('--format-%s' % extension, type=Boolean, default=False, dest=extension)
                self.formats.append(extension)
        self.arg_parser.add_argument('--format-svg', type=Boolean, default=False, dest='svg')
        self.formats.append('svg')
        self.arg_parser.add_argument('--format-threadlist', type=Boolean, default=False, dest='threadlist')
        self.formats.append('threadlist')
        self.arg_parser.add_argument('--format-png-realistic', type=Boolean, default=False, dest='png_realistic')
        self.arg_parser.add_argument('--dpi-realistic', type=int, default=300, dest='dpi_realistic')
        self.formats.append('png_realistic')
        self.arg_parser.add_argument('--format-png-simple', type=Boolean, default=False, dest='png_simple')
        self.arg_parser.add_argument('--png-simple-line-width', type=float, default=0.3, dest='line_width')
        self.arg_parser.add_argument('--dpi-simple', type=int, default=300, dest='dpi_simple')
        self.formats.append('png_simple')

        self.arg_parser.add_argument('--x-repeats', type=int, default=1, dest='x_repeats', )
        self.arg_parser.add_argument('--y-repeats', type=int, default=1, dest='y_repeats',)
        self.arg_parser.add_argument('--x-spacing', type=float, default=100, dest='x_spacing')
        self.arg_parser.add_argument('--y-spacing', type=float, default=100, dest='y_spacing',)

    def effect(self):
        if not self.get_elements():
            return

        self.metadata = self.get_inkstitch_metadata()
        collapse_len = self.metadata['collapse_len_mm']
        min_stitch_len = self.metadata['min_stitch_len_mm']
        stitch_groups = self.elements_to_stitch_groups(self.elements)
        stitch_plan = stitch_groups_to_stitch_plan(stitch_groups, collapse_len=collapse_len, min_stitch_len=min_stitch_len)
        ThreadCatalog().match_and_apply_palette(stitch_plan, self.get_inkstitch_metadata()['thread-palette'])

        if self.options.x_repeats != 1 or self.options.y_repeats != 1:
            stitch_plan = self._make_offsets(stitch_plan)

        base_file_name = self._get_file_name()
        path = tempfile.mkdtemp()

        files = self.generate_output_files(stitch_plan, path, base_file_name)

        if not files:
            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, os.path.basename(file))

        # 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 file in files:
            os.remove(file)
        os.rmdir(path)

        # don't let inkex output the SVG!
        sys.exit(0)

    def _get_file_name(self):
        if self.options.custom_file_name:
            base_file_name = self.options.custom_file_name
        else:
            base_file_name = self.get_base_file_name()
        return base_file_name

    def _make_offsets(self, stitch_plan):
        dx = self.options.x_spacing * PIXELS_PER_MM
        dy = self.options.y_spacing * PIXELS_PER_MM
        offsets = []
        for x in range(self.options.x_repeats):
            for y in range(self.options.y_repeats):
                offsets.append(Point(x * dx, y * dy))
        return stitch_plan.make_offsets(offsets)

    def generate_output_files(self, stitch_plan, path, base_file_name):
        files = []
        for format in self.formats:
            if getattr(self.options, format):
                output_file = os.path.join(path, "%s.%s" % (base_file_name, format))
                if 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'))
                elif format == 'threadlist':
                    output_file = os.path.join(path, "%s_%s.txt" % (base_file_name, _("threadlist")))
                    with open(output_file, 'w', encoding='utf-8') as output:
                        output.write(get_threadlist(stitch_plan, base_file_name))
                elif format == 'png_realistic':
                    output_file = os.path.join(path, f"{base_file_name}_realistic.png")
                    layer = render_stitch_plan(self.svg, stitch_plan, True, visual_commands=False, render_jumps=False)
                    self.generate_png_output(output_file, layer, self.options.dpi_realistic)
                elif format == 'png_simple':
                    output_file = os.path.join(path, f"{base_file_name}_simple.png")
                    line_width = convert_unit(f"{self.options.line_width}mm", self.svg.document_unit)
                    layer = render_stitch_plan(self.svg, stitch_plan, False, visual_commands=False,
                                               render_jumps=False, line_width=line_width)
                    self.generate_png_output(output_file, layer, self.options.dpi_simple)
                else:
                    write_embroidery_file(output_file, stitch_plan, self.document.getroot())
                files.append(output_file)
        return files

    def generate_png_output(self, output_file, layer, dpi):
        with tempfile.TemporaryDirectory() as tempdir:
            temp_svg_path = f"{tempdir}/temp.svg"
            with open(temp_svg_path, "wb") as f:
                f.write(self.svg.tostring())
            generate_png(self.svg, layer, temp_svg_path, output_file, dpi)