MayaFlux 0.4.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
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(), ::tolower);
82
83 for (const auto& dir : search_dirs) {
84 if (!fs::exists(dir)) {
85 continue;
86 }
87 for (const auto& entry : fs::directory_iterator(dir)) {
88 const std::string ext = entry.path().extension().string();
89 if (ext != ".ttf" && ext != ".otf") {
90 continue;
91 }
92 std::string lower_name = entry.path().filename().string();
93 std::ranges::transform(lower_name,
94 lower_name.begin(), ::tolower);
95 if (lower_name.find(lower_family) != std::string::npos) {
96 MF_DEBUG(Journal::Component::Portal, Journal::Context::Init, "{} -> {}", family, entry.path().string());
97 return entry.path().string();
98 }
99 }
100 }
101
102 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "no match for '{}'", family);
103 return std::nullopt;
104}
105
106#elif defined(MAYAFLUX_PLATFORM_WINDOWS)
107
108#include <windows.h>
109#ifdef ERROR
110#undef ERROR
111#endif // ERROR
112
113namespace fs = std::filesystem;
114
115std::optional<std::string> find_font(std::string_view family, std::string_view /*style*/)
116{
117 char windir[MAX_PATH] {};
118 GetWindowsDirectoryA(windir, MAX_PATH);
119 const fs::path fonts_dir = fs::path(windir) / "Fonts";
120
121 if (!fs::exists(fonts_dir)) {
122 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "fonts directory not found at {}", fonts_dir.string());
123 return std::nullopt;
124 }
125
126 std::string lower_family(family);
127 std::ranges::transform(lower_family, lower_family.begin(),
128 [](unsigned char c) { return std::tolower(c); });
129 std::erase_if(lower_family,
130 [](unsigned char c) { return std::isspace(c); });
131
132 for (const auto& entry : fs::directory_iterator(fonts_dir)) {
133 const std::string ext = entry.path().extension().string();
134 if (ext != ".ttf" && ext != ".otf") {
135 continue;
136 }
137 std::string lower_name = entry.path().filename().string();
138 std::ranges::transform(lower_name, lower_name.begin(),
139 [](unsigned char c) { return std::tolower(c); });
140 if (lower_name.find(lower_family) != std::string::npos) {
141 MF_DEBUG(Journal::Component::Portal, Journal::Context::Init, "{} -> {}", family, entry.path().string());
142 return entry.path().string();
143 }
144 }
145
146 MF_WARN(Journal::Component::Portal, Journal::Context::Init, "no match for '{}'", family);
147 return std::nullopt;
148}
149
150#endif
151
152} // namespace MayaFlux::Portal::Text
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
vk::CommandBuffer cmd
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.