Skip to content

Commit 2949036

Browse files
committed
Store chunk parse errors as warnings
1 parent e9883a4 commit 2949036

File tree

1 file changed

+77
-63
lines changed

1 file changed

+77
-63
lines changed

src/decoder/stream.rs

Lines changed: 77 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,10 @@ pub struct StreamingDecoder {
524524
inflater: ZlibStream,
525525
/// The complete image info read from all prior chunks.
526526
pub(crate) info: Option<Info<'static>>,
527+
528+
/// Non-fatal errors during decoding
529+
warnings: Vec<(ChunkType, DecodingError)>,
530+
527531
/// The animation chunk sequence number.
528532
current_seq_no: Option<u32>,
529533
/// Whether we have already seen a start of an IDAT chunk. (Used to validate chunk ordering -
@@ -574,6 +578,7 @@ impl StreamingDecoder {
574578
current_chunk: ChunkState::default(),
575579
inflater,
576580
info: None,
581+
warnings: Vec::new(),
577582
current_seq_no: None,
578583
have_idat: false,
579584
have_iccp: false,
@@ -592,6 +597,7 @@ impl StreamingDecoder {
592597
self.current_chunk.raw_bytes.clear();
593598
self.inflater.reset();
594599
self.info = None;
600+
self.warnings.clear();
595601
self.current_seq_no = None;
596602
self.have_idat = false;
597603
}
@@ -641,6 +647,13 @@ impl StreamingDecoder {
641647
.set_skip_ancillary_crc_failures(skip_ancillary_crc_failures)
642648
}
643649

650+
/// Non-fatal errors encoutered when parsing ancillary chunks
651+
///
652+
/// Please also check getters on [`Info`], since some chunks may be parsed lazily
653+
pub fn warnings(&self) -> &[(ChunkType, DecodingError)] {
654+
&self.warnings
655+
}
656+
644657
/// Low level StreamingDecoder interface.
645658
///
646659
/// Allows to stream partial data to the encoder. Returns a tuple containing the bytes that have
@@ -984,32 +997,42 @@ impl StreamingDecoder {
984997
chunk::fcTL => self.parse_fctl(),
985998
chunk::cHRM => self.parse_chrm(),
986999
chunk::sRGB => self.parse_srgb(),
987-
chunk::cICP => Ok(self.parse_cicp()),
988-
chunk::mDCV => Ok(self.parse_mdcv()),
989-
chunk::cLLI => Ok(self.parse_clli()),
990-
chunk::bKGD => Ok(self.parse_bkgd()),
1000+
chunk::cICP => self.parse_cicp(),
1001+
chunk::mDCV => self.parse_mdcv(),
1002+
chunk::cLLI => self.parse_clli(),
1003+
chunk::bKGD => self.parse_bkgd(),
9911004
chunk::iCCP if !self.decode_options.ignore_iccp_chunk => self.parse_iccp(),
9921005
chunk::tEXt if !self.decode_options.ignore_text_chunk => self.parse_text(),
9931006
chunk::zTXt if !self.decode_options.ignore_text_chunk => self.parse_ztxt(),
9941007
chunk::iTXt if !self.decode_options.ignore_text_chunk => self.parse_itxt(),
995-
_ => Ok(Decoded::PartialChunk(type_str)),
1008+
_ => return Ok(Decoded::PartialChunk(type_str)),
9961009
};
9971010

998-
parse_result.map_err(|e| {
999-
self.state = None;
1000-
match e {
1011+
match parse_result {
1012+
Ok(ok) => Ok(ok),
1013+
// ignore ancillary chunk failures (apng extensions are de-facto critical)
1014+
Err(err)
1015+
if !chunk::is_critical(type_str)
1016+
&& type_str != chunk::acTL
1017+
&& type_str != chunk::fcTL =>
1018+
{
1019+
self.warnings.push((type_str, err));
1020+
Ok(Decoded::Nothing)
1021+
}
1022+
Err(DecodingError::IoError(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
10011023
// `parse_chunk` is invoked after gathering **all** bytes of a chunk, so
10021024
// `UnexpectedEof` from something like `read_be` is permanent and indicates an
10031025
// invalid PNG that should be represented as a `FormatError`, rather than as a
10041026
// (potentially recoverable) `IoError` / `UnexpectedEof`.
1005-
DecodingError::IoError(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
1006-
let fmt_err: FormatError =
1007-
FormatErrorInner::ChunkTooShort { kind: type_str }.into();
1008-
fmt_err.into()
1009-
}
1010-
e => e,
1027+
self.state = None;
1028+
let fmt_err = FormatError::from(FormatErrorInner::ChunkTooShort { kind: type_str });
1029+
Err(fmt_err.into())
10111030
}
1012-
})
1031+
Err(err) => {
1032+
self.state = None;
1033+
Err(err)
1034+
}
1035+
}
10131036
}
10141037

10151038
fn parse_fctl(&mut self) -> Result<Decoded, DecodingError> {
@@ -1108,32 +1131,27 @@ impl StreamingDecoder {
11081131
}
11091132

11101133
fn parse_sbit(&mut self) -> Result<Decoded, DecodingError> {
1111-
let mut parse = || {
1112-
let info = self.info.as_mut().unwrap();
1113-
if info.palette.is_some() {
1114-
return Err(DecodingError::Format(
1115-
FormatErrorInner::AfterPlte { kind: chunk::sBIT }.into(),
1116-
));
1117-
}
1118-
1119-
if self.have_idat {
1120-
return Err(DecodingError::Format(
1121-
FormatErrorInner::AfterIdat { kind: chunk::sBIT }.into(),
1122-
));
1123-
}
1134+
let info = self.info.as_mut().unwrap();
1135+
if info.palette.is_some() {
1136+
return Err(DecodingError::Format(
1137+
FormatErrorInner::AfterPlte { kind: chunk::sBIT }.into(),
1138+
));
1139+
}
11241140

1125-
if info.sbit.is_some() {
1126-
return Err(DecodingError::Format(
1127-
FormatErrorInner::DuplicateChunk { kind: chunk::sBIT }.into(),
1128-
));
1129-
}
1141+
if self.have_idat {
1142+
return Err(DecodingError::Format(
1143+
FormatErrorInner::AfterIdat { kind: chunk::sBIT }.into(),
1144+
));
1145+
}
11301146

1131-
let vec = mem::take(&mut self.current_chunk.raw_bytes);
1132-
info.sbit = Some(Cow::Owned(vec));
1133-
Ok(Decoded::Nothing)
1134-
};
1147+
if info.sbit.is_some() {
1148+
return Err(DecodingError::Format(
1149+
FormatErrorInner::DuplicateChunk { kind: chunk::sBIT }.into(),
1150+
));
1151+
}
11351152

1136-
parse().ok();
1153+
let vec = mem::take(&mut self.current_chunk.raw_bytes);
1154+
info.sbit = Some(Cow::Owned(vec));
11371155
Ok(Decoded::Nothing)
11381156
}
11391157

@@ -1315,10 +1333,7 @@ impl StreamingDecoder {
13151333
}
13161334
}
13171335

1318-
// NOTE: This function cannot return `DecodingError` and handles parsing
1319-
// errors or spec violations as-if the chunk was missing. See
1320-
// https://github.com/image-rs/image-png/issues/525 for more discussion.
1321-
fn parse_cicp(&mut self) -> Decoded {
1336+
fn parse_cicp(&mut self) -> Result<Decoded, DecodingError> {
13221337
fn parse(mut buf: &[u8]) -> Result<CodingIndependentCodePoints, std::io::Error> {
13231338
let color_primaries: u8 = buf.read_be()?;
13241339
let transfer_function: u8 = buf.read_be()?;
@@ -1357,16 +1372,13 @@ impl StreamingDecoder {
13571372
let info = self.info.as_mut().unwrap();
13581373
let is_before_plte_and_idat = !self.have_idat && info.palette.is_none();
13591374
if is_before_plte_and_idat && info.coding_independent_code_points.is_none() {
1360-
info.coding_independent_code_points = parse(&self.current_chunk.raw_bytes[..]).ok();
1375+
info.coding_independent_code_points = Some(parse(&self.current_chunk.raw_bytes[..])?);
13611376
}
13621377

1363-
Decoded::Nothing
1378+
Ok(Decoded::Nothing)
13641379
}
13651380

1366-
// NOTE: This function cannot return `DecodingError` and handles parsing
1367-
// errors or spec violations as-if the chunk was missing. See
1368-
// https://github.com/image-rs/image-png/issues/525 for more discussion.
1369-
fn parse_mdcv(&mut self) -> Decoded {
1381+
fn parse_mdcv(&mut self) -> Result<Decoded, DecodingError> {
13701382
fn parse(mut buf: &[u8]) -> Result<MasteringDisplayColorVolume, std::io::Error> {
13711383
let red_x: u16 = buf.read_be()?;
13721384
let red_y: u16 = buf.read_be()?;
@@ -1408,16 +1420,13 @@ impl StreamingDecoder {
14081420
let info = self.info.as_mut().unwrap();
14091421
let is_before_plte_and_idat = !self.have_idat && info.palette.is_none();
14101422
if is_before_plte_and_idat && info.mastering_display_color_volume.is_none() {
1411-
info.mastering_display_color_volume = parse(&self.current_chunk.raw_bytes[..]).ok();
1423+
info.mastering_display_color_volume = Some(parse(&self.current_chunk.raw_bytes[..])?);
14121424
}
14131425

1414-
Decoded::Nothing
1426+
Ok(Decoded::Nothing)
14151427
}
14161428

1417-
// NOTE: This function cannot return `DecodingError` and handles parsing
1418-
// errors or spec violations as-if the chunk was missing. See
1419-
// https://github.com/image-rs/image-png/issues/525 for more discussion.
1420-
fn parse_clli(&mut self) -> Decoded {
1429+
fn parse_clli(&mut self) -> Result<Decoded, DecodingError> {
14211430
fn parse(mut buf: &[u8]) -> Result<ContentLightLevelInfo, std::io::Error> {
14221431
let max_content_light_level: u32 = buf.read_be()?;
14231432
let max_frame_average_light_level: u32 = buf.read_be()?;
@@ -1433,10 +1442,10 @@ impl StreamingDecoder {
14331442
// We ignore a second, duplicated cLLI chunk (if any).
14341443
let info = self.info.as_mut().unwrap();
14351444
if info.content_light_level.is_none() {
1436-
info.content_light_level = parse(&self.current_chunk.raw_bytes[..]).ok();
1445+
info.content_light_level = Some(parse(&self.current_chunk.raw_bytes[..])?);
14371446
}
14381447

1439-
Decoded::Nothing
1448+
Ok(Decoded::Nothing)
14401449
}
14411450

14421451
fn parse_iccp(&mut self) -> Result<Decoded, DecodingError> {
@@ -1459,7 +1468,7 @@ impl StreamingDecoder {
14591468
Ok(Decoded::Nothing)
14601469
} else {
14611470
self.have_iccp = true;
1462-
let _ = self.parse_iccp_raw();
1471+
self.parse_iccp_raw()?;
14631472
Ok(Decoded::Nothing)
14641473
}
14651474
}
@@ -1699,16 +1708,16 @@ impl StreamingDecoder {
16991708
Ok(Decoded::Nothing)
17001709
}
17011710

1702-
// NOTE: This function cannot return `DecodingError` and handles parsing
1703-
// errors or spec violations as-if the chunk was missing. See
1704-
// https://github.com/image-rs/image-png/issues/525 for more discussion.
1705-
fn parse_bkgd(&mut self) -> Decoded {
1711+
fn parse_bkgd(&mut self) -> Result<Decoded, DecodingError> {
17061712
let info = self.info.as_mut().unwrap();
17071713
if info.bkgd.is_none() && !self.have_idat {
17081714
let expected = match info.color_type {
17091715
ColorType::Indexed => {
17101716
if info.palette.is_none() {
1711-
return Decoded::Nothing;
1717+
return Err(FormatError::from(FormatErrorInner::InvalidColorType(
1718+
ColorType::Indexed as _,
1719+
))
1720+
.into());
17121721
};
17131722
1
17141723
}
@@ -1719,10 +1728,15 @@ impl StreamingDecoder {
17191728
let len = vec.len();
17201729
if len == expected {
17211730
info.bkgd = Some(Cow::Owned(vec));
1731+
} else {
1732+
return Err(FormatError::from(FormatErrorInner::ChunkTooShort {
1733+
kind: chunk::bKGD,
1734+
})
1735+
.into());
17221736
}
17231737
}
17241738

1725-
Decoded::Nothing
1739+
Ok(Decoded::Nothing)
17261740
}
17271741
}
17281742

0 commit comments

Comments
 (0)