use typst_library::diag::SourceResult; use typst_library::engine::Engine; use typst_library::layout::grid::resolve::{Footer, Header, Repeatable}; use typst_library::layout::{Abs, Axes, Frame, Regions}; use super::layouter::GridLayouter; use super::rowspans::UnbreakableRowGroup; impl GridLayouter<'_> { /// Layouts the header's rows. /// Skips regions as necessary. pub fn layout_header( &mut self, header: &Header, engine: &mut Engine, disambiguator: usize, ) -> SourceResult<()> { let header_rows = self.simulate_header(header, &self.regions, engine, disambiguator)?; let mut skipped_region = false; while self.unbreakable_rows_left == 0 && !self.regions.size.y.fits(header_rows.height + self.footer_height) && self.regions.may_progress() { // Advance regions without any output until we can place the // header and the footer. self.finish_region_internal(Frame::soft(Axes::splat(Abs::zero())), vec![]); skipped_region = true; } // Reset the header height for this region. // It will be re-calculated when laying out each header row. self.header_height = Abs::zero(); if let Some(Repeatable::Repeated(footer)) = &self.grid.footer { if skipped_region { // Simulate the footer again; the region's 'full' might have // changed. self.footer_height = self .simulate_footer(footer, &self.regions, engine, disambiguator)? .height; } } // Header is unbreakable. // Thus, no risk of 'finish_region' being recursively called from // within 'layout_row'. self.unbreakable_rows_left += header.end; for y in 0..header.end { self.layout_row(y, engine, disambiguator)?; } Ok(()) } /// Simulate the header's group of rows. pub fn simulate_header( &self, header: &Header, regions: &Regions<'_>, engine: &mut Engine, disambiguator: usize, ) -> SourceResult { // Note that we assume the invariant that any rowspan in a header is // fully contained within that header. Therefore, there won't be any // unbreakable rowspans exceeding the header's rows, and we can safely // assume that the amount of unbreakable rows following the first row // in the header will be precisely the rows in the header. self.simulate_unbreakable_row_group( 0, Some(header.end), regions, engine, disambiguator, ) } /// Updates `self.footer_height` by simulating the footer, and skips to fitting region. pub fn prepare_footer( &mut self, footer: &Footer, engine: &mut Engine, disambiguator: usize, ) -> SourceResult<()> { let footer_height = self .simulate_footer(footer, &self.regions, engine, disambiguator)? .height; let mut skipped_region = false; while self.unbreakable_rows_left == 0 && !self.regions.size.y.fits(footer_height) && self.regions.may_progress() { // Advance regions without any output until we can place the // footer. self.finish_region_internal(Frame::soft(Axes::splat(Abs::zero())), vec![]); skipped_region = true; } self.footer_height = if skipped_region { // Simulate the footer again; the region's 'full' might have // changed. self.simulate_footer(footer, &self.regions, engine, disambiguator)? .height } else { footer_height }; Ok(()) } /// Lays out all rows in the footer. /// They are unbreakable. pub fn layout_footer( &mut self, footer: &Footer, engine: &mut Engine, disambiguator: usize, ) -> SourceResult<()> { // Ensure footer rows have their own height available. // Won't change much as we're creating an unbreakable row group // anyway, so this is mostly for correctness. self.regions.size.y += self.footer_height; let footer_len = self.grid.rows.len() - footer.start; self.unbreakable_rows_left += footer_len; for y in footer.start..self.grid.rows.len() { self.layout_row(y, engine, disambiguator)?; } Ok(()) } // Simulate the footer's group of rows. pub fn simulate_footer( &self, footer: &Footer, regions: &Regions<'_>, engine: &mut Engine, disambiguator: usize, ) -> SourceResult { // Note that we assume the invariant that any rowspan in a footer is // fully contained within that footer. Therefore, there won't be any // unbreakable rowspans exceeding the footer's rows, and we can safely // assume that the amount of unbreakable rows following the first row // in the footer will be precisely the rows in the footer. self.simulate_unbreakable_row_group( footer.start, Some(self.grid.rows.len() - footer.start), regions, engine, disambiguator, ) } }