Skip to main content

otf_pdf_syntax/
filters.rs

1//! Stream filters (ISO 32000-2 §7.4).
2//!
3//! The general-purpose filters are decoded here. The image codecs (DCT, JPX,
4//! JBIG2, CCITT) are left encoded: decoding stops at the first of them and the
5//! rest of the chain is returned for the caller (the renderer, Phase 8).
6//!
7//! Every decoder enforces [`Limits::max_decoded_size`] and
8//! [`Limits::max_compression_ratio`] while it produces output, so a
9//! decompression bomb is stopped before it allocates, not after.
10
11use flate2::{Decompress, FlushDecompress, Status};
12
13use crate::diag::{DiagnosticKind, Diagnostics};
14use crate::error::{Error, Result};
15use crate::limits::{LimitKind, Limits, Strictness};
16use crate::object::{Dict, Name, Object};
17
18/// One filter in a chain, with its (already resolved) parameters.
19#[derive(Debug, Clone, PartialEq)]
20pub struct Filter {
21    /// The filter's name, abbreviations expanded (`Fl` → `FlateDecode`).
22    pub name: Name,
23    /// Its `/DecodeParms` entry, if any.
24    pub params: Option<Dict>,
25}
26
27/// The result of decoding a filter chain.
28#[derive(Debug, Clone, PartialEq)]
29pub struct Decoded {
30    /// The decoded bytes, or the partly decoded bytes if `remaining` is not
31    /// empty.
32    pub data: Vec<u8>,
33    /// Filters not applied because they are image codecs or unknown, in
34    /// order. Empty when the data is fully decoded.
35    pub remaining: Vec<Filter>,
36}
37
38/// Expands the inline-image abbreviations of filter names (§8.9.7).
39pub fn canonical_filter_name(name: &[u8]) -> &[u8] {
40    match name {
41        b"AHx" => b"ASCIIHexDecode",
42        b"A85" => b"ASCII85Decode",
43        b"LZW" => b"LZWDecode",
44        b"Fl" => b"FlateDecode",
45        b"RL" => b"RunLengthDecode",
46        b"CCF" => b"CCITTFaxDecode",
47        b"DCT" => b"DCTDecode",
48        other => other,
49    }
50}
51
52/// Reads the filter chain from a stream dictionary whose `/Filter` and
53/// `/DecodeParms` values are already resolved to direct objects. Inline
54/// image abbreviations (`/F`, `/DP`) are accepted too.
55pub fn filter_chain(dict: &Dict) -> Vec<Filter> {
56    let names: Vec<&[u8]> = match dict.get(b"Filter").or_else(|| dict.get(b"F")) {
57        Some(Object::Name(n)) => vec![n.as_bytes()],
58        Some(Object::Array(a)) => a.iter().filter_map(Object::as_name).collect(),
59        _ => Vec::new(),
60    };
61    let params = dict.get(b"DecodeParms").or_else(|| dict.get(b"DP"));
62    names
63        .into_iter()
64        .enumerate()
65        .map(|(i, name)| {
66            let p = match params {
67                Some(Object::Dict(d)) if i == 0 => Some(d.clone()),
68                Some(Object::Array(a)) => a.get(i).and_then(Object::as_dict).cloned(),
69                _ => None,
70            };
71            Filter {
72                name: Name::from(canonical_filter_name(name)),
73                params: p,
74            }
75        })
76        .collect()
77}
78
79fn is_image_codec(name: &[u8]) -> bool {
80    matches!(
81        name,
82        b"DCTDecode" | b"JPXDecode" | b"JBIG2Decode" | b"CCITTFaxDecode"
83    )
84}
85
86/// Decodes `input` through `filters`.
87pub fn decode(
88    input: &[u8],
89    filters: &[Filter],
90    limits: &Limits,
91    strictness: Strictness,
92    diagnostics: Option<&Diagnostics>,
93) -> Result<Decoded> {
94    let mut data = input.to_vec();
95    for (i, filter) in filters.iter().enumerate() {
96        let name = filter.name.as_bytes();
97        if is_image_codec(name) {
98            return Ok(Decoded {
99                data,
100                remaining: filters.get(i..).unwrap_or_default().to_vec(),
101            });
102        }
103        limits.charge(data.len() as u64)?;
104        let mut out = Output::new(limits, data.len());
105        let params = filter.params.as_ref();
106        let result = match name {
107            b"FlateDecode" => flate(&data, &mut out),
108            b"LZWDecode" => {
109                let early = params.and_then(|p| p.get_int(b"EarlyChange")).unwrap_or(1) != 0;
110                lzw(&data, early, &mut out)
111            }
112            b"ASCIIHexDecode" => ascii_hex(&data, &mut out),
113            b"ASCII85Decode" => ascii85(&data, &mut out),
114            b"RunLengthDecode" => run_length(&data, &mut out),
115            // Decryption happens before filters; the filter itself is a no-op.
116            b"Crypt" => out.extend(&data),
117            _ => {
118                if strictness == Strictness::Strict {
119                    return Err(Error::Filter("unknown filter"));
120                }
121                if let Some(d) = diagnostics {
122                    d.push(DiagnosticKind::UnknownFilter(name.to_vec()), None, None);
123                }
124                return Ok(Decoded {
125                    data,
126                    remaining: filters.get(i..).unwrap_or_default().to_vec(),
127                });
128            }
129        };
130        match result {
131            Ok(()) => {}
132            // A limit is never repaired.
133            Err(e @ Error::Limit(_)) => return Err(e),
134            // A corrupt tail: keep what decoded, as every viewer does.
135            Err(e) => {
136                if strictness == Strictness::Strict || out.buf.is_empty() {
137                    return Err(e);
138                }
139                if let Some(d) = diagnostics {
140                    d.push(DiagnosticKind::TruncatedStream, None, None);
141                }
142            }
143        }
144        data = out.buf;
145        if matches!(name, b"FlateDecode" | b"LZWDecode")
146            && let Some(p) = params
147        {
148            data = predict(data, p, limits)?;
149        }
150    }
151    Ok(Decoded {
152        data,
153        remaining: Vec::new(),
154    })
155}
156
157/// A growing output buffer that enforces the size and ratio limits.
158struct Output<'l> {
159    buf: Vec<u8>,
160    limit: u64,
161    limits: &'l Limits,
162    input_len: u64,
163}
164
165impl<'l> Output<'l> {
166    fn new(limits: &'l Limits, input_len: usize) -> Self {
167        Self {
168            buf: Vec::new(),
169            limit: limits.max_decoded_size,
170            limits,
171            input_len: input_len as u64,
172        }
173    }
174
175    fn check(&self, len: u64) -> Result<()> {
176        if len > self.limit {
177            return Err(Error::Limit(LimitKind::DecodedSize));
178        }
179        if len > self.limits.ratio_floor
180            && len / self.input_len.max(1) > self.limits.max_compression_ratio
181        {
182            return Err(Error::Limit(LimitKind::CompressionRatio));
183        }
184        Ok(())
185    }
186
187    fn push(&mut self, b: u8) -> Result<()> {
188        self.check(self.buf.len() as u64 + 1)?;
189        self.buf.push(b);
190        Ok(())
191    }
192
193    fn extend(&mut self, bytes: &[u8]) -> Result<()> {
194        self.check((self.buf.len() + bytes.len()) as u64)?;
195        self.buf.extend_from_slice(bytes);
196        Ok(())
197    }
198}
199
200fn flate(input: &[u8], out: &mut Output<'_>) -> Result<()> {
201    match inflate(input, true, out) {
202        // No zlib header: some writers emit raw deflate.
203        Err(Error::Filter(_)) if out.buf.is_empty() => inflate(input, false, out),
204        other => other,
205    }
206}
207
208fn inflate(input: &[u8], zlib: bool, out: &mut Output<'_>) -> Result<()> {
209    const CHUNK: usize = 64 * 1024;
210    let mut d = Decompress::new(zlib);
211    loop {
212        let consumed = usize::try_from(d.total_in()).unwrap_or(usize::MAX);
213        let rest = input.get(consumed..).unwrap_or_default();
214        // Room for one more chunk, but never more than the limit allows plus
215        // one byte (so exceeding it is detected).
216        let len = out.buf.len();
217        let room = (out.limit.saturating_sub(len as u64) + 1).min(CHUNK as u64) as usize;
218        // Exact, so amortized doubling cannot allocate past the limit.
219        out.buf.reserve_exact(room);
220        let before_out = d.total_out();
221        let status = d.decompress_vec(rest, &mut out.buf, FlushDecompress::None);
222        out.check(out.buf.len() as u64)?;
223        match status {
224            Ok(Status::StreamEnd) => return Ok(()),
225            Ok(_) => {
226                let consumed_now = usize::try_from(d.total_in()).unwrap_or(usize::MAX);
227                // There was output room, so no progress means the input ran
228                // out before the end marker: truncated, but what decoded is
229                // good.
230                if consumed_now == consumed && d.total_out() == before_out {
231                    return Err(Error::Filter("truncated deflate data"));
232                }
233            }
234            Err(_) => return Err(Error::Filter("corrupt deflate data")),
235        }
236    }
237}
238
239fn lzw(input: &[u8], early_change: bool, out: &mut Output<'_>) -> Result<()> {
240    const CLEAR: u16 = 256;
241    const EOD: u16 = 257;
242    // Each entry: (prefix code, last byte, length).
243    let mut table: Vec<(u16, u8, u32)> = (0..=255u16).map(|b| (u16::MAX, b as u8, 1)).collect();
244    table.push((0, 0, 0)); // CLEAR
245    table.push((0, 0, 0)); // EOD
246    let mut bits = 9u32;
247    let mut acc = 0u32;
248    let mut acc_bits = 0u32;
249    let mut prev: Option<u16> = None;
250    let mut scratch = Vec::new();
251
252    let reset = |table: &mut Vec<(u16, u8, u32)>| table.truncate(258);
253
254    for &byte in input {
255        acc = (acc << 8) | u32::from(byte);
256        acc_bits += 8;
257        while acc_bits >= bits {
258            let code = ((acc >> (acc_bits - bits)) & ((1 << bits) - 1)) as u16;
259            acc_bits -= bits;
260            acc &= (1 << acc_bits) - 1;
261            if code == CLEAR {
262                reset(&mut table);
263                bits = 9;
264                prev = None;
265                continue;
266            }
267            if code == EOD {
268                return Ok(());
269            }
270            let next = table.len() as u16;
271            let first_byte;
272            if usize::from(code) < table.len() {
273                expand(&table, code, &mut scratch);
274                first_byte = scratch.first().copied().unwrap_or(0);
275                out.extend(&scratch)?;
276            } else if code == next {
277                // The KwKwK case: the code being defined right now.
278                let Some(p) = prev else {
279                    return Err(Error::Filter("invalid LZW code"));
280                };
281                expand(&table, p, &mut scratch);
282                let first = scratch.first().copied().unwrap_or(0);
283                scratch.push(first);
284                first_byte = first;
285                out.extend(&scratch)?;
286            } else {
287                return Err(Error::Filter("invalid LZW code"));
288            }
289            if let Some(p) = prev
290                && table.len() < 4096
291            {
292                let len = table.get(usize::from(p)).map_or(0, |e| e.2) + 1;
293                table.push((p, first_byte, len));
294            }
295            prev = Some(code);
296            let threshold = table.len() + usize::from(early_change);
297            bits = match threshold {
298                0..=511 => 9,
299                512..=1023 => 10,
300                1024..=2047 => 11,
301                _ => 12,
302            };
303        }
304    }
305    // No EOD: tolerated, as by every reader.
306    Ok(())
307}
308
309/// Writes the bytes of `code` into `scratch`.
310fn expand(table: &[(u16, u8, u32)], code: u16, scratch: &mut Vec<u8>) {
311    scratch.clear();
312    let mut c = code;
313    // The chain length is bounded by the table size (4096).
314    for _ in 0..4096 {
315        let Some(&(prefix, byte, _)) = table.get(usize::from(c)) else {
316            break;
317        };
318        scratch.push(byte);
319        if prefix == u16::MAX {
320            break;
321        }
322        c = prefix;
323    }
324    scratch.reverse();
325}
326
327fn ascii_hex(input: &[u8], out: &mut Output<'_>) -> Result<()> {
328    let mut high: Option<u8> = None;
329    for &b in input {
330        let v = match b {
331            b'0'..=b'9' => b - b'0',
332            b'a'..=b'f' => b - b'a' + 10,
333            b'A'..=b'F' => b - b'A' + 10,
334            b'>' => break,
335            _ if crate::lexer::is_whitespace(b) => continue,
336            _ => return Err(Error::Filter("invalid ASCIIHex character")),
337        };
338        match high.take() {
339            Some(h) => out.push(h << 4 | v)?,
340            None => high = Some(v),
341        }
342    }
343    if let Some(h) = high {
344        out.push(h << 4)?;
345    }
346    Ok(())
347}
348
349fn ascii85(input: &[u8], out: &mut Output<'_>) -> Result<()> {
350    let input = input.strip_prefix(b"<~").unwrap_or(input);
351    let mut group = [0u8; 5];
352    let mut n = 0usize;
353    for &b in input {
354        match b {
355            b'~' => break,
356            b'z' if n == 0 => out.extend(&[0; 4])?,
357            b'!'..=b'u' => {
358                if let Some(slot) = group.get_mut(n) {
359                    *slot = b - b'!';
360                }
361                n += 1;
362                if n == 5 {
363                    out.extend(&ascii85_group(&group)?)?;
364                    n = 0;
365                }
366            }
367            _ if crate::lexer::is_whitespace(b) => {}
368            _ => return Err(Error::Filter("invalid ASCII85 character")),
369        }
370    }
371    if n == 1 {
372        return Err(Error::Filter("ASCII85 final group of one character"));
373    }
374    if n > 1 {
375        for slot in group.iter_mut().skip(n) {
376            *slot = 84; // 'u' - '!'
377        }
378        let bytes = ascii85_group(&group)?;
379        out.extend(bytes.get(..n - 1).unwrap_or_default())?;
380    }
381    Ok(())
382}
383
384fn ascii85_group(group: &[u8; 5]) -> Result<[u8; 4]> {
385    let value = group.iter().fold(0u64, |acc, &d| acc * 85 + u64::from(d));
386    let value = u32::try_from(value).map_err(|_| Error::Filter("ASCII85 group overflow"))?;
387    Ok(value.to_be_bytes())
388}
389
390fn run_length(input: &[u8], out: &mut Output<'_>) -> Result<()> {
391    let mut i = 0;
392    while let Some(&len) = input.get(i) {
393        i += 1;
394        match len {
395            128 => return Ok(()),
396            0..=127 => {
397                let n = usize::from(len) + 1;
398                let Some(run) = input.get(i..i + n) else {
399                    out.extend(input.get(i..).unwrap_or_default())?;
400                    return Err(Error::Filter("truncated RunLength data"));
401                };
402                out.extend(run)?;
403                i += n;
404            }
405            _ => {
406                let Some(&b) = input.get(i) else {
407                    return Err(Error::Filter("truncated RunLength data"));
408                };
409                i += 1;
410                for _ in 0..(257 - usize::from(len)) {
411                    out.push(b)?;
412                }
413            }
414        }
415    }
416    Ok(())
417}
418
419/// Undoes a PNG or TIFF predictor (§7.4.4.4).
420fn predict(data: Vec<u8>, params: &Dict, limits: &Limits) -> Result<Vec<u8>> {
421    let predictor = params.get_int(b"Predictor").unwrap_or(1);
422    if predictor <= 1 {
423        return Ok(data);
424    }
425    let colors = params.get_int(b"Colors").unwrap_or(1);
426    let bpc = params.get_int(b"BitsPerComponent").unwrap_or(8);
427    let columns = params.get_int(b"Columns").unwrap_or(1);
428    if !(1..=32).contains(&colors)
429        || !matches!(bpc, 1 | 2 | 4 | 8 | 16)
430        || columns < 1
431        || columns as u64 > limits.max_decoded_size
432    {
433        return Err(Error::Filter("invalid predictor parameters"));
434    }
435    let (colors, bpc, columns) = (colors as usize, bpc as usize, columns as usize);
436    let bits_per_pixel = colors * bpc;
437    let row_len = (bits_per_pixel * columns).div_ceil(8);
438    let bpp = bits_per_pixel.div_ceil(8);
439    match predictor {
440        2 => Ok(tiff_predict(data, colors, bpc, row_len)),
441        10..=15 => png_predict(&data, row_len, bpp),
442        _ => Err(Error::Filter("unknown predictor")),
443    }
444}
445
446fn png_predict(data: &[u8], row_len: usize, bpp: usize) -> Result<Vec<u8>> {
447    let mut out = Vec::with_capacity(data.len());
448    let mut prev = vec![0u8; row_len];
449    let mut row = vec![0u8; row_len];
450    for chunk in data.chunks(row_len + 1) {
451        let Some((&kind, encoded)) = chunk.split_first() else {
452            break;
453        };
454        row.fill(0);
455        for (i, &x) in encoded.iter().enumerate() {
456            let a = if i >= bpp {
457                row.get(i - bpp).copied().unwrap_or(0)
458            } else {
459                0
460            };
461            let b = prev.get(i).copied().unwrap_or(0);
462            let c = if i >= bpp {
463                prev.get(i - bpp).copied().unwrap_or(0)
464            } else {
465                0
466            };
467            let value = match kind {
468                1 => x.wrapping_add(a),
469                2 => x.wrapping_add(b),
470                3 => x.wrapping_add(((u16::from(a) + u16::from(b)) / 2) as u8),
471                4 => x.wrapping_add(paeth(a, b, c)),
472                // 0, and leniently any unknown filter type: None.
473                _ => x,
474            };
475            if let Some(slot) = row.get_mut(i) {
476                *slot = value;
477            }
478        }
479        out.extend_from_slice(row.get(..encoded.len()).unwrap_or_default());
480        std::mem::swap(&mut prev, &mut row);
481    }
482    Ok(out)
483}
484
485fn paeth(a: u8, b: u8, c: u8) -> u8 {
486    let p = i16::from(a) + i16::from(b) - i16::from(c);
487    let pa = (p - i16::from(a)).abs();
488    let pb = (p - i16::from(b)).abs();
489    let pc = (p - i16::from(c)).abs();
490    if pa <= pb && pa <= pc {
491        a
492    } else if pb <= pc {
493        b
494    } else {
495        c
496    }
497}
498
499fn tiff_predict(mut data: Vec<u8>, colors: usize, bpc: usize, row_len: usize) -> Vec<u8> {
500    for row in data.chunks_mut(row_len) {
501        match bpc {
502            8 => {
503                for i in colors..row.len() {
504                    let left = row.get(i - colors).copied().unwrap_or(0);
505                    if let Some(x) = row.get_mut(i) {
506                        *x = x.wrapping_add(left);
507                    }
508                }
509            }
510            16 => {
511                let step = colors * 2;
512                let mut i = step;
513                while i + 1 < row.len() {
514                    let left = u16::from_be_bytes([
515                        row.get(i - step).copied().unwrap_or(0),
516                        row.get(i - step + 1).copied().unwrap_or(0),
517                    ]);
518                    let cur = u16::from_be_bytes([
519                        row.get(i).copied().unwrap_or(0),
520                        row.get(i + 1).copied().unwrap_or(0),
521                    ]);
522                    let [hi, lo] = cur.wrapping_add(left).to_be_bytes();
523                    if let Some(x) = row.get_mut(i) {
524                        *x = hi;
525                    }
526                    if let Some(x) = row.get_mut(i + 1) {
527                        *x = lo;
528                    }
529                    i += 2;
530                }
531            }
532            _ => {
533                // 1, 2 or 4 bits: work sample by sample.
534                let samples = row.len() * 8 / bpc;
535                let mask = (1u16 << bpc) - 1;
536                let get = |row: &[u8], s: usize| -> u16 {
537                    let bit = s * bpc;
538                    let byte = row.get(bit / 8).copied().unwrap_or(0);
539                    (u16::from(byte) >> (8 - bpc - bit % 8)) & mask
540                };
541                for s in colors..samples {
542                    let v = (get(row, s) + get(row, s - colors)) & mask;
543                    let bit = s * bpc;
544                    let shift = 8 - bpc - bit % 8;
545                    if let Some(byte) = row.get_mut(bit / 8) {
546                        *byte = (*byte & !((mask as u8) << shift)) | ((v as u8) << shift);
547                    }
548                }
549            }
550        }
551    }
552    data
553}
554
555#[cfg(test)]
556mod tests {
557    use super::*;
558    use std::io::Write;
559
560    fn filter(name: &str) -> Filter {
561        Filter {
562            name: Name::from(name),
563            params: None,
564        }
565    }
566
567    fn run(input: &[u8], filters: &[Filter]) -> Result<Decoded> {
568        decode(input, filters, &Limits::default(), Strictness::Strict, None)
569    }
570
571    fn data(input: &[u8], name: &str) -> Vec<u8> {
572        run(input, &[filter(name)]).unwrap().data
573    }
574
575    fn zlib(data: &[u8]) -> Vec<u8> {
576        let mut e = flate2::write::ZlibEncoder::new(Vec::new(), flate2::Compression::default());
577        e.write_all(data).unwrap();
578        e.finish().unwrap()
579    }
580
581    #[test]
582    fn chain_parsing() {
583        let mut d = Dict::new();
584        d.insert(
585            "Filter",
586            vec![
587                Object::Name(Name::from("A85")),
588                Object::Name(Name::from("Fl")),
589            ],
590        );
591        let mut p = Dict::new();
592        p.insert("Predictor", 12i64);
593        d.insert("DecodeParms", vec![Object::Null, Object::Dict(p.clone())]);
594        let chain = filter_chain(&d);
595        assert_eq!(chain.len(), 2);
596        assert_eq!(chain[0].name, Name::from("ASCII85Decode"));
597        assert_eq!(chain[0].params, None);
598        assert_eq!(chain[1].name, Name::from("FlateDecode"));
599        assert_eq!(chain[1].params, Some(p));
600
601        let mut single = Dict::new();
602        single.insert("F", Name::from("AHx"));
603        single.insert("DP", Dict::new());
604        let chain = filter_chain(&single);
605        assert_eq!(chain[0].name, Name::from("ASCIIHexDecode"));
606        assert!(chain[0].params.is_some());
607        assert!(filter_chain(&Dict::new()).is_empty());
608    }
609
610    #[test]
611    fn flate_roundtrip() {
612        let text = b"Hello, PDF world! ".repeat(100);
613        assert_eq!(data(&zlib(&text), "FlateDecode"), text);
614    }
615
616    #[test]
617    fn raw_deflate_without_header() {
618        let mut e = flate2::write::DeflateEncoder::new(Vec::new(), flate2::Compression::default());
619        e.write_all(b"raw deflate").unwrap();
620        assert_eq!(data(&e.finish().unwrap(), "FlateDecode"), b"raw deflate");
621    }
622
623    #[test]
624    fn truncated_flate_keeps_prefix() {
625        let text: Vec<u8> = (0..20000u32).map(|i| (i * 7 % 251) as u8).collect();
626        let z = zlib(&text);
627        let cut = &z[..z.len() / 2];
628        let diags = Diagnostics::default();
629        let out = decode(
630            cut,
631            &[filter("FlateDecode")],
632            &Limits::default(),
633            Strictness::Lenient,
634            Some(&diags),
635        )
636        .unwrap();
637        assert!(!out.data.is_empty());
638        assert!(text.starts_with(&out.data));
639        assert_eq!(diags.snapshot()[0].kind, DiagnosticKind::TruncatedStream);
640        assert!(
641            decode(
642                cut,
643                &[filter("FlateDecode")],
644                &Limits::default(),
645                Strictness::Strict,
646                None
647            )
648            .is_err()
649        );
650    }
651
652    #[test]
653    fn garbage_flate_is_an_error() {
654        assert!(run(b"\xff\xff\xff\xff", &[filter("FlateDecode")]).is_err());
655    }
656
657    #[test]
658    fn decompression_bomb_stops_at_limit() {
659        let bomb = zlib(&vec![0u8; 8 << 20]);
660        let limits = Limits {
661            max_decoded_size: 1 << 20,
662            ..Limits::default()
663        };
664        assert_eq!(
665            decode(
666                &bomb,
667                &[filter("FlateDecode")],
668                &limits,
669                Strictness::Lenient,
670                None
671            ),
672            Err(Error::Limit(LimitKind::DecodedSize))
673        );
674        let limits = Limits {
675            max_compression_ratio: 100,
676            ratio_floor: 1 << 16,
677            ..Limits::default()
678        };
679        assert_eq!(
680            decode(
681                &bomb,
682                &[filter("FlateDecode")],
683                &limits,
684                Strictness::Lenient,
685                None
686            ),
687            Err(Error::Limit(LimitKind::CompressionRatio))
688        );
689    }
690
691    #[test]
692    fn png_predictors() {
693        // Two rows of 3 bytes, one per filter type, encoded by hand.
694        let raw = [1u8, 2, 3, 4, 6, 8];
695        let mut p = Dict::new();
696        p.insert("Predictor", 12i64);
697        p.insert("Columns", 3i64);
698        for (kind, encoded) in [
699            (0u8, vec![0, 1, 2, 3, 0, 4, 6, 8]),
700            (1, vec![1, 1, 1, 1, 1, 4, 2, 2]),
701            (2, vec![2, 1, 2, 3, 2, 3, 4, 5]),
702            (3, vec![3, 1, 2, 2, 3, 4, 3, 4]),
703            (4, vec![4, 1, 1, 1, 4, 3, 2, 2]),
704        ] {
705            let f = Filter {
706                name: Name::from("FlateDecode"),
707                params: Some(p.clone()),
708            };
709            assert_eq!(run(&zlib(&encoded), &[f]).unwrap().data, raw, "type {kind}");
710        }
711    }
712
713    #[test]
714    fn tiff_predictor_8_bit() {
715        let mut p = Dict::new();
716        p.insert("Predictor", 2i64);
717        p.insert("Columns", 3i64);
718        p.insert("Colors", 1i64);
719        let f = Filter {
720            name: Name::from("FlateDecode"),
721            params: Some(p),
722        };
723        assert_eq!(
724            run(&zlib(&[1, 1, 1, 5, 1, 1]), &[f]).unwrap().data,
725            [1, 2, 3, 5, 6, 7]
726        );
727    }
728
729    #[test]
730    fn tiff_predictor_16_and_4_bit() {
731        assert_eq!(tiff_predict(vec![0, 1, 0, 2], 1, 16, 4), vec![0, 1, 0, 3]);
732        // Four 4-bit samples 1,1,1,1 → 1,2,3,4.
733        assert_eq!(tiff_predict(vec![0x11, 0x11], 1, 4, 2), vec![0x12, 0x34]);
734    }
735
736    #[test]
737    fn bad_predictor_params() {
738        let mut p = Dict::new();
739        p.insert("Predictor", 12i64);
740        p.insert("BitsPerComponent", 3i64);
741        let f = Filter {
742            name: Name::from("FlateDecode"),
743            params: Some(p),
744        };
745        assert!(run(&zlib(b"x"), &[f]).is_err());
746    }
747
748    #[test]
749    fn lzw() {
750        // The example from ISO 32000-2 §7.4.4.2.
751        let encoded = [0x80, 0x0b, 0x60, 0x50, 0x22, 0x0c, 0x0c, 0x85, 0x01];
752        assert_eq!(data(&encoded, "LZWDecode"), b"-----A---B");
753    }
754
755    #[test]
756    fn lzw_invalid_code() {
757        // 9-bit code 300 with an empty table.
758        assert!(run(&[0x96, 0x00], &[filter("LZWDecode")]).is_err());
759    }
760
761    #[test]
762    fn ascii_hex() {
763        assert_eq!(data(b"48 65 6C6c6f>", "ASCIIHexDecode"), b"Hello");
764        assert_eq!(data(b"901FA>", "ASCIIHexDecode"), b"\x90\x1f\xa0");
765        assert_eq!(data(b"41", "ASCIIHexDecode"), b"A");
766        assert!(run(b"4G>", &[filter("ASCIIHexDecode")]).is_err());
767    }
768
769    #[test]
770    fn ascii85() {
771        assert_eq!(
772            data(b"87cURD]i,\"Ebo80~>", "ASCII85Decode"),
773            b"Hello World!"
774        );
775        assert_eq!(
776            data(b"<~87cURD]i,\"Ebo80~>", "ASCII85Decode"),
777            b"Hello World!"
778        );
779        assert_eq!(data(b"z~>", "ASCII85Decode"), [0, 0, 0, 0]);
780        assert_eq!(
781            data(b"87cUR D]i,\n\"Ebo8~>", "ASCII85Decode"),
782            b"Hello World"
783        );
784        assert!(run(b"s8W-\"~>", &[filter("ASCII85Decode")]).is_err());
785        assert!(run(b"87cURD]i,\"Ebo80a~>", &[filter("ASCII85Decode")]).is_err());
786        assert!(run(b"\x01", &[filter("ASCII85Decode")]).is_err());
787    }
788
789    #[test]
790    fn run_length() {
791        assert_eq!(
792            data(&[2, b'a', b'b', b'c', 254, b'x', 128, 9], "RunLengthDecode"),
793            b"abcxxx"
794        );
795        assert!(run(&[5, b'a'], &[filter("RunLengthDecode")]).is_err());
796        assert!(run(&[200], &[filter("RunLengthDecode")]).is_err());
797    }
798
799    #[test]
800    fn image_codecs_are_left_encoded() {
801        let out = run(b"48>", &[filter("ASCIIHexDecode"), filter("DCTDecode")]).unwrap();
802        assert_eq!(out.data, b"H");
803        assert_eq!(out.remaining, vec![filter("DCTDecode")]);
804    }
805
806    #[test]
807    fn unknown_filter() {
808        let diags = Diagnostics::default();
809        let out = decode(
810            b"abc",
811            &[filter("Foo")],
812            &Limits::default(),
813            Strictness::Lenient,
814            Some(&diags),
815        )
816        .unwrap();
817        assert_eq!(out.data, b"abc");
818        assert_eq!(out.remaining.len(), 1);
819        assert_eq!(
820            diags.snapshot()[0].kind,
821            DiagnosticKind::UnknownFilter(b"Foo".to_vec())
822        );
823        assert!(
824            decode(
825                b"abc",
826                &[filter("Foo")],
827                &Limits::default(),
828                Strictness::Strict,
829                None
830            )
831            .is_err()
832        );
833    }
834
835    #[test]
836    fn crypt_filter_passthrough() {
837        assert_eq!(data(b"abc", "Crypt"), b"abc");
838    }
839}