fix(content): count an unreadable inline image with no EI as skipped (#1318) (#1418)

Follow-up to #1404. An inline image whose parameters can't be read and that has no EI after them ran to the end of the stream as a BI op with no data, and those bytes were not counted. It is now skipped like one with an EI, its bytes counted in Parsed::skipped. A stream that ends inside the parameters (BI /W 1) still keeps its BI op.
This commit is contained in:
Marco Arnulfo authored and GitHub committed 2026-10-12 08:38:01 -04:00
1 parent 291d738160
commit d7590ebfbe
2 files changed
+23 -6

No files matched your search

+7 -6
View File
@@ -149,7 +149,7 @@ pub fn parse(data: &[u8]) -> Parsed {
}
/// Read an inline image starting after `BI`: (parameters, data, position after `EI`). `Err` holds
/// the position after the `EI` of an image whose parameters can't be read.
/// the position after the `EI` (or the end of the stream) of an image whose parameters can't be read.
fn inline_image(data: &[u8], pos: usize) -> Result<(Dict, Vec<u8>, usize), usize> {
let mut lx = Lexer::new(data, pos);
let mut dict = Dict::new();
@@ -164,11 +164,12 @@ fn inline_image(data: &[u8], pos: usize) -> Result<(Dict, Vec<u8>, usize), usize
_ => None,
};
let Some((k, v)) = entry else {
// Without an `EI` after it, the image runs to the end of the stream, as before.
return match end_of_image(data, at) {
Some((_, end)) => Err(end),
None => Ok((dict, Vec::new(), data.len())),
};
// A stream that ends inside the parameters is cut short, not unreadable: keep what was read.
if at >= data.len() {
return Ok((dict, Vec::new(), data.len()));
}
// Without an `EI` after it, the image runs to the end of the stream.
return Err(end_of_image(data, at).map_or(data.len(), |(_, end)| end));
};
dict.set(k, v);
}
+16
View File
@@ -62,6 +62,22 @@ fn an_unreadable_inline_image_is_skipped_to_its_end() {
assert_eq!(control.ops.len(), 6);
}
/// Issue #1318, follow-up: without an `EI`, an unreadable inline image runs to the end of the
/// stream, and those bytes are counted. A readable one whose data is cut short is kept, data and
/// all, so nothing of it is skipped.
#[test]
fn an_unreadable_inline_image_without_ei_is_counted() {
let p = parse(b"q BI /W 4 H 4 ID aa");
let names: Vec<&[u8]> = p.ops.iter().map(|o| o.op.as_slice()).collect();
assert_eq!(names, [&b"q"[..]]);
assert_eq!(p.skipped, b"BI /W 4 H 4 ID aa".len());
let control = parse(b"q BI /W 4 /H 4 ID aa");
assert_eq!(control.skipped, 0);
let names: Vec<&[u8]> = control.ops.iter().map(|o| o.op.as_slice()).collect();
assert_eq!(names, [&b"q"[..], b"BI"]);
assert_eq!(control.ops[1].inline.as_ref().map(|(_, data)| data.as_slice()), Some(&b"aa"[..]));
}
#[test]
fn matrices_compose_and_invert() {
let a = Matrix([2.0, 0.0, 0.0, 3.0, 10.0, 20.0]);