1use 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
5pub 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 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 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}