The paint tree
Read what the renderer paints with takumi-paint, for PPTX, Canvas, and native drawing exporters.
takumi-paint lays out a document and returns what painting it would draw, instead of an image. Use it when the output is not pixels: an editable PPTX, a Canvas replay, a native drawing API.
import { , } from "takumi-paint";
const = await (
< ={{ : 640, : 32, : "#F7F3EC", : "column", : 8 }}>
< ={{ : 40, : 700, : "#B3261E" }}>Hello paint</>
<>Every box and text run, with the values the renderer used.</>
</>,
{ : 640 },
);
function (: ) {
for (const of ()) {
if (.) .(....);
else .(1, 0, 0, 1, ., .);
if (.?.) {
. = (..);
.(0, 0, ., .);
}
for (const of . ?? []) {
.();
if (.) .(....);
. = `${..} ${.}px ${..}`;
. = (.);
.(., ., .);
.();
}
}
.();
}
const = ([, , , ]: number[]) => `rgb(${} ${} ${} / ${ / 255})`;The package runs the same layout as takumi-js and walks the same stacking-context scene the image, SVG, and PDF backends paint. It records used values: a 50% width is a number, currentColor is a color, and <b>world</b> is its own run with its own face.
Coordinates
Every length is a device pixel, and so are the width and height options. A 20px font measures 40 under a devicePixelRatio of 2.
A node's x and y place its border box on the canvas. transform appears only when the box is rotated, scaled, or skewed, and its translation equals x, y. Everything inside a node, such as clip, background, image, and textRuns, is relative to the node's border-box top-left.
Paint order
Nodes come in the order the renderer paints them, so zIndex does not appear. walk() yields them in that order. Within a node, draw shadows.outer, background, shadows.inset, and border, then image, inlineBackgrounds, and textRuns, then children, then outline.
Each node is a compositing group. Its opacity, clip, and blendMode apply to everything inside it, children included.
Fields
Prop
Type
Description
source?PaintSourceThe input node this box came from: its child-index path, id, tag name, and class name
background?PaintBackgroundUsed background color, clip, and image layers
border?PaintBorderBorder widths, colors, styles, and radii
shadows?PaintBoxShadowsBox shadows split into inset and outer
outline?PaintOutlineThe outline, painted after the children
image?PaintImageThe content box an image sits in and where it draws after object-fit and object-position
textRuns?PaintTextRun[]Shaped text runs with their font, size, color, decorations, and glyph positions
inlineBackgrounds?PaintInlineBackground[]One rounded rectangle per line for each inline span with a background
unresolvedEffects?PaintUnresolvedEffectsfilter, backdrop-filter, mask-image, and clip-path as CSS text
Each run carries its font: the registered family, the face index, the weight and style after variations, and any synthetic bold or skew the shaper applied. textRuns() yields every run with the node that paints it, and find() looks a node up by its source id.
What stays CSS
Linear and radial gradients come resolved: stops in pixels along the axis, plus the tile geometry. A conic gradient is carried as CSS text. filter, backdrop-filter, mask-image, and clip-path are carried as CSS text under unresolvedEffects, so an exporter can decide what to do with them.
Last updated on