简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

ErisPulse-Takumi

ErisPulse-Takumi is a third-party image rendering module maintained by ccd2s, based on takumi-py, allowing the Bot to render HTML, node trees, Jinja templates, SVG, and animations into images. The module includes built-in Chinese and English fonts (Noto Sans SC / Roboto / Source Code Pro), requiring no additional configuration.

Important

Takumi is not a built-in feature of the ErisPulse framework and needs to be installed separately:

epsdk install Takumi

Applicable scenarios:

Installation and Activation

epsdk install Takumi

After installation, the module is automatically loaded. Confirm activation in the configuration:

[Takumi]
enabled = true

Quick Start

After the module is automatically loaded, it can be obtained through the module manager or using the sdk shortcut:

from ErisPulse import sdk

takumi = sdk.module.get("Takumi")
# Equivalent syntax: takumi = sdk.Takumi

Render HTML

png = takumi.render_html(
    """
    <div class="card">
      <h1>Hello, ErisPulse</h1>
      <p>Rendered by Takumi</p>
    </div>
    """,
    stylesheets=["""
    .card {
      width: 800px;
      padding: 48px;
      color: white;
      background: #111827;
      font-family: "Noto Sans SC";
    }
    """],
    width=800,
    height=None,   # Auto height based on content
    lang="en",
)

Render Node Tree

png = takumi.render_node(
    {
        "type": "text",
        "text": "Both Chinese and English can be rendered directly",
        "style": {"fontSize": 48, "color": "#111827"},
    },
    width=800,
    height=None,
    lang="en",
)

png is a bytes object, which can be sent using event.reply(png, method="Image") (see Sending Rendered Results).


Rendering API

sdk.Takumi proxies all capabilities of the underlying takumi_py.Renderer: all rendering, measurement, SVG, animation, and template methods are directly callable on sdk.Takumi. For these methods, the module automatically injects the built-in font fallback stack (takumi.families) at the time of invocation, eliminating the need to manually pass font_families; however, explicit input is respected if provided.

Method Overview

Category Method Return Description
Static Rendering render_html(html, ...) bytes Render HTML string
render_node(node, ...) bytes Render node tree (dict)
render_template(name, ctx, ...) bytes Render Jinja template
render_compiled(node, ...) bytes Render pre-compiled node
SVG Output render_svg_html(html, ...) str Output SVG (HTML input)
render_svg_node(node, ...) str Output SVG (node tree input)
render_svg_template(name, ctx, ...) str Output SVG (template input)
render_svg_compiled(node, ...) str Output SVG (pre-compiled input)
Animation render_animation(scenes, ...) bytes Encode multi-frame animation
render_sequence_at_time(scenes, time_ms, ...) bytes Extract frame at a specific time from sequence
Measurement measure_node(node, ...) dict Measure node tree layout
measure_html(html, ...) dict Measure HTML layout
measure_compiled(node, ...) dict Measure pre-compiled node
Compilation compile_node(node) CompiledNode Compile node tree
compile_html(html, ...) CompiledNode Compile HTML
Font register_font(font) list[str] Register custom font, return family list
register_fonts(fonts) list[str] Batch register fonts

CompiledNode exposes a resource_urls() method, allowing pre-discovery of HTTP(S) image references, facilitating resource preparation in advance.

Common Parameters

The following parameters apply to static rendering and SVG methods (animation methods have additional parameters such as fps, see corresponding examples):

Parameter Type Default Description
stylesheets list[str] None List of document-level CSS strings; inline style is still parsed with HTML
width int | None 1200 Viewport width (in pixels); None infers width from layout
height int | None 630 Canvas height (in pixels); None auto-sizes content (see Viewport and Output Format)
lang str | None None BCP-47 language tag (e.g., zh-CN), affects text shaping and line breaking
font_families list[str] Auto-injected Font fallback stack; convenience methods auto-inject built-in fonts
format str "png" Output format (see Viewport and Output Format)
device_pixel_ratio float 1.0 Device pixel ratio, controls output resolution
time_ms int 0 Animation sampling time (in milliseconds)
dithering str "none" Dithering algorithm: none / ordered-bayer / floyd-steinberg
quality int | None None Lossy encoding quality
lossless bool | None None Whether to use lossless encoding
images list None Image resources for this render (ImageResource or (src, bytes) tuple)
keyframes Mapping None Structured keyframes, no need to write @keyframes
options RenderOptions — Aggregate parameters via RenderOptions(...), fields match the above table

Full field definitions are available in takumi_py.RenderOptions.

Node Tree Example

