1use markup5ever::{expanded_name, local_name, ns};
4
5use crate::coord_units;
6use crate::coord_units::CoordUnits;
7
8use crate::aspect_ratio::*;
9use crate::document::{AcquiredNode, AcquiredNodes, NodeId, NodeStack};
10use crate::drawing_ctx::Viewport;
11use crate::element::{ElementData, ElementTrait, set_attribute};
12use crate::error::*;
13use crate::href::{is_href, set_href};
14use crate::length::*;
15use crate::node::{Node, NodeBorrow, WeakNode};
16use crate::parsers::ParseValue;
17use crate::rect::Rect;
18use crate::rsvg_log;
19use crate::session::Session;
20use crate::transform::{Transform, TransformAttribute};
21use crate::unit_interval::UnitInterval;
22use crate::viewbox::*;
23use crate::xml::Attributes;
24
25coord_units!(PatternUnits, CoordUnits::ObjectBoundingBox);
26coord_units!(PatternContentUnits, CoordUnits::UserSpaceOnUse);
27
28#[derive(Clone, Default)]
29struct Common {
30 units: Option<PatternUnits>,
31 content_units: Option<PatternContentUnits>,
32 vbox: Option<Option<ViewBox>>,
37 preserve_aspect_ratio: Option<AspectRatio>,
38 transform: Option<TransformAttribute>,
39 x: Option<Length<Horizontal>>,
40 y: Option<Length<Vertical>>,
41 width: Option<ULength<Horizontal>>,
42 height: Option<ULength<Vertical>>,
43}
44
45struct Unresolved {
51 pattern: UnresolvedPattern,
52 fallback: Option<NodeId>,
53}
54
55#[derive(Clone)]
57enum UnresolvedChildren {
58 Unresolved,
60
61 ResolvedEmpty,
65
66 WithChildren(WeakNode),
68}
69
70#[derive(Clone)]
72enum Children {
73 Empty,
74
75 WithChildren(WeakNode),
77}
78
79struct UnresolvedPattern {
84 common: Common,
85
86 children: UnresolvedChildren,
90}
91
92#[derive(Clone)]
93pub struct ResolvedPattern {
94 units: PatternUnits,
95 content_units: PatternContentUnits,
96 vbox: Option<ViewBox>,
97 preserve_aspect_ratio: AspectRatio,
98 transform: TransformAttribute,
99 x: Length<Horizontal>,
100 y: Length<Vertical>,
101 width: ULength<Horizontal>,
102 height: ULength<Vertical>,
103 opacity: UnitInterval,
104
105 children: Children,
107}
108
109pub struct UserSpacePattern {
111 pub width: f64,
112 pub height: f64,
113 pub transform: Transform,
114 pub coord_transform: Transform,
115 pub content_transform: Transform,
116 pub opacity: UnitInterval,
117
118 node_with_children: Node,
120}
121
122#[derive(Default)]
123pub struct Pattern {
124 common: Common,
125 fallback: Option<NodeId>,
126}
127
128impl ElementTrait for Pattern {
129 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
130 for (attr, value) in attrs.iter() {
131 match attr.expanded() {
132 expanded_name!("", "patternUnits") => {
133 set_attribute(&mut self.common.units, attr.parse(value), session)
134 }
135 expanded_name!("", "patternContentUnits") => {
136 set_attribute(&mut self.common.content_units, attr.parse(value), session);
137 }
138 expanded_name!("", "viewBox") => {
139 set_attribute(&mut self.common.vbox, attr.parse(value), session)
140 }
141 expanded_name!("", "preserveAspectRatio") => {
142 set_attribute(
143 &mut self.common.preserve_aspect_ratio,
144 attr.parse(value),
145 session,
146 );
147 }
148 expanded_name!("", "patternTransform") => {
149 set_attribute(&mut self.common.transform, attr.parse(value), session);
150 }
151 ref a if is_href(a) => {
152 let mut href = None;
153 set_attribute(
154 &mut href,
155 NodeId::parse(value).map(Some).attribute(attr.clone()),
156 session,
157 );
158 set_href(a, &mut self.fallback, href);
159 }
160 expanded_name!("", "x") => {
161 set_attribute(&mut self.common.x, attr.parse(value), session)
162 }
163 expanded_name!("", "y") => {
164 set_attribute(&mut self.common.y, attr.parse(value), session)
165 }
166 expanded_name!("", "width") => {
167 set_attribute(&mut self.common.width, attr.parse(value), session)
168 }
169 expanded_name!("", "height") => {
170 set_attribute(&mut self.common.height, attr.parse(value), session)
171 }
172 _ => (),
173 }
174 }
175 }
176}
177
178impl UnresolvedPattern {
179 fn into_resolved(self, opacity: UnitInterval) -> ResolvedPattern {
180 assert!(self.is_resolved());
181
182 ResolvedPattern {
183 units: self.common.units.unwrap(),
184 content_units: self.common.content_units.unwrap(),
185 vbox: self.common.vbox.unwrap(),
186 preserve_aspect_ratio: self.common.preserve_aspect_ratio.unwrap(),
187 transform: self.common.transform.unwrap(),
188 x: self.common.x.unwrap(),
189 y: self.common.y.unwrap(),
190 width: self.common.width.unwrap(),
191 height: self.common.height.unwrap(),
192 opacity,
193
194 children: self.children.to_resolved(),
195 }
196 }
197
198 fn is_resolved(&self) -> bool {
199 self.common.units.is_some()
200 && self.common.content_units.is_some()
201 && self.common.vbox.is_some()
202 && self.common.preserve_aspect_ratio.is_some()
203 && self.common.transform.is_some()
204 && self.common.x.is_some()
205 && self.common.y.is_some()
206 && self.common.width.is_some()
207 && self.common.height.is_some()
208 && self.children.is_resolved()
209 }
210
211 fn resolve_from_fallback(&self, fallback: &UnresolvedPattern) -> UnresolvedPattern {
212 let units = self.common.units.or(fallback.common.units);
213 let content_units = self.common.content_units.or(fallback.common.content_units);
214 let vbox = self.common.vbox.or(fallback.common.vbox);
215 let preserve_aspect_ratio = self
216 .common
217 .preserve_aspect_ratio
218 .or(fallback.common.preserve_aspect_ratio);
219 let transform = self.common.transform.or(fallback.common.transform);
220 let x = self.common.x.or(fallback.common.x);
221 let y = self.common.y.or(fallback.common.y);
222 let width = self.common.width.or(fallback.common.width);
223 let height = self.common.height.or(fallback.common.height);
224 let children = self.children.resolve_from_fallback(&fallback.children);
225
226 UnresolvedPattern {
227 common: Common {
228 units,
229 content_units,
230 vbox,
231 preserve_aspect_ratio,
232 transform,
233 x,
234 y,
235 width,
236 height,
237 },
238 children,
239 }
240 }
241
242 fn resolve_from_defaults(&self) -> UnresolvedPattern {
243 let units = self.common.units.or_else(|| Some(PatternUnits::default()));
244 let content_units = self
245 .common
246 .content_units
247 .or_else(|| Some(PatternContentUnits::default()));
248 let vbox = self.common.vbox.or(Some(None));
249 let preserve_aspect_ratio = self
250 .common
251 .preserve_aspect_ratio
252 .or_else(|| Some(AspectRatio::default()));
253 let transform = self
254 .common
255 .transform
256 .or_else(|| Some(TransformAttribute::default()));
257 let x = self.common.x.or_else(|| Some(Default::default()));
258 let y = self.common.y.or_else(|| Some(Default::default()));
259 let width = self.common.width.or_else(|| Some(Default::default()));
260 let height = self.common.height.or_else(|| Some(Default::default()));
261 let children = self.children.resolve_from_defaults();
262
263 UnresolvedPattern {
264 common: Common {
265 units,
266 content_units,
267 vbox,
268 preserve_aspect_ratio,
269 transform,
270 x,
271 y,
272 width,
273 height,
274 },
275 children,
276 }
277 }
278}
279
280impl UnresolvedChildren {
281 fn from_node(node: &Node) -> UnresolvedChildren {
282 let weak = node.downgrade();
283
284 if node.children().any(|child| child.is_element()) {
285 UnresolvedChildren::WithChildren(weak)
286 } else {
287 UnresolvedChildren::Unresolved
288 }
289 }
290
291 fn is_resolved(&self) -> bool {
292 !matches!(*self, UnresolvedChildren::Unresolved)
293 }
294
295 fn resolve_from_fallback(&self, fallback: &UnresolvedChildren) -> UnresolvedChildren {
296 use UnresolvedChildren::*;
297
298 match (self, fallback) {
299 (&Unresolved, &Unresolved) => Unresolved,
300 (WithChildren(wc), _) => WithChildren(wc.clone()),
301 (_, WithChildren(wc)) => WithChildren(wc.clone()),
302 (_, _) => unreachable!(),
303 }
304 }
305
306 fn resolve_from_defaults(&self) -> UnresolvedChildren {
307 use UnresolvedChildren::*;
308
309 match *self {
310 Unresolved => ResolvedEmpty,
311 _ => (*self).clone(),
312 }
313 }
314
315 fn to_resolved(&self) -> Children {
316 use UnresolvedChildren::*;
317
318 assert!(self.is_resolved());
319
320 match *self {
321 ResolvedEmpty => Children::Empty,
322 WithChildren(ref wc) => Children::WithChildren(wc.clone()),
323 _ => unreachable!(),
324 }
325 }
326}
327
328fn nonempty_rect(bbox: &Option<Rect>) -> Option<Rect> {
329 match *bbox {
330 None => None,
331 Some(r) if r.is_empty() => None,
332 Some(r) => Some(r),
333 }
334}
335
336impl ResolvedPattern {
337 fn node_with_children(&self) -> Option<Node> {
338 match self.children {
339 Children::Empty => None,
342
343 Children::WithChildren(ref wc) => Some(wc.upgrade().unwrap()),
344 }
345 }
346
347 fn get_rect(&self, params: &NormalizeParams) -> Rect {
348 let x = self.x.to_user(params);
349 let y = self.y.to_user(params);
350 let w = self.width.to_user(params);
351 let h = self.height.to_user(params);
352
353 Rect::new(x, y, x + w, y + h)
354 }
355
356 pub fn to_user_space(
357 &self,
358 object_bbox: &Option<Rect>,
359 viewport: &Viewport,
360 values: &NormalizeValues,
361 ) -> Option<UserSpacePattern> {
362 let node_with_children = self.node_with_children()?;
363
364 let viewport = viewport.with_units(self.units.0);
365 let params = NormalizeParams::from_values(values, &viewport);
366
367 let rect = self.get_rect(¶ms);
368
369 let (width, height, coord_transform) = match self.units {
371 PatternUnits(CoordUnits::ObjectBoundingBox) => {
372 let bbrect = nonempty_rect(object_bbox)?;
373 (
374 rect.width() * bbrect.width(),
375 rect.height() * bbrect.height(),
376 Transform::new_translate(
377 bbrect.x0 + rect.x0 * bbrect.width(),
378 bbrect.y0 + rect.y0 * bbrect.height(),
379 ),
380 )
381 }
382 PatternUnits(CoordUnits::UserSpaceOnUse) => (
383 rect.width(),
384 rect.height(),
385 Transform::new_translate(rect.x0, rect.y0),
386 ),
387 };
388
389 let pattern_transform = self.transform.to_transform();
390
391 let coord_transform = coord_transform.post_transform(&pattern_transform);
392
393 let content_transform = if let Some(vbox) = self.vbox {
395 let r = self
397 .preserve_aspect_ratio
398 .compute(&vbox, &Rect::from_size(width, height));
399
400 let sw = r.width() / vbox.width();
401 let sh = r.height() / vbox.height();
402 let x = r.x0 - vbox.x0 * sw;
403 let y = r.y0 - vbox.y0 * sh;
404
405 Transform::new_scale(sw, sh).pre_translate(x, y)
406 } else {
407 match self.content_units {
408 PatternContentUnits(CoordUnits::ObjectBoundingBox) => {
409 let bbrect = nonempty_rect(object_bbox)?;
410 Transform::new_scale(bbrect.width(), bbrect.height())
411 }
412 PatternContentUnits(CoordUnits::UserSpaceOnUse) => Transform::identity(),
413 }
414 };
415
416 Some(UserSpacePattern {
417 width,
418 height,
419 transform: pattern_transform,
420 coord_transform,
421 content_transform,
422 opacity: self.opacity,
423 node_with_children,
424 })
425 }
426}
427
428impl UserSpacePattern {
429 pub fn acquire_pattern_node(
434 &self,
435 acquired_nodes: &mut AcquiredNodes<'_>,
436 ) -> Result<AcquiredNode, AcquireError> {
437 acquired_nodes.acquire_ref(&self.node_with_children)
438 }
439}
440
441impl Pattern {
442 fn get_unresolved(&self, node: &Node) -> Unresolved {
443 let pattern = UnresolvedPattern {
444 common: self.common.clone(),
445 children: UnresolvedChildren::from_node(node),
446 };
447
448 Unresolved {
449 pattern,
450 fallback: self.fallback.clone(),
451 }
452 }
453
454 pub fn resolve(
455 &self,
456 node: &Node,
457 acquired_nodes: &mut AcquiredNodes<'_>,
458 opacity: UnitInterval,
459 session: &Session,
460 ) -> Result<ResolvedPattern, AcquireError> {
461 let Unresolved {
462 mut pattern,
463 mut fallback,
464 } = self.get_unresolved(node);
465
466 let mut stack = NodeStack::new();
467
468 while !pattern.is_resolved() {
469 if let Some(ref node_id) = fallback {
470 match acquired_nodes.acquire(node_id) {
471 Ok(acquired) => {
472 let acquired_node = acquired.get();
473
474 if stack.contains(acquired_node) {
475 return Err(AcquireError::CircularReference(acquired_node.clone()));
476 }
477
478 match *acquired_node.borrow_element_data() {
479 ElementData::Pattern(ref p) => {
480 let unresolved = p.get_unresolved(acquired_node);
481 pattern = pattern.resolve_from_fallback(&unresolved.pattern);
482 fallback = unresolved.fallback;
483
484 stack.push(acquired_node);
485 }
486 _ => return Err(AcquireError::InvalidLinkType(node_id.clone())),
487 }
488 }
489
490 Err(AcquireError::MaxReferencesExceeded) => {
491 return Err(AcquireError::MaxReferencesExceeded);
492 }
493
494 Err(e) => {
495 rsvg_log!(session, "Stopping pattern resolution: {}", e);
496 pattern = pattern.resolve_from_defaults();
497 break;
498 }
499 }
500 } else {
501 pattern = pattern.resolve_from_defaults();
502 break;
503 }
504 }
505
506 Ok(pattern.into_resolved(opacity))
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513
514 use markup5ever::{QualName, ns};
515
516 use crate::borrow_element_as;
517 use crate::node::NodeData;
518
519 #[test]
520 fn pattern_resolved_from_defaults_is_really_resolved() {
521 let node = Node::new(NodeData::new_element(
522 &Session::default(),
523 &QualName::new(None, ns!(svg), local_name!("pattern")),
524 Attributes::new(),
525 ));
526
527 let unresolved = borrow_element_as!(node, Pattern).get_unresolved(&node);
528 let pattern = unresolved.pattern.resolve_from_defaults();
529 assert!(pattern.is_resolved());
530 }
531}