Skip to main content

takumi-py

takumi-py provides Python 3.10+ bindings for the Takumi Rust renderer.

[!IMPORTANT] takumi-py is currently in a testing stage. APIs, wheel build targets, release automation, and exception types may still change while the Takumi core binding surface is completed; do not treat it as a stable production dependency yet.

The binding focuses on exposing practical Takumi core capabilities instead of copying the WASM/JS convenience layer. It currently supports:

  • Node Tree, HTML string, and Jinja template rendering into image bytes.
  • RenderOptions, including auto viewport, DPR, debug border, dithering, and time_ms.
  • Custom fonts, per-render image resources, font fallback families, language hints, and SVG output.
  • Rust-backed HTML parsing with configurable presets, Tailwind attribute mapping, and depth limits.
  • Layout measurement with a typed measured node tree result.
  • CSS and structured keyframe animation time sampling, sequence animation, and WebP/APNG/GIF animated encoders.
  • PEP 561 typing, with _core.pyi covering the public native binding surface.

It intentionally does not include Playwright fallback, remote fetch, abort signal support, data URL convenience APIs, a Node.js sidecar, or Takumi internal layout/cache/glyph types.

Development

uv sync --all-groups --all-extras
uv run maturin develop
make check

make check checks Ruff formatting and linting, ty, pytest with coverage, and the Rust formatting/build checks.

Install From Source

uv sync --all-groups --all-extras
uv run maturin develop

Node Tree

from pathlib import Path

from takumi_py import Renderer

renderer = Renderer()

png = renderer.render_node(
    {"type": "text", "text": "Hello from Python"},
    stylesheets=["span { font-size: 72px; color: black; }"],
    width=1200,
    height=630,
)

Path("out.png").write_bytes(png)

Render Options

from takumi_py import RenderOptions, Renderer

raw = Renderer().render_node(
    {
        "type": "container",
        "style": {
            "width": "240px",
            "height": "120px",
            "backgroundColor": "white",
        },
    },
    options=RenderOptions(
        width=None,
        height=None,
        format="raw",
        device_pixel_ratio=2.0,
        dithering="ordered-bayer",
    ),
)

HTML

from takumi_py import Renderer

html = """
<div class="card">
  <h1>Hello</h1>
</div>
"""

stylesheets = ["""
.card {
  width: 1200px;
  height: 630px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: white;
  background: #111827;
}
"""]

png = Renderer().render_html(
    html,
    stylesheets=stylesheets,
    width=1200,
    height=630,
)

HTML parsing is performed by Takumi's Rust parser. Use HtmlOptions when you need to disable Chromium presets, read Tailwind classes from a custom attribute, or cap parse depth:

from takumi_py import HtmlOptions, Renderer

png = Renderer().render_html(
    '<div class="w-[1200px] h-[630px]"></div>',
    html_options=HtmlOptions(
        presets="none",
        tailwind_property="class",
        max_depth=64,
    ),
    width=None,
    height=None,
)

Compiled nodes expose Takumi's image URL discovery API:

compiled = Renderer().compile_node(
    {"type": "image", "src": "https://example.com/logo.png"}
)

print(compiled.resource_urls())

resource_urls() follows Takumi's native image URL discovery semantics and reports HTTP(S) image references from image nodes and styles. It does not fetch those resources and does not report already-provided memory:// resources or byte buffers.

Measure

from takumi_py import Renderer

measured = Renderer().measure_node(
    {
        "type": "container",
        "style": {"width": "240px", "height": "120px"},
        "children": [{"type": "text", "text": "Hello"}],
    },
    width=240,
    height=120,
)

print(measured.width, measured.height)

Resources

from pathlib import Path

from takumi_py import FontResource, ImageResource, Renderer

renderer = Renderer(load_default_fonts=False)
families = renderer.register_font(
    FontResource(
        Path("Inter-Regular.woff2").read_bytes(),
        name="Inter",
        weight=400,
        style="normal",
        generic_family="sans-serif",
    )
)

png = renderer.render_node(
    {"type": "image", "src": "memory://logo", "width": 128, "height": 128},
    width=128,
    height=128,
    images=[
        ImageResource(
            "memory://logo",
            Path("logo.svg").read_bytes(),
            cache="none",
        )
    ],
    font_families=families,
    lang="en",
)

fetched_resources, load_font, load_fonts, persistent_images, put_persistent_image, and clear_image_store remain available as deprecated compatibility shims for the v0.2 line. New code should pass images per render and use register_font / register_fonts.

register_font returns the family names registered by Takumi. Pass that list as font_families when you want a render call to use those families as its fallback stack. lang accepts a BCP-47 language tag and is forwarded to Takumi's locale-aware text shaping and line-breaking.

The render-level lang option is not injected as a node attribute, so it does not make CSS :lang() selectors match. Takumi's selector matcher follows the HTML language-determination model and walks actual node metadata or HTML attributes. If CSS needs :lang(...), set lang on the HTML element or node that should establish the language:

