hayro_interpret/
context.rs

1use crate::cache::Cache;
2use crate::color::ColorSpace;
3use crate::convert::convert_transform;
4use crate::font::Font;
5use crate::interpret::state::State;
6use crate::ocg::OcgState;
7use crate::{FillRule, InterpreterSettings, StrokeProps};
8use hayro_syntax::content::ops::Transform;
9use hayro_syntax::object::Dict;
10use hayro_syntax::object::Name;
11use hayro_syntax::object::ObjRef;
12use hayro_syntax::object::Object;
13use hayro_syntax::page::Resources;
14use hayro_syntax::xref::XRef;
15use kurbo::{Affine, BezPath, Point};
16use log::warn;
17use std::collections::HashMap;
18
19/// A context for interpreting PDF files.
20pub struct Context<'a> {
21    states: Vec<State<'a>>,
22    path: BezPath,
23    sub_path_start: Point,
24    last_point: Point,
25    clip: Option<FillRule>,
26    font_cache: HashMap<ObjRef, Option<Font<'a>>>,
27    root_transforms: Vec<Affine>,
28    bbox: Vec<kurbo::Rect>,
29    pub(crate) settings: InterpreterSettings,
30    pub(crate) object_cache: Cache,
31    pub(crate) xref: &'a XRef,
32    pub(crate) ocg_state: OcgState,
33}
34
35impl<'a> Context<'a> {
36    /// Create a new context.
37    pub fn new(
38        initial_transform: Affine,
39        bbox: kurbo::Rect,
40        xref: &'a XRef,
41        settings: InterpreterSettings,
42    ) -> Self {
43        let cache = Cache::new();
44        let state = State::new(initial_transform);
45
46        Self::new_with(initial_transform, bbox, cache, xref, settings, state)
47    }
48
49    pub(crate) fn new_with(
50        initial_transform: Affine,
51        bbox: kurbo::Rect,
52        cache: Cache,
53        xref: &'a XRef,
54        settings: InterpreterSettings,
55        state: State<'a>,
56    ) -> Self {
57        let ocg_state = {
58            let root_ref = xref.root_id();
59            let catalog = xref.get::<Dict>(root_ref).unwrap();
60            OcgState::from_catalog(&catalog)
61        };
62
63        Self {
64            states: vec![state],
65            settings,
66            xref,
67            root_transforms: vec![initial_transform],
68            last_point: Point::default(),
69            sub_path_start: Point::default(),
70            clip: None,
71            bbox: vec![bbox],
72            path: BezPath::new(),
73            font_cache: HashMap::new(),
74            object_cache: cache,
75            ocg_state,
76        }
77    }
78
79    pub(crate) fn save_state(&mut self) {
80        let Some(cur) = self.states.last().cloned() else {
81            warn!("attempted to save state without existing state");
82            return;
83        };
84
85        self.states.push(cur);
86    }
87
88    pub(crate) fn bbox(&self) -> kurbo::Rect {
89        self.bbox.last().copied().unwrap_or_else(|| {
90            warn!("failed to get a bbox");
91
92            kurbo::Rect::new(0.0, 0.0, 1.0, 1.0)
93        })
94    }
95
96    pub(crate) fn push_bbox(&mut self, bbox: kurbo::Rect) {
97        let new = self.bbox().intersect(bbox);
98        self.bbox.push(new);
99    }
100
101    pub(crate) fn pop_bbox(&mut self) {
102        self.bbox.pop();
103    }
104
105    pub(crate) fn push_root_transform(&mut self) {
106        self.root_transforms.push(self.get().ctm);
107    }
108
109    pub(crate) fn pop_root_transform(&mut self) {
110        self.root_transforms.pop();
111    }
112
113    pub(crate) fn root_transform(&self) -> Affine {
114        self.root_transforms
115            .last()
116            .copied()
117            .unwrap_or(Affine::IDENTITY)
118    }
119
120    pub(crate) fn restore_state(&mut self) {
121        if self.states.len() > 1 {
122            self.states.pop();
123        } else {
124            warn!("overflow in `restore_state");
125        }
126    }
127
128    pub(crate) fn path(&self) -> &BezPath {
129        &self.path
130    }
131
132    pub(crate) fn path_mut(&mut self) -> &mut BezPath {
133        &mut self.path
134    }
135
136    pub(crate) fn sub_path_start(&self) -> &Point {
137        &self.sub_path_start
138    }
139
140    pub(crate) fn sub_path_start_mut(&mut self) -> &mut Point {
141        &mut self.sub_path_start
142    }
143
144    pub(crate) fn last_point(&self) -> &Point {
145        &self.last_point
146    }
147
148    pub(crate) fn last_point_mut(&mut self) -> &mut Point {
149        &mut self.last_point
150    }
151
152    pub(crate) fn clip(&self) -> &Option<FillRule> {
153        &self.clip
154    }
155
156    pub(crate) fn clip_mut(&mut self) -> &mut Option<FillRule> {
157        &mut self.clip
158    }
159
160    pub(crate) fn get(&self) -> &State<'a> {
161        self.states.last().unwrap()
162    }
163
164    pub(crate) fn get_mut(&mut self) -> &mut State<'a> {
165        self.states.last_mut().unwrap()
166    }
167
168    pub(crate) fn pre_concat_transform(&mut self, transform: Transform) {
169        self.pre_concat_affine(convert_transform(transform))
170    }
171
172    pub(crate) fn pre_concat_affine(&mut self, transform: Affine) {
173        self.get_mut().ctm *= transform;
174    }
175
176    pub(crate) fn get_font(&mut self, resources: &Resources<'a>, name: Name) -> Option<Font<'a>> {
177        resources.get_font(
178            name,
179            Box::new(|ref_| {
180                self.font_cache
181                    .entry(ref_)
182                    .or_insert_with(|| {
183                        resources
184                            .resolve_ref::<Dict>(ref_)
185                            .and_then(|o| Font::new(&o, &self.settings.font_resolver))
186                    })
187                    .clone()
188            }),
189            Box::new(|c| Font::new(&c, &self.settings.font_resolver)),
190        )
191    }
192
193    pub(crate) fn get_color_space(
194        &mut self,
195        resources: &Resources,
196        name: Name,
197    ) -> Option<ColorSpace> {
198        resources.get_color_space(
199            name,
200            Box::new(|ref_| {
201                self.object_cache.get_or_insert_with(ref_.into(), || {
202                    resources
203                        .resolve_ref::<Object>(ref_)
204                        .map(|o| ColorSpace::new(o, &self.object_cache))
205                })
206            }),
207            Box::new(|c| Some(ColorSpace::new(c, &self.object_cache))),
208        )?
209    }
210
211    pub(crate) fn stroke_props(&self) -> StrokeProps {
212        self.get().graphics_state.stroke_props.clone()
213    }
214
215    pub(crate) fn num_states(&self) -> usize {
216        self.states.len()
217    }
218}