1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! Wapper type for shared metadata between XMP Metadata and the `DocumentInfo` dictionary
use crate::OffsetDateTime;
use crate::{DocumentInfo, IccProfile, IccProfileType, PdfConformance, XmpMetadata};
use lopdf;
use crate::glob_defines::ICC_PROFILE_ECI_V2;
/// This is a wrapper in order to keep shared data between the documents XMP metadata and
/// the "Info" dictionary in sync
#[derive(Debug, Clone)]
pub struct PdfMetadata {
/// Creation date of the document
pub creation_date: OffsetDateTime,
/// Modification date of the document
pub modification_date: OffsetDateTime,
/// Creation date of the metadata
pub metadata_date: OffsetDateTime,
/// PDF document title
pub document_title: String,
/// PDF document author
pub author: String,
/// The creator of the document
pub creator: String,
/// The producer of the document
pub producer: String,
/// Keywords associated with the document
pub keywords: Vec<String>,
/// The subject of the document
pub subject: String,
/// Identifier associated with the document
pub identifier: String,
/// Is the document trapped?
pub trapping: bool,
/// PDF document version
pub document_version: u32,
/// PDF Standard
pub conformance: PdfConformance,
/// XMP Metadata. Is ignored on save if the PDF conformance does not allow XMP
pub xmp_metadata: XmpMetadata,
/// PDF Info dictionary. Contains metadata for this document
pub document_info: DocumentInfo,
/// Target color profile
pub target_icc_profile: Option<IccProfile>,
}
impl PdfMetadata {
/// Creates a new metadata object
pub fn new<S>(
title: S,
document_version: u32,
trapping: bool,
conformance: PdfConformance,
) -> Self
where
S: Into<String>,
{
let current_time = OffsetDateTime::now_utc();
Self {
creation_date: current_time.clone(),
modification_date: current_time.clone(),
metadata_date: current_time,
document_title: title.into(),
author: String::new(),
creator: String::new(),
producer: String::new(),
keywords: Vec::new(),
subject: String::new(),
identifier: String::new(),
trapping,
document_version,
conformance,
xmp_metadata: XmpMetadata::new(Some("default".into()), 1),
document_info: DocumentInfo::new(),
target_icc_profile: None,
}
}
/// Consumes the metadata, returning the (Option<xmp_metadata>, document_info, icc_profile_stream).
pub fn into_obj(self) -> (Option<lopdf::Object>, lopdf::Object, Option<IccProfile>) {
let metadata = self.clone();
let xmp_obj = {
if self.conformance.must_have_xmp_metadata() {
Some(self.xmp_metadata.into_obj(&metadata))
} else {
None
}
};
let doc_info_obj = self.document_info.into_obj(&metadata);
// add icc profile if necessary
let icc_profile = {
if self.conformance.must_have_icc_profile() {
match self.target_icc_profile {
Some(icc) => Some(icc),
None => Some(
IccProfile::new(ICC_PROFILE_ECI_V2.to_vec(), IccProfileType::Cmyk)
.with_alternate_profile(false)
.with_range(true),
),
}
} else {
None
}
};
(xmp_obj, doc_info_obj, icc_profile)
}
}