renderer.render_html(
    '<section lang="zh-Hant"><div class="headline">你好</div></section>',
    stylesheets=[
        '.headline:lang(zh-Hant) { font-family: "Noto Sans TC"; }',
    ],
)

renderer.render_node(
    {
        "type": "container",
        "lang": "ja",
        "children": [{"type": "text", "text": "こんにちは"}],
    },
    stylesheets=[':lang(ja) { font-family: "Noto Sans JP"; }'],
)

ImageResource.cache accepts "auto" or "none" and is forwarded to Takumi's native image cache. Tuple resources like ("memory://logo", data) remain accepted and default to "auto".

FontResource accepts Takumi v2 descriptor fields:

FontResource(
    font_bytes,
    name="Inter",
    weight=700,
    style="italic",
    subset_of="Brand Sans",
    generic_family="sans-serif",
)

style uses CSS font-style syntax such as "normal", "italic", or "oblique 12deg". Invalid style values raise FontError during registration.

SVG

from takumi_py import Renderer

svg = Renderer().render_svg_html(
    """
    <div class="card">Hello</div>
    """,
    stylesheets=[".card { width: 1200px; height: 630px; color: black; }"],
    width=1200,
    height=630,
)

Animation

from takumi_py import AnimationScene, RenderOptions, Renderer

renderer = Renderer()

frame = renderer.render_html(
    """
    <div class="box"></div>
    """,
    stylesheets=["""
    @keyframes fade {
      from { opacity: 0; }
      to { opacity: 1; }
    }
    .box {
      width: 64px;
      height: 64px;
      background: black;
      animation: fade 1000ms both;
    }
    """],
    width=64,
    height=64,
    time_ms=500,
)

structured_frame = renderer.render_node(
    {
        "type": "container",
        "className": "box",
    },
    stylesheets=[
        ".box { width: 64px; height: 64px; animation: fade 1000ms both; }"
    ],
    keyframes={
        "fade": {
            "from": {"opacity": 0},
            "to": {"opacity": 1},
        }
    },
    width=64,
    height=64,
    time_ms=500,
)

options_frame = renderer.render_node(
    {"type": "container", "className": "box"},
    stylesheets=[
        ".box { width: 64px; height: 64px; animation: fade 1000ms both; }"
    ],
    options=RenderOptions(
        width=64,
        height=64,
        time_ms=500,
        keyframes={
            "fade": {
                "from": {"opacity": 0},
                "to": {"opacity": 1},
            }
        },
    ),
)

webp = renderer.render_animation(
    [
        AnimationScene(
            {
                "type": "container",
                "style": {
                    "width": "100%",
                    "height": "100%",
                    "backgroundColor": "black",
                },
            },
            duration_ms=100,
        ),
        AnimationScene(
            {
                "type": "container",
                "style": {
                    "width": "100%",
                    "height": "100%",
                    "backgroundColor": "white",
                },
            },
            duration_ms=100,
        ),
    ],
    width=64,
    height=64,
    fps=20,
)

Takumi v2 Migration

takumi-py now targets Takumi v2. The main resource model changed from a renderer-level global context to explicit per-render resources:

  • Use images=[ImageResource(...)] instead of fetched_resources.
  • Use register_font / register_fonts instead of load_font / load_fonts.
  • Pass font_families and lang on render calls when you need deterministic font fallback or locale-aware shaping.
  • Use HTML or node lang attributes, not the render-level lang option, when CSS selectors depend on :lang(...).
  • ImageResource.cache is forwarded to the native image cache for per-render, constructor, and deprecated persistent-image resources.
  • FontResource accepts Takumi v2 descriptor fields: name, weight, style, subset_of, and generic_family.
  • The built-in fallback font follows Takumi v2: a Latin Geist subset marked as last resort, so caller-registered fonts win ordinary fallback selection.
  • HtmlOptions exposes Takumi's Rust from_html parser options.
  • CompiledNode.resource_urls() wraps Takumi's image URL discovery and reports HTTP(S) image/style references for callers that want to prepare resources before rendering.
  • Pass keyframes=... or RenderOptions(keyframes=...) to use Takumi's structured keyframe input without embedding @keyframes CSS text.
  • WebP defaults to lossless when neither quality nor lossless is specified. Passing both quality and lossless=True is rejected.
  • SVG output is available through render_svg_node, render_svg_html, render_svg_template, and render_svg_compiled.
  • HTML parsing is handled by Takumi's Rust parser. Inline style attributes are parsed with the HTML payload; pass document-level CSS explicitly through stylesheets=[...] on HTML render, measure, SVG, or template calls.

Takumi v2 also changes several CSS defaults to be closer to the Web platform. If an old image shifts, check for implicit defaults such as position, border/outline width, transform-origin, object-position, and SVG currentColor inheritance before treating it as a binding regression.

Jinja

from takumi_py import TemplateRenderer

