use crate::object::Dict;
use crate::source::AsyncObjectSource;
use crate::tree::{resolved_dict, Entries};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LegalAttestation {
pub java_script_actions: u32,
pub launch_actions: u32,
pub uri_actions: u32,
pub movie_actions: u32,
pub sound_actions: u32,
pub hide_annotation_actions: u32,
pub go_to_remote_actions: u32,
pub alternate_images: u32,
pub external_streams: u32,
pub true_type_fonts: u32,
pub external_ref_xobjects: u32,
pub external_opi_dicts: u32,
pub non_embedded_fonts: u32,
pub dev_dep_gs_op: u32,
pub dev_dep_gs_ht: u32,
pub dev_dep_gs_tr: u32,
pub dev_dep_gs_ucr: u32,
pub dev_dep_gs_bg: u32,
pub dev_dep_gs_fl: u32,
pub annotations: u32,
pub optional_content: u32,
pub attestation: Option<String>,
}
pub async fn legal_attestation_with<S: AsyncObjectSource>(
src: &S,
trailer: &Dict,
) -> Option<LegalAttestation> {
let catalog = resolved_dict(src, trailer.get("Root")?).await?;
let dict = resolved_dict(src, catalog.get("Legal")?).await?;
let entries = Entries { src, dict: &dict };
let mut legal = LegalAttestation {
attestation: entries.text("Attestation").await,
..LegalAttestation::default()
};
for (key, count) in [
("JavaScriptActions", &mut legal.java_script_actions),
("LaunchActions", &mut legal.launch_actions),
("URIActions", &mut legal.uri_actions),
("MovieActions", &mut legal.movie_actions),
("SoundActions", &mut legal.sound_actions),
("HideAnnotationActions", &mut legal.hide_annotation_actions),
("GoToRemoteActions", &mut legal.go_to_remote_actions),
("AlternateImages", &mut legal.alternate_images),
("ExternalStreams", &mut legal.external_streams),
("TrueTypeFonts", &mut legal.true_type_fonts),
("ExternalRefXobjects", &mut legal.external_ref_xobjects),
("ExternalOPIdicts", &mut legal.external_opi_dicts),
("NonEmbeddedFonts", &mut legal.non_embedded_fonts),
("DevDepGS_OP", &mut legal.dev_dep_gs_op),
("DevDepGS_HT", &mut legal.dev_dep_gs_ht),
("DevDepGS_TR", &mut legal.dev_dep_gs_tr),
("DevDepGS_UCR", &mut legal.dev_dep_gs_ucr),
("DevDepGS_BG", &mut legal.dev_dep_gs_bg),
("DevDepGS_FL", &mut legal.dev_dep_gs_fl),
("Annotations", &mut legal.annotations),
("OptionalContent", &mut legal.optional_content),
] {
*count = entries
.value(key)
.await
.and_then(|value| value.as_int())
.and_then(|value| u32::try_from(value).ok())
.unwrap_or(0);
}
Some(legal)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::document::Document;
use pdfboss_testkit::PdfBuilder;
fn doc_with(catalog_extra: &str) -> Document {
let mut b = PdfBuilder::new();
b.object(
1,
&format!("<< /Type /Catalog /Pages 2 0 R {catalog_extra} >>"),
);
b.object(2, "<< /Type /Pages /Kids [] /Count 0 >>");
Document::load(b.build(1)).unwrap()
}
#[test]
fn reads_the_legal_attestation_counts() {
let legal = doc_with(
"/Legal << /JavaScriptActions 2 /LaunchActions 1 /URIActions 3 /MovieActions 4 \
/SoundActions 5 /HideAnnotationActions 6 /GoToRemoteActions 7 /AlternateImages 8 \
/ExternalStreams 9 /TrueTypeFonts 10 /ExternalRefXobjects 11 /ExternalOPIdicts 12 \
/NonEmbeddedFonts 13 /DevDepGS_OP 14 /DevDepGS_HT 15 /DevDepGS_TR 16 /DevDepGS_UCR 17 \
/DevDepGS_BG 18 /DevDepGS_FL 19 /Annotations 20 /OptionalContent 21 \
/Attestation (Reviewed by counsel) >>",
)
.legal_attestation()
.unwrap();
assert_eq!(
legal,
LegalAttestation {
java_script_actions: 2,
launch_actions: 1,
uri_actions: 3,
movie_actions: 4,
sound_actions: 5,
hide_annotation_actions: 6,
go_to_remote_actions: 7,
alternate_images: 8,
external_streams: 9,
true_type_fonts: 10,
external_ref_xobjects: 11,
external_opi_dicts: 12,
non_embedded_fonts: 13,
dev_dep_gs_op: 14,
dev_dep_gs_ht: 15,
dev_dep_gs_tr: 16,
dev_dep_gs_ucr: 17,
dev_dep_gs_bg: 18,
dev_dep_gs_fl: 19,
annotations: 20,
optional_content: 21,
attestation: Some("Reviewed by counsel".into()),
}
);
let sparse = doc_with("/Legal << /URIActions 3 >>")
.legal_attestation()
.unwrap();
assert_eq!(sparse.uri_actions, 3);
assert_eq!(sparse.non_embedded_fonts, 0);
assert_eq!(sparse.attestation, None);
}
#[test]
fn missing_or_malformed_legal_reads_as_absent() {
assert_eq!(doc_with("").legal_attestation(), None);
assert_eq!(doc_with("/Legal 5").legal_attestation(), None);
assert_eq!(
doc_with("/Legal << >>").legal_attestation(),
Some(LegalAttestation::default())
);
let odd = doc_with("/Legal << /URIActions (x) /Annotations -3 /Attestation 7 >>")
.legal_attestation()
.unwrap();
assert_eq!(odd.uri_actions, 0);
assert_eq!(odd.annotations, 0);
assert_eq!(odd.attestation, None);
}
}