A fast library for rendering HTML/CSS to images
Project description
dynimg
A fast library and CLI for rendering HTML/CSS to images. Use from Python, Rust, or the command line. Built on Blitz, a modular Rust rendering engine.
Perfect for generating dynamic images like Open Graph (OG) images, social media cards, email headers, and more.
Features
- Python + Rust + CLI: Use from Python, as a Rust library, or command-line tool
- Multiple output formats: PNG, WebP (lossless), and JPEG
- High-quality rendering: Configurable scale factor for retina displays
- Fast: Native Rust performance with no browser overhead
- Secure by default: Network and filesystem access disabled unless explicitly enabled
Installation
Python
pip install dynimg
Rust CLI
cargo install dynimg
Rust Library
[dependencies]
dynimg = "0.1"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
Library Usage
use dynimg::{render, RenderOptions};
#[tokio::main]
async fn main() -> Result<(), dynimg::Error> {
let html = r#"
<html>
<body style="background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; justify-content: center; align-items: center;
height: 630px; margin: 0;">
<h1 style="color: white; font-family: system-ui; font-size: 64px;">
Hello World
</h1>
</body>
</html>
"#;
// Render with default options (1200×auto viewport, 2x scale)
let image = render(html, RenderOptions::default()).await?;
// Save as PNG
image.save_png("output.png")?;
// Or get raw bytes
let png_bytes = image.to_png()?;
let webp_bytes = image.to_webp();
let jpeg_bytes = image.to_jpeg(90)?;
Ok(())
}
Configuration
use dynimg::RenderOptions;
// Using builder pattern
let options = RenderOptions::default()
.width(1200)
.height(630)
.scale(2.0)
.allow_net()
.assets_dir("./assets");
// Or struct initialization
let options = RenderOptions {
width: 1200,
height: Some(630),
scale: 2.0,
allow_net: true,
assets_dir: Some("./assets".into()),
base_url: None,
};
Convenience function
use dynimg::{render_to_file, RenderOptions};
// Render directly to a file (format detected from extension)
render_to_file(html, "output.png", RenderOptions::default(), 90).await?;
CLI Usage
Basic Usage
Render an HTML file to PNG:
dynimg input.html -o output.png
Output Formats
# PNG (lossless)
dynimg input.html -o image.png
# WebP (lossless)
dynimg input.html -o image.webp
# JPEG (lossy)
dynimg input.html -o image.jpg --quality 90
Image Dimensions
The --width and --height options set the viewport size (CSS layout dimensions). The actual output image is scaled by the --scale factor (default: 2x for high-DPI/retina displays).
Output image size = viewport × scale
# Default: 1200px viewport → 2400px output (at 2x scale)
dynimg input.html -o output.png
# OG image: 1200×630 viewport → 2400×1260 output
dynimg input.html -o output.png --width 1200 --height 630
# 1x scale for exact pixel dimensions (1200×630 output)
dynimg input.html -o output.png --width 1200 --height 630 --scale 1
# 3x scale for extra-high-DPI (3600×1890 output)
dynimg input.html -o output.png --width 1200 --height 630 --scale 3
Your HTML/CSS should use the viewport dimensions (e.g., width: 1200px) - the scale factor handles the high-resolution rendering.
Reading from stdin
echo '<html><body><h1>Hello</h1></body></html>' | dynimg - -o output.png
Loading External Resources
By default, network and filesystem access are disabled for security. Enable them to load images, fonts, and other resources:
# Load images/fonts from URLs
dynimg input.html -o output.png --allow-net
# Load images/fonts from a local assets directory
dynimg input.html -o output.png --assets ./assets
# Allow both
dynimg input.html -o output.png --allow-net --assets ./assets
When using --assets, all local paths are resolved relative to the asset directory. Attempts to load files outside this directory will error:
<!-- With --assets ./assets -->
<img src="logo.png"> <!-- loads ./assets/logo.png -->
<img src="img/hero.png"> <!-- loads ./assets/img/hero.png -->
<img src="../secret.png"> <!-- ERROR: outside assets directory -->
For self-contained templates, consider using inline base64 data URIs instead:
<img src="data:image/png;base64,iVBORw0KGgo...">
CLI Reference
dynimg [OPTIONS] <INPUT> -o <OUTPUT>
Arguments:
<INPUT> HTML file path or '-' for stdin
Options:
-o, --output <FILE> Output image path (format detected from extension)
-w, --width <PIXELS> Viewport width in CSS pixels [default: 1200]
-H, --height <PIXELS> Viewport height in CSS pixels [default: document height]
-s, --scale <FACTOR> Scale multiplier for output (2 = 2x resolution) [default: 2]
-q, --quality <1-100> JPEG quality [default: 90]
--allow-net Allow network access for loading remote resources
--assets <DIR> Asset directory for local resources
-v, --verbose Enable verbose logging
--help Print help
--version Print version
Options can also be set via HTML meta tags (see below). CLI flags override meta tags.
Note: Output image dimensions = viewport × scale. A 1200×630 viewport at 2x scale produces a 2400×1260 image.
Python Usage
import dynimg
html = """
<html>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
<body style="background: linear-gradient(135deg, #667eea, #764ba2);
display: flex; justify-content: center; align-items: center;
height: 630px; margin: 0;">
<h1 style="color: white; font-family: system-ui; font-size: 64px;">
Hello World
</h1>
</body>
</html>
"""
# Render with default options
image = dynimg.render(html)
# Save to file
image.save("output.png")
# Or get bytes
png_bytes = image.to_png()
webp_bytes = image.to_webp()
jpeg_bytes = image.to_jpeg(quality=90)
Configuration
import dynimg
options = dynimg.RenderOptions(
width=1200, # Viewport width (default: 1200)
height=630, # Viewport height (default: auto)
scale=2.0, # Output scale factor (default: 2.0)
allow_net=True, # Allow network requests (default: False)
assets_dir="./assets", # Local assets directory (default: None)
base_url="https://example.com", # Base URL for relative URLs (default: None)
)
image = dynimg.render(html, options)
Direct File Output
# Render directly to a file (format detected from extension)
dynimg.render_to_file(html, "output.png")
# With options
dynimg.render_to_file(html, "output.png", options=options)
# JPEG with quality setting
dynimg.render_to_file(html, "output.jpg", quality=90)
Image Properties
image = dynimg.render(html)
print(f"Size: {image.width}x{image.height}")
print(f"Bytes: {len(image.data)}")
HTML Meta Tags
You can configure rendering options directly in your HTML using meta tags. CLI flags take precedence over meta tags.
<meta name="dynimg:width" content="1200"> <!-- viewport width -->
<meta name="dynimg:height" content="630"> <!-- viewport height -->
<meta name="dynimg:scale" content="2"> <!-- output multiplier -->
<meta name="dynimg:quality" content="90"> <!-- JPEG quality -->
This is useful for templates that should always render at specific dimensions. Remember: the output image size is viewport × scale.
Example HTML Template
<!DOCTYPE html>
<html>
<head>
<meta name="dynimg:width" content="1200">
<meta name="dynimg:height" content="630">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 1200px;
height: 630px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: system-ui, sans-serif;
}
h1 {
color: white;
font-size: 64px;
margin: 0;
}
p {
color: rgba(255,255,255,0.8);
font-size: 32px;
}
</style>
</head>
<body>
<div class="container">
<h1>Hello World</h1>
<p>Welcome to my site</p>
</div>
</body>
</html>
Supported CSS Features
dynimg uses Blitz for rendering, which supports:
- Flexbox and Grid layouts
- CSS variables
- Media queries
- Complex selectors
- Gradients and shadows
- Web fonts (via
@font-face, requires--allow-netor--assets) - Images (requires
--allow-netor--assets, or use data URIs)
Performance
dynimg is designed for speed:
- No browser startup overhead
- Native Rust rendering pipeline
- Efficient image encoding
Typical rendering time: 50-200ms depending on complexity.
Development
Building
# Build CLI
cargo build --release
# Build Python wheel
pip install maturin
maturin build --release --features python
# Install locally for development
maturin develop --features python
Running Tests
cargo test
cargo clippy -- -D warnings
cargo fmt -- --check
Releasing
Releases are automated via GitHub Actions. To create a new release:
- Update the version in
Cargo.toml - Create and push a git tag:
git tag v0.1.0
git push origin v0.1.0
This triggers the release workflow which:
- Builds wheels for Linux (x86_64, aarch64) and macOS (x86_64, aarch64)
- Creates a GitHub Release with all artifacts
- (Optional) Publishes to PyPI (when enabled)
License
MIT
AI Warning
This is AI slop, if you want to use it, fork and make it your own!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dynimg-0.1.14.tar.gz.
File metadata
- Download URL: dynimg-0.1.14.tar.gz
- Upload date:
- Size: 617.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b40d4136d8cae8862a6c75072a8a4046bcd31bf9e86ec6dee5770313a7b1293
|
|
| MD5 |
6c6118e3fc9b00c27d46dbb569caa68a
|
|
| BLAKE2b-256 |
1ce787f8d2a3e7c67fe7eabab2e458f0741aa3680e02da51f0921acde1c5c927
|
Provenance
The following attestation bundles were made for dynimg-0.1.14.tar.gz:
Publisher:
release.yml on blopker/dynimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dynimg-0.1.14.tar.gz -
Subject digest:
3b40d4136d8cae8862a6c75072a8a4046bcd31bf9e86ec6dee5770313a7b1293 - Sigstore transparency entry: 912148035
- Sigstore integration time:
-
Permalink:
blopker/dynimg@682c932c19be16fa91c8a8dc53e535b080d66246 -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/blopker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@682c932c19be16fa91c8a8dc53e535b080d66246 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 9.1 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5e6dfc9e7ad98179a52b02e6844d95a08d7a7ac31cfd75a0e22159aa758ebca8
|
|
| MD5 |
8bd5b67710f0d30561a82f7227571120
|
|
| BLAKE2b-256 |
1fd35e4d347805bb0a491d5cf61d99a987b666b92c77ada2b0e2d95980f0f2d5
|
Provenance
The following attestation bundles were made for dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on blopker/dynimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
5e6dfc9e7ad98179a52b02e6844d95a08d7a7ac31cfd75a0e22159aa758ebca8 - Sigstore transparency entry: 912148289
- Sigstore integration time:
-
Permalink:
blopker/dynimg@682c932c19be16fa91c8a8dc53e535b080d66246 -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/blopker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@682c932c19be16fa91c8a8dc53e535b080d66246 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 8.7 MB
- Tags: CPython 3.11+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f7943711ca32a10a20965d967d4c7d5932e57da7c5ff9ed12b86b3cf6a92f42
|
|
| MD5 |
833adb4ba7ca0de3b24cb312b77eb266
|
|
| BLAKE2b-256 |
ad53e4ca6cc7c65ada5431460148b455d6c2f6dfd49bb30c90a5c1316ad52d88
|
Provenance
The following attestation bundles were made for dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on blopker/dynimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
9f7943711ca32a10a20965d967d4c7d5932e57da7c5ff9ed12b86b3cf6a92f42 - Sigstore transparency entry: 912148150
- Sigstore integration time:
-
Permalink:
blopker/dynimg@682c932c19be16fa91c8a8dc53e535b080d66246 -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/blopker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@682c932c19be16fa91c8a8dc53e535b080d66246 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 7.7 MB
- Tags: CPython 3.11+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b3091603650ffd1d6511dcf1d9c968f8e120b81d9023ce524a16347c5634060
|
|
| MD5 |
da5598031760b14390e2705c6b21b9df
|
|
| BLAKE2b-256 |
a1fffd2928637d040d4884a47477cc14c215546c160ef6a4bed4f6f0e3fd6e85
|
Provenance
The following attestation bundles were made for dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on blopker/dynimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl -
Subject digest:
8b3091603650ffd1d6511dcf1d9c968f8e120b81d9023ce524a16347c5634060 - Sigstore transparency entry: 912148218
- Sigstore integration time:
-
Permalink:
blopker/dynimg@682c932c19be16fa91c8a8dc53e535b080d66246 -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/blopker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@682c932c19be16fa91c8a8dc53e535b080d66246 -
Trigger Event:
push
-
Statement type:
File details
Details for the file dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 8.1 MB
- Tags: CPython 3.11+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3df1468c7245267c598485000dd6ebeb9822e013d80f833c7396798c1eac72f
|
|
| MD5 |
afe7e69a93f0c68ab92d8cd6cfb971ea
|
|
| BLAKE2b-256 |
8bdb15a779f767516759c93f07cc772195f3daac412831e35a2a118e5ecdec79
|
Provenance
The following attestation bundles were made for dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl:
Publisher:
release.yml on blopker/dynimg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl -
Subject digest:
b3df1468c7245267c598485000dd6ebeb9822e013d80f833c7396798c1eac72f - Sigstore transparency entry: 912148357
- Sigstore integration time:
-
Permalink:
blopker/dynimg@682c932c19be16fa91c8a8dc53e535b080d66246 -
Branch / Tag:
refs/tags/v0.1.14 - Owner: https://github.com/blopker
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@682c932c19be16fa91c8a8dc53e535b080d66246 -
Trigger Event:
push
-
Statement type: