Source code for psd_tools.api.smart_object

"""
Smart object module.
"""
from __future__ import absolute_import, unicode_literals
import contextlib
import logging
import io
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 )