diff --git a/CHANGELOG.md b/CHANGELOG.md index 37802fdd..dc58d430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ New features and improvements: * Relative coordinates are now used by default to reduce file size. If absolute coordinates are needed, they a new `--absolute` option for the `write` command. * A homing command (as defined by the `final_pu_params` configuration parameter) is no longer emitted between layers. * The viewer (`show` command) now catches interruptions from the terminal (ctrl-C) and closes itself (#321) +* The `read` command now accepts `-` as file path to read from the standard input (#322) Bug fixes: * ... diff --git a/tests/test_files.py b/tests/test_files.py index b934be0c..3b9db481 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -7,7 +7,7 @@ import pytest import vpype as vp -from vpype_cli import cli +from vpype_cli import DebugData, cli from .utils import TEST_FILE_DIRECTORY @@ -150,3 +150,18 @@ def test_read_with_viewbox(tmp_path): assert height == 100 assert len(lc) == 1 assert np.all(np.isclose(lc[0], np.array([0, 100 + 100j]))) + + +def test_read_stdin(runner): + svg = f""" + + + + """ + + result = runner.invoke(cli, "read - dbsample dbdump", input=svg) + data = DebugData.load(result.output) + + assert result.exit_code == 0 + assert data[0].count == 1 diff --git a/vpype/io.py b/vpype/io.py index 266be90f..eeb19faa 100644 --- a/vpype/io.py +++ b/vpype/io.py @@ -200,7 +200,7 @@ def _extract_paths(group: svgelements.Group, recursive) -> _PathListType: def read_svg( - filename: str, + file: Union[str, TextIO], quantization: float, crop: bool = True, simplify: bool = False, @@ -215,7 +215,7 @@ def read_svg( as tolerance. Args: - filename: path of the SVG file + file: path of the SVG file or stream object quantization: maximum size of segment used to approximate curved geometries crop: crop the geometries to the SVG boundaries simplify: run Shapely's simplify on loaded geometry @@ -231,7 +231,7 @@ def read_svg( """ # default width is for SVG with % width/height - svg = svgelements.SVG.parse(filename, width=default_width, height=default_height) + svg = svgelements.SVG.parse(file, width=default_width, height=default_height) paths = _extract_paths(svg, recursive=True) lc = _convert_flattened_paths(paths, quantization, simplify, parallel) @@ -242,7 +242,7 @@ def read_svg( def read_multilayer_svg( - filename: str, + file: Union[str, TextIO], quantization: float, crop: bool = True, simplify: bool = False, @@ -266,7 +266,7 @@ def read_multilayer_svg( as tolerance. Args: - filename: path of the SVG file + file: path of the SVG file or stream object quantization: maximum size of segment used to approximate curved geometries crop: crop the geometries to the SVG boundaries simplify: run Shapely's simplify on loaded geometry @@ -281,7 +281,7 @@ def read_multilayer_svg( SVG dimensions """ - svg = svgelements.SVG.parse(filename, width=default_width, height=default_height) + svg = svgelements.SVG.parse(file, width=default_width, height=default_height) document = Document() diff --git a/vpype_cli/read.py b/vpype_cli/read.py index 31beddbf..05c51238 100644 --- a/vpype_cli/read.py +++ b/vpype_cli/read.py @@ -1,5 +1,6 @@ import logging -from typing import Optional, Tuple, cast +import sys +from typing import Optional, Tuple import click @@ -7,7 +8,6 @@ Document, LayerType, LengthType, - LineCollection, PageSizeType, global_processor, read_multilayer_svg, @@ -21,7 +21,7 @@ @cli.command(group="Input") -@click.argument("file", type=click.Path(exists=True, dir_okay=False)) +@click.argument("file", type=click.Path(exists=True, dir_okay=False, allow_dash=True)) @click.option("-m", "--single-layer", is_flag=True, help="Single layer mode.") @click.option( "-l", @@ -89,6 +89,8 @@ def read( ) -> Document: """Extract geometries from a SVG file. + FILE may be a file path path or a dash (-) to read from the standard input instead. + By default, the `read` command attempts to preserve the layer structure of the SVG. In this context, top-level groups () are each considered a layer. If any, all non-group, top-level SVG elements are imported into layer 1. @@ -162,6 +164,9 @@ def read( if display_landscape: width, height = height, width + if file == "-": + file = sys.stdin + if single_layer: lc, width, height = read_svg( file,