// colitur ordo booklet -- Typst. flavour: typst // Build: colitur table --year 2027 --template ordo.typ > ordo.typ && typst compile ordo.typ // // ONE compile pass, not two: Typst resolves its own table of contents (built // from headings via the outline function below) and every page number it // shows internally, inside a single invocation -- there is no LaTeX-style // pageref/aux-file dance and no "run it twice so the numbers settle". That is // a genuine advantage of this flavour over ordo.tex, not merely a different // syntax for the same thing. // // A5, one week per page, each day in a framed box with a colour swatch. // Every fixed string (headings, the Epistle/Gospel/Commemoration/Week // labels) comes from the view's term vocabulary rather than being written // into this file, so a translated booklet needs no template edit -- only a // different --lang. Same discipline as ordo.tex; see that file's own header // for the full reasoning, repeated only where Typst changes it below. // // The observed day's own display name is a PLAIN resolved string // (View.of_days), never a lang-keyed object, exactly as in every other // shipped flavour -- there is no dotted-la-with-slug-fallback idiom to // write here at all. // // The engine has no parent-path syntax: nested inside a month's own week // loop, a bare week-number reference finds the WEEK's own number, and there // is no way to reach the enclosing month's from there. That is why each // week object carries its own month number and month name fields // (lib/render/view.ml), used below via month_name/month_num instead of a // parent-path reference the engine cannot express. // // The engine also rejects an empty tag path outright (a bare-dot section or // variable is a parse error, not an invented "current context" behaviour), // so every loop below names a field on the object it walks rather than // trying to interpolate the object itself. // // This template's own line comments are plain text to the engine: it has no // awareness of Typst's // syntax, and a stray double-brace pair inside one // would still be parsed as a tag -- which is why this whole header, like // ordo.tex's own, is written without ever typing two curly braces next to // each other, even to name a field. // // One TOC level, not two: LaTeX's own ordo.tex prints the month name ONCE // per month with its weeks dotted-and-paginated underneath, because it // walks the months-then-weeks nesting directly in its own hand-built TOC // loop. Typst's outline instead builds its table of contents automatically // from real document headings, and there is no "is this the month's first // week" flag in the view model for a template to test -- inventing one // would be new engine state for a cosmetic grouping, not a real gap. Each // week page therefore carries ONE flat heading, "Month . Week N", which is // both what a reader sees at the top of that page and, automatically, its // own single-line entry in the table of contents below. // // Colour swatch: the six colour names this rite emits (white, red, green, // violet, rose, black -- Colour.to_string's own closed vocabulary) are used // as literal dictionary keys inside a Typst string argument below, the same // trick ordo.tex's own \swatch{licol...} macro-name concatenation relies // on. Both templates depend on that vocabulary staying plain lowercase // ASCII words with no markup metacharacter in it; the escaper still runs // over the substituted value regardless; a value outside that closed set // would need a different placement, not a different escaper. #set page( paper: "a5", margin: (inside: 14mm, outside: 10mm, top: 12mm, bottom: 12mm), numbering: "1", header: align(center)[#text(size: 8pt)[{{term.ordo}} {{year}} #sym.dot.c {{rite}}]], ) #set text(size: 9pt) #set par(justify: false) #show heading: it => { set text(size: 12pt, weight: "bold") block(above: 3mm, below: 2mm)[#it.body] } #let licol = ( white: rgb("#FFFFFF"), red: rgb("#C1272D"), green: rgb("#2E7D32"), violet: rgb("#6A1B9A"), rose: rgb("#E91E8C"), black: rgb("#000000"), ) // A framed swatch, always stroked in black regardless of fill -- so white // (and, were the page ever black, black) still reads as a mark rather than // a gap in the row. Mirrors ordo.tex's own \swatch macro. #let swatch(colour) = box(width: 2.4mm, height: 2.4mm, fill: licol.at(colour), stroke: 0.4pt + black) #align(center)[ #text(size: 18pt, weight: "bold")[{{term.ordo}} {{year}}] \ {{rite}} ] #v(4mm) #text(weight: "bold")[{{term.contents}}] #v(1mm) #outline(title: none, depth: 1, indent: 4mm) #pagebreak(weak: true) {{#months}} {{#weeks}} = {{month_name}} #sym.dot.c {{term.week}} {{num}} {{#days}} {{#in_month}} #block(width: 100%, above: 0.6mm, below: 0mm, breakable: false, stroke: 0.4pt + luma(160), inset: (x: 1.6mm, y: 0.6mm))[ // A single Typst paragraph throughout, deliberately: two markup lines // separated only by a bare newline stay on ONE rendered line (they are // still the same paragraph, soft-wrapped) -- verified live, the first // draft of this box merged the rank/colour line into the citation line // for exactly that reason. A trailing backslash forces a real line // break WITHOUT starting a new paragraph (no inter-paragraph spacing // to tune down), which is what a compact multi-line box wants; #par's // own block-level break, used further up for the day/weekday/swatch // row against the name below it, is reserved for the one place this // box actually wants a fresh paragraph. #set par(leading: 0.42em) #text(weight: "bold")[{{dom}}] #h(2mm) #text(size: 8pt)[{{weekday}}] #h(1fr) #swatch("{{colour}}") \ #text(weight: "bold")[{{name}}] \ #text(size: 7pt)[{{rank_name}} #sym.dot.c {{colour_name}}] \ #text(size: 7pt)[{{#first}}{{term.epistle}} {{first}}{{/first}}{{#gospel}}{{#first}} #h(2mm) {{/first}}{{term.gospel}} {{gospel}}{{/gospel}}]{{#comms}} \ #text(size: 7pt)[{{term.commemoration}} {{name}}]{{/comms}} ] {{/in_month}} {{/days}} #pagebreak(weak: true) {{/weeks}} {{/months}}