mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 12:13:19 +00:00
Merge pull request #6003 from mockydevs/fix/eps-huge-image-skips-data
EPS import: an image too large to read skips its data instead of failing the file
This commit is contained in:
@@ -401,6 +401,30 @@ impl Interp<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Skip the data an image left out (too large to read) would read from the program's own
|
||||
/// file, so the program doesn't go on to read that data as PostScript. The filters that find
|
||||
/// their own end without decompressing (`ASCIIHexDecode`, `ASCII85Decode`, `SubFileDecode`)
|
||||
/// read the file up to it, the way the image reading all its data would leave it; the
|
||||
/// filters over them, which would only decode what those pass, aren't run. When the first
|
||||
/// filter decompresses, it is run alone, and a failure (data too large to hold) leaves the
|
||||
/// data where it was, as before.
|
||||
fn skip_data(&mut self, src: &Source) {
|
||||
let Source::File(f) = src else { return };
|
||||
let (chain, from) = match &*f.borrow() {
|
||||
Stream::Pending { chain, from } => (chain.clone(), from.clone()),
|
||||
_ => return,
|
||||
};
|
||||
let delimited = chain.iter().take_while(|c| matches!(&*c.name, "ASCIIHexDecode" | "ASCII85Decode" | "SubFileDecode")).count();
|
||||
let head = chain.get(..delimited.max(1)).unwrap_or_default().to_vec();
|
||||
*f.borrow_mut() = Stream::Pending { chain: head, from: from.clone() };
|
||||
// A failure leaves the file as it was; the import goes on without the image either way.
|
||||
if self.materialize(f).is_err() {
|
||||
*f.borrow_mut() = Stream::Pending { chain, from };
|
||||
return;
|
||||
}
|
||||
*f.borrow_mut() = Stream::Buf { data: vec![], pos: 0, rgb: None };
|
||||
}
|
||||
|
||||
/// The bytes a data procedure gives, called until it gives an empty string or `n` bytes.
|
||||
fn gather(&mut self, p: &Obj, n: usize) -> Res<Vec<u8>> {
|
||||
Ok(self.read_sources(&[Source::Proc(p.clone())], n, n)?.pop().unwrap_or_default())
|
||||
@@ -520,6 +544,9 @@ impl Interp<'_> {
|
||||
let n = image.space.n();
|
||||
if image.w.saturating_mul(image.h) > MAX_PIXELS || stencil.w.saturating_mul(stencil.h) > MAX_PIXELS {
|
||||
self.out.warn(TOO_LARGE);
|
||||
for src in image.sources.iter().chain(&stencil.sources) {
|
||||
self.skip_data(src);
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
if interleave != 3.0 {
|
||||
@@ -626,6 +653,9 @@ impl Interp<'_> {
|
||||
}
|
||||
if s.w.saturating_mul(s.h) > MAX_PIXELS {
|
||||
self.out.warn(TOO_LARGE);
|
||||
for src in &s.sources {
|
||||
self.skip_data(src);
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
self.alloc(s.w * s.h * 4)?;
|
||||
|
||||
@@ -516,3 +516,35 @@ fn tiff_predicted_rows_at_every_depth() {
|
||||
}
|
||||
assert!(wrong.is_empty(), "{wrong:#?}");
|
||||
}
|
||||
|
||||
/// An image too large to read is left out, and its data is skipped, as reading it would leave
|
||||
/// the program: the art after it is read, not the data as PostScript. Here the data ends at its
|
||||
/// `~>` (an inline image of a content stream run as a file, as PDF-reading prologs write it), at
|
||||
/// a `SubFileDecode` end string, and at the end of compressed data that is read whole.
|
||||
#[test]
|
||||
fn the_data_of_an_image_too_large_to_read_is_skipped() {
|
||||
let after = "0 0 1 setrgbcolor 10 10 80 80 rectfill";
|
||||
for program in [
|
||||
format!(
|
||||
"/ID {{ << /ImageType 1 /Width 10000 /Height 10000 /BitsPerComponent 8 /Decode [0 1 0 1 0 1] /ImageMatrix [1 0 0 1 0 0] \
|
||||
/DataSource currentfile 0 (~>) /SubFileDecode filter /ASCII85Decode filter /LZWDecode filter >> \
|
||||
/DeviceRGB setcolorspace image }} def\n\
|
||||
currentfile 0 (endstream) /SubFileDecode filter cvx exec\nID\nJ1_V7fG\"r'\"b]2#:hXAd)~>\n{after}\nendstream\n"
|
||||
),
|
||||
format!("10000 10000 8 [1 0 0 1 0 0] currentfile /ASCIIHexDecode filter false 3 colorimage\n0a1b 2c3d 4e5f>\n{after}"),
|
||||
format!(
|
||||
"10000 10000 8 [1 0 0 1 0 0] currentfile 0 (%%EndData) /SubFileDecode filter /FlateDecode filter image\n\
|
||||
x\u{9c}garbage\n%%EndData\n{after}"
|
||||
),
|
||||
format!(
|
||||
"10000 10000 true [1 0 0 1 0 0] currentfile /ASCII85Decode filter imagemask\n{}\n{after}",
|
||||
crate::ps::ascii85(&crate::ps::deflate(&[0u8; 64]))
|
||||
),
|
||||
] {
|
||||
let r = read(&program);
|
||||
assert!(r.warnings.iter().any(|w| w.contains("too large to read")), "{program}: {:?}", r.warnings);
|
||||
let o = super::tests::objects(&r.document);
|
||||
assert_eq!(o.len(), 1, "{program}: {:?}", r.warnings);
|
||||
assert_eq!(super::tests::fill(&o[0]), Some(&vectorcraft_color::Paint::solid(vectorcraft_color::Color::rgb(0.0, 0.0, 1.0))), "{program}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user