Source code for psd_tools.api.smart_object

"""
Smart object module.
"""
from __future__ import absolute_import, unicode_literals

import contextlib
import io
import logging
import os

from psd_tools.constants import Tag

logger = logging.getLogger(__name__)


[docs] class SmartObject(object): """ Smart object that represents embedded or external file. Smart objects are attached to :py:class:`~psd_tools.api.layers.SmartObjectLayer`. """ def __init__(self, layer): self._config = None for key in (Tag.SMART_OBJECT_LAYER_DATA1, Tag.SMART_OBJECT_LAYER_DATA2): if key in layer.tagged_blocks: self._config = layer.tagged_blocks.get_data(key) break self._data = None for key in ( Tag.LINKED_LAYER1, Tag.LINKED_LAYER2, Tag.LINKED_LAYER3, Tag.LINKED_LAYER_EXTERNAL, ): if key in layer._psd.tagged_blocks: data = layer._psd.tagged_blocks.get_data(key) for item in data: if item.uuid == self.unique_id: self._data = item break if self._data: break @property def kind(self): """Kind of the link, 'data', 'alias', or 'external'.""" return self._data.kind.name.lower() @property def filename(self): """Original file name of the object.""" return self._data.filename.strip("\x00")
[docs] @contextlib.contextmanager def open(self, external_dir=None): """ Open the smart object as binary IO. :param external_dir: Path to the directory of the external file. Example:: with layer.smart_object.open() as f: data = f.read() """ if self.kind == "data": with io.BytesIO(self._data.data) as f: yield f elif self.kind == "external": filepath = self._data.linked_file[b"fullPath"].value filepath = filepath.replace("\x00", "").replace("file://", "") if not os.path.exists(filepath): filepath = self._data.linked_file[b"relPath"].value filepath = filepath.replace("\x00", "") if external_dir is not None: filepath = os.path.join(external_dir, filepath) with open(filepath, "rb") as f: yield f else: raise NotImplementedError("alias is not supported.")
@property def data(self): """Embedded file content, or empty if kind is `external` or `alias`""" if self.kind == "data": return self._data.data else: with self.open() as f: return f.read() @property def unique_id(self): """UUID of the object.""" return self._config.data.get(b"Idnt").value.strip("\x00") @property def filesize(self): """File size of the object.""" if self.kind == "data": return len(self._data.data) return self._data.filesize @property def filetype(self): """Preferred file extension, such as `jpg`.""" return self._data.filetype.lower().strip().decode("ascii")
[docs] def is_psd(self): """Return True if the file is embedded PSD/PSB.""" return self.filetype in ("8bpb", "8bps")
@property def warp(self): """Warp parameters.""" return self._config.data.get(b"warp") @property def resolution(self): """Resolution of the object.""" return self._config.data.get(b"Rslt").value
[docs] def save(self, filename=None): """ Save the smart object to a file. :param filename: File name to export. If None, use the embedded name. """ if filename is None: filename = self.filename with open(filename, "wb") as f: f.write(self.data)
def __repr__(self): return "SmartObject(%r kind=%r type=%r size=%s)" % ( self.filename, self.kind, self.filetype, self.filesize, )