mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
Page numbering adds the start number to the position of the page in the run. At the top of the u32 range that addition wrapped, so a release build saved zero for the second page and still reported success, and a debug build hit a caught overflow error instead. Check the whole run before drawing anything. When the last page number would pass the u32 limit the operation returns an actionable error and the document keeps no header, rather than a half applied run. Fixes #804. Co-authored-by: Big Pickle <noreply@opencode.ai>
This commit is contained in:
+16
-1
@@ -414,6 +414,20 @@ pub fn add_header_footer(doc: &mut Document, pages: &[usize], hf: &HeaderFooter,
|
||||
if !(hf.font_size.is_finite() && hf.font_size > 0.0 && hf.font_size <= 200.0 && hf.margins.iter().all(|m| m.is_finite() && *m >= 0.0)) {
|
||||
return Err(EditError::Invalid("invalid font size or margins".into()));
|
||||
}
|
||||
// Page numbers count up from start_number, so the last selected page needs the most room.
|
||||
// Refuse the whole run before drawing anything: headers applied to some pages and then a
|
||||
// failed page number would leave the document half changed.
|
||||
let last_number = u64::from(hf.start_number)
|
||||
.checked_add(pages.len().saturating_sub(1) as u64)
|
||||
.ok_or_else(|| EditError::Invalid("the header page numbers are too large".into()))?;
|
||||
if last_number > u64::from(u32::MAX) {
|
||||
return Err(EditError::Invalid(format!(
|
||||
"the header start number {} leaves no room for {} pages (page numbers stop at {})",
|
||||
hf.start_number,
|
||||
pages.len(),
|
||||
u32::MAX
|
||||
)));
|
||||
}
|
||||
if replace {
|
||||
remove_marks(doc, pages, MarkKind::HeaderFooter)?;
|
||||
}
|
||||
@@ -423,7 +437,8 @@ pub fn add_header_footer(doc: &mut Document, pages: &[usize], hf: &HeaderFooter,
|
||||
for (k, &i) in pages.iter().enumerate() {
|
||||
let page = &all[i];
|
||||
let (w, h) = page.display_size(doc);
|
||||
let number = hf.start_number + k as u32;
|
||||
let number =
|
||||
u32::try_from(u64::from(hf.start_number) + k as u64).map_err(|_| EditError::Invalid("the header page numbers are too large".into()))?;
|
||||
let bates = bates0 + k as u64;
|
||||
let size = hf.font_size;
|
||||
let [top, bottom, left, right] = hf.margins;
|
||||
|
||||
@@ -96,6 +96,25 @@ fn header_and_footer_are_drawn_in_display_space_and_wrap_the_original_content()
|
||||
assert_eq!(marks_present(&doc), [MarkKind::HeaderFooter]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn header_start_number_that_would_wrap_is_refused_without_drawing() {
|
||||
// u32::MAX is a valid start_number on its own, but there is no room for a second page
|
||||
// number after it. The old code wrapped the second page to zero; now the whole run is
|
||||
// refused and the document keeps no header at all.
|
||||
let mut doc = fixture();
|
||||
let hf = HeaderFooter {
|
||||
text: [String::new(), String::new(), "<<1>>".into(), String::new(), String::new(), String::new()],
|
||||
start_number: u32::MAX,
|
||||
..HeaderFooter::default()
|
||||
};
|
||||
let err = add_header_footer(&mut doc, &[0, 1], &hf, false, &cx()).unwrap_err();
|
||||
assert!(err.to_string().contains("leaves no room"), "{err}");
|
||||
assert!(marks_present(&doc).is_empty(), "nothing is drawn when the run is refused");
|
||||
// A single page still fits the start number itself.
|
||||
add_header_footer(&mut doc, &[0], &hf, false, &cx()).unwrap();
|
||||
assert_eq!(marks_present(&doc).len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn replace_and_remove_restore_the_original_content() {
|
||||
let mut doc = fixture();
|
||||
|
||||
Reference in New Issue
Block a user