Skip to content

v0.1.0: owd-typst and owd-docx formats driven by the brand - #10

Merged
larnsce merged 8 commits into
mainfrom
dev
Sep 3, 2026
Merged

v0.1.0: owd-typst and owd-docx formats driven by the brand#10
larnsce merged 8 commits into
mainfrom
dev

Conversation

@larnsce

@larnsce larnsce commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New Quarto extension owd with two formats. owd-typst reads _brand/_brand.yml at render time: brand fonts, headings and title rule in the primary colour, the medium logo once in the title block, white pages, A4, a footer with footer-text and page numbers. owd-docx uses a reference.docx whose styles tools/make-reference-docx.R generates from the same brand file, byte for byte reproducible.
  • _brand/ mirrors openwashdata/brand (via quarto use brand), template.qmd is the starter document and the CI smoke test, plus README, NEWS, .quartoignore and a page image.
  • CI: render.yaml renders both formats on push and PR and fails when reference.docx is stale against the brand; brand-sync.yaml refreshes the brand weekly and opens a PR into dev (needs the token decision in CI: brand-sync workflow #7 before it can open PRs).
  • Verified on Quarto 1.9.38 and, with an explicit brand: key, on 1.8.27; quarto-required is >=1.8.0.

Refs #1, #2, #3, #4, #5, #6, #7, #8.

Test plan

  • render.yaml green on this PR, both jobs
  • template.pdf from the template artifact: white pages, logo once in the title block, footer on every page, purple headings
  • template.docx from the same artifact opened in Word: Atkinson Hyperlegible text, purple bold headings, shaded code block, tinted table header row, headings listed in the navigation pane
  • After the merge: gh workflow run brand-sync.yaml on an unchanged brand ends without a pull request

larnsce and others added 6 commits September 3, 2026 09:56
Minimal owd-typst format with Quarto's default partials, the brand
mirror in _brand/ from quarto use brand openwashdata/brand, a spike
document and the ignore file. Spike findings: Quarto 1.9 discovers
_brand/ on its own, 1.8 needs an explicit brand key, so the floor is
1.8.0. logo: false does not suppress the page background logo, so
the typst format will override page.typ.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
typst-template.typ defines article() with white pages, headings and the
title rule in the brand primary colour, the medium logo once in the title
block, a footer with the site and page numbers, and code blocks tinted
with the primary background. typst-show.typ resolves the brand
dictionaries and typography variables and passes them as arguments, with
font fallbacks appended. page.typ is Quarto 1.9's partial without the
page background logo. Empty brand dictionaries and own string helpers
keep the template compiling on Quarto 1.8 and without a brand.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
)

tools/bootstrap-reference-docx.R creates the reference document once from
pandoc's default on A4 with 25 mm margins. tools/make-reference-docx.R
rewrites word/styles.xml and word/theme/theme1.xml from _brand/_brand.yml:
brand fonts as explicit run fonts with every theme reference removed,
headings and title in the primary colour, links in the link colour, a
Source Code style with Source Code Pro and shading, italic captions,
a tinted bold first table row, and single spacing where the 1.5 body
line height would look wrong. Edits set attributes in schema order, so
outline levels and keep-with-next survive and a header or footer added
in Word is left alone. The zip step fixes timestamps and the timezone,
so two runs give identical bytes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
The README covers install for a new document and an existing project,
the two update commands and why they differ, fonts, Quarto 1.8, the
layout washr data packages already have, how the brand flows, and how
maintainers regenerate reference.docx. .quartoignore keeps repository
files out of projects created with quarto use template.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
, #7)

render.yaml renders template.qmd to both formats on push and pull
request, caches the Typst font download, uploads the outputs, and fails
when the committed reference.docx differs from what the generator
produces from the current brand. brand-sync.yaml runs weekly and on
request: refreshes _brand/ with quarto use brand --force, regenerates
reference.docx, renders as a gate and opens a pull request into dev
when anything changed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
Without an entry in word/fontTable.xml Word falls back to Times New Roman
when a brand font is not installed. The generator now writes one entry
per brand font with an alternate name (Arial for text, Courier New for
code), family, pitch and the OS/2 unicode and codepage ranges read from
the font files. pandoc copies the table into every rendered document.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
@larnsce

larnsce commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Added on dev: the generator now writes font table entries for the brand fonts with alternate names (Arial for text, Courier New for code), family, pitch and the OS/2 ranges read from the font files. A reader without the fonts sees a sans-serif and a monospace instead of Times New Roman. Confirmed that pandoc copies the table into the rendered file. The screenshot that prompted this was Word substituting for fonts not installed on the reviewing Mac; they are installed there now.

From the review notes in template.docx: headings sit on a single line
height with space above and a little below by level, body paragraphs get
6 pt after and nothing before, the line height is Word's 1.15 instead of
the brand's screen value of 1.5, and the margins are 20 mm. Data tables
get all borders in ink and a tinted bold first row through a new
OwdTable style that docx-tables.lua assigns to Table elements; Quarto's
caption and figure wrappers and callouts stay on the border free Table
style. The generator now also sets the page margins in document.xml.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
@larnsce

larnsce commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Review notes from template.docx applied on dev:

  • Spacing below headings: headings now sit on a single line height (the body line height had inflated their line box) with 18/6 pt around Heading 1, 12/4 pt around Heading 2, 10/3 pt around Heading 3; body paragraphs have 6 pt after and nothing before.
  • Line spacing and margins: 1.15 instead of 1.5, margins 20 mm instead of 25 mm. The Typst format keeps 25 mm for now.
  • Table borders: data tables get all borders in ink plus the tinted bold header row through a new OwdTable style. Borders could not go on the plain Table style because Quarto renders caption wrappers, figure wrappers and callouts as tables on that style; a small Lua filter (docx-tables.lua) assigns OwdTable to real tables only. Verified in the rendered file: wrapper, callout and figure stay border free.

Two questions came up in review: whether the formats compute a table of
contents, and why Word kept showing a serif after the brand fonts were
installed. Neither format adds a TOC unless the document sets toc: true;
Typst computes it at render time, Word fills its TOC field on the update
prompt. Word reads the font list at start, so it needs a restart after
a font install.

Refs #5
Assisted-by: Claude claude-fable-5-1
Claude-Session: https://claude.ai/code/session_01YZGMkJRUtmXRukGv5PLhEx
@larnsce
larnsce merged commit cc1be79 into main Sep 3, 2026
4 checks passed
This was referenced Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant