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