pdf/
primitive.rs

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/// Primitive Dictionary type.
115#[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    /// like remove, but takes the name of the calling type and returns `PdfError::MissingEntry` if the entry is not found
142    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    /// assert that the given key/value pair is in the dictionary (`required=true`),
151    /// or the key is not present at all (`required=false`)
152    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/// Primitive Stream (as opposed to the higher-level `Stream`)
249#[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/// Primitive String type.
386#[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    /// without encoding information the PdfString cannot be decoded into a String
460    /// therefore only lossy decoding is possible replacing unknown characters.
461    /// For decoding correctly see
462    /// pdf_tools/src/lib.rs
463    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    /// without encoding information the PdfString cannot be sensibly decoded into a String
472    /// converts to a Rust String but only works for valid UTF-8, UTF-16BE and ASCII characters
473    /// if invalid bytes found an Error is returned
474    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
491// TODO:
492// Noticed some inconsistency here.. I think to_* and as_* should not take Resolve, and not accept
493// Reference. Only from_primitive() for the respective type resolves References.
494impl Primitive {
495    /// For debugging / error messages: get the name of the variant
496    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    /// resolve the primitive if it is a refernce, otherwise do nothing
511    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        // verify UTF-16-BE fails on invalid
862        let s = PdfString::new([0xfe, 0xff, 0xd8, 0x34].as_slice().into());
863        assert!(s.to_string().is_err()); // FIXME verify it is a PdfError::Utf16Decode
864        // verify UTF-16-BE supports umlauts
865        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        // verify valid UTF-8 bytestream with umlaut works
869        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        // verify valid ISO-8859-1 bytestream with umlaut fails
873        let s = PdfString::new([b'm', b'i', b't', 0xe4/*ä in latin1*/].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()); // FIXME verify it is a PdfError::Utf16Decode
877    }
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}