From a4a595378626eb21cbd8b56346a41af17fb93c2c Mon Sep 17 00:00:00 2001 From: Joshua Holmer Date: Mon, 2 May 2016 09:25:48 -0400 Subject: [PATCH] Implement --fix to fix CRC errors in PNG block headers Closes #5 --- CHANGELOG.md | 1 + src/lib.rs | 2 +- src/png.rs | 15 ++++---- tests/files/fix_errors.png | Bin 0 -> 44345 bytes tests/filters.rs | 4 +- tests/flags.rs | 73 +++++++++++++++++++++++++++---------- tests/interlaced.rs | 4 +- tests/reduction.rs | 16 ++++---- 8 files changed, 75 insertions(+), 40 deletions(-) create mode 100644 tests/files/fix_errors.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7c9fa9..565e4d73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - Fix issue where output directory would not be created if it did not exist - Use miniz for compression strategies where it outperforms zlib - [SEMVER_MINOR] Partially implement -p / --preserve, as far as stable Rust will allow for now + - [SEMVER_MINOR] Implement --fix to ignore CRC errors and recalculate correct CRC in output **Version 0.5.0** - [SEMVER_MINOR] Palette entries can now reduced, on by default ([#11](https://github.com/shssoichiro/oxipng/issues/11)) diff --git a/src/lib.rs b/src/lib.rs index 5df3467a..3d06793b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -90,7 +90,7 @@ pub fn optimize(filepath: &Path, opts: &Options) -> Result<(), String> { writeln!(&mut stderr(), "Processing: {}", filepath.to_str().unwrap()).ok(); } let in_file = Path::new(filepath); - let mut png = match png::PngData::new(&in_file) { + let mut png = match png::PngData::new(&in_file, opts.fix_errors) { Ok(x) => x, Err(x) => return Err(x), }; diff --git a/src/png.rs b/src/png.rs index 5ae5af0d..a54517d6 100644 --- a/src/png.rs +++ b/src/png.rs @@ -4,8 +4,7 @@ use crc::crc32; use std::collections::{HashMap, HashSet}; use std::fmt; use std::fs::File; -use std::io::Cursor; -use std::io::prelude::*; +use std::io::{Cursor, Read, Write}; use std::iter::Iterator; use std::path::Path; @@ -292,7 +291,7 @@ pub struct IhdrData { impl PngData { /// Create a new `PngData` struct by opening a file - pub fn new(filepath: &Path) -> Result { + pub fn new(filepath: &Path, fix_errors: bool) -> Result { let mut file = match File::open(filepath) { Ok(f) => f, Err(_) => return Err("Failed to open file for reading".to_owned()), @@ -314,7 +313,7 @@ impl PngData { let mut aux_headers: HashMap> = HashMap::new(); let mut idat_headers: Vec = Vec::new(); loop { - let header = parse_next_header(byte_data.as_ref(), &mut byte_offset); + let header = parse_next_header(byte_data.as_ref(), &mut byte_offset, fix_errors); let header = match header { Ok(x) => x, Err(x) => return Err(x), @@ -1574,7 +1573,8 @@ fn file_header_is_valid(bytes: &[u8]) -> bool { } fn parse_next_header(byte_data: &[u8], - byte_offset: &mut usize) + byte_offset: &mut usize, + fix_errors: bool) -> Result)>, String> { let mut rdr = Cursor::new(byte_data.iter() .skip(*byte_offset) @@ -1615,8 +1615,9 @@ fn parse_next_header(byte_data: &[u8], }; *byte_offset += 4; header_bytes.extend(data.clone()); - if crc32::checksum_ieee(header_bytes.as_ref()) != crc { - return Err(format!("Corrupt data chunk found--CRC Mismatch in {}", header)); + if !fix_errors && crc32::checksum_ieee(header_bytes.as_ref()) != crc { + return Err(format!("Corrupt data chunk found--CRC Mismatch in {}\nThis may be recoverable by using --fix", + header)); } Ok(Some((header, data))) diff --git a/tests/files/fix_errors.png b/tests/files/fix_errors.png new file mode 100644 index 0000000000000000000000000000000000000000..93e822fded396d1873d930eeea6169aab78d295f GIT binary patch literal 44345 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 wq6 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 x, Err(x) => { remove_file(output).ok(); diff --git a/tests/flags.rs b/tests/flags.rs index 82ed0ce7..884a3d81 100644 --- a/tests/flags.rs +++ b/tests/flags.rs @@ -56,7 +56,7 @@ fn test_it_converts(input: &Path, bit_depth_in: png::BitDepth, color_type_out: png::ColorType, bit_depth_out: png::BitDepth) { - let png = png::PngData::new(input).unwrap(); + let png = png::PngData::new(input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.color_type == color_type_in); assert!(png.ihdr_data.bit_depth == bit_depth_in); @@ -67,7 +67,7 @@ fn test_it_converts(input: &Path, }; assert!(output.exists()); - let png = match png::PngData::new(output) { + let png = match png::PngData::new(output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -111,7 +111,7 @@ fn strip_headers_list() { opts.strip = png::Headers::Some(vec!["iCCP".to_owned(), "tEXt".to_owned()]); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.aux_headers.contains_key("tEXt")); assert!(png.aux_headers.contains_key("iTXt")); @@ -123,7 +123,7 @@ fn strip_headers_list() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -152,7 +152,7 @@ fn strip_headers_safe() { opts.strip = png::Headers::Safe; let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.aux_headers.contains_key("tEXt")); assert!(png.aux_headers.contains_key("iTXt")); @@ -164,7 +164,7 @@ fn strip_headers_safe() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -193,7 +193,7 @@ fn strip_headers_all() { opts.strip = png::Headers::All; let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.aux_headers.contains_key("tEXt")); assert!(png.aux_headers.contains_key("iTXt")); @@ -205,7 +205,7 @@ fn strip_headers_all() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -234,7 +234,7 @@ fn strip_headers_none() { opts.strip = png::Headers::None; let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.aux_headers.contains_key("tEXt")); assert!(png.aux_headers.contains_key("iTXt")); @@ -246,7 +246,7 @@ fn strip_headers_none() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -275,7 +275,7 @@ fn interlacing_0_to_1() { opts.interlace = Some(1); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.interlaced == 0); @@ -285,7 +285,7 @@ fn interlacing_0_to_1() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -312,7 +312,7 @@ fn interlacing_1_to_0() { opts.interlace = Some(0); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.interlaced == 1); @@ -322,7 +322,7 @@ fn interlacing_1_to_0() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -349,7 +349,7 @@ fn interlacing_0_to_1_small_files() { opts.interlace = Some(1); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.interlaced == 0); assert!(png.ihdr_data.color_type == png::ColorType::Indexed); @@ -361,7 +361,7 @@ fn interlacing_0_to_1_small_files() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -390,7 +390,7 @@ fn interlacing_1_to_0_small_files() { opts.interlace = Some(0); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.interlaced == 1); assert!(png.ihdr_data.color_type == png::ColorType::Indexed); @@ -402,7 +402,7 @@ fn interlacing_1_to_0_small_files() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -434,7 +434,7 @@ fn interlaced_0_to_1_other_filter_mode() { opts.filter = filter; let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.interlaced == 0); @@ -444,7 +444,7 @@ fn interlaced_0_to_1_other_filter_mode() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -481,3 +481,36 @@ fn preserve_attrs() { // TODO: Actually check permissions } + +#[test] +fn fix_errors() { + let input = PathBuf::from("tests/files/fix_errors.png"); + let mut opts = get_opts(&input); + opts.fix_errors = true; + let output = opts.out_file.clone(); + + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); + + assert!(png.ihdr_data.color_type == png::ColorType::RGBA); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight); + + match oxipng::optimize(&input, &opts) { + Ok(_) => (), + Err(x) => panic!(x.to_owned()), + }; + assert!(output.exists()); + + let png = match png::PngData::new(&output, false) { + Ok(x) => x, + Err(x) => { + remove_file(output).ok(); + panic!(x.to_owned()) + } + }; + + assert!(png.ihdr_data.color_type == png::ColorType::Grayscale); + assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight); + + // Cannot check if pixels are equal because image crate cannot read corrupt (input) PNGs + remove_file(output).ok(); +} diff --git a/tests/interlaced.rs b/tests/interlaced.rs index 57df44e6..4803b58b 100644 --- a/tests/interlaced.rs +++ b/tests/interlaced.rs @@ -56,7 +56,7 @@ fn test_it_converts(input: &Path, bit_depth_in: png::BitDepth, color_type_out: png::ColorType, bit_depth_out: png::BitDepth) { - let png = png::PngData::new(input).unwrap(); + let png = png::PngData::new(input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.color_type == color_type_in); assert!(png.ihdr_data.bit_depth == bit_depth_in); @@ -68,7 +68,7 @@ fn test_it_converts(input: &Path, }; assert!(output.exists()); - let png = match png::PngData::new(output) { + let png = match png::PngData::new(output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); diff --git a/tests/reduction.rs b/tests/reduction.rs index 08d7e836..72338a79 100644 --- a/tests/reduction.rs +++ b/tests/reduction.rs @@ -56,7 +56,7 @@ fn test_it_converts(input: &Path, bit_depth_in: png::BitDepth, color_type_out: png::ColorType, bit_depth_out: png::BitDepth) { - let png = png::PngData::new(input).unwrap(); + let png = png::PngData::new(input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.color_type == color_type_in); assert!(png.ihdr_data.bit_depth == bit_depth_in); @@ -68,7 +68,7 @@ fn test_it_converts(input: &Path, }; assert!(output.exists()); - let png = match png::PngData::new(output) { + let png = match png::PngData::new(output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(output).ok(); @@ -1281,7 +1281,7 @@ fn palette_should_be_reduced_with_dupes() { let opts = get_opts(&input); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.color_type == png::ColorType::Indexed); assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight); @@ -1293,7 +1293,7 @@ fn palette_should_be_reduced_with_dupes() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(&output).ok(); @@ -1321,7 +1321,7 @@ fn palette_should_be_reduced_with_unused() { let opts = get_opts(&input); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.color_type == png::ColorType::Indexed); assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight); @@ -1333,7 +1333,7 @@ fn palette_should_be_reduced_with_unused() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(&output).ok(); @@ -1361,7 +1361,7 @@ fn palette_should_be_reduced_with_both() { let opts = get_opts(&input); let output = opts.out_file.clone(); - let png = png::PngData::new(&input).unwrap(); + let png = png::PngData::new(&input, opts.fix_errors).unwrap(); assert!(png.ihdr_data.color_type == png::ColorType::Indexed); assert!(png.ihdr_data.bit_depth == png::BitDepth::Eight); @@ -1373,7 +1373,7 @@ fn palette_should_be_reduced_with_both() { }; assert!(output.exists()); - let png = match png::PngData::new(&output) { + let png = match png::PngData::new(&output, opts.fix_errors) { Ok(x) => x, Err(x) => { remove_file(&output).ok();