#[cfg(test)]
mod catalog_entries_tests {
use crate::actions::Action;
use crate::document::Document;
use crate::page::Page;
use crate::page_labels::{PageLabel, PageLabelStyle, PageLabelTree};
use crate::structure::{Destination, NamedDestinations, PageDestination};
use crate::viewer_preferences::ViewerPreferences;
use crate::writer::PdfWriter;
fn serialize(document: &mut Document) -> String {
let mut buffer = Vec::new();
{
let mut writer = PdfWriter::new_with_writer(&mut buffer);
writer.write_document(document).unwrap();
}
String::from_utf8_lossy(&buffer).into_owned()
}
#[test]
fn test_write_catalog_includes_open_action() {
let mut document = Document::new();
document.add_page(Page::a4());
document.set_open_action(Action::goto(Destination::fit(PageDestination::PageNumber(
0,
))));
let content = serialize(&mut document);
assert!(
content.contains("/OpenAction"),
"catalog should emit /OpenAction when Document::open_action is set"
);
assert!(
content.contains("/S /GoTo"),
"open action dict should serialize as a /GoTo action (/S /GoTo)"
);
}
#[test]
fn test_write_catalog_includes_viewer_preferences() {
let mut document = Document::new();
document.add_page(Page::a4());
document.set_viewer_preferences(ViewerPreferences::new().hide_toolbar(true));
let content = serialize(&mut document);
assert!(
content.contains("/ViewerPreferences"),
"catalog should emit /ViewerPreferences when set on Document"
);
assert!(
content.contains("/HideToolbar true"),
"viewer prefs dict should serialize /HideToolbar true (ISO 32000-1 §12.2)"
);
}
#[test]
fn test_write_catalog_includes_named_destinations() {
let mut document = Document::new();
document.add_page(Page::a4());
let mut dests = NamedDestinations::new();
dests.add_destination(
"target".to_string(),
Destination::fit(PageDestination::PageNumber(0)).to_array(),
);
document.set_named_destinations(dests);
let content = serialize(&mut document);
assert!(
content.contains("/Names"),
"catalog should emit /Names when named destinations are set"
);
assert!(
content.contains("/Dests"),
"/Names dictionary must contain /Dests for named destinations"
);
assert!(
content.contains("(target)"),
"named destination key should appear as a string in the name tree \
(expected (target))"
);
}
#[test]
fn test_write_catalog_includes_all_four_entries_simultaneously() {
let mut document = Document::new();
document.add_page(Page::a4());
document.set_open_action(Action::goto(Destination::fit(PageDestination::PageNumber(
0,
))));
document.set_viewer_preferences(ViewerPreferences::new().hide_toolbar(true));
let mut dests = NamedDestinations::new();
dests.add_destination(
"combined-target".to_string(),
Destination::fit(PageDestination::PageNumber(0)).to_array(),
);
document.set_named_destinations(dests);
let mut labels = PageLabelTree::new();
labels.add_range(0, PageLabel::new(PageLabelStyle::DecimalArabic));
document.set_page_labels(labels);
let content = serialize(&mut document);
assert!(content.contains("/OpenAction"));
assert!(content.contains("/S /GoTo"));
assert!(content.contains("/ViewerPreferences"));
assert!(content.contains("/HideToolbar true"));
assert!(content.contains("/Names"));
assert!(content.contains("/Dests"));
assert!(content.contains("(combined-target)"));
assert!(content.contains("/PageLabels"));
assert!(content.contains("/S /D"));
}
#[test]
fn test_write_catalog_includes_page_labels() {
let mut document = Document::new();
document.add_page(Page::a4());
let mut labels = PageLabelTree::new();
labels.add_range(0, PageLabel::new(PageLabelStyle::DecimalArabic));
document.set_page_labels(labels);
let content = serialize(&mut document);
assert!(
content.contains("/PageLabels"),
"catalog should emit /PageLabels when set on Document"
);
assert!(
content.contains("/S /D"),
"decimal page label dict should serialise as /S /D per spec"
);
}
#[test]
fn test_page_labels_uppercase_roman_emits_uppercase_s_name() {
let mut document = Document::new();
document.add_page(Page::a4());
let mut labels = PageLabelTree::new();
labels.add_range(0, PageLabel::roman_uppercase());
document.set_page_labels(labels);
let content = serialize(&mut document);
assert!(
content.contains("/S /R"),
"UppercaseRoman must serialise as /S /R per ISO 32000-1 §12.4.2 \
Table 159; PDF never emitted /S /R"
);
assert!(
!content.contains("/S /r"),
"UppercaseRoman must NOT emit /S /r (that is the lowercase style); \
/S /r found in output for a label built from roman_uppercase()"
);
}
#[test]
fn test_page_labels_lowercase_roman_emits_lowercase_s_name() {
let mut document = Document::new();
document.add_page(Page::a4());
let mut labels = PageLabelTree::new();
labels.add_range(0, PageLabel::roman_lowercase());
document.set_page_labels(labels);
let content = serialize(&mut document);
assert!(
content.contains("/S /r"),
"LowercaseRoman must serialise as /S /r per ISO 32000-1 §12.4.2 \
Table 159; PDF never emitted /S /r"
);
assert!(
!content.contains("/S /R"),
"LowercaseRoman must NOT emit /S /R (that is the uppercase style); \
/S /R found in output for a label built from roman_lowercase()"
);
}
#[test]
fn test_page_labels_mixed_roman_ranges_keep_correct_case() {
let mut document = Document::new();
document.add_page(Page::a4());
document.add_page(Page::a4());
let mut labels = PageLabelTree::new();
labels.add_range(0, PageLabel::roman_uppercase());
labels.add_range(1, PageLabel::roman_lowercase());
document.set_page_labels(labels);
let content = serialize(&mut document);
assert!(
content.contains("/S /R"),
"mixed-case roman ranges must emit /S /R for the uppercase range"
);
assert!(
content.contains("/S /r"),
"mixed-case roman ranges must emit /S /r for the lowercase range"
);
}
}