I want to get all system fonts (inside c://Windows//Fonts
) as dictionary since I need to differentiate between bold and italic etc. Though when listing the content of the directory via os.listdir
or in terminal it's not possible to tell which font is what. (or at least in most cases) Further, even if you wanted to iterate through all fonts you could barely tell whether it's the 'regular' font or a variant.
So windows list the folder as follows:
Each of these 'font-folders' looks like (depending on their different styles) :
Lastly, this is what I get via the list command (unreadable and unusable for most cases):
So this is the output I wish I could achieve (or similar):
path = "C://Windows//Fonts"
# do some magic
dictionary = {
'Arial':'Regular': 'Arial-Regular.ttf','Bold':'Arial-Bold.ttf',
'Carlito:'Regular':' 8514fix.fon','Bold':'someweirdotherfile.fon'
}
The only things I got so far are the bare installed font names not their filenames. So if there is any way to either get the content as dictionary or to get the filename of the fonts please be so kind and give me a tip :)
To me, the best solution is to use DirecWrite which is the Windows API to manipulate fonts. It doesn'T involve using any hacks to get the information you need.
There is many way to use DirectWrite to get the information you want, but here is the way that is compatible with the maximum version of Windows.
Dependencies:
import ctypes
import json
from pathlib import Path
import time
from comtypes import COMError, GUID, IUnknown, STDMETHOD
from ctypes import WINFUNCTYPE, Structure, byref, c_ubyte, create_unicode_buffer, HRESULT, POINTER, windll, wintypes
from dataclasses import asdict, dataclass
from enum import IntEnum, IntFlag
from sys import getwindowsversion
from typing import Dict, List, Set
@dataclass
class FontFace:
file_path: List[str] # See this link to know why we need a list: https://stackoverflow.com/questions/41161152/when-can-an-idwritefontface-have-more-than-one-file
face_name: str
@dataclass
class FontFamily:
family_name: str
fonts: List[FontFace]
class DWRITE_FACTORY_TYPE(IntEnum):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_factory_type
DWRITE_FACTORY_TYPE_SHARED = 0
DWRITE_FACTORY_TYPE_ISOLATED = 1
class DWRITE_FONT_SIMULATIONS(IntFlag):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_font_simulations
DWRITE_FONT_SIMULATIONS_NONE = 0x0000
DWRITE_FONT_SIMULATIONS_BOLD = 0x0001
DWRITE_FONT_SIMULATIONS_OBLIQUE = 0x0002
class DWRITE_FONT_FAMILY_MODEL(IntEnum):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/ne-dwrite_3-dwrite_font_family_model
DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC = 0
DWRITE_FONT_FAMILY_MODEL_WEIGHT_STRETCH_STYLE = 1
class DWRITE_INFORMATIONAL_STRING_ID(IntEnum):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_informational_string_id
DWRITE_INFORMATIONAL_STRING_NONE = 0
DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE = 1
DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS = 2
DWRITE_INFORMATIONAL_STRING_TRADEMARK = 3
DWRITE_INFORMATIONAL_STRING_MANUFACTURER = 4
DWRITE_INFORMATIONAL_STRING_DESIGNER = 5
DWRITE_INFORMATIONAL_STRING_DESIGNER_URL = 6
DWRITE_INFORMATIONAL_STRING_DESCRIPTION = 7
DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL = 8
DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION = 9
DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL = 10
DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES = 11
DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES = 12
DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES = 13
DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES = 14
DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT = 15
DWRITE_INFORMATIONAL_STRING_FULL_NAME = 16
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME = 17
DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME = 18
DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME = 19
DWRITE_INFORMATIONAL_STRING_DESIGN_SCRIPT_LANGUAGE_TAG = 20
DWRITE_INFORMATIONAL_STRING_SUPPORTED_SCRIPT_LANGUAGE_TAG = 21
DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES = DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_FAMILY_NAMES
DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES = DWRITE_INFORMATIONAL_STRING_TYPOGRAPHIC_SUBFAMILY_NAMES
DWRITE_INFORMATIONAL_STRING_WWS_FAMILY_NAME = DWRITE_INFORMATIONAL_STRING_WEIGHT_STRETCH_STYLE_FAMILY_NAME
class IDWriteFontFileLoader(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontfileloader
_iid_ = GUID("{727cad4e-d6af-4c9e-8a08-d695b11caa49}")
_methods_ = [
STDMETHOD(None, "CreateStreamFromKey"), # Need to be implemented
]
class IDWriteLocalFontFileLoader(IDWriteFontFileLoader):
# https://learn.microsoft.com/en-us/windows/win32/directwrite/idwritelocalfontfileloader
_iid_ = GUID("{b2d9f3ec-c9fe-4a11-a2ec-d86208f7c0a2}")
_methods_ = [
STDMETHOD(HRESULT, "GetFilePathLengthFromKey", [wintypes.LPCVOID, wintypes.UINT, POINTER(wintypes.UINT)]),
STDMETHOD(HRESULT, "GetFilePathFromKey", [wintypes.LPCVOID, wintypes.UINT, POINTER(wintypes.WCHAR), wintypes.UINT]),
STDMETHOD(None, "GetLastWriteTimeFromKey"), # Need to be implemented
]
class IDWriteFontFile(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontfile
_iid_ = GUID("{739d886a-cef5-47dc-8769-1a8b41bebbb0}")
_methods_ = [
STDMETHOD(HRESULT, "GetReferenceKey", [POINTER(wintypes.LPCVOID), POINTER(wintypes.UINT)]),
STDMETHOD(HRESULT, "GetLoader", [POINTER(POINTER(IDWriteFontFileLoader))]),
STDMETHOD(HRESULT, "Analyze", [POINTER(wintypes.BOOL), POINTER(wintypes.UINT), POINTER(wintypes.UINT), POINTER(wintypes.UINT)]),
]
class IDWriteLocalizedStrings(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritelocalizedstrings
_iid_ = GUID("{08256209-099a-4b34-b86d-c22b110e7771}")
_methods_ = [
STDMETHOD(wintypes.UINT, "GetCount"),
STDMETHOD(HRESULT, "FindLocaleName", [POINTER(wintypes.WCHAR), POINTER(wintypes.UINT), POINTER(wintypes.BOOL)]),
STDMETHOD(HRESULT, "GetLocaleNameLength", [wintypes.UINT, POINTER(wintypes.UINT)]),
STDMETHOD(HRESULT, "GetLocaleName", [wintypes.UINT, POINTER(wintypes.WCHAR), wintypes.UINT]),
STDMETHOD(HRESULT, "GetStringLength", [wintypes.UINT, POINTER(wintypes.UINT)]),
STDMETHOD(HRESULT, "GetString", [wintypes.UINT, POINTER(wintypes.WCHAR), wintypes.UINT]),
]
class IDWriteFontFace(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontface
_iid_ = GUID("{5f49804d-7024-4d43-bfa9-d25984f53849}")
_methods_ = [
STDMETHOD(None, "GetType"), # Need to be implemented
STDMETHOD(HRESULT, "GetFiles", [POINTER(wintypes.UINT), POINTER(POINTER(IDWriteFontFile))]),
STDMETHOD(None, "GetIndex"), # Need to be implemented
STDMETHOD(wintypes.UINT, "GetSimulations"),
STDMETHOD(None, "IsSymbolFont"), # Need to be implemented
STDMETHOD(None, "GetMetrics"), # Need to be implemented
STDMETHOD(None, "GetGlyphCount"), # Need to be implemented
STDMETHOD(None, "GetDesignGlyphMetrics"), # Need to be implemented
STDMETHOD(None, "GetGlyphIndices"), # Need to be implemented
STDMETHOD(None, "TryGetFontTable"), # Need to be implemented
STDMETHOD(None, "ReleaseFontTable"), # Need to be implemented
STDMETHOD(None, "GetGlyphRunOutline"), # Need to be implemented
STDMETHOD(None, "GetRecommendedRenderingMode"), # Need to be implemented
STDMETHOD(None, "GetGdiCompatibleMetrics"), # Need to be implemented
STDMETHOD(None, "GetGdiCompatibleGlyphMetrics"), # Need to be implemented
]
class IDWriteFont(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefont
_iid_ = GUID("{acd16696-8c14-4f5d-877e-fe3fc1d32737}")
class IDWriteFontList(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontlist
_iid_ = GUID("{1a0d8438-1d97-4ec1-aef9-a2fb86ed6acb}")
_methods_ = [
STDMETHOD(None, "GetFontCollection"), # Need to be implemented
STDMETHOD(wintypes.UINT, "GetFontCount"),
STDMETHOD(HRESULT, "GetFont", [wintypes.UINT, POINTER(POINTER(IDWriteFont))]),
]
class IDWriteFontFamily(IDWriteFontList):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontfamily
_iid_ = GUID("{da20d8ef-812a-4c43-9802-62ec4abd7add}")
_methods_ = [
STDMETHOD(HRESULT, "GetFamilyNames", [POINTER(POINTER(IDWriteLocalizedStrings))]),
STDMETHOD(None, "GetFirstMatchingFont"), # Need to be implemented
STDMETHOD(None, "GetMatchingFonts"), # Need to be implemented
]
IDWriteFont._methods_ = [
STDMETHOD(HRESULT, "GetFontFamily", [POINTER(POINTER(IDWriteFontFamily))]),
STDMETHOD(None, "GetWeight"), # Need to be implemented
STDMETHOD(None, "GetStretch"), # Need to be implemented
STDMETHOD(None, "GetStyle"), # Need to be implemented
STDMETHOD(None, "IsSymbolFont"), # Need to be implemented
STDMETHOD(HRESULT, "GetFaceNames", [POINTER(POINTER(IDWriteLocalizedStrings))]),
STDMETHOD(HRESULT, "GetInformationalStrings", [wintypes.UINT, POINTER(POINTER(IDWriteLocalizedStrings)), POINTER(wintypes.BOOL)]),
STDMETHOD(wintypes.UINT, "GetSimulations"),
STDMETHOD(None, "GetMetrics"), # Need to be implemented
STDMETHOD(None, "HasCharacter"), # Need to be implemented
STDMETHOD(HRESULT, "CreateFontFace", [POINTER(POINTER(IDWriteFontFace))]),
]
class IDWriteFontCollection(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefontcollection
_iid_ = GUID("{a84cee02-3eea-4eee-a827-87c1a02a0fcc}")
_methods_ = [
STDMETHOD(wintypes.UINT, "GetFontFamilyCount"),
STDMETHOD(HRESULT, "GetFontFamily", [wintypes.UINT, POINTER(POINTER(IDWriteFontFamily))]),
STDMETHOD(None, "FindFamilyName"), # Need to be implemented
STDMETHOD(HRESULT, "GetFontFromFontFace", [POINTER(IDWriteFontFace), POINTER(POINTER(IDWriteFont))]),
]
class IDWriteFontCollection1(IDWriteFontCollection):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontcollection1
_iid_ = GUID("{53585141-D9F8-4095-8321-D73CF6BD116C}")
_methods_ = [
STDMETHOD(None, "GetFontSet1"), # Need to be implemented
]
class IDWriteFontCollection2(IDWriteFontCollection1):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefontcollection2
_iid_ = GUID("{514039C6-4617-4064-BF8B-92EA83E506E0}")
_methods_ = [
STDMETHOD(None, "GetFontFamily"), # Need to be implemented
STDMETHOD(None, "GetMatchingFonts"), # Need to be implemented
STDMETHOD(None, "GetFontFamilyModel"), # Need to be implemented
STDMETHOD(None, "GetFontSet2"), # Need to be implemented
]
class IDWriteFactory(IUnknown):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritefactory
_iid_ = GUID("{b859ee5a-d838-4b5b-a2e8-1adc7d93db48}")
_methods_ = [
STDMETHOD(HRESULT, "GetSystemFontCollection1", [POINTER(POINTER(IDWriteFontCollection)), wintypes.BOOL]),
STDMETHOD(None, "CreateCustomFontCollection"), # Need to be implemented
STDMETHOD(None, "RegisterFontCollectionLoader"), # Need to be implemented
STDMETHOD(None, "UnregisterFontCollectionLoader"), # Need to be implemented
STDMETHOD(None, "CreateFontFileReference"), # Need to be implemented
STDMETHOD(None, "CreateCustomFontFileReference"), # Need to be implemented
STDMETHOD(None, "CreateFontFace"), # Need to be implemented
STDMETHOD(None, "CreateRenderingParams"), # Need to be implemented
STDMETHOD(None, "CreateMonitorRenderingParams"), # Need to be implemented
STDMETHOD(None, "CreateCustomRenderingParams"), # Need to be implemented
STDMETHOD(None, "RegisterFontFileLoader"), # Need to be implemented
STDMETHOD(None, "UnregisterFontFileLoader"), # Need to be implemented
STDMETHOD(None, "CreateTextFormat"), # Need to be implemented
STDMETHOD(None, "CreateTypography"), # Need to be implemented
STDMETHOD(None, "GetGdiInterop"), # Need to be implemented
STDMETHOD(None, "CreateTextLayout"), # Need to be implemented
STDMETHOD(None, "CreateGdiCompatibleTextLayout"), # Need to be implemented
STDMETHOD(None, "CreateEllipsisTrimmingSign"), # Need to be implemented
STDMETHOD(None, "CreateTextAnalyzer"), # Need to be implemented
STDMETHOD(None, "CreateNumberSubstitution"), # Need to be implemented
STDMETHOD(None, "CreateGlyphRunAnalysis"), # Need to be implemented
]
class IDWriteFactory1(IDWriteFactory):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_1/nn-dwrite_1-idwritefactory1
_iid_ = GUID("{30572f99-dac6-41db-a16e-0486307e606a}")
_methods_ = [
STDMETHOD(None, "GetEudcFontCollection"), # Need to be implemented
STDMETHOD(None, "CreateCustomRenderingParams1"), # Need to be implemented
]
class IDWriteFactory2(IDWriteFactory1):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_2/nn-dwrite_2-idwritefactory2
_iid_ = GUID("{0439fc60-ca44-4994-8dee-3a9af7b732ec}")
_methods_ = [
STDMETHOD(None, "GetSystemFontFallback"), # Need to be implemented
STDMETHOD(None, "CreateFontFallbackBuilder"), # Need to be implemented
STDMETHOD(None, "TranslateColorGlyphRun"), # Need to be implemented
STDMETHOD(None, "CreateCustomRenderingParams2"), # Need to be implemented
STDMETHOD(None, "CreateGlyphRunAnalysis"), # Need to be implemented
]
class IDWriteFactory3(IDWriteFactory2):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefactory3
_iid_ = GUID("{9A1B41C3-D3BB-466A-87FC-FE67556A3B65}")
_methods_ = [
STDMETHOD(None, "CreateGlyphRunAnalysis"), # Need to be implemented
STDMETHOD(None, "CreateCustomRenderingParams3"), # Need to be implemented
STDMETHOD(None, "CreateFontFaceReference"), # Need to be implemented
STDMETHOD(None, "CreateFontFaceReference"), # Need to be implemented
STDMETHOD(None, "GetSystemFontSet1"), # Need to be implemented
STDMETHOD(None, "CreateFontSetBuilder"), # Need to be implemented
STDMETHOD(None, "CreateFontCollectionFromFontSet"), # Need to be implemented
STDMETHOD(HRESULT, "GetSystemFontCollection2", [wintypes.BOOL, POINTER(POINTER(IDWriteFontCollection1)), wintypes.BOOL]),
STDMETHOD(None, "GetFontDownloadQueue"), # Need to be implemented
]
class IDWriteFactory4(IDWriteFactory3):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefactory4
_iid_ = GUID("{4B0B5BD3-0797-4549-8AC5-FE915CC53856}")
_methods_ = [
STDMETHOD(None, "TranslateColorGlyphRun"), # Need to be implemented
STDMETHOD(None, "ComputeGlyphOrigins"), # Need to be implemented
STDMETHOD(None, "ComputeGlyphOrigins"), # Need to be implemented
]
class IDWriteFactory5(IDWriteFactory4):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefactory5
_iid_ = GUID("{958DB99A-BE2A-4F09-AF7D-65189803D1D3}")
_methods_ = [
STDMETHOD(None, "CreateFontSetBuilder"), # Need to be implemented
STDMETHOD(None, "CreateInMemoryFontFileLoader"), # Need to be implemented
STDMETHOD(None, "CreateHttpFontFileLoader"), # Need to be implemented
STDMETHOD(None, "AnalyzeContainerType"), # Need to be implemented
STDMETHOD(None, "UnpackFontFile"), # Need to be implemented
]
class IDWriteFactory6(IDWriteFactory5):
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite_3/nn-dwrite_3-idwritefactory6
_iid_ = GUID("{F3744D80-21F7-42EB-B35D-995BC72FC223}")
_methods_ = [
STDMETHOD(None, "CreateFontFaceReference"), # Need to be implemented
STDMETHOD(None, "CreateFontResource"), # Need to be implemented
STDMETHOD(None, "GetSystemFontSet2"), # Need to be implemented
STDMETHOD(HRESULT, "GetSystemFontCollection3", [wintypes.BOOL, wintypes.UINT, POINTER(POINTER(IDWriteFontCollection2))]),
STDMETHOD(None, "CreateFontCollectionFromFontSet"), # Need to be implemented
STDMETHOD(None, "CreateFontSetBuilder"), # Need to be implemented
STDMETHOD(None, "CreateTextFormat"), # Need to be implemented
]
class Kernel32:
def __init__(self):
kernel32 = windll.kernel32
# https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-getuserdefaultlocalename
self.GetUserDefaultLocaleName = kernel32.GetUserDefaultLocaleName
self.GetUserDefaultLocaleName.restype = wintypes.INT
self.GetUserDefaultLocaleName.argtypes = [wintypes.LPWSTR, wintypes.INT]
self.GetUserDefaultLocaleName.errcheck = self.errcheck_is_result_0
self.LOCALE_NAME_MAX_LENGTH = 85
def GetUserDefaultLocaleNameFunc(self) -> str:
locale_buffer = create_unicode_buffer(self.LOCALE_NAME_MAX_LENGTH)
try:
self.GetUserDefaultLocaleName(locale_buffer, self.LOCALE_NAME_MAX_LENGTH)
except ValueError:
return "en-us"
return locale_buffer.value
@staticmethod
def errcheck_is_result_0(result, func, args):
if result == 0:
raise ValueError(f"Error encountered with {func.__name__}.")
return result
class DirectWrite:
def __init__(self):
dwrite = windll.dwrite
# https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nf-dwrite-dwritecreatefactory
self.DWriteCreateFactory = dwrite.DWriteCreateFactory
self.DWriteCreateFactory.restype = HRESULT
self.DWriteCreateFactory.argtypes = [wintypes.UINT, GUID, POINTER(POINTER(IUnknown))]
def get_str_from_localized_strings(self, localized_strings) -> str:
# From https://learn.microsoft.com/en-us/windows/win32/api/dwrite/nn-dwrite-idwritelocalizedstrings#remarks
kernel32 = Kernel32()
locale_name = kernel32.GetUserDefaultLocaleNameFunc()
index = wintypes.UINT()
exists = wintypes.BOOL()
localized_strings.FindLocaleName(locale_name, byref(index), byref(exists))
if not exists:
localized_strings.FindLocaleName("en-us", byref(index), byref(exists))
if not exists:
index = 0
length = wintypes.UINT()
localized_strings.GetStringLength(index, byref(length))
localized_strings_buffer = create_unicode_buffer(length.value + 1)
localized_strings.GetString(index, localized_strings_buffer, len(localized_strings_buffer))
return localized_strings_buffer.value
class WindowsVersionHelpers:
@staticmethod
def is_windows_version_or_greater(windows_version, major: int, minor: int, build: int) -> bool:
"""
Parameters:
windows_version: An object from getwindowsversion.
major (int): The minimum major OS version number.
minor (int): The minimum minor OS version number.
build (int): The minimum build version number.
Returns:
True if the specified version matches or if it is greater than the version of the current Windows OS. Otherwise, False.
"""
if windows_version.major > major:
return True
elif windows_version.major == major and windows_version.minor > minor:
return True
else:
return (
windows_version.major == major
and windows_version.minor == minor
and windows_version.build >= build
)
@staticmethod
def is_windows_vista_sp2_or_greater(windows_version) -> bool:
# From https://www.lifewire.com/windows-version-numbers-2625171
return WindowsVersionHelpers.is_windows_version_or_greater(windows_version, 6, 0, 6002)
@staticmethod
def is_windows_fall_creators_update_or_greater(windows_version) -> bool:
# From https://www.lifewire.com/windows-version-numbers-2625171
return WindowsVersionHelpers.is_windows_version_or_greater(windows_version, 10, 0, 16299)
def get_file_path_from_IDWriteFontFace(font_face: POINTER(IDWriteFontFace)) -> List[str]:
face_file_path = []
file_count = wintypes.UINT()
font_face.GetFiles(byref(file_count), None)
font_files = (POINTER(IDWriteFontFile) * file_count.value)()
font_face.GetFiles(byref(file_count), font_files)
for font_file in font_files:
font_file_reference_key = wintypes.LPCVOID()
font_file_reference_key_size = wintypes.UINT()
font_file.GetReferenceKey(byref(font_file_reference_key), byref(font_file_reference_key_size))
loader = POINTER(IDWriteFontFileLoader)()
font_file.GetLoader(byref(loader))
local_loader = loader.QueryInterface(IDWriteLocalFontFileLoader)
is_supported_font_type = wintypes.BOOL()
font_file_type = wintypes.UINT()
font_face_type = wintypes.UINT()
number_of_faces = wintypes.UINT()
font_file.Analyze(byref(is_supported_font_type), byref(font_file_type), byref(font_face_type), byref(number_of_faces))
path_len = wintypes.UINT()
local_loader.GetFilePathLengthFromKey(font_file_reference_key, font_file_reference_key_size, byref(path_len))
buffer = create_unicode_buffer(path_len.value + 1)
local_loader.GetFilePathFromKey(font_file_reference_key, font_file_reference_key_size, buffer, len(buffer))
face_file_path.append(str(Path(buffer.value).resolve()))
return face_file_path
def get_system_fonts_families_simulate_gdi() -> List[FontFamily]:
"""
It use the [RBIZ font family model](https://learn.microsoft.com/en-us/windows/win32/directwrite/font-selection#rbiz-font-family-model)
This method only simulates how GDI works. In some case, the result may be different from GDI.
"""
windows_version = getwindowsversion()
if not WindowsVersionHelpers.is_windows_vista_sp2_or_greater(windows_version):
raise OSError("This program only works on Windows Vista SP2 or more")
dwrite = DirectWrite()
fonts_families: Dict[str, FontFamily] = {}
dwrite_factory = POINTER(IDWriteFactory)()
dwrite.DWriteCreateFactory(DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_ISOLATED, dwrite_factory._iid_, byref(dwrite_factory))
sys_collection = POINTER(IDWriteFontCollection)()
dwrite_factory.GetSystemFontCollection1(byref(sys_collection), False)
for i in range(sys_collection.GetFontFamilyCount()):
family = POINTER(IDWriteFontFamily)()
sys_collection.GetFontFamily(i, byref(family))
for j in range(family.GetFontCount()):
try:
font = POINTER(IDWriteFont)()
family.GetFont(j, byref(font))
except COMError:
# If the file doesn't exist, DirectWrite raise an exception
continue
simulations = font.GetSimulations()
if simulations != DWRITE_FONT_SIMULATIONS.DWRITE_FONT_SIMULATIONS_NONE:
continue
family_names = POINTER(IDWriteLocalizedStrings)()
exists = wintypes.BOOL()
font.GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_ID.DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, byref(family_names), byref(exists))
family_names_str = dwrite.get_str_from_localized_strings(family_names)
if not exists:
raise ValueError("Could not fetch the family names")
if family_names_str in fonts_families:
font_familie = fonts_families[family_names_str]
else:
font_familie = FontFamily(family_names_str, [])
fonts_families[family_names_str] = font_familie
face_name = POINTER(IDWriteLocalizedStrings)()
exists = wintypes.BOOL()
font.GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_ID.DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES, byref(face_name), byref(exists))
face_name_str = dwrite.get_str_from_localized_strings(face_name)
if not exists:
raise ValueError("Could not fetch the family names")
font_face = POINTER(IDWriteFontFace)()
font.CreateFontFace(byref(font_face))
face_file_path = get_file_path_from_IDWriteFontFace(font_face)
font_familie.fonts.append(FontFace(face_file_path, face_name_str))
return fonts_families.values()
def get_system_fonts_families_WWS() -> List[FontFamily]:
"""
It use the [Weight-stretch-style font family model](https://learn.microsoft.com/en-us/windows/win32/directwrite/font-selection#weight-stretch-style-font-family-model)
"""
windows_version = getwindowsversion()
if not WindowsVersionHelpers.is_windows_vista_sp2_or_greater(windows_version):
raise OSError("This program only works on Windows Vista SP2 or more")
dwrite = DirectWrite()
fonts_families: List[FontFamily] = []
dwrite_factory = POINTER(IDWriteFactory)()
dwrite.DWriteCreateFactory(DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_ISOLATED, dwrite_factory._iid_, byref(dwrite_factory))
sys_collection = POINTER(IDWriteFontCollection)()
dwrite_factory.GetSystemFontCollection1(byref(sys_collection), False)
for i in range(sys_collection.GetFontFamilyCount()):
family = POINTER(IDWriteFontFamily)()
sys_collection.GetFontFamily(i, byref(family))
family_names = POINTER(IDWriteLocalizedStrings)()
family.GetFamilyNames(byref(family_names))
family_names_str = dwrite.get_str_from_localized_strings(family_names)
faces: List[FontFace] = []
for j in range(family.GetFontCount()):
try:
font = POINTER(IDWriteFont)()
family.GetFont(j, byref(font))
except COMError:
# If the file doesn't exist, DirectWrite raise an exception
continue
simulations = font.GetSimulations()
if simulations != DWRITE_FONT_SIMULATIONS.DWRITE_FONT_SIMULATIONS_NONE:
continue
face_name = POINTER(IDWriteLocalizedStrings)()
font.GetFaceNames(byref(face_name))
face_name_str = dwrite.get_str_from_localized_strings(face_name)
font_face = POINTER(IDWriteFontFace)()
font.CreateFontFace(byref(font_face))
face_file_path = get_file_path_from_IDWriteFontFace(font_face)
faces.append(FontFace(face_file_path, face_name_str))
fonts_families.append(FontFamily(family_names_str, faces))
return fonts_families
def get_system_fonts_families_typographic_model() -> List[FontFamily]:
"""
It use the [Typographic font family model](https://learn.microsoft.com/en-us/windows/win32/directwrite/font-selection#typographic-font-family-model)
"""
windows_version = getwindowsversion()
if not WindowsVersionHelpers.is_windows_fall_creators_update_or_greater(windows_version):
raise OSError("This program only works on Windows 10 Fall Creators Update or more")
dwrite = DirectWrite()
fonts_families: List[FontFamily] = []
dwrite_factory = POINTER(IDWriteFactory6)()
dwrite.DWriteCreateFactory(DWRITE_FACTORY_TYPE.DWRITE_FACTORY_TYPE_ISOLATED, dwrite_factory._iid_, byref(dwrite_factory))
sys_collection = POINTER(IDWriteFontCollection2)()
dwrite_factory.GetSystemFontCollection3(False, DWRITE_FONT_FAMILY_MODEL.DWRITE_FONT_FAMILY_MODEL_TYPOGRAPHIC, byref(sys_collection))
for i in range(sys_collection.GetFontFamilyCount()):
family = POINTER(IDWriteFontFamily)()
sys_collection.GetFontFamily(i, byref(family))
family_names = POINTER(IDWriteLocalizedStrings)()
family.GetFamilyNames(byref(family_names))
family_names_str = dwrite.get_str_from_localized_strings(family_names)
faces: List[FontFace] = []
for j in range(family.GetFontCount()):
try:
font = POINTER(IDWriteFont)()
family.GetFont(j, byref(font))
except COMError:
# If the file doesn't exist, DirectWrite raise an exception
continue
simulations = font.GetSimulations()
if simulations != DWRITE_FONT_SIMULATIONS.DWRITE_FONT_SIMULATIONS_NONE:
continue
face_file_path: List[Path] = []
face_name = POINTER(IDWriteLocalizedStrings)()
font.GetFaceNames(byref(face_name))
face_name_str = dwrite.get_str_from_localized_strings(face_name)
font_face = POINTER(IDWriteFontFace)()
font.CreateFontFace(byref(font_face))
face_file_path = get_file_path_from_IDWriteFontFace(font_face)
faces.append(FontFace(face_file_path, face_name_str))
fonts_families.append(FontFamily(family_names_str, faces))
return fonts_families
def main():
# use get_system_fonts_families_simulate_gdi, get_system_fonts_families_WWS or get_system_fonts_families_typographic_model
font_families = get_system_fonts_families_simulate_gdi()
font_families_dict = [asdict(font_family) for font_family in get_system_fonts_families_simulate_gdi()]
json_string = json.dumps(font_families_dict, indent=4)
print(json_string)
if __name__ == "__main__":
main()