// 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, CONTINUOUS (Defect 2): no forced page break per week any more -- // see ordo.tex's own header for the measured "why" (a full 7-day week // already fills most of an A5 page even tightened, so the real saving // comes from the year's many SHORT boundary weeks no longer wasting a // whole page each). 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, month name and abbreviated // month name fields (lib/render/view.ml), used below via month_name/ // month_num/month_abbr instead of a parent-path reference the engine // cannot express. The same view resolves the week's own Roman numeral too // (num_roman, arabic num kept alongside it for a tradition that wants it), // the day-of-month of its first and last in-month day (first_dom/ // last_dom), and single_day (true for a one-day week) -- the punctuation // choice between Ian 1 and Ian 1-2 this logic-less engine cannot make for // itself, so the heading below picks the dash only inside an inverted // single_day section. // // 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, so this stays a flat list of week headings // rather than being restructured into two heading levels -- changing // outline depth/structure is a bigger, riskier change than this defect // calls for. Each heading is the week's own Roman numeral plus the span // of in-month dates it covers (e.g. Hebdomada I (Ian 1-2)), with no month // name repeated -- the month heading below is a separate, non-heading // BANNER (not part of the outline) that already establishes it once per // month. Defect 2 DID add a "is this the month's first week" // flag to the view model (lib/render/view.ml's own [first], the same // "cheap flag beats invented template logic" call [last] already made // elsewhere) -- used below for a separate, non-heading month BANNER // printed just above that first week's own heading, not by restructuring // the heading/outline itself. The banner's own sticky:true (separate from // the heading show-rule's below) is needed for the identical reason: an // early draft without it put "Aprilis 2027" alone at the very bottom of // a page with the entire month below it on the next one -- not caught by // a check for an orphaned HEADING (a banner is not one), only found by // looking at the rendered page. Chaining two sticky blocks keeps // banner+heading+first day box moving together. // // Each day box is tightened from an earlier draft (0.6mm inset, 0.42em // leading, 7pt meta text on its own separate rank/colour line) for this // same continuous flow -- see ordo.tex's own header for the measured // reasoning, identical here: a full week already fills most of a page, // so folding rank/colour onto the SAME line as the citations (this box // is far wider than it is tall) is what buys back a second week's worth // of room, not the font/inset trim alone. // // 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_name}}]], ) #set text(size: 9pt) #set par(justify: false) // sticky: true keeps a heading from being stranded alone at a page's // bottom with its own week's first day box pushed to the next page -- // Typst's own built-in answer to what ordo.tex needs \needspace (an // external package) for. Verified live: a heading is provably never the // LAST thing on a page anywhere in the rendered output (the task report // has the scan). #show heading: it => { set text(size: 12pt, weight: "bold") block(above: 3mm, below: 2mm, sticky: true)[#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_name}} ] #v(4mm) #text(weight: "bold")[{{term.contents}}] #v(1mm) #outline(title: none, depth: 1, indent: 4mm) #pagebreak(weak: true) {{#months}} {{#weeks}} {{#first}} #block(above: 2mm, below: 0mm, sticky: true)[ #line(length: 100%, stroke: 0.6pt + luma(120)) #text(size: 15pt, weight: "bold")[{{month_name}} {{year}}] ] {{/first}} = {{term.week}} {{num_roman}} ({{month_abbr}} {{first_dom}}{{^single_day}}--{{last_dom}}{{/single_day}}) {{#days}} {{#in_month}} #block(width: 100%, above: 0.4mm, below: 0mm, breakable: false, stroke: 0.4pt + luma(160), inset: (x: 1.6mm, y: 0.4mm))[ #set par(leading: 0.36em) #text(weight: "bold")[{{dom}}] #h(2mm) #text(size: 7pt)[{{weekday}}] #h(1fr) #swatch("{{colour}}") \ #text(weight: "bold")[{{name}}] \ #text(size: 6.5pt)[{{rank_name}} #sym.dot.c {{colour_name}}{{#first}} #h(2mm) {{term.epistle}} {{first}}{{/first}}{{#gospel}} #h(2mm) {{term.gospel}} {{gospel}}{{/gospel}}]{{#comms}} \ #text(size: 6.5pt)[{{term.commemoration}} {{name}}]{{/comms}} ] {{/in_month}} {{/days}} {{/weeks}} {{/months}}