Skip to main content

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-net or --assets)
  • Images (requires --allow-net or --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:

  1. Update the version in Cargo.toml
  2. 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dynimg-0.1.14.tar.gz (617.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (9.1 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ x86-64

dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (8.7 MB view details)

Uploaded CPython 3.11+manylinux: glibc 2.17+ ARM64

dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl (7.7 MB view details)

Uploaded CPython 3.11+macOS 11.0+ ARM64

dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl (8.1 MB view details)

Uploaded CPython 3.11+macOS 10.12+ x86-64

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

Hashes for dynimg-0.1.14.tar.gz
Algorithm Hash digest
SHA256 3b40d4136d8cae8862a6c75072a8a4046bcd31bf9e86ec6dee5770313a7b1293
MD5 6c6118e3fc9b00c27d46dbb569caa68a
BLAKE2b-256 1ce787f8d2a3e7c67fe7eabab2e458f0741aa3680e02da51f0921acde1c5c927

See more details on using hashes here.

Provenance

The following attestation bundles were made for dynimg-0.1.14.tar.gz:

Publisher: release.yml on blopker/dynimg

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for dynimg-0.1.14-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 5e6dfc9e7ad98179a52b02e6844d95a08d7a7ac31cfd75a0e22159aa758ebca8
MD5 8bd5b67710f0d30561a82f7227571120
BLAKE2b-256 1fd35e4d347805bb0a491d5cf61d99a987b666b92c77ada2b0e2d95980f0f2d5

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for dynimg-0.1.14-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 9f7943711ca32a10a20965d967d4c7d5932e57da7c5ff9ed12b86b3cf6a92f42
MD5 833adb4ba7ca0de3b24cb312b77eb266
BLAKE2b-256 ad53e4ca6cc7c65ada5431460148b455d6c2f6dfd49bb30c90a5c1316ad52d88

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dynimg-0.1.14-cp311-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8b3091603650ffd1d6511dcf1d9c968f8e120b81d9023ce524a16347c5634060
MD5 da5598031760b14390e2705c6b21b9df
BLAKE2b-256 a1fffd2928637d040d4884a47477cc14c215546c160ef6a4bed4f6f0e3fd6e85

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for dynimg-0.1.14-cp311-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 b3df1468c7245267c598485000dd6ebeb9822e013d80f833c7396798c1eac72f
MD5 afe7e69a93f0c68ab92d8cd6cfb971ea
BLAKE2b-256 8bdb15a779f767516759c93f07cc772195f3daac412831e35a2a118e5ecdec79

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page