# 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.