Title: [MNT]: Possibly use CoreText to find fonts for faster performance · Issue #31965 · matplotlib/matplotlib · GitHub
Open Graph Title: [MNT]: Possibly use CoreText to find fonts for faster performance · Issue #31965 · matplotlib/matplotlib
X Title: [MNT]: Possibly use CoreText to find fonts for faster performance · Issue #31965 · matplotlib/matplotlib
Description: Summary While investigating #28249, I became curious why system_profiler was taking 7 seconds on my machine to simply list installed fonts. The problem is that it needs to determine the localized name of the font as part of the XML outpu...
Open Graph Description: Summary While investigating #28249, I became curious why system_profiler was taking 7 seconds on my machine to simply list installed fonts. The problem is that it needs to determine the localized n...
X Description: Summary While investigating #28249, I became curious why system_profiler was taking 7 seconds on my machine to simply list installed fonts. The problem is that it needs to determine the localized n...
Opengraph URL: https://github.com/matplotlib/matplotlib/issues/31965
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"[MNT]: Possibly use CoreText to find fonts for faster performance","articleBody":"### Summary\n\nWhile investigating #28249, I became curious why `system_profiler` was taking 7 seconds on my machine to simply list installed fonts. The problem is that it needs to determine the localized name of the font as part of the XML output, which involves loading each font.\n\nIf we only care about the path, we should be able to query CoreText directly. The C code would look something like this:\n\n\u003cdetails\u003e\n\u003csummary\u003eC Program\u003c/summary\u003e\n\n```c\n#include \u003cCoreFoundation/CoreFoundation.h\u003e\n#include \u003cCoreText/CoreText.h\u003e\n#include \u003csys/param.h\u003e\n\nint main(int argc, const char *argv[])\n{\n CTFontCollectionRef collection = CTFontCollectionCreateFromAvailableFonts(NULL);\n CFArrayRef descriptors = collection ? CTFontCollectionCreateMatchingFontDescriptors(collection) : NULL;\n CFIndex count = descriptors ? CFArrayGetCount(descriptors) : 0;\n\n for (CFIndex i = 0; i \u003c count; i++) {\n char UTF8Path[MAXPATHLEN * 4];\n CTFontDescriptorRef descriptor = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descriptors, i);\n\n CFURLRef url = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);\n CFStringRef path = NULL;\n \n if (url) {\n path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);\n CFRelease(url);\n }\n \n if (path) {\n CFStringGetCString(path, UTF8Path, sizeof(UTF8Path), kCFStringEncodingUTF8);\n printf(\"%s\\n\", UTF8Path);\n CFRelease(path);\n }\n }\n\n printf(\"\\n%ld fonts total.\\n\", (long)count);\n\n if (descriptors) CFRelease(descriptors);\n if (collection) CFRelease(collection);\n\n return 0;\n}\n```\n\n\u003c/details\u003e\n\nThat returns 1206 fonts in 63ms.\n\n---\n\nI asked Claude to convert to a Python script using `ctypes`:\n\n\u003cdetails\u003e\n\u003csummary\u003ePython Script\u003c/summary\u003e\n\n```python\nimport ctypes\nimport ctypes.util\n\nCoreText = ctypes.cdll.LoadLibrary(ctypes.util.find_library(\"CoreText\"))\nCF = ctypes.cdll.LoadLibrary(ctypes.util.find_library(\"CoreFoundation\"))\n\nCF.CFArrayGetCount.argtypes = [ctypes.c_void_p]\nCF.CFArrayGetCount.restype = ctypes.c_long\nCF.CFArrayGetValueAtIndex.argtypes = [ctypes.c_void_p, ctypes.c_long]\nCF.CFArrayGetValueAtIndex.restype = ctypes.c_void_p\nCF.CFRelease.argtypes = [ctypes.c_void_p]\nCF.CFRelease.restype = None\nCF.CFStringGetCString.argtypes = [ctypes.c_void_p, ctypes.c_char_p, ctypes.c_long, ctypes.c_uint32]\nCF.CFStringGetCString.restype = ctypes.c_bool\nCF.CFURLCopyFileSystemPath.argtypes = [ctypes.c_void_p, ctypes.c_long]\nCF.CFURLCopyFileSystemPath.restype = ctypes.c_void_p\n\nCoreText.CTFontCollectionCreateFromAvailableFonts.argtypes = [ctypes.c_void_p]\nCoreText.CTFontCollectionCreateFromAvailableFonts.restype = ctypes.c_void_p\nCoreText.CTFontCollectionCreateMatchingFontDescriptors.argtypes = [ctypes.c_void_p]\nCoreText.CTFontCollectionCreateMatchingFontDescriptors.restype = ctypes.c_void_p\nCoreText.CTFontDescriptorCopyAttribute.argtypes = [ctypes.c_void_p, ctypes.c_void_p]\nCoreText.CTFontDescriptorCopyAttribute.restype = ctypes.c_void_p\n\nkCTFontURLAttribute = ctypes.c_void_p.in_dll(CoreText, \"kCTFontURLAttribute\")\nkCFURLPOSIXPathStyle = 0\nkCFStringEncodingUTF8 = 0x08000100\n\ndef cfstring_to_str(cfstr):\n buf = ctypes.create_string_buffer(4096)\n CF.CFStringGetCString(cfstr, buf, 4096, kCFStringEncodingUTF8)\n return buf.value.decode(\"utf-8\")\n\ndef list_fonts():\n collection = CoreText.CTFontCollectionCreateFromAvailableFonts(None)\n descriptors = CoreText.CTFontCollectionCreateMatchingFontDescriptors(collection)\n count = CF.CFArrayGetCount(descriptors)\n paths = []\n\n for i in range(count):\n descriptor = CF.CFArrayGetValueAtIndex(descriptors, i)\n url = CoreText.CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute.value)\n if url:\n cfpath = CF.CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle)\n if cfpath:\n paths.append(cfstring_to_str(cfpath))\n CF.CFRelease(cfpath)\n CF.CFRelease(url)\n\n CF.CFRelease(descriptors)\n CF.CFRelease(collection)\n return paths\n\nprint(f\"{len(list_fonts())}\")\n```\n\u003c/details\u003e\n\nThat's a bit slower at ~90ms, but better than the 7 seconds it takes for `system_profiler`. Is it worth the added complexity of using ctypes in font_manager.py?\n\n### Proposed fix\n\n_No response_","author":{"url":"https://github.com/iccir","@type":"Person","name":"iccir"},"datePublished":"2026-06-28T03:52:59.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":2},"url":"https://github.com/31965/matplotlib/issues/31965"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:78f222ae-dbb6-64df-7aad-46458d9b8ef1 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | A3CC:8AE8C:7E0E030:B0A68F7:6A50480C |
| html-safe-nonce | 03c03823aeb1f984fd09e396fbc008cce4bf45d8e00a64471291f6ef269ce672 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBM0NDOjhBRThDOjdFMEUwMzA6QjBBNjhGNzo2QTUwNDgwQyIsInZpc2l0b3JfaWQiOiI3NzY4NTgzNzY1NTY0MDgyMTg4IiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 109fe5726b6906c69d763fc061f7d60f6023f7b9002c530f5b7f74d6d8039fee |
| hovercard-subject-tag | issue:4761002817 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/matplotlib/matplotlib/31965/issue_layout |
| twitter:image | https://opengraph.githubassets.com/3bfbcbf53c76b733c1c3a4eb152a1bd7f46d8ea3284d692c613d5e3a1e9eb38f/matplotlib/matplotlib/issues/31965 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/3bfbcbf53c76b733c1c3a4eb152a1bd7f46d8ea3284d692c613d5e3a1e9eb38f/matplotlib/matplotlib/issues/31965 |
| og:image:alt | Summary While investigating #28249, I became curious why system_profiler was taking 7 seconds on my machine to simply list installed fonts. The problem is that it needs to determine the localized n... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | iccir |
| hostname | github.com |
| expected-hostname | github.com |
| None | d6dc8294eb500fa36bbc57472d61fe87c522f9c3c1d64f70f4926f66a66a7efb |
| turbo-cache-control | no-preview |
| go-import | github.com/matplotlib/matplotlib git https://github.com/matplotlib/matplotlib.git |
| octolytics-dimension-user_id | 215947 |
| octolytics-dimension-user_login | matplotlib |
| octolytics-dimension-repository_id | 1385122 |
| octolytics-dimension-repository_nwo | matplotlib/matplotlib |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 1385122 |
| octolytics-dimension-repository_network_root_nwo | matplotlib/matplotlib |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 5741a45fe07e5f377d0d4c524a92e3056acc51bb |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width