Skip to content

Commit

Permalink
Added sorting by attribute to read command (#378)
Browse files Browse the repository at this point in the history
- added vpype.read_svg_by_attribute() to read SVG sorting geometries by attribute(s)
- multiple refactoring in vpype/io.py
- added `--attr` option to `read` command to enable sorty by attribute
- `read` now use single-layer mode when `--layer` is used, even if `--single-layer` is not used
- updated tests
- added `global_opt` param to `vpype_cli.execute()`
- added cookbook section

Fixes #35 
Fixes #36
  • Loading branch information
abey79 authored Jan 16, 2022
1 parent 13fe6a5 commit 8b70225
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 73 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ New features and improvements:
* The new `pens` command can apply a predefined or custom scheme on multiple layers at once. Two schemes, `rgb` and `cmyk`, are included and others may be defined in the configuration file.
* The `show` and `write` commands were updated to take into account these new layer properties.

* The `read` command can now optionally sort geometries by attributes (e.g. stroke color, stroke width, etc.) instead of by SVG layer (#378)

* The `read` and `write` commands now preserve a sub-set of SVG attributes (experimental) (#359)

The `read` command now seeks for SVG attributes (e.g. `stroke-dasharray`) which are shared by all geometries in each layer. When found, such attributes are saved as layer properties (with their name prefixed with `svg:`, e.g. `svg:stroke-dasharray`). The `write` command can optionally restore these attributes in the output SVG (using the `--restore-attribs`), thereby maintaining some of the visual aspects of the original SVG (e.g. dashed lines).
Expand All @@ -37,6 +39,8 @@ New features and improvements:

API changes:
* `vpype.Document` and `vpype.LineCollection` have additional members to manage properties through the `vpype._MetadataMixin` mix-in class (#359)
* Added `vpype.read_svg_by_attribute()` to read SVG while sorting geometries by arbitrary attributes (#378)
* Added an argument to `vpype_cli.execute()` to pass global option such as `--verbose` (#378)

Other changes:
* Renamed the bundled config file to `vpype_config.toml` (#359)
Expand Down
21 changes: 21 additions & 0 deletions docs/cookbook.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,27 @@ This command will :ref:`cmd_read` a SVG file, add a single-line :ref:`cmd_frame`
$ vpype read input.svg frame --offset 5cm write output.svg



Preserve color (or other attributes) when reading SVG
=====================================================

By default, the :ref:`cmd_read` command sorts geometries into layers based on the input SVG's top-level groups, akin to Inkscape's layers. Stroke color is preserved *only* if it is identical for every geometries within a layer.

When preserving the color is desirable, the :ref:`cmd_read` command can sort geometries by colors instead of by top-level groups. This is achieved by using the :option:`--attr <read --attr>` option::

$ vpype read --attr stroke input.svg [...]

Here, we tell the :ref:`cmd_read` command to sort geometry by ``stroke``, which is the SVG attribute that defines the color of an element. As a result, a layer will be created for each different color encountered in the input SVG file.

The same applies for any SVG attributes, even those not explicitly supported by *vpype*. For example, ``--attr stroke-width`` will sort layers by stroke width and ``--attr stroke-dasharray`` by type of stroke dash pattern.

Multiple attributes can even be provided::

$ vpype read --attr stroke --attr stroke-width input.svg [...]

In this case, a layer will be created for each unique combination of color and stroke width.


Make a previsualisation SVG
===========================

Expand Down
24 changes: 24 additions & 0 deletions tests/data/test_svg/misc/multilayer_by_attributes.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tests/test_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Command:
Command("ellipse 0 0 2 4"),
Command(f"read '{EXAMPLE_SVG}'", preserves_metadata=False),
Command(f"read -m '{EXAMPLE_SVG}'", preserves_metadata=False),
Command(f"read -a stroke '{EXAMPLE_SVG}'", preserves_metadata=False),
Command("write -f svg -"),
Command("write -f hpgl -d hp7475a -p a4 -"),
Command("rotate 0"),
Expand Down
35 changes: 35 additions & 0 deletions tests/test_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
import difflib
import os
import re
from typing import Set

import numpy as np
import pytest

import vpype as vp
import vpype_cli
from vpype_cli import DebugData, cli

from .utils import TEST_FILE_DIRECTORY
Expand Down Expand Up @@ -367,3 +369,36 @@ def test_read_layer_name(runner):
layer 3 property vp:name: (str) my layer 3
"""
)


def test_read_by_attribute():
def _prop_set(document: vp.Document, prop: str) -> Set:
return {layer.property(prop) for layer in document.layers.values()}

file = TEST_FILE_DIRECTORY / "misc" / "multilayer_by_attributes.svg"
doc = vp.read_svg_by_attributes(str(file), ["stroke"], 0.1)
assert len(doc.layers) == 2
assert _prop_set(doc, "vp:color") == {vp.Color("#906"), vp.Color("#00f")}

doc = vp.read_svg_by_attributes(str(file), ["stroke", "stroke-width"], 0.1)
assert len(doc.layers) == 3
assert _prop_set(doc, "vp:color") == {vp.Color("#906"), vp.Color("#00f")}
assert _prop_set(doc, "vp:pen_width") == pytest.approx({1, 4})


def test_read_layer_assumes_single_layer(runner, caplog):
test_file = TEST_FILE_DIRECTORY / "misc" / "multilayer.svg"
doc = vpype_cli.execute(f"read --layer 2 '{test_file}'", global_opt="-v")

assert "assuming single-layer mode" in caplog.text
assert len(doc.layers) == 1
assert 2 in doc.layers


def test_read_single_layer_attr_warning(runner, caplog):
test_file = TEST_FILE_DIRECTORY / "misc" / "multilayer_by_attributes.svg"
doc = vpype_cli.execute(f"read -m -a stroke '{test_file}'")

assert "`--attr` is ignored in single-layer mode" in caplog.text
assert len(doc.layers) == 1
assert 1 in doc.layers
Loading

0 comments on commit 8b70225

Please sign in to comment.