Backend Python library for rendering LaTeX problem statements and CP editorial animations to self-contained HTML
Project description
Scriba
Status: v0.7.0 · MIT · Python 3.10+
Scriba is a backend Python library that renders LaTeX problem statements and
competitive-programming editorials to self-contained HTML fragments. It is
LaTeX-first: drop a .tex source in, get out HTML plus the exact CSS/JS
asset basenames needed to display it.
What is Scriba?
- LaTeX-first rendering for CP problem statements and editorials, with
KaTeX math, Pygments code highlighting, lists, tables, sections, figures,
\href/\urlwith XSS hardening, and\begin{lstlisting}code blocks. - Self-contained output contract: every render produces an HTML fragment plus a namespaced set of required CSS and JS basenames and a block-data map — consumers decide how to serve the static assets.
\begin{animation}environment (shipping since 0.2.0) for step-through editorial walkthroughs with 16 built-in primitives (arrays, grids, graphs, trees, DP tables, number lines, matrices/heatmaps, stacks, plane-2D, metric plots, and the 5 data-structure primitives: code panel, hash map, linked list, queue, variable watch).\begin{diagram}for inline static graph/tree figures is reserved under extension E5. Seedocs/spec/ruleset.mdfor the full grammar and error catalog.
What's new in v0.6.0
- Wave 8 — vstack layout. Array, DP-table, and related primitives now
compose their caption, index labels, and cells through a shared
scriba/animation/primitives/layout.pyvstack helper. No more hardcoded Y offsets: cell, index, and caption font sizes drive the layout through real font metrics. - Wave 9 — CSS-first text halo cascade. Every
[data-primitive] textelement now inheritspaint-order: stroke fill markerswith a--scriba-haloCSS variable that each state class overrides. The block is wrapped in@media (forced-colors: none)so Windows High Contrast Mode strips it cleanly. The per-calltext_outline=parameter on primitives is deprecated and scheduled for removal in v0.7.0 — authors should rely on the CSS cascade instead. - RFC-001 — structural mutation ops. Tree, Graph, and Plane2D primitives
gained safe structural ops (
add_node,remove_node,reparent, and friends) plus a newhiddenstate for elements that are modeled but not yet rendered. Seedocs/guides/hidden-state-pattern.mdfor the intended authoring flow. - RFC-002 — strict mode and document warnings. The pipeline now
surfaces non-fatal issues on
Document.warningsas a tuple of typedCollectedWarningentries (code, message, source line/col, primitive, severity). SettingRenderContext(strict=True)promotes a designated set of dangerous codes (E1461,E1462,E1463,E1484,E1501,E1502,E1503) into hard render errors;strict_exceptopts specific codes back out. Strict mode is aRenderContextfield, not a core CLI flag — CLIs wrap it themselves. Seedocs/guides/strict-mode.md. - Examples reorganized. 53
.texexamples acrossexamples/quickstart/,examples/algorithms/,examples/cses/, andexamples/primitives/. HTML outputs are now build artifacts (./examples/build.sh), not checked into git. Seedocs/cookbook/README.md.
Install
pip install scriba
Scriba shells out to a small Node.js worker for KaTeX math, so the host environment needs Node.js 18+ on PATH:
# System prerequisite — Node.js only
apt-get install nodejs # or: brew install node
KaTeX 0.16.11 is vendored inside the wheel (at
scriba/tex/vendor/katex/katex.min.js), so no separate
npm install -g katex step is required. pip install scriba is all
you need once Node is present.
Hello world
from scriba import Pipeline, RenderContext, SubprocessWorkerPool
from scriba.tex import TexRenderer
pool = SubprocessWorkerPool()
pipeline = Pipeline([TexRenderer(worker_pool=pool, pygments_theme="one-light")])
ctx = RenderContext(
resource_resolver=lambda name: f"/cdn/problems/1/{name}",
theme="light", dark_mode=False, metadata={}, render_inline_tex=None,
)
doc = pipeline.render(r"\section{Hello} Let $x^2$ be the square.", ctx)
print(doc.html) # HTML fragment
print(doc.required_css) # namespaced CSS keys
pipeline.close()
Standalone CLI
For quick rendering without writing Python, use render.py directly:
python render.py input.tex # → input.html
python render.py input.tex -o out.html # → custom output path
python render.py input.tex --open # → render and open in browser
Output is a single, fully portable HTML file — all CSS, KaTeX math
fonts, syntax highlighting, and images (via \includegraphics) are
inlined as data URIs. No internet connection or external files needed.
Just open the .html file in any browser.
For legacy filmstrip mode (static frames, no JavaScript):
python render.py input.tex --static
Sanitize before embedding
Scriba does not sanitize its output — consumers must pass it through a vetted sanitizer before embedding in a page. Scriba ships an allowlist that matches its output contract:
import bleach
from bleach.css_sanitizer import CSSSanitizer
from scriba import ALLOWED_TAGS, ALLOWED_ATTRS
css = CSSSanitizer(allowed_css_properties=("transform","transform-origin","width","height"))
safe = bleach.clean(doc.html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRS,
css_sanitizer=css, strip=True)
Serving static assets
Assets ship inside the Python package. Copy them at deploy time:
from importlib.resources import files
import shutil
shutil.copytree(str(files("scriba.tex.static")), "./public/scriba", dirs_exist_ok=True)
Then include them alongside the rendered fragment:
<link rel="stylesheet" href="/cdn/katex/katex.min.css">
<link rel="stylesheet" href="/public/scriba/scriba-tex-content.css">
<link rel="stylesheet" href="/public/scriba/scriba-tex-pygments-light.css">
<script defer src="/public/scriba/scriba-tex-copy.js"></script>
<article class="scriba-tex-content">{{ doc.html }}</article>
Note: This section applies to the Pipeline API (library usage), where you serve assets yourself. If you use
render.pyinstead, the output HTML is fully self-contained — all CSS, KaTeX fonts (base64), and Pygments highlighting are inlined. No separate asset serving needed.
Documentation
Full architecture, contracts, and roadmap live under the project docs tree: https://github.com/ojcloud/scriba/tree/main/docs
License
MIT. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file scriba_tex-0.7.0.tar.gz.
File metadata
- Download URL: scriba_tex-0.7.0.tar.gz
- Upload date:
- Size: 1.5 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90b9e544a76e6973ab4ce5f39d08e325e41078de2d1b562ae4b130041711e52f
|
|
| MD5 |
9ba584bb1c0e994df469b8188528ec9a
|
|
| BLAKE2b-256 |
2d3118c93b3c168e8cebb13bbea564cc00cb7358199304a527e3e13d537cc00d
|
File details
Details for the file scriba_tex-0.7.0-py3-none-any.whl.
File metadata
- Download URL: scriba_tex-0.7.0-py3-none-any.whl
- Upload date:
- Size: 615.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.0 {"installer":{"name":"uv","version":"0.11.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8117973bf8729a247536ed2464ad0c045f049d99b6d839ff4792591022fa6ee1
|
|
| MD5 |
5b42444c4662abd65ff38e256fc81c77
|
|
| BLAKE2b-256 |
88608601bd32c9b0f5a7481fb06fa902002915c58f2429d12b37888d69a9090a
|