pdf_writer/
object.rs

1use std::convert::TryFrom;
2use std::marker::PhantomData;
3use std::mem::ManuallyDrop;
4use std::num::NonZeroI32;
5
6use super::*;
7
8/// A primitive PDF object.
9pub trait Primitive {
10    /// Write the object into a buffer.
11    fn write(self, buf: &mut Buf);
12}
13
14impl<T: Primitive> Primitive for &T
15where
16    T: Copy,
17{
18    #[inline]
19    fn write(self, buf: &mut Buf) {
20        (*self).write(buf);
21    }
22}
23
24impl Primitive for bool {
25    #[inline]
26    fn write(self, buf: &mut Buf) {
27        if self {
28            buf.extend(b"true");
29        } else {
30            buf.extend(b"false");
31        }
32    }
33}
34
35impl Primitive for i32 {
36    #[inline]
37    fn write(self, buf: &mut Buf) {
38        buf.push_int(self);
39    }
40}
41
42impl Primitive for f32 {
43    #[inline]
44    fn write(self, buf: &mut Buf) {
45        buf.push_float(self);
46    }
47}
48
49/// A string object (any byte sequence).
50///
51/// This is written as `(Thing)`.
52#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
53pub struct Str<'a>(pub &'a [u8]);
54
55impl Str<'_> {
56    /// Whether the parentheses in the byte string are balanced.
57    fn is_balanced(self) -> bool {
58        let mut depth = 0;
59        for &byte in self.0 {
60            match byte {
61                b'(' => depth += 1,
62                b')' if depth > 0 => depth -= 1,
63                b')' => return false,
64                _ => {}
65            }
66        }
67        depth == 0
68    }
69}
70
71impl Primitive for Str<'_> {
72    fn write(self, buf: &mut Buf) {
73        buf.limits.register_str_len(self.0.len());
74
75        // We use:
76        // - Literal strings for ASCII with nice escape sequences to make it
77        //   also be represented fully in visible ASCII. We also escape
78        //   parentheses because they are delimiters.
79        // - Hex strings for anything non-ASCII.
80        if self.0.iter().all(|b| b.is_ascii()) {
81            buf.reserve(self.0.len());
82            buf.inner.push(b'(');
83
84            let mut balanced = None;
85            for &byte in self.0 {
86                match byte {
87                    b'(' | b')' => {
88                        if !*balanced
89                            .get_or_insert_with(|| byte != b')' && self.is_balanced())
90                        {
91                            buf.push(b'\\');
92                        }
93                        buf.push(byte);
94                    }
95                    b'\\' => buf.extend(br"\\"),
96                    b' '..=b'~' => buf.push(byte),
97                    b'\n' => buf.extend(br"\n"),
98                    b'\r' => buf.extend(br"\r"),
99                    b'\t' => buf.extend(br"\t"),
100                    b'\x08' => buf.extend(br"\b"),
101                    b'\x0c' => buf.extend(br"\f"),
102                    _ => {
103                        buf.push(b'\\');
104                        buf.push_octal(byte);
105                    }
106                }
107            }
108
109            buf.push(b')');
110        } else {
111            buf.reserve(2 + 2 * self.0.len());
112            buf.push(b'<');
113
114            for &byte in self.0 {
115                buf.push_hex(byte);
116            }
117
118            buf.push(b'>');
119        }
120    }
121}
122
123/// A unicode text string object.
124///
125/// This is written as a [`Str`] containing either bare ASCII (if possible) or a
126/// byte order mark followed by UTF-16-BE bytes.
127///
128/// The natural language is inherited from the document catalog's
129/// [`/Lang` key](crate::Catalog::lang). If you need to specify another language
130/// or if the string contains multiple natural languages, see
131/// [`TextStrWithLang`].
132#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
133pub struct TextStr<'a>(pub &'a str);
134
135impl Primitive for TextStr<'_> {
136    fn write(self, buf: &mut Buf) {
137        buf.limits.register_str_len(self.0.len());
138
139        // ASCII and PDFDocEncoding match for 32 up to 126.
140        if self.0.bytes().all(|b| matches!(b, 32..=126)) {
141            Str(self.0.as_bytes()).write(buf);
142        } else {
143            buf.reserve(6 + 4 * self.0.len());
144            write_utf16be_text_str_header(buf);
145            for value in self.0.encode_utf16() {
146                buf.push_hex_u16(value);
147            }
148            write_utf16be_text_str_footer(buf);
149        }
150    }
151}
152
153/// An identifier for the natural language in a section of a
154/// [`TextStrWithLang`].
155#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
156pub struct LanguageIdentifier {
157    /// A two-byte ISO 639 language code.
158    lang: [u8; 2],
159    /// A two-byte ISO 3166 country code.
160    region: Option<[u8; 2]>,
161}
162
163impl LanguageIdentifier {
164    /// Create a new language identifier.
165    ///
166    /// Language and region codes are not checked for validity.
167    pub fn new(lang: [u8; 2], region: Option<[u8; 2]>) -> Self {
168        Self { lang, region }
169    }
170
171    /// Create a new language identifier from a language, with an unset region.
172    ///
173    /// The method returns `Some` if the argument has two alphanumeric ASCII
174    /// bytes.
175    pub fn from_lang(lang: &str) -> Option<Self> {
176        let lang = Self::str_to_code(lang)?;
177        Some(Self::new(lang, None))
178    }
179
180    /// Create a new language identifier from a language and a region
181    ///
182    /// The method returns `Some` if both arguments have two alphanumeric ASCII
183    /// bytes.
184    pub fn from_lang_region(lang: &str, region: &str) -> Option<Self> {
185        let lang = Self::str_to_code(lang)?;
186        let region = Self::str_to_code(region)?;
187        Some(Self::new(lang, Some(region)))
188    }
189
190    /// Returns the length of the language identifier. It does not include the
191    /// enclosing escape bytes.
192    fn len(self) -> usize {
193        if self.region.is_some() {
194            4
195        } else {
196            2
197        }
198    }
199
200    fn str_to_code(string: &str) -> Option<[u8; 2]> {
201        if string.chars().all(|c| c.is_ascii_alphanumeric()) {
202            string.as_bytes().try_into().ok()
203        } else {
204            None
205        }
206    }
207}
208
209/// A text string with a natural language specified.
210///
211///
212/// This is written as a string containing either bare ASCII (if possible) or a
213/// byte order mark followed by UTF-16-BE bytes. Both forms are interspersed by
214/// the requisite ASCII language escape sequences.
215///
216/// For a text string with an undefined natural language, see [`TextStr`].
217#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
218pub struct TextStrWithLang<'a, 'b>(pub &'b [(LanguageIdentifier, &'a str)]);
219
220impl<'a, 'b> Primitive for TextStrWithLang<'a, 'b> {
221    fn write(self, buf: &mut Buf) {
222        let mut len = 0;
223        let mut buf_len = 6;
224
225        for (lang, text) in self.0 {
226            // Each language tag is enclosed in two escape characters, plus the
227            // two-letter language code and optional two-letter region code.
228            len += text.len() + lang.len() + 2;
229            // Each hexadecimal character is four bytes long, plus four bytes
230            // for the escape sequence. The language tag is encoded in
231            // hexadecimal, so each byte becomes two hexadecimal characters.
232            buf_len += 4 * text.len() + lang.len() * 2 + 4;
233        }
234
235        buf.limits.register_str_len(len);
236
237        // Escape sequences for languages may only appear in Unicode-encoded
238        // text strings, see clause 7.9.2.2 of ISO 32000-1:2008.
239        buf.reserve(buf_len);
240        write_utf16be_text_str_header(buf);
241
242        for (lang, text) in self.0 {
243            write_utf16be_lang_code(*lang, buf);
244            for value in text.encode_utf16() {
245                buf.push_hex_u16(value);
246            }
247        }
248
249        write_utf16be_text_str_footer(buf);
250    }
251}
252
253fn write_utf16be_text_str_header(buf: &mut Buf) {
254    buf.push(b'<');
255    buf.push_hex(254);
256    buf.push_hex(255);
257}
258
259fn write_utf16be_text_str_footer(buf: &mut Buf) {
260    buf.push(b'>');
261}
262
263fn write_utf16be_lang_code(lang: LanguageIdentifier, buf: &mut Buf) {
264    // The escape character U+001B encloses the language tag. It must not
265    // otherwise appear in a text string and the spec offers no opportunity to
266    // escape it. In the future, `pdf-writer` may offer a constructor for
267    // [`TextStrWithLang`] and [`TextStr`] that either checks for it or replaces
268    // it with the object replacement character U+FFFD.
269    buf.push_hex_u16(0x001B);
270    buf.push_hex_u16(u16::from(lang.lang[0]));
271    buf.push_hex_u16(u16::from(lang.lang[1]));
272    if let Some(region) = lang.region {
273        buf.push_hex_u16(u16::from(region[0]));
274        buf.push_hex_u16(u16::from(region[1]));
275    }
276    buf.push_hex_u16(0x001B);
277}
278
279/// A trait for types that can be used everywhere a text string is expected.
280/// This includes both [`TextStr`] and [`TextStrWithLang`].
281///
282/// Methods that accept an implementor of this trait expect strings in natural
283/// language for which a language specification makes sense, often for use in
284/// the UI or with AT.
285pub trait TextStrLike: Primitive {}
286
287impl<'a> TextStrLike for TextStr<'a> {}
288impl<'a, 'b> TextStrLike for TextStrWithLang<'a, 'b> {}
289
290/// A name object.
291///
292/// Written as `/Thing`.
293#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
294pub struct Name<'a>(pub &'a [u8]);
295
296impl Primitive for Name<'_> {
297    fn write(self, buf: &mut Buf) {
298        buf.limits.register_name_len(self.0.len());
299
300        buf.reserve(1 + self.0.len());
301        buf.push(b'/');
302        for &byte in self.0 {
303            // - Number sign shall use hexadecimal escape
304            // - Regular characters within the range exlacamation mark .. tilde
305            //   can be written directly
306            if byte != b'#' && matches!(byte, b'!'..=b'~') && is_regular_character(byte) {
307                buf.push(byte);
308            } else {
309                buf.push(b'#');
310                buf.push_hex(byte);
311            }
312        }
313    }
314}
315
316/// Regular characters are a PDF concept.
317fn is_regular_character(byte: u8) -> bool {
318    !matches!(
319        byte,
320        b'\0'
321            | b'\t'
322            | b'\n'
323            | b'\x0C'
324            | b'\r'
325            | b' '
326            | b'('
327            | b')'
328            | b'<'
329            | b'>'
330            | b'['
331            | b']'
332            | b'{'
333            | b'}'
334            | b'/'
335            | b'%'
336    )
337}
338
339/// The null object.
340#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
341pub struct Null;
342
343impl Primitive for Null {
344    #[inline]
345    fn write(self, buf: &mut Buf) {
346        buf.extend(b"null");
347    }
348}
349
350/// A reference to an indirect object.
351#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
352pub struct Ref(NonZeroI32);
353
354impl Ref {
355    /// Create a new indirect reference.
356    ///
357    /// The provided value must be greater than zero.
358    ///
359    /// Panics if `id` is out of the valid range.
360    #[inline]
361    #[track_caller]
362    pub const fn new(id: i32) -> Ref {
363        let option = if id > 0 { NonZeroI32::new(id) } else { None };
364        match option {
365            Some(val) => Self(val),
366            None => panic!("indirect reference out of valid range"),
367        }
368    }
369
370    /// Return the underlying number as a primitive type.
371    #[inline]
372    pub const fn get(self) -> i32 {
373        self.0.get()
374    }
375
376    /// The next consecutive ID.
377    #[inline]
378    pub const fn next(self) -> Self {
379        Self::new(self.get() + 1)
380    }
381
382    /// Increase this ID by one and return the old one. Useful to turn this ID
383    /// into a bump allocator of sorts.
384    #[inline]
385    pub fn bump(&mut self) -> Self {
386        let prev = *self;
387        *self = self.next();
388        prev
389    }
390}
391
392impl Primitive for Ref {
393    #[inline]
394    fn write(self, buf: &mut Buf) {
395        buf.push_int(self.0.get());
396        buf.extend(b" 0 R");
397    }
398}
399
400/// A rectangle, specified by two opposite corners.
401#[derive(Debug, Copy, Clone, PartialEq)]
402pub struct Rect {
403    /// The x-coordinate of the first (typically, lower-left) corner.
404    pub x1: f32,
405    /// The y-coordinate of the first (typically, lower-left) corner.
406    pub y1: f32,
407    /// The x-coordinate of the second (typically, upper-right) corner.
408    pub x2: f32,
409    /// The y-coordinate of the second (typically, upper-right) corner.
410    pub y2: f32,
411}
412
413impl Rect {
414    /// Create a new rectangle from four coordinate values.
415    #[inline]
416    pub fn new(x1: f32, y1: f32, x2: f32, y2: f32) -> Self {
417        Self { x1, y1, x2, y2 }
418    }
419
420    /// Convert this rectangle into 8 floats describing the four corners of the
421    /// rectangle in counterclockwise order.
422    #[inline]
423    pub fn to_quad_points(self) -> [f32; 8] {
424        [self.x1, self.y1, self.x2, self.y1, self.x2, self.y2, self.x1, self.y2]
425    }
426}
427
428impl Primitive for Rect {
429    #[inline]
430    fn write(self, buf: &mut Buf) {
431        buf.push(b'[');
432        buf.push_val(self.x1);
433        buf.push(b' ');
434        buf.push_val(self.y1);
435        buf.push(b' ');
436        buf.push_val(self.x2);
437        buf.push(b' ');
438        buf.push_val(self.y2);
439        buf.push(b']');
440
441        buf.limits.register_array_len(4);
442    }
443}
444
445/// A date, written as a text string.
446///
447/// A field is only respected if all superior fields are supplied. For example,
448/// to set the minute, the hour, day, etc. have to be set. Similarly, in order
449/// for the time zone information to be written, all time information (including
450/// seconds) must be written. `utc_offset_minute` is optional if supplying time
451/// zone info. It must only be used to specify sub-hour time zone offsets.
452#[derive(Debug, Copy, Clone, Eq, PartialEq)]
453pub struct Date {
454    /// The year (0-9999).
455    year: u16,
456    /// The month (0-11).
457    month: Option<u8>,
458    /// The month (0-30).
459    day: Option<u8>,
460    /// The hour (0-23).
461    hour: Option<u8>,
462    /// The minute (0-59).
463    minute: Option<u8>,
464    /// The second (0-59).
465    second: Option<u8>,
466    /// The hour offset from UTC (-23 through 23).
467    utc_offset_hour: Option<i8>,
468    /// The minute offset from UTC (0-59). Will carry over the sign from
469    /// `utc_offset_hour`.
470    utc_offset_minute: u8,
471}
472
473impl Date {
474    /// Create a new, minimal date. The year will be clamped within the range
475    /// 0-9999.
476    #[inline]
477    pub fn new(year: u16) -> Self {
478        Self {
479            year: year.min(9999),
480            month: None,
481            day: None,
482            hour: None,
483            minute: None,
484            second: None,
485            utc_offset_hour: None,
486            utc_offset_minute: 0,
487        }
488    }
489
490    /// Add the month field. It will be clamped within the range 1-12.
491    #[inline]
492    pub fn month(mut self, month: u8) -> Self {
493        self.month = Some(month.clamp(1, 12));
494        self
495    }
496
497    /// Add the day field. It will be clamped within the range 1-31.
498    #[inline]
499    pub fn day(mut self, day: u8) -> Self {
500        self.day = Some(day.clamp(1, 31));
501        self
502    }
503
504    /// Add the hour field. It will be clamped within the range 0-23.
505    #[inline]
506    pub fn hour(mut self, hour: u8) -> Self {
507        self.hour = Some(hour.min(23));
508        self
509    }
510
511    /// Add the minute field. It will be clamped within the range 0-59.
512    #[inline]
513    pub fn minute(mut self, minute: u8) -> Self {
514        self.minute = Some(minute.min(59));
515        self
516    }
517
518    /// Add the second field. It will be clamped within the range 0-59.
519    #[inline]
520    pub fn second(mut self, second: u8) -> Self {
521        self.second = Some(second.min(59));
522        self
523    }
524
525    /// Add the offset from UTC in hours. If not specified, the time will be
526    /// assumed to be local to the viewer's time zone. It will be clamped within
527    /// the range -23-23.
528    #[inline]
529    pub fn utc_offset_hour(mut self, hour: i8) -> Self {
530        self.utc_offset_hour = Some(hour.clamp(-23, 23));
531        self
532    }
533
534    /// Add the offset from UTC in minutes. This will have the same sign as set in
535    /// [`Self::utc_offset_hour`]. It will be clamped within the range 0-59.
536    #[inline]
537    pub fn utc_offset_minute(mut self, minute: u8) -> Self {
538        self.utc_offset_minute = minute.min(59);
539        self
540    }
541}
542
543impl Primitive for Date {
544    fn write(self, buf: &mut Buf) {
545        buf.extend(b"(D:");
546
547        (|| {
548            write!(buf.inner, "{:04}", self.year).unwrap();
549            write!(buf.inner, "{:02}", self.month?).unwrap();
550            write!(buf.inner, "{:02}", self.day?).unwrap();
551            write!(buf.inner, "{:02}", self.hour?).unwrap();
552            write!(buf.inner, "{:02}", self.minute?).unwrap();
553            write!(buf.inner, "{:02}", self.second?).unwrap();
554            let utc_offset_hour = self.utc_offset_hour?;
555            if utc_offset_hour == 0 && self.utc_offset_minute == 0 {
556                buf.push(b'Z');
557            } else {
558                write!(
559                    buf.inner,
560                    "{:+03}'{:02}",
561                    utc_offset_hour, self.utc_offset_minute
562                )
563                .unwrap();
564            }
565            Some(())
566        })();
567
568        buf.push(b')');
569    }
570}
571
572/// Writer for an arbitrary object.
573#[must_use = "not consuming this leaves the writer in an inconsistent state"]
574pub struct Obj<'a> {
575    buf: &'a mut Buf,
576    indirect: bool,
577    indent: u8,
578}
579
580impl<'a> Obj<'a> {
581    /// Start a new direct object.
582    #[inline]
583    pub(crate) fn direct(buf: &'a mut Buf, indent: u8) -> Self {
584        Self { buf, indirect: false, indent }
585    }
586
587    /// Start a new indirect object.
588    #[inline]
589    pub(crate) fn indirect(buf: &'a mut Buf, id: Ref) -> Self {
590        buf.push_int(id.get());
591        buf.extend(b" 0 obj\n");
592        Self { buf, indirect: true, indent: 0 }
593    }
594
595    /// Write a primitive object.
596    #[inline]
597    pub fn primitive<T: Primitive>(self, value: T) {
598        value.write(self.buf);
599        if self.indirect {
600            self.buf.extend(b"\nendobj\n\n");
601        }
602    }
603
604    /// Start writing an array.
605    #[inline]
606    pub fn array(self) -> Array<'a> {
607        self.start()
608    }
609
610    /// Start writing a dictionary.
611    #[inline]
612    pub fn dict(self) -> Dict<'a> {
613        self.start()
614    }
615
616    /// Start writing with an arbitrary writer.
617    ///
618    /// For example, using this, you could write a Type 1 font directly into
619    /// a page's resource directionary.
620    /// ```
621    /// use pdf_writer::{Pdf, Ref, Name, writers::Type1Font};
622    ///
623    /// let mut pdf = Pdf::new();
624    /// pdf.page(Ref::new(1))
625    ///     .resources()
626    ///     .fonts()
627    ///     .insert(Name(b"F1"))
628    ///     .start::<Type1Font>()
629    ///     .base_font(Name(b"Helvetica"));
630    /// ```
631    #[inline]
632    pub fn start<W: Writer<'a>>(self) -> W {
633        W::start(self)
634    }
635}
636
637/// A writer for a specific type of PDF object.
638pub trait Writer<'a> {
639    /// Start writing the object.
640    fn start(obj: Obj<'a>) -> Self;
641}
642
643/// Rewrites a writer's lifetime.
644///
645/// This is a workaround to ignore the `'b` lifetime in a
646/// `TypedArray<'a, SomeWriter<'b>>` because that lifetime is meaningless. What
647/// we actually want is each item's `SomeWriter` to borrow from the array itself.
648pub trait Rewrite<'a> {
649    /// The writer with the rewritten lifetime.
650    type Output: Writer<'a>;
651}
652
653/// Writer for an array.
654pub struct Array<'a> {
655    buf: &'a mut Buf,
656    indirect: bool,
657    indent: u8,
658    len: i32,
659}
660
661writer!(Array: |obj| {
662    obj.buf.push(b'[');
663    Self {
664        buf: obj.buf,
665        indirect: obj.indirect,
666        indent: obj.indent,
667        len: 0,
668    }
669});
670
671impl<'a> Array<'a> {
672    /// The number of written items.
673    #[inline]
674    pub fn len(&self) -> i32 {
675        self.len
676    }
677
678    /// Whether no items have been written so far.
679    #[inline]
680    pub fn is_empty(&self) -> bool {
681        self.len == 0
682    }
683
684    /// Start writing an arbitrary item.
685    #[inline]
686    pub fn push(&mut self) -> Obj<'_> {
687        if self.len != 0 {
688            self.buf.push(b' ');
689        }
690        self.len += 1;
691        Obj::direct(self.buf, self.indent)
692    }
693
694    /// Write an item with a primitive value.
695    ///
696    /// This is a shorthand for `array.push().primitive(value)`.
697    #[inline]
698    pub fn item<T: Primitive>(&mut self, value: T) -> &mut Self {
699        self.push().primitive(value);
700        self
701    }
702
703    /// Write a sequence of items with primitive values.
704    #[inline]
705    pub fn items<T: Primitive>(
706        &mut self,
707        values: impl IntoIterator<Item = T>,
708    ) -> &mut Self {
709        for value in values {
710            self.item(value);
711        }
712        self
713    }
714
715    /// Convert into a typed version.
716    #[inline]
717    pub fn typed<T>(self) -> TypedArray<'a, T> {
718        TypedArray::wrap(self)
719    }
720}
721
722impl Drop for Array<'_> {
723    #[inline]
724    fn drop(&mut self) {
725        self.buf.limits.register_array_len(self.len() as usize);
726        self.buf.push(b']');
727        if self.indirect {
728            self.buf.extend(b"\nendobj\n\n");
729        }
730    }
731}
732
733/// Writer for an array of items of a fixed type.
734pub struct TypedArray<'a, T> {
735    array: Array<'a>,
736    phantom: PhantomData<fn() -> T>,
737}
738
739impl<'a, T> Writer<'a> for TypedArray<'a, T> {
740    fn start(obj: Obj<'a>) -> Self {
741        Self { array: obj.array(), phantom: PhantomData }
742    }
743}
744
745impl<'a, T> Rewrite<'a> for TypedArray<'_, T> {
746    type Output = TypedArray<'a, T>;
747}
748
749impl<'a, T> TypedArray<'a, T> {
750    /// Wrap an array to make it type-safe.
751    #[inline]
752    pub fn wrap(array: Array<'a>) -> Self {
753        Self { array, phantom: PhantomData }
754    }
755
756    /// The number of written items.
757    #[inline]
758    pub fn len(&self) -> i32 {
759        self.array.len()
760    }
761
762    /// Whether no items have been written so far.
763    #[inline]
764    pub fn is_empty(&self) -> bool {
765        self.len() == 0
766    }
767
768    /// Write an item.
769    #[inline]
770    pub fn item(&mut self, value: T) -> &mut Self
771    where
772        T: Primitive,
773    {
774        self.array.item(value);
775        self
776    }
777
778    /// Write a sequence of items.
779    #[inline]
780    pub fn items(&mut self, values: impl IntoIterator<Item = T>) -> &mut Self
781    where
782        T: Primitive,
783    {
784        self.array.items(values);
785        self
786    }
787
788    /// Start writing an item with the typed writer.
789    ///
790    /// Returns `T` but with its lifetime rewritten from `'a` to `'b`.
791    #[inline]
792    pub fn push<'b>(&'b mut self) -> <T as Rewrite<'b>>::Output
793    where
794        T: Writer<'a> + Rewrite<'b>,
795    {
796        <T as Rewrite>::Output::start(self.array.push())
797    }
798}
799
800/// Writer for a dictionary.
801pub struct Dict<'a> {
802    buf: &'a mut Buf,
803    indirect: bool,
804    indent: u8,
805    len: i32,
806}
807
808writer!(Dict: |obj| {
809    obj.buf.extend(b"<<");
810    Self {
811        buf: obj.buf,
812        indirect: obj.indirect,
813        indent: obj.indent.saturating_add(2),
814        len: 0,
815    }
816});
817
818impl<'a> Dict<'a> {
819    /// The number of written pairs.
820    #[inline]
821    pub fn len(&self) -> i32 {
822        self.len
823    }
824
825    /// Whether no pairs have been written so far.
826    #[inline]
827    pub fn is_empty(&self) -> bool {
828        self.len == 0
829    }
830
831    /// Start writing a pair with an arbitrary value.
832    #[inline]
833    pub fn insert(&mut self, key: Name) -> Obj<'_> {
834        self.len += 1;
835        self.buf.push(b'\n');
836
837        for _ in 0..self.indent {
838            self.buf.push(b' ');
839        }
840
841        self.buf.push_val(key);
842        self.buf.push(b' ');
843
844        Obj::direct(self.buf, self.indent)
845    }
846
847    /// Write a pair with a primitive value.
848    ///
849    /// This is a shorthand for `dict.insert(key).primitive(value)`.
850    #[inline]
851    pub fn pair<T: Primitive>(&mut self, key: Name, value: T) -> &mut Self {
852        self.insert(key).primitive(value);
853        self
854    }
855
856    /// Write a sequence of pairs with primitive values.
857    pub fn pairs<'n, T: Primitive>(
858        &mut self,
859        pairs: impl IntoIterator<Item = (Name<'n>, T)>,
860    ) -> &mut Self {
861        for (key, value) in pairs {
862            self.pair(key, value);
863        }
864        self
865    }
866
867    /// Convert into a typed version.
868    #[inline]
869    pub fn typed<T>(self) -> TypedDict<'a, T> {
870        TypedDict::wrap(self)
871    }
872}
873
874impl Drop for Dict<'_> {
875    #[inline]
876    fn drop(&mut self) {
877        self.buf.limits.register_dict_entries(self.len as usize);
878
879        if self.len != 0 {
880            self.buf.push(b'\n');
881            for _ in 0..self.indent - 2 {
882                self.buf.push(b' ');
883            }
884        }
885        self.buf.extend(b">>");
886        if self.indirect {
887            self.buf.extend(b"\nendobj\n\n");
888        }
889    }
890}
891
892/// Writer for a dictionary mapping to a fixed type.
893pub struct TypedDict<'a, T> {
894    dict: Dict<'a>,
895    phantom: PhantomData<fn() -> T>,
896}
897
898impl<'a, T> Writer<'a> for TypedDict<'a, T> {
899    fn start(obj: Obj<'a>) -> Self {
900        Self { dict: obj.dict(), phantom: PhantomData }
901    }
902}
903
904impl<'a, T> Rewrite<'a> for TypedDict<'_, T> {
905    type Output = TypedDict<'a, T>;
906}
907
908impl<'a, T> TypedDict<'a, T> {
909    /// Wrap a dictionary to make it type-safe.
910    #[inline]
911    pub fn wrap(dict: Dict<'a>) -> Self {
912        Self { dict, phantom: PhantomData }
913    }
914
915    /// The number of written pairs.
916    #[inline]
917    pub fn len(&self) -> i32 {
918        self.dict.len()
919    }
920
921    /// Whether no pairs have been written so far.
922    #[inline]
923    pub fn is_empty(&self) -> bool {
924        self.len() == 0
925    }
926
927    /// Write a key-value pair.
928    #[inline]
929    pub fn pair(&mut self, key: Name, value: T) -> &mut Self
930    where
931        T: Primitive,
932    {
933        self.dict.pair(key, value);
934        self
935    }
936
937    /// Write a sequence of key-value pairs.
938    #[inline]
939    pub fn pairs<'n>(
940        &mut self,
941        pairs: impl IntoIterator<Item = (Name<'n>, T)>,
942    ) -> &mut Self
943    where
944        T: Primitive,
945    {
946        self.dict.pairs(pairs);
947        self
948    }
949
950    /// Start writing a pair with the typed writer.
951    ///
952    /// Returns `T` but with its lifetime rewritten from `'a` to `'b`.
953    #[inline]
954    pub fn insert<'b>(&'b mut self, key: Name) -> <T as Rewrite<'b>>::Output
955    where
956        T: Writer<'a> + Rewrite<'b>,
957    {
958        <T as Rewrite>::Output::start(self.dict.insert(key))
959    }
960}
961
962/// Writer for an indirect stream object.
963pub struct Stream<'a> {
964    dict: ManuallyDrop<Dict<'a>>,
965    data: &'a [u8],
966}
967
968impl<'a> Stream<'a> {
969    /// Start writing a stream.
970    ///
971    /// Panics if the object writer is not indirect or the stream length exceeds
972    /// `i32::MAX`.
973    pub(crate) fn start(obj: Obj<'a>, data: &'a [u8]) -> Self {
974        assert!(obj.indirect);
975
976        let mut dict = obj.dict();
977        dict.pair(
978            Name(b"Length"),
979            i32::try_from(data.len()).unwrap_or_else(|_| {
980                panic!("data length (is `{}`) must be <= i32::MAX", data.len());
981            }),
982        );
983
984        Self { dict: ManuallyDrop::new(dict), data }
985    }
986
987    /// Write the `/Filter` attribute.
988    pub fn filter(&mut self, filter: Filter) -> &mut Self {
989        self.pair(Name(b"Filter"), filter.to_name());
990        self
991    }
992
993    /// Start writing the `/DecodeParms` attribute.
994    ///
995    /// This is a dictionary that specifies parameters to be used in decoding
996    /// the stream data using the filter specified by the
997    /// [`/Filter`](Self::filter) attribute.
998    pub fn decode_parms(&mut self) -> DecodeParms<'_> {
999        self.insert(Name(b"DecodeParms")).start()
1000    }
1001}
1002
1003impl Drop for Stream<'_> {
1004    fn drop(&mut self) {
1005        let dict_len = self.dict.len as usize;
1006        self.dict.buf.limits.register_dict_entries(dict_len);
1007
1008        self.dict.buf.extend(b"\n>>");
1009        self.dict.buf.extend(b"\nstream\n");
1010        self.dict.buf.extend(self.data.as_ref());
1011        self.dict.buf.extend(b"\nendstream");
1012        self.dict.buf.extend(b"\nendobj\n\n");
1013    }
1014}
1015
1016deref!('a, Stream<'a> => Dict<'a>, dict);
1017
1018/// A compression filter for a stream.
1019#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
1020#[allow(missing_docs)]
1021pub enum Filter {
1022    AsciiHexDecode,
1023    Ascii85Decode,
1024    /// Lempel-Ziv-Welch (LZW) compression.
1025    ///
1026    /// Note that this filter is forbidden in PDF/A.
1027    LzwDecode,
1028    FlateDecode,
1029    RunLengthDecode,
1030    CcittFaxDecode,
1031    Jbig2Decode,
1032    /// Decodes JPEG/JFIF files with a SOF0, SOF1, or (PDF 1.3+) SOF2 marker.
1033    ///
1034    /// See ISO 32000-1:2008, Section 7.4.8 and Adobe Technical Note #5116.
1035    DctDecode,
1036    /// Decodes JPEG2000 files with a JPX baseline marker.
1037    ///
1038    /// Note that additional restrictions are imposed by PDF/A and PDF/X.
1039    JpxDecode,
1040    /// Encrypt the stream.
1041    ///
1042    /// Note that this filter is restricted in PDF/A.
1043    Crypt,
1044}
1045
1046impl Filter {
1047    pub(crate) fn to_name(self) -> Name<'static> {
1048        match self {
1049            Self::AsciiHexDecode => Name(b"ASCIIHexDecode"),
1050            Self::Ascii85Decode => Name(b"ASCII85Decode"),
1051            Self::LzwDecode => Name(b"LZWDecode"),
1052            Self::FlateDecode => Name(b"FlateDecode"),
1053            Self::RunLengthDecode => Name(b"RunLengthDecode"),
1054            Self::CcittFaxDecode => Name(b"CCITTFaxDecode"),
1055            Self::Jbig2Decode => Name(b"JBIG2Decode"),
1056            Self::DctDecode => Name(b"DCTDecode"),
1057            Self::JpxDecode => Name(b"JPXDecode"),
1058            Self::Crypt => Name(b"Crypt"),
1059        }
1060    }
1061}
1062
1063/// Writer for an _filter decode parameters dictionary_.
1064///
1065/// This struct is created by [`Stream::decode_parms`].
1066pub struct DecodeParms<'a> {
1067    dict: Dict<'a>,
1068}
1069
1070writer!(DecodeParms: |obj| Self { dict: obj.dict() });
1071
1072/// Properties for `FlateDecode` and `LzwDecode`.
1073impl DecodeParms<'_> {
1074    /// Write the `/Predictor` attribute for `FlateDecode` and `LzwDecode`.
1075    ///
1076    /// No predictor is used by default.
1077    pub fn predictor(&mut self, predictor: Predictor) -> &mut Self {
1078        self.pair(Name(b"Predictor"), predictor.to_i32());
1079        self
1080    }
1081
1082    /// Write the `/Colors` attribute for `FlateDecode` and `LzwDecode`.
1083    ///
1084    /// Must be greater than 0. [`/Predictor`](Self::predictor) must be set.
1085    /// Defaults to 1.
1086    pub fn colors(&mut self, colors: i32) -> &mut Self {
1087        if colors <= 0 {
1088            panic!("`Colors` must be greater than 0");
1089        }
1090
1091        self.pair(Name(b"Colors"), colors);
1092        self
1093    }
1094
1095    /// Write the `/BitsPerComponent` attribute for `FlateDecode` and
1096    /// `LzwDecode`.
1097    ///
1098    /// Must be one of 1, 2, 4, 8, or 16. [`/Predictor`](Self::predictor) must
1099    /// be set. Defaults to 8.
1100    pub fn bits_per_component(&mut self, bits: i32) -> &mut Self {
1101        if ![1, 2, 4, 8, 16].contains(&bits) {
1102            panic!("`BitsPerComponent` must be one of 1, 2, 4, 8, or 16");
1103        }
1104
1105        self.pair(Name(b"BitsPerComponent"), bits);
1106        self
1107    }
1108
1109    /// Write the `/Columns` attribute for `FlateDecode` and `LzwDecode` or
1110    /// `CcittFaxDecode`.
1111    ///
1112    /// When used with `FlateDecode` and `LzwDecode`, it indicates the number of
1113    /// samples in each row. In that case, [`/Predictor`](Self::predictor) must
1114    /// be set and the default is 1.
1115    ///
1116    /// When used with `CcittFaxDecode` it denominates the width of the image in
1117    /// pixels and defaults to 1728.
1118    pub fn columns(&mut self, columns: i32) -> &mut Self {
1119        self.pair(Name(b"Columns"), columns);
1120        self
1121    }
1122
1123    /// Write the `/EarlyChange` attribute for `LzwDecode`.
1124    ///
1125    /// If `true` (1), the code length increases one code earlier, if `false`
1126    /// (0), length change is postponed as long as possible.
1127    ///
1128    /// Defaults to 1.
1129    pub fn early_change(&mut self, early_change: bool) -> &mut Self {
1130        self.pair(Name(b"EarlyChange"), if early_change { 1 } else { 0 });
1131        self
1132    }
1133}
1134
1135/// Properties for `CcittFaxDecode`. Also see [`Self::columns`].
1136impl DecodeParms<'_> {
1137    /// Write the `/K` attribute for `CcittFaxDecode`.
1138    ///
1139    /// Defaults to 0.
1140    pub fn k(&mut self, k: i32) -> &mut Self {
1141        self.pair(Name(b"K"), k);
1142        self
1143    }
1144
1145    /// Write the `/EndOfLine` attribute for `CcittFaxDecode`.
1146    ///
1147    /// Whether the EOL bit pattern is present in the encoding. Defaults to
1148    /// `false`.
1149    pub fn end_of_line(&mut self, eol: bool) -> &mut Self {
1150        self.pair(Name(b"EndOfLine"), eol);
1151        self
1152    }
1153
1154    /// Write the `/EncodedByteAlign` attribute for `CcittFaxDecode`.
1155    ///
1156    /// Whether to expect zero bits before each encoded line. Defaults to
1157    /// `false`.
1158    pub fn encoded_byte_align(&mut self, encoded_byte_align: bool) -> &mut Self {
1159        self.pair(Name(b"EncodedByteAlign"), encoded_byte_align);
1160        self
1161    }
1162
1163    /// Write the `/Rows` attribute for `CcittFaxDecode`.
1164    ///
1165    /// The image's height. Defaults to 0.
1166    pub fn rows(&mut self, rows: i32) -> &mut Self {
1167        self.pair(Name(b"Rows"), rows);
1168        self
1169    }
1170
1171    /// Write the `/EndOfBlock` attribute for `CcittFaxDecode`.
1172    ///
1173    /// Whether to expect an EOB code at the end of the data. Defaults to
1174    /// `true`.
1175    pub fn end_of_block(&mut self, end_of_block: bool) -> &mut Self {
1176        self.pair(Name(b"EndOfBlock"), end_of_block);
1177        self
1178    }
1179
1180    /// Write the `/BlackIs1` attribute for `CcittFaxDecode`.
1181    ///
1182    /// Whether to invert the bits in the image. Defaults to `false`.
1183    pub fn black_is_1(&mut self, black_is_1: bool) -> &mut Self {
1184        self.pair(Name(b"BlackIs1"), black_is_1);
1185        self
1186    }
1187
1188    /// Write the `/DamagedRowsBeforeError` attribute for `CcittFaxDecode`.
1189    ///
1190    /// How many damaged rows are allowed before an error is raised. Defaults to
1191    /// 0.
1192    pub fn damaged_rows_before_error(&mut self, count: i32) -> &mut Self {
1193        self.pair(Name(b"DamagedRowsBeforeError"), count);
1194        self
1195    }
1196}
1197
1198/// Properties for `Jbig2Decode`.
1199impl DecodeParms<'_> {
1200    /// Write the `/JBIG2Globals` attribute for `Jbig2Decode`.
1201    ///
1202    /// A reference to a stream containing global segments.
1203    pub fn jbig2_globals(&mut self, globals: Ref) -> &mut Self {
1204        self.pair(Name(b"JBIG2Globals"), globals);
1205        self
1206    }
1207}
1208
1209/// Properties for `JpxDecode`.
1210impl DecodeParms<'_> {
1211    /// Write the `/ColorTransform` attribute for `JpxDecode`.
1212    ///
1213    /// How to handle color data. If `true` (1), images with three color
1214    /// channels shall be decoded from the YCbCr space and images with four
1215    /// color channels are decoded from YCbCrK. If `false` (0), no
1216    /// transformation is applied. The default depends on the `APP14` marker in
1217    /// the data stream.
1218    pub fn color_transform(&mut self, color_transform: bool) -> &mut Self {
1219        self.pair(Name(b"ColorTransform"), if color_transform { 1 } else { 0 });
1220        self
1221    }
1222}
1223
1224/// Properties for `Crypt`.
1225impl DecodeParms<'_> {
1226    /// Write the `/Type` attribute for `Crypt` as `CryptFilterDecodeParms`.
1227    pub fn crypt_type(&mut self) -> &mut Self {
1228        self.pair(Name(b"Type"), Name(b"CryptFilterDecodeParms"));
1229        self
1230    }
1231
1232    /// Write the `/Name` attribute for `Crypt`.
1233    ///
1234    /// The name of the crypt filter corresponding to a `CF` entry of the
1235    /// encryption dictionary.
1236    pub fn name(&mut self, name: Name) -> &mut Self {
1237        self.pair(Name(b"Name"), name);
1238        self
1239    }
1240}
1241
1242deref!('a, DecodeParms<'a> => Dict<'a>, dict);
1243
1244/// Which kind of predictor to use for a `FlateDecode` or `LzwDecode` stream.
1245#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Hash)]
1246#[allow(missing_docs)]
1247pub enum Predictor {
1248    /// No prediction.
1249    #[default]
1250    None,
1251    /// TIFF Predictor 2.
1252    Tiff,
1253    PngNone,
1254    PngSub,
1255    PngUp,
1256    PngAverage,
1257    PngPaeth,
1258    PngOptimum,
1259}
1260
1261impl Predictor {
1262    /// Convert the predictor to its integer representation according to ISO
1263    /// 32000-2:2020, Table E.
1264    fn to_i32(self) -> i32 {
1265        match self {
1266            Self::None => 1,
1267            Self::Tiff => 2,
1268            Self::PngNone => 10,
1269            Self::PngSub => 11,
1270            Self::PngUp => 12,
1271            Self::PngAverage => 13,
1272            Self::PngPaeth => 14,
1273            Self::PngOptimum => 15,
1274        }
1275    }
1276}
1277
1278/// Writer for a _name tree node_.
1279///
1280/// Name trees associate a large number of names with PDF objects. They are
1281/// lexically ordered search trees. Root nodes may directly contain all leafs,
1282/// however, this might degrade performance for very large numbers of
1283/// name-object pairs.
1284///
1285/// For each node, either the `/Kids` or `/Names` attribute must be set, but
1286/// never both.
1287pub struct NameTree<'a, T> {
1288    dict: Dict<'a>,
1289    phantom: PhantomData<T>,
1290}
1291
1292impl<'a, T> Writer<'a> for NameTree<'a, T> {
1293    fn start(obj: Obj<'a>) -> Self {
1294        Self { dict: obj.dict(), phantom: PhantomData }
1295    }
1296}
1297
1298impl<'a, T> Rewrite<'a> for NameTree<'_, T> {
1299    type Output = NameTree<'a, T>;
1300}
1301
1302impl<T> NameTree<'_, T> {
1303    /// Start writing the `/Kids` attribute with the children of this node.
1304    pub fn kids(&mut self) -> TypedArray<'_, Ref> {
1305        self.dict.insert(Name(b"Kids")).array().typed()
1306    }
1307
1308    /// Start writing the `/Names` attribute to set the immediate name-to-object
1309    /// mappings of this node.
1310    pub fn names(&mut self) -> NameTreeEntries<'_, T> {
1311        self.dict.insert(Name(b"Names")).start()
1312    }
1313
1314    /// Write the `/Limits` array to set the range of names in this node. This
1315    /// is required for every node except the root node.
1316    pub fn limits(&mut self, min: Name, max: Name) -> &mut Self {
1317        self.dict.insert(Name(b"Limits")).array().typed().items([min, max]);
1318        self
1319    }
1320}
1321
1322/// Writer for a _name tree names_ array.
1323///
1324/// The children must be added in ascending lexical order. Their minimum and
1325/// maximum keys must not exceed the `/Limits` property of the parent [`NameTree`]
1326/// node. This struct is created by [`NameTree::names`].
1327pub struct NameTreeEntries<'a, T> {
1328    arr: Array<'a>,
1329    phantom: PhantomData<T>,
1330}
1331
1332impl<'a, T> Writer<'a> for NameTreeEntries<'a, T> {
1333    fn start(obj: Obj<'a>) -> Self {
1334        Self { arr: obj.array(), phantom: PhantomData }
1335    }
1336}
1337
1338impl<'a, T> Rewrite<'a> for NameTreeEntries<'_, T> {
1339    type Output = NameTreeEntries<'a, T>;
1340}
1341
1342impl<T> NameTreeEntries<'_, T>
1343where
1344    T: Primitive,
1345{
1346    /// Insert a name-value pair.
1347    pub fn insert(&mut self, key: Str, value: T) -> &mut Self {
1348        self.arr.item(key);
1349        self.arr.item(value);
1350        self
1351    }
1352}
1353
1354/// Writer for a _number tree node_.
1355///
1356/// Number trees associate a many integers with PDF objects. They are search
1357/// trees in ascending order. Root nodes may directly contain all leafs,
1358/// however, this might degrade performance for very large numbers of
1359/// integer-object pairs.
1360///
1361/// For each node, either the `/Kids` or `/Nums` attribute must be set, but
1362/// never both.
1363pub struct NumberTree<'a, T> {
1364    dict: Dict<'a>,
1365    phantom: PhantomData<T>,
1366}
1367
1368impl<'a, T> Writer<'a> for NumberTree<'a, T> {
1369    fn start(obj: Obj<'a>) -> Self {
1370        Self { dict: obj.dict(), phantom: PhantomData }
1371    }
1372}
1373
1374impl<'a, T> Rewrite<'a> for NumberTree<'_, T> {
1375    type Output = NumberTree<'a, T>;
1376}
1377
1378impl<T> NumberTree<'_, T> {
1379    /// Start writing the `/Kids` attribute with the children of this node.
1380    pub fn kids(&mut self) -> TypedArray<'_, Ref> {
1381        self.dict.insert(Name(b"Kids")).array().typed()
1382    }
1383
1384    /// Start writing the `/Nums` attribute to set the immediate
1385    /// number-to-object mappings of this node.
1386    pub fn nums(&mut self) -> NumberTreeEntries<'_, T> {
1387        self.dict.insert(Name(b"Nums")).start()
1388    }
1389
1390    /// Write the `/Limits` array to set the range of numbers in this node. This
1391    /// is required for every node except the root node.
1392    pub fn limits(&mut self, min: i32, max: i32) -> &mut Self {
1393        self.dict.insert(Name(b"Limits")).array().typed().items([min, max]);
1394        self
1395    }
1396}
1397
1398/// Writer for a _number tree numbers_ array.
1399///
1400/// The children must be added in ascending order. Their minimum and
1401/// maximum keys must not exceed the `/Limits` property of the parent [`NumberTree`]
1402/// node. This struct is created by [`NumberTree::nums`].
1403pub struct NumberTreeEntries<'a, T> {
1404    arr: Array<'a>,
1405    phantom: PhantomData<T>,
1406}
1407
1408impl<'a, T> Writer<'a> for NumberTreeEntries<'a, T> {
1409    fn start(obj: Obj<'a>) -> Self {
1410        Self { arr: obj.array(), phantom: PhantomData }
1411    }
1412}
1413
1414impl<'a, T> Rewrite<'a> for NumberTreeEntries<'_, T> {
1415    type Output = NumberTreeEntries<'a, T>;
1416}
1417
1418impl<T> NumberTreeEntries<'_, T>
1419where
1420    T: Primitive,
1421{
1422    /// Insert a number-value pair.
1423    pub fn insert(&mut self, key: i32, value: T) -> &mut Self {
1424        self.arr.item(key);
1425        self.arr.item(value);
1426        self
1427    }
1428}
1429
1430/// Finish objects in postfix-style.
1431///
1432/// In many cases you can use writers in builder-pattern style so that they are
1433/// automatically dropped at the appropriate time. Sometimes though you need to
1434/// bind a writer to a variable and still want to regain access to the
1435/// [`Pdf`] in the same scope. In that case, you need to manually invoke
1436/// the writer's `Drop` implementation. You can of course, just write
1437/// `drop(array)` to finish your array, but you might find it more aesthetically
1438/// pleasing to write `array.finish()`. That's what this trait is for.
1439///
1440/// ```
1441/// # use pdf_writer::{Pdf, Ref, Finish, Name, Str};
1442/// # let mut pdf = Pdf::new();
1443/// let mut array = pdf.indirect(Ref::new(1)).array();
1444/// array.push().dict().pair(Name(b"Key"), Str(b"Value"));
1445/// array.item(2);
1446/// array.finish(); // instead of drop(array)
1447///
1448/// // Do more stuff with `pdf` ...
1449/// ```
1450pub trait Finish: Sized {
1451    /// Does nothing but move `self`, equivalent to [`drop`].
1452    #[inline]
1453    fn finish(self) {}
1454}
1455
1456impl<T> Finish for T {}
1457
1458#[cfg(test)]
1459mod tests {
1460    use super::*;
1461
1462    #[test]
1463    fn test_primitive_objects() {
1464        // Test really simple objects.
1465        test_primitive!(true, b"true");
1466        test_primitive!(false, b"false");
1467        test_primitive!(78, b"78");
1468        test_primitive!(4.22, b"4.22");
1469        test_primitive!(1.184e-7, b"0.0000001184");
1470        test_primitive!(4.2e13, b"42000000000000");
1471        test_primitive!(Ref::new(7), b"7 0 R");
1472        test_primitive!(Null, b"null");
1473
1474        // Test strings.
1475        test_primitive!(Str(b"Hello, World!"), b"(Hello, World!)");
1476        test_primitive!(Str(b"()"), br"(())");
1477        test_primitive!(Str(b")()"), br"(\)\(\))");
1478        test_primitive!(Str(b"()(())"), br"(()(()))");
1479        test_primitive!(Str(b"(()))"), br"(\(\(\)\)\))");
1480        test_primitive!(Str(b"\\"), br"(\\)");
1481        test_primitive!(Str(b"\n\ta"), br"(\n\ta)");
1482        test_primitive!(Str(br"\n"), br"(\\n)");
1483        test_primitive!(Str(b"a\x14b"), br"(a\024b)");
1484        test_primitive!(Str(b"\xFF\xAA"), b"<FFAA>");
1485        test_primitive!(Str(b"\x0A\x7F\x1F"), br"(\n\177\037)");
1486
1487        // Test text strings.
1488        test_primitive!(TextStr("Hallo"), b"(Hallo)");
1489        test_primitive!(TextStr("😀!"), b"<FEFFD83DDE000021>");
1490
1491        // Test names.
1492        test_primitive!(Name(b"Filter"), b"/Filter");
1493        test_primitive!(Name(b"A B"), br"/A#20B");
1494        test_primitive!(Name(b"~+c"), br"/~+c");
1495        test_primitive!(Name(b"/A-B"), br"/#2FA-B");
1496        test_primitive!(Name(b"<A>"), br"/#3CA#3E");
1497        test_primitive!(Name(b"#"), br"/#23");
1498        test_primitive!(Name(b"\n"), br"/#0A");
1499    }
1500
1501    #[test]
1502    fn test_dates() {
1503        test_primitive!(Date::new(2021), b"(D:2021)");
1504        test_primitive!(Date::new(2021).month(30), b"(D:202112)");
1505
1506        let date = Date::new(2020).month(3).day(17).hour(1).minute(2).second(3);
1507        test_primitive!(date, b"(D:20200317010203)");
1508        test_primitive!(date.utc_offset_hour(0), b"(D:20200317010203Z)");
1509        test_primitive!(date.utc_offset_hour(4), b"(D:20200317010203+04'00)");
1510        test_primitive!(
1511            date.utc_offset_hour(-17).utc_offset_minute(10),
1512            b"(D:20200317010203-17'10)"
1513        );
1514    }
1515
1516    #[test]
1517    fn test_arrays() {
1518        test_obj!(|obj| obj.array(), b"[]");
1519        test_obj!(|obj| obj.array().item(12).item(Null), b"[12 null]");
1520        test_obj!(|obj| obj.array().typed().items(vec![1, 2, 3]), b"[1 2 3]");
1521        test_obj!(
1522            |obj| {
1523                let mut array = obj.array();
1524                array.push().array().typed().items(vec![1, 2]);
1525                array.item(3);
1526            },
1527            b"[[1 2] 3]",
1528        );
1529    }
1530
1531    #[test]
1532    fn test_dicts() {
1533        test_obj!(|obj| obj.dict(), b"<<>>");
1534        test_obj!(
1535            |obj| obj.dict().pair(Name(b"Quality"), Name(b"Good")),
1536            b"<<\n  /Quality /Good\n>>",
1537        );
1538        test_obj!(
1539            |obj| {
1540                obj.dict().pair(Name(b"A"), 1).pair(Name(b"B"), 2);
1541            },
1542            b"<<\n  /A 1\n  /B 2\n>>",
1543        );
1544    }
1545
1546    #[test]
1547    fn test_streams() {
1548        let mut w = Pdf::new();
1549        w.stream(Ref::new(1), &b"Hi there!"[..]).filter(Filter::Crypt);
1550        test!(
1551            w.finish(),
1552            b"%PDF-1.7\n%\x80\x80\x80\x80\n",
1553            b"1 0 obj",
1554            b"<<\n  /Length 9\n  /Filter /Crypt\n>>",
1555            b"stream",
1556            b"Hi there!",
1557            b"endstream",
1558            b"endobj\n",
1559            b"xref",
1560            b"0 2",
1561            b"0000000000 65535 f\r",
1562            b"0000000016 00000 n\r",
1563            b"trailer",
1564            b"<<\n  /Size 2\n>>",
1565            b"startxref\n94\n%%EOF",
1566        )
1567    }
1568}