1use crate::error::*;
2use crate::object::{PlainRef, Resolve, Object, NoResolve, ObjectWrite, Updater, DeepClone, Cloner};
3
4use std::sync::Arc;
5use std::{str, fmt, io};
6use std::ops::{Index, Range};
7use std::ops::Deref;
8use std::convert::TryInto;
9use std::borrow::{Borrow, Cow};
10use indexmap::IndexMap;
11use itertools::Itertools;
12use istring::{SmallString, IBytes};
13use datasize::DataSize;
14
15#[derive(Clone, Debug, PartialEq)]
16pub enum Primitive {
17 Null,
18 Integer (i32),
19 Number (f32),
20 Boolean (bool),
21 String (PdfString),
22 Stream (PdfStream),
23 Dictionary (Dictionary),
24 Array (Vec<Primitive>),
25 Reference (PlainRef),
26 Name (SmallString),
27}
28impl DataSize for Primitive {
29 const IS_DYNAMIC: bool = true;
30 const STATIC_HEAP_SIZE: usize = std::mem::size_of::<Self>();
31
32 fn estimate_heap_size(&self) -> usize {
33 match self {
34 Primitive::String(ref s) => s.estimate_heap_size(),
35 Primitive::Stream(ref s) => s.estimate_heap_size(),
36 Primitive::Dictionary(ref d) => d.estimate_heap_size(),
37 Primitive::Array(ref arr) => arr.estimate_heap_size(),
38 Primitive::Name(ref s) => s.estimate_heap_size(),
39 _ => 0
40 }
41 }
42}
43
44impl fmt::Display for Primitive {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 match self {
47 Primitive::Null => write!(f, "null"),
48 Primitive::Integer(i) => i.fmt(f),
49 Primitive::Number(n) => n.fmt(f),
50 Primitive::Boolean(b) => b.fmt(f),
51 Primitive::String(ref s) => write!(f, "{:?}", s),
52 Primitive::Stream(_) => write!(f, "stream"),
53 Primitive::Dictionary(ref d) => d.fmt(f),
54 Primitive::Array(ref arr) => write!(f, "[{}]", arr.iter().format(", ")),
55 Primitive::Reference(r) => write!(f, "@{}", r.id),
56 Primitive::Name(ref s) => write!(f, "/{}", s)
57 }
58 }
59}
60impl Primitive {
61 pub fn serialize(&self, out: &mut impl io::Write) -> Result<()> {
62 match self {
63 Primitive::Null => write!(out, "null")?,
64 Primitive::Integer(i) => write!(out, "{}", i)?,
65 Primitive::Number(n) => write!(out, "{}", n)?,
66 Primitive::Boolean(b) => write!(out, "{}", b)?,
67 Primitive::String(ref s) => s.serialize(out)?,
68 Primitive::Stream(ref s) => s.serialize(out)?,
69 Primitive::Dictionary(ref d) => d.serialize(out)?,
70 Primitive::Array(ref arr) => serialize_list(arr, out)?,
71 Primitive::Reference(r) => write!(out, "{} {} R", r.id, r.gen)?,
72 Primitive::Name(ref s) => serialize_name(s, out)?,
73 }
74 Ok(())
75 }
76 pub fn array<O, T, I, U>(i: I, update: &mut U) -> Result<Primitive>
77 where O: ObjectWrite, I: Iterator<Item=T>,
78 T: Borrow<O>, U: Updater
79 {
80 i.map(|t| t.borrow().to_primitive(update)).collect::<Result<_>>().map(Primitive::Array)
81 }
82 pub fn name(name: impl Into<SmallString>) -> Primitive {
83 Primitive::Name(name.into())
84 }
85}
86
87fn serialize_list(arr: &[Primitive], out: &mut impl io::Write) -> Result<()> {
88 let mut parts = arr.iter();
89 write!(out, "[")?;
90 if let Some(first) = parts.next() {
91 first.serialize(out)?;
92 }
93 for p in parts {
94 write!(out, " ")?;
95 p.serialize(out)?;
96 }
97 write!(out, "]")?;
98 Ok(())
99}
100
101pub fn serialize_name(s: &str, out: &mut impl io::Write) -> Result<()> {
102 write!(out, "/")?;
103 for b in s.chars() {
104 match b {
105 '\\' | '(' | ')' => write!(out, r"\")?,
106 c if c > '~' => panic!("only ASCII"),
107 _ => ()
108 }
109 write!(out, "{}", b)?;
110 }
111 Ok(())
112}
113
114#[derive(Default, Clone, PartialEq)]
116pub struct Dictionary {
117 dict: IndexMap<Name, Primitive>
118}
119impl Dictionary {
120 pub fn new() -> Dictionary {
121 Dictionary { dict: IndexMap::new()}
122 }
123 pub fn len(&self) -> usize {
124 self.dict.len()
125 }
126 pub fn is_empty(&self) -> bool {
127 self.len() == 0
128 }
129 pub fn get(&self, key: &str) -> Option<&Primitive> {
130 self.dict.get(key)
131 }
132 pub fn insert(&mut self, key: impl Into<Name>, val: impl Into<Primitive>) -> Option<Primitive> {
133 self.dict.insert(key.into(), val.into())
134 }
135 pub fn iter(&self) -> impl Iterator<Item=(&Name, &Primitive)> {
136 self.dict.iter()
137 }
138 pub fn remove(&mut self, key: &str) -> Option<Primitive> {
139 self.dict.remove(key)
140 }
141 pub fn require(&mut self, typ: &'static str, key: &str) -> Result<Primitive> {
143 self.remove(key).ok_or(
144 PdfError::MissingEntry {
145 typ,
146 field: key.into()
147 }
148 )
149 }
150 pub fn expect(&self, typ: &'static str, key: &str, value: &str, required: bool) -> Result<()> {
153 match self.dict.get(key) {
154 Some(ty) => {
155 let ty = ty.as_name()?;
156 if ty != value {
157 Err(PdfError::KeyValueMismatch {
158 key: key.into(),
159 value: value.into(),
160 found: ty.into()
161 })
162 } else {
163 Ok(())
164 }
165 },
166 None if required => Err(PdfError::MissingEntry { typ, field: key.into() }),
167 None => Ok(())
168 }
169 }
170 pub fn append(&mut self, other: Dictionary) {
171 self.dict.extend(other.dict);
172 }
173}
174impl DataSize for Dictionary {
175 const IS_DYNAMIC: bool = true;
176 const STATIC_HEAP_SIZE: usize = std::mem::size_of::<Self>();
177 fn estimate_heap_size(&self) -> usize {
178 self.iter().map(|(k, v)| 16 + k.estimate_heap_size() + v.estimate_heap_size()).sum()
179 }
180}
181impl ObjectWrite for Dictionary {
182 fn to_primitive(&self, _update: &mut impl Updater) -> Result<Primitive> {
183 Ok(Primitive::Dictionary(self.clone()))
184 }
185}
186impl DeepClone for Dictionary {
187 fn deep_clone(&self, cloner: &mut impl Cloner) -> Result<Self> {
188 Ok(Dictionary {
189 dict: self.dict.iter()
190 .map(|(key, value)| Ok((key.clone(), value.deep_clone(cloner)?)))
191 .try_collect::<_, _, PdfError>()?
192 })
193 }
194}
195impl Deref for Dictionary {
196 type Target = IndexMap<Name, Primitive>;
197 fn deref(&self) -> &IndexMap<Name, Primitive> {
198 &self.dict
199 }
200}
201impl Dictionary {
202 fn serialize(&self, out: &mut impl io::Write) -> Result<()> {
203 writeln!(out, "<<")?;
204 for (key, val) in self.iter() {
205 write!(out, "{} ", key)?;
206 val.serialize(out)?;
207 writeln!(out)?;
208 }
209 writeln!(out, ">>")?;
210 Ok(())
211 }
212}
213impl fmt::Debug for Dictionary {
214 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
215 writeln!(f, "{{")?;
216 for (k, v) in self {
217 writeln!(f, "{:>15}: {}", k, v)?;
218 }
219 write!(f, "}}")
220 }
221}
222impl fmt::Display for Dictionary {
223 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224 write!(f, "<{}>", self.iter().format_with(", ", |(k, v), f| f(&format_args!("{}={}", k, v))))
225 }
226}
227impl<'a> Index<&'a str> for Dictionary {
228 type Output = Primitive;
229 fn index(&self, idx: &'a str) -> &Primitive {
230 self.dict.index(idx)
231 }
232}
233impl IntoIterator for Dictionary {
234 type Item = (Name, Primitive);
235 type IntoIter = indexmap::map::IntoIter<Name, Primitive>;
236 fn into_iter(self) -> Self::IntoIter {
237 self.dict.into_iter()
238 }
239}
240impl<'a> IntoIterator for &'a Dictionary {
241 type Item = (&'a Name, &'a Primitive);
242 type IntoIter = indexmap::map::Iter<'a, Name, Primitive>;
243 fn into_iter(self) -> Self::IntoIter {
244 self.dict.iter()
245 }
246}
247
248#[derive(Clone, Debug, PartialEq, DataSize)]
250pub struct PdfStream {
251 pub info: Dictionary,
252 pub (crate) inner: StreamInner,
253}
254
255#[derive(Clone, Debug, PartialEq, DataSize)]
256pub enum StreamInner {
257 InFile { id: PlainRef, file_range: Range<usize> },
258 Pending { data: Arc<[u8]> },
259}
260impl Object for PdfStream {
261 fn from_primitive(p: Primitive, resolve: &impl Resolve) -> Result<Self> {
262 match p {
263 Primitive::Stream (stream) => Ok(stream),
264 Primitive::Reference (r) => PdfStream::from_primitive(resolve.resolve(r)?, resolve),
265 p => Err(PdfError::UnexpectedPrimitive {expected: "Stream", found: p.get_debug_name()})
266 }
267 }
268}
269impl ObjectWrite for PdfStream {
270 fn to_primitive(&self, update: &mut impl Updater) -> Result<Primitive> {
271 Ok(self.clone().into())
272 }
273}
274impl PdfStream {
275 pub fn serialize(&self, out: &mut impl io::Write) -> Result<()> {
276 self.info.serialize(out)?;
277
278 writeln!(out, "stream")?;
279 match self.inner {
280 StreamInner::InFile { .. } => {
281 unimplemented!()
282 }
283 StreamInner::Pending { ref data } => {
284 out.write_all(data)?;
285 }
286 }
287 writeln!(out, "\nendstream")?;
288 Ok(())
289 }
290 pub fn raw_data(&self, resolve: &impl Resolve) -> Result<Arc<[u8]>> {
291 match self.inner {
292 StreamInner::InFile { id, ref file_range } => resolve.stream_data(id, file_range.clone()),
293 StreamInner::Pending { ref data } => Ok(data.clone())
294 }
295 }
296}
297impl DeepClone for PdfStream {
298 fn deep_clone(&self, cloner: &mut impl Cloner) -> Result<Self> {
299 let data = match self.inner {
300 StreamInner::InFile { id, ref file_range } => cloner.stream_data(id, file_range.clone())?,
301 StreamInner::Pending { ref data } => data.clone()
302 };
303 Ok(PdfStream {
304 info: self.info.deep_clone(cloner)?, inner: StreamInner::Pending { data }
305 })
306 }
307}
308
309
310macro_rules! unexpected_primitive {
311 ($expected:ident, $found:expr) => (
312 Err(PdfError::UnexpectedPrimitive {
313 expected: stringify!($expected),
314 found: $found
315 })
316 )
317}
318
319#[derive(Clone, PartialEq, Eq, Hash, Debug, Ord, PartialOrd, DataSize)]
320pub struct Name(pub SmallString);
321impl Name {
322 #[inline]
323 pub fn as_str(&self) -> &str {
324 &self.0
325 }
326}
327impl Deref for Name {
328 type Target = str;
329 #[inline]
330 fn deref(&self) -> &str {
331 &self.0
332 }
333}
334impl From<String> for Name {
335 #[inline]
336 fn from(s: String) -> Name {
337 Name(s.into())
338 }
339}
340impl From<SmallString> for Name {
341 #[inline]
342 fn from(s: SmallString) -> Name {
343 Name(s)
344 }
345}
346impl<'a> From<&'a str> for Name {
347 #[inline]
348 fn from(s: &'a str) -> Name {
349 Name(s.into())
350 }
351}
352impl PartialEq<str> for Name {
353 #[inline]
354 fn eq(&self, rhs: &str) -> bool {
355 self.as_str() == rhs
356 }
357}
358impl fmt::Display for Name {
359 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
360 write!(f, "/{}", self.0)
361 }
362}
363impl std::borrow::Borrow<str> for Name {
364 #[inline]
365 fn borrow(&self) -> &str {
366 self.0.as_str()
367 }
368}
369#[test]
370fn test_name() {
371 use std::collections::hash_map::DefaultHasher;
372 use std::hash::{Hash, Hasher};
373
374 let s = "Hello World!";
375 let hasher = DefaultHasher::new();
376
377 fn hash(hasher: &DefaultHasher, value: impl Hash) -> u64 {
378 let mut hasher = hasher.clone();
379 value.hash(&mut hasher);
380 hasher.finish()
381 }
382 assert_eq!(hash(&hasher, Name(s.into())), hash(&hasher, s));
383}
384
385#[derive(Clone, PartialEq, Eq, Hash, DataSize)]
387pub struct PdfString {
388 pub data: IBytes,
389}
390impl fmt::Debug for PdfString {
391 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
392 write!(f, "\"")?;
393 for &b in self.data.as_slice() {
394 match b {
395 b'"' => write!(f, "\\\"")?,
396 b' ' ..= b'~' => write!(f, "{}", b as char)?,
397 o @ 0 ..= 7 => write!(f, "\\{}", o)?,
398 x => write!(f, "\\x{:02x}", x)?
399 }
400 }
401 write!(f, "\"")
402 }
403}
404impl Object for PdfString {
405 fn from_primitive(p: Primitive, r: &impl Resolve) -> Result<Self> {
406 match p {
407 Primitive::String (string) => Ok(string),
408 Primitive::Reference(id) => PdfString::from_primitive(r.resolve(id)?, &NoResolve),
409 _ => unexpected_primitive!(String, p.get_debug_name()),
410 }
411 }
412}
413impl ObjectWrite for PdfString {
414 fn to_primitive(&self, _update: &mut impl Updater) -> Result<Primitive> {
415 Ok(Primitive::String(self.clone()))
416 }
417}
418
419impl PdfString {
420 pub fn serialize(&self, out: &mut impl io::Write) -> Result<()> {
421 if self.data.iter().any(|&b| b >= 0x80) {
422 write!(out, "<")?;
423 for &b in self.data.as_slice() {
424 write!(out, "{:02x}", b)?;
425 }
426 write!(out, ">")?;
427 } else {
428 write!(out, r"(")?;
429 for &b in self.data.as_slice() {
430 match b {
431 b'\\' | b'(' | b')' => write!(out, r"\")?,
432 _ => ()
433 }
434 out.write_all(&[b])?;
435 }
436 write!(out, r")")?;
437 }
438 Ok(())
439 }
440}
441impl AsRef<[u8]> for PdfString {
442 fn as_ref(&self) -> &[u8] {
443 self.as_bytes()
444 }
445}
446
447impl PdfString {
448 pub fn new(data: IBytes) -> PdfString {
449 PdfString {
450 data
451 }
452 }
453 pub fn as_bytes(&self) -> &[u8] {
454 &self.data
455 }
456 pub fn into_bytes(self) -> IBytes {
457 self.data
458 }
459 pub fn to_string_lossy(&self) -> String {
464 if self.data.starts_with(&[0xfe, 0xff]) {
465 crate::font::utf16be_to_string_lossy(&self.data[2..])
466 }
467 else {
468 String::from_utf8_lossy(&self.data).into()
469 }
470 }
471 pub fn to_string(&self) -> Result<String> {
475 if self.data.starts_with(&[0xfe, 0xff]) {
476 Ok(String::from(std::str::from_utf8(crate::font::utf16be_to_string(&self.data[2..])?.as_bytes())
477 .map_err(|_| PdfError::Utf8Decode)?))
478 }
479 else {
480 Ok(String::from(std::str::from_utf8(&self.data)
481 .map_err(|_| PdfError::Utf8Decode)?))
482 }
483 }
484}
485impl<'a> From<&'a str> for PdfString {
486 fn from(value: &'a str) -> Self {
487 PdfString { data: value.into() }
488 }
489}
490
491impl Primitive {
495 pub fn get_debug_name(&self) -> &'static str {
497 match *self {
498 Primitive::Null => "Null",
499 Primitive::Integer (..) => "Integer",
500 Primitive::Number (..) => "Number",
501 Primitive::Boolean (..) => "Boolean",
502 Primitive::String (..) => "String",
503 Primitive::Stream (..) => "Stream",
504 Primitive::Dictionary (..) => "Dictionary",
505 Primitive::Array (..) => "Array",
506 Primitive::Reference (..) => "Reference",
507 Primitive::Name (..) => "Name",
508 }
509 }
510 pub fn resolve(self, r: &impl Resolve) -> Result<Primitive> {
512 match self {
513 Primitive::Reference(id) => r.resolve(id),
514 _ => Ok(self)
515 }
516 }
517 pub fn as_integer(&self) -> Result<i32> {
518 match *self {
519 Primitive::Integer(n) => Ok(n),
520 ref p => unexpected_primitive!(Integer, p.get_debug_name())
521 }
522 }
523 pub fn as_u8(&self) -> Result<u8> {
524 match *self {
525 Primitive::Integer(n) if (0..256).contains(&n) => Ok(n as u8),
526 Primitive::Integer(_) => bail!("invalid integer"),
527 ref p => unexpected_primitive!(Integer, p.get_debug_name())
528 }
529 }
530 pub fn as_u32(&self) -> Result<u32> {
531 match *self {
532 Primitive::Integer(n) if n >= 0 => Ok(n as u32),
533 Primitive::Integer(_) => bail!("negative integer"),
534 ref p => unexpected_primitive!(Integer, p.get_debug_name())
535 }
536 }
537 pub fn as_usize(&self) -> Result<usize> {
538 match *self {
539 Primitive::Integer(n) if n >= 0 => Ok(n as usize),
540 Primitive::Integer(_) => bail!("negative integer"),
541 ref p => unexpected_primitive!(Integer, p.get_debug_name())
542 }
543 }
544 pub fn as_number(&self) -> Result<f32> {
545 match *self {
546 Primitive::Integer(n) => Ok(n as f32),
547 Primitive::Number(f) => Ok(f),
548 ref p => unexpected_primitive!(Number, p.get_debug_name())
549 }
550 }
551 pub fn as_bool(&self) -> Result<bool> {
552 match *self {
553 Primitive::Boolean (b) => Ok(b),
554 ref p => unexpected_primitive!(Number, p.get_debug_name())
555 }
556 }
557 pub fn as_name(&self) -> Result<&str> {
558 match self {
559 Primitive::Name(ref name) => Ok(name.as_str()),
560 p => unexpected_primitive!(Name, p.get_debug_name())
561 }
562 }
563 pub fn as_string(&self) -> Result<&PdfString> {
564 match self {
565 Primitive::String(ref data) => Ok(data),
566 p => unexpected_primitive!(String, p.get_debug_name())
567 }
568 }
569 pub fn as_array(&self) -> Result<&[Primitive]> {
570 match self {
571 Primitive::Array(ref v) => Ok(v),
572 p => unexpected_primitive!(Array, p.get_debug_name())
573 }
574 }
575 pub fn into_reference(self) -> Result<PlainRef> {
576 match self {
577 Primitive::Reference(id) => Ok(id),
578 p => unexpected_primitive!(Reference, p.get_debug_name())
579 }
580 }
581 pub fn into_array(self) -> Result<Vec<Primitive>> {
582 match self {
583 Primitive::Array(v) => Ok(v),
584 p => unexpected_primitive!(Array, p.get_debug_name())
585 }
586 }
587 pub fn into_dictionary(self) -> Result<Dictionary> {
588 match self {
589 Primitive::Dictionary(dict) => Ok(dict),
590 p => unexpected_primitive!(Dictionary, p.get_debug_name())
591 }
592 }
593 pub fn into_name(self) -> Result<Name> {
594 match self {
595 Primitive::Name(name) => Ok(Name(name)),
596 p => unexpected_primitive!(Name, p.get_debug_name())
597 }
598 }
599 pub fn into_string(self) -> Result<PdfString> {
600 match self {
601 Primitive::String(data) => Ok(data),
602 p => unexpected_primitive!(String, p.get_debug_name())
603 }
604 }
605 pub fn to_string_lossy(&self) -> Result<String> {
606 let s = self.as_string()?;
607 Ok(s.to_string_lossy())
608 }
609 pub fn to_string(&self) -> Result<String> {
610 let s = self.as_string()?;
611 s.to_string()
612 }
613 pub fn into_stream(self, _r: &impl Resolve) -> Result<PdfStream> {
614 match self {
615 Primitive::Stream (s) => Ok(s),
616 p => unexpected_primitive!(Stream, p.get_debug_name())
617 }
618 }
619}
620
621impl From<i32> for Primitive {
622 fn from(x: i32) -> Primitive {
623 Primitive::Integer(x)
624 }
625}
626impl From<f32> for Primitive {
627 fn from(x: f32) -> Primitive {
628 Primitive::Number(x)
629 }
630}
631impl From<bool> for Primitive {
632 fn from(x: bool) -> Primitive {
633 Primitive::Boolean(x)
634 }
635}
636impl From<Name> for Primitive {
637 fn from(Name(s): Name) -> Primitive {
638 Primitive::Name(s)
639 }
640}
641impl From<PdfString> for Primitive {
642 fn from(x: PdfString) -> Primitive {
643 Primitive::String (x)
644 }
645}
646impl From<PdfStream> for Primitive {
647 fn from(x: PdfStream) -> Primitive {
648 Primitive::Stream (x)
649 }
650}
651impl From<Dictionary> for Primitive {
652 fn from(x: Dictionary) -> Primitive {
653 Primitive::Dictionary (x)
654 }
655}
656impl From<Vec<Primitive>> for Primitive {
657 fn from(x: Vec<Primitive>) -> Primitive {
658 Primitive::Array (x)
659 }
660}
661
662impl From<PlainRef> for Primitive {
663 fn from(x: PlainRef) -> Primitive {
664 Primitive::Reference (x)
665 }
666}
667impl<'a> TryInto<f32> for &'a Primitive {
668 type Error = PdfError;
669 fn try_into(self) -> Result<f32> {
670 self.as_number()
671 }
672}
673impl<'a> TryInto<i32> for &'a Primitive {
674 type Error = PdfError;
675 fn try_into(self) -> Result<i32> {
676 self.as_integer()
677 }
678}
679impl<'a> TryInto<Name> for &'a Primitive {
680 type Error = PdfError;
681 fn try_into(self) -> Result<Name> {
682 match self {
683 Primitive::Name(s) => Ok(Name(s.clone())),
684 p => Err(PdfError::UnexpectedPrimitive {
685 expected: "Name",
686 found: p.get_debug_name()
687 })
688 }
689 }
690}
691impl<'a> TryInto<&'a [Primitive]> for &'a Primitive {
692 type Error = PdfError;
693 fn try_into(self) -> Result<&'a [Primitive]> {
694 self.as_array()
695 }
696}
697impl<'a> TryInto<&'a [u8]> for &'a Primitive {
698 type Error = PdfError;
699 fn try_into(self) -> Result<&'a [u8]> {
700 match *self {
701 Primitive::Name(ref s) => Ok(s.as_bytes()),
702 Primitive::String(ref s) => Ok(s.as_bytes()),
703 ref p => Err(PdfError::UnexpectedPrimitive {
704 expected: "Name or String",
705 found: p.get_debug_name()
706 })
707 }
708 }
709}
710impl<'a> TryInto<Cow<'a, str>> for &'a Primitive {
711 type Error = PdfError;
712 fn try_into(self) -> Result<Cow<'a, str>> {
713 match *self {
714 Primitive::Name(ref s) => Ok(Cow::Borrowed(s)),
715 Primitive::String(ref s) => Ok(Cow::Owned(s.to_string_lossy())),
716 ref p => Err(PdfError::UnexpectedPrimitive {
717 expected: "Name or String",
718 found: p.get_debug_name()
719 })
720 }
721 }
722}
723impl<'a> TryInto<String> for &'a Primitive {
724 type Error = PdfError;
725 fn try_into(self) -> Result<String> {
726 match *self {
727 Primitive::Name(ref s) => Ok(s.as_str().into()),
728 Primitive::String(ref s) => Ok(s.to_string_lossy()),
729 ref p => Err(PdfError::UnexpectedPrimitive {
730 expected: "Name or String",
731 found: p.get_debug_name()
732 })
733 }
734 }
735}
736
737fn parse_or<T: str::FromStr + Clone>(buffer: &str, range: Range<usize>, default: T) -> T {
738 buffer.get(range)
739 .map(|s| str::parse::<T>(s).unwrap_or_else(|_| default.clone()))
740 .unwrap_or(default)
741}
742
743#[derive(Clone, Debug, PartialEq, Eq)]
744pub struct Date {
745 pub year: u16,
746 pub month: u8,
747 pub day: u8,
748 pub hour: u8,
749 pub minute: u8,
750 pub second: u8,
751 pub rel: TimeRel,
752 pub tz_hour: u8,
753 pub tz_minute: u8,
754}
755
756#[derive(Clone, Debug, Copy, PartialEq, Eq)]
757pub enum TimeRel {
758 Earlier,
759 Later,
760 Universal
761}
762datasize::non_dynamic_const_heap_size!(Date, std::mem::size_of::<Date>());
763
764impl Object for Date {
765 fn from_primitive(p: Primitive, r: &impl Resolve) -> Result<Self> {
766 match p.resolve(r)? {
767 Primitive::String (PdfString {data}) => {
768 let s = str::from_utf8(&data)?;
769 if s.starts_with("D:") {
770 let year = match s.get(2..6) {
771 Some(year) => {
772 str::parse::<u16>(year)?
773 }
774 None => bail!("Missing obligatory year in date")
775 };
776
777 let (time, rel, zone) = match s.find(['+', '-', 'Z']) {
778 Some(p) => {
779 let rel = match &s[p..p+1] {
780 "-" => TimeRel::Earlier,
781 "+" => TimeRel::Later,
782 "Z" => TimeRel::Universal,
783 _ => unreachable!()
784 };
785 (&s[..p], rel, &s[p+1..])
786 }
787 None => (s, TimeRel::Universal, "")
788 };
789
790 let month = parse_or(time, 6..8, 1);
791 let day = parse_or(time, 8..10, 1);
792 let hour = parse_or(time, 10..12, 0);
793 let minute = parse_or(time, 12..14, 0);
794 let second = parse_or(time, 14..16, 0);
795 let tz_hour = parse_or(zone, 0..2, 0);
796 let tz_minute = parse_or(zone, 3..5, 0);
797
798 Ok(Date {
799 year, month, day,
800 hour, minute, second,
801 tz_hour, tz_minute,
802 rel
803 })
804 } else {
805 bail!("Failed parsing date");
806 }
807 }
808 p => unexpected_primitive!(String, p.get_debug_name()),
809 }
810 }
811}
812
813impl ObjectWrite for Date {
814 fn to_primitive(&self, _update: &mut impl Updater) -> Result<Primitive> {
815 let Date {
816 year, month, day,
817 hour, minute, second,
818 tz_hour, tz_minute, rel,
819 } = *self;
820 if year > 9999 || day > 99 || hour > 23 || minute >= 60 || second >= 60 || tz_hour >= 24 || tz_minute >= 60 {
821 bail!("not a valid date");
822 }
823 let o = match rel {
824 TimeRel::Earlier => "-",
825 TimeRel::Later => "+",
826 TimeRel::Universal => "Z"
827 };
828
829 let s = format!("D:{year:04}{month:02}{day:02}{hour:02}{minute:02}{second:02}{o}{tz_hour:02}'{tz_minute:02}");
830 Ok(Primitive::String(PdfString { data: s.into() }))
831 }
832}
833
834#[cfg(test)]
835mod tests {
836 use crate::{primitive::{PdfString, TimeRel}, object::{NoResolve, Object}};
837
838 use super::Date;
839 #[test]
840 fn utf16be_string() {
841 let s = PdfString::new([0xfe, 0xff, 0x20, 0x09].as_slice().into());
842 assert_eq!(s.to_string_lossy(), "\u{2009}");
843 }
844
845 #[test]
846 fn utf16be_invalid_string() {
847 let s = PdfString::new([0xfe, 0xff, 0xd8, 0x34].as_slice().into());
848 let repl_ch = String::from(std::char::REPLACEMENT_CHARACTER);
849 assert_eq!(s.to_string_lossy(), repl_ch);
850 }
851
852 #[test]
853 fn utf16be_invalid_bytelen() {
854 let s = PdfString::new([0xfe, 0xff, 0xd8, 0x34, 0x20].as_slice().into());
855 let repl_ch = String::from(std::char::REPLACEMENT_CHARACTER);
856 assert_eq!(s.to_string_lossy(), repl_ch);
857 }
858
859 #[test]
860 fn pdfstring_lossy_vs_ascii() {
861 let s = PdfString::new([0xfe, 0xff, 0xd8, 0x34].as_slice().into());
863 assert!(s.to_string().is_err()); let s = PdfString::new([0xfe, 0xff, 0x00, 0xe4 ].as_slice().into());
866 assert_eq!(s.to_string_lossy(), "ä");
867 assert_eq!(s.to_string().unwrap(), "ä");
868 let s = PdfString::new([b'm', b'i', b't', 0xc3, 0xa4 ].as_slice().into());
870 assert_eq!(s.to_string_lossy(), "mitä");
871 assert_eq!(s.to_string().unwrap(), "mitä");
872 let s = PdfString::new([b'm', b'i', b't', 0xe4].as_slice().into());
874 let repl_ch = ['m', 'i', 't', std::char::REPLACEMENT_CHARACTER].iter().collect::<String>();
875 assert_eq!(s.to_string_lossy(), repl_ch);
876 assert!(s.to_string().is_err()); }
878
879 #[test]
880 fn date() {
881 let p = PdfString::from("D:199812231952-08'00");
882 let d = Date::from_primitive(p.into(), &NoResolve);
883
884 let d2 = Date {
885 year: 1998,
886 month: 12,
887 day: 23,
888 hour: 19,
889 minute: 52,
890 second: 00,
891 rel: TimeRel::Earlier,
892 tz_hour: 8,
893 tz_minute: 0
894 };
895 assert_eq!(d.unwrap(), d2);
896 }
897}