1
//! The `image` element.
2

            
3
use markup5ever::{expanded_name, local_name, ns};
4

            
5
use crate::aspect_ratio::AspectRatio;
6
use crate::bbox::BoundingBox;
7
use crate::document::{AcquiredNodes, Document, Resource};
8
use crate::drawing_ctx::{DrawingCtx, SvgNesting, Viewport};
9
use crate::element::{set_attribute, ElementTrait};
10
use crate::error::*;
11
use crate::href::{is_href, set_href};
12
use crate::layout::{self, Layer, LayerKind, StackingContext};
13
use crate::length::*;
14
use crate::node::{CascadedValues, Node, NodeBorrow};
15
use crate::parsers::ParseValue;
16
use crate::rect::Rect;
17
use crate::rsvg_log;
18
use crate::session::Session;
19
use crate::surface_utils::shared_surface::{SharedImageSurface, SurfaceType};
20
use crate::xml::Attributes;
21

            
22
/// The `<image>` element.
23
///
24
/// Note that its x/y/width/height are properties in SVG2, so they are
25
/// defined as part of [the properties machinery](properties.rs).
26
#[derive(Default)]
27
pub struct Image {
28
    aspect: AspectRatio,
29
    href: Option<String>,
30
}
31

            
32
impl ElementTrait for Image {
33
2109
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
34
10944
        for (attr, value) in attrs.iter() {
35
10944
            match attr.expanded() {
36
                expanded_name!("", "preserveAspectRatio") => {
37
589
                    set_attribute(&mut self.aspect, attr.parse(value), session)
38
                }
39

            
40
                // "path" is used by some older Adobe Illustrator versions
41
10355
                ref a if is_href(a) || *a == expanded_name!("", "path") => {
42
2090
                    set_href(a, &mut self.href, Some(value.to_string()))
43
                }
44

            
45
8265
                _ => (),
46
            }
47
        }
48
2109
    }
49

            
50
2109
    fn layout(
51
2109
        &self,
52
2109
        node: &Node,
53
2109
        acquired_nodes: &mut AcquiredNodes<'_>,
54
2109
        cascaded: &CascadedValues<'_>,
55
2109
        viewport: &Viewport,
56
2109
        draw_ctx: &mut DrawingCtx,
57
2109
        _clipping: bool,
58
2109
    ) -> Result<Option<Layer>, InternalRenderingError> {
59
2109
        if let Some(ref url) = self.href {
60
2090
            self.layout_from_url(url, node, acquired_nodes, cascaded, viewport, draw_ctx)
61
        } else {
62
19
            Ok(None)
63
        }
64
2109
    }
65

            
66
2109
    fn draw(
67
2109
        &self,
68
2109
        node: &Node,
69
2109
        acquired_nodes: &mut AcquiredNodes<'_>,
70
2109
        cascaded: &CascadedValues<'_>,
71
2109
        viewport: &Viewport,
72
2109
        draw_ctx: &mut DrawingCtx,
73
2109
        clipping: bool,
74
2109
    ) -> Result<BoundingBox, InternalRenderingError> {
75
2109
        let layer = self.layout(node, acquired_nodes, cascaded, viewport, draw_ctx, clipping)?;
76

            
77
2109
        if let Some(layer) = layer {
78
2052
            draw_ctx.draw_layer(&layer, acquired_nodes, clipping, viewport)
79
        } else {
80
57
            Ok(viewport.empty_bbox())
81
        }
82
2109
    }
