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

> Learn how PNG and SVG exports handle sizing and scaling in diagram design. Discover vector vs raster exports with precise control for static capture. Optimize your diagrams.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: deep-dive
- Published: 2026-09-05

---

**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`](https://github.com/cathrynlavery/diagram-design/blob/main/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:

```python

# 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:

```xml
<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
<?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`](https://github.com/cathrynlavery/diagram-design/blob/main/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:

```python

# 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:

```bash

# 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`](https://github.com/cathrynlavery/diagram-design/blob/main/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:

```python

# 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`](https://github.com/cathrynlavery/diagram-design/blob/main/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`](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/references/export.md):

- **Gallery files** ([`assets/index.html`](https://github.com/cathrynlavery/diagram-design/blob/main/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:

```bash

# 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`](https://github.com/cathrynlavery/diagram-design/blob/main/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`](https://github.com/cathrynlavery/diagram-design/blob/main/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.