diff options
| author | Kaalleen <36401965+kaalleen@users.noreply.github.com> | 2024-07-03 07:09:06 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-07-03 07:09:06 +0200 |
| commit | 2b390b908e93a7f6f529b9db624dd0d2c3ca184a (patch) | |
| tree | 569a352d3f5de13d6507ce89938d087a1f9d6ec0 | |
| parent | 6c166dca61e17efbb3173dcf5ef484e3aa739a50 (diff) | |
Raster output (#3036)
* stitch plan preview png: 300, 600 dpi
* add png export dpi option
| -rw-r--r-- | lib/extensions/png_realistic.py | 8 | ||||
| -rw-r--r-- | lib/extensions/png_simple.py | 21 | ||||
| -rw-r--r-- | lib/extensions/stitch_plan_preview.py | 33 | ||||
| -rw-r--r-- | lib/extensions/zip.py | 18 | ||||
| -rw-r--r-- | templates/png_realistic.xml | 14 | ||||
| -rw-r--r-- | templates/png_simple.xml | 3 | ||||
| -rw-r--r-- | templates/stitch_plan_preview.xml | 4 | ||||
| -rw-r--r-- | templates/zip.xml | 40 |
8 files changed, 92 insertions, 49 deletions
diff --git a/lib/extensions/png_realistic.py b/lib/extensions/png_realistic.py index 24ebce55..e3216efd 100644 --- a/lib/extensions/png_realistic.py +++ b/lib/extensions/png_realistic.py @@ -13,6 +13,12 @@ from .png_simple import write_png_output class PngRealistic(InkstitchExtension): + def __init__(self, *args, **kwargs): + InkstitchExtension.__init__(self) + + self.arg_parser.add_argument('--notebook') + self.arg_parser.add_argument('--dpi', type=int, default=300, dest='dpi') + def effect(self): if not self.get_elements(): return @@ -26,7 +32,7 @@ class PngRealistic(InkstitchExtension): layer = render_stitch_plan(self.svg, stitch_plan, True, visual_commands=False, render_jumps=False) - write_png_output(self.svg, layer) + write_png_output(self.svg, layer, self.options.dpi) # don't let inkex output the SVG! sys.exit(0) diff --git a/lib/extensions/png_simple.py b/lib/extensions/png_simple.py index 7902b43b..dd7748d9 100644 --- a/lib/extensions/png_simple.py +++ b/lib/extensions/png_simple.py @@ -20,8 +20,9 @@ class PngSimple(InkstitchExtension): def __init__(self, *args, **kwargs): InkstitchExtension.__init__(self) - self.arg_parser.add_argument('--notebook', type=str, default='') - self.arg_parser.add_argument('--line_width', type=str, default='', dest='line_width') + self.arg_parser.add_argument('--notebook') + self.arg_parser.add_argument('--line_width', type=float, default=0.3, dest='line_width') + self.arg_parser.add_argument('--dpi', type=int, default=300, dest='dpi') def effect(self): if not self.get_elements(): @@ -38,13 +39,13 @@ class PngSimple(InkstitchExtension): layer = render_stitch_plan(self.svg, stitch_plan, False, visual_commands=False, render_jumps=False, line_width=line_width) - write_png_output(self.svg, layer) + write_png_output(self.svg, layer, self.options.dpi) # don't let inkex output the SVG! sys.exit(0) -def write_png_output(svg, layer): +def write_png_output(svg, layer, dpi): with TemporaryDirectory() as tempdir: # Inkex's command functionality also writes files to temp directories like this. temp_svg_path = f"{tempdir}/temp.svg" @@ -52,7 +53,7 @@ def write_png_output(svg, layer): with open(temp_svg_path, "wb") as f: f.write(svg.tostring()) - generate_png(svg, layer, temp_svg_path, temp_png_path) + generate_png(svg, layer, temp_svg_path, temp_png_path, dpi) # inkscape will read the file contents from stdout and copy # to the destination file that the user chose @@ -60,13 +61,13 @@ def write_png_output(svg, layer): sys.stdout.buffer.write(output_file.read()) -def generate_png(svg, layer, input_path, output_path): +def generate_png(svg, layer, input_path, output_path, dpi): inkscape(input_path, actions="; ".join([ - f"export-id:{layer.get_id()}", + f"export-id: {layer.get_id()}", "export-id-only", "export-type:png", - f"export-dpi:{96*8}", - f"export-filename:{output_path}", - f"export-background:{get_pagecolor(svg.namedview)}", + f"export-dpi: {dpi}", + f"export-filename: {output_path}", + f"export-background: {get_pagecolor(svg.namedview)}", "export-do" # Inkscape docs say this should be implicit at the end, but it doesn't seem to be. ])) diff --git a/lib/extensions/stitch_plan_preview.py b/lib/extensions/stitch_plan_preview.py index 464e4200..8f76a447 100644 --- a/lib/extensions/stitch_plan_preview.py +++ b/lib/extensions/stitch_plan_preview.py @@ -38,7 +38,7 @@ class StitchPlanPreview(InkstitchExtension): self.arg_parser.add_argument("-o", "--overwrite", type=Boolean, default=True, dest="overwrite") def effect(self): - realistic, raster_mult = self.parse_mode() + realistic, dpi = self.parse_mode() # delete old stitch plan self.remove_old() @@ -58,7 +58,7 @@ class StitchPlanPreview(InkstitchExtension): layer = render_stitch_plan(svg, stitch_plan, realistic, visual_commands, render_jumps=self.options.render_jumps) if self.options.ignore_layer and not self.options.mode[-1].isdigit(): add_layer_commands(layer, ["ignore_layer"]) - layer = self.rasterize(svg, layer, raster_mult) + layer = self.rasterize(svg, layer, dpi) # update layer visibility (unchanged, hidden, lower opacity) groups = self.document.getroot().findall(SVG_GROUP_TAG) @@ -72,27 +72,27 @@ class StitchPlanPreview(InkstitchExtension): def parse_mode(self) -> Tuple[bool, Optional[int]]: """ Parse the "mode" option and return a tuple of a bool indicating if realistic rendering should be used, - and an optional int indicating the resolution multiplier to use for rasterization, or None if rasterization should not be used. + and an optional int indicating the dpi value to use for rasterization, or None if rasterization should not be used. """ realistic = False - raster_mult: Optional[int] = None + dpi: Optional[int] = None render_mode = self.options.mode if render_mode == "simple": pass elif render_mode.startswith("realistic-"): realistic = True - raster_option = render_mode.split('-')[1] - if raster_option != "vector": + dpi_option = render_mode.split('-')[1] + if dpi_option != "vector": try: - raster_mult = int(raster_option) + dpi = int(dpi_option) except ValueError: - errormsg(f"Invalid raster mode {raster_option}") + errormsg(f"Invalid raster mode {dpi_option}") sys.exit(1) else: errormsg(f"Invalid render mode {render_mode}") sys.exit(1) - return (realistic, raster_mult) + return (realistic, dpi) def remove_old(self): svg = self.document.getroot() @@ -104,8 +104,8 @@ class StitchPlanPreview(InkstitchExtension): if layer is not None: layer.set('id', svg.get_unique_id('inkstitch_stitch_plan_')) - def rasterize(self, svg: BaseElement, layer: BaseElement, raster_mult: Optional[int]) -> BaseElement: - if raster_mult is None: + def rasterize(self, svg: BaseElement, layer: BaseElement, dpi: Optional[int]) -> BaseElement: + if dpi is None: # Don't rasterize if there's no reason to. return layer else: @@ -121,16 +121,17 @@ class StitchPlanPreview(InkstitchExtension): # Instead, especially because we need to invoke Inkscape anyway to perform the rasterization, we get # the bounding box with query commands before we perform the export. This is quite cheap. out = inkscape(temp_svg_path, actions="; ".join([ - f"select-by-id:{layer.get_id()}", + f"select-by-id: {layer.get_id()}", "query-x", "query-y", "query-width", "query-height", - f"export-id:{layer.get_id()}", + f"export-id: {layer.get_id()}", "export-id-only", - "export-type:png", - f"export-dpi:{96*raster_mult}", - f"export-filename:{temp_png_path}", + "export-type: png", + f"export-dpi: {dpi}", + "export-png-color-mode: RGBA_16", + f"export-filename: {temp_png_path}", "export-do" # Inkscape docs say this should be implicit at the end, but it doesn't seem to be. ])) diff --git a/lib/extensions/zip.py b/lib/extensions/zip.py index 1c33c080..c2385886 100644 --- a/lib/extensions/zip.py +++ b/lib/extensions/zip.py @@ -30,7 +30,7 @@ class Zip(InkstitchExtension): def __init__(self, *args, **kwargs): InkstitchExtension.__init__(self) - self.arg_parser.add_argument('--notebook', type=str, default='') + 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... @@ -44,10 +44,12 @@ class Zip(InkstitchExtension): 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('--format-png-realistic', type=Boolean, default=False, dest='png_realistic') + self.arg_parser.add_argument('--dpi-realistic', type=int, default='', 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('--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='', dest='dpi_simple') self.formats.append('png_simple') self.arg_parser.add_argument('--x-repeats', type=int, default=1, dest='x_repeats', ) @@ -131,21 +133,21 @@ class Zip(InkstitchExtension): 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.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.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): + 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) + generate_png(self.svg, layer, temp_svg_path, output_file, dpi) diff --git a/templates/png_realistic.xml b/templates/png_realistic.xml index 93f0ee2b..7aae6696 100644 --- a/templates/png_realistic.xml +++ b/templates/png_realistic.xml @@ -10,6 +10,20 @@ <dataloss>true</dataloss> </output> <param name="extension" type="string" gui-hidden="true">png_realistic</param> + <param name="notebook" type="notebook"> + <page name="settings" gui-text="Settings"> + <param name="dpi" type="int" min="100" max="2000" gui-text="DPI">300</param> + </page> + <page name="info" gui-text="Help"> + <label appearance="header">PNG file export</label> + <label>Export embroidery design to PNG</label> + <spacer /> + <separator /> + <spacer /> + <label>Read more on our website</label> + <label appearance="url">https://inkstitch.org/docs/import-export/</label> + </page> + </param> <script> {{ command_tag | safe }} </script> diff --git a/templates/png_simple.xml b/templates/png_simple.xml index be12e784..e42a8819 100644 --- a/templates/png_simple.xml +++ b/templates/png_simple.xml @@ -13,6 +13,7 @@ <param name="notebook" type="notebook"> <page name="settings" gui-text="Settings"> <param name="line_width" type="float" precision="2" min="0.01" max="5" gui-text="Line width (mm)">0.3</param> + <param name="dpi" type="int" min="100" max="2000" gui-text="DPI">300</param> </page> <page name="info" gui-text="Help"> <label appearance="header">PNG file export</label> @@ -20,7 +21,7 @@ <spacer /> <separator /> <spacer /> - <label>Read more on our webiste</label> + <label>Read more on our website</label> <label appearance="url">https://inkstitch.org/docs/import-export/</label> </page> </param> diff --git a/templates/stitch_plan_preview.xml b/templates/stitch_plan_preview.xml index f7180915..a3385ed3 100644 --- a/templates/stitch_plan_preview.xml +++ b/templates/stitch_plan_preview.xml @@ -14,8 +14,8 @@ <param name="render-mode" type="optiongroup" appearance="combo" gui-text="Render Mode" gui-description="Realistic modes will render to a raster image for performance reasons. Realistic Vector may cause Inkscape to slow down for complex designs."> <option value="simple">Simple</option> - <option value="realistic-8">Realistic</option> - <option value="realistic-16">Realistic High Quality</option> + <option value="realistic-300">Realistic</option> + <option value="realistic-600">Realistic High Quality</option> <option value="realistic-vector">Realistic Vector (slow)</option> </param> <spacer /> diff --git a/templates/zip.xml b/templates/zip.xml index c34027eb..277bbae9 100644 --- a/templates/zip.xml +++ b/templates/zip.xml @@ -9,23 +9,41 @@ <filetypetooltip>Create a ZIP with multiple embroidery file formats using Ink/Stitch</filetypetooltip> <dataloss>true</dataloss> </output> + <param name="extension" type="string" gui-hidden="true">zip</param> <param name="custom-file-name" type="string" gui-text="Custom file name" gui-description="Defines the file names inside the zip archive. Leave empty for default file name."></param> <spacer /> <param name="notebook" type="notebook"> <page name="file-formats" gui-text="File Formats"> <label>Output formats:</label> - {%- for format, description, mimetype, category in formats %} - {%- if category != "vector" and category != "debug" %} - <param name="format-{{ format }}" type="boolean" gui-text=".{{ format | upper }}: {{ description }}">false</param> - {%- endif %} - {%- endfor %} - <param name="format-threadlist" type="boolean" gui-text=".TXT: Threadlist [COLOR]">false</param> - <param name="format-png_realistic" type="boolean" gui-text=".PNG: Portable Network Graphics (Realistic) [IMAGE]">false</param> - <param name="format-png_simple" type="boolean" gui-text=".PNG: Portable Network Graphics (Simple) [IMAGE]">false</param> - <param name="png_simple_line_width" type="float" precision="2" min="0.01" max="5" gui-text="Line width (mm)" indent="4">0.3</param> - <param name="format-svg" type="boolean" gui-text=".SVG: Scalable Vector Graphic">false</param> - <param name="extension" type="string" gui-hidden="true">zip</param> + <hbox> + <vbox> + {%- for format, description, mimetype, category in formats %} + {%- if category != "vector" and category != "debug" %} + <param name="format-{{ format }}" type="boolean" gui-text=".{{ format | upper }}: {{ description }}">false</param> + {%- endif %} + {%- if loop.index == 13 %} + </vbox> + <spacer /> + <separator /> + <spacer /> + <vbox> + {%- endif %} + {%- endfor %} + <param name="format-threadlist" type="boolean" gui-text=".TXT: Threadlist [COLOR]">false</param> + </vbox> + <spacer /> + <separator /> + <spacer /> + <vbox> + <param name="format-svg" type="boolean" gui-text=".SVG: Scalable Vector Graphic">false</param> + <param name="format-png-realistic" type="boolean" gui-text=".PNG: Portable Network Graphics (Realistic) [IMAGE]">false</param> + <param name="dpi-realistic" type="int" min="100" max="2000" gui-text="DPI" indent="4">300</param> + <param name="format-png-simple" type="boolean" gui-text=".PNG: Portable Network Graphics (Simple) [IMAGE]">false</param> + <param name="png-simple-line-width" type="float" precision="2" min="0.01" max="5" gui-text="Line width (mm)" indent="4">0.3</param> + <param name="dpi-simple" type="int" min="100" max="2000" gui-text="DPI" indent="4">300</param> + </vbox> + </hbox> </page> <page name="panelization" gui-text="Panelization Options"> <label>For single design set horizontal and vertical repeats to 1</label> |
