Skip to content

PhotoshopHandler

src.utils.adobe.PhotoshopHandler

Bases: ApplicationHandler

Wrapper for a single global Photoshop Application object equipped with soft loading, caching mechanisms, environment settings, and more.

Source code in src\utils\adobe.py
class PhotoshopHandler(ApplicationHandler):
    """Wrapper for a single global Photoshop Application object equipped with soft loading,
    caching mechanisms, environment settings, and more."""
    DIMS_1200 = (3264, 4440)
    DIMS_800 = (2176, 2960)
    DIMS_600 = (1632, 2220)
    _instance = None

    def __new__(cls, env: Optional[Any] = None) -> 'PhotoshopHandler':
        """Always return the same Photoshop Application instance on successive calls.

        Args:
            env (AppEnvironment): Global app environment containing relevant env variables.

        Returns:
            The existing or newly created PhotoshopHandler instance.
        """
        # Use existing Photoshop instance or create new one
        if cls._instance is None:
            try:
                cls._instance = super().__new__(cls)
            except PS_EXCEPTIONS:
                cls._instance = super(Photoshop, cls).__new__(cls)

        # Establish the app environment object
        return cls._instance

    """
    * Managing the application object
    """

    def refresh_app(self):
        """Replace the existing Photoshop Application instance with a new one."""
        if not self.is_running():
            try:
                # Load Photoshop and default preferences
                super(PhotoshopHandler, self).__init__(env=self._env)
                self.preferences.rulerUnits = Units.Pixels
                self.preferences.typeUnits = Units.Points
            except Exception as e:
                # Photoshop is either busy or unresponsive
                return OSError(get_photoshop_error_message(e))
        return

    """
    * Class Methods
    """

    @classmethod
    def is_running(cls) -> bool:
        """Check if the current Photoshop Application instance is still valid."""
        with suppress(Exception):
            _ = cls._instance.version
            return True
        return False

    """
    * Action Descriptor ID Conversions
    """

    @cache
    def charIDToTypeID(self, index: str) -> int:
        """Caching handler for charIDToTypeID.

        Args:
            index: Char ID to convert to Type ID.

        Returns:
            Type ID converted from Char ID.
        """
        return super().charIDToTypeID(index)

    @cache
    def CharIDToTypeID(self, index: str) -> int:
        """Uppercase redirect for charIDToTypeID."""
        return self.charIDToTypeID(index)

    @cache
    def cID(self, index: str) -> int:
        """Shorthand redirect for charIDToTypeID."""
        return self.charIDToTypeID(index)

    @cache
    def typeIDToCharID(self, index: int) -> str:
        """Caching handler for typeIDToCharID.

        Args:
            index: Type ID to convert to Char ID.

        Returns:
            Character representation of Type ID.
        """
        return super().typeIDToCharID(index)

    @cache
    def t2c(self, index: int) -> str:
        """Shorthand redirect for typeIDToCharID."""
        return self.typeIDToCharID(index)

    """
    * String ID Conversions
    """

    @cache
    def stringIDToTypeID(self, index: str) -> int:
        """Caching handler for stringIDToTypeID.

        Args:
            index: String ID to convert to Type ID.

        Returns:
            Type ID converted from string ID.
        """
        return super().stringIDToTypeID(index)

    @cache
    def StringIDToTypeID(self, index: str) -> int:
        """Uppercase redirect for stringIDTotypeID."""
        return self.stringIDToTypeID(index)

    @cache
    def sID(self, index: str) -> int:
        """Shorthand redirect for stringIDToTypeID."""
        return self.stringIDToTypeID(index)

    @cache
    def typeIDToStringID(self, index: int) -> str:
        """Caching handler for typeIDToStringID.

        Args:
            index: Type ID to convert to String ID.

        Returns:
            str: String representation of Type ID.
        """
        return super().typeIDToStringID(index)

    @cache
    def t2s(self, index: int) -> str:
        """Shorthand redirect for typeIDToStringID."""
        return self.typeIDToStringID(index)

    """
    * String / Char ID Conversions
    """

    @cache
    def charIDToStringID(self, index: int) -> str:
        """Converts a Char ID to a String ID.

        Args:
            index: Char ID to convert to String ID.

        Returns:
            str: String representation of Char ID.
        """
        return self.typeIDToStringID(
            self.charIDToTypeID(index))

    @cache
    def stringIDToCharID(self, index: int) -> str:
        """Converts a String ID to a Char ID.

        Args:
            index: String ID to convert to Char ID.

        Returns:
            str: Character representation of String ID.
        """
        return self.typeIDToCharID(
            self.stringIDToTypeID(index))

    """
    * Executing Action Descriptors
    """

    def executeAction(
        self, event_id: int,
        descriptor: ActionDescriptor,
        dialogs: DialogModes = DialogModes.DisplayNoDialogs
    ) -> Any:
        """Middleware to allow all dialogs when an error occurs upon calling executeAction in development mode.

        Args:
            event_id: Action descriptor event ID.
            descriptor: Main action descriptor tree to execute.
            dialogs: DialogMode which governs whether to display dialogs.

        Returns:
            Result of the action descriptor execution.
        """
        if self.is_error_dialog_enabled():
            # Allow error dialogs if enabled in the app environment
            return super().executeAction(event_id, descriptor, DialogModes.DisplayErrorDialogs)
        return super().executeAction(event_id, descriptor, dialogs)

    def ExecuteAction(
            self, event_id: int,
            descriptor: ActionDescriptor,
            dialogs: DialogModes = DialogModes.DisplayNoDialogs
    ) -> Any:
        """Utility definition rerouting to original `executeAction`."""
        self.executeAction(event_id, descriptor, dialogs)

    """
    * Version Checks
    """

    @cache
    def supports_target_text_replace(self) -> bool:
        """bool: Checks if Photoshop version supports targeted text replacement."""
        return self.version_meets_requirement('22.0.0')

    @cache
    def supports_webp(self) -> bool:
        """bool: Checks if Photoshop version supports WEBP files."""
        return self.version_meets_requirement('23.2.0')

    @cache
    def supports_generative_fill(self) -> bool:
        """Checks if Photoshop version supports Generative Fill."""
        return self.version_meets_requirement('24.6.0')

    def version_meets_requirement(self, value: str) -> bool:
        """Checks if Photoshop version meets or exceeds required value.

        Args:
            value: Minimum version string required.
        """
        if parse(self.version) >= parse(value):
            return True
        return False

    """
    * Dimensions
    """

    @cache
    def scale_by_dpi(self, value: Union[int, float]) -> int:
        """Scales a value by comparing document DPI to ideal DPI.

        Args:
            value: Integer or float value to adjust by DPI ratio.

        Returns:
            Adjusted value as an integer.
        """
        return int((self.activeDocument.width / 3264) * value)

