Repository: abey79/vpype

Issue:
Allow for `layout` arg to maintain size of svg element
Currently when using the `layout` arg vpype will use the bounding box of the geometries to align it to the center and scale it to the size taking into account any margins and paper size. 

It would be cool if we could use the dimensions of the svg element to align to center and scale to the paper size. 

The usecase for this would be handling multi layered plots that are made up of separate svg files that are split based on plot layer it would make it easy to align to paper size and any margins while maintaining any empty space around the layers to keep consistent with other layers.

In the CLI my suggestion is to have a `--no-bb` flag in the `layout` arg  


Discussion:

Resolving patch:
diff --git a/vpype_cli/operations.py b/vpype_cli/operations.py
--- a/vpype_cli/operations.py
+++ b/vpype_cli/operations.py
@@ -529,6 +529,10 @@ def pagesize(document: vp.Document, size, landscape) -> vp.Document:
 geometries. If `--fit-to-margins` is used, the page size is increased to accommodate the
 margin. By construction, `--align` and `--valign` have no effect with `tight`.
 
+By default, `layout` considers the bounding box of the existing geometries as the area to
+fit on the page. Alternatively, the current page size can be used with the `--no-bbox` option.
+Using `--no-box` without a page size set is an error.
+
 On an empty pipeline, `layout` simply sets the page size to SIZE, unless `tight` is used. In
 this case, `layout` has no effect at all.
 
@@ -571,6 +575,13 @@ def pagesize(document: vp.Document, size, landscape) -> vp.Document:
     default="center",
     help="Vertical alignment",
 )
+@click.option(
+    "-b",
+    "--no-bbox",
+    is_flag=True,
+    default=False,
+    help="Use existing page size instead of the geometry bounding box",
+)
 @global_processor
 def layout(
     document: vp.Document,
@@ -579,11 +590,24 @@ def layout(
     margin: float | None,
     align: str,
     valign: str,
+    no_bbox: bool,
 ) -> vp.Document:
     """Layout command"""
 
     size = _normalize_page_size(size, landscape)
-    bounds = document.bounds()
+    if no_bbox:
+        page_size = document.page_size
+        if page_size is None:
+            raise click.BadParameter("no page size defined and --no-bbox used")
+        bounds: tuple[float, float, float, float] | None = (
+            0.0,
+            0.0,
+            page_size[0],
+            page_size[1],
+        )
+    else:
+        bounds = document.bounds()
+
     tight = size == vp.PAGE_SIZES["tight"]
 
     # handle empty geometry special cases


Test patch:
diff --git a/tests/test_commands.py b/tests/test_commands.py
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -82,6 +82,14 @@ class Command:
     Command("reverse"),
     Command("reverse --flip"),
     Command("layout a4", keeps_page_size=False),
+    Command("layout -m 3cm a4", keeps_page_size=False),
+    Command(
+        "layout --no-bbox a4",
+        keeps_page_size=False,
+        exit_code_one_layer=2,
+        exit_code_no_layer=2,
+        exit_code_two_layers=2,
+    ),
     Command("squiggles"),
     Command("text 'hello wold'"),
     Command("penwidth 0.15mm", preserves_metadata=False),
@@ -569,6 +577,12 @@ def test_layout_tight():
     assert doc.page_size == pytest.approx((4 * CM, 5 * CM))
 
 
+def test_layout_no_bbox():
+    doc = vpype_cli.execute("pagesize 10x10cm rect 0 0 1cm 1cm layout --no-bbox 30x30cm")
+    assert doc.page_size == pytest.approx((30 * CM, 30 * CM))
+    assert doc.bounds() == pytest.approx((10 * CM, 10 * CM, 11 * CM, 11 * CM))
+
+
 def test_layout_empty():
     """page size is set to size provided to layout, unless it's tight, in which case it is
     unchanged"""