hayro_interpret/
context.rs1use 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
19pub 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 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}