typst
typst copied to clipboard
`scale`-d grid doesn't get correctly layouted
Description
#set page(height: 2in)
#let body = for ii in range(5) [
A heading
#grid(lorem((ii + 1) * 10))
]
#scale(body, x: 50%, y: 50%, origin: top + left)
Produces
When it should produce no overlapping text
Reproduction URL
No response
Operating system
No response
Typst version
- [X] I am using the latest version of Typst
This also applies to lists and tables. The problem seems to be that elements using GridLayouter always break after the total page/region height has been spanned, even if they're inside an unbreakable block (in this case, scale doesn't allow breaking - the same occurs inside figure, for example). In contrast, adding #lorem(400) correctly keeps going inside scale or unbreakable blocks even after the total page height has been spanned. Perhaps there is a way for grid-like elements to be aware if they're inside an unbreakable block, and thus assume an "infinite page height"? Can we pass this information somehow, maybe through Regions? (cc @laurmaedje for possible ideas)
Possibly related: #2073
I think the grid layouter is missing calls to self.regions.in_last(). The flow layouter, for instance, won't finish the region if it knows that it is in the last region.
Thanks for the hint! I'll take a look to see if I can fix this soon. (Maybe after I'm done with the Part 2b PR.)
One important part of the problem is regarding the height given to cells in measure_auto_row. Making an auto row unbreakable by checking self.regions.in_last() isn't enough, since that condition is necessary but not sufficient for the grid to be inside an unbreakable box (and would thus cause regressions on non-unbreakable grids). Instead, one must manually check for self.regions.backlog.is_empty() && self.regions.last.is_none() i.e. ensure there's really no way forward.
But just making the row unbreakable is still not enough: the available height (self.regions.size.y) also matters. Setting the available height to infinity made a simple example work (but still not the one at the original post):
Code
Hello world!
#block(breakable: false, height: 1em, table(
[a],
[b],
[c],
[d],
[e],
))
Wow, cool equations!
Thanks!
Without the change, the table's last rows would be compressed into the same location.
With that said, I wouldn't be too eager to change the behavior demonstrated above, as it could be intended; I used a block just as a way to portray the behavior within a page of 1em height (where it also works).
But even that still doesn't fix scale for some reason. I'll try to investigate more soon.
Thinking further, I think the problem in the OP has (almost) nothing to do with grids (despite the issue I mention in the previous comment, which exists but seems to be unrelated). A heading is clearly not in any grid, and still behaves that way. But it's still related to my observation regarding measuring with infinite height. The following patch makes the OP's example appear to behave correctly:
diff --git a/crates/typst/src/layout/transform.rs b/crates/typst/src/layout/transform.rs
index 13ff66ae..b75c7741 100644
--- a/crates/typst/src/layout/transform.rs
+++ b/crates/typst/src/layout/transform.rs
@@ -387,7 +387,7 @@ fn measure_and_layout(
}
// Measure the size of the body.
- let pod = Regions::one(size, Axes::splat(false));
+ let pod = Regions::one(Axes::new(size.x, Abs::inf()), Axes::splat(false));
let frame = body.measure(engine, styles, pod)?.into_frame();
// Actually perform the layout.
Results in:
The patch is, of course, naive, because it appears to break the reflow option. But it demonstrates that perhaps we should use infinite height when measuring unbreakable things which don't affect layout.
I think I might've just ran into a similar issue or at least some related behaviour regarding reflow when trying to scale a figure to page width (like in https://github.com/typst/typst/discussions/1256): I didn't want the figure's caption to be scaled down with the actual image (which is a cetz canvas), so I applied a scale to just the figure contents.
With reflow: true, the figure overlaps with the following heading, paragraph, and subsequent figure (a following table). Even with a manual #pagebreak() before the new section, the scaled figure's caption is rendered on top of (instead of below) the canvas. Using reflow: false, the figure renders on its own page, with the caption placed at the very bottom of the page (presumably because that's where it would be if the content wasn't scaled, the canvas is quite large).