1- from ctypes import c_bool , c_char_p , c_long , c_void_p , cdll , create_string_buffer , util
2- from os import pathconf
1+ from ctypes import c_bool , c_char_p , c_int , c_long , c_uint32 , c_void_p , cdll , create_string_buffer , util
2+ from enum import IntEnum
33from pathlib import Path
44from platform import mac_ver
55from typing import Set
66from .exceptions import OSNotSupported
77from .system_fonts import SystemFonts
88
99
10+ class CFURLPathStyle (IntEnum ):
11+ # https://developer.apple.com/documentation/corefoundation/cfurlpathstyle?language=objc
12+ kCFURLPOSIXPathStyle = 0
13+ kCFURLHFSPathStyle = 1
14+ kCFURLWindowsPathStyle = 2
15+
16+
1017class MacFonts (SystemFonts ):
1118 _core_foundation = None
1219 _core_text = None
1320 # CoreText has an API to get the format of the font: https://developer.apple.com/documentation/coretext/ctfontformat
1421 # But, the API is "semi-broken" since it says .dfont are TrueType. This is kinda true, but it is not a behaviour that we want.
1522 # So, we only check the file extension and see if it is valid.
1623 VALID_FONT_FORMATS = ["ttf" , "otf" , "ttc" ]
24+ kCFStringEncodingUTF8 = 0x08000100 # https://developer.apple.com/documentation/corefoundation/cfstringbuiltinencodings/kcfstringencodingutf8?language=objc
1725
1826 def get_system_fonts_filename () -> Set [str ]:
1927 if MacVersionHelpers .is_mac_version_or_greater (10 , 6 ):
@@ -25,28 +33,37 @@ def get_system_fonts_filename() -> Set[str]:
2533 font_urls = MacFonts ._core_text .CTFontManagerCopyAvailableFontURLs ()
2634 font_count = MacFonts ._core_foundation .CFArrayGetCount (font_urls )
2735
28- max_length = pathconf ("/" , "PC_PATH_MAX" )
29-
3036 for i in range (font_count ):
3137 url = MacFonts ._core_foundation .CFArrayGetValueAtIndex (font_urls , i )
3238
33- file_name_ptr = create_string_buffer (max_length )
34- no_error = MacFonts ._core_foundation .CFURLGetFileSystemRepresentation (url , True , file_name_ptr , max_length )
35-
36- if no_error :
37- filename = file_name_ptr .value .decode ()
39+ filename = MacFonts ._cfstring_to_string (MacFonts ._core_text .CFURLCopyFileSystemPath (url , CFURLPathStyle .kCFURLPOSIXPathStyle ))
3840
39- if Path (filename ).suffix .lstrip ("." ).strip ().lower () in MacFonts .VALID_FONT_FORMATS :
40- fonts_filename .add (filename )
41- else :
42- raise Exception ("An unexpected error has occurred while decoded the CFURL." )
41+ if Path (filename ).suffix .lstrip ("." ).strip ().lower () in MacFonts .VALID_FONT_FORMATS :
42+ fonts_filename .add (filename )
4343
4444 MacFonts ._core_foundation .CFRelease (font_urls )
4545 else :
4646 raise OSNotSupported ("FindSystemFontsFilename only works on Mac 10.6 or more" )
4747
4848 return fonts_filename
4949
50+ @staticmethod
51+ def _cfstring_to_string (cfstring : c_void_p ) -> str :
52+ """
53+ Parameters:
54+ cfstring (c_void_p): An CFString instance.
55+ Returns:
56+ The decoded CFString.
57+ """
58+ length = MacFonts ._core_foundation .CFStringGetLength (cfstring )
59+ size = MacFonts ._core_foundation .CFStringGetMaximumSizeForEncoding (length , MacFonts .kCFStringEncodingUTF8 )
60+ buffer = create_string_buffer (size + 1 )
61+ result = MacFonts ._core_foundation .CFStringGetCString (cfstring , buffer , len (buffer ), MacFonts .kCFStringEncodingUTF8 )
62+ if result :
63+ return str (buffer .value , 'utf-8' )
64+ else :
65+ raise Exception ("An unexpected error has occurred while decoded the CFString." )
66+
5067 @staticmethod
5168 def _load_core_library ():
5269 core_foundation_library_name = util .find_library ("CoreFoundation" )
@@ -64,6 +81,8 @@ def _load_core_library():
6481 MacFonts ._core_text = cdll .LoadLibrary (core_text_library_name )
6582
6683 CFIndex = c_long
84+ CFStringEncoding = c_uint32
85+ CFURLPathStyle = c_int
6786
6887 # https://developer.apple.com/documentation/corefoundation/1521153-cfrelease
6988 MacFonts ._core_foundation .CFRelease .restype = c_void_p
@@ -77,14 +96,25 @@ def _load_core_library():
7796 MacFonts ._core_foundation .CFArrayGetValueAtIndex .restype = c_void_p
7897 MacFonts ._core_foundation .CFArrayGetValueAtIndex .argtypes = [c_void_p , CFIndex ]
7998
80- # https://developer.apple.com/documentation/corefoundation/1541515-cfurlgetfilesystemrepresentation?language=objc
81- MacFonts ._core_foundation .CFURLGetFileSystemRepresentation .restype = c_bool
82- MacFonts ._core_foundation .CFURLGetFileSystemRepresentation .argtypes = [c_void_p , c_bool , c_char_p , CFIndex ]
99+ # https://developer.apple.com/documentation/corefoundation/1542853-cfstringgetlength?language=objc
100+ MacFonts ._core_foundation .CFStringGetLength .restype = CFIndex
101+ MacFonts ._core_foundation .CFStringGetLength .argtypes = [c_void_p ]
102+
103+ # https://developer.apple.com/documentation/corefoundation/1542143-cfstringgetmaximumsizeforencodin?language=objc
104+ MacFonts ._core_foundation .CFStringGetMaximumSizeForEncoding .restype = CFIndex
105+ MacFonts ._core_foundation .CFStringGetMaximumSizeForEncoding .argtypes = [c_void_p , CFStringEncoding ]
106+
107+ # https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring?language=objc
108+ MacFonts ._core_foundation .CFStringGetCString .restype = c_bool
109+ MacFonts ._core_foundation .CFStringGetCString .argtypes = [c_void_p , c_char_p , CFIndex , CFStringEncoding ]
83110
84111 # https://developer.apple.com/documentation/coretext/1499478-ctfontmanagercopyavailablefontur?language=objc
85112 MacFonts ._core_text .CTFontManagerCopyAvailableFontURLs .restype = c_void_p
86113 MacFonts ._core_text .CTFontManagerCopyAvailableFontURLs .argtypes = []
87114
115+ # https://developer.apple.com/documentation/corefoundation/1541581-cfurlcopyfilesystempath?language=objc
116+ MacFonts ._core_text .CFURLCopyFileSystemPath .restype = c_void_p
117+ MacFonts ._core_text .CFURLCopyFileSystemPath .argtypes = [c_void_p , CFURLPathStyle ]
88118
89119class MacVersionHelpers :
90120 @staticmethod
0 commit comments