Skip to content

Functions

src.utils.fonts.register_font(ps_app: PhotoshopHandler, font_path: str) -> bool

Add FontResource using given font file, refresh Photoshop fonts.

Parameters:

Name Type Description Default
ps_app PhotoshopHandler

Photoshop application object.

required
font_path str

Path to compatible font file.

required

Returns:

Type Description
bool

True if succeeded, False if failed.

Source code in src\utils\fonts.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def register_font(ps_app: PhotoshopHandler, font_path: str) -> bool:
    """Add FontResource using given font file, refresh Photoshop fonts.

    Args:
        ps_app: Photoshop application object.
        font_path: Path to compatible font file.

    Returns:
        True if succeeded, False if failed.
    """
    result = ctypes.windll.gdi32.AddFontResourceW(osp.abspath(font_path))
    if result != 0:
        # Font Resource added successfully
        try:
            # Notify all programs
            print(f"{osp.basename(font_path)} added to font cache!")
            hwnd_broadcast = wintypes.HWND(-1)
            ctypes.windll.user32.SendMessageW(
                hwnd_broadcast, wintypes.UINT(0x001D), wintypes.WPARAM(0), wintypes.LPARAM(0)
            )
            ps_app.refreshFonts()
        except Exception as e:
            print(e)
        return True
    return False

src.utils.fonts.unregister_font(ps_app: PhotoshopHandler, font_path: str) -> bool

Remove FontResource using given font file, refresh Photoshop fonts.

Parameters:

Name Type Description Default
ps_app PhotoshopHandler

Photoshop application object.

required
font_path str

Path to compatible font file.

required

Returns:

Type Description
bool

True if succeeded, False if failed.

Source code in src\utils\fonts.py
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def unregister_font(ps_app: PhotoshopHandler, font_path: str) -> bool:
    """Remove FontResource using given font file, refresh Photoshop fonts.

    Args:
        ps_app: Photoshop application object.
        font_path: Path to compatible font file.

    Returns:
        True if succeeded, False if failed.
    """
    result = ctypes.windll.gdi32.RemoveFontResourceW(osp.abspath(font_path))
    if result != 0:
        # Font Resource removed successfully
        try:
            # Notify all programs
            print(f"{osp.basename(font_path)} removed from font cache!")
            hwnd_broadcast = wintypes.HWND(-1)
            ctypes.windll.user32.SendMessageW(
                hwnd_broadcast, wintypes.UINT(0x001D), wintypes.WPARAM(0), wintypes.LPARAM(0)
            )
            ps_app.refreshFonts()
        except Exception as e:
            print(e)
        return True
    return False

src.utils.fonts.get_ps_font_dict(ps_app: PhotoshopHandler) -> dict[str, str]

Gets a dictionary of every font accessible in Photoshop.

Parameters:

Name Type Description Default
ps_app PhotoshopHandler

Photoshop application object.

required

Returns:

Type Description
dict[str, str]

Dictionary with postScriptName as key, display name as value.

Source code in src\utils\fonts.py
101
102
103
104
105
106
107
108
109
110
111
112
113
114
def get_ps_font_dict(ps_app: PhotoshopHandler) -> dict[str, str]:
    """Gets a dictionary of every font accessible in Photoshop.

    Args:
        ps_app: Photoshop application object.

    Returns:
        Dictionary with postScriptName as key, display name as value.
    """
    fonts = {}
    for f in ps_app.fonts:
        with suppress(PS_EXCEPTIONS):
            fonts[f.name] = f.postScriptName
    return fonts

src.utils.fonts.get_document_fonts(ps_app: PhotoshopHandler, container: Optional[type[LayerContainer]] = None, fonts: Optional[dict] = None, ps_fonts: Optional[dict] = None) -> dict

Get a list of all fonts used in a given Photoshop Document or LayerSet.

Parameters:

Name Type Description Default
ps_app PhotoshopHandler

Photoshop application object.

required
container type[LayerContainer] | None

Photoshop Document or LayerSet object.

None
fonts dict | None

Existing fonts list to build onto.

None
ps_fonts dict | None

Pre-computed Photoshop fonts list.

None

Returns:

Type Description
dict

Unique list of font names.