png = takumi.render_node(
    {
        "type": "container",
        "style": {"padding": "32px", "backgroundColor": "#111827"},
        "children": [
            {"type": "text", "text": "Title", "style": {"fontSize": 32, "color": "white"}},
            {"type": "text", "text": "Body", "style": {"fontSize": 18, "color": "#9ca3af"}},
        ],
    },
    width=800,
    height=None,
    lang="zh-CN",
)

Jinja Template Example

png = takumi.render_template(
    "card.html.jinja",
    {"title": "Takumi", "subtitle": "Jinja to image"},
    stylesheets=["""
    .card {
      width: 800px;
      padding: 48px;
      color: white;
      background: #111827;
    }
    """],
    width=800,
    height=None,
    lang="zh-CN",
)

Custom Jinja filters can be injected via filters={...}, or a full jinja2.Environment can be passed via environment=.... Template directory and environment configuration are detailed in takumi-py template documentation.

SVG Output Example

svg = takumi.render_svg_html(
    '<div class="card">Hello</div>',
    stylesheets=[".card { width: 800px; color: black; }"],
    width=800,
    height=None,
)

Animation Example

from takumi_py import AnimationScene

webp = takumi.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,
    format="webp",
)

Each frame is constructed using AnimationScene(node, duration_ms=...), where duration_ms must be a positive number.

Viewport and Output Format

Output Format

Scenario format value
Static image png (default) / jpeg / jpg / webp / ico / raw
Animation webp (default) / apng / gif

format="raw" returns a row-major RGBA byte stream, for custom pixel-level processing.

About width and height

The roles of width and height are asymmetrical:

Tip

Recommended combination: fixed width + height=None. Only when a fixed canvas size or clipping effect is needed, should a specific height be passed.

Note

Either width or height can technically be passed as None to let it be inferred by the layout (e.g. when node itself has already declared its size); when both are provided, the output size is determined.

Fonts

Built-in Fonts

Font Family Category
Noto Sans SC Noto Sans SC sans-serif
Roboto Roboto sans-serif
Roboto Italic Roboto sans-serif (italic)
Source Code Pro Source Code Pro monospace
Source Code Pro Italic Source Code Pro monospace (italic)

Module attributes:

Attribute Description
takumi.fonts List of built-in font file names
takumi.families List of registered font families

Automatic Injection

All rendering, measurement, SVG, animation, and template methods on sdk.Takumi automatically inject takumi.families as the font fallback stack. If you directly call takumi.renderer (native instance) or create an independent instance via create_renderer(), you must manually pass font_families=takumi.families.

Custom Fonts

from takumi_py import FontResource

families = takumi.renderer.register_font(
    FontResource(
        font_bytes,
        name="MyFont",
        weight=400,
        style="normal",
        generic_family="sans-serif",
    )
)

register_font returns a list of registered family names, which can be passed as font_families in subsequent rendering.

Renderer Instances

Native Renderer

takumi.renderer is the raw takumi_py.Renderer instance. When called directly, font_families must be passed manually:

png = takumi.renderer.render_html(
    "<div>Hello</div>",
    font_families=takumi.families,
    lang="zh-CN",
)

Standalone Renderer

For scenarios requiring isolated font/image/resource caching (long-lifecycle processes, multi-tenant scenarios), you can create a standalone Renderer, which automatically registers built-in fonts:

renderer = takumi.create_renderer(cache_max_bytes=64 * 1024 * 1024)

png = renderer.render_html(
    "<div>Standalone Renderer</div>",
    font_families=takumi.families,
    width=800,
    height=None,
    lang="zh-CN",
)

create_renderer() accepts constructor parameters from takumi_py.Renderer:

Parameter Type Default Description
load_default_fonts bool False Whether to load fonts bundled with takumi-py (built-in fonts are always loaded)
fonts list[FontResource] None Additional custom fonts to register
cache_max_bytes int | None None Maximum resource cache size (in bytes); 0 disables caching
persistent_images list None Persistent image resources

Standalone instances bypass the module proxy, so if you want to retain a unified built-in font fallback stack, you must explicitly pass font_families=takumi.families. If font_families is explicitly passed, the module respects the caller's setting and does not inject the default fallback stack; RenderOptions(font_families=...) is also valid.

Sending Rendered Results

The rendered image is in bytes format and can be sent directly via event reply:

from ErisPulse import sdk

takumi = sdk.Takumi
png = takumi.render_html("<div>hello</div>", lang="en")

# Method 1: Reply using Image method
await event.reply(png, method="Image")

# Method 2: Reply using OneBot12 message segment
from ErisPulse.Core.Event import MessageBuilder
await event.reply_ob12(
    MessageBuilder().image(png).build()
)

The adapter handles the image encapsulation for different platforms. See MessageBuilder Detailed Explanation and Send Method Specification for more information.


Configuration

[Takumi]
enabled = true