"""This class provides all photoshop API core functions.""" # Import built-in modules import os import platform import winreg from typing import Any from typing import List # Import third-party modules from comtypes.client import CreateObject # Import local modules from . import constants from .errors import PhotoshopPythonAPIError class Photoshop(object): _root = "Photoshop" REG_PATH = "SOFTWARE\\Adobe\\Photoshop" _object_name = "Application" object_name = None def __init__(self, ps_version: str = None, parent: Any = None): self._program_name = None self._has_parent = False version_mappings = constants.PHOTOSHOP_VERSION_MAPPINGS self.photoshop_version = os.getenv("PS_VERSION", ps_version) self.app_id = version_mappings.get( self.photoshop_version, self._get_program_id() ) try: self.app = self.instance_app(self.app_id) except OSError: try: self.app = self.instance_app(self._get_program_id()) except OSError: raise PhotoshopPythonAPIError( "Please check if you have " "Photoshop installed correctly.", ) if parent: self.adobe = self.app self.app = parent self._has_parent = True @property def typename(self): return self.__class__.__name__ def __call__(self, *args, **kwargs): return self.app def __str__(self): return f"{self.__class__.__name__} <{self._program_name}>" def __repr__(self): return self def __getattribute__(self, item): try: return super().__getattribute__(item) except AttributeError: return getattr(self.app, item) @staticmethod def open_key(key): """Open the register key. Args: key (str): The key of register. Returns: str: The handle to the specified key. """ machine_type = platform.machine() mappings = {"AMD64": winreg.KEY_WOW64_64KEY} return winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, key, access=winreg.KEY_READ | mappings.get(machine_type, winreg.KEY_WOW64_32KEY), ) def get_application_path(self): """str: The absolute path of Photoshop installed location.""" key = self.open_key(f"{self.REG_PATH}\\{self.get_program_id()}") return winreg.QueryValueEx(key, "ApplicationPath")[0] def get_plugin_path(self): """str: The absolute plugin path of Photoshop.""" return os.path.join(self.get_application_path(), "Plug-ins") def get_presets_path(self): """str: The absolute presets path of Photoshop.""" return os.path.join(self.get_application_path(), "Presets") def get_script_path(self): """str: The absolute scripts path of Photoshop.""" return os.path.join(self.get_presets_path(), "Scripts") def instance_app(self, ps_id): naming_space = [self._root] if not self.object_name: naming_space.append(self._object_name) else: naming_space.append(self.object_name) naming_space.append(ps_id) self._program_name = self._assemble_program_name(naming_space) return CreateObject(self._program_name, dynamic=True) def get_program_id(self) -> str: key = self.open_key(self.REG_PATH) return winreg.EnumKey(key, 0) def _get_program_id(self) -> str: self.app_id = self.get_program_id().split(".")[0] return self.app_id @staticmethod def _assemble_program_name(names: List[str]): """Assemble program name of Photoshop. Args: names (list of str): The name to be assembled. .e.g: [ 'Photoshop', 'ActionDescriptor', '140' ] Returns: str: Assembled name. Examples: Photoshop.ActionDescriptor Photoshop.ActionDescriptor.140 Photoshop.ActionList Photoshop.ActionList.140 Photoshop.ActionReference Photoshop.ActionReference.140 Photoshop.Application Photoshop.Application.140 Photoshop.BatchOptions Photoshop.BatchOptions.140 Photoshop.BitmapConversionOptions Photoshop.BMPSaveOptions Photoshop.BMPSaveOptions.140 Photoshop.CameraRAWOpenOptions Photoshop.CameraRAWOpenOptions.140 """ return ".".join(names) def eval_javascript(self, javascript, Arguments=None, ExecutionMode=None): executor = self.app if self._has_parent: executor = self.adobe return executor.doJavaScript(javascript, Arguments, ExecutionMode)