Source code in src\utils\fonts.py
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
def get_document_fonts(
    ps_app: PhotoshopHandler,
    container: Optional[type[LayerContainer]] = None,
    fonts: Optional[dict] = None,
    ps_fonts: Optional[dict] = None
) -> dict:
    """Get a list of all fonts used in a given Photoshop Document or LayerSet.

    Args:
        ps_app: Photoshop application object.
        container: Photoshop Document or LayerSet object.
        fonts: Existing fonts list to build onto.
        ps_fonts: Pre-computed Photoshop fonts list.

    Returns:
        Unique list of font names.
    """
    # Establish starting fonts and Photoshop fonts
    fonts = fonts or {}
    ps_fonts = ps_fonts or get_ps_font_dict(ps_app)
    container = container or ps_app.activeDocument

    # Check each layer for a TextItem with a font
    for layer in [n for n in container.artLayers if n.kind == LayerKind.TextLayer]:
        try:
            # Log a new font or update an existing one
            font = str(layer.textItem.font)
            if font in fonts:
                fonts[font]['count'] += 1
            else:
                fonts[font] = {
                    'name': ps_fonts.get(font, None),
                    'count': 1
                }
        except PS_EXCEPTIONS:
            # Font property couldn't be accessed
            print(f"Font unreadable for layer: {layer.name}")

    # Make additional calls for nested groups
    for group in container.layerSets:
        fonts = get_document_fonts(ps_app, group, fonts, ps_fonts=ps_fonts)
    return fonts

src.utils.fonts.get_font_details(path: str) -> Optional[tuple[str, FontDetails]]

Gets the font name and postscript name for a given font file.

Parameters:

Name Type Description Default
path str

Path to ttf or otf file.

required

Returns:

Type Description
tuple[str, FontDetails] | None

Tuple containing name and postscript name.

Source code in src\utils\fonts.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
def get_font_details(path: str) -> Optional[tuple[str, FontDetails]]:
    """Gets the font name and postscript name for a given font file.

    Args:
        path: Path to ttf or otf file.

    Returns:
        Tuple containing name and postscript name.
    """
    with suppress(PS_EXCEPTIONS, TTLibError):
        with TTFont(path) as font:
            font_name = font['name'].getName(4, 3, 1, 1033).toUnicode()
            font_postscript = font['name'].getDebugName(6)
            version_match = REG_FONT_VER.search(font['name'].getDebugName(5))
            font_version = version_match.group(1).lstrip('0') if version_match else None
        return font_postscript, {'name': font_name, 'version': font_version}
    return

src.utils.fonts.get_fonts_from_folder(folder: str) -> dict[str, FontDetails]

Return a dictionary of font details for the fonts contained in a target directory.

Parameters:

Name Type Description Default
folder str

Directory containing font files to read (supports TTF and OTF fonts).

required

Returns:

Type Description
dict[str, FontDetails]

Dictionary of FontDetails.

Source code in src\utils\fonts.py
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def get_fonts_from_folder(folder: str) -> dict[str, FontDetails]:
    """Return a dictionary of font details for the fonts contained in a target directory.

    Args:
        folder: Directory containing font files to read (supports TTF and OTF fonts).

    Returns:
        Dictionary of FontDetails.
    """
    # Get a list of the font names in your `fonts` folder
    with suppress(Exception):
        ext = (".otf", ".ttf", ".OTF", ".TTF")
        local_fonts = [osp.join(folder, f) for f in os.listdir(folder) if f.endswith(ext)]
        return {n[0]: n[1] for n in [get_font_details(f) for f in local_fonts] if n}
    return {}

src.utils.fonts.get_installed_fonts_dict() -> dict[str, FontDetails]

Gets a dictionary of every font installed by the user.

Returns:

Type Description
dict[str, FontDetails]

Dictionary with postScriptName as key, and tuple of display name and version as value.

Source code in src\utils\fonts.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
def get_installed_fonts_dict() -> dict[str, FontDetails]:
    """Gets a dictionary of every font installed by the user.

    Returns:
        Dictionary with postScriptName as key, and tuple of display name and version as value.
    """
    with suppress(Exception):
        installed_fonts_dir = os.path.expandvars(r'%userprofile%\AppData\Local\Microsoft\Windows\Fonts')
        system_fonts_dir = os.path.join(os.path.join(os.environ['WINDIR']), 'Fonts')
        return {
            **get_fonts_from_folder(installed_fonts_dir),
            **get_fonts_from_folder(system_fonts_dir)
        }
    return {}

src.utils.fonts.get_outdated_fonts(fonts: dict[str, FontDetails], missing: Optional[dict[str, FontDetails]] = None) -> dict[str, FontDetails]

