1use markup5ever::{expanded_name, local_name, ns};
4
5use crate::aspect_ratio::AspectRatio;
6use crate::document::{AcquiredNodes, Document, Resource};
7use crate::drawing_ctx::{DrawingCtx, SvgNesting, Viewport};
8use crate::element::{DrawResult, ElementTrait, set_attribute};
9use crate::error::*;
10use crate::href::{is_href, set_href};
11use crate::layout::{self, Layer, LayerKind, StackingContext};
12use crate::length::*;
13use crate::node::{CascadedValues, Node, NodeBorrow};
14use crate::parsers::ParseValue;
15use crate::rect::Rect;
16use crate::rsvg_log;
17use crate::session::Session;
18use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
19use crate::xml::Attributes;
20
21#[derive(Default)]
26pub struct Image {
27 aspect: AspectRatio,
28 href: Option<String>,
29}
30
31impl ElementTrait for Image {
32 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
33 for (attr, value) in attrs.iter() {
34 match attr.expanded() {
35 expanded_name!("", "preserveAspectRatio") => {
36 set_attribute(&mut self.aspect, attr.parse(value), session)
37 }
38
39 ref a if is_href(a) || *a == expanded_name!("", "path") => {
41 set_href(a, &mut self.href, Some(value.to_string()))
42 }
43
44 _ => (),
45 }
46 }
47 }
48
49 fn layout(
50 &self,
51 node: &Node,
52 acquired_nodes: &mut AcquiredNodes<'_>,
53 cascaded: &CascadedValues<'_>,
54 viewport: &Viewport,
55 draw_ctx: &mut DrawingCtx,
56 _clipping: bool,
57 ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
58 if let Some(ref url) = self.href {
59 self.layout_from_url(url, node, acquired_nodes, cascaded, viewport, draw_ctx)
60 } else {
61 Ok(None)
62 }
63 }
64
65 fn draw(
66 &self,
67 node: &Node,
68 acquired_nodes: &mut AcquiredNodes<'_>,
69 cascaded: &CascadedValues<'_>,
70 viewport: &Viewport,
71 draw_ctx: &mut DrawingCtx,
72 clipping: bool,
73 ) -> DrawResult {
74 let layer = self.layout(node, acquired_nodes, cascaded, viewport, draw_ctx, clipping)?;
75
76 if let Some(layer) = layer {
77 draw_ctx.draw_layer(&layer, acquired_nodes, clipping, viewport)
78 } else {
79 Ok(viewport.empty_bbox())
80 }
81 }
82}
83
84impl Image {
85 fn layout_from_url(
86 &self,
87 url: &str,
88 node: &Node,
89 acquired_nodes: &mut AcquiredNodes<'_>,
90 cascaded: &CascadedValues<'_>,
91 viewport: &Viewport,
92 draw_ctx: &mut DrawingCtx,
93 ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
94 match acquired_nodes.lookup_resource(url) {
95 Ok(Resource::Image(surface)) => self.layout_from_surface(
96 &surface,
97 node,
98 acquired_nodes,
99 cascaded,
100 viewport,
101 draw_ctx,
102 ),
103
104 Ok(Resource::Document(document)) => self.layout_from_svg(
105 &document,
106 node,
107 acquired_nodes,
108 cascaded,
109 viewport,
110 draw_ctx,
111 ),
112
113 Err(e) => {
114 rsvg_log!(
115 draw_ctx.session(),
116 "could not load image \"{}\": {}",
117 url,
118 e
119 );
120 Ok(None)
121 }
122 }
123 }
124
125 fn layout_from_surface(
127 &self,
128 surface: &SharedImageSurface,
129 node: &Node,
130 acquired_nodes: &mut AcquiredNodes<'_>,
131 cascaded: &CascadedValues<'_>,
132 viewport: &Viewport,
133 draw_ctx: &mut DrawingCtx,
134 ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
135 let values = cascaded.get();
136
137 let params = NormalizeParams::new(values, viewport);
138
139 let x = values.x().0.to_user(¶ms);
140 let y = values.y().0.to_user(¶ms);
141
142 let w = match values.width().0 {
143 LengthOrAuto::Length(l) => l.to_user(¶ms),
144 LengthOrAuto::Auto => surface.width() as f64,
145 };
146 let h = match values.height().0 {
147 LengthOrAuto::Length(l) => l.to_user(¶ms),
148 LengthOrAuto::Auto => surface.height() as f64,
149 };
150
151 let rect = Rect::new(x, y, x + w, y + h);
152
153 let overflow = values.overflow();
154
155 let image = Box::new(layout::Image {
156 surface: surface.clone(),
157 rect,
158 aspect: self.aspect,
159 overflow,
160 image_rendering: values.image_rendering(),
161 });
162
163 let elt = node.borrow_element();
164 let stacking_ctx = StackingContext::new(
165 draw_ctx.session(),
166 acquired_nodes,
167 &elt,
168 values.transform(),
169 None,
170 values,
171 );
172
173 let layer = Layer {
174 kind: LayerKind::Image(image),
175 stacking_ctx,
176 };
177
178 Ok(Some(layer))
179 }
180
181 fn layout_from_svg(
189 &self,
190 document: &Document,
191 node: &Node,
192 acquired_nodes: &mut AcquiredNodes<'_>,
193 cascaded: &CascadedValues<'_>,
194 viewport: &Viewport,
195 draw_ctx: &mut DrawingCtx,
196 ) -> Result<Option<Layer>, Box<InternalRenderingError>> {
197 let dimensions = document.get_intrinsic_dimensions();
198
199 let values = cascaded.get();
200
201 let params = NormalizeParams::new(values, viewport);
202
203 let x = values.x().0.to_user(¶ms);
204 let y = values.y().0.to_user(¶ms);
205
206 let w = match values.width().0 {
207 LengthOrAuto::Length(l) => l.to_user(¶ms),
208 LengthOrAuto::Auto => dimensions.width.to_user(¶ms),
209 };
210
211 let h = match values.height().0 {
212 LengthOrAuto::Length(l) => l.to_user(¶ms),
213 LengthOrAuto::Auto => dimensions.height.to_user(¶ms),
214 };
215
216 let rect = Rect::new(x, y, x + w, y + h);
217
218 let overflow = values.overflow();
219
220 let dest_rect = match dimensions.vbox {
221 None => Rect::from_size(w, h),
222 Some(vbox) => self.aspect.compute(&vbox, &Rect::new(x, y, x + w, y + h)),
223 };
224
225 let dest_size = dest_rect.size();
226
227 let surface_dest_rect = Rect::from_size(dest_size.0, dest_size.1);
228
229 let surface_width = checked_i32(dest_size.0.ceil())?;
231 let surface_height = checked_i32(dest_size.1.ceil())?;
232 let surface =
233 cairo::ImageSurface::create(cairo::Format::ARgb32, surface_width, surface_height)?;
234
235 {
236 let cr = cairo::Context::new(&surface)?;
237
238 let options = draw_ctx.rendering_options(SvgNesting::ReferencedFromImageElement);
239
240 document.render_document(&cr, &cairo::Rectangle::from(surface_dest_rect), &options)?;
241 }
242
243 let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;
244
245 let image = Box::new(layout::Image {
246 surface,
247 rect,
248 aspect: self.aspect,
249 overflow,
250 image_rendering: values.image_rendering(),
251 });
252
253 let elt = node.borrow_element();
254 let stacking_ctx = StackingContext::new(
255 draw_ctx.session(),
256 acquired_nodes,
257 &elt,
258 values.transform(),
259 None,
260 values,
261 );
262
263 let layer = Layer {
264 kind: LayerKind::Image(image),
265 stacking_ctx,
266 };
267
268 Ok(Some(layer))
269 }
270}
271
272pub fn checked_i32(x: f64) -> Result<i32, cairo::Error> {
273 cast::i32(x).map_err(|_| cairo::Error::InvalidSize)
274}