# Path: gymnast/pdf_types/common.py
# class PdfType(object):
#     """Abstract base class for PDF objects"""
#     def pdf_encode(self):
#         """Translate the object into bytes in PDF format"""
#         raise NotImplementedError
#     @property
#     def value(self):
#         """Objects, references, and such will override this in clever
#         ways."""
#         return self
#     @property
#     def parsed_object(self):
#         """Objects, references, and such will override this in clever
#         ways."""
#         return self
# 
# Path: gymnast/filters/stream_filter.py
# class StreamFilter(object):
#     """PDF stream filter stream dispatcher.  Stream filters are registered by
#     calling PdfOperation.register() and passing a subclass of StreamFilterBase.
# 
#     Information on filters at can be found at
#     https://partners.adobe.com/public/developer/en/ps/sdk/TN5603.Filters.pdf"""
#     # Nothing to see here.  Pay no attention to that man behind the curtain.
#     _filters    = {}
#     _nop_filter = StreamFilterBase('NOPFilter', lambda x: x)
# 
#     @classmethod
#     def register(cls, filter_name, decoder, eod=None, encoder=None):
#         """Register a new stream filter"""
#         new_filt = StreamFilterBase(filter_name, decoder, eod, encoder)
#         cls._filters[filter_name] = new_filt
# 
#     @classmethod
#     def __getitem__(cls, filter_name):
#         filter_name = ensure_str(filter_name)
#         try:
#             return cls._filters[filter_name]
#         except KeyError:
#             return cls._nop_filter
# 
# Path: gymnast/misc.py
# def ensure_list(val):
#     """Converts the argument to a list, wrapping in [] if needed"""
#     return val if isinstance(val, list) else [val]


from functools import partial, reduce
from .common   import PdfType
from ..filters import StreamFilter
from ..misc    import ensure_list
            self._filedata = True

        if self._filter_key in header:
            self._decoded  = False
        else:
            self._decoded  = True
            self._decoded_data = self._data
    @property
    def header(self):
        """Stream header"""
        return self._header
    @property
    def _filter_key(self):
        return 'FFilter'      if self._filedata else 'Filter'
    @property
    def _params_key(self):
        return 'FDecodeParms' if self._filedata else 'DecodeParms'

    def decode(self):
        """Decode the data in the stream by sequentially applying the
        filters with their parameters"""
        if self._decoded:
            return self._decoded_data
        # Need to use self._filter_key because, for some reason beyond my
        # grasp, the key changes when the stream data is external
        # Also, since these may be lists, let's make that happen
        filters = ensure_list(self._header.get(self._filter_key, []))
        params  = ensure_list(self._header.get(self._params_key, []))
        if not params:
            params = [{} for f in filters]

# Path: gymnast/pdf_types/common.py
# class PdfType(object):
#     """Abstract base class for PDF objects"""
#     def pdf_encode(self):
#         """Translate the object into bytes in PDF format"""
#         raise NotImplementedError
#     @property
#     def value(self):
#         """Objects, references, and such will override this in clever
#         ways."""
#         return self
#     @property
#     def parsed_object(self):
#         """Objects, references, and such will override this in clever
#         ways."""
#         return self
# 
# Path: gymnast/filters/stream_filter.py
# class StreamFilter(object):
#     """PDF stream filter stream dispatcher.  Stream filters are registered by
#     calling PdfOperation.register() and passing a subclass of StreamFilterBase.
# 
#     Information on filters at can be found at
#     https://partners.adobe.com/public/developer/en/ps/sdk/TN5603.Filters.pdf"""
#     # Nothing to see here.  Pay no attention to that man behind the curtain.
#     _filters    = {}
#     _nop_filter = StreamFilterBase('NOPFilter', lambda x: x)
# 
#     @classmethod
#     def register(cls, filter_name, decoder, eod=None, encoder=None):
#         """Register a new stream filter"""
#         new_filt = StreamFilterBase(filter_name, decoder, eod, encoder)
#         cls._filters[filter_name] = new_filt
# 
#     @classmethod
#     def __getitem__(cls, filter_name):
#         filter_name = ensure_str(filter_name)
#         try:
#             return cls._filters[filter_name]
#         except KeyError:
#             return cls._nop_filter
# 
# Path: gymnast/misc.py
# def ensure_list(val):
#     """Converts the argument to a list, wrapping in [] if needed"""
#     return val if isinstance(val, list) else [val]


# Path: gymnast/pdf_types/streams.py
from functools import partial, reduce
from .common   import PdfType
from ..filters import StreamFilter
from ..misc    import ensure_list

            self._filedata = True

        if self._filter_key in header:
            self._decoded  = False
        else:
            self._decoded  = True
            self._decoded_data = self._data
    @property
    def header(self):
        """Stream header"""
        return self._header
    @property
    def _filter_key(self):
        return 'FFilter'      if self._filedata else 'Filter'
    @property
    def _params_key(self):
        return 'FDecodeParms' if self._filedata else 'DecodeParms'

    def decode(self):
        """Decode the data in the stream by sequentially applying the
        filters with their parameters"""
        if self._decoded:
            return self._decoded_data
        # Need to use self._filter_key because, for some reason beyond my
        # grasp, the key changes when the stream data is external
        # Also, since these may be lists, let's make that happen
        filters = ensure_list(self._header.get(self._filter_key, []))
        params  = ensure_list(self._header.get(self._params_key, []))
        if not params:
            params = [{} for f in filters]

        composed_filters = chain_funcs((partial(StreamFilter[f].decode, **p)