MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FontDiscovery.cpp
Go to the documentation of this file.
1#include "FontDiscovery.hpp"
2
4
6
7#if defined(MAYAFLUX_PLATFORM_LINUX)
8#include <fontconfig/fontconfig.h>
9#endif
10
11namespace MayaFlux::Portal::Text {
12
13#if defined(MAYAFLUX_PLATFORM_LINUX)
14
15std::optional<std::string> find_font(std::string_view family, std::string_view style)
16{
17 FcConfig* cfg = FcInitLoadConfigAndFonts();
18 if (!cfg) {
19 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "FcInitLoadConfigAndFonts failed");
20 return std::nullopt;
21 }
22
23 FcPattern* pat = FcNameParse(
24 reinterpret_cast<const FcChar8*>(std::string(family).c_str()));
25 if (!style.empty()) {
26 FcPatternAddString(pat, FC_STYLE,
27 reinterpret_cast<const FcChar8*>(std::string(style).c_str()));
28 }
29 FcConfigSubstitute(cfg, pat, FcMatchPattern);
30 FcDefaultSubstitute(pat);
31
32 FcResult result {};
33 FcPattern* match = FcFontMatch(cfg, pat, &result);
34
35 std::optional<std::string> out;
36 if (match) {
37 FcChar8* file_path {};
38 if (FcPatternGetString(match, FC_FILE, 0, &file_path) == FcResultMatch) {
39 out = reinterpret_cast<const char*>(file_path);
41 }
42 FcPatternDestroy(match);
43 } else {
44 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "no fontconfig match for '{}'", family);
45 }
46
47 FcPatternDestroy(pat);
48 FcConfigDestroy(cfg);
49 return out;
50}
51
52#elif defined(MAYAFLUX_PLATFORM_MACOS)
53
54namespace fs = std::filesystem;
55
56std::optional<std::string> find_font(std::string_view family, std::string_view style)
57{
58 std::string query = std::string(family);
59 if (!style.empty()) {
60 query += ":style=" + std::string(style);
61 }
62
63 const std::string cmd = "fc-match --format='%{file}' '" + query + "' 2>/dev/null";
64 std::string result = Platform::SystemConfig::exec_command(cmd.c_str());
66
67 if (!result.empty() && fs::exists(result)) {
69 return result;
70 }
71
72 const std::vector<fs::path> search_dirs = {
73 fs::path(Platform::safe_getenv("HOME")) / "Library" / "Fonts",
74 "/Library/Fonts",
75 "/System/Library/Fonts",
76 "/System/Library/Fonts/Supplemental"
77 };
78
79 std::string lower_family(family);
80 std::ranges::transform(lower_family,
81 lower_family.begin(), [](unsigned char c) { return std::tolower(c); });
82 std::erase_if(lower_family,
83 [](unsigned char c) { return std::isspace(c); });
84
85 for (const auto& dir : search_dirs) {
86 if (!fs::exists(dir)) {
87 continue;
88 }
89 for (const auto& entry : fs::directory_iterator(dir)) {
90 const std::string ext = entry.path().extension().string();
91 if (ext != ".ttf" && ext != ".otf" && ext != ".ttc" && ext != ".otc" && ext != ".dfont") {
92 continue;
93 }
94 std::string lower_name = entry.path().filename().string();
95 std::ranges::transform(lower_name,
96 lower_name.begin(), [](unsigned char c) { return std::tolower(c); });
97 std::erase_if(lower_name,
98 [](unsigned char c) { return std::isspace(c); });
99 if (lower_name.find(lower_family) != std::string::npos) {
100 MF_DEBUG(Journal::Component::Portal, Journal::Context::Init, "{} -> {}", family, entry.path().string());
101 return entry.path().string();
102 }
103 }
104 }
105
106 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "no match for '{}'", family);
107 return std::nullopt;
108}
109
110#elif defined(MAYAFLUX_PLATFORM_WINDOWS)
111
112#include <windows.h>
113
114namespace fs = std::filesystem;
115
116std::optional<std::string> find_font(std::string_view family, std::string_view /*style*/)
117{
118 char windir[MAX_PATH] {};
119 GetWindowsDirectoryA(windir, MAX_PATH);
120 const fs::path fonts_dir = fs::path(windir) / "Fonts";
121
122 if (!fs::exists(fonts_dir)) {
123 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "fonts directory not found at {}", fonts_dir.string());
124 return std::nullopt;
125 }
126
127 std::string lower_family(family);
128 std::ranges::transform(lower_family, lower_family.begin(),
129 [](unsigned char c) { return std::tolower(c); });
130 std::erase_if(lower_family,
131 [](unsigned char c) { return std::isspace(c); });
132
133 for (const auto& entry : fs::directory_iterator(fonts_dir)) {
134 const std::string ext = entry.path().extension().string();
135 if (ext != ".ttf" && ext != ".otf") {
136 continue;
137 }
138 std::string lower_name = entry.path().filename().string();
139 std::ranges::transform(lower_name, lower_name.begin(),
140 [](unsigned char c) { return std::tolower(c); });
141 if (lower_name.find(lower_family) != std::string::npos) {
142 MF_DEBUG(Journal::Component::Portal, Journal::Context::Init, "{} -> {}", family, entry.path().string());
143 return entry.path().string();
144 }
145 }
146
147 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "no match for '{}'", family);
148 return std::nullopt;
149}
150
151#endif
152
153} // namespace MayaFlux::Portal::Text
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
vk::CommandBuffer cmd
std::vector< float > * out
static void trim_output(std::string &str)
static std::string exec_command(const char *cmd)
@ Init
Engine/subsystem initialization.
@ Portal
High-level user-facing API layer.
std::string safe_getenv(const char *var)
MAYAFLUX_API std::optional< std::string > find_font(std::string_view family, std::string_view style={})
Locate a system font file by family name and optional style.