Functions

CharIDToTypeID(index: str) -> int

Uppercase redirect for charIDToTypeID.

Source code in src\utils\adobe.py
@cache
def CharIDToTypeID(self, index: str) -> int:
    """Uppercase redirect for charIDToTypeID."""
    return self.charIDToTypeID(index)

ExecuteAction(event_id: int, descriptor: ActionDescriptor, dialogs: DialogModes = DialogModes.DisplayNoDialogs) -> Any

Utility definition rerouting to original executeAction.

Source code in src\utils\adobe.py
def ExecuteAction(
        self, event_id: int,
        descriptor: ActionDescriptor,
        dialogs: DialogModes = DialogModes.DisplayNoDialogs
) -> Any:
    """Utility definition rerouting to original `executeAction`."""
    self.executeAction(event_id, descriptor, dialogs)

StringIDToTypeID(index: str) -> int

Uppercase redirect for stringIDTotypeID.

Source code in src\utils\adobe.py
@cache
def StringIDToTypeID(self, index: str) -> int:
    """Uppercase redirect for stringIDTotypeID."""
    return self.stringIDToTypeID(index)

cID(index: str) -> int

Shorthand redirect for charIDToTypeID.

Source code in src\utils\adobe.py
@cache
def cID(self, index: str) -> int:
    """Shorthand redirect for charIDToTypeID."""
    return self.charIDToTypeID(index)

