@@ -112,27 +112,6 @@ impl fmt::Display for Filter {
112112 }
113113}
114114
115- /// Per-filter decoded parameters extracted from `/DecodeParms`.
116- ///
117- /// Each variant holds the strongly-typed parameters for one filter in the
118- /// chain. Variants are added as new filters gain parameter support.
119- #[ derive( Debug , Clone ) ]
120- pub ( crate ) enum DecodeParms {
121- /// No parameters needed or provided for this filter.
122- None ,
123- /// Parameters for the `CCITTFaxDecode` filter.
124- CcittFax ( CCITTFaxParams ) ,
125- /// Parameters for `LZWDecode`: EarlyChange flag + optional predictor.
126- Lzw {
127- early_change : bool ,
128- predictor : PredictorParams ,
129- } ,
130- /// Predictor parameters for `FlateDecode`.
131- Flate { predictor : PredictorParams } ,
132- /// JBIG2 globals stream data, if present.
133- Jbig2 { globals : Option < Vec < u8 > > } ,
134- }
135-
136115/// Methods for parsing the `/Filter` entry from a PDF dictionary.
137116impl Filter {
138117 const KEY : & ' static str = "Filter" ;
@@ -285,32 +264,58 @@ pub fn decode_data_with_resolver(
285264 return Ok ( data) ;
286265 } ;
287266
288- let decode_params = parse_decode_params ( dictionary, filters, objects) ?;
267+ let decode_parms = dictionary
268+ . get ( "DecodeParms" )
269+ . map ( |entry| objects. resolve_object ( entry) )
270+ . transpose ( ) ?;
271+
272+ for ( index, filter) in filters. iter ( ) . enumerate ( ) {
273+ let param_dict = match decode_parms {
274+ None => None ,
275+ Some ( ObjectVariant :: Dictionary ( dictionary) ) => Some ( dictionary. as_ref ( ) ) ,
276+ Some ( ObjectVariant :: Array ( array) ) => {
277+ array. as_slice ( ) . optional_dictionary ( index, objects) ?
278+ }
279+ Some ( other) => {
280+ return Err ( FilterError :: from (
281+ pdf_object:: error:: ObjectError :: TypeMismatch (
282+ "Dictionary or Array" ,
283+ other. name ( ) ,
284+ ) ,
285+ ) ) ;
286+ }
287+ } ;
289288
290- for ( filter, params) in filters. iter ( ) . zip ( decode_params. iter ( ) ) {
291289 match filter {
292290 Filter :: FlateDecode => {
293291 let decoded = Filter :: decode_flate ( data. as_slice ( ) ) ?;
294- let decoded = match params {
295- DecodeParms :: Flate { predictor } if !predictor. is_none ( ) => {
296- crate :: predictor:: apply_predictor ( & decoded, predictor) ?
297- }
298- _ => decoded,
292+ let predictor = match param_dict {
293+ Some ( dictionary) => PredictorParams :: from_dictionary ( dictionary, objects) ?,
294+ None => PredictorParams :: default ( ) ,
295+ } ;
296+ let decoded = if predictor. is_none ( ) {
297+ decoded
298+ } else {
299+ crate :: predictor:: apply_predictor ( & decoded, & predictor) ?
299300 } ;
300301 data = Arc :: new ( decoded) ;
301302 }
302303 Filter :: LZWDecode => {
303- let ( early_change, predictor) = match params {
304- DecodeParms :: Lzw {
305- early_change,
306- predictor,
307- } => ( * early_change, Some ( predictor) ) ,
308- _ => ( true , None ) ,
304+ let ( early_change, predictor) = match param_dict {
305+ Some ( dictionary) => (
306+ dictionary
307+ . optional_number ( "EarlyChange" , objects) ?
308+ . unwrap_or ( 1 )
309+ != 0 ,
310+ PredictorParams :: from_dictionary ( dictionary, objects) ?,
311+ ) ,
312+ None => ( true , PredictorParams :: default ( ) ) ,
309313 } ;
310314 let decoded = crate :: lzw:: decode ( data. as_slice ( ) , early_change) ?;
311- let decoded = match predictor {
312- Some ( p) if !p. is_none ( ) => crate :: predictor:: apply_predictor ( & decoded, p) ?,
313- _ => decoded,
315+ let decoded = if predictor. is_none ( ) {
316+ decoded
317+ } else {
318+ crate :: predictor:: apply_predictor ( & decoded, & predictor) ?
314319 } ;
315320 data = Arc :: new ( decoded) ;
316321 }
@@ -336,19 +341,20 @@ pub fn decode_data_with_resolver(
336341 }
337342 Filter :: JBIG2Decode => {
338343 let ( width, height) = resolve_jbig2_dimensions ( dictionary, objects) ?;
339- let globals = match params {
340- DecodeParms :: Jbig2 { globals } => globals . as_deref ( ) ,
341- _ => None ,
344+ let globals = match param_dict {
345+ Some ( dictionary ) => resolve_jbig2_globals ( dictionary , objects ) ? ,
346+ None => None ,
342347 } ;
343- let decoded = pdf_jbig2:: decode ( data. as_slice ( ) , width, height, globals) ?;
348+ let decoded =
349+ pdf_jbig2:: decode ( data. as_slice ( ) , width, height, globals. as_deref ( ) ) ?;
344350 data = Arc :: new ( decoded) ;
345351 }
346352 Filter :: CCITTFaxDecode => {
347- let ccitt_params = match params {
348- DecodeParms :: CcittFax ( p ) => p ,
349- _ => & CCITTFaxParams :: DEFAULT ,
353+ let ccitt_params = match param_dict {
354+ Some ( dictionary ) => CCITTFaxParams :: from_dictionary ( dictionary , objects ) ? ,
355+ None => CCITTFaxParams :: default ( ) ,
350356 } ;
351- let decoded = pdf_ccitt:: decode ( data. as_slice ( ) , ccitt_params) ?;
357+ let decoded = pdf_ccitt:: decode ( data. as_slice ( ) , & ccitt_params) ?;
352358 data = Arc :: new ( decoded) ;
353359 }
354360 Filter :: Unsupported ( name) => {
@@ -383,110 +389,6 @@ pub fn decode(stream: &StreamObject) -> Result<Arc<Vec<u8>>, FilterError> {
383389 decode_with_resolver ( stream, & objects)
384390}
385391
386- /// Parses the `/DecodeParms` entry from a stream dictionary into a
387- /// [`Vec<DecodeParms>`] aligned 1-to-1 with `filters`.
388- ///
389- /// Per PDF spec §7.3.8.2, `/DecodeParms` is either a single dictionary (when
390- /// there is one filter) or an array of dictionaries (one per filter).
391- fn parse_decode_params (
392- dict : & Dictionary ,
393- filters : & [ Filter ] ,
394- objects : & dyn ObjectResolver ,
395- ) -> Result < Vec < DecodeParms > , FilterError > {
396- let param_dicts = resolve_decode_parms_dicts ( dict, filters, objects) ?;
397-
398- filters
399- . iter ( )
400- . zip ( param_dicts. iter ( ) . copied ( ) )
401- . map ( |( filter, param_dict) | decode_parms_for_filter ( filter, param_dict, objects) )
402- . collect ( )
403- }
404-
405- fn resolve_decode_parms_dicts < ' a > (
406- dict : & ' a Dictionary ,
407- filters : & [ Filter ] ,
408- objects : & ' a dyn ObjectResolver ,
409- ) -> Result < Vec < Option < & ' a Dictionary > > , FilterError > {
410- let Some ( entry) = dict. get ( "DecodeParms" ) else {
411- return Ok ( vec ! [ None ; filters. len( ) ] ) ;
412- } ;
413-
414- let resolved = objects. resolve_object ( entry) ?;
415-
416- match resolved {
417- ObjectVariant :: Dictionary ( d) => Ok ( vec ! [ Some ( d. as_ref( ) ) ; filters. len( ) ] ) ,
418- ObjectVariant :: Array ( arr) => {
419- resolve_decode_parms_array ( arr. as_slice ( ) , filters. len ( ) , objects)
420- }
421- other => Err ( FilterError :: from (
422- pdf_object:: error:: ObjectError :: TypeMismatch ( "Dictionary or Array" , other. name ( ) ) ,
423- ) ) ,
424- }
425- }
426-
427- fn resolve_decode_parms_array < ' a > (
428- arr : & ' a [ ObjectVariant ] ,
429- filter_count : usize ,
430- objects : & ' a dyn ObjectResolver ,
431- ) -> Result < Vec < Option < & ' a Dictionary > > , FilterError > {
432- ( 0 ..filter_count)
433- . map ( |index| {
434- let Some ( item) = arr. get ( index) else {
435- return Ok ( None ) ;
436- } ;
437-
438- let resolved = objects. resolve_object ( item) ?;
439- match resolved {
440- ObjectVariant :: Dictionary ( d) => Ok ( Some ( d. as_ref ( ) ) ) ,
441- ObjectVariant :: Null => Ok ( None ) ,
442- other => Err ( FilterError :: from (
443- pdf_object:: error:: ObjectError :: TypeMismatch ( "Dictionary" , other. name ( ) ) ,
444- ) ) ,
445- }
446- } )
447- . collect ( )
448- }
449-
450- fn decode_parms_for_filter (
451- filter : & Filter ,
452- param_dict : Option < & Dictionary > ,
453- objects : & dyn ObjectResolver ,
454- ) -> Result < DecodeParms , FilterError > {
455- let params = match ( filter, param_dict) {
456- ( Filter :: CCITTFaxDecode , Some ( d) ) => {
457- let p = CCITTFaxParams :: from_dictionary ( d, objects) ?;
458- DecodeParms :: CcittFax ( p)
459- }
460- ( Filter :: CCITTFaxDecode , None ) => DecodeParms :: CcittFax ( CCITTFaxParams :: default ( ) ) ,
461- ( Filter :: LZWDecode , Some ( d) ) => {
462- let early_change = d. optional_number ( "EarlyChange" , objects) ?. unwrap_or ( 1 ) != 0 ;
463- let predictor = PredictorParams :: from_dictionary ( d, objects) ?;
464- DecodeParms :: Lzw {
465- early_change,
466- predictor,
467- }
468- }
469- ( Filter :: LZWDecode , None ) => DecodeParms :: Lzw {
470- early_change : true ,
471- predictor : PredictorParams :: default ( ) ,
472- } ,
473- ( Filter :: FlateDecode , Some ( d) ) => {
474- let predictor = PredictorParams :: from_dictionary ( d, objects) ?;
475- DecodeParms :: Flate { predictor }
476- }
477- ( Filter :: FlateDecode , None ) => DecodeParms :: Flate {
478- predictor : PredictorParams :: default ( ) ,
479- } ,
480- ( Filter :: JBIG2Decode , Some ( d) ) => DecodeParms :: Jbig2 {
481- globals : resolve_jbig2_globals ( d, objects) ?,
482- } ,
483- ( Filter :: JBIG2Decode , None ) => DecodeParms :: Jbig2 { globals : None } ,
484- _ => DecodeParms :: None ,
485- } ;
486-
487- Ok ( params)
488- }
489-
490392fn resolve_jbig2_dimensions (
491393 dict : & Dictionary ,
492394 objects : & dyn ObjectResolver ,
@@ -762,6 +664,81 @@ mod tests {
762664 assert_eq ! ( decoded. as_ref( ) , b"hello" ) ;
763665 }
764666
667+ #[ test]
668+ fn test_decode_with_sparse_decode_parms_array ( ) {
669+ use flate2:: { Compression , write:: ZlibEncoder } ;
670+ use std:: fmt:: Write as _;
671+ use std:: io:: Write as _;
672+
673+ let mut encoder = ZlibEncoder :: new ( Vec :: new ( ) , Compression :: default ( ) ) ;
674+ encoder. write_all ( b"hello" ) . expect ( "zlib write failed" ) ;
675+ let compressed = encoder. finish ( ) . expect ( "zlib finish failed" ) ;
676+
677+ let mut encoded = String :: new ( ) ;
678+ for byte in compressed {
679+ write ! ( & mut encoded, "{byte:02x}" ) . expect ( "hex write failed" ) ;
680+ }
681+ encoded. push ( '>' ) ;
682+
683+ let dictionary = Dictionary :: new ( BTreeMap :: from ( [
684+ (
685+ "Filter" . to_string ( ) ,
686+ ObjectVariant :: Array ( vec ! [
687+ ObjectVariant :: Name ( b"ASCIIHexDecode" . to_vec( ) ) ,
688+ ObjectVariant :: Name ( b"FlateDecode" . to_vec( ) ) ,
689+ ] ) ,
690+ ) ,
691+ (
692+ "DecodeParms" . to_string ( ) ,
693+ ObjectVariant :: Array ( vec ! [ ObjectVariant :: Null ] ) ,
694+ ) ,
695+ ] ) ) ;
696+
697+ let decoded = decode_data_with_resolver (
698+ & dictionary,
699+ Arc :: new ( encoded. into_bytes ( ) ) ,
700+ & PassthroughResolver ,
701+ )
702+ . expect ( "decode failed" ) ;
703+
704+ assert_eq ! ( decoded. as_ref( ) , b"hello" ) ;
705+ }
706+
707+ #[ test]
708+ fn test_decode_parms_array_accepts_indirect_dictionary_object ( ) {
709+ use flate2:: { Compression , write:: ZlibEncoder } ;
710+ use pdf_object:: indirect_object:: IndirectObject ;
711+ use std:: io:: Write ;
712+
713+ let mut encoder = ZlibEncoder :: new ( Vec :: new ( ) , Compression :: default ( ) ) ;
714+ encoder. write_all ( b"hello" ) . expect ( "zlib write failed" ) ;
715+ let compressed = encoder. finish ( ) . expect ( "zlib finish failed" ) ;
716+
717+ let decode_parms = ObjectVariant :: IndirectObject ( Box :: new ( IndirectObject :: new (
718+ 2 ,
719+ 0 ,
720+ Some ( ObjectVariant :: Dictionary ( Box :: new ( Dictionary :: new (
721+ BTreeMap :: new ( ) ,
722+ ) ) ) ) ,
723+ ) ) ) ;
724+ let dictionary = Dictionary :: new ( BTreeMap :: from ( [
725+ (
726+ "Filter" . to_string ( ) ,
727+ ObjectVariant :: Name ( b"FlateDecode" . to_vec ( ) ) ,
728+ ) ,
729+ (
730+ "DecodeParms" . to_string ( ) ,
731+ ObjectVariant :: Array ( vec ! [ decode_parms] ) ,
732+ ) ,
733+ ] ) ) ;
734+
735+ let decoded =
736+ decode_data_with_resolver ( & dictionary, Arc :: new ( compressed) , & PassthroughResolver )
737+ . expect ( "decode failed" ) ;
738+
739+ assert_eq ! ( decoded. as_ref( ) , b"hello" ) ;
740+ }
741+
765742 #[ test]
766743 fn test_decode_run_length_stream ( ) {
767744 let mut dict = BTreeMap :: new ( ) ;
0 commit comments