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
text.cpp
1/*
2 Copyright (c) 2026 ETIB Corporation
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy of
5 this software and associated documentation files (the "Software"), to deal in
6 the Software without restriction, including without limitation the rights to
7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 of the Software, and to permit persons to whom the Software is furnished to do
9 so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
21 */
22
24#include <utility/graphic/size.hpp>
25
26#include <cstdint>
27#include <memory>
28#include <vector>
29
30namespace utility::graphic
31{
32 Text::Text(std::shared_ptr<RessourceProvider> ressourceProvider,
33 const PoseF &pose, const Color32Bit &color,
34 const std::string &content, uint32_t fontSize,
35 const std::string &font)
36 : Renderable(pose, color)
37 , logging::Loggable<Text, logging::DefaultLogger>()
38 , _content(content)
39 , _fontPath(font)
40 , _fontSize(fontSize)
41 {
42 _font = ressourceProvider->loadFont(_fontPath);
43 _meshes = { std::make_shared<Mesh>(std::vector<VertexF> {},
44 std::vector<uint32_t> {}) };
45 updateMesh();
46 }
47
48 std::shared_ptr<Mesh> Text::getMesh() const
49 {
50 return _meshes.front();
51 }
52
53 const std::string &Text::getFontFamily(void) const
54 {
55 return _fontPath;
56 }
57
58 const std::string &Text::getContent(void) const
59 {
60 return _content;
61 }
62
63 Text &Text::setContent(const std::string &content)
64 {
65 _content = content;
66 updateMesh();
67 return *this;
68 }
69
70 uint32_t Text::getFontSize(void) const
71 {
72 return _fontSize;
73 }
74
75 Text &Text::setFontSize(uint32_t fontSize)
76 {
77 _fontSize = fontSize;
78
79 updateMesh();
80
81 return *this;
82 }
83
85 {
86 graphic::SizeF dimensions({ 0.0, 0.0 });
87
88 std::vector<uint32_t> codepoints = utf8ToCodepoints(_content);
89 const math::Vector2F measured =
90 _font->measureText(_fontSize, codepoints);
91
92 dimensions.setWidth(measured[0]);
93 dimensions.setHeight(measured[1]);
94
95 getLogger().debug() << "Calculated text dimensions for content '"
96 << _content << "' with font size " << _fontSize
97 << ": width = " << dimensions.getWidth()
98 << ", height = " << dimensions.getHeight();
99
100 return dimensions;
101 }
102
104 // Protected Methods //
106
108 {
109 _meshes.front() = std::make_shared<Mesh>(std::vector<VertexF> {},
110 std::vector<uint32_t> {});
111
112 // Glyph quads are built in local (object) space: the pose position and
113 // orientation are applied later through the model matrix, so they are
114 // not baked into positions or normals here.
115 const math::Vector3F localNormal { 0.0f, 0.0f, -1.0f };
116
117 uint32_t indexOffset = 0;
118 std::vector<uint32_t> codepoints = utf8ToCodepoints(_content);
119 std::vector<Glyph> glyphs =
120 _font->processCodePoints(_fontSize, codepoints);
121
122 double penX = 0.0;
123 double baseline = std::round(_font->getAscender(_fontSize));
124
125 for (const auto &g: glyphs) {
126 const float xpos =
127 static_cast<float>(std::round(penX + g.bearing[VEC_X]));
128 const float ypos =
129 static_cast<float>(std::round(-baseline + g.bearing[VEC_Y]));
130
131 const float w = g.size[VEC_X];
132 const float h = g.size[VEC_Y];
133
134 VertexF v0(PositionF(xpos, ypos, 0.0f), localNormal,
135 math::Vector2F({ g.uvMin[VEC_X], g.uvMin[VEC_Y] }),
136 _color);
137
138 VertexF v1(PositionF(xpos, ypos - h, 0.0f), localNormal,
139 math::Vector2F({ g.uvMin[VEC_X], g.uvMax[VEC_Y] }),
140 _color);
141
142 VertexF v2(PositionF(xpos + w, ypos - h, 0.0f), localNormal,
143 math::Vector2F({ g.uvMax[VEC_X], g.uvMax[VEC_Y] }),
144 _color);
145
146 VertexF v3(PositionF(xpos + w, ypos, 0.0f), localNormal,
147 math::Vector2F({ g.uvMax[VEC_X], g.uvMin[VEC_Y] }),
148 _color);
149
150 _meshes.front()->addIndex(indexOffset + 0);
151 _meshes.front()->addIndex(indexOffset + 1);
152 _meshes.front()->addIndex(indexOffset + 2);
153
154 _meshes.front()->addIndex(indexOffset + 0);
155 _meshes.front()->addIndex(indexOffset + 2);
156 _meshes.front()->addIndex(indexOffset + 3);
157
158 _meshes.front()->addVertex(v0);
159 _meshes.front()->addVertex(v1);
160 _meshes.front()->addVertex(v2);
161 _meshes.front()->addVertex(v3);
162
163 indexOffset += 4;
164 penX += g.advance;
165 }
166 }
167 std::vector<uint32_t> Text::utf8ToCodepoints(const std::string &str) const
168 {
169 std::vector<uint32_t> codepoints;
170
171 size_t i = 0;
172 while (i < str.size()) {
173 uint8_t c0 = static_cast<uint8_t>(str[i]);
174 uint32_t codepoint = 0;
175 size_t bytes = 0;
176
177 if (c0 < 0x80) {
178 codepoint = c0;
179 bytes = 1;
180 } else if (c0 >= 0xC2 && c0 <= 0xDF) {
181 codepoint = c0 & 0x1F;
182 bytes = 2;
183 } else if (c0 >= 0xE0 && c0 <= 0xEF) {
184 codepoint = c0 & 0x0F;
185 bytes = 3;
186 } else if (c0 >= 0xF0 && c0 <= 0xF4) {
187 codepoint = c0 & 0x07;
188 bytes = 4;
189 } else {
190 ++i;
191 continue;
192 }
193
194 if (i + bytes > str.size()) {
195 break;
196 }
197
198 bool valid = true;
199 for (size_t j = 1; j < bytes; ++j) {
200 uint8_t cc = static_cast<uint8_t>(str[i + j]);
201 if ((cc & 0xC0) != 0x80) {
202 valid = false;
203 break;
204 }
205 codepoint = (codepoint << 6) | (cc & 0x3F);
206 }
207
208 if (valid) {
209 if ((bytes == 2 && codepoint < 0x80)
210 || (bytes == 3
211 && (codepoint < 0x800
212 || (codepoint >= 0xD800 && codepoint <= 0xDFFF)))
213 || (bytes == 4
214 && (codepoint < 0x10000 || codepoint > 0x10FFFF))) {
215 valid = false;
216 }
217 }
218
219 if (valid) {
220 codepoints.push_back(codepoint);
221 i += bytes;
222 } else {
223 ++i;
224 }
225 }
226
227 return codepoints;
228 }
229} // namespace utility::graphic
Position in 3D space represented as a vector of three PositionComponentType components (x,...
Definition position.hpp:53
The Renderable class represents an object that can be rendered in the graphics application.
std::vector< std::shared_ptr< Mesh > > _meshes
Meshes associated with this renderable object.
Color32Bit _color
Text RGBA color.
Storage for text content and rendering properties.
Definition text.hpp:75
std::shared_ptr< Font > _font
Shared pointer to the font used for rendering.
Definition text.hpp:83
codePointString utf8ToCodepoints(const std::string &str) const
Converts a UTF-8 encoded string to a string of Unicode code points.
Definition text.cpp:167
Text(std::shared_ptr< RessourceProvider > ressourceProvider, const PoseF &pose, const Color32Bit &color, const std::string &content, uint32_t fontSize, const std::string &font)
Constructor.
Definition text.cpp:32
const std::string & getFontFamily(void) const
Get the font family (path) used for rendering the text.
Definition text.cpp:53
void updateMesh(void)
Updates the mesh for rendering the text based on the current content, font, and font size.
Definition text.cpp:107
const std::string & getContent(void) const
Get the text content.
Definition text.cpp:58
std::shared_ptr< Mesh > getMesh() const
Get the mesh for rendering the text.
Definition text.cpp:48
Text & setFontSize(uint32_t fontSize)
Set the font size.
Definition text.cpp:75
uint32_t getFontSize(void) const
Get the font size.
Definition text.cpp:70
Text & setContent(const std::string &content)
Set the text content.
Definition text.cpp:63
graphic::SizeF getTextDimensions(void) const
Get the dimensions of the rendered text.
Definition text.cpp:84
Vertex type containing common mesh attributes.
Definition vertex.hpp:38
LoggerType & getLogger(void)
Get the internal logger.
Definition loggable.hpp:98
3D vector class inheriting from glm::vec3.
Definition vector.hpp:83
Text rendering properties and storage.