FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[MNT]: findSystemFonts - Use system API instead to look into folder · Issue #24001 · matplotlib/matplotlib · GitHub

Repository navigation

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

Closed

Description

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.

Activity

  1. ashb commented on Oct 26, 2022

    So I've been working on the Windows part of this (it turns out it was slightly more complex than the functions you highlighted, but that gave me the pointers I needed):

    In [1]: __import__('windows_fonts').FontCollection()
    Out[1]: <windows_fonts.FontCollection at 0x29a415bdb90>
    
    In [2]: for f in _1:
       ...:     print(repr(f))
       ...:
    <FontFamily name="Iosevka Term">
    <FontFamily name="Cascadia Code">
    <FontFamily name="Cascadia Mono">
    <FontFamily name="Arial">
    <FontFamily name="Bahnschrift">
    <FontFamily name="Calibri">
    <FontFamily name="Cambria">
    <FontFamily name="Cambria Math">
    <FontFamily name="Candara">
    <FontFamily name="Comic Sans MS">
    <FontFamily name="Consolas">
    <FontFamily name="Constantia">
    <FontFamily name="Corbel">
    <FontFamily name="Courier New">
    <FontFamily name="Ebrima">
    <FontFamily name="Franklin Gothic">
    <FontFamily name="Gabriola">
    ...
    <FontFamily name="Marlett">
    
    In [3]: list(f)
    Out[3]:
    [<FontVariant family=<FontFamily name="Marlett">, style=Style.NORMAL weight=Weight.MEDIUM>,
     <FontVariant family=<FontFamily name="Marlett">, style=Style.OBLIQUE weight=Weight.MEDIUM>,
     <FontVariant family=<FontFamily name="Marlett">, style=Style.NORMAL weight=Weight.BOLD>,
     <FontVariant family=<FontFamily name="Marlett">, style=Style.OBLIQUE weight=Weight.BOLD>]
    
    In [4]: list(map(lambda f: f.filename, f))
    Out[4]:
    ['C:\\WINDOWS\\FONTS\\MARLETT.TTF',
     'C:\\WINDOWS\\FONTS\\MARLETT.TTF',
     'C:\\WINDOWS\\FONTS\\MARLETT.TTF',
     'C:\\WINDOWS\\FONTS\\MARLETT.TTF']
    

    (I'm planning to release this as a stand-alone python module as I actually need it for a separate project)

  2. moi15moi commented on Oct 26, 2022

    Author

    it turns out it was slightly more complex than the functions you highlighted, but that gave me the pointers I needed

    I am sorry. I thought it was the right logic.
    I hope i was in the right direction, so you did not waste too much time on that part.

    By curiosity, which method have you used?

    • Logic for Windows 10 to this day
      OR
    • Logic for Windows Vista to this day
  3. ashb commented on Oct 27, 2022

    @moi15moi You had the right functions, but the issue was that GetFilePathFromKey needs to be called on an instance of IDWriteLocalFontFileLoader, and unlike most other COM objects where you can just do CoCreateInstance to get one, the only way I've found (from searching) is to get an instance of this is to load a local font file, then ask for it's loader.

    And I think I've used the Vista+ approach.

  4. moi15moi commented on Oct 27, 2022

    Author

    Ok, then, i think you could have called GetLoader from the object you have with GetFiles

    Here is the updated version

    Logic for Windows Vista to this day:

    Here is the pseudo-code

    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()
    		
    		files = font_face.GetFiles()
                    loader = files.GetLoader()
                    reference_key, reference_key_size = files.GetReferenceKey()
    		path = loader.GetFilePathFromKey(reference_key, reference_key_size)

    Edit: GetLoader return an instance of IDWriteFontFileLoader. It doesn't return of instance of IDWriteLocalFontFileLoader.

    Sorry, i missread that.

    But, i just found this: https://social.msdn.microsoft.com/Forums/vstudio/en-US/8f57b186-9cf6-417a-8923-092b406d94ea/obtain-filename-from-directwrite-font?forum=windowsuidevelopment

  5. ashb commented on Oct 31, 2022

    First alpha release available at https://pypi.org/project/windows-fonts/0.1.0a1/ pip install --pre windows-fonts

  6. kostyafarber commented on Nov 8, 2022

    Contributor

    Hey is anybody working on the mac portion of this? If not I'd like to have a crack.

  7. greglucas commented on Nov 9, 2022

    Contributor

    @kostyafarber, it doesn't look like anyone is working on the mac side of this and in general we have very few people looking into the objective c portion of the code, so I would say go for it! You can also find other mac issues with this label: GUI: MacOSX

  8. kostyafarber commented on Nov 10, 2022

    Contributor

    For the windows version would we need to write up the code in c++ and write a Python binding and use it in font_manager.py?

    For the Mac version are we happy to explore using https://pypi.org/project/pyobjc-framework-CoreText/ or would we prefer to use a binding as well?

  9. tacaswell commented on Nov 16, 2022

    Member

    I would much rather have bindings in Matplotlib than pick up a dependency for this. If it is so complex that we need a dependency rather than just implementing it, than I think that continuing to search the directories is fine.

  10. moi15moi commented on Apr 28, 2023

    Author

    I decided to do a repos to show you how I implemented it: https://github.com/moi15moi/FindSystemFontsFilename

    I haven't open an PR here since font_manager can search for .afm file which cannot be done with DirectWrite and CoreText (it may be possible with FontConfig, but I haven't check)

  11. github-actions commented on Jun 15, 2026

    This issue has been marked "inactive" because it has been 365 days since the last comment. If this issue is still present in recent Matplotlib releases, or the feature request is still wanted, please leave a comment and this label will be removed. If there are no updates in another 30 days, this issue will be automatically closed, but you are free to re-open or create a new issue if needed. We value issue reports, and this procedure is meant to help us resurface and prioritize issues that have not been addressed yet, not make them disappear. Thanks for your help!

  12. added
    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.
    on Jul 16, 2026
  13. QuLogic commented on Jul 16, 2026

    Member

    Windows uses the registry since #24655, and macOS will use CoreText once #31977 is in.

  14. linked a pull request that will close this issueUse CoreText to find macOS fonts #31977on Jul 16, 2026
  15. removed
    status: inactiveMarked by the “Stale” Github Action
    status: closed as inactiveIssues closed by the "Stale" Github Action. Please comment on any you think should still be open.
    on Jul 16, 2026
  16. added this to the v3.12.0 milestone on Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL