1use 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#[derive(Debug, Clone, PartialEq)]
20pub struct Filter {
21 pub name: Name,
23 pub params: Option<Dict>,
25}
26
27#[derive(Debug, Clone, PartialEq)]
29pub struct Decoded {
30 pub data: Vec<u8>,
33 pub remaining: Vec<Filter>,
36}
37
38pub 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
52pub 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
86pub 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 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 Err(e @ Error::Limit(_)) => return Err(e),
134 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
157struct 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 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 let len = out.buf.len();
217 let room = (out.limit.saturating_sub(len as u64) + 1).min(CHUNK as u64) as usize;
218 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 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 let mut table: Vec<(u16, u8, u32)> = (0..=255u16).map(|b| (u16::MAX, b as u8, 1)).collect();
244 table.push((0, 0, 0)); table.push((0, 0, 0)); 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 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 Ok(())
307}
308
309fn expand(table: &[(u16, u8, u32)], code: u16, scratch: &mut Vec<u8>) {
311 scratch.clear();
312 let mut c = code;
313 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; }
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
419fn 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 _ => 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 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 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 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 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 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}