clone

a browser-first diagram editor with live svg previews, local drafts and encrypted, accountless sharing

main
diagramzip / renderers / python / blockdiag-family / README.md
README.md 4.3K rendered
1# BlockDiag family renderer23One Cloudflare Python Worker renders BlockDiag, SeqDiag, ActDiag, NwDiag,4PacketDiag, and RackDiag directly from pinned upstream Python libraries. The5request hostname selects the engine. Request bodies cannot select a different6engine.78The Worker imports renderer libraries directly and does not execute a command,9spawn a process, call Java, or fetch the Fly compatibility origin. Remote and10filesystem-backed images are rejected. SVG is sanitized before presentation11metadata, padding, backgrounds, and framing are applied.1213The six engines share one Worker because they form one compatible upstream14dependency family. The four source trees provide the same parser, builder, SVG15drawer, font, and node-renderer model. They retain six hostnames, so the hostname16remains the only engine selector. The shared deployment avoids six copies of17Pillow, the font, and the registration table. One request cannot select another18engine.1920## Reproduce locally2122Run these commands from this directory:2324```sh25uv sync26uv run pytest -q27uv run pywrangler sync28uv run pywrangler dev --port 878829```3031The Worker build is Pyodide-based. `pywrangler sync` is therefore the32important packaging check; ordinary `uv sync` only checks the CPython test33environment. Deploy only after both checks pass:3435```sh36uv run pywrangler deploy37```3839For a local request, route the hostname to the development port, for example:4041```sh42curl --resolve blockdiag.render.diagram.zip:8788:127.0.0.1 \43 -H 'Content-Type: application/json' \44 --data '{"source":"blockdiag { a -> b; }"}' \45 http://blockdiag.render.diagram.zip:8788/v1/svg46```4748The request body must not contain an `engine` field. Wrangler's local custom49domain emulation resolves every configured hostname as the first route. Use the50CPython six-engine example suite locally. Smoke all six real hostnames after51deployment. Do not treat a local `Host` header as proof of per-host dispatch.5253## Upstream parity reference5455When an upstream comparison is needed, run the tagged Kroki image directly.56It is an external reference and is not part of DiagramZip's build or runtime:5758```sh59docker run --rm -p 8000:8000 yuzutech/kroki:0.32.160```6162Send the same fixtures to the reference image and to the Worker development63port, then compare structural SVG coverage and record engine-specific losses.64Pixel equality is not a gate for this extraction.6566Pinned upstream revisions and their licenses are recorded in67`THIRD_PARTY_NOTICES.md` and `uv.lock`.6869## Boundaries and known losses7071The request protocol enforces a 1 MiB body limit and a 512 KiB source limit. It72allows at most 64 options and 4 KiB per option. Padding ranges from 0 through73256. The sanitized SVG limit is 4 MiB. Only SVG output is exposed. Remote URLs74and filesystem images are rejected before they can be fetched. Returned SVG is parsed with75`defusedxml`; scripts, event handlers, active elements, external references,76unsafe CSS, and unsafe embedded resources are removed.7778Supported renderer options currently include `size=WIDTHxHEIGHT`, `antialias`,79`no-transparency`, and `no-doctype`. The two raster-only flags, `antialias` and80`no-transparency`, intentionally have no effect on SVG. The common protocol81validates other upstream command-line options but does not pass them to a CLI.82They remain documented loss until a direct API mapping is added. The Worker83uses the bundled DejaVu Serif font path from the Docker reference. This path84provides deterministic text metrics. Font fallback can differ on a larger host.8586The vendored runtime replaces upstream `importlib.metadata` entry-point87discovery with an explicit registration table. It installs the SVG drawer,88all bundled node renderers (including PacketDiag), and only the built-in89`attributes`, `autoclass`, and `autolane` plugins. This keeps the Worker90reproducible and prevents arbitrary installed plugins from entering the91rendering process.9293The upstream libraries expose process-wide mutable renderer and plugin94registries. Registration is one-time startup work. Each render and its cleanup95are synchronous with no `await` boundary. Requests cannot interleave while one96isolate changes those registries. Cloudflare can run multiple isolates. Each97isolate has its own Python runtime and registries. Keep rendering synchronous98until the registries become request-local.