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: or p{}): 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 br inside p{}
  • p{ ... }: treat contents as a single paragraph; each br forces 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 bulletColumnWidth so 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; }