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_root_transform(&mut self) {
88        self.root_transforms.push(self.get().ctm);
89    }
90
91    pub(crate) fn pop_root_transform(&mut self) {
92        self.root_transforms.pop();
93    }
94
95    pub(crate) fn root_transform(&self) -> Affine {
96        self.root_transforms
97            .last()
98            .copied()
99            .unwrap_or(Affine::IDENTITY)
100    }
101
102    pub(crate) fn restore_state(&mut self) {
103        if self.states.len() > 1 {
104            self.states.pop();
105        } else {
106            warn!("overflow in `restore_state");
107        }
108    }
109
110    pub(crate) fn path(&self) -> &BezPath {
111        &self.path
112    }
113
114    pub(crate) fn path_mut(&mut self) -> &mut BezPath {
115        &mut self.path
116    }
117
118    pub(crate) fn sub_path_start(&self) -> &Point {
119        &self.sub_path_start
120    }
121
122    pub(crate) fn sub_path_start_mut(&mut self) -> &mut Point {
123        &mut self.sub_path_start
124    }
125
126    pub(crate) fn last_point(&self) -> &Point {
127        &self.last_point
128    }
129
130    pub(crate) fn last_point_mut(&mut self) -> &mut Point {
131        &mut self.last_point
132    }
133
134    pub(crate) fn clip(&self) -> &Option<FillRule> {
135        &self.clip
136    }
137
138    pub(crate) fn clip_mut(&mut self) -> &mut Option<FillRule> {
139        &mut self.clip
140    }
141
142    pub(crate) fn get(&self) -> &State<'a> {
143        self.states.last().unwrap()
144    }
145
146    pub(crate) fn get_mut(&mut self) -> &mut State<'a> {
147        self.states.last_mut().unwrap()
148    }
149
150    pub(crate) fn pre_concat_transform(&mut self, transform: Transform) {
151        self.pre_concat_affine(convert_transform(transform))
152    }
153
154    pub(crate) fn pre_concat_affine(&mut self, transform: Affine) {
155        self.get_mut().ctm *= transform;
156    }
157
158    pub(crate) fn get_font(&mut self, resources: &Resources<'a>, name: Name) -> Option<Font<'a>> {
159        resources.get_font(
160            name,
161            Box::new(|ref_| {
162                self.font_cache
163                    .entry(ref_)
164                    .or_insert_with(|| {
165                        resources
166                            .resolve_ref::<Dict>(ref_)
167                            .and_then(|o| Font::new(&o, &self.settings.font_resolver))
168                    })
169                    .clone()
170            }),
171            Box::new(|c| Font::new(&c, &self.settings.font_resolver)),
172        )
173    }
174
175    pub(crate) fn get_color_space(
176        &mut self,
177        resources: &Resources,
178        name: Name,
179    ) -> Option<ColorSpace> {
180        resources.get_color_space(
181            name,
182            Box::new(|ref_| {
183                self.object_cache.get_or_insert_with(ref_.into(), || {
184                    resources
185                        .resolve_ref::<Object>(ref_)
186                        .map(|o| ColorSpace::new(o, &self.object_cache))
187                })
188            }),
189            Box::new(|c| Some(ColorSpace::new(c, &self.object_cache))),
190        )?
191    }
192
193    pub(crate) fn stroke_props(&self) -> StrokeProps {
194        self.get().stroke_props.clone()
195    }
196
197    pub(crate) fn num_states(&self) -> usize {
198        self.states.len()
199    }
200
201    pub(crate) fn fill_rule(&self) -> FillRule {
202        self.get().fill_rule
203    }
204}