From 3933dc061656cfb8801c0874d4366137bda4a4c3 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 28 Jan 2026 02:22:12 +0000 Subject: [PATCH] feat: Add RenderResult dataclass and render_content() function - Add RenderResult dataclass with content, width, height fields - Add to_svg() method for convenience when you need the full SVG - Add render_content() method to SVGRenderer class - Add render_content() convenience function at module level - Export RenderResult and render_content from __init__.py - Add comprehensive tests for the new API - Update README with documentation and examples This allows callers to compose multiple mdsvg outputs into larger SVGs without needing regex extraction to strip wrappers. Co-authored-by: davefowler --- README.md | 43 ++++++++++ src/mdsvg/__init__.py | 12 ++- src/mdsvg/renderer.py | 181 +++++++++++++++++++++++++++++++++++++++-- tests/test_renderer.py | 121 +++++++++++++++++++++++++++ 4 files changed, 349 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 167f325..9a4b659 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,49 @@ size = measure("# Hello\n\nLong paragraph...", width=400) print(f"Height needed: {size.height}px") ``` +### Structured Result (for Composing SVGs) + +When you need to embed mdsvg output in larger SVG compositions, use `render_content()` to get the SVG elements without the wrapper, along with the actual dimensions: + +```python +from mdsvg import render_content, RenderResult + +# Get structured result +result: RenderResult = render_content("# Hello World", width=400) + +result.content # SVG elements without wrapper (includes style block) +result.width # 400.0 +result.height # Actual rendered height + +# Convert to full SVG when needed +svg = result.to_svg() # Full SVG with wrapper + +# Embed in a larger SVG composition +large_svg = f""" + + + + {result.content} + + +""" +``` + +This eliminates the need for regex extraction when composing multiple mdsvg outputs: + +```python +# Compose multiple sections side by side +left = render_content("# Section 1\n\nLeft content", width=350) +right = render_content("# Section 2\n\nRight content", width=350) + +combined = f""" + + {left.content} + {right.content} + +""" +``` + ### Custom Styling ```python diff --git a/src/mdsvg/__init__.py b/src/mdsvg/__init__.py index fef6ff3..e949388 100644 --- a/src/mdsvg/__init__.py +++ b/src/mdsvg/__init__.py @@ -16,6 +16,14 @@ >>> from mdsvg import measure >>> size = measure("# Hello\n\nLong paragraph...") >>> print(f"Height: {size.height}px") + +Get structured result for composing into larger SVGs: + >>> from mdsvg import render_content, RenderResult + >>> result = render_content("# Hello", width=400) + >>> result.content # SVG elements without wrapper + >>> result.width # 400.0 + >>> result.height # Actual rendered height + >>> result.to_svg() # Full SVG with wrapper """ # Precise text measurement @@ -37,7 +45,7 @@ ) from .measure import Size, TextMetrics, estimate_text_width, measure_spans, wrap_text from .parser import MarkdownParser, parse -from .renderer import SVGRenderer, measure, render, render_blocks +from .renderer import RenderResult, SVGRenderer, measure, render, render_blocks, render_content from .style import ( COMPACT_PRESET, DARK_THEME, @@ -75,11 +83,13 @@ __all__ = [ # Main API "render", + "render_content", "render_blocks", "measure", "parse", # Classes "Style", + "RenderResult", "MarkdownParser", "SVGRenderer", # Themes diff --git a/src/mdsvg/renderer.py b/src/mdsvg/renderer.py index 7a4b5af..2c2b7a1 100644 --- a/src/mdsvg/renderer.py +++ b/src/mdsvg/renderer.py @@ -30,6 +30,54 @@ from .utils import escape_svg_text, format_number +@dataclass +class RenderResult: + """Result of rendering markdown content. + + Contains the rendered SVG content (without wrapper), along with dimensions. + This allows callers to compose multiple mdsvg outputs into a larger SVG + without needing to regex-strip wrappers. + + Attributes: + content: SVG elements without the wrapper (includes