1extern crate alloc;
4
5use crate::annotation::{self, Annotation};
6use crate::types::LineEnding;
7use pdf_syntax::object::dict::keys::*;
8use pdf_syntax::object::{Array, Dict, Name, Rect, Stream};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
19pub enum StampName {
20 Approved,
22 Experimental,
24 NotApproved,
26 AsIs,
28 Expired,
30 NotForPublicRelease,
32 Confidential,
34 Final,
36 Sold,
38 Departmental,
40 ForComment,
42 TopSecret,
44 Draft,
47 ForPublicRelease,
49 Custom(alloc::string::String),
53}
54
55impl StampName {
56 pub fn to_pdf_name(&self) -> &str {
61 match self {
62 Self::Approved => "Approved",
63 Self::Experimental => "Experimental",
64 Self::NotApproved => "NotApproved",
65 Self::AsIs => "AsIs",
66 Self::Expired => "Expired",
67 Self::NotForPublicRelease => "NotForPublicRelease",
68 Self::Confidential => "Confidential",
69 Self::Final => "Final",
70 Self::Sold => "Sold",
71 Self::Departmental => "Departmental",
72 Self::ForComment => "ForComment",
73 Self::TopSecret => "TopSecret",
74 Self::Draft => "Draft",
75 Self::ForPublicRelease => "ForPublicRelease",
76 Self::Custom(s) => s.as_str(),
77 }
78 }
79
80 pub fn from_name(name: &[u8]) -> Self {
82 match name {
83 b"Approved" => Self::Approved,
84 b"Experimental" => Self::Experimental,
85 b"NotApproved" => Self::NotApproved,
86 b"AsIs" => Self::AsIs,
87 b"Expired" => Self::Expired,
88 b"NotForPublicRelease" => Self::NotForPublicRelease,
89 b"Confidential" => Self::Confidential,
90 b"Final" => Self::Final,
91 b"Sold" => Self::Sold,
92 b"Departmental" => Self::Departmental,
93 b"ForComment" => Self::ForComment,
94 b"TopSecret" => Self::TopSecret,
95 b"Draft" => Self::Draft,
96 b"ForPublicRelease" => Self::ForPublicRelease,
97 other => {
98 let s = core::str::from_utf8(other).unwrap_or("Unknown").to_owned();
99 Self::Custom(s)
100 }
101 }
102 }
103}
104
105#[derive(Debug)]
107pub struct StampAnnotation {
108 pub name: StampName,
110}
111
112impl StampAnnotation {
113 pub fn from_annot(annot: &Annotation<'_>) -> Self {
115 let name = annot
116 .dict()
117 .get::<Name>(NAME)
118 .map(|n: Name| StampName::from_name(n.as_ref()))
119 .unwrap_or(StampName::Draft);
120 Self { name }
121 }
122}
123
124#[derive(Debug)]
126pub struct FileAttachmentAnnotation<'a> {
127 dict: Dict<'a>,
128 pub icon: alloc::string::String,
130}
131
132impl<'a> FileAttachmentAnnotation<'a> {
133 pub fn from_annot(annot: &Annotation<'a>) -> Self {
135 let dict = annot.dict().clone();
136 let icon = dict
137 .get::<Name>(NAME)
138 .map(|n: Name| alloc::string::String::from(n.as_str()))
139 .unwrap_or_else(|| alloc::string::String::from("PushPin"));
140 Self { dict, icon }
141 }
142
143 pub fn file_spec(&self) -> Option<Dict<'a>> {
145 self.dict.get::<Dict<'_>>(FS)
146 }
147
148 pub fn embedded_file(&self) -> Option<Stream<'a>> {
150 let fs = self.file_spec()?;
151 let ef = fs.get::<Dict<'_>>(EF)?;
152 ef.get::<Stream<'_>>(F).or_else(|| ef.get::<Stream<'_>>(UF))
153 }
154
155 pub fn filename(&self) -> Option<alloc::string::String> {
157 let fs = self.file_spec()?;
158 fs.get::<pdf_syntax::object::String>(UF)
159 .or_else(|| fs.get::<pdf_syntax::object::String>(F))
160 .map(|s| annotation::pdf_string_to_string(&s))
161 }
162}
163
164#[derive(Debug)]
166pub struct FreeTextAnnotation {
167 pub default_appearance: alloc::string::String,
169 pub justification: u32,
171 pub default_style: Option<alloc::string::String>,
173 pub rich_content: Option<alloc::string::String>,
175 pub callout_line: Option<Vec<f32>>,
177 pub intent: Option<alloc::string::String>,
179 pub line_ending: LineEnding,
181 pub rd: Option<Rect>,
183}
184
185impl FreeTextAnnotation {
186 pub fn from_annot(annot: &Annotation<'_>) -> Self {
188 let dict = annot.dict();
189 let default_appearance = dict
190 .get::<pdf_syntax::object::String>(DA)
191 .map(|s| annotation::pdf_string_to_string(&s))
192 .unwrap_or_default();
193 let justification = dict.get::<u32>(Q).unwrap_or(0);
194 let default_style = dict
195 .get::<pdf_syntax::object::String>(DS)
196 .map(|s| annotation::pdf_string_to_string(&s));
197 let rich_content = dict
198 .get::<pdf_syntax::object::String>(RC)
199 .map(|s| annotation::pdf_string_to_string(&s));
200 let callout_line: Option<Vec<f32>> = dict
201 .get::<Array<'_>>(CL)
202 .map(|arr: Array<'_>| arr.iter::<f32>().collect());
203 let intent = dict
204 .get::<Name>(IT)
205 .map(|n: Name| alloc::string::String::from(n.as_str()));
206 let line_ending = dict
207 .get::<Name>(LE)
208 .map(|n: Name| LineEnding::from_name(n.as_ref()))
209 .unwrap_or(LineEnding::None);
210 let rd = dict.get::<Rect>(RD);
211 Self {
212 default_appearance,
213 justification,
214 default_style,
215 rich_content,
216 callout_line,
217 intent,
218 line_ending,
219 rd,
220 }
221 }
222}
223
224#[derive(Debug)]
226pub struct SoundAnnotation {
227 pub icon: alloc::string::String,
229}
230
231impl SoundAnnotation {
232 pub fn from_annot(annot: &Annotation<'_>) -> Self {
234 let icon = annot
235 .dict()
236 .get::<Name>(NAME)
237 .map(|n: Name| alloc::string::String::from(n.as_str()))
238 .unwrap_or_else(|| alloc::string::String::from("Speaker"));
239 Self { icon }
240 }
241}
242
243#[derive(Debug)]
245pub struct MovieAnnotation {
246 pub title: Option<alloc::string::String>,
248}
249
250impl MovieAnnotation {
251 pub fn from_annot(annot: &Annotation<'_>) -> Self {
253 let title = annot
254 .dict()
255 .get::<pdf_syntax::object::String>(T)
256 .map(|s| annotation::pdf_string_to_string(&s));
257 Self { title }
258 }
259}