diff --git a/Cargo.lock b/Cargo.lock index e6e3e4f..760a095 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1173,6 +1173,7 @@ dependencies = [ "image", "indoc", "predicates", + "rayon", "rqrr", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index af37712..15a05b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ rqrr = "0.7.1" image = "0.25.1" tempfile = "3.10.1" serde = { version = "1.0.203", features = ["derive"] } +rayon = "1.10.0" [dev-dependencies] assert_cmd = "2.0.14" diff --git a/src/lib.rs b/src/lib.rs index 862c6a6..1d240c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,7 @@ mod qr_parser; use crate::models::qr_data::QRData; use image; +use rayon::prelude::*; use rqrr::PreparedImage; use tempfile::tempdir; @@ -19,21 +20,24 @@ pub fn get_qr_bill_data(file_path: String, fail_on_error: bool) -> Vec { input if input.ends_with(".pdf") => { pdf_converter::convert_to_png(&file_path, &tmp_dir.path()) } - input if input.ends_with(".png") => { + input if input.ends_with(".png") || input.ends_with(".jpg") || input.ends_with(".jpeg") => { vec![image::open(&file_path).expect("Error loading image")] } _ => panic!("Unsupported file format"), }; - let mut all_qr_codes = Vec::new(); - for img in images { - let mut img = PreparedImage::prepare(img.to_luma8()); - img.detect_grids() - .into_iter() - .filter_map(|result| result.decode().ok()) - .map(|(_, content)| qr_parser::get_qr_code_data(&content)) - .for_each(|qr_data| all_qr_codes.push(qr_data)); - } + let all_qr_codes: Vec<_> = images + .into_par_iter() + .map(|img| { + let mut img = PreparedImage::prepare(img.to_luma8()); + img.detect_grids() + .into_par_iter() + .filter_map(|result| result.decode().ok()) + .map(|(_, content)| qr_parser::get_qr_code_data(&content)) + .collect::>() + }) + .flatten() + .collect(); // check if there were any errors if fail_on_error && all_qr_codes.iter().any(|result| result.is_err()) { diff --git a/src/pdf_converter.rs b/src/pdf_converter.rs index f4b89ba..1111291 100644 --- a/src/pdf_converter.rs +++ b/src/pdf_converter.rs @@ -27,8 +27,8 @@ where "-dSAFER", "-dNOPAUSE", "-dFILTERTEXT", - "-r300", - "-sDEVICE=pngmono", + "-r400", + "-sDEVICE=pnggray", &format!( "-sOutputFile={}/%03d.png", tmp_path.as_ref().to_string_lossy() diff --git a/tests/data/double.jpeg b/tests/data/double.jpeg new file mode 100644 index 0000000..a2d3a64 Binary files /dev/null and b/tests/data/double.jpeg differ diff --git a/tests/data/double.jpg b/tests/data/double.jpg new file mode 100644 index 0000000..a2d3a64 Binary files /dev/null and b/tests/data/double.jpg differ diff --git a/tests/data/full.jpeg b/tests/data/full.jpeg new file mode 100644 index 0000000..8d1151b Binary files /dev/null and b/tests/data/full.jpeg differ diff --git a/tests/data/full.jpg b/tests/data/full.jpg new file mode 100644 index 0000000..8d1151b Binary files /dev/null and b/tests/data/full.jpg differ diff --git a/tests/data/minimal.jpeg b/tests/data/minimal.jpeg new file mode 100644 index 0000000..861433f Binary files /dev/null and b/tests/data/minimal.jpeg differ diff --git a/tests/data/minimal.jpg b/tests/data/minimal.jpg new file mode 100644 index 0000000..861433f Binary files /dev/null and b/tests/data/minimal.jpg differ diff --git a/tests/data/none.jpeg b/tests/data/none.jpeg new file mode 100644 index 0000000..3be3990 Binary files /dev/null and b/tests/data/none.jpeg differ diff --git a/tests/data/none.jpg b/tests/data/none.jpg new file mode 100644 index 0000000..3be3990 Binary files /dev/null and b/tests/data/none.jpg differ diff --git a/tests/data/rotated.jpeg b/tests/data/rotated.jpeg new file mode 100644 index 0000000..b5ca9a1 Binary files /dev/null and b/tests/data/rotated.jpeg differ diff --git a/tests/data/rotated.jpg b/tests/data/rotated.jpg new file mode 100644 index 0000000..b5ca9a1 Binary files /dev/null and b/tests/data/rotated.jpg differ diff --git a/tests/edge_cases.rs b/tests/edge_cases.rs index 76108a9..85924cb 100644 --- a/tests/edge_cases.rs +++ b/tests/edge_cases.rs @@ -6,25 +6,24 @@ use swiss_qr_bill_decoder::models::qr_data::QRData; fn minimal_png() { let actual = get_qr_bill_data("tests/data/minimal.png".to_string(), true); - let expected = vec![QRData::new( - "CH4289144165265158476".to_string(), - Address::new( - "S".to_string(), - "A".to_string(), - " ".to_string(), - "8000 Zürich".to_string(), - "CH".to_string(), - ), - None, - None, - "CHF".to_string(), - "NON".to_string(), - None, - None, - )]; + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_minimal()); +} - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); +#[test] +fn minimal_jpg() { + let actual = get_qr_bill_data("tests/data/minimal.jpg".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_minimal()); +} + +#[test] +fn minimal_jpeg() { + let actual = get_qr_bill_data("tests/data/minimal.jpeg".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_minimal()); } #[test] @@ -32,7 +31,151 @@ fn minimal_png() { fn minimal_pdf() { let actual = get_qr_bill_data("tests/data/minimal.pdf".to_string(), true); - let expected = vec![QRData::new( + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_minimal()); +} + +#[test] +fn full_png() { + let actual = get_qr_bill_data("tests/data/full.png".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_full()); +} + +#[test] +fn full_jpg() { + let actual = get_qr_bill_data("tests/data/full.jpg".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_full()); +} + +#[test] +fn full_jpeg() { + let actual = get_qr_bill_data("tests/data/full.jpeg".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_full()); +} + +#[test] +#[ignore] +fn full_pdf() { + let actual = get_qr_bill_data("tests/data/full.pdf".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_full()); +} + +#[test] +fn rotated_png() { + let actual = get_qr_bill_data("tests/data/rotated.png".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_rotated()); +} + +#[test] +fn rotated_jpg() { + let actual = get_qr_bill_data("tests/data/rotated.jpg".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_rotated()); +} + +#[test] +fn rotated_jpeg() { + let actual = get_qr_bill_data("tests/data/rotated.jpeg".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_rotated()); +} + +#[test] +#[ignore] +fn rotated_pdf() { + let actual = get_qr_bill_data("tests/data/rotated.pdf".to_string(), true); + + assert_eq!(actual.len(), 1); + assert_eq!(actual[0], expected_rotated()); +} + +#[test] +fn double_png() { + let actual = get_qr_bill_data("tests/data/double.png".to_string(), true); + + let expected = vec![expected_double_1(), expected_double_2()]; + + assert_eq!(actual.len(), expected.len()); + assert_eq!(actual[0], expected[0]); + assert_eq!(actual[1], expected[1]); +} + +#[test] +fn double_jpg() { + let actual = get_qr_bill_data("tests/data/double.jpg".to_string(), true); + + let expected = vec![expected_double_1(), expected_double_2()]; + + assert_eq!(actual.len(), expected.len()); + assert_eq!(actual[0], expected[0]); + assert_eq!(actual[1], expected[1]); +} + +#[test] +fn double_jpeg() { + let actual = get_qr_bill_data("tests/data/double.jpeg".to_string(), true); + + let expected = vec![expected_double_1(), expected_double_2()]; + + assert_eq!(actual.len(), expected.len()); + assert_eq!(actual[0], expected[0]); + assert_eq!(actual[1], expected[1]); +} + +#[test] +#[ignore] +fn double_pdf() { + let actual = get_qr_bill_data("tests/data/double.pdf".to_string(), true); + let expected = vec![expected_double_1(), expected_double_2()]; + + assert_eq!(actual.len(), expected.len()); + assert_eq!(actual[0], expected[0]); + assert_eq!(actual[1], expected[1]); +} + +#[test] +fn none_png() { + let actual = get_qr_bill_data("tests/data/none.png".to_string(), true); + + assert!(actual.is_empty()); +} + +#[test] +fn none_jpg() { + let actual = get_qr_bill_data("tests/data/none.jpg".to_string(), true); + + assert!(actual.is_empty()); +} + +#[test] +fn none_jpeg() { + let actual = get_qr_bill_data("tests/data/none.jpeg".to_string(), true); + + assert!(actual.is_empty()); +} + +#[test] +#[ignore] +fn none_pdf() { + let actual = get_qr_bill_data("tests/data/none.pdf".to_string(), true); + + assert!(actual.is_empty()); +} + +pub fn expected_minimal() -> QRData { + QRData::new( "CH4289144165265158476".to_string(), Address::new( "S".to_string(), @@ -47,17 +190,11 @@ fn minimal_pdf() { "NON".to_string(), None, None, - )]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); + ) } -#[test] -fn full_png() { - let actual = get_qr_bill_data("tests/data/full.png".to_string(), true); - - let expected = vec![QRData::new( +pub fn expected_full() -> QRData { + QRData::new( "CH3389144927977473182".to_string(), Address::new( "S".to_string(), @@ -78,18 +215,11 @@ fn full_png() { "SCOR".to_string(), Some("RF541234".to_string()), Some("This is a test Message".to_string()), - )]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); + ) } -#[test] -#[ignore] -fn full_pdf() { - let actual = get_qr_bill_data("tests/data/full.pdf".to_string(), true); - - let expected = vec![QRData::new( +pub fn expected_rotated() -> QRData { + QRData::new( "CH3389144927977473182".to_string(), Address::new( "S".to_string(), @@ -110,17 +240,11 @@ fn full_pdf() { "SCOR".to_string(), Some("RF541234".to_string()), Some("This is a test Message".to_string()), - )]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); + ) } -#[test] -fn rotated_png() { - let actual = get_qr_bill_data("tests/data/rotated.png".to_string(), true); - - let expected = vec![QRData::new( +pub fn expected_double_1() -> QRData { + QRData::new( "CH3389144927977473182".to_string(), Address::new( "S".to_string(), @@ -141,155 +265,24 @@ fn rotated_png() { "SCOR".to_string(), Some("RF541234".to_string()), Some("This is a test Message".to_string()), - )]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); + ) } -#[test] -#[ignore] -fn rotated_pdf() { - let actual = get_qr_bill_data("tests/data/rotated.pdf".to_string(), true); - - let expected = vec![QRData::new( - "CH3389144927977473182".to_string(), +pub fn expected_double_2() -> QRData { + QRData::new( + "CH4289144165265158476".to_string(), Address::new( "S".to_string(), - "Test Recipient AG".to_string(), - "Teststreet 42a".to_string(), - "9000 Zürich".to_string(), + "A".to_string(), + " ".to_string(), + "8000 Zürich".to_string(), "CH".to_string(), ), - Some(Address::new( - "S".to_string(), - "Sender AG".to_string(), - "Senderstreet 99C".to_string(), - "1234 Sendertown".to_string(), - "AT".to_string(), - )), - Some("1337.42".to_string()), - "EUR".to_string(), - "SCOR".to_string(), - Some("RF541234".to_string()), - Some("This is a test Message".to_string()), - )]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); -} - -#[test] -fn double_png() { - let actual = get_qr_bill_data("tests/data/double.png".to_string(), true); - - let expected = vec![ - QRData::new( - "CH3389144927977473182".to_string(), - Address::new( - "S".to_string(), - "Test Recipient AG".to_string(), - "Teststreet 42a".to_string(), - "9000 Zürich".to_string(), - "CH".to_string(), - ), - Some(Address::new( - "S".to_string(), - "Sender AG".to_string(), - "Senderstreet 99C".to_string(), - "1234 Sendertown".to_string(), - "AT".to_string(), - )), - Some("1337.42".to_string()), - "EUR".to_string(), - "SCOR".to_string(), - Some("RF541234".to_string()), - Some("This is a test Message".to_string()), - ), - QRData::new( - "CH4289144165265158476".to_string(), - Address::new( - "S".to_string(), - "A".to_string(), - " ".to_string(), - "8000 Zürich".to_string(), - "CH".to_string(), - ), - None, - None, - "CHF".to_string(), - "NON".to_string(), - None, - None, - ), - ]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); -} - - -#[test] -#[ignore] -fn double_pdf() { - let actual = get_qr_bill_data("tests/data/double.pdf".to_string(), true); - - let expected = vec![ - QRData::new( - "CH3389144927977473182".to_string(), - Address::new( - "S".to_string(), - "Test Recipient AG".to_string(), - "Teststreet 42a".to_string(), - "9000 Zürich".to_string(), - "CH".to_string(), - ), - Some(Address::new( - "S".to_string(), - "Sender AG".to_string(), - "Senderstreet 99C".to_string(), - "1234 Sendertown".to_string(), - "AT".to_string(), - )), - Some("1337.42".to_string()), - "EUR".to_string(), - "SCOR".to_string(), - Some("RF541234".to_string()), - Some("This is a test Message".to_string()), - ), - QRData::new( - "CH4289144165265158476".to_string(), - Address::new( - "S".to_string(), - "A".to_string(), - " ".to_string(), - "8000 Zürich".to_string(), - "CH".to_string(), - ), - None, - None, - "CHF".to_string(), - "NON".to_string(), - None, - None, - ), - ]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); -} - -#[test] -fn none_png() { - let actual = get_qr_bill_data("tests/data/none.png".to_string(), true); - - assert!(actual.is_empty()); + None, + None, + "CHF".to_string(), + "NON".to_string(), + None, + None, + ) } - -#[test] -#[ignore] -fn none_pdf() { - let actual = get_qr_bill_data("tests/data/none.pdf".to_string(), true); - - assert!(actual.is_empty()); -} \ No newline at end of file diff --git a/tests/six_examples.rs b/tests/six_examples.rs index 60be5df..da6d007 100644 --- a/tests/six_examples.rs +++ b/tests/six_examples.rs @@ -4,33 +4,33 @@ use swiss_qr_bill_decoder::models::qr_data::QRData; #[test] fn six_example_01() { - let actual = get_qr_bill_data("tests/data/six_examples/01.png".to_string(), true); - - let expected = vec![QRData::new( - "CH6431961000004421557".to_string(), - Address::new( - "S".to_string(), - "Max Muster & Söhne".to_string(), - "Musterstrasse 123".to_string(), - "8000 Seldwyla".to_string(), - "CH".to_string(), - ), - Some(Address::new( - "S".to_string(), - "Simon Muster".to_string(), - "Musterstrasse 1".to_string(), - "8000 Seldwyla".to_string(), - "CH".to_string(), - )), - Some("50.00".to_string()), - "CHF".to_string(), - "QRR".to_string(), - Some("000008207791225857421286694".to_string()), - Some("Payment of travel".to_string()), - )]; - - assert_eq!(actual.len(), expected.len()); - assert_eq!(actual[0], expected[0]); + let actual = get_qr_bill_data("tests/data/six_examples/01.png".to_string(), true); + + let expected = vec![QRData::new( + "CH6431961000004421557".to_string(), + Address::new( + "S".to_string(), + "Max Muster & Söhne".to_string(), + "Musterstrasse 123".to_string(), + "8000 Seldwyla".to_string(), + "CH".to_string(), + ), + Some(Address::new( + "S".to_string(), + "Simon Muster".to_string(), + "Musterstrasse 1".to_string(), + "8000 Seldwyla".to_string(), + "CH".to_string(), + )), + Some("50.00".to_string()), + "CHF".to_string(), + "QRR".to_string(), + Some("000008207791225857421286694".to_string()), + Some("Payment of travel".to_string()), + )]; + + assert_eq!(actual.len(), expected.len()); + assert_eq!(actual[0], expected[0]); } #[test] @@ -123,7 +123,7 @@ fn six_example_04() { #[test] fn six_example_05() { let actual = get_qr_bill_data("tests/data/six_examples/05.png".to_string(), true); - + let expected = vec![QRData::new( "CH5800791123000889012".to_string(), Address::new( @@ -180,4 +180,4 @@ fn six_example_06() { assert_eq!(actual.len(), expected.len()); assert_eq!(actual[0], expected[0]); -} \ No newline at end of file +}