mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 14:33:34 +00:00
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.
This commit is contained in:
@@ -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<Filter>),
|
||||
/// 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<Filter>,
|
||||
from: Option<Rc<RefCell<Stream>>>,
|
||||
},
|
||||
Buf {
|
||||
data: Vec<u8>,
|
||||
pos: usize,
|
||||
|
||||
@@ -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<RefCell<Stream>>) -> 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<u8>, Option<u8>)> = 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") };
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user