pdf_create/
low.rs

1//! Low-Level API
2//!
3//! This module contains structs and enums for representing/creating a PDF
4//! that is already split up into objects with opaque reference IDs.
5
6use std::{borrow::Cow, io};
7
8use crate::{common::Dict, common::Encoding, common::Matrix, common::ObjRef, common::ProcSet, common::Rectangle, encoding::ascii_85_encode, write::Formatter, write::PdfName, common::PdfString, write::Serialize};
9
10/// Destination of a GoTo action
11#[derive(Debug, Clone)]
12pub enum Destination {
13    /// Page @0, fit the page into view and scroll to height {1}
14    PageFitH(ObjRef, usize),
15}
16
17/// A PDF action
18#[derive(Debug, Clone)]
19pub enum Action {
20    /// Go to some destination within the document
21    GoTo(Destination),
22}
23
24impl Serialize for Destination {
25    fn write(&self, f: &mut Formatter) -> io::Result<()> {
26        match self {
27            Self::PageFitH(r, top) => f
28                .pdf_arr()
29                .entry(r)?
30                .entry(&PdfName("FitH"))?
31                .entry(top)?
32                .finish(),
33        }
34    }
35}
36
37/// The root outline item
38#[derive(Debug, Clone)]
39pub struct Outline {
40    /// The first item
41    pub first: ObjRef,
42    /// The last item
43    pub last: ObjRef,
44    /// The total amount of items
45    pub count: usize,
46}
47
48impl Serialize for Outline {
49    fn write(&self, f: &mut Formatter) -> io::Result<()> {
50        f.pdf_dict()
51            .field("Type", &PdfName("Outline"))?
52            .field("First", &self.first)?
53            .field("Last", &self.last)?
54            .field("Count", &self.count)?
55            .finish()
56    }
57}
58
59/// A child outline item
60#[derive(Debug, Clone)]
61pub struct OutlineItem {
62    /// The title of the outline item
63    pub title: PdfString,
64    /// The parent of this item
65    pub parent: ObjRef,
66    /// The previous siblig
67    pub prev: Option<ObjRef>,
68    /// The next sibling
69    pub next: Option<ObjRef>,
70    /// The first child
71    pub first: Option<ObjRef>,
72    /// The last child
73    pub last: Option<ObjRef>,
74    /// The total amount of children
75    pub count: usize,
76    /// The destination to be used
77    pub action: Action,
78}
79
80impl Serialize for OutlineItem {
81    fn write(&self, f: &mut Formatter) -> io::Result<()> {
82        let mut dict = f.pdf_dict();
83        dict.field("Title", &self.title)?
84            .field("Parent", &self.parent)?
85            .opt_field("Prev", &self.prev)?
86            .opt_field("Next", &self.next)?
87            .opt_field("First", &self.first)?
88            .opt_field("Last", &self.last)?
89            .field("Count", &self.count)?;
90        match &self.action {
91            Action::GoTo(dest) => dict.field("Dest", dest),
92        }?;
93        dict.finish()
94    }
95}
96
97/// A page object
98pub struct Page<'a> {
99    /// Reference to the parent
100    pub parent: ObjRef,
101    /// The content stream of the page
102    pub contents: ObjRef,
103    /// The resources of this page
104    pub resources: Resources<'a>,
105    /// (required, inheritable) describes the bound of the physical page
106    /// in default user units
107    pub media_box: Option<Rectangle<i32>>,
108}
109
110impl Serialize for Page<'_> {
111    fn write(&self, f: &mut Formatter) -> io::Result<()> {
112        f.pdf_dict()
113            .field("Type", &PdfName("Page"))?
114            .field("Parent", &self.parent)?
115            .opt_field("MediaBox", &self.media_box)?
116            .field("Resources", &self.resources)?
117            .field("Contents", &self.contents)?
118            .finish()
119    }
120}
121
122/// A resource entry
123pub enum Resource<T> {
124    /// Reference to another object
125    Ref(ObjRef),
126    /// A resource that is serialized in place
127    Immediate(T),
128}
129
130impl<T: Serialize> Serialize for Resource<T> {
131    fn write(&self, f: &mut Formatter) -> io::Result<()> {
132        match self {
133            Self::Ref(r) => r.write(f),
134            Self::Immediate(value) => value.write(f),
135        }
136    }
137}
138
139/// A type 3 font resource
140pub struct Type3Font<'a> {
141    /// The name of the object
142    pub name: Option<PdfName<'a>>,
143    /// The largest boundig box that fits all glyphs
144    pub font_bbox: Rectangle<i32>,
145    /// The matrix to map glyph space into text space
146    pub font_matrix: Matrix<f32>,
147    /// The first used char key
148    pub first_char: u8,
149    /// The last used char key
150    pub last_char: u8,
151    /// Dict of encoding value to char names
152    pub encoding: Resource<Encoding<'a>>,
153    /// Dict of char names to drawing procedures
154    pub char_procs: Dict<ObjRef>,
155    /// Width of every char between first and last
156    pub widths: &'a [u32],
157}
158
159/// A font resource
160pub enum Font<'a> {
161    /// A type 3 font resource
162    Type3(Type3Font<'a>),
163}
164
165impl Serialize for Font<'_> {
166    fn write(&self, f: &mut Formatter) -> io::Result<()> {
167        let mut dict = f.pdf_dict();
168        dict.field("Type", &PdfName("Font"))?;
169        match self {
170            Self::Type3(font) => {
171                dict.field("Subtype", &PdfName("Type3"))?
172                    .opt_field("BaseFont", &font.name)?
173                    .field("FontBBox", &font.font_bbox)?
174                    .field("FontMatrix", &font.font_matrix)?
175                    .field("FirstChar", &font.first_char)?
176                    .field("LastChar", &font.last_char)?
177                    .field("Encoding", &font.encoding)?
178                    .field("CharProcs", &font.char_procs)?
179                    .arr_field("Widths", &font.widths)?;
180            }
181        }
182        dict.finish()?;
183        Ok(())
184    }
185}
186
187/// A character drawing procedure
188pub struct CharProc<'a>(pub Cow<'a, [u8]>);
189
190impl<'a> Serialize for CharProc<'a> {
191    fn write(&self, f: &mut Formatter) -> io::Result<()> {
192        f.pdf_dict()
193            .field("Length", &self.0.len())?
194            .field("Filter", &PdfName("ASCII85Decode"))?
195            .finish()?;
196        let mut buf = Vec::new();
197        ascii_85_encode(self.0.as_ref(), &mut buf)?;
198        buf.push(10);
199        f.pdf_stream(&buf)?;
200        Ok(())
201    }
202}
203
204/// An emedded object resource
205pub enum XObject {
206    /// An image object
207    Image {},
208}
209
210impl Serialize for XObject {
211    fn write(&self, _f: &mut Formatter) -> io::Result<()> {
212        todo!()
213    }
214}
215
216/// A dict of resources
217pub type DictResource<T> = Dict<Resource<T>>;
218/// A referenced or immediate dict of resources
219pub type ResDictRes<T> = Resource<Dict<Resource<T>>>;
220
221/// The resources of a page
222pub struct Resources<'a> {
223    /// A dict of font resources
224    pub font: ResDictRes<Font<'a>>,
225    /// A dict of embedded object resources
226    pub x_object: ResDictRes<XObject>,
227    /// A set of valid procedures
228    pub proc_set: &'a [ProcSet],
229}
230
231impl Serialize for Resources<'_> {
232    fn write(&self, f: &mut Formatter) -> io::Result<()> {
233        f.pdf_dict()
234            .dict_res_field("Font", &self.font)?
235            .dict_res_field("XObject", &self.x_object)?
236            .arr_field("ProcSet", &self.proc_set)?
237            .finish()
238    }
239}
240
241/// The list of pages
242pub struct Pages {
243    /// References to the individual pages
244    pub kids: Vec<ObjRef>,
245}
246
247impl Serialize for Pages {
248    fn write(&self, f: &mut Formatter) -> io::Result<()> {
249        f.pdf_dict()
250            .field("Type", &PdfName("Pages"))?
251            .field("Count", &self.kids.len())?
252            .field("Kids", &self.kids)?
253            .finish()
254    }
255}
256
257/// A data stream
258pub struct Stream {
259    /// The (unencoded) data
260    pub data: Vec<u8>,
261}
262
263impl Serialize for Stream {
264    fn write(&self, f: &mut Formatter) -> io::Result<()> {
265        f.pdf_dict().field("Length", &self.data.len())?.finish()?;
266        f.pdf_stream(&self.data)?;
267        Ok(())
268    }
269}
270
271/// Well-known PDF Versions
272pub enum PdfVersion {
273    /// PDF-1.0
274    V1_0,
275    /// PDF-1.1
276    V1_1,
277    /// PDF-1.2
278    V1_2,
279    /// PDF-1.3
280    V1_3,
281    /// PDF-1.4
282    V1_4,
283    /// PDF-1.5
284    V1_5,
285    /// PDF-1.6
286    V1_6,
287    /// PDF-1.7
288    V1_7,
289}
290
291impl Serialize for PdfVersion {
292    fn write(&self, f: &mut Formatter) -> io::Result<()> {
293        match self {
294            Self::V1_0 => PdfName("1.0").write(f),
295            Self::V1_1 => PdfName("1.1").write(f),
296            Self::V1_2 => PdfName("1.2").write(f),
297            Self::V1_3 => PdfName("1.3").write(f),
298            Self::V1_4 => PdfName("1.4").write(f),
299            Self::V1_5 => PdfName("1.5").write(f),
300            Self::V1_6 => PdfName("1.6").write(f),
301            Self::V1_7 => PdfName("1.7").write(f),
302        }
303    }
304}
305
306/// The catalog/root of the document
307pub struct Catalog {
308    /// The PDF Version
309    pub version: Option<PdfVersion>,
310    // Extensions
311    /// Reference to the list of pages
312    pub pages: ObjRef,
313    /// Optional reference to the page labels
314    pub page_labels: Option<ObjRef>,
315    /// Optional reference to the outline
316    pub outline: Option<ObjRef>,
317}
318
319impl Serialize for Catalog {
320    fn write(&self, f: &mut Formatter) -> io::Result<()> {
321        f.pdf_dict()
322            .field("Type", &PdfName("Catalog"))?
323            .opt_field("Version", &self.version)?
324            .field("Pages", &self.pages)?
325            .opt_field("PageLabels", &self.page_labels)?
326            .opt_field("Outlines", &self.outline)?
327            .finish()
328    }
329}
330
331/// The trailer of the document
332pub struct Trailer {
333    /// The size of the document / number of objects
334    pub size: usize,
335    /// Optional reference to the info struct
336    pub info: Option<ObjRef>,
337    /// Refernce to the root/catalog
338    pub root: ObjRef,
339}
340
341impl Serialize for Trailer {
342    fn write(&self, f: &mut Formatter) -> io::Result<()> {
343        f.pdf_dict()
344            .field("Size", &self.size)?
345            .opt_field("Info", &self.info)?
346            .field("Root", &self.root)?
347            .finish()
348    }
349}