Skip to content

[MNT]: findSystemFonts - Use system API instead to look into folder #24001

Open
@moi15moi

Description

@moi15moi

Summary

Currently, findSystemFonts look at all the file in the specified folders for macos and windows.

This is not a very good method, because a font in a "Font folder" does not necessarily need to be picked up.
A very good example of that is in this issue: #22859

This is why I propose to use CoreText (for mac) and DirectWrite (for Windows).
These are API provided by Apple and Microsoft.

Proposed fix

CoreText - For MAC

In this example, I use this library: https://pypi.org/project/pyobjc-framework-CoreText/

import CoreText

fontURLs = CoreText.CTFontManagerCopyAvailableFontURLs()
fontPath = set()

for i in range(CoreText.CFArrayGetCount(fontURLs)):
    url = CoreText.CFArrayGetValueAtIndex(fontURLs, i)

    fontPath.add(str(url.path()))

DirectWrite - For Windows

I don't know much C/C++, so I can't give an good example.
On recent Windows build, it is pretty simple to get the font path with DirectWrite, but since matplotlib seems to support Windows 7, we would maybe need to use the "complex" solution.

But, here is how I understand it (I have no proof that it works):

Logic for to support only Windows 10:

Here is an "pseudo-code" of how I see it.

font_collection = GetSystemFontCollection()
font_set = font_collection.GetFontSet()

for i in range(font_set.GetFontCount()):
	font_face_reference = font_set.GetFontFaceReference(i)
	font_file = font_face_reference.GetFontFile()
	reference_key, reference_key_size = font_file.GetReferenceKey()
	path = GetFilePathFromKey(reference_key, reference_key_size)

Logic for Windows Vista to this day:

font_collection = GetSystemFontCollection()
 
for i in range(font_collection.GetFontFamilyCount()):
	font_family = font_collection.GetFontFamily(i)
	matching_fonts = GetMatchingFonts(ANY_WEIGHT, ANY_STRETCH, ANY_STYLE)
	
	for j in range(matching_fonts.GetFontCount()):
		font = matching_fonts.GetFont(j)
		font_face = font.CreateFontFace()
		
		for file in font_face.GetFiles():
			reference_key, reference_key_size = font_file.GetReferenceKey()
			path = GetFilePathFromKey(reference_key, reference_key_size)

I have try to see if the GDI api could help us to get the path of the fonts, but from what I can see, it is not an available feature: https://learn.microsoft.com/en-us/windows/win32/gdi/font-and-text-functions

FontConfig (Bonus) - For Linux

Currently, matplotlib parse the result from subprocess.

It would be an good idea to directly use FontConfig. See this answer from stackoverflow to know how to do it: https://stackoverflow.com/a/14634033

I did not found any FontConfig bindings library that are still maintained, so it would need to be implemented with Cython.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions