I’m considering typst for the follwing use case:
Given a dataset in the form of a csv file I’d like to produce a PDF with one page per record. Each field of a record is placed at a specific location on the page inside a container (box or block) element with a predefined size.
There are edge cases where the input data is long enough to overflow the container. Since the produced PDF can contain about a thousand pages, I’d like to automate the detection of overfull containers so that I don’t have to scan visually the PDF, by manually scrolling through it.
If typst could warn me about those cases I could intercept the diagnostics and tell the user on which page the problem occurs.
There is nothing strange about it. Let me elaborate a bit more:
Imagine you want to print a lot of badges and you have a set of A4 template sheet papers to print on. You have to print onto a certain area of the A4 paper, where you pull off the self-adhesive part and then fold it. There is a design of said badge which defines things like:
“The name goes exactly here and the affiliation goes exactly there.”
Each of those fields have a per-design specified position and maximum size. This means if the content is to large we simply run out of room to print on. This happens e.g. when people provide a very long affiliation information which then necessitates manual abbreviation.
The data loading and iterative page build is something I omitted here, because I already know that this is easily possible with typst.
Thank you for your time. These are some interesting examples but my key problem is not addressed there.
With regards to the use of panic() I first have to figure out a way to detect the overfull condition in the first place. Maybe with measure() somehow.
Ok, I found some solutions which solve my original question in three different ways. I combined them all into one example so anyone can pick and choose what fits best for them:
In-Document-Warning (Summary): use a state to collect and typeset a warning summary
In-Document-Warning (Outline): use invisible headings to record the warnings into the outline
Metadata: put the warning into a metadata label which can then be queried from the CLI
// file: overfull.typ
// --- page setup ---
#set page(width: 10cm, height: 6cm)
// --- example dataset ---
#let records = (
(firstname: "Alice", lastname: "Anderson", affiliation: "ACME"),
(firstname: "Bob", lastname: "Baker", affiliation: "A Company that Manufactures Everything\ - Sales Department -"),
)
// In-Document-Warning (Summary): create a state
#let overfull_box_on_pages = state("overfull", ())
// --- overfull aware box (vertical limit detection) ---
#let box_vlim(body, width: auto, height: auto, highlight: red, ..box_args) = layout(
parent_size => {
let width = width
if width == auto {
width = parent_size.width
}
let boxed = box(body, width: width, ..box_args)
let size = measure(boxed)
if type(height) == length and size.height.mm() > height.to-absolute().mm() {
boxed = box(body, width: width, height: height, fill: highlight, ..box_args)
// Metadata: this can be queried from the CLI with:
// `typst query overfull.typ "<overfull>" --field value`
[#metadata(here().page())<overfull>]
// In-Document-Warning (Summary): collect page information into state
let pages = overfull_box_on_pages.get()
pages.push(here().page())
overfull_box_on_pages.update(pages)
// In-Document-Warning (Outline): create an invisible heading which shows up in the outline
// https://forum.typst.app/t/how-to-add-an-invisible-heading-to-an-outline/685/2?u=jacob
{
show heading: none
heading(numbering: none)[Overfull Box]
}
}
boxed
})
// interpret dataset fields as markup
#let typeset(src) = {
eval(src, mode: "markup")
}
// construct document page by page
#for i in range(records.len()) {
box_vlim(height: 2em)[
#typeset(records.at(i).firstname)\
#typeset(records.at(i).lastname)
]+"\n"
box_vlim(height: 1em)[
#typeset(records.at(i).affiliation)
]
if i < (records.len() - 1) {
pagebreak()
}
}
// In-Document-Warning (Outline, Summary): show outline and summary
#context {
let pages = overfull_box_on_pages.final()
if pages != () {
pagebreak()
outline(title: "Warnings")
pagebreak()
[= Warnings Summary]
[overfull box on pages: #pages]
}
}