yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
font_loader.cc
Go to the documentation of this file.
2
3#include <cstring>
4#include <filesystem>
5#include <string>
6#include <vector>
7
8#include "absl/status/status.h"
9#include "absl/strings/str_cat.h"
10#include "absl/strings/str_format.h"
11#include "app/gui/core/icons.h"
12#include "imgui/imgui.h"
13#include "util/file_util.h"
14#include "util/macro.h"
15#include "util/platform_paths.h"
16
17namespace yaze {
18
19static const char* KARLA_REGULAR = "Karla-Regular.ttf";
20static const char* ROBOTO_MEDIUM = "Roboto-Medium.ttf";
21static const char* COUSINE_REGULAR = "Cousine-Regular.ttf";
22static const char* DROID_SANS = "DroidSans.ttf";
23static const char* NOTO_SANS_JP = "NotoSansJP.ttf";
24static const char* IBM_PLEX_JP = "IBMPlexSansJP-Bold.ttf";
25
26static const float FONT_SIZE_DEFAULT = 16.0F;
27static const float FONT_SIZE_DROID_SANS = 18.0F;
28static const float ICON_FONT_SIZE = 18.0F;
29
30namespace {
31
32std::string ResolveRepoFontPath(const std::string& font_path) {
33 // Prefer the robust asset search (exe-dir, ~/.yaze, /usr/share, repo-relative)
34 // so installed binaries and .desktop/double-click launches (CWD=$HOME or /)
35 // still find bundled fonts at startup.
36 auto found = util::PlatformPaths::FindAsset("font/" + font_path);
37 if (found.ok()) {
38 return found->string();
39 }
40 // Fallback: CWD-relative probes for running directly from the repo/build dir.
41 const std::vector<std::filesystem::path> candidates = {
42 std::filesystem::path("assets/font") / font_path,
43 std::filesystem::path("../assets/font") / font_path,
44 std::filesystem::path("../../assets/font") / font_path,
45 std::filesystem::path("../../../assets/font") / font_path,
46 };
47 for (const auto& candidate : candidates) {
48 if (std::filesystem::exists(candidate)) {
49 return candidate.string();
50 }
51 }
52 return (std::filesystem::path("assets/font") / font_path).string();
53}
54
55std::string SetFontPath(const std::string& font_path) {
56#ifdef __APPLE__
57#if TARGET_OS_IOS == 1
58 const std::string bundle_root = util::GetBundleResourcePath();
59 std::string bundle_path =
60 absl::StrCat(bundle_root, "assets/font/", font_path);
61 if (std::filesystem::exists(bundle_path)) {
62 return bundle_path;
63 }
64 bundle_path = absl::StrCat(bundle_root, font_path);
65 if (std::filesystem::exists(bundle_path)) {
66 return bundle_path;
67 }
68 return ResolveRepoFontPath(font_path);
69#else
70 std::string bundle_path = absl::StrCat(util::GetBundleResourcePath(),
71 "Contents/Resources/font/", font_path);
72 if (std::filesystem::exists(bundle_path)) {
73 return bundle_path;
74 }
75 return ResolveRepoFontPath(font_path);
76#endif
77#else
78 return ResolveRepoFontPath(font_path);
79#endif
80}
81
82absl::Status LoadFont(const FontConfig& font_config) {
83 ImGuiIO& imgui_io = ImGui::GetIO();
84 std::string actual_font_path = SetFontPath(font_config.font_path);
85 // Check if the file exists with std library first, since ImGui IO will assert
86 // if the file does not exist
87 if (!std::filesystem::exists(actual_font_path)) {
88 return absl::InternalError(
89 absl::StrFormat("Font file %s does not exist", actual_font_path));
90 }
91
92 if (!imgui_io.Fonts->AddFontFromFileTTF(actual_font_path.data(),
93 font_config.font_size)) {
94 return absl::InternalError(
95 absl::StrFormat("Failed to load font from %s", actual_font_path));
96 }
97 return absl::OkStatus();
98}
99
100absl::Status AddIconFont(const FontConfig& /*config*/) {
101 static const ImWchar icons_ranges[] = {ICON_MIN_MD, 0xf900, 0};
102 ImFontConfig icons_config{};
103 icons_config.MergeMode = true;
104 icons_config.GlyphOffset.y = 5.0F;
105 icons_config.GlyphMinAdvanceX = 13.0F;
106 icons_config.PixelSnapH = true;
107 std::string icon_font_path = SetFontPath(FONT_ICON_FILE_NAME_MD);
108 ImGuiIO& imgui_io = ImGui::GetIO();
109 if (!imgui_io.Fonts->AddFontFromFileTTF(icon_font_path.c_str(),
110 ICON_FONT_SIZE, &icons_config,
111 icons_ranges)) {
112 return absl::InternalError("Failed to add icon fonts");
113 }
114 return absl::OkStatus();
115}
116
117absl::Status AddJapaneseFont(const FontConfig& /*config*/) {
118 ImFontConfig japanese_font_config{};
119 japanese_font_config.MergeMode = true;
120 japanese_font_config.GlyphOffset.y = 5.0F;
121 japanese_font_config.GlyphMinAdvanceX = 13.0F;
122 japanese_font_config.PixelSnapH = true;
123 std::string japanese_font_path = SetFontPath(NOTO_SANS_JP);
124 ImGuiIO& imgui_io = ImGui::GetIO();
125 if (!imgui_io.Fonts->AddFontFromFileTTF(
126 japanese_font_path.data(), ICON_FONT_SIZE, &japanese_font_config,
127 imgui_io.Fonts->GetGlyphRangesJapanese())) {
128 return absl::InternalError("Failed to add Japanese fonts");
129 }
130 return absl::OkStatus();
131}
132
133} // namespace
134
135absl::Status LoadPackageFonts() {
136 if (font_registry.fonts.empty()) {
137 // Initialize the font names and sizes with proper ImFontConfig
138 // initialization
140 FontConfig{KARLA_REGULAR, FONT_SIZE_DEFAULT, {}, {}},
141 FontConfig{ROBOTO_MEDIUM, FONT_SIZE_DEFAULT, {}, {}},
142 FontConfig{COUSINE_REGULAR, FONT_SIZE_DEFAULT, {}, {}},
143 FontConfig{IBM_PLEX_JP, FONT_SIZE_DEFAULT, {}, {}},
144 FontConfig{DROID_SANS, FONT_SIZE_DROID_SANS, {}, {}},
145 };
146 }
147
148 // Load fonts with associated icon and Japanese merges
149 for (const auto& font_config : font_registry.fonts) {
150 RETURN_IF_ERROR(LoadFont(font_config));
151 RETURN_IF_ERROR(AddIconFont(font_config));
152 RETURN_IF_ERROR(AddJapaneseFont(font_config));
153 }
154 return absl::OkStatus();
155}
156
157void SetActiveFontIndex(int index) {
158 ImGuiIO& io = ImGui::GetIO();
159 if (io.Fonts == nullptr || io.Fonts->Fonts.Size == 0) {
160 return;
161 }
162 if (index < 0 || index >= io.Fonts->Fonts.Size) {
163 index = 0;
164 }
165 io.FontDefault = io.Fonts->Fonts[index];
166}
167
168absl::Status ReloadPackageFont(const FontConfig& config) {
169 ImGuiIO& imgui_io = ImGui::GetIO();
170 std::string actual_font_path = SetFontPath(config.font_path);
171 if (!imgui_io.Fonts->AddFontFromFileTTF(actual_font_path.data(),
172 config.font_size)) {
173 return absl::InternalError(
174 absl::StrFormat("Failed to load font from %s", actual_font_path));
175 }
176 RETURN_IF_ERROR(AddIconFont(config));
177 RETURN_IF_ERROR(AddJapaneseFont(config));
178 return absl::OkStatus();
179}
180
181absl::Status LoadFontFromMemory(const std::string& name,
182 const std::string& data, float size_pixels) {
183 ImGuiIO& imgui_io = ImGui::GetIO();
184
185 // ImGui takes ownership of the data and will free it
186 void* font_data = ImGui::MemAlloc(data.size());
187 if (!font_data) {
188 return absl::InternalError("Failed to allocate memory for font data");
189 }
190 std::memcpy(font_data, data.data(), data.size());
191
192 ImFontConfig config;
193 std::strncpy(config.Name, name.c_str(), sizeof(config.Name) - 1);
194 config.Name[sizeof(config.Name) - 1] = 0;
195
196 if (!imgui_io.Fonts->AddFontFromMemoryTTF(
197 font_data, static_cast<int>(data.size()), size_pixels, &config)) {
198 ImGui::MemFree(font_data);
199 return absl::InternalError("Failed to load font from memory");
200 }
201
202 // We also need to add icons and Japanese characters to this new font
203 // Note: This is a simplified version of AddIconFont/AddJapaneseFont that
204 // works with the current font being added (since we can't easily merge into
205 // a font that's just been added without rebuilding atlas immediately)
206 // For now, we'll just load the base font. Merging requires more complex logic.
207
208 // Important: We must rebuild the font atlas!
209 // This is usually done by the backend, but since we changed fonts at runtime...
210 // Ideally, this should be done before NewFrame().
211 // If called during a frame, changes won't appear until texture is rebuilt.
212
213 return absl::OkStatus();
214}
215
216#ifdef __linux__
217void LoadSystemFonts() {
218 // Load Linux System Fonts into ImGui
219 // System font loading is now handled by NFD (Native File Dialog)
220 // This function is kept for compatibility but does nothing
221}
222#endif
223
224} // namespace yaze
static absl::StatusOr< std::filesystem::path > FindAsset(const std::string &relative_path)
Find an asset file in multiple standard locations.
#define ICON_MIN_MD
Definition icons.h:10
#define FONT_ICON_FILE_NAME_MD
Definition icons.h:8
absl::Status AddJapaneseFont(const FontConfig &)
std::string ResolveRepoFontPath(const std::string &font_path)
std::string SetFontPath(const std::string &font_path)
absl::Status LoadFont(const FontConfig &font_config)
absl::Status AddIconFont(const FontConfig &)
std::string GetBundleResourcePath()
GetBundleResourcePath returns the path to the bundle resource directory. Specific to MacOS.
absl::Status ReloadPackageFont(const FontConfig &config)
absl::Status LoadPackageFonts()
void LoadSystemFonts()
absl::Status LoadFontFromMemory(const std::string &name, const std::string &data, float size_pixels)
void SetActiveFontIndex(int index)
FontState font_registry
Definition font_loader.h:23
#define RETURN_IF_ERROR(expr)
Definition snes.cc:22
const char * font_path
Definition font_loader.h:13
std::vector< FontConfig > fonts
Definition font_loader.h:20