utility 2026.1.9
A comprehensive C++ utilities library tailored for the development of modern desktop and extended reality (XR) applications.
Loading...
Searching...
No Matches
font.cpp
1/*
2** ETIB PROJECT, 2026
3** utility
4** File description:
5** font
6*/
7
8#include <utility/graphic/text/font.hpp>
9
10#include <algorithm>
11
12#include <ft2build.h>
13#include FT_FREETYPE_H
14
15namespace utility::graphic
16{
17
18 Font::Font(const std::vector<File> &fontAssets)
19 {
20 // Initialize FreeType library
21 if (FT_Init_FreeType(reinterpret_cast<FT_Library *>(&_ftLibrary))) {
22 throw std::runtime_error("Could not initialize FreeType library.");
23 }
24
25 for (const auto &fontAsset: fontAssets) {
26 auto buffer = std::make_shared<std::vector<uint8_t>>();
27 const std::string content = fontAsset.content();
28 buffer->assign(content.begin(), content.end());
29 _faceBuffers[fontAsset.path()] = buffer;
30
31 FT_Face face;
32 if (FT_New_Memory_Face(
33 reinterpret_cast<FT_Library>(_ftLibrary),
34 reinterpret_cast<const FT_Byte *>(buffer->data()),
35 static_cast<FT_Long>(buffer->size()), 0, &face)) {
36 throw std::runtime_error(
37 "Could not load font " + fontAsset.path() + ": "
38 + FT_Error_String(FT_Err_Cannot_Open_Resource));
39 }
40 _faces[fontAsset.path()] = face;
41 }
42 }
43
45 {
46 if (!isLoaded()) {
47 return;
48 }
49 for (const auto &[_, face]: _faces) {
50 FT_Done_Face(static_cast<FT_Face>(face));
51 }
52 if (_ftLibrary) {
53 FT_Done_FreeType(reinterpret_cast<FT_Library>(_ftLibrary));
54 }
55 }
56
58 // Public Methods //
60
61 float Font::getAscender(uint32_t fontSize) const
62 {
63 auto fontSizedIt = _sizes.find({ "", fontSize });
64 if (fontSizedIt != _sizes.end()) {
65 return fontSizedIt->second->_ascender;
66 }
67 return 0.0f;
68 }
69
70 float Font::getDescender(uint32_t fontSize) const
71 {
72 auto fontSizedIt = _sizes.find({ "", fontSize });
73 if (fontSizedIt != _sizes.end()) {
74 return fontSizedIt->second->_descender;
75 }
76 return 0.0f;
77 }
78
79 float Font::getLineHeight(uint32_t fontSize) const
80 {
81 auto fontSizedIt = _sizes.find({ "", fontSize });
82 if (fontSizedIt != _sizes.end()) {
83 return fontSizedIt->second->_lineHeight;
84 }
85 return 0.0f;
86 }
87
88 std::vector<Glyph>
89 Font::processCodePoints(uint32_t fontSize,
90 const codePointString &codePoints)
91 {
92 std::vector<Glyph> glyphs;
93 glyphs.reserve(codePoints.size());
94 std::map<std::string, std::shared_ptr<FontSized>> dirtyFontSizeds;
95
96 for (const auto &codePoint: codePoints) {
97 const std::string faceName = _getFaceNameForGlyph(codePoint);
98
99 if (faceName.empty()) {
100 continue;
101 }
102
103 const FontSizedKey key { faceName, fontSize };
104
105 std::shared_ptr<FontSized> fontSized;
106 auto fontSizedIt = _sizes.find(key);
107
108 if (fontSizedIt == _sizes.end()) {
109 fontSized =
110 std::make_shared<FontSized>(fontSize, _faces.at(faceName));
111 _sizes.emplace(key, fontSized);
112 } else {
113 fontSized = fontSizedIt->second;
114 }
115
116 const bool hadGlyph = fontSized->hasGlyph(codePoint);
117 glyphs.push_back(fontSized->generateGlyph(codePoint));
118 if (!hadGlyph) {
119 dirtyFontSizeds[faceName] = fontSized;
120 }
121 }
122
124 for (const auto &[faceName, fontSized]: dirtyFontSizeds) {
125 onNewTextureCreated(faceName + "_" + std::to_string(fontSize),
126 fontSized->getAtlas());
127 }
128 }
129
130 return glyphs;
131 }
133 const codePointString &codePoints) const
134 {
135 double width = 0.0;
136 double maxTop = 0.0;
137 double maxBottom = 0.0;
138
139 for (const auto &codePoint: codePoints) {
140 const std::string faceName = _getFaceNameForGlyph(codePoint);
141 if (faceName.empty()) {
142 continue;
143 }
144 const FontSizedKey key { faceName, fontSize };
145 auto fontSizedIt = _sizes.find(key);
146 if (fontSizedIt == _sizes.end()) {
147 // Not yet rasterized; measure metrics without mutating state.
148 FontSized probe(fontSize, _faces.at(faceName));
149 const Glyph metric = probe.measureGlyph(codePoint);
150 width += metric.advance;
151 maxTop =
152 std::max(maxTop, static_cast<double>(metric.bearing[1]));
153 maxBottom = std::max(
154 maxBottom,
155 static_cast<double>(metric.size[1] - metric.bearing[1]));
156 } else {
157 const Glyph metric =
158 fontSizedIt->second->measureGlyph(codePoint);
159 width += metric.advance;
160 maxTop =
161 std::max(maxTop, static_cast<double>(metric.bearing[1]));
162 maxBottom = std::max(
163 maxBottom,
164 static_cast<double>(metric.size[1] - metric.bearing[1]));
165 }
166 }
167
168 return math::Vector2F { static_cast<float>(width),
169 static_cast<float>(maxTop + maxBottom) };
170 }
171
172 std::vector<std::string> Font::getFontPaths(void) const
173 {
174 std::vector<std::string> fontPaths;
175 for (const auto &[fontPath, _]: _faces) {
176 fontPaths.push_back(fontPath);
177 }
178 return fontPaths;
179 }
180
181 bool Font::isLoaded(void) const
182 {
183 if (_faces.empty())
184 return false;
185 for (const auto &[_, face]: _faces) {
186 if (!face || static_cast<FT_Face>(face)->num_glyphs == 0) {
187 return false;
188 }
189 }
190 return true;
191 }
192
193 bool Font::hasGlyph(char32_t codepoint) const
194 {
195 if (!isLoaded()) {
196 return false;
197 }
198 for (const auto &[_, face]: _faces) {
199 if (FT_Get_Char_Index(static_cast<FT_Face>(face), codepoint) != 0) {
200 return true;
201 }
202 }
203 return false;
204 }
205
207 // Protected Methods //
209
210 std::string Font::_getFaceNameForGlyph(uint32_t codePoint) const
211 {
212 if (_faces.empty()) {
213 return "";
214 }
215
216 for (const auto &[faceName, face]: _faces) {
217 FT_UInt glyphIndex =
218 FT_Get_Char_Index(static_cast<FT_Face>(face), codePoint);
219 if (glyphIndex != 0) {
220 return faceName;
221 }
222 }
223 return "";
224 }
225
226} // namespace utility::graphic
The FontSized class represents a specific size of a font face, managing the glyphs and texture atlas ...
Glyph measureGlyph(uint32_t codePoint) const
Compute glyph metrics for a code point without rasterizing or mutating the texture atlas.
std::vector< std::string > getFontPaths(void) const
Retrieves the paths of the loaded font assets.
Definition font.cpp:172
float getLineHeight(uint32_t fontSize) const
Retrieves the line height for the specified font size.
Definition font.cpp:79
std::map< FontSizedKey, std::shared_ptr< FontSized > > _sizes
Map of font sizes to their corresponding FontSized objects.
Definition font.hpp:260
bool hasGlyph(char32_t codepoint) const
Checks if the font contains a glyph for the specified Unicode code point.
Definition font.cpp:193
~Font()
Destructs the Font object, releasing any allocated resources.
Definition font.cpp:44
float getAscender(uint32_t fontSize) const
Retrieves the ascender value for the specified font size.
Definition font.cpp:61
bool isLoaded(void) const
Checks if the font has been successfully loaded.
Definition font.cpp:181
std::string _getFaceNameForGlyph(uint32_t codePoint) const
Retrieves the font face name associated with a specific Unicode code point.
Definition font.cpp:210
std::map< std::string, void * > _faces
Map of font paths to their corresponding FreeType face objects.
Definition font.hpp:245
std::function< void(std::string, std::shared_ptr< Texture >)> onNewTextureCreated
Member function to set a callback that is called when a new texture atlas is created for a font size.
Definition font.hpp:210
std::map< std::string, std::shared_ptr< std::vector< uint8_t > > > _faceBuffers
In-memory buffers backing FreeType faces.
Definition font.hpp:271
float getDescender(uint32_t fontSize) const
Retrieves the descender value for the specified font size.
Definition font.cpp:70
math::Vector2F measureText(uint32_t fontSize, const codePointString &codePoints) const
Measure the bounding size of a string of code points without rasterizing or mutating any glyph atlas.
Definition font.cpp:132
Font(const std::vector< File > &fontAssets)
Constructs a Font object by loading font data from the provided file assets.
Definition font.cpp:18
void * _ftLibrary
FreeType library instance used for managing font resources.
Definition font.hpp:236
std::vector< Glyph > processCodePoints(uint32_t fontSize, const codePointString &codePoints)
Processes a string of Unicode code points to generate glyphs for rendering text.
Definition font.cpp:89
3D vector class inheriting from glm::vec3.
Definition vector.hpp:83
std::vector< uint32_t > codePointString
A type representing a string of Unicode code points.
A struct representing a unique key for identifying a specific font face and size combination.
Definition font.hpp:39
The Glyph struct represents a single character's visual representation in a font atlas.
Definition glyph.hpp:29
math::Vector2F size
Size of the glyph in pixels (width and height).
Definition glyph.hpp:33
math::Vector2F bearing
Bearing of the glyph, representing the offset from the baseline to the top-left corner of the glyph.
Definition glyph.hpp:39
float advance
Advance value, specifying the horizontal distance to advance the cursor after rendering the glyph.
Definition glyph.hpp:45