kopia lustrzana https://github.com/inkstitch/inkstitch
Raster output (#3036)
* stitch plan preview png: 300, 600 dpi * add png export dpi optionpull/3046/head
rodzic
6c166dca61
commit
2b390b908e
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
]))
|
||||
|
|
|
@ -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.
|
||||
]))
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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>
|
||||
|
|
Ładowanie…
Reference in New Issue