OpenTTD Source 20260218-master-g2123fca5ea
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
58static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length);
61
65class UniscribeParagraphLayout : public ParagraphLayouter {
66private:
68
69 std::vector<UniscribeRun> ranges;
70 std::vector<UniscribeRun>::iterator cur_range;
72
73public:
75 class UniscribeVisualRun : public ParagraphLayouter::VisualRun {
76 private:
77 std::vector<GlyphID> glyphs;
78 std::vector<Position> positions;
79 std::vector<WORD> char_to_glyph;
80
81 int start_pos;
82 int total_advance;
83 Font *font;
84
85 mutable std::vector<int> glyph_to_char;
86
87 public:
88 UniscribeVisualRun(const UniscribeRun &range, int x);
89 UniscribeVisualRun(UniscribeVisualRun &&other) noexcept;
90
91 std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
92 std::span<const Position> GetPositions() const override { return this->positions; }
93 std::span<const int> GetGlyphToCharMap() const override;
94
95 const Font *GetFont() const override { return this->font; }
96 int GetLeading() const override { return this->font->fc->GetHeight(); }
97 size_t GetGlyphCount() const override { return this->glyphs.size(); }
98 int GetAdvance() const { return this->total_advance; }
99 };
100
102 class UniscribeLine : public std::vector<UniscribeVisualRun>, public ParagraphLayouter::Line {
103 public:
104 int GetLeading() const override;
105 int GetWidth() const override;
106 size_t CountRuns() const override { return this->size(); }
107 const VisualRun &GetVisualRun(size_t run) const override { return this->at(run); }
108
109 int GetInternalCharLength(char32_t c) const override
110 {
111 /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */
112 return c >= 0x010000U ? 2 : 1;
113 }
114 };
115
116 UniscribeParagraphLayout(std::vector<UniscribeRun> &&ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(std::move(ranges))
117 {
118 this->Reflow();
119 }
120
121 ~UniscribeParagraphLayout() override = default;
122
123 void Reflow() override
124 {
125 this->cur_range = this->ranges.begin();
126 this->cur_range_offset = 0;
127 }
128
129 std::unique_ptr<const Line> NextLine(int max_width) override;
130};
131
132void UniscribeResetScriptCache(FontSize size)
133{
134 if (_script_cache[size] != nullptr) {
135 ScriptFreeCache(&_script_cache[size]);
136 _script_cache[size] = nullptr;
137 }
138}
139
141static HFONT HFontFromFont(Font *font)
142{
143 if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect(reinterpret_cast<PLOGFONT>(const_cast<void *>(font->fc->GetOSHandle())));
144
145 LOGFONT logfont{};
146 logfont.lfHeight = font->fc->GetHeight();
147 logfont.lfWeight = FW_NORMAL;
148 logfont.lfCharSet = DEFAULT_CHARSET;
149 convert_to_fs(font->fc->GetFontName(), logfont.lfFaceName);
150
151 return CreateFontIndirect(&logfont);
152}
153
156{
157 /* Initial size guess for the number of glyphs recommended by Uniscribe. */
158 range.glyphs.resize(range.len * 3 / 2 + 16);
159 range.vis_attribs.resize(range.glyphs.size());
160
161 /* The char-to-glyph array is the same size as the input. */
162 range.char_to_glyph.resize(range.len);
163
164 HDC temp_dc = nullptr;
165 HFONT old_font = nullptr;
166 HFONT cur_font = nullptr;
167
168 while (true) {
169 /* Shape the text run by determining the glyphs needed for display. */
170 int glyphs_used = 0;
171 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);
172
173 if (SUCCEEDED(hr)) {
174 range.glyphs.resize(glyphs_used);
175 range.vis_attribs.resize(glyphs_used);
176
177 /* Calculate the glyph positions. */
178 ABC abc;
179 range.advances.resize(range.glyphs.size());
180 range.offsets.resize(range.glyphs.size());
181 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);
182 if (SUCCEEDED(hr)) {
183 /* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs
184 * into a new vector and query the real glyph to use for these special chars. */
185 range.ft_glyphs.resize(range.glyphs.size());
186 for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
187 range.ft_glyphs[g_id] = range.glyphs[g_id];
188 }
189 for (int i = 0; i < range.len; i++) {
190 if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
191 auto pos = range.char_to_glyph[i];
192 if (range.ft_glyphs[pos] == 0) { // Font doesn't have our special glyph, so remap.
193 range.ft_glyphs[pos] = range.font->fc->MapCharToGlyph(buff[range.pos + i]);
194 range.offsets[pos].dv = (range.font->fc->GetHeight() - ScaleSpriteTrad(FontCache::GetDefaultFontHeight(range.font->fc->GetSize()))) / 2; // Align sprite font to centre
195 range.advances[pos] = range.font->fc->GetGlyphWidth(range.ft_glyphs[pos]);
196 }
197 }
198 }
199
200 range.total_advance = 0;
201 for (size_t i = 0; i < range.advances.size(); i++) {
202#ifdef WITH_FREETYPE
203 /* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
204 if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.ft_glyphs[i]);
205#endif
206 range.total_advance += range.advances[i];
207 }
208 break;
209 }
210 }
211
212 if (hr == E_OUTOFMEMORY) {
213 /* The glyph buffer needs to be larger. Just double it every time. */
214 range.glyphs.resize(range.glyphs.size() * 2);
215 range.vis_attribs.resize(range.vis_attribs.size() * 2);
216 } else if (hr == E_PENDING) {
217 /* Glyph data is not in cache, load native font. */
218 cur_font = HFontFromFont(range.font);
219 if (cur_font == nullptr) return false; // Sorry, no dice.
220
221 temp_dc = CreateCompatibleDC(nullptr);
222 SetMapMode(temp_dc, MM_TEXT);
223 old_font = (HFONT)SelectObject(temp_dc, cur_font);
224 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
225 /* Try again with the generic shaping engine. */
226 range.sa.eScript = SCRIPT_UNDEFINED;
227 } else {
228 /* Some unknown other error. */
229 if (temp_dc != nullptr) {
230 SelectObject(temp_dc, old_font);
231 DeleteObject(cur_font);
232 ReleaseDC(nullptr, temp_dc);
233 }
234 return false;
235 }
236 }
237
238 if (temp_dc != nullptr) {
239 SelectObject(temp_dc, old_font);
240 DeleteObject(cur_font);
241 ReleaseDC(nullptr, temp_dc);
242 }
243
244 return true;
245}
246
247static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
248{
249 /* Itemize text. */
250 SCRIPT_CONTROL control{};
251 control.uDefaultLanguage = _current_language->winlangid;
252
253 SCRIPT_STATE state{};
254 state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0;
255
256 std::vector<SCRIPT_ITEM> items(16);
257 while (true) {
258 /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */
259 int generated = 0;
260 HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated);
261
262 if (SUCCEEDED(hr)) {
263 /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */
264 items.resize(generated + 1);
265 break;
266 }
267 /* Some kind of error except item buffer too small. */
268 if (hr != E_OUTOFMEMORY) return std::vector<SCRIPT_ITEM>();
269
270 items.resize(items.size() * 2);
271 }
272
273 return items;
274}
275
276/* static */ std::unique_ptr<ParagraphLayouter> UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
277{
278 int32_t length = buff_end - buff;
279 /* Can't layout an empty string. */
280 if (length == 0) return nullptr;
281
282 /* Can't layout our in-built sprite fonts. */
283 for (auto const &[position, font] : font_mapping) {
284 if (font->fc->IsBuiltInFont()) return nullptr;
285 }
286
287 /* Itemize text. */
288 std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
289 if (items.empty()) return nullptr;
290
291 /* Build ranges from the items and the font map. A range is a run of text
292 * that is part of a single item and formatted using a single font style. */
293 std::vector<UniscribeRun> ranges;
294
295 int cur_pos = 0;
296 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
297 for (auto const &[position, font] : font_mapping) {
298 while (cur_pos < position && cur_item != items.end() - 1) {
299 /* Add a range that spans the intersection of the remaining item and font run. */
300 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
301 assert(stop_pos - cur_pos > 0);
302 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
303
304 /* Shape the range. */
305 if (!UniscribeShapeRun(buff, ranges.back())) {
306 return nullptr;
307 }
308
309 /* If we are at the end of the current item, advance to the next item. */
310 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
311 cur_pos = stop_pos;
312 }
313 }
314
315 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
316}
317
318/* virtual */ std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(int max_width)
319{
320 std::vector<UniscribeRun>::iterator start_run = this->cur_range;
321 std::vector<UniscribeRun>::iterator last_run = this->cur_range;
322
323 if (start_run == this->ranges.end()) return nullptr;
324
325 /* Add remaining width of the first run if it is a broken run. */
326 int cur_width = 0;
327 if (this->cur_range_offset != 0) {
328 std::vector<int> dx(start_run->len);
329 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]);
330
331 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
332 cur_width += *c;
333 }
334 ++last_run;
335 }
336
337 /* Gather runs until the line is full. */
338 while (last_run != this->ranges.end() && cur_width <= max_width) {
339 cur_width += last_run->total_advance;
340 ++last_run;
341 }
342
343 /* If the text does not fit into the available width, find a suitable breaking point. */
344 int remaining_offset = (last_run - 1)->len + 1;
345 int whitespace_count = 0;
346 if (cur_width > max_width) {
347 std::vector<SCRIPT_LOGATTR> log_attribs;
348
349 /* Get word break information. */
350 int width_avail = max_width;
351 int num_chars = this->cur_range_offset;
352 int start_offs = this->cur_range_offset;
353 int last_cluster = this->cur_range_offset + 1;
354 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
355 log_attribs.resize(r->pos - start_run->pos + r->len);
356 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;
357
358 std::vector<int> dx(r->len);
359 ScriptGetLogicalWidths(&r->sa, r->len, (int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
360
361 /* Count absolute max character count on the line. */
362 for (int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
363 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
364 width_avail -= dx[c];
365 }
366
367 start_offs = 0;
368 }
369
370 /* Walk backwards to find the last suitable breaking point. */
371 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
372
373 if (num_chars == this->cur_range_offset) {
374 /* Didn't find any suitable word break point, just break on the last cluster boundary. */
375 num_chars = last_cluster;
376 }
377
378 /* Eat any whitespace characters before the breaking point. */
379 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
380 /* Count whitespace after the breaking point. */
381 while (num_chars + whitespace_count < (int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
382
383 /* Get last run that corresponds to the number of characters to show. */
384 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
385 num_chars -= run->len;
386
387 if (num_chars <= 0) {
388 remaining_offset = num_chars + run->len + 1;
389 last_run = run + 1;
390 assert(remaining_offset - 1 > 0);
391 break;
392 }
393 }
394 }
395
396 /* Build display order from the runs. */
397 std::vector<BYTE> bidi_level;
398 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
399 bidi_level.push_back(r->sa.s.uBidiLevel);
400 }
401 std::vector<INT> vis_to_log(bidi_level.size());
402 if (FAILED(ScriptLayout((int)bidi_level.size(), &bidi_level[0], &vis_to_log[0], nullptr))) return nullptr;
403
404 /* Create line. */
405 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
406
407 int cur_pos = 0;
408 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
409 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
410 UniscribeRun run = *i_run;
411
412 /* Partial run after line break (either start or end)? Reshape run to get the first/last glyphs right. */
413 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
414 run.len = remaining_offset - 1;
415
416 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
417 }
418 if (i_run == start_run && this->cur_range_offset > 0) {
419 assert(run.len - this->cur_range_offset > 0);
420 run.pos += this->cur_range_offset;
421 run.len -= this->cur_range_offset;
422
423 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
424 }
425
426 line->emplace_back(run, cur_pos);
427 cur_pos += run.total_advance;
428 }
429
430 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
431 /* We didn't use up all of the last run, store remainder for the next line. */
432 this->cur_range_offset = remaining_offset + whitespace_count - 1;
433 this->cur_range = last_run - 1;
434 assert(this->cur_range->len > this->cur_range_offset);
435 } else {
436 this->cur_range_offset = 0;
437 this->cur_range = last_run;
438 }
439
440 return line;
441}
442
448{
449 int leading = 0;
450 for (const auto &run : *this) {
451 leading = std::max(leading, run.GetLeading());
452 }
453
454 return leading;
455}
456
462{
463 int length = 0;
464 for (const auto &run : *this) {
465 length += run.GetAdvance();
466 }
467
468 return length;
469}
470
471UniscribeParagraphLayout::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)
472{
473 this->positions.reserve(this->GetGlyphCount());
474
475 int advance = x;
476 for (size_t i = 0; i < this->GetGlyphCount(); i++) {
477 int x_advance = range.advances[i];
478 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
479
480 advance += x_advance;
481 }
482}
483
484UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
485 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
486 start_pos(other.start_pos), total_advance(other.total_advance), font(other.font),
487 glyph_to_char(std::move(other.glyph_to_char))
488{
489}
490
492{
493 if (this->glyph_to_char.empty()) {
494 this->glyph_to_char.resize(this->GetGlyphCount());
495
496 /* The char to glyph array contains the first glyph index of the cluster that is associated
497 * with each character. It is possible for a cluster to be formed of several chars. */
498 for (int c = 0; c < (int)this->char_to_glyph.size(); c++) {
499 /* If multiple chars map to one glyph, only refer back to the first character. */
500 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
501 }
502
503 /* We only marked the first glyph of each cluster in the loop above. Fill the gaps. */
504 int last_char = this->glyph_to_char[0];
505 for (size_t g = 0; g < this->GetGlyphCount(); g++) {
506 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
507 this->glyph_to_char[g] = last_char;
508 }
509 }
510
511 return this->glyph_to_char;
512}
513
514
515/* virtual */ void UniscribeStringIterator::SetString(std::string_view s)
516{
517 this->utf16_to_utf8.clear();
518 this->str_info.clear();
519 this->cur_pos = 0;
520
521 /* Uniscribe operates on UTF-16, thus we have to convert the input string.
522 * To be able to return proper offsets, we have to create a mapping at the same time. */
523 std::vector<wchar_t> utf16_str;
524 Utf8View view(s);
525 for (auto it = view.begin(), end = view.end(); it != end; ++it) {
526 size_t idx = it.GetByteOffset();
527 char32_t c = *it;
528 if (c < 0x10000) {
529 utf16_str.push_back((wchar_t)c);
530 } else {
531 /* Make a surrogate pair. */
532 utf16_str.push_back((wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
533 utf16_str.push_back((wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
534 this->utf16_to_utf8.push_back(idx);
535 }
536 this->utf16_to_utf8.push_back(idx);
537 }
538 this->utf16_to_utf8.push_back(s.size());
539
540 /* Query Uniscribe for word and cluster break information. */
541 this->str_info.resize(utf16_to_utf8.size());
542
543 if (!utf16_str.empty()) {
544 /* Itemize string into language runs. */
545 std::vector<SCRIPT_ITEM> runs = UniscribeItemizeString(&utf16_str[0], (int32_t)utf16_str.size());
546
547 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
548 /* Get information on valid word and character break.s */
549 int len = (run + 1)->iCharPos - run->iCharPos;
550 std::vector<SCRIPT_LOGATTR> attr(len);
551 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
552
553 /* Extract the information we're interested in. */
554 for (size_t c = 0; c < attr.size(); c++) {
555 /* First character of a run is always a valid word break. */
556 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
557 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
558 }
559 }
560 }
561
562 /* End-of-string is always a valid stopping point. */
563 this->str_info.back().char_stop = true;
564 this->str_info.back().word_stop = true;
565}
566
567/* virtual */ size_t UniscribeStringIterator::SetCurPosition(size_t pos)
568{
569 /* Convert incoming position to an UTF-16 string index. */
570 size_t utf16_pos = 0;
571 for (size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
572 if (this->utf16_to_utf8[i] == pos) {
573 utf16_pos = i;
574 break;
575 }
576 }
577
578 /* Sanitize in case we get a position inside a grapheme cluster. */
579 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
580 this->cur_pos = utf16_pos;
581
582 return this->utf16_to_utf8[this->cur_pos];
583}
584
585/* virtual */ size_t UniscribeStringIterator::Next(IterType what)
586{
587 assert(this->cur_pos <= this->utf16_to_utf8.size());
589
590 if (this->cur_pos == this->utf16_to_utf8.size()) return END;
591
592 do {
593 this->cur_pos++;
594 } 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));
595
596 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
597}
598
600{
601 assert(this->cur_pos <= this->utf16_to_utf8.size());
603
604 if (this->cur_pos == 0) return END;
605
606 do {
607 this->cur_pos--;
608 } while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
609
610 return this->utf16_to_utf8[this->cur_pos];
611}
virtual std::string GetFontName()=0
Get the name of this font.
int GetHeight() const
Get the height of the font.
Definition fontcache.h:59
virtual const void * GetOSHandle()
Get the native OS font handle, if there is one.
Definition fontcache.h:114
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:53
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:174
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:132
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)
Generate and place glyphs for a run of characters.
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:391
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