[librsvg: 5/8] (#797) - Handle the case where the toplevel SVG has only one of width/height missing
- From: Marge Bot <marge-bot src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 5/8] (#797) - Handle the case where the toplevel SVG has only one of width/height missing
- Date: Fri, 1 Oct 2021 19:08:11 +0000 (UTC)
commit d3f0ac17b189f98b1213e93958afb873470359e3
Author: Federico Mena Quintero <federico gnome org>
Date: Fri Oct 1 12:36:57 2021 -0500
(#797) - Handle the case where the toplevel SVG has only one of width/height missing
Consider this:
<svg xmlns="http://www.w3.org/2000/svg" width="60" viewBox="0 0 30 40">
The height should default to 100%. If this were being rendered to a
viewport, then no problem - use 60 units for the width, and the
viewport's entire height.
But we are being asked to produce a "natural size", and we don't have
a viewport size. So, just produce a size based on the width and the
viewBox's aspect ratio.
Fixes https://gitlab.gnome.org/GNOME/librsvg/-/issues/797
Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/595>
src/api.rs | 6 +-
src/c_api/sizing.rs | 37 ++++----
src/handle.rs | 26 ++++++
tests/src/legacy_sizing.rs | 204 ++++++++++++++++++++++++++++++++-------------
4 files changed, 196 insertions(+), 77 deletions(-)
---
diff --git a/src/api.rs b/src/api.rs
index 12da4b03..b38880d4 100644
--- a/src/api.rs
+++ b/src/api.rs
@@ -214,7 +214,7 @@ fn url_from_file(file: &gio::File) -> Result<Url, LoadingError> {
///
/// You can create this from one of the `read` methods in
/// [`Loader`].
-pub struct SvgHandle(Handle);
+pub struct SvgHandle(pub(crate) Handle);
impl SvgHandle {
/// Checks if the SVG has an element with the specified `id`.
@@ -243,8 +243,8 @@ impl SvgHandle {
/// Can render an `SvgHandle` to a Cairo context.
pub struct CairoRenderer<'a> {
- handle: &'a SvgHandle,
- dpi: Dpi,
+ pub(crate) handle: &'a SvgHandle,
+ pub(crate) dpi: Dpi,
user_language: UserLanguage,
is_testing: bool,
}
diff --git a/src/c_api/sizing.rs b/src/c_api/sizing.rs
index adb95ce4..7095fc45 100644
--- a/src/c_api/sizing.rs
+++ b/src/c_api/sizing.rs
@@ -11,7 +11,9 @@
//! of the web platform, which assumes that all embedded content goes into a viewport.
use crate::api::{CairoRenderer, IntrinsicDimensions, Length, RenderingError};
-use float_cmp::approx_eq;
+use crate::dpi::Dpi;
+use crate::handle::Handle;
+use crate::length::*;
use super::handle::CairoRectangleExt;
@@ -47,7 +49,9 @@ impl<'a> LegacySize for CairoRenderer<'a> {
let size_from_intrinsic_dimensions =
self.intrinsic_size_in_pixels().or_else(|| {
size_in_pixels_from_percentage_width_and_height(
+ &self.handle.0,
&self.intrinsic_dimensions(),
+ self.dpi,
)
});
@@ -78,7 +82,9 @@ fn unit_rectangle() -> cairo::Rectangle {
/// units cannot be resolved against anything in particular. The idea is to return
/// some dimensions with the correct aspect ratio.
fn size_in_pixels_from_percentage_width_and_height(
+ handle: &Handle,
dim: &IntrinsicDimensions,
+ dpi: Dpi,
) -> Option<(f64, f64)> {
let IntrinsicDimensions {
width,
@@ -86,26 +92,21 @@ fn size_in_pixels_from_percentage_width_and_height(
vbox,
} = *dim;
- use crate::api::LengthUnit::*;
+ use LengthUnit::*;
- // If both width and height are 100%, just use the vbox size as a pixel size.
- // This gives a size with the correct aspect ratio.
+ // Unwrap or return None if we don't know the aspect ratio -> Let the caller handle it.
+ let vbox = vbox?;
- match (width, height, vbox) {
- (None, None, Some(vbox)) => Some((vbox.width, vbox.height)),
+ let (w, h) = handle.width_height_to_user(dpi);
- (
- Some(Length {
- length: w,
- unit: Percent,
- }),
- Some(Length {
- length: h,
- unit: Percent,
- }),
- Some(vbox),
- ) if approx_eq!(f64, w, 1.0) && approx_eq!(f64, h, 1.0) => Some((vbox.width, vbox.height)),
+ // missing width/height default to "auto", which compute to "100%"
+ let width = width.unwrap_or_else(|| Length::new(1.0, Percent));
+ let height = height.unwrap_or_else(|| Length::new(1.0, Percent));
- _ => None,
+ match (width.unit, height.unit) {
+ (Percent, Percent) => Some((vbox.width, vbox.height)),
+ (_, Percent) => Some((w, w * vbox.height / vbox.width)),
+ (Percent, _) => Some((h * vbox.width / vbox.height, h)),
+ (_, _) => unreachable!("should have been called with percentage units"),
}
}
diff --git a/src/handle.rs b/src/handle.rs
index 2bd121da..4c6a5325 100644
--- a/src/handle.rs
+++ b/src/handle.rs
@@ -141,6 +141,32 @@ impl Handle {
Some((w.to_user(¶ms), h.to_user(¶ms)))
}
+ /// Normalizes the svg's width/height properties with a 0-sized viewport
+ ///
+ /// This assumes that if one of the properties is in percentage units, then
+ /// its corresponding value will not be used. E.g. if width=100%, the caller
+ /// will ignore the resulting width value.
+ pub fn width_height_to_user(&self, dpi: Dpi) -> (f64, f64) {
+ let dimensions = self.get_intrinsic_dimensions();
+
+ // missing width/height default to "auto", which compute to "100%"
+ let width = dimensions
+ .width
+ .unwrap_or_else(|| ULength::new(1.0, LengthUnit::Percent));
+ let height = dimensions
+ .height
+ .unwrap_or_else(|| ULength::new(1.0, LengthUnit::Percent));
+
+ let view_params = ViewParams::new(dpi, 0.0, 0.0);
+ let root = self.document.root();
+ let cascaded = CascadedValues::new_from_node(&root);
+ let values = cascaded.get();
+
+ let params = NormalizeParams::new(values, &view_params);
+
+ (width.to_user(¶ms), height.to_user(¶ms))
+ }
+
fn get_node_or_root(&self, id: Option<&str>) -> Result<Node, RenderingError> {
if let Some(id) = id {
Ok(self.lookup_node(id)?)
diff --git a/tests/src/legacy_sizing.rs b/tests/src/legacy_sizing.rs
index f36cccff..3f33f7d1 100644
--- a/tests/src/legacy_sizing.rs
+++ b/tests/src/legacy_sizing.rs
@@ -11,22 +11,26 @@ fn just_viewbox_uses_viewbox_size() {
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 200"/>
"#,
)
- .unwrap();
+ .unwrap();
assert_eq!(
- CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
- (cairo::Rectangle {
- x: 0.0,
- y: 0.0,
- width: 100.0,
- height: 200.0,
- },
- cairo::Rectangle {
- x: 0.0,
- y: 0.0,
- width: 100.0,
- height: 200.0,
- })
+ CairoRenderer::new(&svg)
+ .legacy_layer_geometry(None)
+ .unwrap(),
+ (
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 100.0,
+ height: 200.0,
+ },
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 100.0,
+ height: 200.0,
+ }
+ )
);
}
@@ -39,22 +43,26 @@ fn no_intrinsic_size_uses_element_geometries() {
</svg>
"#,
)
- .unwrap();
+ .unwrap();
assert_eq!(
- CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
- (cairo::Rectangle {
- x: 10.0,
- y: 20.0,
- width: 30.0,
- height: 40.0,
- },
- cairo::Rectangle {
- x: 10.0,
- y: 20.0,
- width: 30.0,
- height: 40.0,
- })
+ CairoRenderer::new(&svg)
+ .legacy_layer_geometry(None)
+ .unwrap(),
+ (
+ cairo::Rectangle {
+ x: 10.0,
+ y: 20.0,
+ width: 30.0,
+ height: 40.0,
+ },
+ cairo::Rectangle {
+ x: 10.0,
+ y: 20.0,
+ width: 30.0,
+ height: 40.0,
+ }
+ )
);
}
@@ -65,22 +73,26 @@ fn hundred_percent_width_height_uses_viewbox() {
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 100 200"/>
"#,
)
- .unwrap();
+ .unwrap();
assert_eq!(
- CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
- (cairo::Rectangle {
- x: 0.0,
- y: 0.0,
- width: 100.0,
- height: 200.0,
- },
- cairo::Rectangle {
- x: 0.0,
- y: 0.0,
- width: 100.0,
- height: 200.0,
- })
+ CairoRenderer::new(&svg)
+ .legacy_layer_geometry(None)
+ .unwrap(),
+ (
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 100.0,
+ height: 200.0,
+ },
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 100.0,
+ height: 200.0,
+ }
+ )
);
}
@@ -93,21 +105,101 @@ fn hundred_percent_width_height_no_viewbox_uses_element_geometries() {
</svg>
"#,
)
- .unwrap();
+ .unwrap();
assert_eq!(
- CairoRenderer::new(&svg).legacy_layer_geometry(None).unwrap(),
- (cairo::Rectangle {
- x: 10.0,
- y: 20.0,
- width: 30.0,
- height: 40.0,
- },
- cairo::Rectangle {
- x: 10.0,
- y: 20.0,
- width: 30.0,
- height: 40.0,
- })
+ CairoRenderer::new(&svg)
+ .legacy_layer_geometry(None)
+ .unwrap(),
+ (
+ cairo::Rectangle {
+ x: 10.0,
+ y: 20.0,
+ width: 30.0,
+ height: 40.0,
+ },
+ cairo::Rectangle {
+ x: 10.0,
+ y: 20.0,
+ width: 30.0,
+ height: 40.0,
+ }
+ )
+ );
+}
+
+#[test]
+fn width_and_viewbox_preserves_aspect_ratio() {
+ let svg = load_svg(
+ br#"<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="60" viewBox="0 0 30 40">
+ <rect x="10" y="20" width="30" height="40" fill="black"/>
+</svg>
+"#,
+ )
+ .unwrap();
+
+ // Per the spec, the height property should default to 100%, so the above would end up
+ // like <svg width="60" height="100%" viewBox="0 0 30 40">
+ //
+ // If that were being *rendered* to a viewport, no problem, just use units horizontally
+ // and 100% of the viewport vertically.
+ //
+ // But we are being asked to compute the SVG's natural geometry, so the best we can do
+ // is to take the aspect ratio defined by the viewBox and apply it to the width.
+
+ assert_eq!(
+ CairoRenderer::new(&svg)
+ .legacy_layer_geometry(None)
+ .unwrap(),
+ (
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 60.0,
+ height: 80.0,
+ },
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 60.0,
+ height: 80.0,
+ }
+ )
+ );
+}
+
+#[test]
+fn height_and_viewbox_preserves_aspect_ratio() {
+ let svg = load_svg(
+ br#"<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" height="80" viewBox="0 0 30 40">
+ <rect x="10" y="20" width="30" height="40" fill="black"/>
+</svg>
+"#,
+ )
+ .unwrap();
+
+ // See the comment above in width_and_viewbox_preserves_aspect_ratio(); this
+ // is equivalent but for the height.
+
+ assert_eq!(
+ CairoRenderer::new(&svg)
+ .legacy_layer_geometry(None)
+ .unwrap(),
+ (
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 60.0,
+ height: 80.0,
+ },
+ cairo::Rectangle {
+ x: 0.0,
+ y: 0.0,
+ width: 60.0,
+ height: 80.0,
+ }
+ )
);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]