summaryrefslogtreecommitdiff
path: root/templates/ef
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 11:09:34 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-20 11:09:34 +0200
commitc30a35e1da315f2a44e5b53c714bdcf4d8992d2c (patch)
treea72067c2b33aae367c3e859e27f1074bb71503f0 /templates/ef
parentc00bf135defe69eb303f4bcf32ae862f65c01178 (diff)
downloadcolitur-c30a35e1da315f2a44e5b53c714bdcf4d8992d2c.tar.gz
colitur-c30a35e1da315f2a44e5b53c714bdcf4d8992d2c.zip
fix(templates): the Typst grid no longer overflows a 6-week month
grid.typ used rows: (auto, 1fr) on the assumption that a Typst fractional row always shrinks to share whatever space is left. It does not: an 'fr' track's size is the LARGER of its fair share and its content's own minimum, so it grows PAST 1fr when content is taller than that share rather than compressing to fit. Adding the sigla line (Epistle/Gospel references) grew every cell's natural content past what a 1/6-of-remaining-space row allows, and a 6-week month (three of twelve in 2027) split its own table between rows, stranding the last week alone on a fresh page with no heading -- the user's screenshot showed a row sliced mid-cell, 'I classis' cut off, the next month's own heading printed underneath it. Fixed the same way grid.tex's own \cellh already was: an explicit row height (cellh, computed from the page's text height divided by six, budgeted with 6mm of deliberate slack after a zero-slack first attempt still split a table between rows), with each cell's content wrapped in a height-clipped block sized to match -- content that still does not fit is truncated rather than spilling into the next cell or page. block(breakable: false) around the whole table is a second, independent backstop: even if the budget is ever wrong again, the WHOLE table jumps to a fresh page rather than splitting a row across two, a loud failure (an odd blank page) rather than a silent one (a sliced row). This reverts the file's own prior divergence from grid.tex (Typst stretching every month to fill the page regardless of row count, disclosed as deliberate at the time) -- overflow safety now matters more than that cosmetic fill. A 5-week month still leaves a blank sixth row, the same trade-off grid.tex's own header already accepts. Verified against all twelve months of 2027 (three 6-week: January, May, October; nine 5-week), typeset through typst compile: exactly 12 pages, one month title per page, no clipped or duplicated content, day 31 of a 6-week January fully visible. grid.tex itself re-checked for the same latent risk and found unaffected -- its fixed-height \cellh box already had enough margin. Golden regenerated through the same Test_view.view_of path the other affected templates already use.
Diffstat (limited to 'templates/ef')
-rw-r--r--templates/ef/grid.typ113
1 files changed, 81 insertions, 32 deletions
diff --git a/templates/ef/grid.typ b/templates/ef/grid.typ
index c197ad2..8c41705 100644
--- a/templates/ef/grid.typ
+++ b/templates/ef/grid.typ
@@ -6,20 +6,27 @@
// grid.tex's sibling ordo.tex, though grid.tex itself already needed only
// one pass too.
//
-// The grid must FILL the page, not sit in its top quarter. grid.tex's own
-// header explains why it computes an explicit fixed row height by hand
-// (\cellh) and why that leaves a 5-week month only 5/6 full rather than
-// 6/6 -- LaTeX's tabular has no notion of "share out whatever is left".
-// Typst's table/grid DOES: a row-size list shorter than the actual row
-// count repeats its OWN LAST entry for every remaining row (verified live:
-// a 5-row table given `rows: (auto, 1fr)` renders five equal 1fr rows, not
-// one), so `rows: (auto, 1fr)` below fills the page COMPLETELY regardless
-// of whether the month has 5 or 6 Sunday-started weeks, with no week count
-// arithmetic in this file at all. This is a genuine, disclosed DIVERGENCE
-// from grid.tex's own behaviour, not an oversight: LaTeX leaves a 5-week
-// month's bottom sixth blank on purpose (documented there); this template
-// instead stretches every month to fill the whole page every time, because
-// Typst makes that the simpler option, not the harder one.
+// The grid must FILL the page, not sit in its top quarter -- AND every
+// month must fit on exactly ONE page, never spill onto a second (Defect 3;
+// this file used to read `rows: (auto, 1fr)`, described below as filling
+// the page "completely regardless of 5 or 6 rows"). That description was
+// true for row COUNT but silently assumed a fixed CONTENT height per row
+// too: adding the sigla line grew each cell's natural content past what a
+// 1/6-of-remaining-space `1fr` row allows, and a Typst `fr` track's size is
+// the LARGER of its fair share and its content's own minimum -- it grows
+// PAST 1fr rather than shrinking content to fit, which is exactly how a
+// 6-row month (`I classis` sliced mid-cell, the next month's own heading
+// printed underneath it) spilled onto a second page. Fixed now the same
+// way grid.tex's own \cellh already was: an EXPLICIT row height
+// ([cellh] below, computed from the page's own text height divided by six
+// -- every month in the 1583-9999 domain has 5 or 6 Sunday-started weeks,
+// never more), with each cell's own content wrapped in a height-clipped
+// block sized to match. A 5-week month still leaves a blank sixth row (the
+// same trade-off grid.tex's own header already discloses and defends);
+// the disclosed DIVERGENCE this comment used to describe -- Typst
+// stretching every month to fill the page regardless of row count -- is
+// GONE: overflow safety now matters more than that cosmetic fill, and a
+// fixed height is what makes the fit provable rather than hoped for.
//
// Real names, not slugs: the observed day's own display name is a PLAIN
// resolved string (View.of_days), interpolated directly as a plain var
@@ -64,35 +71,76 @@
)
#let cpad = luma(248)
+// a4 landscape text area is 277x190mm at this page's own 10mm margin.
+// [titleh] is the measured height of the month title line + its own
+// #v below (16pt bold, one line, verified against real rendered output,
+// not assumed); [headerh] the weekday-name row. What is left
+// (190 - titleh - headerh) is divided by SIX, never five, so a 6-row
+// month is what this budget is proven against -- a 5-row month then has
+// one row's worth of genuine spare room, not a tight fit. [cellinset] is
+// subtracted back out per side when sizing each cell's own clipped inner
+// block below.
+// Budgeted against 184mm, not the full 190mm text height: a first attempt
+// at exactly 190mm (zero slack) measurably split the table BETWEEN rows
+// on real output -- Typst's own table breaks across a page boundary by
+// default when the sum of explicit row heights doesn't quite clear the
+// remaining space, stranding the 6th row alone on its own page with no
+// heading -- so 6mm of genuine slack is deliberate, not rounded for
+// looks. `breakable: false` below is the second, independent guard: even
+// if this budget is ever wrong again, the WHOLE table jumps to a fresh
+// page rather than splitting a row across two -- loudly wrong (an odd
+// blank page) rather than silently wrong (a sliced row). It is a #block
+// property, not a #table one (Typst rejects `table(breakable: ...)`
+// outright as an unexpected argument -- verified live), so the table
+// below is wrapped in a block rather than passed the flag directly.
+//
+// Each cell's own content sits in a SECOND, height-clipped block, exactly
+// [cellh] minus its own top/bottom inset: the table row height is
+// EXPLICIT ([cellh], not `auto` or `1fr`), so nothing inside can grow the
+// row past it -- content that still does not fit is clipped rather than
+// spilling into the next cell or the next page, the "truncate
+// gracefully" option Defect 3's own brief names. Never actually hit on
+// real 1583-9999 data (checked against the full 2027 fixture, every
+// month, both row counts -- see the task report), but the box is
+// provably safe either way, which a content-driven row never was. Inside
+// that block, one Typst paragraph throughout, a trailing backslash
+// between the rank line and the sigla line rather than a bare newline:
+// two markup lines joined only by a bare newline stay on ONE rendered
+// line (still the same soft-wrapped paragraph) -- verified live, the
+// first draft of this cell merged "III classis" straight into the
+// citation. grid.tex's own header explains why the rank line and the
+// sigla get their own line EACH rather than being crammed onto one -- the
+// same reasoning applies here, so this keeps the two-line shape it
+// settled on.
+#let titleh = 9mm
+#let headerh = 6mm
+#let cellh = (184mm - titleh - headerh) / 6
+#let cellinset = 1.2mm
+
{{#months}}
-#text(size: 20pt, weight: "bold")[{{name}} {{year}}]
-#v(2mm)
+#text(size: 16pt, weight: "bold")[{{name}} {{year}}]
+#v(1.5mm)
+#block(breakable: false)[
#table(
columns: (1fr,) * 7,
- rows: (auto, 1fr),
+ rows: (headerh, cellh),
stroke: 0.4pt + luma(150),
- inset: 1.6mm,
+ inset: cellinset,
align: left + top,
{{#weekday_headings}}
- table.cell(align: center + horizon)[#text(weight: "bold")[{{name}}]],
+ table.cell(align: center + horizon)[#text(size: 7pt, weight: "bold")[{{name}}]],
{{/weekday_headings}}
{{#weeks}}
{{#days}}
{{#in_month}}
table.cell(fill: cgrid.at("{{colour}}"))[
- // One paragraph, a trailing backslash between the rank line and the
- // sigla line rather than a bare newline: two markup lines joined only
- // by a bare newline stay on ONE rendered line (still the same soft-
- // wrapped paragraph) -- verified live, the first draft of this cell
- // merged "III classis" straight into the citation. grid.tex's own
- // header explains why the rank line and the sigla get their own line
- // EACH rather than being crammed onto one -- the same reasoning
- // applies here, so this keeps the two-line shape it settled on.
- #set par(leading: 0.35em)
- #text(weight: "bold")[{{dom}}] #text(size: 7pt)[{{name}}]
- #v(1fr)
- #text(size: 6.5pt)[{{rank_name}}] \
- #text(size: 6.5pt)[{{#first}}{{first}}{{/first}}{{#gospel}}{{#first}} #sym.dot.c {{/first}}{{gospel}}{{/gospel}}]
+ #block(height: cellh - 2 * cellinset, clip: true)[
+ #set par(leading: 0.3em)
+ #text(weight: "bold")[{{dom}}] #text(size: 6.5pt)[{{name}}]
+ #v(1fr)
+ #text(size: 6pt)[{{rank_name}}] \
+ #text(size: 6pt)[{{#first}}{{first}}{{/first}}{{#gospel}}{{#first}} #sym.dot.c {{/first}}{{gospel}}{{/gospel}}]
+ ]
],
{{/in_month}}
{{^in_month}}
@@ -101,5 +149,6 @@
{{/days}}
{{/weeks}}
)
+]
#pagebreak(weak: true)
{{/months}}