[librsvg: 4/15] Move the legacy sizing logic to c_api
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 4/15] Move the legacy sizing logic to c_api
- Date: Tue, 27 Oct 2020 23:36:50 +0000 (UTC)
commit 08c68dd881392b374d634773904623a18a4d5b48
Author: Federico Mena Quintero <federico gnome org>
Date: Tue Oct 27 10:54:02 2020 -0600
Move the legacy sizing logic to c_api
Export a Handle::get_intrinsic_size_in_pixels(), which resolves
non-percent lengths for the <svg>'s width/height attributes.
For other dimensions (percentages, nonexitent width/height, etc.), we
do the legacy logic in c_api.
Cargo.lock | 1 +
librsvg/Cargo.toml | 1 +
librsvg/c_api.rs | 147 ++++++++++++++++++++++++++++++++++++++-----
librsvg/pixbuf_utils.rs | 2 +-
rsvg_internals/src/handle.rs | 138 ++++++++--------------------------------
5 files changed, 160 insertions(+), 129 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 4c2687be..efa21792 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -815,6 +815,7 @@ dependencies = [
"cairo-sys-rs",
"cast",
"criterion",
+ "float-cmp",
"gdk-pixbuf",
"gdk-pixbuf-sys",
"gio",
diff --git a/librsvg/Cargo.toml b/librsvg/Cargo.toml
index 0032c304..ff94bc87 100644
--- a/librsvg/Cargo.toml
+++ b/librsvg/Cargo.toml
@@ -16,6 +16,7 @@ bitflags = "1.0"
cairo-rs = { version="0.8.0", features=["v1_16"] }
cairo-sys-rs = "0.9.0"
cast = "0.2.3"
+float-cmp = "0.8.0"
gdk-pixbuf = "0.8.0"
gdk-pixbuf-sys = "0.9.0"
glib = "0.9.0"
diff --git a/librsvg/c_api.rs b/librsvg/c_api.rs
index 1a2262c9..edd5c990 100644
--- a/librsvg/c_api.rs
+++ b/librsvg/c_api.rs
@@ -8,14 +8,13 @@ use std::str;
use std::sync::Once;
use std::{f64, i32};
+use bitflags::bitflags;
+use float_cmp::approx_eq;
use gdk_pixbuf::Pixbuf;
+use gio::prelude::*;
use glib::error::ErrorDomain;
use url::Url;
-use bitflags::bitflags;
-
-use gio::prelude::*;
-
use glib::object::ObjectClass;
use glib::subclass;
use glib::subclass::object::ObjectClassSubclassExt;
@@ -32,7 +31,7 @@ use glib::types::instance_of;
use gobject_sys::{GEnumValue, GFlagsValue};
use rsvg_internals::{
- rsvg_log, DefsLookupErrorKind, Handle, IntrinsicDimensions, LoadOptions, LoadingError, Rect,
+ rsvg_log, DefsLookupErrorKind, Handle, IntrinsicDimensions, Length, LoadOptions, LoadingError,
RenderingError, RsvgLength, SharedImageSurface, SurfaceType, UrlResolver, ViewBox,
};
@@ -580,6 +579,21 @@ impl Drop for SizeCallback {
}
}
+trait CairoRectangleExt {
+ fn from_size(width: f64, height: f64) -> Self;
+}
+
+impl CairoRectangleExt for cairo::Rectangle {
+ fn from_size(width: f64, height: f64) -> Self {
+ Self {
+ x: 0.0,
+ y: 0.0,
+ width,
+ height,
+ }
+ }
+}
+
impl CHandle {
pub fn set_base_url(&self, url: &str) {
let state = self.load_state.borrow();
@@ -816,8 +830,8 @@ impl CHandle {
.get_geometry_sub(id)
.and_then(|(ink_r, _)| {
// Keep these in sync with tests/src/reference.rs
- let width = checked_i32(ink_r.width().round())?;
- let height = checked_i32(ink_r.height().round())?;
+ let width = checked_i32(ink_r.width.round())?;
+ let height = checked_i32(ink_r.height.round())?;
Ok((ink_r, width, height))
})
@@ -827,8 +841,8 @@ impl CHandle {
RsvgDimensionData {
width: w,
height: h,
- em: ink_r.width(),
- ex: ink_r.height(),
+ em: ink_r.width,
+ ex: ink_r.height,
}
});
@@ -846,8 +860,8 @@ impl CHandle {
self.get_geometry_sub(id)
.and_then(|(ink_r, _)| {
- let width = checked_i32(ink_r.width().round())?;
- let height = checked_i32(ink_r.height().round())?;
+ let width = checked_i32(ink_r.width.round())?;
+ let height = checked_i32(ink_r.height.round())?;
Ok((ink_r, width, height))
})
@@ -855,18 +869,70 @@ impl CHandle {
inner.size_callback.call(width, height);
Ok(RsvgPositionData {
- x: checked_i32(ink_r.x0)?,
- y: checked_i32(ink_r.y0)?,
+ x: checked_i32(ink_r.x)?,
+ y: checked_i32(ink_r.y)?,
})
})
.map_err(warn_on_invalid_id)
}
- pub fn get_geometry_sub(&self, id: Option<&str>) -> Result<(Rect, Rect), RenderingError> {
+ pub fn get_geometry_sub(
+ &self,
+ id: Option<&str>,
+ ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> {
let handle = self.get_handle_ref()?;
let inner = self.inner.borrow();
- handle.get_geometry_sub(id, inner.dpi.into(), inner.is_testing)
+ match id {
+ Some(id) => handle.get_geometry_for_layer(
+ Some(id),
+ &unit_rectangle(),
+ inner.dpi.into(),
+ inner.is_testing,
+ ),
+
+ None => self.document_size_in_pixels(),
+ }
+ }
+
+ fn document_size_in_pixels(
+ &self,
+ ) -> Result<(cairo::Rectangle, cairo::Rectangle), RenderingError> {
+ let handle = self.get_handle_ref()?;
+ let inner = self.inner.borrow();
+
+ if let Some((width, height)) = self.get_svg_size()? {
+ let rect = cairo::Rectangle::from_size(width, height);
+ Ok((rect, rect))
+ } else {
+ handle.get_geometry_for_layer(
+ None,
+ &unit_rectangle(),
+ inner.dpi.into(),
+ inner.is_testing,
+ )
+ }
+ }
+
+ // Returns the SVG's size suitable for the legacy C API, or None
+ // if it must be computed by hand.
+ //
+ // The legacy C API can compute an SVG document's size from the
+ // `width`, `height`, and `viewBox` attributes of the toplevel `<svg>`
+ // element. If these are not available, then the size must be computed
+ // by actually measuring the geometries of elements in the document; this last
+ // case is implemented by the caller.
+ //
+ // See https://www.w3.org/TR/css-images-3/#sizing-terms for terminology and logic.
+ fn get_svg_size(&self) -> Result<Option<(f64, f64)>, RenderingError> {
+ let handle = self.get_handle_ref()?;
+ let inner = self.inner.borrow();
+
+ Ok(handle
+ .get_intrinsic_size_in_pixels(inner.dpi.into())
+ .or_else(|| {
+ size_in_pixels_from_percentage_width_and_height(&handle.get_intrinsic_dimensions())
+ }))
}
fn set_stylesheet(&self, css: &str) -> Result<(), LoadingError> {
@@ -1007,6 +1073,57 @@ impl CHandle {
}
}
+/// If the width and height are in percentage units, computes a size equal to the
+/// `viewBox`'s aspect ratio if it exists, or else returns None.
+///
+/// For example, a `viewBox="0 0 100 200"` will yield `Some(100.0, 200.0)`.
+///
+/// Note that this only checks that the width and height are in percentage units, but
+/// it actually ignores their values. This is because at the point this function is
+/// called, there is no viewport to embed the SVG document in, so those percentage
+/// 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(
+ dim: &IntrinsicDimensions,
+) -> Option<(f64, f64)> {
+ let IntrinsicDimensions {
+ width,
+ height,
+ vbox,
+ } = *dim;
+
+ use rsvg_internals::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.
+
+ match (width, height, vbox) {
+ (None, None, Some(vbox)) => Some((vbox.width(), vbox.height())),
+
+ (
+ 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()))
+ }
+
+ _ => None,
+ }
+}
+
+fn unit_rectangle() -> cairo::Rectangle {
+ cairo::Rectangle::from_size(1.0, 1.0)
+}
+
fn is_rsvg_handle(obj: *const RsvgHandle) -> bool {
unsafe { instance_of::<CHandle>(obj as *const _) }
}
diff --git a/librsvg/pixbuf_utils.rs b/librsvg/pixbuf_utils.rs
index fe15812e..566e6354 100644
--- a/librsvg/pixbuf_utils.rs
+++ b/librsvg/pixbuf_utils.rs
@@ -216,7 +216,7 @@ fn pixbuf_from_file_with_size_mode(
handle
.get_geometry_sub(None)
.and_then(|(ink_r, _)| {
- let (document_width, document_height) = (ink_r.width(), ink_r.height());
+ let (document_width, document_height) = (ink_r.width, ink_r.height);
let (desired_width, desired_height) =
get_final_size(document_width, document_height, size_mode);
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index dfdf1543..3463eb88 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -2,15 +2,12 @@
//!
//! This module provides the primitives on which the public APIs are implemented.
-use float_cmp::approx_eq;
-
use crate::bbox::BoundingBox;
use crate::css::{Origin, Stylesheet};
use crate::document::{AcquiredNodes, Document};
use crate::dpi::Dpi;
use crate::drawing_ctx::{draw_tree, DrawingMode, ViewParams};
use crate::error::{DefsLookupErrorKind, LoadingError, RenderingError};
-use crate::length::*;
use crate::node::{CascadedValues, Node, NodeBorrow};
use crate::rect::Rect;
use crate::structure::IntrinsicDimensions;
@@ -109,27 +106,35 @@ impl Handle {
}
}
- /// Returns (ink_rect, logical_rect)
- pub fn get_geometry_sub(
- &self,
- id: Option<&str>,
- dpi: Dpi,
- is_testing: bool,
- ) -> Result<(Rect, Rect), RenderingError> {
- let node = self.get_node_or_root(id)?;
- let root = self.document.root();
- let is_root = node == root;
+ /// If the intrinsic dimensions are in physical units, computes their pixel size, or
+ /// returns `None`.
+ ///
+ /// If any of the width/height are percentages, we cannot compute the size here. Here
+ /// just normalize lengths with physical units, or units based on the font size.
+ pub fn get_intrinsic_size_in_pixels(&self, dpi: Dpi) -> Option<(f64, f64)> {
+ let dimensions = self.get_intrinsic_dimensions();
+
+ if dimensions.width.is_none() || dimensions.height.is_none() {
+ // If either of width/height don't exist, the spec says they should default to 100%,
+ // which is a percentage-based unit - which we can't resolve here.
+ return None;
+ }
- if is_root {
- let cascaded = CascadedValues::new_from_node(&node);
+ let w = dimensions.width.unwrap();
+ let h = dimensions.height.unwrap();
- if let Some((w, h)) = get_svg_size(&self.get_intrinsic_dimensions(), &cascaded, dpi) {
- let rect = Rect::from_size(w, h);
- return Ok((rect, rect));
- }
+ use crate::length::LengthUnit::*;
+
+ if w.unit == Percent || h.unit == Percent {
+ return None;
}
- self.geometry_for_layer(node, unit_rectangle(), dpi, is_testing)
+ let params = ViewParams::new(dpi, 0.0, 0.0);
+ let root = self.document.root();
+ let cascaded = CascadedValues::new_from_node(&root);
+ let values = cascaded.get();
+
+ Some((w.normalize(values, ¶ms), h.normalize(values, ¶ms)))
}
fn get_node_or_root(&self, id: Option<&str>) -> Result<Node, RenderingError> {
@@ -381,96 +386,3 @@ fn check_cairo_context(cr: &cairo::Context) -> Result<(), RenderingError> {
fn unit_rectangle() -> Rect {
Rect::from_size(1.0, 1.0)
}
-
-/// Returns the SVG's size suitable for the legacy C API, or None
-/// if it must be computed by hand.
-///
-/// The legacy C API can compute an SVG document's size from the
-/// `width`, `height`, and `viewBox` attributes of the toplevel `<svg>`
-/// element. If these are not available, then the size must be computed
-/// by actually measuring the geometries of elements in the document; this last
-/// case is implemented by the caller.
-///
-/// See https://www.w3.org/TR/css-images-3/#sizing-terms for terminology and logic.
-fn get_svg_size(
- dimensions: &IntrinsicDimensions,
- cascaded: &CascadedValues,
- dpi: Dpi,
-) -> Option<(f64, f64)> {
- size_from_intrinsic_dimensions(dimensions, cascaded, dpi)
- .or_else(|| size_in_pixels_from_vbox(dimensions))
-}
-
-/// Computes a size from `width` and `height` in physical units, or returns None.
-///
-/// If any of the width/height are percentages, we cannot compute the size here. Here
-/// just normalize lengths with physical units, or units based on the font size.
-fn size_from_intrinsic_dimensions(
- dimensions: &IntrinsicDimensions,
- cascaded: &CascadedValues,
- dpi: Dpi,
-) -> Option<(f64, f64)> {
- if let (None, None) = (dimensions.width, dimensions.height) {
- return None;
- }
-
- let w = dimensions.width.unwrap();
- let h = dimensions.height.unwrap();
-
- use crate::length::LengthUnit::*;
-
- if w.unit == Percent || h.unit == Percent {
- return None;
- }
-
- let params = ViewParams::new(dpi, 0.0, 0.0);
- let values = cascaded.get();
-
- Some((w.normalize(values, ¶ms), h.normalize(values, ¶ms)))
-}
-
-/// If the width and height do not exist (so default to 100% per the spec) or if they
-/// exist and are in percentage units, computes a size equal to the `viewBox`'s aspect
-/// ratio if it exists, or else returns None.
-///
-/// For example, a `viewBox="0 0 100 200"` will yield `Some(100.0, 200.0)`.
-///
-/// Note that this only checks that the width and height are in percentage units, but
-/// it actually ignores their values. This is because at the point this function is
-/// called, there is no viewport to embed the SVG document in, so those percentage
-/// 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_vbox(dim: &IntrinsicDimensions) -> Option<(f64, f64)> {
- let IntrinsicDimensions {
- width,
- height,
- vbox,
- } = *dim;
-
- use crate::length::LengthUnit::*;
-
- // If width and height don't exist, or if they are both 100%, just use the vbox size as a pixel size.
- // This gives a size with the correct aspect ratio.
-
- match (width, height, vbox) {
- (None, None, Some(vbox)) => Some((vbox.width(), vbox.height())),
-
- (
- 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()))
- }
-
- _ => None,
- }
-}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]