Here’s an implementation of the bisection algorithm I was referring to:
#let fill-height-with-text(min: 0.3em, max: 5em, eps: 0.1em, it) = layout(size => {
let fits(text-size, it) = {
measure(width: size.width, { set text(text-size); it }).height <= size.height
}
if not fits(min, it) { panic("Content doesn't fit even at minimum text size") }
if fits(max, it) { set text(max); it }
let (a, b) = (min, max)
while b - a > eps {
let new = 0.5 * (a + b)
if fits(new, it) {
a = new
} else {
b = new
}
}
set text(a)
it
})
#block(height: 5cm, fill: luma(80%), fill-height-with-text(lorem(10)))
#block(height: 5cm, fill: luma(80%), columns(2)[
#fill-height-with-text(lorem(10))
#fill-height-with-text(lorem(7))
])