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::{FillProps, 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
18pub 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 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.resolve_ref::<Dict>(ref_).and_then(|o| {
166 Font::new(
167 &o,
168 &self.settings.font_resolver,
169 &self.settings.warning_sink,
170 )
171 })
172 })
173 .clone()
174 }),
175 Box::new(|c| {
176 Font::new(
177 &c,
178 &self.settings.font_resolver,
179 &self.settings.warning_sink,
180 )
181 }),
182 )
183 }
184
185 pub(crate) fn get_color_space(
186 &mut self,
187 resources: &Resources,
188 name: Name,
189 ) -> Option<ColorSpace> {
190 resources.get_color_space(
191 name,
192 Box::new(|ref_| {
193 self.object_cache.get_or_insert_with(ref_.into(), || {
194 resources
195 .resolve_ref::<Object>(ref_)
196 .map(|o| ColorSpace::new(o))
197 })
198 }),
199 Box::new(|c| Some(ColorSpace::new(c))),
200 )?
201 }
202
203 pub(crate) fn stroke_props(&self) -> StrokeProps {
204 let state = self.get();
205
206 StrokeProps {
207 line_width: state.line_width,
208 line_cap: state.line_cap,
209 line_join: state.line_join,
210 miter_limit: state.miter_limit,
211 dash_array: state.dash_array.clone(),
212 dash_offset: state.dash_offset,
213 }
214 }
215
216 pub(crate) fn num_states(&self) -> usize {
217 self.states.len()
218 }
219
220 pub(crate) fn fill_props(&self) -> FillProps {
221 let state = self.get();
222
223 FillProps {
224 fill_rule: state.fill_rule,
225 }
226 }
227}