TGUI 1.13
Loading...
Searching...
No Matches
Utf.hpp
1
2//
3// TGUI - Texus' Graphical User Interface
4// Copyright (C) 2012-2026 Bruno Van de Velde (vdv_b@tgui.eu)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
24
25#ifndef TGUI_UTF_HPP
26#define TGUI_UTF_HPP
27
28#include <TGUI/Config.hpp>
29
30#include <array>
31#include <cstdint>
32#include <string>
33
35
36namespace tgui::utf
37{
43 template <typename CharT> // CharT is either char or char8_t
44 void encodeCharUtf8(char32_t input, std::basic_string<CharT>& outStrUtf8)
45 {
46 if (input < 128)
47 {
48 outStrUtf8.push_back(static_cast<CharT>(input));
49 return;
50 }
51
52 // Encode the character (if it is valid)
53 if ((input > 0x0010FFFF) || ((input >= 0xD800) && (input <= 0xDBFF)))
54 return;
55
56 // Get the number of bytes to write
57 std::size_t bytestoWrite;
58 std::uint8_t firstByteMask;
59 if (input < 0x800)
60 {
61 bytestoWrite = 2;
62 firstByteMask = 0xC0;
63 }
64 else if (input < 0x10000)
65 {
66 bytestoWrite = 3;
67 firstByteMask = 0xE0;
68 }
69 else
70 {
71 bytestoWrite = 4;
72 firstByteMask = 0xF0;
73 }
74
75 // Extract the bytes to write
76 std::array<CharT, 4> bytes;
77 if (bytestoWrite == 4)
78 {
79 bytes[3] = static_cast<CharT>((input | 0x80) & 0xBF);
80 input >>= 6;
81 }
82 if (bytestoWrite >= 3)
83 {
84 bytes[2] = static_cast<CharT>((input | 0x80) & 0xBF);
85 input >>= 6;
86 }
87 bytes[1] = static_cast<CharT>((input | 0x80) & 0xBF);
88 input >>= 6;
89 bytes[0] = static_cast<CharT>(input | firstByteMask);
90
91 // Add them to the output
92 outStrUtf8.append(bytes.cbegin(), bytes.cbegin() + static_cast<std::ptrdiff_t>(bytestoWrite));
93 }
94
102 template <typename CharIt> // CharIt is an iterator for a string containing either char or char8_t
103 CharIt decodeCharUtf8(CharIt inputCharIt, CharIt inputEndIt, std::u32string& outStrUtf32)
104 {
105 if (static_cast<std::uint8_t>(*inputCharIt) < 128)
106 {
107 outStrUtf32.push_back(static_cast<char32_t>(static_cast<std::uint8_t>(*inputCharIt)));
108 return ++inputCharIt;
109 }
110
111 // Some useful precomputed data
112 static const std::array<std::uint32_t, 6> offsetsMap = {0x00000000, 0x00003080, 0x000E2080, 0x03C82080, 0xFA082080, 0x82082080};
113 static const std::array<std::uint8_t, 128> trailingMap =
114 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
116 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
117 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5};
118
119 // decode the character
120 const std::uint8_t trailingBytes = trailingMap[static_cast<std::uint8_t>(*inputCharIt) - 128];
121 const std::uint32_t offset = offsetsMap[trailingBytes];
122 const auto remainingBytes = std::distance(inputCharIt, inputEndIt) - 1;
123 if (remainingBytes >= static_cast<decltype(remainingBytes)>(trailingBytes))
124 {
125 char32_t outputChar = 0;
126 for (std::uint8_t i = 0; i < trailingBytes; ++i)
127 {
128 outputChar += static_cast<char32_t>(static_cast<std::uint8_t>(*inputCharIt++));
129 outputChar <<= 6;
130 }
131
132 outputChar += static_cast<char32_t>(static_cast<std::uint8_t>(*inputCharIt++));
133 outputChar -= offset;
134 outStrUtf32.push_back(outputChar);
135 }
136 else // Incomplete character
137 inputCharIt = inputEndIt;
138
139 return inputCharIt;
140 }
141
142#if defined(__cpp_lib_char8_t) && (__cpp_lib_char8_t >= 201811L)
148 [[nodiscard]] inline std::u8string convertUtf32toUtf8(const std::u32string& strUtf32)
149 {
150 std::u8string outStrUtf8;
151 outStrUtf8.reserve(strUtf32.length() + 1);
152 for (const char32_t& codepoint : strUtf32)
153 encodeCharUtf8(codepoint, outStrUtf8);
154
155 return outStrUtf8;
156 }
157#endif
158
165 template <typename CharIt>
166 [[nodiscard]] std::u32string convertUtf8toUtf32(CharIt inputBegin, CharIt inputEnd)
167 {
168 std::u32string outStrUtf32;
169 outStrUtf32.reserve(static_cast<std::size_t>((inputEnd - inputBegin) + 1));
170
171 auto it = inputBegin;
172 while (it < inputEnd)
173 it = decodeCharUtf8(it, inputEnd, outStrUtf32);
174
175 return outStrUtf32;
176 }
177
184 template <typename U16CharIt>
185 [[nodiscard]] std::u32string convertUtf16toUtf32(U16CharIt inputBegin, U16CharIt inputEnd)
186 {
187 std::u32string outStrUtf32;
188 outStrUtf32.reserve(static_cast<std::size_t>((inputEnd - inputBegin) + 1));
189
190 auto it = inputBegin;
191 while (it < inputEnd)
192 {
193 const char16_t first = *it++;
194
195 // Copy the character if it isn't a surrogate pair
196 if ((first < 0xD800) || (first > 0xDBFF))
197 {
198 outStrUtf32.push_back(static_cast<char32_t>(first));
199 continue;
200 }
201
202 // We need to read another character
203 if (it == inputEnd)
204 break;
205
206 const char16_t second = *it++;
207 if ((second >= 0xDC00) && (second <= 0xDFFF))
208 outStrUtf32.push_back(
209 ((static_cast<char32_t>(first) - 0xD800) << 10) + (static_cast<char32_t>(second) - 0xDC00) + 0x0010000);
210 }
211
212 return outStrUtf32;
213 }
214
220 template <typename WCharIt>
221 [[nodiscard]] std::u32string convertWidetoUtf32(WCharIt inputBegin, WCharIt inputEnd)
222 {
223 std::u32string outStrUtf32;
224 outStrUtf32.reserve(static_cast<std::size_t>((inputEnd - inputBegin) + 1));
225
226 // std::wstring uses UCS-2 on Windows and UCS-4 on unix, so we can be cast directly
227 for (auto it = inputBegin; it != inputEnd; ++it)
228 outStrUtf32.push_back(static_cast<char32_t>(*it));
229
230 return outStrUtf32;
231 }
232
238 [[nodiscard]] inline std::string convertUtf32toStdStringUtf8(const std::u32string& strUtf32)
239 {
240 std::string outStrUtf8;
241 outStrUtf8.reserve(strUtf32.length() + 1);
242 for (const char32_t codepoint : strUtf32)
243 encodeCharUtf8(codepoint, outStrUtf8);
244
245 return outStrUtf8;
246 }
247
253 [[nodiscard]] inline std::wstring convertUtf32toWide(const std::u32string& strUtf32)
254 {
255 std::wstring outStr;
256 outStr.reserve(strUtf32.length() + 1);
257
258 if constexpr (sizeof(wchar_t) == 4)
259 {
260 // On Unix, wide characters are UCS-4 and we can just copy the characters
261 for (const char32_t codepoint : strUtf32)
262 outStr.push_back(static_cast<wchar_t>(codepoint));
263 }
264 else
265 {
266 // On Windows, wide characters are UCS-2. We just drop the characters that don't fit within a single wide character here.
267 for (const char32_t codepoint : strUtf32)
268 {
269 if ((codepoint < 0xD800) || ((codepoint > 0xDFFF) && (codepoint <= 0xFFFF)))
270 outStr.push_back(static_cast<wchar_t>(codepoint));
271 }
272 }
273
274 return outStr;
275 }
276
282 [[nodiscard]] inline std::u16string convertUtf32toUtf16(const std::u32string& strUtf32)
283 {
284 std::u16string outStrUtf16;
285 outStrUtf16.reserve(strUtf32.length() + 1);
286
287 for (const char32_t codepoint : strUtf32)
288 {
289 // If the codepoint fits inside 2 bytes and it would represent a valid character then just copy it
290 if (codepoint <= 0xFFFF)
291 {
292 if ((codepoint < 0xD800) || (codepoint > 0xDFFF))
293 outStrUtf16.push_back(static_cast<char16_t>(codepoint));
294
295 continue;
296 }
297 if (codepoint > 0x0010FFFF)
298 continue; // Invalid character (greater than the maximum Unicode value)
299
300 // The input character needs be converted to two UTF-16 elements
301 outStrUtf16.push_back(static_cast<char16_t>(((codepoint - 0x0010000) >> 10) + 0xD800));
302 outStrUtf16.push_back(static_cast<char16_t>(((codepoint - 0x0010000) & 0x3FFUL) + 0xDC00));
303 }
304
305 return outStrUtf16;
306 }
307} // namespace tgui::utf
308
309#endif // TGUI_UTF_HPP