huh, that framing inspired me. You can actually do that*: measure how much space the table requres with auto
rows, and then put it in a fitting block with 1fr
rows:
#let t = table(
columns: 9,
// rows not specified
stroke: (paint: gray.lighten(65%)),
align: horizon,
table.cell(colspan: 2)[Number of #acrpl("GPU")],
table.vline(stroke: (paint: black)),
..range(7).map(n => [#(calc.pow(2,n))]),
table.hline(stroke: (paint: black)),
table.cell(rowspan: 3, align: horizon, rotate(-90deg, reflow: true, [Iteration time (ms)])),
[Blocking #acs("MPI")], [136.8], [69.1], [35.9], [22.1], [14.8], [15.1], [40.8],
[Non-blocking #acs("MPI")], [136.8], [68.7], [34.7], [18.2], [9.5], [5.6], [7.2],
[Masked\ #acs("MPI")], [136.8], [68.6], [34.3], [17.2], [8.7], [4.4], [3.4]
)
#layout(outer-size => {
// measure without rows configured, but constrained by available space
let size = measure(..outer-size, t)
// use set rules to change rows
set table(rows: (auto,) + 3 * (1fr,))
// render the table in a block with fixed size, so that 1fr only fills that space
block(..size, t)
})
* this has the problem that the table might actually need more space when row heights are distributed, so it’s still not perfect. For example, if one row was significantly taller than the others, then distributing height away from it will make it overflow. You can see that by adding lorem(20)
to one of the rows.
(@Andrew’s solution seems to have the same problem, but additionally his table is generally a bit taller than necessary (when there’s actually height to distribute) because he doesn’t factor in table insets.)
I think that means that there are generally two cases:
- if the leftmost column’s height is larger than what is needed for accommodating three rows as tall as the tallest one (like your example), then my (and Andrew’s) trick works
- if the sum of the rows is taller than the leftmost column, then our tricks will no longer work, and instead the row height needs to be based on the tallest cell in all three rows