charIDToStringID(index: int) -> str

Converts a Char ID to a String ID.

Parameters:

Name Type Description Default
index int

Char ID to convert to String ID.

required

Returns:

Name Type Description
str str

String representation of Char ID.

Source code in src\utils\adobe.py
@cache
def charIDToStringID(self, index: int) -> str:
    """Converts a Char ID to a String ID.

    Args:
        index: Char ID to convert to String ID.

    Returns:
        str: String representation of Char ID.
    """
    return self.typeIDToStringID(
        self.charIDToTypeID(index))

charIDToTypeID(index: str) -> int

Caching handler for charIDToTypeID.

Parameters:

Name Type Description Default
index str

Char ID to convert to Type ID.

required

Returns:

Type Description
int

Type ID converted from Char ID.

Source code in src\utils\adobe.py
@cache
def charIDToTypeID(self, index: str) -> int:
    """Caching handler for charIDToTypeID.

    Args:
        index: Char ID to convert to Type ID.

    Returns:
        Type ID converted from Char ID.
    """
    return super().charIDToTypeID(index)

executeAction(event_id: int, descriptor: ActionDescriptor, dialogs: DialogModes = DialogModes.DisplayNoDialogs) -> Any

Middleware to allow all dialogs when an error occurs upon calling executeAction in development mode.

Parameters:

Name Type Description Default
event_id int

Action descriptor event ID.

required
descriptor ActionDescriptor

Main action descriptor tree to execute.

required
dialogs DialogModes

DialogMode which governs whether to display dialogs.

DisplayNoDialogs

Returns:

Type Description
Any

Result of the action descriptor execution.

Source code in src\utils\adobe.py
def executeAction(
    self, event_id: int,
    descriptor: ActionDescriptor,
    dialogs: DialogModes = DialogModes.DisplayNoDialogs
) -> Any:
    """Middleware to allow all dialogs when an error occurs upon calling executeAction in development mode.

    Args:
        event_id: Action descriptor event ID.
        descriptor: Main action descriptor tree to execute.
        dialogs: DialogMode which governs whether to display dialogs.

    Returns:
        Result of the action descriptor execution.
    """
    if self.is_error_dialog_enabled():
        # Allow error dialogs if enabled in the app environment
        return super().executeAction(event_id, descriptor, DialogModes.DisplayErrorDialogs)
    return super().executeAction(event_id, descriptor, dialogs)

is_error_dialog_enabled() -> bool

Source code in src\utils\adobe.py
def is_error_dialog_enabled(self) -> bool:
    """bool: Whether to allow error dialogs, defined in app environment object."""
    if self._env:
        return self._env.PS_ERROR_DIALOG
    return False

is_running() -> bool

Check if the current Photoshop Application instance is still valid.

Source code in src\utils\adobe.py
@classmethod
def is_running(cls) -> bool:
    """Check if the current Photoshop Application instance is still valid."""
    with suppress(Exception):
        _ = cls._instance.version
        return True
    return False

refresh_app()

Replace the existing Photoshop Application instance with a new one.

Source code in src\utils\adobe.py
def refresh_app(self):
    """Replace the existing Photoshop Application instance with a new one."""
    if not self.is_running():
        try:
            # Load Photoshop and default preferences
            super(PhotoshopHandler, self).__init__(env=self._env)
            self.preferences.rulerUnits = Units.Pixels
            self.preferences.typeUnits = Units.Points
        except Exception as e:
            # Photoshop is either busy or unresponsive
            return OSError(get_photoshop_error_message(e))
    return

sID(index: str) -> int

Shorthand redirect for stringIDToTypeID.

Source code in src\utils\adobe.py
@cache
def sID(self, index: str) -> int:
    """Shorthand redirect for stringIDToTypeID."""
    return self.stringIDToTypeID(index)

scale_by_dpi(value: Union[int, float]) -> int

Scales a value by comparing document DPI to ideal DPI.

Parameters:

Name Type Description Default
value int | float

Integer or float value to adjust by DPI ratio.

required

Returns:

Type Description
int

Adjusted value as an integer.