renderer = TemplateRenderer("examples/templates")
stylesheets = ["""
.card {
  width: 1200px;
  height: 630px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 64px;
  background: #111827;
  color: white;
}
"""]

png = renderer.render(
    "card.html.jinja",
    {
        "title": "takumi-py",
        "subtitle": "HTML / Jinja to image",
    },
    stylesheets=stylesheets,
    width=1200,
    height=630,
)

Release

Releases are handled by GitHub Actions. After the release commit is on main, tag that commit with v plus the project.version value from pyproject.toml, then push the tag to build wheels/sdist, publish to PyPI, and create a GitHub Release.

git tag v0.2.0 <commit-on-main>
git push origin main v0.2.0

The tag must match project.version in pyproject.toml; for example, version 0.2.0 must be released as v0.2.0.

Before publishing, the workflow generates GitHub build provenance attestations for every wheel and source distribution. The PyPI publication is recorded as a GitHub Deployment and links to the released version on PyPI.

Test Coverage

The Python test suite covers static rendering, the HTML adapter, templates, core fixtures, options, measurement, resources, animation, typing artifacts, and generated HTML fixtures from the upstream Takumi core test suite.

License

takumi-py is licensed under GPL-3.0-or-later. See LICENSE.

This repository includes takumi as a git submodule. takumi is licensed separately under MIT OR Apache-2.0; see THIRD_PARTY_NOTICES.md.

Download files

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

Source Distribution

takumi_py-0.2.0.tar.gz (8.3 MB view details)

Uploaded Source

Built Distributions

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

takumi_py-0.2.0-cp310-abi3-win_amd64.whl (4.3 MB view details)

Uploaded CPython 3.10+Windows x86-64

takumi_py-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (4.9 MB view details)

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

takumi_py-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (4.9 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

takumi_py-0.2.0-cp310-abi3-macosx_11_0_arm64.whl (4.4 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

File details

Details for the file takumi_py-0.2.0.tar.gz.

File metadata

  • Download URL: takumi_py-0.2.0.tar.gz
  • Upload date:
  • Size: 8.3 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for takumi_py-0.2.0.tar.gz
Algorithm Hash digest
SHA256 096a98e5f200a28b58c83759c623705a7c715a4bcc102be9aaeb6d24fbe073bd
MD5 e3d38ddb6f42527d185350ff412034c3
BLAKE2b-256 59fc85c77ff016617eb3f3a2093a89288374bc8310d82c7162c61290ef789fc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for takumi_py-0.2.0.tar.gz:

Publisher: publish.yml on BalconyJH/takumi-py

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

File details

Details for the file takumi_py-0.2.0-cp310-abi3-win_amd64.whl.

File metadata

  • Download URL: takumi_py-0.2.0-cp310-abi3-win_amd64.whl
  • Upload date:
  • Size: 4.3 MB
  • Tags: CPython 3.10+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for takumi_py-0.2.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 9d1340965f8dd56a4e4b25bfda1f4b12282cd13d252b521bd5e77a2e91623ea8
MD5 aa493dcb23f5e52d23a55b85fc0c74a5
BLAKE2b-256 e4e2e73da347dc3f33b4918e4fbe921dd944cd32100af18c1c93281999b61a37

See more details on using hashes here.

Provenance

The following attestation bundles were made for takumi_py-0.2.0-cp310-abi3-win_amd64.whl:

Publisher: publish.yml on BalconyJH/takumi-py

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

File details

Details for the file takumi_py-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for takumi_py-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 f3536ed28e94022fc750a7d31939a3941c0069b4c6d4a9f93eba80f457aab6a2
MD5 df1ce637351dcee9b537cca6de294193
BLAKE2b-256 c6c1e6f2768438ac949db0b724ef7e171065ec16e70d5b052cf3f03f64b372ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for takumi_py-0.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on BalconyJH/takumi-py

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

File details

Details for the file takumi_py-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for takumi_py-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 6d56482cf09b25db82c251be200b766f9ecc19a3ea32155567649868a99d2f41
MD5 30eaa50638d0442a315789370a04e537
BLAKE2b-256 60408024180776c16bc414dad112ed858eb7b4f3eadf8a59d0c80fd258e1036e

See more details on using hashes here.

Provenance

The following attestation bundles were made for takumi_py-0.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: publish.yml on BalconyJH/takumi-py

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

File details

Details for the file takumi_py-0.2.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for takumi_py-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 29e11242af625395e26489dd74b0c4d7d460180a36770cafd11e9fee11f8e182
MD5 815d02538e438fc0e4cd0ca39331d50d
BLAKE2b-256 0ba5b005a244440abf0efc3505cff5b468660a94caabf59aa9df0ad835fe4cb2

See more details on using hashes here.

Provenance

The following attestation bundles were made for takumi_py-0.2.0-cp310-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on BalconyJH/takumi-py

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

Release history Release notifications | RSS feed

0.3.0

5 files

This release

0.2.0 This release

5 files

0.1.0

6 files

Supported by

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