Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Install a font using python in Windows

I am developing a python script to install a list of fonts in Windows. I have a list like this,

fonts_list = ["/path/to/file/myFont1.ttf", "/path/to/file/myFont1.ttf", "/path/to/file/myFont1.ttf"]

I simply tried, copying font files using both shutil and os, and after that I tried adding it to Windows registry, even with the admin privileges, it didn't work. Output from the script did not display any errors but there is no any font in Windows Fonts directory which are mentioned in the list.

os.system(reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /v "FontName (TrueType)")

So I started seeking more seeds from web and then I found this one, Installing TTF fonts on windows with python. But it didn't work too. Next I tried calling Windows fontview.exe, but for a list of fonts it is a disaster.

os.system("fontview " + fontsList[0])

Is there anyway to install a font in windows programmatically? Or is there any user level directory in Windows to simply copy font files like Linux systems have ~/.fonts and Macs have ~/Library/Fonts? Please help.

like image 848
lpsandaruwan Avatar asked Jan 05 '23 16:01

lpsandaruwan


1 Answers

Here's an install_font function to copy a font to the system Fonts folder, load it in the current session, notify running programs, and update the registry. It only depends on Python's standard library and should work in both Python 2 and 3.

ctypes definitions

import os
import shutil
import ctypes
from ctypes import wintypes

try:
    import winreg
except ImportError:
    import _winreg as winreg

user32 = ctypes.WinDLL('user32', use_last_error=True)
gdi32 = ctypes.WinDLL('gdi32', use_last_error=True)

FONTS_REG_PATH = r'Software\Microsoft\Windows NT\CurrentVersion\Fonts'

HWND_BROADCAST   = 0xFFFF
SMTO_ABORTIFHUNG = 0x0002
WM_FONTCHANGE    = 0x001D
GFRI_DESCRIPTION = 1
GFRI_ISTRUETYPE  = 3

if not hasattr(wintypes, 'LPDWORD'):
    wintypes.LPDWORD = ctypes.POINTER(wintypes.DWORD)

user32.SendMessageTimeoutW.restype = wintypes.LPVOID
user32.SendMessageTimeoutW.argtypes = (
    wintypes.HWND,   # hWnd
    wintypes.UINT,   # Msg
    wintypes.LPVOID, # wParam
    wintypes.LPVOID, # lParam
    wintypes.UINT,   # fuFlags
    wintypes.UINT,   # uTimeout
    wintypes.LPVOID) # lpdwResult

gdi32.AddFontResourceW.argtypes = (
    wintypes.LPCWSTR,) # lpszFilename

# http://www.undocprint.org/winspool/getfontresourceinfo
gdi32.GetFontResourceInfoW.argtypes = (
    wintypes.LPCWSTR, # lpszFilename
    wintypes.LPDWORD, # cbBuffer
    wintypes.LPVOID,  # lpBuffer
    wintypes.DWORD)   # dwQueryType

function definition

def install_font(src_path):
    # copy the font to the Windows Fonts folder
    dst_path = os.path.join(os.environ['SystemRoot'], 'Fonts',
                            os.path.basename(src_path))
    shutil.copy(src_path, dst_path)
    # load the font in the current session
    if not gdi32.AddFontResourceW(dst_path):
        os.remove(dst_path)
        raise WindowsError('AddFontResource failed to load "%s"' % src_path)
    # notify running programs
    user32.SendMessageTimeoutW(HWND_BROADCAST, WM_FONTCHANGE, 0, 0,
                               SMTO_ABORTIFHUNG, 1000, None)
    # store the fontname/filename in the registry
    filename = os.path.basename(dst_path)
    fontname = os.path.splitext(filename)[0]
    # try to get the font's real name
    cb = wintypes.DWORD()
    if gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb), None,
                                  GFRI_DESCRIPTION):
        buf = (ctypes.c_wchar * cb.value)()
        if gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb), buf,
                                      GFRI_DESCRIPTION):
            fontname = buf.value
    is_truetype = wintypes.BOOL()
    cb.value = ctypes.sizeof(is_truetype)
    gdi32.GetFontResourceInfoW(filename, ctypes.byref(cb),
        ctypes.byref(is_truetype), GFRI_ISTRUETYPE)
    if is_truetype:
        fontname += ' (TrueType)'
    with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, FONTS_REG_PATH, 0,
                        winreg.KEY_SET_VALUE) as key:
        winreg.SetValueEx(key, fontname, 0, winreg.REG_SZ, filename)
like image 161
Eryk Sun Avatar answered Jan 07 '23 07:01

Eryk Sun