diff --git a/find_system_fonts_filename/mac_fonts.py b/find_system_fonts_filename/mac_fonts.py index f0a5335..b0a57ab 100644 --- a/find_system_fonts_filename/mac_fonts.py +++ b/find_system_fonts_filename/mac_fonts.py @@ -1,5 +1,5 @@ -from ctypes import c_bool, c_char_p, c_long, c_void_p, cdll, create_string_buffer, util -from os import pathconf +from ctypes import c_bool, c_char_p, c_int, c_long, c_uint32, c_void_p, cdll, create_string_buffer, util +from enum import IntEnum from pathlib import Path from platform import mac_ver from typing import Set @@ -7,6 +7,13 @@ from .system_fonts import SystemFonts +class CFURLPathStyle(IntEnum): + # https://developer.apple.com/documentation/corefoundation/cfurlpathstyle?language=objc + kCFURLPOSIXPathStyle = 0 + kCFURLHFSPathStyle = 1 + kCFURLWindowsPathStyle = 2 + + class MacFonts(SystemFonts): _core_foundation = None _core_text = None @@ -14,6 +21,7 @@ class MacFonts(SystemFonts): # 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. # So, we only check the file extension and see if it is valid. VALID_FONT_FORMATS = ["ttf", "otf", "ttc"] + kCFStringEncodingUTF8 = 0x08000100 # https://developer.apple.com/documentation/corefoundation/cfstringbuiltinencodings/kcfstringencodingutf8?language=objc def get_system_fonts_filename() -> Set[str]: if MacVersionHelpers.is_mac_version_or_greater(10, 6): @@ -25,21 +33,13 @@ def get_system_fonts_filename() -> Set[str]: font_urls = MacFonts._core_text.CTFontManagerCopyAvailableFontURLs() font_count = MacFonts._core_foundation.CFArrayGetCount(font_urls) - max_length = pathconf("/", "PC_PATH_MAX") - for i in range(font_count): url = MacFonts._core_foundation.CFArrayGetValueAtIndex(font_urls, i) - file_name_ptr = create_string_buffer(max_length) - no_error = MacFonts._core_foundation.CFURLGetFileSystemRepresentation(url, True, file_name_ptr, max_length) - - if no_error: - filename = file_name_ptr.value.decode() + filename = MacFonts._cfstring_to_string(MacFonts._core_text.CFURLCopyFileSystemPath(url, CFURLPathStyle.kCFURLPOSIXPathStyle)) - if Path(filename).suffix.lstrip(".").strip().lower() in MacFonts.VALID_FONT_FORMATS: - fonts_filename.add(filename) - else: - raise Exception("An unexpected error has occurred while decoded the CFURL.") + if Path(filename).suffix.lstrip(".").strip().lower() in MacFonts.VALID_FONT_FORMATS: + fonts_filename.add(filename) MacFonts._core_foundation.CFRelease(font_urls) else: @@ -47,6 +47,23 @@ def get_system_fonts_filename() -> Set[str]: return fonts_filename + @staticmethod + def _cfstring_to_string(cfstring: c_void_p) -> str: + """ + Parameters: + cfstring (c_void_p): An CFString instance. + Returns: + The decoded CFString. + """ + length = MacFonts._core_foundation.CFStringGetLength(cfstring) + size = MacFonts._core_foundation.CFStringGetMaximumSizeForEncoding(length, MacFonts.kCFStringEncodingUTF8) + buffer = create_string_buffer(size + 1) + result = MacFonts._core_foundation.CFStringGetCString(cfstring, buffer, len(buffer), MacFonts.kCFStringEncodingUTF8) + if result: + return str(buffer.value, 'utf-8') + else: + raise Exception("An unexpected error has occurred while decoded the CFString.") + @staticmethod def _load_core_library(): core_foundation_library_name = util.find_library("CoreFoundation") @@ -64,6 +81,8 @@ def _load_core_library(): MacFonts._core_text = cdll.LoadLibrary(core_text_library_name) CFIndex = c_long + CFStringEncoding = c_uint32 + CFURLPathStyle = c_int # https://developer.apple.com/documentation/corefoundation/1521153-cfrelease MacFonts._core_foundation.CFRelease.restype = c_void_p @@ -77,14 +96,25 @@ def _load_core_library(): MacFonts._core_foundation.CFArrayGetValueAtIndex.restype = c_void_p MacFonts._core_foundation.CFArrayGetValueAtIndex.argtypes = [c_void_p, CFIndex] - # https://developer.apple.com/documentation/corefoundation/1541515-cfurlgetfilesystemrepresentation?language=objc - MacFonts._core_foundation.CFURLGetFileSystemRepresentation.restype = c_bool - MacFonts._core_foundation.CFURLGetFileSystemRepresentation.argtypes = [c_void_p, c_bool, c_char_p, CFIndex] + # https://developer.apple.com/documentation/corefoundation/1542853-cfstringgetlength?language=objc + MacFonts._core_foundation.CFStringGetLength.restype = CFIndex + MacFonts._core_foundation.CFStringGetLength.argtypes = [c_void_p] + + # https://developer.apple.com/documentation/corefoundation/1542143-cfstringgetmaximumsizeforencodin?language=objc + MacFonts._core_foundation.CFStringGetMaximumSizeForEncoding.restype = CFIndex + MacFonts._core_foundation.CFStringGetMaximumSizeForEncoding.argtypes = [c_void_p, CFStringEncoding] + + # https://developer.apple.com/documentation/corefoundation/1542721-cfstringgetcstring?language=objc + MacFonts._core_foundation.CFStringGetCString.restype = c_bool + MacFonts._core_foundation.CFStringGetCString.argtypes = [c_void_p, c_char_p, CFIndex, CFStringEncoding] # https://developer.apple.com/documentation/coretext/1499478-ctfontmanagercopyavailablefontur?language=objc MacFonts._core_text.CTFontManagerCopyAvailableFontURLs.restype = c_void_p MacFonts._core_text.CTFontManagerCopyAvailableFontURLs.argtypes = [] + # https://developer.apple.com/documentation/corefoundation/1541581-cfurlcopyfilesystempath?language=objc + MacFonts._core_text.CFURLCopyFileSystemPath.restype = c_void_p + MacFonts._core_text.CFURLCopyFileSystemPath.argtypes = [c_void_p, CFURLPathStyle] class MacVersionHelpers: @staticmethod