How to Convert SVG Embedded in HTML to PNG
Learn when to use a browser or CairoSVG, with runnable Python, Node.js, cURL, sizing, foreignObject, assets, troubleshooting, and automation tips.
To convert an SVG embedded in HTML to PNG, first identify what you need to preserve:
- The live HTML page: use a browser renderer and capture the page or SVG element at the required viewport and pixel size.
- A self-contained SVG drawing: extract the SVG and use a converter such as CairoSVG.
- HTML inside
<foreignObject>: prefer a browser because the result depends on HTML layout, CSS, fonts, and browser support.
An inline SVG can inherit CSS from its document. A standalone SVG has a different rendering context, and HTML embedded through foreignObject is a separate case. Browser rendering is the safest general workflow when the PNG must match the appearance of the live page. MDN’s SVG documentation describes these rendering contexts; the SVG specification defines the foreignObject positioning rectangle.
Choose the right conversion path
| Input | Recommended method | Why |
|---|---|---|
Inline <svg> styled by page CSS |
Browser screenshot or element export | Preserves document layout, inherited styles, and loaded fonts. |
| SVG with JavaScript-generated paths or dimensions | Browser screenshot | Runs the page’s JavaScript before capture. |
SVG containing <foreignObject> |
Browser screenshot | HTML layout and CSS support affect the result. |
Standalone vector-only .svg |
CairoSVG CLI or Python | Simple, scriptable conversion with width, height, and DPI controls. |
| Page with external images, fonts, or stylesheets | Browser, or a converter configured for those resources | Every dependency must be available and permitted at render time. |
CairoSVG is a standalone SVG converter, not a general HTML-page renderer. Its documentation notes that it has no real DOM, which limits JavaScript-dependent content. See the CairoSVG documentation for current installation and API details.
Browser method: render the HTML and save the SVG as PNG
Use this method when the requested PNG should look like the page a visitor sees. The example below uses Playwright with Chromium and captures one SVG element. It waits for fonts and images, sets a deterministic viewport, and writes a PNG.
Install Playwright
python -m pip install playwright
python -m playwright install chromium
Python with Playwright
from pathlib import Path
from playwright.sync_api import sync_playwright
URL = "https://example.com/chart.html"
SELECTOR = "svg#sales-chart"
OUTPUT = "chart.png"
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(
viewport={"width": 1400, "height": 900},
device_scale_factor=2,
)
page.goto(URL, wait_until="networkidle", timeout=90_000)
page.evaluate("document.fonts.ready")
page.locator(SELECTOR).screenshot(path=OUTPUT, animations="disabled")
browser.close()
print(f"Wrote {Path(OUTPUT).resolve()}")
Replace SELECTOR with the SVG’s CSS selector. For a full-page PNG, replace the locator call with page.screenshot(path="page.png", full_page=True). If the chart is rendered after a fetch or animation, wait for a reliable selector or application state rather than relying only on a fixed delay.
Node.js with Playwright
import { chromium } from "playwright";
const browser = await chromium.launch();
const page = await browser.newPage({
viewport: { width: 1400, height: 900 },
deviceScaleFactor: 2,
});
await page.goto("https://example.com/chart.html", {
waitUntil: "networkidle",
timeout: 90_000,
});
await page.evaluate(() => document.fonts.ready);
await page.locator("svg#sales-chart").screenshot({
path: "chart.png",
animations: "disabled",
});
await browser.close();
Install with npm install playwright and then run npx playwright install chromium.
Browser screenshot checklist
- Set the viewport width and height explicitly.
- Set
device_scale_factor(ordeviceScaleFactor) when you need a retina-sized PNG. - Wait for network requests, fonts, and application data to finish.
- Capture the SVG element for a transparent, tightly cropped result, or the page for surrounding HTML.
- Disable animations or wait for a known animation frame.
- Verify the output dimensions and background color.
Standalone method: convert an SVG file with CairoSVG
Use CairoSVG when the input is genuinely a standalone SVG and does not rely on the surrounding HTML DOM or JavaScript.
Command line
cairosvg input.svg -o output.png
Depending on your installation, the executable may be available as cairosvg or through the package’s documented command-line entry point. Consult the current CairoSVG installation instructions.
Python
import cairosvg
cairosvg.svg2png(
url="input.svg",
write_to="output.png",
output_width=1600,
output_height=900,
dpi=144,
)
CairoSVG also accepts SVG bytes through bytestring=... and can read a URL or file object. Set either output dimensions or DPI when the SVG’s intrinsic size is not the size you need. Preserve the SVG’s viewBox when possible so scaling remains proportional.
Extract an inline SVG before conversion
If the HTML contains a simple inline SVG that does not depend on page CSS, copy the complete <svg>...</svg> element into a file. Include its xmlns, width, height, and viewBox attributes. Inline styles and embedded <style> rules should travel with it. Page-level selectors, inherited variables, external fonts, and JavaScript will not automatically travel with the extracted file.
Dimensions, transparency, and scaling
viewBox: defines the SVG’s internal coordinate system. Keep it when exporting at a new size.widthandheight: provide the intrinsic display size. Missing values can produce an unexpected viewport.- Browser pixel size: CSS pixels multiplied by the device scale factor determine the PNG’s pixel dimensions.
- CairoSVG output size: use its documented width, height, or DPI options; do not silently stretch one axis.
- Transparency: an SVG can have a transparent background. A browser page may appear white because of page CSS, while an element-only capture can retain transparency depending on the capture tool.
- Aspect ratio: preserve the ratio unless intentional cropping or stretching is required. Check
preserveAspectRatiowhen content appears shifted or clipped.
Assets and foreignObject edge cases
External images and fonts
Remote images, web fonts, CSS files, and icon sprites must be reachable by the renderer. A browser can load them in page context, but canvas-based export can be restricted by origin security rules. Wait for fonts with document.fonts.ready, and confirm that images have completed loading before capture. For deterministic builds, host required assets where the renderer can access them and pin the font files and CSS versions.
HTML inside foreignObject
foreignObject places an HTML fragment inside a rectangle in the SVG. Give that element explicit x, y, width, and height values, and ensure the nested HTML has the CSS it needs. Different converters may not implement this exactly like a browser. If the HTML fragment matters, capture it in Chromium or another target browser and inspect the PNG.
Canvas export and security restrictions
If you draw an SVG into a canvas and call toDataURL() or toBlob(), cross-origin resources can taint the canvas. Use same-origin assets or the correct CORS response headers, and verify the page’s security policy. A browser screenshot avoids some canvas-specific steps but still requires assets to load successfully.
Or skip the browser setup
ScreenshotNeo provides a GET endpoint that renders a URL and returns PNG, JPEG, WebP, or PDF. Use it when your SVG is already available on a page and you want a hosted browser capture.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/chart.html -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/chart.html"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com/chart.html' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', data));
See the ScreenshotNeo API documentation for the available options. Before capture it accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots.
Create a free ScreenshotNeo account and start with 1,000 screenshots each month at no charge.
Relevant capture options
For an HTML page containing SVG, choose options based on the failure you are trying to prevent:
| Need | Useful option |
|---|---|
| SVG is below the fold or uses lazy-loaded assets | Full-page capture with lazy images loaded. |
| Only the drawing should be exported | Capture one element by CSS selector. |
| Dark-themed chart | Dark mode or a custom color scheme. |
| Exact device appearance | A device preset, custom viewport, and retina scale. |
| Page has overlays | Hide selectors, consent handling, popup removal, or custom CSS. |
| Chart appears after data fetch | Wait for a selector, delay, or network idle. |
| Third-party assets interfere | Block ads, trackers, requests, or resource types. |
| Private page | Custom headers, cookies, user agent, or Authorization. |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| PNG is blank | Capture occurred before SVG or data loaded. | Wait for a visible SVG, a chart-specific selector, network idle, or the application’s ready state. |
| SVG is clipped | Missing or incorrect viewport, width, height, or viewBox. |
Set explicit dimensions and preserve the aspect ratio; capture the element’s bounding box. |
| Text uses the wrong font | Web font had not loaded or was inaccessible. | Wait for document.fonts.ready, check font requests, and provide a fallback. |
foreignObject content disappears |
Converter lacks equivalent HTML support or the rectangle is zero-sized. | Use a browser renderer, set explicit dimensions, and inline the required CSS. |
| Images are missing | Bad URL, blocked request, authentication, or CORS restriction. | Inspect network failures, provide credentials where appropriate, and use accessible same-origin or CORS-enabled assets. |
| Colors differ | Different color profile, dark mode, inherited CSS, or transparent background. | Set the color scheme and background explicitly and compare computed styles. |
| Animation is captured mid-frame | Capture happened while transitions were running. | Disable animations with injected CSS or wait for a stable state. |
| CairoSVG fails on JavaScript | It does not provide a real browser DOM. | Render the HTML in a browser first, then capture the result. |
| Request times out | Slow scripts, blocked resources, or a page that never reaches idle. | Use a targeted readiness selector, block unnecessary requests, and set a bounded timeout with retry logic. |
Performance, reliability, and cost
- Reuse browsers: keep one Playwright browser process and create isolated pages or contexts for batches.
- Capture the smallest target: an SVG element is faster and produces smaller files than a full page.
- Control waiting: network idle can be slow on pages with analytics; a specific ready selector is usually more predictable.
- Reduce dependencies: block trackers and unnecessary media, or inline critical SVG styles and assets.
- Retry safely: retry navigation and transient network failures with a limit; do not repeatedly submit a page that is consistently invalid.
- Validate output: check HTTP status, PNG signature, dimensions, alpha channel, and expected content before storing the file.
- Cache stable pages: cache by URL, viewport, device scale, and relevant page version. Invalidate when the SVG or its assets change.
- Hosted capture billing: ScreenshotNeo bills only clean shots. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; inspect
X-Page-VerdictandX-Billedwhen accounting for usage.
Validation checklist
- Does the PNG have the intended width and height?
- Is the background transparent or solid as required?
- Are all paths, text, markers, filters, gradients, and masks present?
- Are external images and fonts loaded?
- Does
foreignObjectcontent match the browser output? - Was the capture taken after data and animations settled?
- Can the conversion be repeated with the same viewport, assets, and renderer version?
FAQ
Can I convert an entire HTML file with CairoSVG?
Not as a general browser-page conversion. CairoSVG converts SVG input and does not provide a real DOM or JavaScript runtime. Use a browser when HTML layout or scripts determine the appearance.
Should I capture the SVG element or the whole page?
Capture the element for a tightly cropped graphic. Capture the page when surrounding HTML, layout, or background is part of the required image.
Why does the same SVG look different in two tools?
Rendering context, inherited CSS, fonts, external assets, foreignObject support, viewport dimensions, and device scale can all change the result.
What format should I use before PNG?
Keep the source as SVG until the final export. This preserves vector detail while you adjust dimensions, then PNG provides a broadly supported raster output.
Can an API capture a private SVG page?
Yes, when the capture service supports the required headers, cookies, user agent, or authorization. Configure access carefully and verify that private assets load in the render context.


