[librsvg] aspect_ratio.rs: New module to parse preserveAspectRatio attributes
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg] aspect_ratio.rs: New module to parse preserveAspectRatio attributes
- Date: Thu, 2 Feb 2017 04:00:29 +0000 (UTC)
commit 6c02c64d0a1597ea99d20c1292c4ff9755e15c59
Author: Federico Mena Quintero <federico gnome org>
Date: Wed Feb 1 13:55:36 2017 -0600
aspect_ratio.rs: New module to parse preserveAspectRatio attributes
Per https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
rust/Cargo.lock | 55 +++++---
rust/Cargo.toml | 1 +
rust/src/aspect_ratio.rs | 309 ++++++++++++++++++++++++++++++++++++++++++++++
rust/src/lib.rs | 5 +
4 files changed, 350 insertions(+), 20 deletions(-)
---
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 6ab7b08..7f5ceaa 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -2,10 +2,11 @@
name = "rsvg_internals"
version = "0.0.1"
dependencies = [
+ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-rs 0.1.1 (git+https://github.com/gtk-rs/cairo.git)",
"cairo-sys-rs 0.3.2 (git+https://github.com/gtk-rs/cairo.git)",
"glib 0.1.1 (git+https://github.com/gtk-rs/glib)",
- "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -19,28 +20,35 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index";
[[package]]
+name = "bitflags"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+
+[[package]]
name = "c_vec"
-version = "1.0.12"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index";
[[package]]
name = "cairo-rs"
version = "0.1.1"
-source = "git+https://github.com/gtk-rs/cairo.git#21a7dc05ff11d232f49af604c598b23eaa9324a2";
+source = "git+https://github.com/gtk-rs/cairo.git#2b5dac9733893fb2c5a6155fc3971f5e5d3e715d";
dependencies = [
- "c_vec 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "c_vec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cairo-sys-rs 0.3.2 (git+https://github.com/gtk-rs/cairo.git)",
"glib 0.1.1 (git+https://github.com/gtk-rs/glib)",
- "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cairo-sys-rs"
version = "0.3.2"
-source = "git+https://github.com/gtk-rs/cairo.git#21a7dc05ff11d232f49af604c598b23eaa9324a2";
+source = "git+https://github.com/gtk-rs/cairo.git#2b5dac9733893fb2c5a6155fc3971f5e5d3e715d";
dependencies = [
- "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -51,48 +59,55 @@ dependencies = [
"bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.3.2 (git+https://github.com/gtk-rs/sys)",
"gobject-sys 0.3.2 (git+https://github.com/gtk-rs/sys)",
- "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "glib-sys"
version = "0.3.2"
-source = "git+https://github.com/gtk-rs/sys#351af46753ad71b2d7ca75a190846d86425691b3";
+source = "git+https://github.com/gtk-rs/sys#9e646efc356e3895576dad548c505ee9b37323a1";
dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gobject-sys"
version = "0.3.2"
-source = "git+https://github.com/gtk-rs/sys#351af46753ad71b2d7ca75a190846d86425691b3";
+source = "git+https://github.com/gtk-rs/sys#9e646efc356e3895576dad548c505ee9b37323a1";
dependencies = [
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"glib-sys 0.3.2 (git+https://github.com/gtk-rs/sys)",
- "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
-version = "0.2.17"
+version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index";
[[package]]
name = "pkg-config"
-version = "0.3.8"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+
+[[package]]
+name = "winapi"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index";
[metadata]
"checksum bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"8dead7461c1127cf637931a1e50934eb6eee8bff2f74433ac7909e9afcee04a3"
"checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23"
-"checksum c_vec 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" =
"aa9e1d9f7d49e289f36f19effbf3d5a5e30163ecf9c7a3c9be94d5374dec5b9a"
+"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
+"checksum c_vec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" =
"0059f5a658f62a4bd3937a7addc52ccfda144b75cce7a92b187e528629cdc507"
"checksum cairo-rs 0.1.1 (git+https://github.com/gtk-rs/cairo.git)" = "<none>"
"checksum cairo-sys-rs 0.3.2 (git+https://github.com/gtk-rs/cairo.git)" = "<none>"
"checksum glib 0.1.1 (git+https://github.com/gtk-rs/glib)" = "<none>"
"checksum glib-sys 0.3.2 (git+https://github.com/gtk-rs/sys)" = "<none>"
"checksum gobject-sys 0.3.2 (git+https://github.com/gtk-rs/sys)" = "<none>"
-"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" =
"044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8"
-"checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" =
"8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
+"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" =
"684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5"
+"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" =
"3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
+"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" =
"167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index f435f47..f9bf72d 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -5,6 +5,7 @@ authors = ["Federico Mena Quintero <federico gnome org>"]
[dependencies]
libc = "0.2"
+bitflags = ""
[dependencies.cairo-sys-rs]
git = "https://github.com/gtk-rs/cairo.git";
diff --git a/rust/src/aspect_ratio.rs b/rust/src/aspect_ratio.rs
new file mode 100644
index 0000000..d72b1f9
--- /dev/null
+++ b/rust/src/aspect_ratio.rs
@@ -0,0 +1,309 @@
+use std::fmt;
+use std::str::FromStr;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum FitMode {
+ Meet,
+ Slice
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum AlignMode {
+ XminYmin,
+ XmidYmin,
+ XmaxYmin,
+ XminYmid,
+ XmidYmid,
+ XmaxYmid,
+ XminYmax,
+ XmidYmax,
+ XmaxYmax
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Align {
+ None,
+ Aligned {
+ align: AlignMode,
+ fit: FitMode
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct AspectRatio {
+ defer: bool,
+ align: Align
+}
+
+impl Default for Align {
+ fn default () -> Align {
+ Align::Aligned {
+ align: AlignMode::XmidYmid,
+ fit: FitMode::Meet
+ }
+ }
+}
+
+bitflags! {
+ flags AspectRatioFlags: u32 {
+ const XMIN_YMIN = (1 << 0),
+ const XMID_YMIN = (1 << 1),
+ const XMAX_YMIN = (1 << 2),
+ const XMIN_YMID = (1 << 3),
+ const XMID_YMID = (1 << 4),
+ const XMAX_YMID = (1 << 5),
+ const XMIN_YMAX = (1 << 6),
+ const XMID_YMAX = (1 << 7),
+ const XMAX_YMAX = (1 << 8),
+ const SLICE = (1 << 30),
+ const DEFER = (1 << 31)
+ }
+}
+
+pub fn aspect_ratio_to_u32 (a: AspectRatio) -> u32 {
+ let mut val = AspectRatioFlags::empty ();
+
+ if a.defer { val = val | DEFER; }
+
+ match a.align {
+ Align::None => { },
+
+ Align::Aligned { align, fit } => {
+ match align {
+ AlignMode::XminYmin => { val = val | XMIN_YMIN; },
+ AlignMode::XmidYmin => { val = val | XMID_YMIN; },
+ AlignMode::XmaxYmin => { val = val | XMAX_YMIN; },
+ AlignMode::XminYmid => { val = val | XMIN_YMID; },
+ AlignMode::XmidYmid => { val = val | XMID_YMID; },
+ AlignMode::XmaxYmid => { val = val | XMAX_YMID; },
+ AlignMode::XminYmax => { val = val | XMIN_YMAX; },
+ AlignMode::XmidYmax => { val = val | XMID_YMAX; },
+ AlignMode::XmaxYmax => { val = val | XMAX_YMAX; },
+ }
+
+ match fit {
+ FitMode::Meet => { },
+ FitMode::Slice => { val = val | SLICE; }
+ }
+ }
+ }
+
+ val.bits ()
+}
+
+pub fn u32_to_aspect_ratio (val: u32) -> AspectRatio {
+ let val = AspectRatioFlags::from_bits (val).unwrap ();
+
+ let defer = val.contains (DEFER);
+
+ let mut aligned: bool = true;
+
+ let align: AlignMode = {
+ if val.contains (XMIN_YMIN) { AlignMode::XminYmin }
+ else if val.contains (XMID_YMIN) { AlignMode::XmidYmin }
+ else if val.contains (XMAX_YMIN) { AlignMode::XmaxYmin }
+ else if val.contains (XMIN_YMID) { AlignMode::XminYmid }
+ else if val.contains (XMID_YMID) { AlignMode::XmidYmid }
+ else if val.contains (XMAX_YMID) { AlignMode::XmaxYmid }
+ else if val.contains (XMIN_YMAX) { AlignMode::XminYmax }
+ else if val.contains (XMID_YMAX) { AlignMode::XmidYmax }
+ else if val.contains (XMAX_YMAX) { AlignMode::XmaxYmax }
+ else {
+ aligned = false;
+ AlignMode::XmidYmid
+ }
+ };
+
+ let fit: FitMode = if val.contains(SLICE) { FitMode::Slice } else { FitMode::Meet };
+
+ AspectRatio {
+ defer: defer,
+ align: if aligned {
+ Align::Aligned {
+ align: align,
+ fit: fit
+ }
+ } else {
+ Align::None
+ }
+ }
+}
+
+fn parse_align_mode (s: &str) -> Option<Align> {
+ match s {
+ "none" => { Some (Align::None) },
+ "XminYmin" => { Some (Align::Aligned { align: AlignMode::XminYmin, fit: FitMode::Meet } ) },
+ "XmidYmin" => { Some (Align::Aligned { align: AlignMode::XmidYmin, fit: FitMode::Meet } ) },
+ "XmaxYmin" => { Some (Align::Aligned { align: AlignMode::XmaxYmin, fit: FitMode::Meet } ) },
+ "XminYmid" => { Some (Align::Aligned { align: AlignMode::XminYmid, fit: FitMode::Meet } ) },
+ "XmidYmid" => { Some (Align::Aligned { align: AlignMode::XmidYmid, fit: FitMode::Meet } ) },
+ "XmaxYmid" => { Some (Align::Aligned { align: AlignMode::XmaxYmid, fit: FitMode::Meet } ) },
+ "XminYmax" => { Some (Align::Aligned { align: AlignMode::XminYmax, fit: FitMode::Meet } ) },
+ "XmidYmax" => { Some (Align::Aligned { align: AlignMode::XmidYmax, fit: FitMode::Meet } ) },
+ "XmaxYmax" => { Some (Align::Aligned { align: AlignMode::XmaxYmax, fit: FitMode::Meet } ) },
+ _ => { None }
+ }
+}
+
+fn parse_fit_mode (s: &str) -> Option<FitMode> {
+ match s {
+ "meet" => { Some (FitMode::Meet) },
+ "slice" => { Some (FitMode::Slice) },
+ _ => { None }
+ }
+}
+
+enum ParseState {
+ Defer,
+ Align,
+ Fit,
+ Finished
+}
+
+impl FromStr for AspectRatio {
+ type Err = ParseAspectRatioError;
+
+ fn from_str(s: &str) -> Result<AspectRatio, ParseAspectRatioError> {
+ let mut defer = false;
+ let mut align: Align = Default::default ();
+ let mut fit_mode = FitMode::Meet;
+
+ let mut state = ParseState::Defer;
+ let mut iter = s.split_whitespace ();
+
+ while let Some (v) = iter.next () {
+ match state {
+ ParseState::Defer => {
+ if v == "defer" {
+ defer = true;
+ state = ParseState::Align;
+ } else if let Some (parsed_align) = parse_align_mode (v) {
+ align = parsed_align;
+ state = ParseState::Fit;
+ } else {
+ return Err(ParseAspectRatioError);
+ }
+ },
+
+ ParseState::Align => {
+ if let Some (parsed_align) = parse_align_mode (v) {
+ align = parsed_align;
+ state = ParseState::Fit;
+ } else {
+ return Err(ParseAspectRatioError);
+ }
+ },
+
+ ParseState::Fit => {
+ if let Some (parsed_fit) = parse_fit_mode (v) {
+ fit_mode = parsed_fit;
+ state = ParseState::Finished;
+ } else {
+ return Err(ParseAspectRatioError);
+ }
+ },
+
+ _ => {
+ return Err(ParseAspectRatioError);
+ }
+ }
+ }
+
+ // The string must match "[defer] <align> [meet | slice]".
+ // Since the meet|slice is optional, we can end up in either
+ // of the following states:
+ match state {
+ ParseState::Fit | ParseState::Finished => {},
+ _ => { return Err(ParseAspectRatioError); }
+ }
+
+ Ok (AspectRatio {
+ defer: defer,
+ align: match align {
+ Align::None => { Align::None },
+ Align::Aligned { align, ..} => {
+ Align::Aligned {
+ align: align,
+ fit: fit_mode
+ }
+ }
+ }
+ })
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ParseAspectRatioError;
+
+impl fmt::Display for ParseAspectRatioError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ "provided string did not match `[defer] <align> [meet | slice]`".fmt (f)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn parsing_invalid_strings_yields_error () {
+ assert_eq! (AspectRatio::from_str (""), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("defer"), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("defer foo"), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("defer xmidymid"), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("defer XmidYmid foo"), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("xmidymid"), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("XmidYmid foo"), Err(ParseAspectRatioError));
+
+ assert_eq! (AspectRatio::from_str ("defer XmidYmid meet foo"), Err(ParseAspectRatioError));
+ }
+
+ #[test]
+ fn parses_valid_strings () {
+ assert_eq! (AspectRatio::from_str ("XmidYmid"),
+ Ok (AspectRatio { defer: false,
+ align: Align::Aligned { align: AlignMode::XmidYmid,
+ fit: FitMode::Meet } }));
+
+ assert_eq! (AspectRatio::from_str ("defer XmidYmid"),
+ Ok (AspectRatio { defer: true,
+ align: Align::Aligned { align: AlignMode::XmidYmid,
+ fit: FitMode::Meet } }));
+
+ assert_eq! (AspectRatio::from_str ("defer XminYmax"),
+ Ok (AspectRatio { defer: true,
+ align: Align::Aligned { align: AlignMode::XminYmax,
+ fit: FitMode::Meet } }));
+
+ assert_eq! (AspectRatio::from_str ("defer XmaxYmid meet"),
+ Ok (AspectRatio { defer: true,
+ align: Align::Aligned { align: AlignMode::XmaxYmid,
+ fit: FitMode::Meet } }));
+
+ assert_eq! (AspectRatio::from_str ("defer XminYmax slice"),
+ Ok (AspectRatio { defer: true,
+ align: Align::Aligned { align: AlignMode::XminYmax,
+ fit: FitMode::Slice } }));
+ }
+
+ fn test_roundtrip (s: &str) {
+ let a = AspectRatio::from_str (s).unwrap ();
+
+ assert_eq! (u32_to_aspect_ratio (aspect_ratio_to_u32 (a)), a);
+ }
+
+ #[test]
+ fn conversion_to_u32_roundtrips () {
+ test_roundtrip ("defer XmidYmid");
+ test_roundtrip ("defer XminYmax slice");
+ test_roundtrip ("XmaxYmax meet");
+ test_roundtrip ("XminYmid slice");
+ }
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 7238382..de25698 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -1,3 +1,6 @@
+#[macro_use]
+extern crate bitflags;
+
pub use bbox::{
RsvgBbox,
rsvg_bbox_init,
@@ -41,6 +44,8 @@ pub use length::{
rsvg_length_hand_normalize,
};
+
+mod aspect_ratio;
mod bbox;
mod drawing_ctx;
mod handle;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]