Python Windows Enum Installed Fonts
Question:
I am trying to get a list of installed fonts on Windows machines so that I can then install missing ones. I’m on Py3.*, Windows 7.
Looking through the win API documentation I know that I need to use EnumFontFamiliesExW
however I’m not sure what the correct syntax is with ctypes in python… I’m totally new to the ctypes module, and don’t know a lot about C. The python documentation for ctypes is extremely hard for me to grasp, so sorry if I’m not able to find this answer there (I did indeed look).
I got as far as the following:
gdi32 = ctypes.WinDLL('gdi32')
print(gdi32.EnumFontFamiliesExW())
Which of course throws an error because I am not specifying any arguments. So how do I pass the correct arguments?
Answers:
pywin32 extension provides an easier option. Use import win32gui
, import win32api
, …
import win32gui
def callback(font, tm, fonttype, names):
names.append(font.lfFaceName)
return True
font_names = []
hdc = win32gui.GetDC(None)
win32gui.EnumFontFamilies(hdc, None, callback, font_names)
print("n".join(font_names))
win32gui.ReleaseDC(hdc, None)
With ctype
you have to know C and WinAPI. Here is an example to do this in ctype
, base on https://github.com/wwwtyro/AegisLuna/blob/master/pyglet/font/win32query.py
import ctypes
from ctypes import wintypes
class LOGFONT(ctypes.Structure): _fields_ = [
('lfHeight', wintypes.LONG),
('lfWidth', wintypes.LONG),
('lfEscapement', wintypes.LONG),
('lfOrientation', wintypes.LONG),
('lfWeight', wintypes.LONG),
('lfItalic', wintypes.BYTE),
('lfUnderline', wintypes.BYTE),
('lfStrikeOut', wintypes.BYTE),
('lfCharSet', wintypes.BYTE),
('lfOutPrecision', wintypes.BYTE),
('lfClipPrecision', wintypes.BYTE),
('lfQuality', wintypes.BYTE),
('lfPitchAndFamily', wintypes.BYTE),
('lfFaceName', ctypes.c_wchar*32)]
#we are not interested in NEWTEXTMETRIC parameter in FONTENUMPROC, use LPVOID instead
FONTENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_int,
ctypes.POINTER(LOGFONT), wintypes.LPVOID, wintypes.DWORD, wintypes.LPARAM)
fontlist = []
def font_enum(logfont, textmetricex, fonttype, param):
str = logfont.contents.lfFaceName;
if (any(str in s for s in fontlist) == False):
fontlist.append(str)
return True
hdc = ctypes.windll.user32.GetDC(None)
ctypes.windll.gdi32.EnumFontFamiliesExW(hdc, None, FONTENUMPROC(font_enum), 0, 0)
ctypes.windll.user32.ReleaseDC(None, hdc)
print("n".join(fontlist))
I am trying to get a list of installed fonts on Windows machines so that I can then install missing ones. I’m on Py3.*, Windows 7.
Looking through the win API documentation I know that I need to use EnumFontFamiliesExW
however I’m not sure what the correct syntax is with ctypes in python… I’m totally new to the ctypes module, and don’t know a lot about C. The python documentation for ctypes is extremely hard for me to grasp, so sorry if I’m not able to find this answer there (I did indeed look).
I got as far as the following:
gdi32 = ctypes.WinDLL('gdi32')
print(gdi32.EnumFontFamiliesExW())
Which of course throws an error because I am not specifying any arguments. So how do I pass the correct arguments?
pywin32 extension provides an easier option. Use import win32gui
, import win32api
, …
import win32gui
def callback(font, tm, fonttype, names):
names.append(font.lfFaceName)
return True
font_names = []
hdc = win32gui.GetDC(None)
win32gui.EnumFontFamilies(hdc, None, callback, font_names)
print("n".join(font_names))
win32gui.ReleaseDC(hdc, None)
With ctype
you have to know C and WinAPI. Here is an example to do this in ctype
, base on https://github.com/wwwtyro/AegisLuna/blob/master/pyglet/font/win32query.py
import ctypes
from ctypes import wintypes
class LOGFONT(ctypes.Structure): _fields_ = [
('lfHeight', wintypes.LONG),
('lfWidth', wintypes.LONG),
('lfEscapement', wintypes.LONG),
('lfOrientation', wintypes.LONG),
('lfWeight', wintypes.LONG),
('lfItalic', wintypes.BYTE),
('lfUnderline', wintypes.BYTE),
('lfStrikeOut', wintypes.BYTE),
('lfCharSet', wintypes.BYTE),
('lfOutPrecision', wintypes.BYTE),
('lfClipPrecision', wintypes.BYTE),
('lfQuality', wintypes.BYTE),
('lfPitchAndFamily', wintypes.BYTE),
('lfFaceName', ctypes.c_wchar*32)]
#we are not interested in NEWTEXTMETRIC parameter in FONTENUMPROC, use LPVOID instead
FONTENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_int,
ctypes.POINTER(LOGFONT), wintypes.LPVOID, wintypes.DWORD, wintypes.LPARAM)
fontlist = []
def font_enum(logfont, textmetricex, fonttype, param):
str = logfont.contents.lfFaceName;
if (any(str in s for s in fontlist) == False):
fontlist.append(str)
return True
hdc = ctypes.windll.user32.GetDC(None)
ctypes.windll.gdi32.EnumFontFamiliesExW(hdc, None, FONTENUMPROC(font_enum), 0, 0)
ctypes.windll.user32.ReleaseDC(None, hdc)
print("n".join(fontlist))