1use std::collections::HashMap;
10
11use rpdfium_codec::{DecodeFilter, FilterParams};
12use rpdfium_core::Name;
13
14use crate::object::Object;
15
16pub fn resolve_filter_chain(dict: &HashMap<Name, Object>) -> Vec<(DecodeFilter, FilterParams)> {
22 let filter_obj = match dict.get(&Name::filter()) {
23 Some(obj) => obj,
24 None => return Vec::new(),
25 };
26
27 let decode_parms_obj = dict.get(&Name::decode_parms());
28
29 let filter_names: Vec<&Name> = match filter_obj {
31 Object::Name(n) => vec![n],
32 Object::Array(arr) => arr.iter().filter_map(|o| o.as_name()).collect(),
33 _ => return Vec::new(),
34 };
35
36 let params_list: Vec<Option<&HashMap<Name, Object>>> = match decode_parms_obj {
38 Some(Object::Dictionary(d)) => vec![Some(d)],
39 Some(Object::Array(arr)) => arr
40 .iter()
41 .map(|o| match o {
42 Object::Dictionary(d) => Some(d),
43 Object::Null => None,
44 _ => None,
45 })
46 .collect(),
47 _ => Vec::new(),
48 };
49
50 filter_names
51 .iter()
52 .enumerate()
53 .filter_map(|(i, name)| {
54 let filter = name_to_filter(name)?;
55 let params = params_list
56 .get(i)
57 .and_then(|opt| *opt)
58 .map(extract_filter_params)
59 .unwrap_or_default();
60 Some((filter, params))
61 })
62 .collect()
63}
64
65pub fn name_to_filter(name: &Name) -> Option<DecodeFilter> {
69 match name.as_bytes() {
70 b"FlateDecode" => Some(DecodeFilter::Flate),
72 b"LZWDecode" => Some(DecodeFilter::LZW),
73 b"ASCII85Decode" => Some(DecodeFilter::ASCII85),
74 b"ASCIIHexDecode" => Some(DecodeFilter::ASCIIHex),
75 b"RunLengthDecode" => Some(DecodeFilter::RunLength),
76 b"DCTDecode" => Some(DecodeFilter::DCT),
77 b"CCITTFaxDecode" => Some(DecodeFilter::CCITTFax),
78 b"JBIG2Decode" => Some(DecodeFilter::JBIG2),
79 b"JPXDecode" => Some(DecodeFilter::JPX),
80 b"Fl" => Some(DecodeFilter::Flate),
82 b"LZW" => Some(DecodeFilter::LZW),
83 b"A85" => Some(DecodeFilter::ASCII85),
84 b"AHx" => Some(DecodeFilter::ASCIIHex),
85 b"RL" => Some(DecodeFilter::RunLength),
86 b"DCT" => Some(DecodeFilter::DCT),
87 b"CCF" => Some(DecodeFilter::CCITTFax),
88 b"JBIG2" => Some(DecodeFilter::JBIG2),
89 b"JPX" => Some(DecodeFilter::JPX),
90 _ => None,
91 }
92}
93
94pub fn extract_filter_params(dict: &HashMap<Name, Object>) -> FilterParams {
96 FilterParams {
97 predictor: dict
98 .get(&Name::predictor())
99 .and_then(|o| o.as_i64())
100 .map(|v| v as i32),
101 columns: dict
102 .get(&Name::columns())
103 .and_then(|o| o.as_i64())
104 .map(|v| v as i32),
105 colors: dict
106 .get(&Name::colors())
107 .and_then(|o| o.as_i64())
108 .map(|v| v as i32),
109 bits_per_component: dict
110 .get(&Name::bits_per_component())
111 .and_then(|o| o.as_i64())
112 .map(|v| v as i32),
113 early_change: dict
114 .get(&Name::early_change())
115 .and_then(|o| o.as_i64())
116 .map(|v| v != 0),
117 k: dict
118 .get(&Name::k())
119 .and_then(|o| o.as_i64())
120 .map(|v| v as i32),
121 rows: dict
122 .get(&Name::rows())
123 .and_then(|o| o.as_i64())
124 .map(|v| v as i32),
125 end_of_line: dict.get(&Name::end_of_line()).and_then(|o| o.as_bool()),
126 encoded_byte_align: dict
127 .get(&Name::encoded_byte_align())
128 .and_then(|o| o.as_bool()),
129 black_is_1: dict.get(&Name::black_is_1()).and_then(|o| o.as_bool()),
130 jbig2_globals: None,
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_no_filter_returns_empty() {
141 let dict = HashMap::new();
142 assert!(resolve_filter_chain(&dict).is_empty());
143 }
144
145 #[test]
146 fn test_single_filter_name() {
147 let mut dict = HashMap::new();
148 dict.insert(Name::filter(), Object::Name(Name::flate_decode()));
149
150 let chain = resolve_filter_chain(&dict);
151 assert_eq!(chain.len(), 1);
152 assert_eq!(chain[0].0, DecodeFilter::Flate);
153 }
154
155 #[test]
156 fn test_filter_array() {
157 let mut dict = HashMap::new();
158 dict.insert(
159 Name::filter(),
160 Object::Array(vec![
161 Object::Name(Name::flate_decode()),
162 Object::Name(Name::ascii85_decode()),
163 ]),
164 );
165
166 let chain = resolve_filter_chain(&dict);
167 assert_eq!(chain.len(), 2);
168 assert_eq!(chain[0].0, DecodeFilter::Flate);
169 assert_eq!(chain[1].0, DecodeFilter::ASCII85);
170 }
171
172 #[test]
173 fn test_abbreviated_filter_names() {
174 assert_eq!(name_to_filter(&Name::from("Fl")), Some(DecodeFilter::Flate));
175 assert_eq!(
176 name_to_filter(&Name::from("A85")),
177 Some(DecodeFilter::ASCII85)
178 );
179 assert_eq!(
180 name_to_filter(&Name::from("AHx")),
181 Some(DecodeFilter::ASCIIHex)
182 );
183 assert_eq!(
184 name_to_filter(&Name::from("RL")),
185 Some(DecodeFilter::RunLength)
186 );
187 assert_eq!(name_to_filter(&Name::from("LZW")), Some(DecodeFilter::LZW));
188 assert_eq!(name_to_filter(&Name::from("DCT")), Some(DecodeFilter::DCT));
189 assert_eq!(
190 name_to_filter(&Name::from("CCF")),
191 Some(DecodeFilter::CCITTFax)
192 );
193 assert_eq!(
194 name_to_filter(&Name::from("JBIG2")),
195 Some(DecodeFilter::JBIG2)
196 );
197 assert_eq!(name_to_filter(&Name::from("JPX")), Some(DecodeFilter::JPX));
198 }
199
200 #[test]
201 fn test_full_filter_names() {
202 assert_eq!(
203 name_to_filter(&Name::flate_decode()),
204 Some(DecodeFilter::Flate)
205 );
206 assert_eq!(name_to_filter(&Name::lzw_decode()), Some(DecodeFilter::LZW));
207 assert_eq!(
208 name_to_filter(&Name::ascii85_decode()),
209 Some(DecodeFilter::ASCII85)
210 );
211 assert_eq!(
212 name_to_filter(&Name::ascii_hex_decode()),
213 Some(DecodeFilter::ASCIIHex)
214 );
215 assert_eq!(
216 name_to_filter(&Name::run_length_decode()),
217 Some(DecodeFilter::RunLength)
218 );
219 assert_eq!(name_to_filter(&Name::dct_decode()), Some(DecodeFilter::DCT));
220 assert_eq!(
221 name_to_filter(&Name::ccitt_fax_decode()),
222 Some(DecodeFilter::CCITTFax)
223 );
224 assert_eq!(
225 name_to_filter(&Name::jbig2_decode()),
226 Some(DecodeFilter::JBIG2)
227 );
228 assert_eq!(name_to_filter(&Name::jpx_decode()), Some(DecodeFilter::JPX));
229 }
230
231 #[test]
232 fn test_unknown_filter_name_returns_none() {
233 assert_eq!(name_to_filter(&Name::from("UnknownFilter")), None);
234 }
235
236 #[test]
237 fn test_single_decode_parms_dict() {
238 let mut params_dict = HashMap::new();
239 params_dict.insert(Name::predictor(), Object::Integer(12));
240 params_dict.insert(Name::columns(), Object::Integer(800));
241
242 let mut dict = HashMap::new();
243 dict.insert(Name::filter(), Object::Name(Name::flate_decode()));
244 dict.insert(Name::decode_parms(), Object::Dictionary(params_dict));
245
246 let chain = resolve_filter_chain(&dict);
247 assert_eq!(chain.len(), 1);
248 assert_eq!(chain[0].0, DecodeFilter::Flate);
249 assert_eq!(chain[0].1.predictor, Some(12));
250 assert_eq!(chain[0].1.columns, Some(800));
251 }
252
253 #[test]
254 fn test_decode_parms_array_with_null() {
255 let mut params_dict = HashMap::new();
256 params_dict.insert(Name::predictor(), Object::Integer(15));
257
258 let mut dict = HashMap::new();
259 dict.insert(
260 Name::filter(),
261 Object::Array(vec![
262 Object::Name(Name::flate_decode()),
263 Object::Name(Name::ascii85_decode()),
264 ]),
265 );
266 dict.insert(
267 Name::decode_parms(),
268 Object::Array(vec![Object::Dictionary(params_dict), Object::Null]),
269 );
270
271 let chain = resolve_filter_chain(&dict);
272 assert_eq!(chain.len(), 2);
273 assert_eq!(chain[0].1.predictor, Some(15));
274 assert_eq!(chain[1].1.predictor, None);
276 }
277
278 #[test]
279 fn test_extract_all_filter_params() {
280 let mut d = HashMap::new();
281 d.insert(Name::predictor(), Object::Integer(12));
282 d.insert(Name::columns(), Object::Integer(100));
283 d.insert(Name::colors(), Object::Integer(3));
284 d.insert(Name::bits_per_component(), Object::Integer(8));
285 d.insert(Name::early_change(), Object::Integer(0));
286 d.insert(Name::k(), Object::Integer(-1));
287 d.insert(Name::rows(), Object::Integer(50));
288 d.insert(Name::end_of_line(), Object::Boolean(true));
289 d.insert(Name::encoded_byte_align(), Object::Boolean(false));
290 d.insert(Name::black_is_1(), Object::Boolean(true));
291
292 let params = extract_filter_params(&d);
293 assert_eq!(params.predictor, Some(12));
294 assert_eq!(params.columns, Some(100));
295 assert_eq!(params.colors, Some(3));
296 assert_eq!(params.bits_per_component, Some(8));
297 assert_eq!(params.early_change, Some(false));
298 assert_eq!(params.k, Some(-1));
299 assert_eq!(params.rows, Some(50));
300 assert_eq!(params.end_of_line, Some(true));
301 assert_eq!(params.encoded_byte_align, Some(false));
302 assert_eq!(params.black_is_1, Some(true));
303 }
304
305 #[test]
306 fn test_extract_empty_params() {
307 let d = HashMap::new();
308 let params = extract_filter_params(&d);
309 assert_eq!(params.predictor, None);
310 assert_eq!(params.columns, None);
311 }
312
313 #[test]
314 fn test_filter_with_no_decode_parms() {
315 let mut dict = HashMap::new();
316 dict.insert(Name::filter(), Object::Name(Name::ascii_hex_decode()));
317 let chain = resolve_filter_chain(&dict);
320 assert_eq!(chain.len(), 1);
321 assert_eq!(chain[0].0, DecodeFilter::ASCIIHex);
322 assert_eq!(chain[0].1.predictor, None);
323 }
324
325 #[test]
326 fn test_non_name_filter_value_returns_empty() {
327 let mut dict = HashMap::new();
328 dict.insert(Name::filter(), Object::Integer(42));
329
330 assert!(resolve_filter_chain(&dict).is_empty());
331 }
332
333 #[test]
335 fn test_dct_not_at_end_of_chain() {
336 let mut dict = HashMap::new();
337 dict.insert(
338 Name::filter(),
339 Object::Array(vec![
340 Object::Name(Name::dct_decode()),
341 Object::Name(Name::flate_decode()),
342 ]),
343 );
344
345 let chain = resolve_filter_chain(&dict);
346 assert_eq!(chain.len(), 2);
347 assert_eq!(chain[0].0, DecodeFilter::DCT);
348 assert_eq!(chain[1].0, DecodeFilter::Flate);
349 }
350
351 #[test]
353 fn test_jpx_followed_by_ascii85() {
354 let mut dict = HashMap::new();
355 dict.insert(
356 Name::filter(),
357 Object::Array(vec![
358 Object::Name(Name::jpx_decode()),
359 Object::Name(Name::ascii85_decode()),
360 ]),
361 );
362
363 let chain = resolve_filter_chain(&dict);
364 assert_eq!(chain.len(), 2);
365 assert_eq!(chain[0].0, DecodeFilter::JPX);
366 assert_eq!(chain[1].0, DecodeFilter::ASCII85);
367 }
368
369 #[test]
371 fn test_triple_filter_chain() {
372 let mut dict = HashMap::new();
373 dict.insert(
374 Name::filter(),
375 Object::Array(vec![
376 Object::Name(Name::flate_decode()),
377 Object::Name(Name::ascii85_decode()),
378 Object::Name(Name::ascii_hex_decode()),
379 ]),
380 );
381
382 let chain = resolve_filter_chain(&dict);
383 assert_eq!(chain.len(), 3);
384 assert_eq!(chain[0].0, DecodeFilter::Flate);
385 assert_eq!(chain[1].0, DecodeFilter::ASCII85);
386 assert_eq!(chain[2].0, DecodeFilter::ASCIIHex);
387 }
388
389 #[test]
397 fn test_parser_decode_get_decoder_array_no_filter() {
398 let dict = HashMap::new();
399 let chain = resolve_filter_chain(&dict);
400 assert!(chain.is_empty());
401 }
402
403 #[test]
407 fn test_parser_decode_get_decoder_array_wrong_filter_type() {
408 let mut dict = HashMap::new();
409 dict.insert(
410 Name::filter(),
411 Object::String(rpdfium_core::PdfString::from_bytes(b"RL".to_vec())),
412 );
413 let chain = resolve_filter_chain(&dict);
414 assert!(chain.is_empty());
415 }
416
417 #[test]
421 fn test_parser_decode_get_decoder_array_single_name() {
422 let mut dict = HashMap::new();
423 dict.insert(Name::filter(), Object::Name(Name::from("RL")));
424 let chain = resolve_filter_chain(&dict);
425 assert_eq!(chain.len(), 1);
426 assert_eq!(chain[0].0, DecodeFilter::RunLength);
427 }
428
429 #[test]
433 fn test_parser_decode_get_decoder_array_empty_array() {
434 let mut dict = HashMap::new();
435 dict.insert(Name::filter(), Object::Array(vec![]));
436 let chain = resolve_filter_chain(&dict);
437 assert!(chain.is_empty());
438 }
439
440 #[test]
444 fn test_parser_decode_get_decoder_array_one_element() {
445 let mut dict = HashMap::new();
446 dict.insert(
447 Name::filter(),
448 Object::Array(vec![Object::Name(Name::from("FlateDecode"))]),
449 );
450 let chain = resolve_filter_chain(&dict);
451 assert_eq!(chain.len(), 1);
452 assert_eq!(chain[0].0, DecodeFilter::Flate);
453 }
454
455 #[test]
459 fn test_parser_decode_get_decoder_array_two_elements() {
460 let mut dict = HashMap::new();
461 dict.insert(
462 Name::filter(),
463 Object::Array(vec![
464 Object::Name(Name::from("AHx")),
465 Object::Name(Name::from("LZWDecode")),
466 ]),
467 );
468 let chain = resolve_filter_chain(&dict);
469 assert_eq!(chain.len(), 2);
470 assert_eq!(chain[0].0, DecodeFilter::ASCIIHex);
471 assert_eq!(chain[1].0, DecodeFilter::LZW);
472 }
473
474 #[test]
478 fn test_parser_decode_validate_pipeline_single_known() {
479 let mut dict = HashMap::new();
480 dict.insert(Name::filter(), Object::Name(Name::from("FlateDecode")));
481 let chain = resolve_filter_chain(&dict);
482 assert_eq!(chain.len(), 1);
483 assert_eq!(chain[0].0, DecodeFilter::Flate);
484 }
485
486 #[test]
490 fn test_parser_decode_validate_pipeline_unknown_filter_skipped() {
491 let mut dict = HashMap::new();
492 dict.insert(Name::filter(), Object::Name(Name::from("FooBar")));
493 let chain = resolve_filter_chain(&dict);
495 assert!(chain.is_empty());
496 }
497
498 #[test]
502 fn test_parser_decode_validate_pipeline_two_decoders() {
503 let mut dict = HashMap::new();
504 dict.insert(
505 Name::filter(),
506 Object::Array(vec![
507 Object::Name(Name::from("AHx")),
508 Object::Name(Name::from("LZWDecode")),
509 ]),
510 );
511 let chain = resolve_filter_chain(&dict);
512 assert_eq!(chain.len(), 2);
513 assert_eq!(chain[0].0, DecodeFilter::ASCIIHex);
514 assert_eq!(chain[1].0, DecodeFilter::LZW);
515 }
516
517 #[test]
521 fn test_parser_decode_validate_pipeline_five_decoders() {
522 let mut dict = HashMap::new();
523 dict.insert(
524 Name::filter(),
525 Object::Array(vec![
526 Object::Name(Name::from("ASCII85Decode")),
527 Object::Name(Name::from("A85")),
528 Object::Name(Name::from("RunLengthDecode")),
529 Object::Name(Name::from("FlateDecode")),
530 Object::Name(Name::from("RL")),
531 ]),
532 );
533 let chain = resolve_filter_chain(&dict);
534 assert_eq!(chain.len(), 5);
535 assert_eq!(chain[0].0, DecodeFilter::ASCII85);
536 assert_eq!(chain[1].0, DecodeFilter::ASCII85);
537 assert_eq!(chain[2].0, DecodeFilter::RunLength);
538 assert_eq!(chain[3].0, DecodeFilter::Flate);
539 assert_eq!(chain[4].0, DecodeFilter::RunLength);
540 }
541
542 #[test]
546 fn test_parser_decode_validate_pipeline_image_at_end() {
547 let mut dict = HashMap::new();
548 dict.insert(
549 Name::filter(),
550 Object::Array(vec![
551 Object::Name(Name::from("RunLengthDecode")),
552 Object::Name(Name::from("ASCII85Decode")),
553 Object::Name(Name::from("FlateDecode")),
554 Object::Name(Name::from("LZW")),
555 Object::Name(Name::from("DCTDecode")),
556 ]),
557 );
558 let chain = resolve_filter_chain(&dict);
559 assert_eq!(chain.len(), 5);
560 assert_eq!(chain[4].0, DecodeFilter::DCT);
561 }
562
563 #[test]
567 fn test_parser_decode_validate_pipeline_duplicate_decoders() {
568 let mut dict = HashMap::new();
569 dict.insert(
570 Name::filter(),
571 Object::Array(vec![
572 Object::Name(Name::from("ASCII85Decode")),
573 Object::Name(Name::from("ASCII85Decode")),
574 ]),
575 );
576 let chain = resolve_filter_chain(&dict);
577 assert_eq!(chain.len(), 2);
578 assert_eq!(chain[0].0, DecodeFilter::ASCII85);
579 assert_eq!(chain[1].0, DecodeFilter::ASCII85);
580 }
581
582 #[test]
588 fn test_parser_decode_validate_pipeline_two_image_decoders() {
589 let mut dict = HashMap::new();
590 dict.insert(
591 Name::filter(),
592 Object::Array(vec![
593 Object::Name(Name::from("DCTDecode")),
594 Object::Name(Name::from("CCITTFaxDecode")),
595 ]),
596 );
597 let chain = resolve_filter_chain(&dict);
599 assert_eq!(chain.len(), 2);
600 assert_eq!(chain[0].0, DecodeFilter::DCT);
601 assert_eq!(chain[1].0, DecodeFilter::CCITTFax);
602 }
603
604 #[test]
609 fn test_parser_decode_validate_pipeline_image_not_at_end() {
610 let mut dict = HashMap::new();
611 dict.insert(
612 Name::filter(),
613 Object::Array(vec![
614 Object::Name(Name::from("DCTDecode")),
615 Object::Name(Name::from("FlateDecode")),
616 ]),
617 );
618 let chain = resolve_filter_chain(&dict);
619 assert_eq!(chain.len(), 2);
620 assert_eq!(chain[0].0, DecodeFilter::DCT);
621 assert_eq!(chain[1].0, DecodeFilter::Flate);
622 }
623
624 #[test]
629 fn test_parser_decode_validate_pipeline_image_in_middle() {
630 let mut dict = HashMap::new();
631 dict.insert(
632 Name::filter(),
633 Object::Array(vec![
634 Object::Name(Name::from("FlateDecode")),
635 Object::Name(Name::from("FlateDecode")),
636 Object::Name(Name::from("DCTDecode")),
637 Object::Name(Name::from("FlateDecode")),
638 Object::Name(Name::from("FlateDecode")),
639 ]),
640 );
641 let chain = resolve_filter_chain(&dict);
642 assert_eq!(chain.len(), 5);
643 assert_eq!(chain[2].0, DecodeFilter::DCT);
644 }
645
646 #[test]
651 fn test_parser_decode_get_decoder_array_invalid_two_image() {
652 let mut dict = HashMap::new();
653 dict.insert(
654 Name::filter(),
655 Object::Array(vec![
656 Object::Name(Name::from("DCTDecode")),
657 Object::Name(Name::from("CCITTFaxDecode")),
658 ]),
659 );
660 let chain = resolve_filter_chain(&dict);
662 assert_eq!(chain.len(), 2);
663 }
664
665 #[test]
670 fn test_parser_decode_validate_pipeline_wrong_type_in_middle() {
671 let mut dict = HashMap::new();
672 dict.insert(
673 Name::filter(),
674 Object::Array(vec![
675 Object::Name(Name::from("ASCII85Decode")),
676 Object::Name(Name::from("A85")),
677 Object::Name(Name::from("RunLengthDecode")),
678 Object::Name(Name::from("FlateDecode")),
679 Object::String(rpdfium_core::PdfString::from_bytes(b"RL".to_vec())),
680 ]),
681 );
682 let chain = resolve_filter_chain(&dict);
684 assert_eq!(chain.len(), 4);
685 assert_eq!(chain[0].0, DecodeFilter::ASCII85);
686 assert_eq!(chain[1].0, DecodeFilter::ASCII85);
687 assert_eq!(chain[2].0, DecodeFilter::RunLength);
688 assert_eq!(chain[3].0, DecodeFilter::Flate);
689 }
690
691 #[test]
695 fn test_parser_decode_validate_pipeline_wrong_type_in_array() {
696 let mut dict = HashMap::new();
697 dict.insert(
698 Name::filter(),
699 Object::Array(vec![Object::String(rpdfium_core::PdfString::from_bytes(
700 b"FlateEncode".to_vec(),
701 ))]),
702 );
703 let chain = resolve_filter_chain(&dict);
705 assert!(chain.is_empty());
706 }
707
708 #[test]
717 fn test_parser_decode_validate_pipeline_with_indirect_objects() {
718 let mut dict = HashMap::new();
721 dict.insert(
722 Name::filter(),
723 Object::Array(vec![
724 Object::Name(Name::from("FlateDecode")),
725 Object::Name(Name::from("LZW")),
726 ]),
727 );
728 let chain = resolve_filter_chain(&dict);
729 assert_eq!(chain.len(), 2);
730 assert_eq!(chain[0].0, DecodeFilter::Flate);
731 assert_eq!(chain[1].0, DecodeFilter::LZW);
732
733 let mut dict2 = HashMap::new();
735 dict2.insert(
736 Name::filter(),
737 Object::Array(vec![
738 Object::Name(Name::from("RunLengthDecode")),
739 Object::Name(Name::from("ASCII85Decode")),
740 Object::Name(Name::from("FlateDecode")),
741 Object::Name(Name::from("LZW")),
742 Object::Name(Name::from("DCTDecode")),
743 ]),
744 );
745 let chain2 = resolve_filter_chain(&dict2);
746 assert_eq!(chain2.len(), 5);
747 assert_eq!(chain2[4].0, DecodeFilter::DCT);
748 }
749}