83
}
84

            
85
impl Image {
86
2090
    fn layout_from_url(
87
2090
        &self,
88
2090
        url: &str,
89
2090
        node: &Node,
90
2090
        acquired_nodes: &mut AcquiredNodes<'_>,
91
2090
        cascaded: &CascadedValues<'_>,
92
2090
        viewport: &Viewport,
93
2090
        draw_ctx: &mut DrawingCtx,
94
2090
    ) -> Result<Option<Layer>, InternalRenderingError> {
95
2090
        match acquired_nodes.lookup_resource(url) {
96
1786
            Ok(Resource::Image(surface)) => self.layout_from_surface(
97
1786
                &surface,
98
1786
                node,
99
1786
                acquired_nodes,
100
1786
                cascaded,
101
1786
                viewport,
102
1786
                draw_ctx,
103
1786
            ),
104

            
105
266
            Ok(Resource::Document(document)) => self.layout_from_svg(
106
266
                &document,
107
266
                node,
108
266
                acquired_nodes,
109
266
                cascaded,
110
266
                viewport,
111
266
                draw_ctx,
112
266
            ),
113

            
114
38
            Err(e) => {
115
38
                rsvg_log!(
116
38
                    draw_ctx.session(),
117
38
                    "could not load image \"{}\": {}",
118
38
                    url,
119
38
                    e
120
38
                );
121
38
                Ok(None)
122
            }
123
        }
124
2090
    }
125

            
126
    /// Draw an `<image>` from a raster image.
127
1786
    fn layout_from_surface(
128
1786
        &self,
129
1786
        surface: &SharedImageSurface,
130
1786
        node: &Node,
131
1786
        acquired_nodes: &mut AcquiredNodes<'_>,
132
1786
        cascaded: &CascadedValues<'_>,
133
1786
        viewport: &Viewport,
134
1786
        draw_ctx: &mut DrawingCtx,
135
1786
    ) -> Result<Option<Layer>, InternalRenderingError> {
136
1786
        let values = cascaded.get();
137
1786

            
138
1786
        let params = NormalizeParams::new(values, viewport);
139
1786

            
140
1786
        let x = values.x().0.to_user(&params);
141
1786
        let y = values.y().0.to_user(&params);
142

            
143
1786
        let w = match values.width().0 {
144
1767
            LengthOrAuto::Length(l) => l.to_user(&params),
145
19
            LengthOrAuto::Auto => surface.width() as f64,
146
        };
147
1786
        let h = match values.height().0 {
148
1767
            LengthOrAuto::Length(l) => l.to_user(&params),
149
19
            LengthOrAuto::Auto => surface.height() as f64,
150
        };
151

            
152
1786
        let rect = Rect::new(x, y, x + w, y + h);
153
1786

            
154
1786
        let overflow = values.overflow();
155
1786

            
156
1786
        let image = Box::new(layout::Image {
157
1786
            surface: surface.clone(),
158
1786
            rect,
159
1786
            aspect: self.aspect,
160
1786
            overflow,
161
1786
            image_rendering: values.image_rendering(),
162
1786
        });
163
1786

            
164
1786
        let elt = node.borrow_element();
165
1786
        let stacking_ctx = StackingContext::new(
166
1786
            draw_ctx.session(),
167
1786
            acquired_nodes,
168
1786
            &elt,
169
1786
            values.transform(),
170
1786
            None,
171
1786
            values,
172
1786
        );
173
1786

            
174
1786
        let layer = Layer {
175
1786
            kind: LayerKind::Image(image),
176
1786
            stacking_ctx,
177
1786
        };
178
1786

            
179
1786
        Ok(Some(layer))
180
1786
    }
181

            
182
    /// Draw an `<image>` from an SVG image.
183
    ///
184
    /// Per the [spec], we need to rasterize the SVG ("The result of processing an ‘image’
185
    /// is always a four-channel RGBA result.")  and then composite it as if it were a PNG
186
    /// or JPEG.
187
    ///
188
    /// [spec]: https://www.w3.org/TR/SVG2/embedded.html#ImageElement
189
266
    fn layout_from_svg(
190
266
        &self,
191
266
        document: &Document,
192
266
        node: &Node,
193
266
        acquired_nodes: &mut AcquiredNodes<'_>,
194
266
        cascaded: &CascadedValues<'_>,
195
266
        viewport: &Viewport,
196
266
        draw_ctx: &mut DrawingCtx,
197
266
    ) -> Result<Option<Layer>, InternalRenderingError> {
198
266
        let dimensions = document.get_intrinsic_dimensions();
199
266

            
200
266
        let values = cascaded.get();
201
266

            
202
266
        let params = NormalizeParams::new(values, viewport);
203
266

            
204
266
        let x = values.x().0.to_user(&params);
205
266
        let y = values.y().0.to_user(&params);
206

            
207
266
        let w = match values.width().0 {
208
266
            LengthOrAuto::Length(l) => l.to_user(&params),
209
            LengthOrAuto::Auto => dimensions.width.to_user(&params),
210
        };
211

            
212
266
        let h = match values.height().0 {
213
266
            LengthOrAuto::Length(l) => l.to_user(&params),
214
            LengthOrAuto::Auto => dimensions.height.to_user(&params),
215
        };
216

            
217
266
        let rect = Rect::new(x, y, x + w, y + h);
218
266

            
219
266
        let overflow = values.overflow();
220

            
221
266
        let dest_rect = match dimensions.vbox {
222
            None => Rect::from_size(w, h),
223
266
            Some(vbox) => self.aspect.compute(&vbox, &Rect::new(x, y, x + w, y + h)),
224
        };
225

            
226
266
        let dest_size = dest_rect.size();
227
266

            
228
266
        let surface_dest_rect = Rect::from_size(dest_size.0, dest_size.1);
229

            
230
        // We use ceil() to avoid chopping off the last pixel if it is partially covered.
231
266
        let surface_width = checked_i32(dest_size.0.ceil())?;
232
266
        let surface_height = checked_i32(dest_size.1.ceil())?;
233
266
        let surface =
234
266
            cairo::ImageSurface::create(cairo::Format::ARgb32, surface_width, surface_height)?;
235

            
236
        {
237
266
            let cr = cairo::Context::new(&surface)?;
238

            
239
266
            let options = draw_ctx.rendering_options(SvgNesting::ReferencedFromImageElement);
240
266

            
241
266
            document.render_document(&cr, &cairo::Rectangle::from(surface_dest_rect), &options)?;
242
        }
243

            
244
266
        let surface = SharedImageSurface::wrap(surface, SurfaceType::SRgb)?;
245

            
246
266
        let image = Box::new(layout::Image {
247
266
            surface,
248
266
            rect,
249
266
            aspect: self.aspect,
250
266
            overflow,
251
266
            image_rendering: values.image_rendering(),
252
266
        });
253
266

            
254
266
        let elt = node.borrow_element();
255
266
        let stacking_ctx = StackingContext::new(
256
266
            draw_ctx.session(),
257
266
            acquired_nodes,
258
266
            &elt,
259
266
            values.transform(),
260
266
            None,
261
266
            values,
262
266
        );
263
266

            
264
266
        let layer = Layer {
265
266
            kind: LayerKind::Image(image),
266
266
            stacking_ctx,
267
266
        };
268
266

            
269
266
        Ok(Some(layer))
270
266
    }
271
}
272

            
273
570
pub fn checked_i32(x: f64) -> Result<i32, cairo::Error> {
274
570
    cast::i32(x).map_err(|_| cairo::Error::InvalidSize)
275
570
}