From 400a49666a3b806ff3ecedcfee3dedfb07149649 Mon Sep 17 00:00:00 2001 From: JonRC Date: Tue, 3 Nov 2026 03:43:13 -0300 Subject: [PATCH] Pending filters read the file they were made over A filter over `currentfile` waits until it is first read, and it took its data from whichever file was running by then. One made over the program's own file and first read inside a file run with `cvx exec` read that file instead, and one made over a file being run, first read in a file that one runs, read the inner file. The pending stream now records the file it was made over (a chained filter inherits it), and `materialize` decodes from that file. --- crates/eps/src/import/data/filters.rs | 12 +++++--- crates/eps/src/import/data/ops.rs | 40 +++++++++++++-------------- crates/eps/src/import/tests_images.rs | 25 +++++++++++++++++ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/crates/eps/src/import/data/filters.rs b/crates/eps/src/import/data/filters.rs index d049e39a9..e3268b240 100644 --- a/crates/eps/src/import/data/filters.rs +++ b/crates/eps/src/import/data/filters.rs @@ -1,15 +1,19 @@ use super::ops::MAX_DATA; use super::{ - DecodeSettings, Decoder, DecoderContext, DictRef, EncodingMode, ImageBlob, Key, Obj, PsError, Rc, Res, find, hex_decode, jpeg_components, ps, - ps_err, + DecodeSettings, Decoder, DecoderContext, DictRef, EncodingMode, ImageBlob, Key, Obj, PsError, Rc, RefCell, Res, find, hex_decode, + jpeg_components, ps, ps_err, }; /// A file object: the program itself, or data a filter decoded. #[derive(Debug)] pub(crate) enum Stream { Current, - /// Filters over the program's own data, decoded when first read. - Pending(Vec), + /// Filters over the data the program reads, decoded when first read, from the file they were + /// made over: a file being run (`from`), or else the program's own file. + Pending { + chain: Vec, + from: Option>>, + }, Buf { data: Vec, pos: usize, diff --git a/crates/eps/src/import/data/ops.rs b/crates/eps/src/import/data/ops.rs index fbac566d0..a39d56081 100644 --- a/crates/eps/src/import/data/ops.rs +++ b/crates/eps/src/import/data/ops.rs @@ -97,7 +97,7 @@ impl Interp<'_> { let n = match &*f.borrow() { Stream::Current => self.lex.rest().len(), Stream::Buf { data, pos, .. } => data.len().saturating_sub(*pos), - Stream::Pending(_) => 0, + Stream::Pending { .. } => 0, }; self.push(Obj::Int(n as i64))?; } @@ -189,7 +189,7 @@ impl Interp<'_> { *pos += rest.len() - lex.data().len(); (o, lex.immediate) } - Stream::Pending(_) => (None, false), + Stream::Pending { .. } => (None, false), }) } @@ -210,7 +210,7 @@ impl Interp<'_> { *pos = end; data.get(start..end).unwrap_or_default().to_vec() } - Stream::Pending(_) => vec![], + Stream::Pending { .. } => vec![], }) } @@ -229,7 +229,7 @@ impl Interp<'_> { *pos += 1; } } - Stream::Pending(_) => {} + Stream::Pending { .. } => {} } } @@ -284,28 +284,28 @@ impl Interp<'_> { let spec = Filter { name, sub, predictor, fax }; let stream = match self.pop()? { Obj::File { stream: f, .. } => { - // A filter over the file being run reads it, as one over the program's own file - // does, from where the program is when the filter is first read. + // A filter over the program's own file or the file being run waits until it is + // first read, and keeps which of them it reads, wherever the program runs by then. let running = self.running.last().is_some_and(|r| Rc::ptr_eq(r, &f)); match &*f.borrow() { Stream::Buf { data, pos, .. } if !running => self.alloc(data.len().saturating_sub(*pos))?, _ => {} } - let chain = match &*f.borrow() { - Stream::Current => Some(vec![]), - Stream::Buf { .. } if running => Some(vec![]), - Stream::Pending(c) => Some(c.clone()), + let pending = match &*f.borrow() { + Stream::Current => Some((vec![], None)), + Stream::Buf { .. } if running => Some((vec![], Some(f.clone()))), + Stream::Pending { chain, from } => Some((chain.clone(), from.clone())), Stream::Buf { .. } => None, }; - match chain { - Some(mut c) => { + match pending { + Some((mut chain, from)) => { // The new filter reads a pending one's data: it has none left of its own // (`flushfile` on it skips nothing more). - if !c.is_empty() { + if !chain.is_empty() { *f.borrow_mut() = Stream::Buf { data: vec![], pos: 0, rgb: None }; } - c.push(spec); - Stream::Pending(c) + chain.push(spec); + Stream::Pending { chain, from } } None => { let mut b = f.borrow_mut(); @@ -348,18 +348,18 @@ impl Interp<'_> { }) } - /// Decode the filters a file waits to run over the program's data, from where it is now: the - /// file being run, or the program's own file. + /// Decode the filters a file waits to run over the data the program reads, from where it is + /// now in the file they were made over: a file being run, or the program's own file. pub(in super::super) fn materialize(&mut self, f: &Rc>) -> Res { - let chain = match &*f.borrow() { - Stream::Pending(c) => c.clone(), + let (chain, from) = match &*f.borrow() { + Stream::Pending { chain, from } => (chain.clone(), from.clone()), _ => return Ok(()), }; let mut buf: Option<(Vec, Option)> = None; for spec in &chain { buf = Some(match buf.take() { None => { - let (data, rgb) = match self.running.last() { + let (data, rgb) = match &from { Some(r) => { let mut b = r.borrow_mut(); let Stream::Buf { data, pos, .. } = &mut *b else { return io("filter") }; diff --git a/crates/eps/src/import/tests_images.rs b/crates/eps/src/import/tests_images.rs index 76dc84670..160074230 100644 --- a/crates/eps/src/import/tests_images.rs +++ b/crates/eps/src/import/tests_images.rs @@ -106,6 +106,31 @@ fn image_data_comes_from_the_file_being_run() { ); } +/// A filter waits until it is first read, then reads the file it was made over, wherever the +/// program runs by then: one over the program's own file, read in a file being run, and one over +/// a file being run, read in a file that one runs. +#[test] +fn filters_read_the_file_they_were_made_over() { + super::tests::check( + "/f currentfile /ASCIIHexDecode filter def\n\ + currentfile 0 (endstream) /SubFileDecode filter cvx exec\n\ + /s f 3 string readstring pop def\n\ + endstream\n\ + 616263>\n\ + s (abc) eq", + ); + super::tests::check( + "currentfile 0 (endstream) /SubFileDecode filter cvx exec\n\ + /g currentfile /ASCIIHexDecode filter def\n\ + currentfile 0 (endinner) /SubFileDecode filter cvx exec\n\ + /t g 3 string readstring pop def\n\ + endinner\n\ + 78797A>\n\ + endstream\n\ + t (xyz) eq", + ); +} + /// CMYK samples keep their ink amounts in a CMYK TIFF: in the operand and dictionary forms, from /// hex and binary data, from one source or one a component, at 4, 8 and 16 bits, read through /// the Decode array. Gray and RGB images stay PNGs.