mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-02 14:38:13 -06:00
This commit is contained in:
@@ -385,7 +385,9 @@ pub fn set_page_label_ranges(doc: &mut Document, ranges: &[LabelRange]) -> Resul
|
||||
spec.set(b"St".to_vec(), Object::Int(i64::from(r.first)));
|
||||
}
|
||||
nums.push(Object::Int(r.start as i64));
|
||||
nums.push(Object::Dict(spec));
|
||||
// Indirect, as Acrobat writes them: a direct dictionary is valid too, but some readers
|
||||
// (pdfannots2json, through unipdf) skip it and show physical page numbers (#1763).
|
||||
nums.push(Object::Ref(doc.add(Object::Dict(spec))));
|
||||
}
|
||||
let mut tree = Dict::new();
|
||||
tree.set(b"Nums".to_vec(), Object::Array(nums));
|
||||
|
||||
@@ -1550,6 +1550,38 @@ fn page_label_limits_validate_the_same_ranges_that_are_written() {
|
||||
assert_eq!(nums[2].as_int(), Some(1));
|
||||
}
|
||||
|
||||
/// Each label dictionary is its own object, referenced from `/Nums`, as Acrobat writes them. A
|
||||
/// direct dictionary is just as valid, but some readers (pdfannots2json, through unipdf) skip it
|
||||
/// and fall back to physical page numbers (#1763).
|
||||
#[test]
|
||||
fn page_labels_are_written_as_indirect_dictionaries() {
|
||||
use crate::{LabelRange, LabelStyle};
|
||||
let mut d = doc_a();
|
||||
let ranges = [
|
||||
LabelRange { start: 0, style: LabelStyle::LowerRoman, prefix: String::new(), first: 1 },
|
||||
LabelRange { start: 1, style: LabelStyle::Decimal, prefix: "A-".into(), first: 324 },
|
||||
LabelRange { start: 2, style: LabelStyle::None, prefix: "Cover".into(), first: 1 },
|
||||
];
|
||||
crate::set_page_label_ranges(&mut d, &ranges).unwrap();
|
||||
let back = full_roundtrip(&d);
|
||||
let catalog = back.get(back.root().unwrap());
|
||||
let tree = back.resolve(catalog.as_dict().unwrap().get(b"PageLabels").unwrap());
|
||||
let nums = tree.as_dict().unwrap().get(b"Nums").unwrap().as_array().unwrap();
|
||||
assert_eq!(nums.len(), 6);
|
||||
let expected = [(0, Some(b"r".as_slice()), None, None), (1, Some(b"D".as_slice()), Some("A-"), Some(324)), (2, None, Some("Cover"), None)];
|
||||
for (pair, (start, style, prefix, first)) in nums.chunks(2).zip(expected) {
|
||||
assert_eq!(pair[0].as_int(), Some(start));
|
||||
let Object::Ref(r) = &pair[1] else { panic!("the label of page {start} is not an indirect object: {:?}", pair[1]) };
|
||||
let spec = back.get(*r);
|
||||
let spec = spec.as_dict().unwrap();
|
||||
assert_eq!(spec.name(b"S"), style);
|
||||
assert_eq!(spec.get(b"P").and_then(|p| p.as_string()).map(|p| p.to_text()).as_deref(), prefix);
|
||||
assert_eq!(spec.int(b"St"), first);
|
||||
}
|
||||
assert_eq!(crate::page_label_ranges(&back), ranges);
|
||||
assert_eq!(crate::page_labels(&back).unwrap(), ["i", "A-324", "Cover"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn page_label_duplicate_starts_keep_the_later_range() {
|
||||
let mut d = doc_a();
|
||||
|
||||
Reference in New Issue
Block a user