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
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
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
149
150
151
152
@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
273
274
275
276
277
278
279
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
192
193
194
195
@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
154
155
156
157
@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
223
224
225
226
227
228
229
230
231
232
233
234
@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
137
138
139
140
141
142
143
144
145
146
147
@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
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
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_running() -> bool

Check if the current Photoshop Application instance is still valid.

Source code in src\utils\adobe.py
125
126
127
128
129
130
131
@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
108
109
110
111
112
113
114
115
116
117
118
119
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
197
198
199
200
@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
314
315
316
317
318
319
320
321
322
323
324
@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
236
237
238
239
240
241
242
243
244
245
246
247
@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
180
181
182
183
184
185
186
187
188
189
190
@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
295
296
297
298
@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
285
286
287
288
@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
290
291
292
293
@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
171
172
173
174
@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
214
215
216
217
@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
159
160
161
162
163
164
165
166
167
168
169
@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
202
203
204
205
206
207
208
209
210
211
212
@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
300
301
302
303
304
305
306
307
308
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