Skip to content

Expose render_html and make the PDF stack an optional extra #2

Closed

Description

@apiad

Problem

Scriptorium can only produce a PDF. For documents the pipeline is already pure HTML + CSS: the preprocessors (footnotes, citations, glossary, timeline) → parse() → emit() build one HTML document, and WeasyPrint only prints it. But that HTML exists only as a local inside render_pdf (scriptorium/galley.py:668), and galley.py imports weasyprint at module level (galley.py:19). So:

  1. No HTML output. Nothing returns the HTML a document renders to, so a screen consumer has to re-implement the preprocessor chain to get it. The chain's order is deliberate (citations after footnotes, glossary last), so a copy drifts.
  2. WeasyPrint is mandatory. It pulls in pango and friends. A consumer that only needs Markdown → HTML still has to install the whole PDF stack.
  3. The entry point lives in galley.py, which is being retired: only the deck path still uses measure() / pack().

First consumer

marginalia (apiad/marginalia#5) is being rebuilt on scriptorium so its reader inherits all of scriptorium's Markdown features (components, math, footnotes, citations, glossary, cross-refs). It needs the HTML of a document and never a PDF. It will run on a server where installing WeasyPrint is pure cost.

Checked on 2026-10-04: a raw <span class="marg-s" data-o="42"></span> block survives process_footnotes → parse → emit as its own <div class="unit">, in order, between a footnoted paragraph, a loose list and the endnotes. HTML comments are already stripped by _split_md, so marginalia's annotations (HTML comments in the chapter source) never reach the PDF.

Wanted

  • render_html(src, *, theme_name=None, cwd=None, execute=True, vars=None, code_root=None, project_meta=None) -> (html, warnings) in a module outside galley.py, holding everything render_pdf does up to and including emit().
  • render_pdf becomes render_html + WeasyPrint, with unchanged behaviour and report.
  • WeasyPrint moves to an optional extra (scriptorium[pdf]) and is imported lazily inside the PDF path. import scriptorium and render_html work without it; render_pdf without it fails with a message naming the extra.
  • scriptorium render --html (or -o x.html) writes the HTML document. A deck theme still needs measuring and the pdf extra, so --html on a deck errors out clearly.
  • Tests: render_html round trip on the existing fixtures, render_pdf output unchanged, and an import test in an environment without WeasyPrint.

Note: parse.py and tests/test_parse.py carry uncommitted changes in the shared checkout on zion; this work should not need to touch either.

Activity

  1. apiad commented on Oct 5, 2026

    @apiad
    OwnerAuthor

    Also needed for any install that is not an editable checkout: the wheel ships no themes. uv build --wheel on d87d5d9 produces 24 files and none under themes/, because [tool.hatch.build.targets.wheel] packages = ["scriptorium"] and THEMES_DIR = Path(__file__).resolve().parent.parent / "themes" (scriptorium/theme.py:26) points outside the package. marginalia installs scriptorium as a git dependency, so load_theme would fail there. Fix: force-include themes into the package and resolve THEMES_DIR inside it, falling back to the repo layout.

  2. self-assigned this
    on Oct 5, 2026
  3. apiad commented on Oct 5, 2026

    @apiad
    OwnerAuthor

    taken by httyc-dictation-pilot

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions