aspect_ratio.rs: New module to parse preserveAspectRatio attributes

Per https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute
parent 41a847e2
......@@ -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]]
......@@ -18,29 +19,36 @@ name = "bitflags"
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"
......@@ -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"
......
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");
}
}
#[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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment