Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not create a block frame for out-of-flow items only #2517

Merged
merged 7 commits into from
Dec 18, 2023

Conversation

MDLC01
Copy link
Contributor

@MDLC01 MDLC01 commented Oct 30, 2023

Fixes #2297

This PR makes it so that, when a breakable block is broken into multiple frames, a frame containing only out-of-flow items (which correspond to place elements, counter updates, etc.) will move its items to the next frame if there is one.

Please review this carefully! I was not familiar with this part of the codebase when I started working on that, so please make sure I did not write anything stupid. All tests pass, so it should be ok, but there are still some things I'm not fully confident about (see comments). In particular, because this fix is implemented by modifying the way FlowElem is laid out, I think it may affect other things (although I could not think of any).

@laurmaedje laurmaedje added the waiting-on-review This PR is waiting to be reviewed. label Nov 7, 2023
crates/typst-library/src/layout/flow.rs Outdated Show resolved Hide resolved
crates/typst-library/src/layout/flow.rs Outdated Show resolved Hide resolved
@laurmaedje laurmaedje removed the waiting-on-review This PR is waiting to be reviewed. label Nov 21, 2023
@laurmaedje
Copy link
Member

Just removing the last frame seems like a strange thing to do. It probably happens to work because the next frame is too large to fit into the first region and the parent flow handles it, but generally a multi-region layouter should produce one frame per region.

One thing that might work is to mem::take the only-out-of-flow items at the start of finish_region to effectively create a completely empty frame and then, once that's done readd them to the current items. This way, you also wouldn't need a new field in the layouter.

@laurmaedje laurmaedje added the waiting-on-author Pull request waits on author label Nov 21, 2023
@MDLC01
Copy link
Contributor Author

MDLC01 commented Nov 26, 2023

Just removing the last frame seems like a strange thing to do.

I agree that completely removing frames is weird. The problem is that, if I push Frame::new(Size::zero(), FrameKind::Soft) after pop()-ing a frame, it adds an empty page in the last test in out-of-flow-in-block.

One thing that might work is to mem::take the only-out-of-flow items at the start of finish_region to effectively create a completely empty frame and then, once that's done readd them to the current items. This way, you also wouldn't need a new field in the layouter.

I'm not sure how to do that, because at at the start of finish_region, we no longer know whether the previous frame contained only out-of-flow elements (we would still need a boolean field, which I agree is better than cloning the elements).

@laurmaedje
Copy link
Member

I'm not sure how to do that, because at at the start of finish_region, we no longer know whether the previous frame contained only out-of-flow elements (we would still need a boolean field, which I agree is better than cloning the elements).

What I meant is that rather than writing and then removing the frame with only out-of-flow elements, just don't create the frame at all, effectively deferring writing of the only out-of-flow elements. (Perhaps with the exception of the final finish_region call where we need to ensure that they are written.)

@MDLC01
Copy link
Contributor Author

MDLC01 commented Dec 3, 2023

Sorry for the delayed response. This is indeed smarter, although I now have this problem where there is an additional blank page in the last test.

By removing the following lines, I can show the first frame even if it is empty (this part is unrelated to my PR, it is just a graphical thing). In the last test, the first frame is actually displaced in the second region, which is what causes the issue.

if let [first, rest @ ..] = frames.as_slice() {
skip = first.is_empty() && rest.iter().any(|frame| !frame.is_empty());
}

The first frame appears in the second region, the second in the third, etc..

I will try to rebase on main and understand why this happens this week, but I suspect this might be the consequence of another bug.

@MDLC01
Copy link
Contributor Author

MDLC01 commented Dec 6, 2023

Turns out setting the frame size to self.initial instead of Size::zero() solved the issue of the last test not passing. Now all tests pass.

I will try to resolve the conflicts and solve the issue that frames with size zero are not necessarily out-of-flow, and then this will be ready to merge. In the meantime, I'm switching this PR to a draft.

@MDLC01 MDLC01 marked this pull request as draft December 6, 2023 12:01
# Conflicts:
#	crates/typst/src/layout/flow.rs
@laurmaedje laurmaedje removed the waiting-on-author Pull request waits on author label Dec 6, 2023
@MDLC01 MDLC01 marked this pull request as ready for review December 10, 2023 12:51
@MDLC01
Copy link
Contributor Author

MDLC01 commented Dec 10, 2023

I think this should now be safe to merge!

@laurmaedje laurmaedje merged commit 356bdeb into typst:main Dec 18, 2023
4 checks passed
@laurmaedje
Copy link
Member

Thank you, also for the patience!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Elements with no content can force a breakable block to create an empty frame
2 participants