DocDraw v1 → PDF Rendering Spec (Draft)
This defines the deterministic rendering rules for producing a “standard business document” PDF from valid DocDraw v1.
Status: Draft (values below should be treated as defaults until finalized).
0) Goals
- Professional “standard document” look: clean paragraphs, readable lists, consistent spacing.
- Deterministic: same DocDraw input → same PDF output.
- No layout guessing: structure comes only from DocDraw tokens.
1) Page setup
Paper + margins
- Page size: Letter (8.5" × 11") by default
- Optional setting: A4
Margins:
- Top: 1.0"
- Bottom: 1.0"
- Left: 1.0"
- Right: 1.0"
Content width = page width − left margin − right margin
Pagination rule
Render blocks top-to-bottom.
If a block cannot fit fully and is “splittable” (paragraph/list), split across pages.
Headings should avoid being orphaned: if a heading would be the last line on a page, push it to next page (unless it fits with at least 1 following line of content).
2) Typography (default style)
Fonts (deterministic choice)
Pick a bundled, permissively licensed family and embed it.
Suggested families:
- Inter (sans) or Source Serif 4 (serif)
Baseline defaults:
- Body font: Source Serif 4 Regular
- Body size: 11pt
- Line height: 1.25× font size (≈ 13.75pt)
Color
- Text:
#111111(almost black) - No colored text in v1
Alignment
- Left aligned
- No full justification
3) Block layout rules
Define vertical spacing unit: U = 6pt
Default block spacing:
- After paragraph (
p:orp{}): U (6pt) - After heading: U (6pt)
- After list block: U (6pt)
- After divider (
---): 2U (12pt)
Paragraphs:
- First-line indent: 0 in v1
- Soft wrapping: words wrap to fit content width
- Hard breaks: only generated by
brinsidep{} p{ ... }: treat contents as a single paragraph; eachbrforces a new line within the same paragraph
4) Headings
Input:
#1:…#6:
Heading typography:
-
1 (H1): 18pt, Semibold
-
2 (H2): 14pt, Semibold
-
3 (H3): 12pt, Semibold
-
4 (H4): 11pt, Semibold
-
5 (H5): 11pt, Semibold
-
6 (H6): 11pt, Semibold
Spacing:
- Space before headings:
-
1: 3U (18pt) unless it is the first block on a page
-
2: 2U (12pt)
-
3–#6: 2U (12pt)
-
- Space after headings: 1U (6pt)
Keep-with-next:
- Heading must keep with at least one following line (either first line of paragraph or first list item). If not possible, push heading to next page.
5) Dividers
Input:
---
Rendering:
- Horizontal line across 100% of content width
- Stroke: 0.5pt
- Color:
#BBBBBB - Space before: 2U (12pt)
- Space after: 2U (12pt)
6) Lists (most important)
Input:
- Bullets:
-L: text(L=1..9) - Numbered:
1-L: text(L=1..9) - Continuations:
..: text(belongs to preceding list item)
List grouping:
- A “list block” is a contiguous run of list items and continuations.
- If a non-list block appears, the list block ends.
Indentation model (deterministic defaults)
Define:
listIndentStep = 18pt(per level)bulletColumnWidth = 18pt(space reserved for marker)hangingIndent = 12pt(text aligns after marker)
For a list item at level L:
- Base left indent = (L - 1) ×
listIndentStep - Marker X position = left margin + base left indent
- Text start X position = marker X +
bulletColumnWidth
Line wrapping within list items:
- Wrap at content width minus current level indent.
- Subsequent wrapped lines align to text start X (hanging indent style).
Vertical spacing in lists:
- Default: 2pt between list items
Bullet marker rendering (defaults)
For -L: bullets:
- Level 1:
• - Level 2:
◦ - Level 3+: pick one and keep it consistent (
▪recommended)
Marker font = body font, same size.
Numbered marker rendering (defaults)
Maintain a counter per list “scope.”
Scope rules:
- Each contiguous run of
1-*items is one ordered list block. - Within that block, numbering is tracked per level:
- Level 1 items count 1,2,3...
- Level 2 resets whenever a new level-1 item begins
- Level 3 resets whenever a new level-2 item begins
Marker format:
- Level 1:
1. 2. 3. - Simple v1 recommendation: always use numeric markers at every level, tracked independently per level
Marker width handling:
- Right-align marker text within
bulletColumnWidthso multi-digit numbers don’t shift text.
Continuations (..:)
- Render as additional wrapped text lines for the prior list item
- No extra vertical spacing before a continuation
- Start at the same text start X
7) Quotes (q:) (optional)
Rendering defaults:
- Left indent: 18pt
- Vertical rule at left edge: 2pt thick,
#DDDDDD - Quote text: body font (italic optional; default regular)
- Space before: 1U
- Space after: 1U
8) Code blocks (code{}) (optional)
Rendering defaults:
- Monospace font (bundled): e.g., JetBrains Mono
- Size: 10pt
- Background fill:
#F6F6F6 - Border:
#E0E0E0, 0.5pt - Padding: 8pt
- Preserve whitespace; wrap long lines (wrapping is simpler)
9) Handling “bad” input
Even though validation should prevent this, renderer behavior should be deterministic:
- Unknown lines: error out with a clear message and line number (no silent interpretation).
10) Export consistency requirements
To keep output stable across machines:
- Embed fonts in the PDF
- Use a single rendering engine (Chromium print OR PDF library) and don’t mix
If using HTML→PDF:
- Use fixed CSS for margins, font metrics, list indentation
- Disable “smart” browser list styling; draw list markers yourself for consistency
11) Minimal HTML/CSS mapping (if using HTML→PDF)
Do not rely on <ul> default rendering. Instead, render a normalized structure:
<div class="p">...</div>
<div class="li level-2">
<span class="marker">•</span><span class="text">...</span>
</div>
Then CSS controls everything deterministically:
.li.level-3 { margin-left: calc((3 - 1) * 18pt); }
.marker { width: 18pt; display: inline-block; text-align: right; padding-right: 6pt; }
.text { display: inline; }