diff --git a/crates/layout/src/fields.rs b/crates/layout/src/fields.rs index d4946c2f1..2b56405a9 100644 --- a/crates/layout/src/fields.rs +++ b/crates/layout/src/fields.rs @@ -63,8 +63,8 @@ impl FieldCtx { self.page_from.iter().rev().find(|(from, _)| *from <= off).map_or(self.page, |(_, page)| *page) } /// A key for caches: the parts of the context a page-dependent paragraph depends on. - pub fn page_key(&self) -> (u32, u32, u32, u32, u64) { - (self.page, self.pages, self.section_pages, self.section, self.bookmarks_key) + pub fn page_key(&self) -> (u32, u32, u32, u32, u64, NumFormat) { + (self.page, self.pages, self.section_pages, self.section, self.bookmarks_key, self.page_format) } /// A key for caches of paragraph `p` with STYLEREF fields (0 when they show their stored /// result): a hash of the results its STYLEREF fields have on this page, so an edit to body diff --git a/crates/layout/src/lib.rs b/crates/layout/src/lib.rs index 62724bd8b..1bc00495a 100644 --- a/crates/layout/src/lib.rs +++ b/crates/layout/src/lib.rs @@ -503,7 +503,7 @@ struct Key { rev: u64, width: u32, label: Option, - page: Option<(u32, u32, u32, u32, u64)>, + page: Option<(u32, u32, u32, u32, u64, wordcraft_doc::section::NumFormat)>, /// The pages a split paragraph's fields are on after its first (`FieldCtx::page_from`). page_from: u64, table: u64, diff --git a/crates/layout/src/tests_pages.rs b/crates/layout/src/tests_pages.rs index b577d1a5a..cda6e1a96 100644 --- a/crates/layout/src/tests_pages.rs +++ b/crates/layout/src/tests_pages.rs @@ -530,3 +530,41 @@ fn footer_border_limits_body() { assert!(page.body.bottom() <= top + 0.01, "body {}, footer stroke {top}", page.body.bottom()); } } + +/// A section numbering its pages in lower-case roman shows them so in its footer when the body +/// shows the page count (`NUMPAGES`): the footer laid out while paginating, before the section's +/// number format is known, isn't taken for the page's own footer. +#[test] +fn a_roman_page_number_in_the_footer_stays_roman_with_the_page_count_in_the_body() { + let field = |instr: &str| InlineObject::Field { + instr: instr.into(), + result: String::new(), + locked: false, + dirty: false, + code: false, + form: String::new(), + }; + let mut d = Document::new(); + let mut fp = Paragraph::new(); + fp.insert_object(0, field("PAGE"), &Default::default()).unwrap(); + let id = d.add_part(PartKind::Footer, vec![para_block(fp)]); + d.last_section.footers.default = Some(id); + let mut np = Paragraph::with_text(" pages", Default::default()); + np.insert_object(0, field("NUMPAGES"), &Default::default()).unwrap(); + d.body = vec![para_block(np)]; + d.body.extend(paras(&"word ".repeat(40), 40)); + let mut end = Paragraph::with_text("end", Default::default()); + end.section = Some(Box::new(d.last_section.clone())); + d.body.push(para_block(end)); + d.body.extend(paras(&"word ".repeat(40), 120)); + d.last_section.page_num_start = Some(1); + d.last_section.page_num_format = wordcraft_doc::section::NumFormat::LowerRoman; + let l = lay(&d); + let roman: Vec<_> = l.pages.iter().filter(|p| p.section == 1).collect(); + assert!(roman.len() > 5); + for p in roman { + let glyphs: usize = p.footer.iter().map(|it| if let Placed::Lines { para, .. } = it { para.glyphs.len() } else { 0 }).sum(); + let want = wordcraft_doc::section::NumFormat::LowerRoman.format(p.number); + assert_eq!(glyphs, want.chars().count(), "page {}: the footer should show {want}", p.number); + } +}