Source code in src\utils\adobe.py
@cache
def scale_by_dpi(self, value: Union[int, float]) -> int:
    """Scales a value by comparing document DPI to ideal DPI.

    Args:
        value: Integer or float value to adjust by DPI ratio.

    Returns:
        Adjusted value as an integer.
    """
    return int((self.activeDocument.width / 3264) * value)

stringIDToCharID(index: int) -> str

Converts a String ID to a Char ID.

Parameters:

Name Type Description Default
index int

String ID to convert to Char ID.

required

Returns:

Name Type Description
str str

Character representation of String ID.

Source code in src\utils\adobe.py
@cache
def stringIDToCharID(self, index: int) -> str:
    """Converts a String ID to a Char ID.

    Args:
        index: String ID to convert to Char ID.

    Returns:
        str: Character representation of String ID.
    """
    return self.typeIDToCharID(
        self.stringIDToTypeID(index))

stringIDToTypeID(index: str) -> int

Caching handler for stringIDToTypeID.

Parameters:

Name Type Description Default
index str

String ID to convert to Type ID.

required

Returns:

Type Description
int

Type ID converted from string ID.

Source code in src\utils\adobe.py
@cache
def stringIDToTypeID(self, index: str) -> int:
    """Caching handler for stringIDToTypeID.

    Args:
        index: String ID to convert to Type ID.

    Returns:
        Type ID converted from string ID.
    """
    return super().stringIDToTypeID(index)

supports_generative_fill() -> bool

Checks if Photoshop version supports Generative Fill.

Source code in src\utils\adobe.py
@cache
def supports_generative_fill(self) -> bool:
    """Checks if Photoshop version supports Generative Fill."""
    return self.version_meets_requirement('24.6.0')

supports_target_text_replace() -> bool

Source code in src\utils\adobe.py
@cache
def supports_target_text_replace(self) -> bool:
    """bool: Checks if Photoshop version supports targeted text replacement."""
    return self.version_meets_requirement('22.0.0')

supports_webp() -> bool

Source code in src\utils\adobe.py
@cache
def supports_webp(self) -> bool:
    """bool: Checks if Photoshop version supports WEBP files."""
    return self.version_meets_requirement('23.2.0')

t2c(index: int) -> str

Shorthand redirect for typeIDToCharID.

Source code in src\utils\adobe.py
@cache
def t2c(self, index: int) -> str:
    """Shorthand redirect for typeIDToCharID."""
    return self.typeIDToCharID(index)

t2s(index: int) -> str

Shorthand redirect for typeIDToStringID.

Source code in src\utils\adobe.py
@cache
def t2s(self, index: int) -> str:
    """Shorthand redirect for typeIDToStringID."""
    return self.typeIDToStringID(index)

typeIDToCharID(index: int) -> str

Caching handler for typeIDToCharID.

Parameters:

Name Type Description Default
index int

Type ID to convert to Char ID.

required

Returns:

Type Description
str

Character representation of Type ID.

Source code in src\utils\adobe.py
@cache
def typeIDToCharID(self, index: int) -> str:
    """Caching handler for typeIDToCharID.

    Args:
        index: Type ID to convert to Char ID.

    Returns:
        Character representation of Type ID.
    """
    return super().typeIDToCharID(index)

typeIDToStringID(index: int) -> str

Caching handler for typeIDToStringID.

Parameters:

Name Type Description Default
index int

Type ID to convert to String ID.

required

Returns:

Name Type Description
str str

String representation of Type ID.

Source code in src\utils\adobe.py
@cache
def typeIDToStringID(self, index: int) -> str:
    """Caching handler for typeIDToStringID.

    Args:
        index: Type ID to convert to String ID.

    Returns:
        str: String representation of Type ID.
    """
    return super().typeIDToStringID(index)

version_meets_requirement(value: str) -> bool

Checks if Photoshop version meets or exceeds required value.

Parameters:

Name Type Description Default
value str

Minimum version string required.

required
Source code in src\utils\adobe.py
def version_meets_requirement(self, value: str) -> bool:
    """Checks if Photoshop version meets or exceeds required value.

    Args:
        value: Minimum version string required.
    """
    if parse(self.version) >= parse(value):
        return True
    return False