# Source code for psd_tools.psd

    """
    Low-level API that translates binary data to Python structure.

    All the data structure in this subpackage inherits from one of the object
    defined in :py:mod:`psd_tools.psd.base` module.
    """

    from __future__ import absolute_import, unicode_literals

    import logging

    import attr

    from .base import BaseElement
    from .color_mode_data import ColorModeData
    from .header import FileHeader
    from .image_data import ImageData
    from .image_resources import ImageResources
    from .layer_and_mask import LayerAndMaskInformation

    logger = logging.getLogger(__name__)

    @attr.s(repr=False, slots=True)
    class PSD(BaseElement):
        """
        Low-level PSD file structure that resembles the specification_.

        .. _specification: https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/

        Example::

            from psd_tools.psd import PSD

            with open(input_file, 'rb') as f:
                psd = PSD.read(f)

            with open(output_file, 'wb') as f:
                psd.write(f)

        .. py:attribute:: header

            See :py:class:`.FileHeader`.

        .. py:attribute:: color_mode_data

            See :py:class:`.ColorModeData`.

        .. py:attribute:: image_resources

            See :py:class:`.ImageResources`.

        .. py:attribute:: layer_and_mask_information

            See :py:class:`.LayerAndMaskInformation`.

        .. py:attribute:: image_data

            See :py:class:`.ImageData`.
        """

        header = attr.ib(factory=FileHeader)
        color_mode_data = attr.ib(factory=ColorModeData)
        image_resources = attr.ib(factory=ImageResources)
        layer_and_mask_information = attr.ib(factory=LayerAndMaskInformation)
        image_data = attr.ib(factory=ImageData)

        @classmethod
        def read(cls, fp, encoding="macroman", **kwargs):
            header = FileHeader.read(fp)
            logger.debug("read %s" % header)
            return cls(
                header,
                ColorModeData.read(fp),
                ImageResources.read(fp, encoding),
                LayerAndMaskInformation.read(fp, encoding, header.version),
                ImageData.read(fp),
            )

        def write(self, fp, encoding="macroman", **kwargs):
            logger.debug("writing %s" % self.header)
            written = self.header.write(fp)
            written += self.color_mode_data.write(fp)
            written += self.image_resources.write(fp, encoding)
            written += self.layer_and_mask_information.write(
                fp, encoding, self.header.version, **kwargs
            )
            written += self.image_data.write(fp)
            return written

        def _iter_layers(self):
            """
            Iterate over (layer_record, channel_data) pairs.
            """
            layer_info = self._get_layer_info()
            if layer_info is not None:
                records = layer_info.layer_records
                channel_data = layer_info.channel_image_data
                if records is not None and channel_data is not None:
                    for record, channels in zip(records, channel_data):
                        yield record, channels

        def _get_layer_info(self):
            from psd_tools.constants import Tag

            tagged_blocks = self.layer_and_mask_information.tagged_blocks
            if tagged_blocks is not None:
                for key in (Tag.LAYER_16, Tag.LAYER_32):
                    if key in tagged_blocks:
                        return tagged_blocks.get_data(key)
            return self.layer_and_mask_information.layer_info