[librsvg: 1/4] tests: add test to check the size of PDF created by rsvg-convert



commit 2a358ca66f1eaa47e325b00244a43892195c5f7c
Author: Sven Neumann <sven svenfoo org>
Date:   Thu Feb 20 11:12:34 2020 +0100

    tests: add test to check the size of PDF created by rsvg-convert
    
    Note that the test actually makes wrong assumptions about the
    expected size and resolution.
    
    See https://gitlab.gnome.org/GNOME/librsvg/issues/514

 Cargo.lock                        |   1 +
 tests/Cargo.toml                  |   1 +
 tests/src/cmdline/predicates.rs   | 122 ++++++++++++++++++++++++++++++++------
 tests/src/cmdline/rsvg_convert.rs |  14 ++++-
 tests/src/main.rs                 |   3 +
 5 files changed, 122 insertions(+), 19 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 69963718..917093af 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -717,6 +717,7 @@ version = "0.1.0"
 dependencies = [
  "assert_cmd 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "float-cmp 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lopdf 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "png 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "predicates 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/tests/Cargo.toml b/tests/Cargo.toml
index d602bcff..0715addb 100644
--- a/tests/Cargo.toml
+++ b/tests/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2018"
 [dev-dependencies]
 assert_cmd = "0.12"
 chrono = "0.4.0"
+float-cmp = "0.6.0"
 lopdf = "0.24.0"
 png = "0.15.3"
 predicates = "1.0.2"
diff --git a/tests/src/cmdline/predicates.rs b/tests/src/cmdline/predicates.rs
index 065d5a2c..4bddabcc 100644
--- a/tests/src/cmdline/predicates.rs
+++ b/tests/src/cmdline/predicates.rs
@@ -12,6 +12,7 @@ pub mod file {
     use predicates::reflection::{Case, Child, PredicateReflection, Product};
     use predicates::str::{ContainsPredicate, StartsWithPredicate};
 
+    use std::cmp;
     use std::fmt;
 
     /// Checks that the variable of type [u8] can be parsed as a PDF file.
@@ -26,6 +27,22 @@ pub mod file {
             }
         }
 
+        pub fn with_page_size(
+            self: Self,
+            width: i64,
+            height: i64,
+            dpi: f64,
+        ) -> DetailPredicate<Self> {
+            DetailPredicate::<Self> {
+                p: self,
+                d: Detail::PageSize(Dimensions {
+                    w: width,
+                    h: height,
+                    unit: dpi / 72.0,
+                }),
+            }
+        }
+
         pub fn with_creation_date(self: Self, when: DateTime<Utc>) -> DetailPredicate<Self> {
             DetailPredicate::<Self> {
                 p: self,
@@ -55,7 +72,7 @@ pub mod file {
         }
     }
 
-    /// Extends a PdfPredicate by a check for page count or creation date.
+    /// Extends a PdfPredicate by a check for page count, page size or creation date.
     #[derive(Debug)]
     pub struct DetailPredicate<PdfPredicate> {
         p: PdfPredicate,
@@ -65,19 +82,65 @@ pub mod file {
     #[derive(Debug)]
     enum Detail {
         PageCount(usize),
+        PageSize(Dimensions),
         CreationDate(DateTime<Utc>),
     }
 
+    #[derive(Debug)]
+    struct Dimensions {
+        w: i64,
+        h: i64,    //
+        unit: f64, // UserUnit, in 1/72 of an inch
+    }
+
+    impl Dimensions {
+        pub fn from_media_box(obj: &lopdf::Object, unit: Option<f64>) -> lopdf::Result<Dimensions> {
+            let a = obj.as_array()?;
+            Ok(Dimensions {
+                w: a[2].as_i64()?,
+                h: a[3].as_i64()?,
+                unit: unit.unwrap_or(1.0),
+            })
+        }
+
+        pub fn width_in_mm(self: &Self) -> f64 {
+            self.w as f64 * self.unit * 72.0 / 254.0
+        }
+
+        pub fn height_in_mm(self: &Self) -> f64 {
+            self.h as f64 * self.unit * 72.0 / 254.0
+        }
+    }
+
+    impl fmt::Display for Dimensions {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            write!(f, "{} mm x {} mm", self.width_in_mm(), self.height_in_mm())
+        }
+    }
+
+    impl cmp::PartialEq for Dimensions {
+        fn eq(&self, other: &Self) -> bool {
+            approx_eq!(f64, self.width_in_mm(), other.width_in_mm())
+                && approx_eq!(f64, self.height_in_mm(), other.height_in_mm())
+        }
+    }
+
+    impl cmp::Eq for Dimensions {}
+
     trait Details {
-        fn get_num_pages(&self) -> usize;
+        fn get_page_count(&self) -> usize;
+        fn get_page_size(&self) -> Option<Dimensions>;
         fn get_creation_date(&self) -> Option<DateTime<Utc>>;
+        fn get_from_trailer<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object>;
+        fn get_from_first_page<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object>;
     }
 
     impl DetailPredicate<PdfPredicate> {
         fn eval_doc(&self, doc: &lopdf::Document) -> bool {
-            match self.d {
-                Detail::PageCount(n) => n == doc.get_num_pages(),
-                Detail::CreationDate(d) => doc.get_creation_date().map_or(false, |date| date == d),
+            match &self.d {
+                Detail::PageCount(n) => doc.get_page_count() == *n,
+                Detail::PageSize(d) => doc.get_page_size().map_or(false, |dim| dim == *d),
+                Detail::CreationDate(d) => doc.get_creation_date().map_or(false, |date| date == *d),
             }
         }
 
@@ -95,10 +158,17 @@ pub mod file {
         }
 
         fn product_for_doc(&self, doc: &lopdf::Document) -> Product {
-            match self.d {
+            match &self.d {
                 Detail::PageCount(_) => Product::new(
                     "actual page count",
-                    format!("{} page(s)", doc.get_num_pages()),
+                    format!("{} page(s)", doc.get_page_count()),
+                ),
+                Detail::PageSize(_) => Product::new(
+                    "actual page size",
+                    match doc.get_page_size() {
+                        Some(dim) => format!("{}", dim),
+                        None => "None".to_string(),
+                    },
                 ),
                 Detail::CreationDate(_) => Product::new(
                     "actual creation date",
@@ -109,23 +179,38 @@ pub mod file {
     }
 
     impl Details for lopdf::Document {
-        fn get_creation_date(self: &Self) -> Option<DateTime<Utc>> {
-            fn get_from_trailer<'a>(
-                doc: &'a lopdf::Document,
-                key: &[u8],
-            ) -> lopdf::Result<&'a lopdf::Object> {
-                let id = doc.trailer.get(b"Info")?.as_reference()?;
-                doc.get_object(id)?.as_dict()?.get(key)
+        fn get_page_count(self: &Self) -> usize {
+            self.get_pages().len()
+        }
+
+        fn get_page_size(self: &Self) -> Option<Dimensions> {
+            let to_f64 = |obj: &lopdf::Object| obj.as_f64();
+            match self.get_from_first_page(b"MediaBox") {
+                Ok(obj) => {
+                    let unit = self.get_from_first_page(b"UserUnit").and_then(to_f64).ok();
+                    Dimensions::from_media_box(obj, unit).ok()
+                }
+                Err(_) => None,
             }
+        }
 
-            match get_from_trailer(self, b"CreationDate") {
+        fn get_creation_date(self: &Self) -> Option<DateTime<Utc>> {
+            match self.get_from_trailer(b"CreationDate") {
                 Ok(obj) => obj.as_datetime().map(|date| date.with_timezone(&Utc)),
                 Err(_) => None,
             }
         }
 
-        fn get_num_pages(self: &Self) -> usize {
-            self.get_pages().len()
+        fn get_from_trailer<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object> {
+            let id = self.trailer.get(b"Info")?.as_reference()?;
+            self.get_object(id)?.as_dict()?.get(key)
+        }
+
+        fn get_from_first_page<'a>(self: &'a Self, key: &[u8]) -> lopdf::Result<&'a lopdf::Object> {
+            match self.page_iter().next() {
+                Some(id) => self.get_object(id)?.as_dict()?.get(key),
+                None => Err(lopdf::Error::ObjectNotFound),
+            }
         }
     }
 
@@ -154,8 +239,9 @@ pub mod file {
 
     impl fmt::Display for DetailPredicate<PdfPredicate> {
         fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-            match self.d {
+            match &self.d {
                 Detail::PageCount(n) => write!(f, "is a PDF with {} page(s)", n),
+                Detail::PageSize(d) => write!(f, "is a PDF sized {}", d),
                 Detail::CreationDate(d) => write!(f, "is a PDF created {:?}", d),
             }
         }
diff --git a/tests/src/cmdline/rsvg_convert.rs b/tests/src/cmdline/rsvg_convert.rs
index eac0d653..f906be81 100644
--- a/tests/src/cmdline/rsvg_convert.rs
+++ b/tests/src/cmdline/rsvg_convert.rs
@@ -131,7 +131,7 @@ fn output_format_pdf() {
         .arg("--format=pdf")
         .assert()
         .success()
-        .stdout(file::is_pdf().with_page_count(1));
+        .stdout(file::is_pdf());
 }
 
 #[test]
@@ -523,6 +523,18 @@ fn defaults_are_used_for_negative_resolutions() {
         .stdout(file::is_png().with_size(90, 360));
 }
 
+#[test]
+fn pdf_page_size() {
+    let input = Path::new("fixtures/dimensions/521-with-viewbox.svg");
+    RsvgConvert::new_with_input(input)
+        .arg("--format=pdf")
+        .assert()
+        .success()
+        // TODO: the PDF size and resolution is actually a bug in rsvg-convert,
+        // see https://gitlab.gnome.org/GNOME/librsvg/issues/514
+        .stdout(file::is_pdf().with_page_size(200, 100, 72.0));
+}
+
 #[test]
 fn background_color_option_with_valid_color() {
     let input = Path::new("fixtures/api/dpi.svg");
diff --git a/tests/src/main.rs b/tests/src/main.rs
index 48bee891..931ec4af 100644
--- a/tests/src/main.rs
+++ b/tests/src/main.rs
@@ -1,3 +1,6 @@
+#[macro_use]
+extern crate float_cmp;
+
 #[cfg(test)]
 mod cmdline;
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]