pdf_create/
lowering.rs

1//! Helpers to turn *high* types into *low* types
2
3use crate::{common::Encoding, high::CharProc, high::Destination, high::DictResource, high::Font, high::Handle, common::ObjRef, high::OutlineItem, high::ResDictRes, high::Resource, high::XObject, low, util::NextID};
4
5/// Make a ObjRef for an original document (generation 0)
6pub fn make_ref(id: u64) -> ObjRef {
7    ObjRef { id, gen: 0 }
8}
9
10fn lower_dest(pages: &[ObjRef], dest: Destination) -> low::Action {
11    use low::Action::*;
12    use low::Destination::*;
13    match dest {
14        Destination::PageFitH(a, top) => {
15            let page = pages[a];
16            GoTo(PageFitH(page, top))
17        }
18    }
19}
20
21pub(super) fn lower_outline_items(
22    acc: &mut Vec<(ObjRef, low::OutlineItem)>,
23    pages: &[ObjRef],
24    items: &[OutlineItem],
25    parent: ObjRef,
26    id_gen: &mut NextID,
27) -> Option<(ObjRef, ObjRef)> {
28    if let Some((last, rest)) = items.split_last() {
29        let mut prev = None;
30        let first_ref = make_ref(id_gen.next());
31        let mut curr = first_ref;
32
33        // most items
34        for item in rest {
35            let (fc, lc) = match lower_outline_items(acc, pages, &item.children, parent, id_gen) {
36                Some((fc, lc)) => (Some(fc), Some(lc)),
37                None => (None, None),
38            };
39            let action = lower_dest(pages, item.dest);
40            let next = make_ref(id_gen.next());
41            acc.push((
42                curr,
43                low::OutlineItem {
44                    title: item.title.clone(),
45                    parent,
46                    prev,
47                    next: Some(next),
48                    first: fc,
49                    last: lc,
50                    count: 0,
51                    action,
52                },
53            ));
54            prev = Some(curr);
55            curr = next;
56        }
57
58        // Last item
59        let (fc, lc) = match lower_outline_items(acc, pages, &last.children, parent, id_gen) {
60            Some((fc, lc)) => (Some(fc), Some(lc)),
61            None => (None, None),
62        };
63        let action = lower_dest(pages, last.dest);
64        acc.push((
65            curr,
66            low::OutlineItem {
67                title: last.title.clone(),
68                parent,
69                prev,
70                next: None,
71                first: fc,
72                last: lc,
73                count: 0,
74                action,
75            },
76        ));
77        Some((first_ref, curr))
78    } else {
79        None
80    }
81}
82
83pub(crate) trait Lowerable<'a> {
84    type Lower;
85    type Ctx;
86
87    fn lower(&'a self, ctx: &mut Self::Ctx, id_gen: &mut NextID) -> Self::Lower;
88    fn name() -> &'static str;
89}
90
91type LowerFontCtx<'a> = (LowerBox<'a, CharProc<'a>>, LowerBox<'a, Encoding<'a>>);
92
93fn lower_font<'a>(
94    font: &'a Font<'a>,
95    (a, _b): &mut LowerFontCtx<'a>,
96    id_gen: &mut NextID,
97) -> low::Font<'a> {
98    match font {
99        Font::Type3(font) => {
100            let char_procs = font
101                .char_procs
102                .iter()
103                .map(|(key, proc)| {
104                    let re = a.put(proc, id_gen);
105                    (key.clone(), re)
106                })
107                .collect();
108            low::Font::Type3(low::Type3Font {
109                name: font.name,
110                font_bbox: font.font_bbox,
111                font_matrix: font.font_matrix,
112                first_char: font.first_char,
113                last_char: font.last_char,
114                encoding: low::Resource::Immediate(font.encoding.clone()),
115                char_procs,
116                widths: &font.widths,
117            })
118        }
119    }
120}
121
122impl<'a> Lowerable<'a> for Font<'a> {
123    type Lower = low::Font<'a>;
124    type Ctx = LowerFontCtx<'a>;
125
126    fn lower(&'a self, ctx: &mut Self::Ctx, id_gen: &mut NextID) -> Self::Lower {
127        lower_font(self, ctx, id_gen)
128    }
129
130    fn name() -> &'static str {
131        "Font"
132    }
133}
134
135impl<'a> Lowerable<'a> for XObject {
136    type Lower = low::XObject;
137    type Ctx = ();
138
139    fn lower(&'a self, _ctx: &mut Self::Ctx, _id_gen: &mut NextID) -> Self::Lower {
140        todo!()
141    }
142
143    fn name() -> &'static str {
144        "XObject"
145    }
146}
147
148impl<'a> Lowerable<'a> for CharProc<'a> {
149    type Lower = low::CharProc<'a>;
150    type Ctx = ();
151
152    fn lower(&self, _ctx: &mut Self::Ctx, _id_gen: &mut NextID) -> Self::Lower {
153        low::CharProc(self.0.clone())
154    }
155
156    fn name() -> &'static str {
157        "CharProc"
158    }
159}
160
161impl<'a> Lowerable<'a> for Encoding<'a> {
162    type Lower = Encoding<'a>;
163    type Ctx = ();
164
165    fn lower(&self, _ctx: &mut Self::Ctx, _id_gen: &mut NextID) -> Self::Lower {
166        self.clone()
167    }
168
169    fn name() -> &'static str {
170        "CharProc"
171    }
172}
173
174pub(crate) struct LowerBox<'a, T> {
175    pub store: Vec<(ObjRef, &'a T)>,
176    res: &'a [T],
177}
178
179impl<'a, T> LowerBox<'a, T> {
180    fn new(res: &'a [T]) -> Self {
181        LowerBox { store: vec![], res }
182    }
183}
184
185pub(crate) fn lower_dict<'a, T: Lowerable<'a>>(
186    dict: &'a DictResource<T>,
187    inner: &mut LowerBox<'a, T>,
188    ctx: &mut T::Ctx,
189    id_gen: &mut NextID,
190) -> low::DictResource<T::Lower> {
191    dict.iter()
192        .map(|(key, res)| (key.clone(), inner.map(res, ctx, id_gen)))
193        .collect()
194}
195
196impl<'a, T: Lowerable<'a>> LowerBox<'a, DictResource<T>> {
197    pub fn map_dict(
198        &mut self,
199        res: &'a ResDictRes<T>,
200        inner: &mut LowerBox<'a, T>,
201        ctx: &mut T::Ctx,
202        id_gen: &mut NextID,
203    ) -> low::ResDictRes<T::Lower> {
204        match res {
205            Resource::Global { index } => {
206                if let Some((r, _)) = self.store.get(*index) {
207                    low::Resource::Ref(*r)
208                } else if let Some(font_dict) = self.res.get(*index) {
209                    let id = id_gen.next();
210                    let r = make_ref(id);
211                    self.store.push((r, font_dict));
212                    low::Resource::Ref(r)
213                } else {
214                    panic!("Couldn't find {} Dict #{}", T::name(), index);
215                }
216            }
217            Resource::Immediate(fonts) => {
218                let dict = lower_dict(fonts.as_ref(), inner, ctx, id_gen);
219                low::Resource::Immediate(dict)
220            }
221        }
222    }
223}
224
225impl<'a, T: Lowerable<'a>> LowerBox<'a, T> {
226    fn put(&mut self, val: &'a T, id_gen: &mut NextID) -> ObjRef {
227        let id = id_gen.next();
228        let r = make_ref(id);
229        self.store.push((r, val));
230        r
231    }
232
233    fn map(
234        &mut self,
235        res: &'a Resource<T>,
236        ctx: &mut T::Ctx,
237        id_gen: &mut NextID,
238    ) -> low::Resource<T::Lower> {
239        match res {
240            Resource::Global { index } => {
241                if let Some((r, _)) = self.store.get(*index) {
242                    low::Resource::Ref(*r)
243                } else if let Some(val) = self.res.get(*index) {
244                    let id = id_gen.next();
245                    let r = make_ref(id);
246                    self.store.push((r, val));
247                    low::Resource::Ref(r)
248                } else {
249                    panic!("Couldn't find {} #{}", T::name(), index);
250                }
251            }
252            Resource::Immediate(content) => {
253                let content_low = content.lower(ctx, id_gen);
254                low::Resource::Immediate(content_low)
255            }
256        }
257    }
258}
259
260pub(crate) struct Lowering<'a> {
261    pub id_gen: NextID,
262    pub x_objects: LowerBox<'a, XObject>,
263    pub x_object_dicts: LowerBox<'a, DictResource<XObject>>,
264    pub fonts: LowerBox<'a, Font<'a>>,
265    pub font_dicts: LowerBox<'a, DictResource<Font<'a>>>,
266    pub font_ctx: LowerFontCtx<'a>,
267}
268
269impl<'a> Lowering<'a> {
270    pub fn new(doc: &'a Handle) -> Self {
271        Lowering {
272            id_gen: NextID::new(1),
273            x_objects: LowerBox::new(&doc.res.x_objects),
274            x_object_dicts: LowerBox::new(&doc.res.x_object_dicts),
275            fonts: LowerBox::new(&doc.res.fonts),
276            font_dicts: LowerBox::new(&doc.res.font_dicts),
277            font_ctx: (
278                LowerBox::new(&doc.res.char_procs),
279                LowerBox::new(&doc.res.encodings),
280            ),
281        }
282    }
283}