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,