OpenTTD Source 20260311-master-g511d3794ce
string_uniscribe.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
9
10#include "../../stdafx.h"
11#include "../../debug.h"
12#include "string_uniscribe.h"
13#include "../../language.h"
14#include "../../strings_func.h"
15#include "../../string_func.h"
16#include "../../core/utf8.hpp"
18#include "../../zoom_func.h"
19#include "win32.h"
20
21#include <windows.h>
22#include <usp10.h>
23
24#include "../../safeguards.h"
25
26#ifdef _MSC_VER
27# pragma comment(lib, "usp10")
28#endif
29
30
32static SCRIPT_CACHE _script_cache[FS_END];
33
38struct UniscribeRun {
39 int pos;
40 int len;
41 Font *font;
42
43 std::vector<GlyphID> ft_glyphs;
44
45 SCRIPT_ANALYSIS sa;
46 std::vector<WORD> char_to_glyph;
47
48 std::vector<SCRIPT_VISATTR> vis_attribs;
49 std::vector<WORD> glyphs;
50 std::vector<int> advances;
51 std::vector<GOFFSET> offsets;
52 int total_advance;
53
54 UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
55};
56
57static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length);
59
63class UniscribeParagraphLayout : public ParagraphLayouter {
64private:
66
67 std::vector<UniscribeRun> ranges;
68 std::vector<UniscribeRun>::iterator cur_range;
70
71public:
73 class UniscribeVisualRun : public ParagraphLayouter::VisualRun {
74 private:
75 std::vector<GlyphID> glyphs;
76 std::vector<Position> positions;
77 std::vector<WORD> char_to_glyph;
78
79 int start_pos;
80 int total_advance;
81 Font *font;
82
83 mutable std::vector<int> glyph_to_char;
84
85 public:
86 UniscribeVisualRun(const UniscribeRun &range, int x);
87 UniscribeVisualRun(UniscribeVisualRun &&other) noexcept;
88
89 std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
90 std::span<const Position> GetPositions() const override { return this->positions; }
91 std::span<const int> GetGlyphToCharMap() const override;
92
93 const Font *GetFont() const override { return this->font; }
94 int GetLeading() const override { return this->font->fc->GetHeight(); }
95 size_t GetGlyphCount() const override { return this->glyphs.size(); }
96 int GetAdvance() const { return this->total_advance; }
97 };
98
100 class UniscribeLine : public std::vector<UniscribeVisualRun>, public ParagraphLayouter::Line {
101 public:
102 int GetLeading() const override;
103 int GetWidth() const override;
104 size_t CountRuns() const override { return this->size(); }
105 const VisualRun &GetVisualRun(size_t run) const override { return this->at(run); }
106
107 int GetInternalCharLength(char32_t c) const override
108 {
109 /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */
110 return c >= 0x010000U ? 2 : 1;
111 }
112 };
113
114 UniscribeParagraphLayout(std::vector<UniscribeRun> &&ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(std::move(ranges))
115 {
116 this->Reflow();
117 }
118
119 ~UniscribeParagraphLayout() override = default;
120
121 void Reflow() override
122 {
123 this->cur_range = this->ranges.begin();
124 this->cur_range_offset = 0;
125 }
126
127 std::unique_ptr<const Line> NextLine(int max_width) override;
128};
129
130void UniscribeResetScriptCache(FontSize size)
131{
132 if (_script_cache[size] != nullptr) {
133 ScriptFreeCache(&_script_cache[size]);
134 _script_cache[size] = nullptr;
135 }
136}
137
143static HFONT HFontFromFont(Font *font)
144{
145 if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect(reinterpret_cast<PLOGFONT>(const_cast<void *>(font->fc->GetOSHandle())));
146
147 LOGFONT logfont{};
148 logfont.lfHeight = font->fc->GetHeight();
149 logfont.lfWeight = FW_NORMAL;
150 logfont.lfCharSet = DEFAULT_CHARSET;
151 convert_to_fs(font->fc->GetFontName(), logfont.lfFaceName);
152
153 return CreateFontIndirect(&logfont);
154}
155
163{
164 /* Initial size guess for the number of glyphs recommended by Uniscribe. */
165 range.glyphs.resize(range.len * 3 / 2 + 16);
166 range.vis_attribs.resize(range.glyphs.size());
167
168 /* The char-to-glyph array is the same size as the input. */
169 range.char_to_glyph.resize(range.len);
170
171 HDC temp_dc = nullptr;
172 HFONT old_font = nullptr;
173 HFONT cur_font = nullptr;
174
175 while (true) {
176 /* Shape the text run by determining the glyphs needed for display. */
177 int glyphs_used = 0;
178 HRESULT hr = ScriptShape(temp_dc, &_script_cache[range.font->fc->GetSize()], buff + range.pos, range.len, (int)range.glyphs.size(), &range.sa, &range.glyphs[0], &range.char_to_glyph[0], &range.vis_attribs[0], &glyphs_used);
179
180 if (SUCCEEDED(hr)) {
181 range.glyphs.resize(glyphs_used);
182 range.vis_attribs.resize(glyphs_used);
183
184 /* Calculate the glyph positions. */
185 ABC abc;
186 range.advances.resize(range.glyphs.size());
187 range.offsets.resize(range.glyphs.size());
188 hr = ScriptPlace(temp_dc, &_script_cache[range.font->fc->GetSize()], &range.glyphs[0], (int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc);
189 if (SUCCEEDED(hr)) {
190 /* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs
191 * into a new vector and query the real glyph to use for these special chars. */
192 range.ft_glyphs.resize(range.glyphs.size());
193 for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
194 range.ft_glyphs[g_id] = range.glyphs[g_id];
195 }
196 for (int i = 0; i < range.len; i++) {
197 if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
198 auto pos = range.char_to_glyph[i];
199 if (range.ft_glyphs[pos] == 0) { // Font doesn't have our special glyph, so remap.
200 range.ft_glyphs[pos] = range.font->fc->MapCharToGlyph(buff[range.pos + i]);
201 range.offsets[pos].dv = (range.font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(range.font->fc->GetSize()))) / 2; // Align sprite font to centre
202 range.advances[pos] = range.font->fc->GetGlyphWidth(range.ft_glyphs[pos]);
203 }
204 }
205 }
206
207 range.total_advance = 0;
208 for (size_t i = 0; i < range.advances.size(); i++) {
209#ifdef WITH_FREETYPE
210 /* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
211 if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.ft_glyphs[i]);
212#endif
213 range.total_advance += range.advances[i];
214 }
215 break;
216 }
217 }
218
219 if (hr == E_OUTOFMEMORY) {
220 /* The glyph buffer needs to be larger. Just double it every time. */
221 range.glyphs.resize(range.glyphs.size() * 2);
222 range.vis_attribs.resize(range.vis_attribs.size() * 2);
223 } else if (hr == E_PENDING) {
224 /* Glyph data is not in cache, load native font. */
225 cur_font = HFontFromFont(range.font);
226 if (cur_font == nullptr) return false; // Sorry, no dice.
227
228 temp_dc = CreateCompatibleDC(nullptr);
229 SetMapMode(temp_dc, MM_TEXT);
230 old_font = (HFONT)SelectObject(temp_dc, cur_font);
231 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
232 /* Try again with the generic shaping engine. */
233 range.sa.eScript = SCRIPT_UNDEFINED;
234 } else {
235 /* Some unknown other error. */
236 if (temp_dc != nullptr) {
237 SelectObject(temp_dc, old_font);
238 DeleteObject(cur_font);
239 ReleaseDC(nullptr, temp_dc);
240 }
241 return false;
242 }
243 }
244
245 if (temp_dc != nullptr) {
246 SelectObject(temp_dc, old_font);
247 DeleteObject(cur_font);
248 ReleaseDC(nullptr, temp_dc);
249 }
250
251 return true;
252}
253
260static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
261{
262 /* Itemize text. */
263 SCRIPT_CONTROL control{};
264 control.uDefaultLanguage = _current_language->winlangid;
265
266 SCRIPT_STATE state{};
267 state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0;
268
269 std::vector<SCRIPT_ITEM> items(16);
270 while (true) {
271 /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */
272 int generated = 0;
273 HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated);
274
275 if (SUCCEEDED(hr)) {
276 /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */
277 items.resize(generated + 1);
278 break;
279 }
280 /* Some kind of error except item buffer too small. */
281 if (hr != E_OUTOFMEMORY) return std::vector<SCRIPT_ITEM>();
282
283 items.resize(items.size() * 2);
284 }
285
286 return items;
287}
288
289/* static */ std::unique_ptr<ParagraphLayouter> UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
290{
291 int32_t length = buff_end - buff;
292 /* Can't layout an empty string. */
293 if (length == 0) return nullptr;
294
295 /* Can't layout our in-built sprite fonts. */
296 for (auto const &[position, font] : font_mapping) {
297 if (font->fc->IsBuiltInFont()) return nullptr;
298 }
299
300 /* Itemize text. */
301 std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
302 if (items.empty()) return nullptr;
303
304 /* Build ranges from the items and the font map. A range is a run of text
305 * that is part of a single item and formatted using a single font style. */
306 std::vector<UniscribeRun> ranges;
307
308 int cur_pos = 0;
309 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
310 for (auto const &[position, font] : font_mapping) {
311 while (cur_pos < position && cur_item != items.end() - 1) {
312 /* Add a range that spans the intersection of the remaining item and font run. */
313 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
314 assert(stop_pos - cur_pos > 0);
315 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
316
317 /* Shape the range. */
318 if (!UniscribeShapeRun(buff, ranges.back())) {
319 return nullptr;
320 }
321
322 /* If we are at the end of the current item, advance to the next item. */
323 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
324 cur_pos = stop_pos;
325 }
326 }
327
328 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
329}
330
331/* virtual */ std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(int max_width)
332{
333 std::vector<UniscribeRun>::iterator start_run = this->cur_range;
334 std::vector<UniscribeRun>::iterator last_run = this->cur_range;
335
336 if (start_run == this->ranges.end()) return nullptr;
337
338 /* Add remaining width of the first run if it is a broken run. */
339 int cur_width = 0;
340 if (this->cur_range_offset != 0) {
341 std::vector<int> dx(start_run->len);
342 ScriptGetLogicalWidths(&start_run->sa, start_run->len, (int)start_run->glyphs.size(), &start_run->advances[0], &start_run->char_to_glyph[0], &start_run->vis_attribs[0], &dx[0]);
343
344 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
345 cur_width += *c;
346 }
347 ++last_run;
348 }
349
350 /* Gather runs until the line is full. */
351 while (last_run != this->ranges.end() && cur_width <= max_width) {
352 cur_width += last_run->total_advance;
353 ++last_run;
354 }
355
356 /* If the text does not fit into the available width, find a suitable breaking point. */
357 int remaining_offset = (last_run - 1)->len + 1;
358 int whitespace_count = 0;
359 if (cur_width > max_width) {
360 std::vector<SCRIPT_LOGATTR> log_attribs;
361
362 /* Get word break information. */
363 int width_avail = max_width;
364 int num_chars = this->cur_range_offset;
365 int start_offs = this->cur_range_offset;
366 int last_cluster = this->cur_range_offset + 1;
367 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
368 log_attribs.resize(r->pos - start_run->pos + r->len);
369 if (FAILED(ScriptBreak(this->text_buffer + r->pos + start_offs, r->len - start_offs, &r->sa, &log_attribs[r->pos - start_run->pos + start_offs]))) return nullptr;
370
371 std::vector<int> dx(r->len);
372 ScriptGetLogicalWidths(&r->sa, r->len, (int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
373
374 /* Count absolute max character count on the line. */
375 for (int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
376 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
377 width_avail -= dx[c];
378 }
379
380 start_offs = 0;
381 }
382
383 /* Walk backwards to find the last suitable breaking point. */
384 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
385
386 if (num_chars == this->cur_range_offset) {
387 /* Didn't find any suitable word break point, just break on the last cluster boundary. */
388 num_chars = last_cluster;
389 }
390
391 /* Eat any whitespace characters before the breaking point. */
392 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
393 /* Count whitespace after the breaking point. */
394 while (num_chars + whitespace_count < (int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
395
396 /* Get last run that corresponds to the number of characters to show. */
397 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
398 num_chars -= run->len;
399
400 if (num_chars <= 0) {
401 remaining_offset = num_chars + run->len + 1;
402 last_run = run + 1;
403 assert(remaining_offset - 1 > 0);
404 break;
405 }
406 }
407 }
408
409 /* Build display order from the runs. */
410 std::vector<BYTE> bidi_level;
411 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
412 bidi_level.push_back(r->sa.s.uBidiLevel);
413 }
414 std::vector<INT> vis_to_log(bidi_level.size());
415 if (FAILED(ScriptLayout((int)bidi_level.size(), &bidi_level[0], &vis_to_log[0], nullptr))) return nullptr;
416
417 /* Create line. */
418 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
419
420 int cur_pos = 0;
421 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
422 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
423 UniscribeRun run = *i_run;
424
425 /* Partial run after line break (either start or end)? Reshape run to get the first/last glyphs right. */
426 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
427 run.len = remaining_offset - 1;
428
429 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
430 }
431 if (i_run == start_run && this->cur_range_offset > 0) {
432 assert(run.len - this->cur_range_offset > 0);
433 run.pos += this->cur_range_offset;
434 run.len -= this->cur_range_offset;
435
436 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
437 }
438
439 line->emplace_back(run, cur_pos);
440 cur_pos += run.total_advance;
441 }
442
443 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
444 /* We didn't use up all of the last run, store remainder for the next line. */
445 this->cur_range_offset = remaining_offset + whitespace_count - 1;
446 this->cur_range = last_run - 1;
447 assert(this->cur_range->len > this->cur_range_offset);
448 } else {
449 this->cur_range_offset = 0;
450 this->cur_range = last_run;
451 }
452
453 return line;
454}
455
461{
462 int leading = 0;
463 for (const auto &run : *this) {
464 leading = std::max(leading, run.GetLeading());
465 }
466
467 return leading;
468}
469
475{
476 int length = 0;
477 for (const auto &run : *this) {
478 length += run.GetAdvance();
479 }
480
481 return length;
482}
483
484UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(const UniscribeRun &range, int x) : glyphs(range.ft_glyphs), char_to_glyph(range.char_to_glyph), start_pos(range.pos), total_advance(range.total_advance), font(range.font)
485{
486 this->positions.reserve(this->GetGlyphCount());
487
488 int advance = x;
489 for (size_t i = 0; i < this->GetGlyphCount(); i++) {
490 int x_advance = range.advances[i];
491 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
492
493 advance += x_advance;
494 }
495}
496
497UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
498 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
499 start_pos(other.start_pos), total_advance(other.total_advance), font(other.font),
500 glyph_to_char(std::move(other.glyph_to_char))
501{
502}
503
505{
506 if (this->glyph_to_char.empty()) {
507 this->glyph_to_char.resize(this->GetGlyphCount());
508
509 /* The char to glyph array contains the first glyph index of the cluster that is associated
510 * with each character. It is possible for a cluster to be formed of several chars. */
511 for (int c = 0; c < (int)this->char_to_glyph.size(); c++) {
512 /* If multiple chars map to one glyph, only refer back to the first character. */
513 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
514 }
515
516 /* We only marked the first glyph of each cluster in the loop above. Fill the gaps. */
517 int last_char = this->glyph_to_char[0];
518 for (size_t g = 0; g < this->GetGlyphCount(); g++) {
519 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
520 this->glyph_to_char[g] = last_char;
521 }
522 }
523
524 return this->glyph_to_char;
525}
526
527
528/* virtual */ void UniscribeStringIterator::SetString(std::string_view s)
529{
530 this->utf16_to_utf8.clear();
531 this->str_info.clear();
532 this->cur_pos = 0;
533
534 /* Uniscribe operates on UTF-16, thus we have to convert the input string.
535 * To be able to return proper offsets, we have to create a mapping at the same time. */
536 std::vector<wchar_t> utf16_str;
537 Utf8View view(s);
538 for (auto it = view.begin(), end = view.end(); it != end; ++it) {
539 size_t idx = it.GetByteOffset();
540 char32_t c = *it;
541 if (c < 0x10000) {
542 utf16_str.push_back((wchar_t)c);
543 } else {
544 /* Make a surrogate pair. */
545 utf16_str.push_back((wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
546 utf16_str.push_back((wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
547 this->utf16_to_utf8.push_back(idx);
548 }
549 this->utf16_to_utf8.push_back(idx);
550 }
551 this->utf16_to_utf8.push_back(s.size());
552
553 /* Query Uniscribe for word and cluster break information. */
554 this->str_info.resize(utf16_to_utf8.size());
555
556 if (!utf16_str.empty()) {
557 /* Itemize string into language runs. */
558 std::vector<SCRIPT_ITEM> runs = UniscribeItemizeString(&utf16_str[0], (int32_t)utf16_str.size());
559
560 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
561 /* Get information on valid word and character break.s */
562 int len = (run + 1)->iCharPos - run->iCharPos;
563 std::vector<SCRIPT_LOGATTR> attr(len);
564 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
565
566 /* Extract the information we're interested in. */
567 for (size_t c = 0; c < attr.size(); c++) {
568 /* First character of a run is always a valid word break. */
569 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
570 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
571 }
572 }
573 }
574
575 /* End-of-string is always a valid stopping point. */
576 this->str_info.back().char_stop = true;
577 this->str_info.back().word_stop = true;
578}
579
580/* virtual */ size_t UniscribeStringIterator::SetCurPosition(size_t pos)
581{
582 /* Convert incoming position to an UTF-16 string index. */
583 size_t utf16_pos = 0;
584 for (size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
585 if (this->utf16_to_utf8[i] == pos) {
586 utf16_pos = i;
587 break;
588 }
589 }
590
591 /* Sanitize in case we get a position inside a grapheme cluster. */
592 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
593 this->cur_pos = utf16_pos;
594
595 return this->utf16_to_utf8[this->cur_pos];
596}
597
598/* virtual */ size_t UniscribeStringIterator::Next(IterType what)
599{
600 assert(this->cur_pos <= this->utf16_to_utf8.size());
602
603 if (this->cur_pos == this->utf16_to_utf8.size()) return END;
604
605 do {
606 this->cur_pos++;
607 } while (this->cur_pos < this->utf16_to_utf8.size() && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
608
609 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
610}
611
613{
614 assert(this->cur_pos <= this->utf16_to_utf8.size());
616
617 if (this->cur_pos == 0) return END;
618
619 do {
620 this->cur_pos--;
621 } while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
622
623 return this->utf16_to_utf8[this->cur_pos];
624}
virtual std::string GetFontName()=0
Get the name of this font.
int GetHeight() const
Get the height of the font.
Definition fontcache.h:60
virtual const void * GetOSHandle()
Get the native OS font handle, if there is one.
Definition fontcache.h:115
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
virtual uint GetGlyphWidth(GlyphID key)=0
Get the width of the glyph with the given key.
FontSize GetSize() const
Get the FontSize of the font.
Definition fontcache.h:54
Container with information about a font.
Definition gfx_layout.h:97
FontCache * fc
The font we are using.
Definition gfx_layout.h:99
A single line worth of VisualRuns.
Definition gfx_layout.h:176
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:133
Interface to glue fallback and normal layouter into one.
Definition gfx_layout.h:111
static const size_t END
Sentinel to indicate end-of-iteration.
Definition string_base.h:25
IterType
Type of the iterator.
Definition string_base.h:19
@ ITER_WORD
Iterate over words.
Definition string_base.h:21
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
Definition string_base.h:20
static std::unique_ptr< ParagraphLayouter > GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
Get the actual ParagraphLayout for the given buffer.
wchar_t CharType
Helper for GetLayouter, to get the right type.
A single line worth of VisualRuns.
int GetWidth() const override
Get the width of this line.
size_t CountRuns() const override
Get the number of runs in this line.
int GetInternalCharLength(char32_t c) const override
Get the number of elements the given character occupies in the underlying text buffer of the Layouter...
int GetLeading() const override
Get the height of the line.
const VisualRun & GetVisualRun(size_t run) const override
Get a reference to the given run.
Visual run contains data about the bit of text with the same font.
const Font * GetFont() const override
Get the font.
std::span< const GlyphID > GetGlyphs() const override
Get the glyphs to draw.
size_t GetGlyphCount() const override
Get the number of glyphs.
std::span< const int > GetGlyphToCharMap() const override
The offset for each of the glyphs to the character run that was passed to the Layouter.
std::span< const Position > GetPositions() const override
Get the positions for each of the glyphs.
int GetLeading() const override
Get the font leading, or distance between the baselines of consecutive lines.
Wrapper for doing layouts with Uniscribe.
int cur_range_offset
Offset from the start of the current run from where to output.
void Reflow() override
Reset the position to the start of the paragraph.
std::vector< UniscribeRun > ranges
All runs of the text.
std::unique_ptr< const Line > NextLine(int max_width) override
Construct a new line with a maximum width.
std::vector< UniscribeRun >::iterator cur_range
The next run to be output.
size_t Prev(IterType what) override
Move the cursor back by one iteration unit.
std::vector< CharInfo > str_info
Break information for each code point.
void SetString(std::string_view s) override
Set a new iteration string.
size_t cur_pos
Current iteration position.
std::vector< size_t > utf16_to_utf8
Mapping from UTF-16 code point position to index in the UTF-8 source string.
size_t Next(IterType what) override
Advance the cursor by one iteration unit.
size_t SetCurPosition(size_t pos) override
Change the current string cursor.
Constant span of UTF-8 encoded data.
Definition utf8.hpp:28
Control codes that are embedded in the translation strings.
Functions related to debugging.
std::vector< std::pair< int, Font * > > FontMap
Mapping from index to font.
Definition gfx_layout.h:106
FontSize
Available font sizes.
Definition gfx_type.h:248
Information about languages and their files.
const LanguageMetadata * _current_language
The currently loaded language.
Definition strings.cpp:54
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
static HFONT HFontFromFont(Font *font)
Load the matching native Windows font.
static std::vector< SCRIPT_ITEM > UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
Break a string into language formatting ranges.
static SCRIPT_CACHE _script_cache[FS_END]
Uniscribe cache for internal font information, cleared when OTTD changes fonts.
static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
Determine the glyph positions for a run.
Functions related to laying out text on Win32.
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
Contains all information about a run of characters.
Handling of UTF-8 encoded data.
wchar_t * convert_to_fs(std::string_view src, std::span< wchar_t > dst_buf)
Convert from OpenTTD's encoding to that of the environment in UNICODE.
Definition win32.cpp:403
Declarations of functions for MS windows systems.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107