Compares the version of each font given against installed fonts.

Parameters:

Name Type Description Default
fonts dict[str, FontDetails]

A dictionary of fonts to check against installed fonts.

required
missing dict[str, FontDetails] | None

An optional dictionary of fonts Photoshop couldn't locate, check in install dir.

None

Returns:

Type Description
dict[str, FontDetails]

A dict of fonts with outdated version number. Dict contains the newer version.

Source code in src\utils\fonts.py
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
def get_outdated_fonts(
    fonts: dict[str, FontDetails],
    missing: Optional[dict[str, FontDetails]] = None
) -> dict[str, FontDetails]:
    """Compares the version of each font given against installed fonts.

    Args:
        fonts: A dictionary of fonts to check against installed fonts.
        missing: An optional dictionary of fonts Photoshop couldn't locate, check in install dir.

    Returns:
        A dict of fonts with outdated version number. Dict contains the newer version.
    """
    # Check each confirmed font for version changes
    outdated: dict[str, FontDetails] = {}
    installed: dict[str, FontDetails] = get_installed_fonts_dict()
    if not missing:
        missing = {}

    # Check fonts for any outdated
    for name, data in fonts.items():
        if name in installed and installed[name].get('version'):
            if parse(installed[name]['version']) < parse(data['version']):
                outdated[name] = data

    # Check missing fonts to see if found in installed dict, if so check for version change
    for k in list(missing.keys()):
        if k in installed and installed[k].get('version'):
            if parse(installed[k]['version']) < parse(missing[k]['version']):
                outdated[k] = missing[k]
            del missing[k]

    return outdated

src.utils.fonts.get_missing_fonts(ps_app: PhotoshopHandler, fonts: dict[str, FontDetails]) -> tuple[dict[str, FontDetails], dict[str, FontDetails]]

Checks each font to see if it's present in the Photoshop font list.

Parameters:

Name Type Description Default
ps_app PhotoshopHandler

Photoshop application object.

required
fonts dict[str, FontDetails]

A dictionary of fonts to check for.

required

Returns:

Type Description
tuple[dict[str, FontDetails], dict[str, FontDetails]]

Tuple containing a dictionary of fonts missing and fonts found.

Source code in src\utils\fonts.py
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
def get_missing_fonts(
    ps_app: PhotoshopHandler,
    fonts: dict[str, FontDetails]
) -> tuple[dict[str, FontDetails], dict[str, FontDetails]]:
    """Checks each font to see if it's present in the Photoshop font list.

    Args:
        ps_app: Photoshop application object.
        fonts: A dictionary of fonts to check for.

    Returns:
        Tuple containing a dictionary of fonts missing and fonts found.
    """
    # Figure out which fonts are missing
    found: dict[str, FontDetails] = {}
    missing: dict[str, FontDetails] = {}
    for script_name, data in fonts.items():
        try:
            # Check if font exists in Photoshop
            _ = ps_app.fonts.app[script_name]
            found[script_name] = data
        except PS_EXCEPTIONS:
            # Font not found in Photoshop
            missing[script_name] = data
    return missing, found

src.utils.fonts.check_app_fonts(ps_app: PhotoshopHandler, folders: list[str]) -> tuple[dict[str, FontDetails], dict[str, FontDetails]]

Checks each font in a folder to see if it is installed or outdated.

Parameters:

Name Type Description Default
ps_app PhotoshopHandler

Photoshop application object.

required
folders list[str]

Folder paths containing fonts to check.

required

Returns:

Type Description
tuple[dict[str, FontDetails], dict[str, FontDetails]]

A tuple containing a dict of missing fonts and a dict of outdated fonts.

Source code in src\utils\fonts.py
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
def check_app_fonts(
    ps_app: PhotoshopHandler,
    folders: list[str]
) -> tuple[dict[str, FontDetails], dict[str, FontDetails]]:
    """Checks each font in a folder to see if it is installed or outdated.

    Args:
        ps_app: Photoshop application object.
        folders: Folder paths containing fonts to check.

    Returns:
        A tuple containing a dict of missing fonts and a dict of outdated fonts.
    """
    # Get a dictionary of fonts found in target folder and fonts installed
    fonts: dict[str, FontDetails] = {}
    for f in folders:
        fonts.update(get_fonts_from_folder(f))
    missing, found = get_missing_fonts(ps_app, fonts)
    return missing, get_outdated_fonts(found, missing)