iftypeset/docs/17-rendering-pipeline.md
codex e92f1c3b93
Some checks are pending
ci / ci (push) Waiting to run
iftypeset: document CI pipeline + Playwright + font contract
2026-01-08 18:10:41 +00:00

2.8 KiB

Rendering pipeline (Markdown -> HTML -> PDF)

This doc explains how a page is built. iftypeset always renders HTML + CSS first, then optionally renders PDF.

Flow diagram (file flow)

Markdown (.md)
  |
  v
parse_markdown (src/iftypeset/md_parser.py)
  |
  v
render-html (src/iftypeset/rendering.py)
  |-- out/render.html
  |-- out/render.css
  |-- out/typeset-report.json
  |-- out/degraded-mode-report.json (if degraded input)
  |
  v
render-pdf (engine adapter)
  |-- out/render.pdf
  |-- out/render-log.json
  |
  v
qa (src/iftypeset/qa.py)
  |-- out/layout-report.json
  |-- out/qa-report.json

Lint and reporting are parallel entry points that do not require PDF:

lint (src/iftypeset/linting.py)
  |-- out/lint-report.json
  |-- out/manual-checklist.md

report (src/iftypeset/reporting.py)
  |-- out/report/index.html
  |-- out/coverage-report.json
  |-- out/trust-contract.md
  |-- out/trust-contract.json

Step-by-step (what actually happens)

  1. Read Markdown, normalize newlines, detect degraded input, and unwrap hard wraps when needed.
  2. Parse Markdown into blocks (heading, paragraph, list, code fence, blockquote, table).
  3. Render HTML by mapping blocks to tags and applying inline transforms:
    • Links and images are preserved
    • Citation tokens like [S01] are wrapped for styling
    • Optional small-caps wrapping for acronyms (profile-controlled)
  4. Generate CSS from the selected profile:
    • Page size and margins
    • Fonts and sizes
    • Colors, tables, lists, figures, code blocks
    • Running-head config (if enabled for supported engines)
  5. Render PDF using the requested engine (default: Playwright):
    • Writes a render log with engine versions and warnings
  6. QA scans HTML output, and if a PDF exists it also runs PDF-aware heuristics.

Where to change behavior

  • Profiles and tokens: spec/profiles/*.yaml
  • HTML mapping + table typing: src/iftypeset/rendering.py
  • CSS generation: src/iftypeset/css_gen.py
  • Markdown parsing: src/iftypeset/md_parser.py
  • QA rules and thresholds: src/iftypeset/qa.py, spec/quality_gates.yaml
  • Lint rules and logic: src/iftypeset/linting.py, spec/rules/**

Commands (repro)

iftypeset render-html --input fixtures/sample.md --out out --profile web_pdf
iftypeset render-pdf --input fixtures/sample.md --out out --profile web_pdf
iftypeset qa --out out --profile web_pdf

Notes on determinism

  • HTML + CSS is deterministic for the same input + profile.
  • PDF output depends on the renderer and installed fonts; versions are recorded in out/render-log.json.
  • Profiles can enable a strict fonts contract (fonts.require_primary: true) and the CLI can force it via --strict-fonts; use --font-dir to supply corporate fonts and avoid silent fallbacks.
  • Running heads are injected only for engines that support header/footer templates.