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::{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_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}