1extern crate alloc;
10
11use crate::annotation::{self, Annotation};
12use crate::types::LineEnding;
13use pdf_syntax::object::dict::keys::*;
14use pdf_syntax::object::{Array, Dict, Name, Rect, Stream};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum StampName {
26 Approved,
28 Experimental,
30 NotApproved,
32 AsIs,
34 Expired,
36 NotForPublicRelease,
38 Confidential,
40 Final,
42 Sold,
44 Departmental,
46 ForComment,
48 TopSecret,
50 Draft,
53 ForPublicRelease,
55 Custom(alloc::string::String),
59}
60
61impl StampName {
62 pub fn to_pdf_name(&self) -> &str {
67 match self {
68 Self::Approved => "Approved",
69 Self::Experimental => "Experimental",
70 Self::NotApproved => "NotApproved",
71 Self::AsIs => "AsIs",
72 Self::Expired => "Expired",
73 Self::NotForPublicRelease => "NotForPublicRelease",
74 Self::Confidential => "Confidential",
75 Self::Final => "Final",
76 Self::Sold => "Sold",
77 Self::Departmental => "Departmental",
78 Self::ForComment => "ForComment",
79 Self::TopSecret => "TopSecret",
80 Self::Draft => "Draft",
81 Self::ForPublicRelease => "ForPublicRelease",
82 Self::Custom(s) => s.as_str(),
83 }
84 }
85
86 pub fn from_name(name: &[u8]) -> Self {
88 match name {
89 b"Approved" => Self::Approved,
90 b"Experimental" => Self::Experimental,
91 b"NotApproved" => Self::NotApproved,
92 b"AsIs" => Self::AsIs,
93 b"Expired" => Self::Expired,
94 b"NotForPublicRelease" => Self::NotForPublicRelease,
95 b"Confidential" => Self::Confidential,
96 b"Final" => Self::Final,
97 b"Sold" => Self::Sold,
98 b"Departmental" => Self::Departmental,
99 b"ForComment" => Self::ForComment,
100 b"TopSecret" => Self::TopSecret,
101 b"Draft" => Self::Draft,
102 b"ForPublicRelease" => Self::ForPublicRelease,
103 other => {
104 let s = core::str::from_utf8(other).unwrap_or("Unknown").to_owned();
105 Self::Custom(s)
106 }
107 }
108 }
109}
110
111#[derive(Debug)]
113pub struct StampAnnotation {
114 pub name: StampName,
116}
117
118impl StampAnnotation {
119 pub fn from_annot(annot: &Annotation<'_>) -> Self {
121 let name = annot
122 .dict()
123 .get::<Name>(NAME)
124 .map(|n: Name| StampName::from_name(n.as_ref()))
125 .unwrap_or(StampName::Draft);
126 Self { name }
127 }
128}
129
130#[derive(Debug)]
132pub struct FileAttachmentAnnotation<'a> {
133 dict: Dict<'a>,
134 pub icon: alloc::string::String,
136}
137
138impl<'a> FileAttachmentAnnotation<'a> {
139 pub fn from_annot(annot: &Annotation<'a>) -> Self {
141 let dict = annot.dict().clone();
142 let icon = dict
143 .get::<Name>(NAME)
144 .map(|n: Name| alloc::string::String::from(n.as_str()))
145 .unwrap_or_else(|| alloc::string::String::from("PushPin"));
146 Self { dict, icon }
147 }
148
149 pub fn file_spec(&self) -> Option<Dict<'a>> {
151 self.dict.get::<Dict<'_>>(FS)
152 }
153
154 pub fn embedded_file(&self) -> Option<Stream<'a>> {
156 let fs = self.file_spec()?;
157 let ef = fs.get::<Dict<'_>>(EF)?;
158 ef.get::<Stream<'_>>(F).or_else(|| ef.get::<Stream<'_>>(UF))
159 }
160
161 pub fn filename(&self) -> Option<alloc::string::String> {
163 let fs = self.file_spec()?;
164 fs.get::<pdf_syntax::object::String>(UF)
165 .or_else(|| fs.get::<pdf_syntax::object::String>(F))
166 .map(|s| annotation::pdf_string_to_string(&s))
167 }
168}
169
170#[derive(Debug)]
172pub struct FreeTextAnnotation {
173 pub default_appearance: alloc::string::String,
175 pub justification: u32,
177 pub default_style: Option<alloc::string::String>,
179 pub rich_content: Option<alloc::string::String>,
181 pub callout_line: Option<Vec<f32>>,
183 pub intent: Option<alloc::string::String>,
185 pub line_ending: LineEnding,
187 pub rd: Option<Rect>,
189}
190
191impl FreeTextAnnotation {
192 pub fn from_annot(annot: &Annotation<'_>) -> Self {
194 let dict = annot.dict();
195 let default_appearance = dict
196 .get::<pdf_syntax::object::String>(DA)
197 .map(|s| annotation::pdf_string_to_string(&s))
198 .unwrap_or_default();
199 let justification = dict.get::<u32>(Q).unwrap_or(0);
200 let default_style = dict
201 .get::<pdf_syntax::object::String>(DS)
202 .map(|s| annotation::pdf_string_to_string(&s));
203 let rich_content = dict
204 .get::<pdf_syntax::object::String>(RC)
205 .map(|s| annotation::pdf_string_to_string(&s));
206 let callout_line: Option<Vec<f32>> = dict
207 .get::<Array<'_>>(CL)
208 .map(|arr: Array<'_>| arr.iter::<f32>().collect());
209 let intent = dict
210 .get::<Name>(IT)
211 .map(|n: Name| alloc::string::String::from(n.as_str()));
212 let line_ending = dict
213 .get::<Name>(LE)
214 .map(|n: Name| LineEnding::from_name(n.as_ref()))
215 .unwrap_or(LineEnding::None);
216 let rd = dict.get::<Rect>(RD);
217 Self {
218 default_appearance,
219 justification,
220 default_style,
221 rich_content,
222 callout_line,
223 intent,
224 line_ending,
225 rd,
226 }
227 }
228}
229
230#[derive(Debug)]
232pub struct SoundAnnotation {
233 pub icon: alloc::string::String,
235}
236
237impl SoundAnnotation {
238 pub fn from_annot(annot: &Annotation<'_>) -> Self {
240 let icon = annot
241 .dict()
242 .get::<Name>(NAME)
243 .map(|n: Name| alloc::string::String::from(n.as_str()))
244 .unwrap_or_else(|| alloc::string::String::from("Speaker"));
245 Self { icon }
246 }
247}
248
249#[derive(Debug)]
251pub struct MovieAnnotation {
252 pub title: Option<alloc::string::String>,
254}
255
256impl MovieAnnotation {
257 pub fn from_annot(annot: &Annotation<'_>) -> Self {
259 let title = annot
260 .dict()
261 .get::<pdf_syntax::object::String>(T)
262 .map(|s| annotation::pdf_string_to_string(&s));
263 Self { title }
264 }
265}