[librsvg] aspect_ratio.rs: New module to parse preserveAspectRatio attributes



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]