1use crate::cache::{Cache, CacheKey};
2use crate::color::ColorSpace;
3use crate::convert::convert_transform;
4use crate::font::{Font, StandardFont};
5use crate::interpret::state::{ClipType, State, TextStateFont};
6use crate::ocg::OcgState;
7use crate::util::{BezPathExt, Float64Ext};
8use crate::{ClipPath, Device, FillRule, InterpreterSettings, StrokeProps};
9use hayro_syntax::content::ops::Transform;
10use hayro_syntax::object::Dict;
11use hayro_syntax::object::Name;
12use hayro_syntax::page::Resources;
13use hayro_syntax::xref::XRef;
14use kurbo::{Affine, BezPath, PathEl, Point, Rect, Shape};
15use std::cell::RefCell;
16use std::collections::HashMap;
17use std::rc::Rc;
18
19pub(crate) const MAX_NESTED_INTERPRETATION_DEPTH: u32 = 50;
21
22#[derive(Clone)]
27pub struct InterpreterCache<'a> {
28 pub(crate) font_cache: Rc<RefCell<HashMap<u128, Option<Font<'a>>>>>,
29 pub(crate) object_cache: Cache,
30}
31
32impl<'a> Default for InterpreterCache<'a> {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl<'a> InterpreterCache<'a> {
39 pub fn new() -> Self {
41 Self {
42 font_cache: Rc::new(RefCell::new(HashMap::new())),
43 object_cache: Cache::new(),
44 }
45 }
46}
47
48pub struct Context<'a> {
50 states: Vec<State<'a>>,
51 path: BezPath,
52 sub_path_start: Point,
53 last_point: Point,
54 clip: Option<FillRule>,
55 root_transforms: Vec<Affine>,
56 bbox: Vec<Rect>,
57 pub(crate) settings: InterpreterSettings,
58 pub(crate) interpreter_cache: InterpreterCache<'a>,
59 pub(crate) xref: &'a XRef,
60 pub(crate) ocg_state: OcgState,
61 nesting_depth: u32,
62}
63
64impl<'a> Context<'a> {
65 pub fn new(
67 initial_transform: Affine,
68 bbox: Rect,
69 cache: &InterpreterCache<'a>,
70 xref: &'a XRef,
71 settings: InterpreterSettings,
72 ) -> Self {
73 let state = State::new(initial_transform);
74
75 Self::new_with(initial_transform, bbox, cache, xref, settings, state, 0)
76 }
77
78 pub(crate) fn new_with(
79 initial_transform: Affine,
80 bbox: Rect,
81 cache: &InterpreterCache<'a>,
82 xref: &'a XRef,
83 settings: InterpreterSettings,
84 state: State<'a>,
85 nesting_depth: u32,
86 ) -> Self {
87 let ocg_state = {
88 let root_ref = xref.root_id();
89 xref.get::<Dict<'_>>(root_ref)
90 .map(|catalog| OcgState::from_catalog(&catalog))
91 .unwrap_or_default()
92 };
93
94 Self {
95 states: vec![state],
96 settings,
97 xref,
98 root_transforms: vec![initial_transform],
99 last_point: Point::default(),
100 sub_path_start: Point::default(),
101 clip: None,
102 bbox: vec![bbox],
103 path: BezPath::new(),
104 interpreter_cache: cache.clone(),
105 ocg_state,
106 nesting_depth,
107 }
108 }
109
110 pub(crate) fn save_state(&mut self) {
111 let Some(cur) = self.states.last().cloned() else {
112 warn!("attempted to save state without existing state");
113 return;
114 };
115
116 self.states.push(cur);
117 }
118
119 pub(crate) fn bbox(&self) -> Rect {
120 self.bbox.last().copied().unwrap_or_else(|| {
121 warn!("failed to get a bbox");
122
123 Rect::new(0.0, 0.0, 1.0, 1.0)
124 })
125 }
126
127 fn push_bbox(&mut self, bbox: Rect) {
128 let new = self.bbox().intersect(bbox);
129 self.bbox.push(new);
130 }
131
132 pub(crate) fn push_clip_path(
133 &mut self,
134 clip_path: BezPath,
135 fill: FillRule,
136 device: &mut impl Device<'a>,
137 ) {
138 if let Some(clip_rect) = path_as_rect(&clip_path) {
139 let cur_bbox = self.bbox();
140
141 if cur_bbox
143 .min_x()
144 .is_nearly_greater_or_equal(clip_rect.min_x())
145 && cur_bbox
146 .min_y()
147 .is_nearly_greater_or_equal(clip_rect.min_y())
148 && cur_bbox.max_x().is_nearly_less_or_equal(clip_rect.max_x())
149 && cur_bbox.max_y().is_nearly_less_or_equal(clip_rect.max_y())
150 {
151 self.get_mut().clips.push(ClipType::Dummy);
152 return;
153 }
154 }
155
156 let bbox = clip_path.bounding_box();
157 device.push_clip_path(&ClipPath {
158 path: clip_path,
159 fill,
160 });
161 self.push_bbox(bbox);
162 self.get_mut().clips.push(ClipType::Real);
163 }
164
165 pub(crate) fn pop_clip_path(&mut self, device: &mut impl Device<'a>) {
166 if let Some(ClipType::Real) = self.get_mut().clips.pop() {
167 device.pop_clip_path();
168 self.pop_bbox();
169 }
170 }
171
172 fn pop_bbox(&mut self) {
173 self.bbox.pop();
174 }
175
176 pub(crate) fn push_root_transform(&mut self) {
177 self.root_transforms.push(self.get().ctm);
178 }
179
180 pub(crate) fn pop_root_transform(&mut self) {
181 self.root_transforms.pop();
182 }
183
184 pub(crate) fn root_transform(&self) -> Affine {
185 self.root_transforms
186 .last()
187 .copied()
188 .unwrap_or(Affine::IDENTITY)
189 }
190
191 pub(crate) fn restore_state(&mut self, device: &mut impl Device<'a>) {
192 let Some(target_clips) = self
193 .states
194 .get(self.states.len().saturating_sub(2))
195 .map(|s| s.clips.len())
196 else {
197 warn!("underflowed graphics state");
198 return;
199 };
200
201 while self.get().clips.len() > target_clips {
202 self.pop_clip_path(device);
203 }
204
205 if self.states.len() > 1 {
207 self.states.pop();
208 }
209
210 device.set_soft_mask(
211 self.states
212 .last()
213 .and_then(|l| l.graphics_state.soft_mask.clone()),
214 );
215 }
216
217 pub(crate) fn path(&self) -> &BezPath {
218 &self.path
219 }
220
221 pub(crate) fn path_mut(&mut self) -> &mut BezPath {
222 &mut self.path
223 }
224
225 pub(crate) fn sub_path_start(&self) -> &Point {
226 &self.sub_path_start
227 }
228
229 pub(crate) fn sub_path_start_mut(&mut self) -> &mut Point {
230 &mut self.sub_path_start
231 }
232
233 pub(crate) fn last_point(&self) -> &Point {
234 &self.last_point
235 }
236
237 pub(crate) fn last_point_mut(&mut self) -> &mut Point {
238 &mut self.last_point
239 }
240
241 pub(crate) fn clip(&self) -> &Option<FillRule> {
242 &self.clip
243 }
244
245 pub(crate) fn clip_mut(&mut self) -> &mut Option<FillRule> {
246 &mut self.clip
247 }
248
249 pub(crate) fn get(&self) -> &State<'a> {
250 self.states.last().unwrap()
251 }
252
253 pub(crate) fn get_mut(&mut self) -> &mut State<'a> {
254 self.states.last_mut().unwrap()
255 }
256
257 pub(crate) fn pre_concat_transform(&mut self, transform: Transform) {
258 self.pre_concat_affine(convert_transform(transform));
259 }
260
261 pub(crate) fn pre_concat_affine(&mut self, transform: Affine) {
262 self.get_mut().ctm *= transform;
263 }
264
265 pub(crate) fn get_color_space(
266 &mut self,
267 resources: &Resources<'_>,
268 name: &Name<'_>,
269 ) -> Option<ColorSpace> {
270 let cs_object = resources.get_color_space(name)?;
271 self.interpreter_cache
272 .object_cache
273 .get_or_insert_with(cs_object.cache_key(), || {
274 ColorSpace::new(cs_object.clone(), &self.interpreter_cache.object_cache)
275 })
276 }
277
278 pub(crate) fn stroke_props(&self) -> StrokeProps {
279 self.get().graphics_state.stroke_props.clone()
280 }
281
282 pub(crate) fn num_states(&self) -> usize {
283 self.states.len()
284 }
285
286 pub(crate) fn nesting_depth(&self) -> u32 {
287 self.nesting_depth
288 }
289
290 pub(crate) fn begin_nested_interpretation(&mut self) -> bool {
291 if self.nesting_depth >= MAX_NESTED_INTERPRETATION_DEPTH {
292 warn!("interpreter nesting depth exceeded");
293
294 return false;
295 }
296
297 self.nesting_depth += 1;
298
299 true
300 }
301
302 pub(crate) fn end_nested_interpretation(&mut self) {
303 self.nesting_depth = self.nesting_depth.saturating_sub(1);
304 }
305 pub(crate) fn resolve_font(&mut self, font_dict: &Dict<'a>) -> Option<TextStateFont<'a>> {
306 let cache_key = font_dict.cache_key();
307
308 let resolved = {
309 let mut font_cache = self.interpreter_cache.font_cache.borrow_mut();
310 font_cache
311 .entry(cache_key)
312 .or_insert_with(|| {
313 Font::new(
314 font_dict,
315 &self.settings.font_resolver,
316 &self.settings.cmap_resolver,
317 )
318 })
319 .clone()
320 };
321
322 if let Some(resolved) = resolved {
323 Some(TextStateFont::Font(resolved))
324 } else {
325 Font::new_standard(StandardFont::Helvetica, &self.settings.font_resolver)
326 .map(TextStateFont::Fallback)
327 }
328 }
329}
330
331pub(crate) fn path_as_rect(path: &BezPath) -> Option<Rect> {
332 if path.elements().len() != 5 {
334 return None;
335 }
336
337 let bbox = path.fast_bounding_box();
338 let (min_x, min_y, max_x, max_y) = (bbox.min_x(), bbox.min_y(), bbox.max_x(), bbox.max_y());
339 let mut corners = [false; 4];
340
341 let mut check_point = |p: Point| {
342 corners[0] |= p.x.is_nearly_equal(min_x) && p.y.is_nearly_equal(min_y);
343 corners[1] |= p.x.is_nearly_equal(min_x) && p.y.is_nearly_equal(max_y);
344 corners[2] |= p.x.is_nearly_equal(max_x) && p.y.is_nearly_equal(min_y);
345 corners[3] |= p.x.is_nearly_equal(max_x) && p.y.is_nearly_equal(max_y);
346 };
347
348 for (idx, el) in path.elements().iter().enumerate() {
349 match el {
350 PathEl::MoveTo(p) => {
351 if idx != 0 {
352 return None;
353 }
354
355 check_point(*p);
356 }
357 PathEl::LineTo(l) => check_point(*l),
358 PathEl::QuadTo(_, _) => return None,
359 PathEl::CurveTo(_, _, _) => return None,
360 PathEl::ClosePath => {}
361 }
362 }
363
364 if corners[0] && corners[1] && corners[2] && corners[3] {
365 Some(bbox)
366 } else {
367 None
368 }
369}