How PNG and SVG Exports Handle Sizing, Scaling, and Static Capture in Diagram Design

SVG exports preserve the original viewBox dimensions exactly as vector, while PNG exports rasterize the same diagram using Playwright with a configurable device scale factor (default 2×) for pixel-perfect control.

The cathrynlavery/diagram-design repository provides a sophisticated export pipeline that converts generated diagram HTML into production-ready SVG and PNG formats. Understanding how each format handles sizing, scaling, and static capture is essential for integrating diagrams into documentation, presentations, and web applications.

SVG Export: Vector-First Preservation

The SVG export path in skills/diagram-design/references/export.md prioritizes fidelity and accessibility over manipulation.

Extraction and Isolation

The exporter parses the source HTML and extracts the first <svg> element it encounters:


# Conceptual flow from export.md#L29-L33

svg_element = html.find_first("svg")  # Extraction point

Most diagram outputs contain a single SVG, making this approach reliable. The exporter aborts with a clear error message if no SVG is found.

Self-Contained SVG Construction

The raw SVG is transformed into a standalone document through several normalization steps:

  • xmlns declaration — Ensures xmlns="http://www.w3.org/2000/svg" is present for XML compliance
  • Original viewBox preservation — No transformation or scaling applied
  • Accessibility metadata — Retains role="img", aria-labelledby, <title>, and <desc> elements unchanged

Font Embedding for Portability

Google Fonts referenced in the original HTML are injected via @import inside a <defs><style> block:

<defs>
  <style>
    @import url('https://fonts.googleapis.com/css2?family=...&amp;display=swap');
  </style>
</defs>

Note the XML-escaped ampersands (&amp;) — required because SVGs are parsed as strict XML, not HTML.

Final Output Format

An XML declaration is prepended to guarantee well-formedness:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Standalone SVG follows -->

Sizing Behavior

The SVG's dimensions are determined exclusively by its viewBox, which is set by the Size dial defined in output-spec.md. Common presets include:

Preset viewBox
doc-inline 0 0 960 600
slide-full 0 0 1920 1080

No additional scaling occurs. The SVG displays at any size without quality loss because it remains vector-based.

PNG Export: Rasterization with Scale Control

The PNG export path uses browser-based rendering to ensure font fidelity and accurate CSS application.

Rendering Architecture

Unlike SVG export, PNG generation renders the complete HTML document (not the extracted SVG) and screenshots the SVG element's bounding box:


# From export.md#L74-L89 — Playwright rasterization

page = playwright.firefox.launch().new_page()
page.goto(f"file://{input_path}")
page.wait_for_load_state("networkidle")
svg_element = page.locator("svg").first
svg_element.screenshot(path=output_path, omit_background=True)

This approach guarantees that:

  • Fonts load via <link> tags in the HTML <head>
  • CSS animations complete before capture
  • The visual output matches browser rendering exactly

Scale Factor and Pixel Dimensions

PNG dimensions are calculated as:


width_px  = viewBox_width  × device_scale_factor
height_px = viewBox_height × device_scale_factor

The default device scale factor is 2, producing crisp output suitable for high-DPI displays. Users control this via the --scale parameter:


# Default: scale 2 (2× pixel density)

/export-diagram diagram.html --png-only

# Higher resolution for print or large displays

/export-diagram diagram.html --png-only --scale=3

# Smaller file size for web thumbnails

/export-diagram diagram.html --png-only --scale=1

Per commands/export-diagram.md, allowed values are strictly 1, 2, or 3 — the command rejects other inputs.

Transparent Background Support

The omit_background=True parameter yields PNGs with transparent canvases, eliminating the white halo effect when placing diagrams on colored backgrounds or image overlays.

Static Capture for Animated Diagrams

When diagrams include motion (query parameter ?motion=static), the export pipeline performs additional synchronization:


# From export.md#L60-L63 — Static frame guarantee

page.goto(f"file://{input_path}?motion=static")
page.wait_for_function("document.fonts.ready")
page.wait_for_selector("[data-frame='static']")

This sequence ensures:

  1. Animation is frozen at a static frame
  2. All web fonts are fully loaded and rendered
  3. The data-frame="static" attribute confirms readiness before screenshot

Format Comparison: Sizing and Scaling

Aspect SVG Export PNG Export
Size basis Original viewBox from output-spec.md presets viewBox × device_scale_factor
Scaling control None — scales infinitely as vector --scale=1|2|3 (default 2)
File type XML-based vector Raster bitmap
Background Transparent by default Transparent (omit_background=True)
Font handling Inlined via @import in <defs> Rendered by browser, fonts loaded via HTML
Accessibility Preserves aria-*, <title>, <desc> Must be added via surrounding document

Edge Cases and Validation Guardrails

The export system includes defensive checks per skills/diagram-design/references/export.md:

  • Gallery files (assets/index.html) — Export refuses when multiple SVGs are detected, requiring explicit single-diagram selection
  • Missing Playwright — Surfaces exact installation command rather than auto-installing: pip install playwright && playwright install firefox
  • Scale validation — Rejects --scale values outside 1-3 range with clear error messaging

Command Reference

Complete examples for common export scenarios:


# Both formats, default settings

/export-diagram path/to/diagram.html

# SVG only — smallest file, infinitely scalable

/export-diagram path/to/diagram.html --svg-only

# High-resolution PNG for print (scale 3)

/export-diagram path/to/diagram.html --png-only --scale=3 --output=print-ready.png

# Quick web thumbnail (scale 1)

/export-diagram path/to/diagram.html --png-only --scale=1 --output=thumb.png

Summary

  • SVG exports extract and sanitize the first <svg> element, preserving exact viewBox dimensions with embedded fonts for standalone use
  • PNG exports use Playwright to rasterize the full HTML document, applying a configurable scale factor (1-3×) to control pixel density
  • Static capture for animated diagrams uses ?motion=static with font readiness verification
  • Transparent backgrounds are default for both formats, with PNG using omit_background=True
  • Validation guardrails prevent ambiguous exports and surface clear error messages for missing dependencies

Frequently Asked Questions

Why does PNG export render the full HTML instead of the extracted SVG?

PNG export renders the complete HTML document to ensure font loading works correctly. Fonts referenced via <link> tags in the HTML <head> must be parsed and loaded by a browser engine — extracting only the SVG would break this chain. Playwright's svg.first.screenshot() then isolates just the diagram's bounding box.

Can I export a PNG at arbitrary scale factors like 1.5× or 4×?

No. The export-diagram command enforces scale values of 1, 2, or 3 only per commands/export-diagram.md. This constraint prevents unexpected file sizes and ensures predictable output quality. For non-standard dimensions, export SVG and rasterize externally.

How do I verify my animated diagram will export as a static PNG correctly?

Include ?motion=static in your workflow. The export pipeline automatically appends this query, waits for document.fonts.ready, and verifies data-frame="static" exists before capturing. You can test this locally by adding the parameter to your diagram URL and confirming motion freezes.

What happens if my diagram HTML contains multiple SVG elements?

The export command rejects gallery files (assets/index.html) and exits with an error requesting a single-diagram file. This prevents ambiguity about which diagram to export. Split multi-diagram pages into individual HTML files before export.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →