OpenTTD Source 20251213-master-g1091fa6071
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
10#include "../../stdafx.h"
11#include "../../debug.h"
12#include "string_uniscribe.h"
13#include "../../gfx_func.h"
14#include "../../gfx_layout_fallback.h"
15#include "../../language.h"
16#include "../../strings_func.h"
17#include "../../string_func.h"
18#include "../../core/utf8.hpp"
19#include "../../table/control_codes.h"
20#include "../../zoom_func.h"
21#include "win32.h"
22
23#include <windows.h>
24#include <usp10.h>
25
26#include "../../safeguards.h"
27
28#ifdef _MSC_VER
29# pragma comment(lib, "usp10")
30#endif
31
32
34static std::map<FontIndex, SCRIPT_CACHE> _script_cache;
35
41 int pos;
42 int len;
43 Font font;
44
45 std::vector<GlyphID> ft_glyphs;
46
47 SCRIPT_ANALYSIS sa;
48 std::vector<WORD> char_to_glyph;
49
50 std::vector<SCRIPT_VISATTR> vis_attribs;
51 std::vector<WORD> glyphs;
52 std::vector<int> advances;
53 std::vector<GOFFSET> offsets;
54 int total_advance;
55
56 UniscribeRun(int pos, int len, const Font &font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
57
59};
60
62static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length);
65
70private:
72
73 std::vector<UniscribeRun> ranges;
74 std::vector<UniscribeRun>::iterator cur_range;
76
77public:
80 private:
81 std::vector<GlyphID> glyphs;
82 std::vector<Position> positions;
83 std::vector<WORD> char_to_glyph;
84
85 int start_pos;
86 int total_advance;
87 int num_glyphs;
88 Font font;
89
90 mutable std::vector<int> glyph_to_char;
91
92 public:
93 UniscribeVisualRun(const UniscribeRun &range, int x);
94 UniscribeVisualRun(UniscribeVisualRun &&other) noexcept;
95
96 std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
97 std::span<const Position> GetPositions() const override { return this->positions; }
98 std::span<const int> GetGlyphToCharMap() const override;
99
100 const Font &GetFont() const override { return this->font; }
101 int GetLeading() const override { return GetCharacterHeight(this->font.GetFontCache().GetSize()); }
102 int GetGlyphCount() const override { return this->num_glyphs; }
103 int GetAdvance() const { return this->total_advance; }
104 };
105
107 class UniscribeLine : public std::vector<UniscribeVisualRun>, public ParagraphLayouter::Line {
108 public:
109 int GetLeading() const override;
110 int GetWidth() const override;
111 int CountRuns() const override { return (uint)this->size(); }
112 const VisualRun &GetVisualRun(int run) const override { return this->at(run); }
113
114 int GetInternalCharLength(char32_t c) const override
115 {
116 /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */
117 return c >= 0x010000U ? 2 : 1;
118 }
119 };
120
121 UniscribeParagraphLayout(std::vector<UniscribeRun> &&ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(std::move(ranges))
122 {
123 this->Reflow();
124 }
125
126 ~UniscribeParagraphLayout() override {}
127
128 void Reflow() override
129 {
130 this->cur_range = this->ranges.begin();
131 this->cur_range_offset = 0;
132 }
133
134 std::unique_ptr<const Line> NextLine(int max_width) override;
135};
136
137void UniscribeResetScriptCache(FontSize)
138{
139 for (auto &sc : _script_cache) {
140 ScriptFreeCache(&sc.second);
141 }
142 _script_cache.clear();
143}
144
146static HFONT HFontFromFont(const Font &font)
147{
148 FontCache &fc = font.GetFontCache();
149
150 if (fc.GetOSHandle() != nullptr) return CreateFontIndirect(reinterpret_cast<PLOGFONT>(const_cast<void *>(fc.GetOSHandle())));
151
152 LOGFONT logfont{};
153 logfont.lfHeight = fc.GetHeight();
154 logfont.lfWeight = FW_NORMAL;
155 logfont.lfCharSet = DEFAULT_CHARSET;
156 convert_to_fs(fc.GetFontName(), logfont.lfFaceName);
157
158 return CreateFontIndirect(&logfont);
159}
160
167{
168 FontCache &fc = this->font.GetFontCache();
169
170 this->glyphs.reserve(this->len);
171
172 /* Read each UTF-16 character, mapping to an appropriate glyph. */
173 for (int i = this->pos; i < this->pos + this->len; i += Utf16IsLeadSurrogate(buff[i]) ? 2 : 1) {
174 char32_t c = Utf16DecodeChar(reinterpret_cast<const uint16_t *>(buff + i));
175 if (this->sa.fRTL) c = SwapRtlPairedCharacters(c);
176 this->glyphs.emplace_back(fc.MapCharToGlyph(c));
177 }
178
179 /* Reverse the sequence if this run is RTL. */
180 if (this->sa.fRTL) {
181 std::reverse(std::begin(this->glyphs), std::end(this->glyphs));
182 }
183
184 this->offsets.reserve(this->glyphs.size());
185
186 /* Set positions of each glyph. */
187 int y_offset = fc.GetGlyphYOffset();
188 int advance = 0;
189 for (const GlyphID glyph : this->glyphs) {
190 this->offsets.emplace_back(advance, y_offset);
191 int x_advance = fc.GetGlyphWidth(glyph);
192 this->advances.push_back(x_advance);
193 advance += x_advance;
194 }
195}
196
199{
200 FontCache &fc = range.font.GetFontCache();
201
202 /* Initial size guess for the number of glyphs recommended by Uniscribe. */
203 range.glyphs.resize(range.len * 3 / 2 + 16);
204 range.vis_attribs.resize(range.glyphs.size());
205
206 /* The char-to-glyph array is the same size as the input. */
207 range.char_to_glyph.resize(range.len);
208
209 HDC temp_dc = nullptr;
210 HFONT old_font = nullptr;
211 HFONT cur_font = nullptr;
212
213 if (fc.IsBuiltInFont()) {
214 range.FallbackShape(buff);
215 return true;
216 }
217
218 while (true) {
219 /* Shape the text run by determining the glyphs needed for display. */
220 int glyphs_used = 0;
221 HRESULT hr = ScriptShape(temp_dc, &_script_cache[fc.GetIndex()], 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);
222
223 if (SUCCEEDED(hr)) {
224 range.glyphs.resize(glyphs_used);
225 range.vis_attribs.resize(glyphs_used);
226
227 /* Calculate the glyph positions. */
228 ABC abc;
229 range.advances.resize(range.glyphs.size());
230 range.offsets.resize(range.glyphs.size());
231 hr = ScriptPlace(temp_dc, &_script_cache[fc.GetIndex()], &range.glyphs[0], (int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc);
232 if (SUCCEEDED(hr)) {
233 /* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs
234 * into a new vector and query the real glyph to use for these special chars. */
235 range.ft_glyphs.resize(range.glyphs.size());
236 for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
237 range.ft_glyphs[g_id] = range.glyphs[g_id];
238 }
239
240 range.total_advance = 0;
241 for (size_t i = 0; i < range.advances.size(); i++) {
242#ifdef WITH_FREETYPE
243 /* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
244 if (range.advances[i] > 0 && range.glyphs[i] != 0xFFFF) range.advances[i] = fc.GetGlyphWidth(range.glyphs[i]);
245#endif
246 range.total_advance += range.advances[i];
247 }
248 break;
249 }
250 }
251
252 if (hr == E_OUTOFMEMORY) {
253 /* The glyph buffer needs to be larger. Just double it every time. */
254 range.glyphs.resize(range.glyphs.size() * 2);
255 range.vis_attribs.resize(range.vis_attribs.size() * 2);
256 } else if (hr == E_PENDING) {
257 /* Glyph data is not in cache, load native font. */
258 cur_font = HFontFromFont(range.font);
259 if (cur_font == nullptr) return false; // Sorry, no dice.
260
261 temp_dc = CreateCompatibleDC(nullptr);
262 SetMapMode(temp_dc, MM_TEXT);
263 old_font = (HFONT)SelectObject(temp_dc, cur_font);
264 } else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
265 /* Try again with the generic shaping engine. */
266 range.sa.eScript = SCRIPT_UNDEFINED;
267 } else {
268 /* Some unknown other error. */
269 if (temp_dc != nullptr) {
270 SelectObject(temp_dc, old_font);
271 DeleteObject(cur_font);
272 ReleaseDC(nullptr, temp_dc);
273 }
274 return false;
275 }
276 }
277
278 if (temp_dc != nullptr) {
279 SelectObject(temp_dc, old_font);
280 DeleteObject(cur_font);
281 ReleaseDC(nullptr, temp_dc);
282 }
283
284 return true;
285}
286
287static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
288{
289 /* Itemize text. */
290 SCRIPT_CONTROL control{};
291 control.uDefaultLanguage = _current_language->winlangid;
292
293 SCRIPT_STATE state{};
294 state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0;
295
296 std::vector<SCRIPT_ITEM> items(16);
297 while (true) {
298 /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */
299 int generated = 0;
300 HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated);
301
302 if (SUCCEEDED(hr)) {
303 /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */
304 items.resize(generated + 1);
305 break;
306 }
307 /* Some kind of error except item buffer too small. */
308 if (hr != E_OUTOFMEMORY) return std::vector<SCRIPT_ITEM>();
309
310 items.resize(items.size() * 2);
311 }
312
313 return items;
314}
315
316/* static */ std::unique_ptr<ParagraphLayouter> UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
317{
318 int32_t length = buff_end - buff;
319 /* Can't layout an empty string. */
320 if (length == 0) return nullptr;
321
322 /* Itemize text. */
323 std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
324 if (items.empty()) return nullptr;
325
326 /* Build ranges from the items and the font map. A range is a run of text
327 * that is part of a single item and formatted using a single font style. */
328 std::vector<UniscribeRun> ranges;
329
330 int cur_pos = 0;
331 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
332 for (auto const &[position, font] : font_mapping) {
333 while (cur_pos < position && cur_item != items.end() - 1) {
334 /* Add a range that spans the intersection of the remaining item and font run. */
335 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
336 assert(stop_pos - cur_pos > 0);
337 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
338
339 /* Shape the range. */
340 if (!UniscribeShapeRun(buff, ranges.back())) {
341 return nullptr;
342 }
343
344 /* If we are at the end of the current item, advance to the next item. */
345 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
346 cur_pos = stop_pos;
347 }
348 }
349
350 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
351}
352
353/* virtual */ std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(int max_width)
354{
355 std::vector<UniscribeRun>::iterator start_run = this->cur_range;
356 std::vector<UniscribeRun>::iterator last_run = this->cur_range;
357
358 if (start_run == this->ranges.end()) return nullptr;
359
360 /* Add remaining width of the first run if it is a broken run. */
361 int cur_width = 0;
362 if (this->cur_range_offset != 0) {
363 std::vector<int> dx(start_run->len);
364 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]);
365
366 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
367 cur_width += *c;
368 }
369 ++last_run;
370 }
371
372 /* Gather runs until the line is full. */
373 while (last_run != this->ranges.end() && cur_width <= max_width) {
374 cur_width += last_run->total_advance;
375 ++last_run;
376 }
377
378 /* If the text does not fit into the available width, find a suitable breaking point. */
379 int remaining_offset = (last_run - 1)->len + 1;
380 int whitespace_count = 0;
381 if (cur_width > max_width) {
382 std::vector<SCRIPT_LOGATTR> log_attribs;
383
384 /* Get word break information. */
385 int width_avail = max_width;
386 int num_chars = this->cur_range_offset;
387 int start_offs = this->cur_range_offset;
388 int last_cluster = this->cur_range_offset + 1;
389 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
390 log_attribs.resize(r->pos - start_run->pos + r->len);
391 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;
392
393 std::vector<int> dx(r->len);
394 ScriptGetLogicalWidths(&r->sa, r->len, (int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
395
396 /* Count absolute max character count on the line. */
397 for (int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
398 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
399 width_avail -= dx[c];
400 }
401
402 start_offs = 0;
403 }
404
405 /* Walk backwards to find the last suitable breaking point. */
406 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
407
408 if (num_chars == this->cur_range_offset) {
409 /* Didn't find any suitable word break point, just break on the last cluster boundary. */
410 num_chars = last_cluster;
411 }
412
413 /* Eat any whitespace characters before the breaking point. */
414 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
415 /* Count whitespace after the breaking point. */
416 while (num_chars + whitespace_count < (int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
417
418 /* Get last run that corresponds to the number of characters to show. */
419 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
420 num_chars -= run->len;
421
422 if (num_chars <= 0) {
423 remaining_offset = num_chars + run->len + 1;
424 last_run = run + 1;
425 assert(remaining_offset - 1 > 0);
426 break;
427 }
428 }
429 }
430
431 /* Build display order from the runs. */
432 std::vector<BYTE> bidi_level;
433 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
434 bidi_level.push_back(r->sa.s.uBidiLevel);
435 }
436 std::vector<INT> vis_to_log(bidi_level.size());
437 if (FAILED(ScriptLayout((int)bidi_level.size(), &bidi_level[0], &vis_to_log[0], nullptr))) return nullptr;
438
439 /* Create line. */
440 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
441
442 int cur_pos = 0;
443 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
444 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
445 UniscribeRun run = *i_run;
446
447 /* Partial run after line break (either start or end)? Reshape run to get the first/last glyphs right. */
448 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
449 run.len = remaining_offset - 1;
450
451 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
452 }
453 if (i_run == start_run && this->cur_range_offset > 0) {
454 assert(run.len - this->cur_range_offset > 0);
455 run.pos += this->cur_range_offset;
456 run.len -= this->cur_range_offset;
457
458 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
459 }
460
461 line->emplace_back(run, cur_pos);
462 cur_pos += run.total_advance;
463 }
464
465 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
466 /* We didn't use up all of the last run, store remainder for the next line. */
467 this->cur_range_offset = remaining_offset + whitespace_count - 1;
468 this->cur_range = last_run - 1;
469 assert(this->cur_range->len > this->cur_range_offset);
470 } else {
471 this->cur_range_offset = 0;
472 this->cur_range = last_run;
473 }
474
475 return line;
476}
477
483{
484 int leading = 0;
485 for (const auto &run : *this) {
486 leading = std::max(leading, run.GetLeading());
487 }
488
489 return leading;
490}
491
497{
498 int length = 0;
499 for (const auto &run : *this) {
500 length += run.GetAdvance();
501 }
502
503 return length;
504}
505
506UniscribeParagraphLayout::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)
507{
508 this->num_glyphs = (int)glyphs.size();
509 this->positions.reserve(this->num_glyphs);
510
511 int advance = x;
512 for (int i = 0; i < this->num_glyphs; i++) {
513 int x_advance = range.advances[i];
514 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
515
516 advance += x_advance;
517 }
518}
519
520UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
521 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
522 start_pos(other.start_pos), total_advance(other.total_advance), num_glyphs(other.num_glyphs), font(other.font),
523 glyph_to_char(std::move(other.glyph_to_char))
524{
525}
526
527std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap() const
528{
529 if (this->glyph_to_char.empty()) {
530 this->glyph_to_char.resize(this->GetGlyphCount());
531
532 /* The char to glyph array contains the first glyph index of the cluster that is associated
533 * with each character. It is possible for a cluster to be formed of several chars. */
534 for (int c = 0; c < (int)this->char_to_glyph.size(); c++) {
535 /* If multiple chars map to one glyph, only refer back to the first character. */
536 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
537 }
538
539 /* We only marked the first glyph of each cluster in the loop above. Fill the gaps. */
540 int last_char = this->glyph_to_char[0];
541 for (int g = 0; g < this->GetGlyphCount(); g++) {
542 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
543 this->glyph_to_char[g] = last_char;
544 }
545 }
546
547 return this->glyph_to_char;
548}
549
550
551/* virtual */ void UniscribeStringIterator::SetString(std::string_view s)
552{
553 this->utf16_to_utf8.clear();
554 this->str_info.clear();
555 this->cur_pos = 0;
556
557 /* Uniscribe operates on UTF-16, thus we have to convert the input string.
558 * To be able to return proper offsets, we have to create a mapping at the same time. */
559 std::vector<wchar_t> utf16_str;
560 Utf8View view(s);
561 for (auto it = view.begin(), end = view.end(); it != end; ++it) {
562 size_t idx = it.GetByteOffset();
563 char32_t c = *it;
564 if (c < 0x10000) {
565 utf16_str.push_back((wchar_t)c);
566 } else {
567 /* Make a surrogate pair. */
568 utf16_str.push_back((wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
569 utf16_str.push_back((wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
570 this->utf16_to_utf8.push_back(idx);
571 }
572 this->utf16_to_utf8.push_back(idx);
573 }
574 this->utf16_to_utf8.push_back(s.size());
575
576 /* Query Uniscribe for word and cluster break information. */
577 this->str_info.resize(utf16_to_utf8.size());
578
579 if (!utf16_str.empty()) {
580 /* Itemize string into language runs. */
581 std::vector<SCRIPT_ITEM> runs = UniscribeItemizeString(&utf16_str[0], (int32_t)utf16_str.size());
582
583 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
584 /* Get information on valid word and character break.s */
585 int len = (run + 1)->iCharPos - run->iCharPos;
586 std::vector<SCRIPT_LOGATTR> attr(len);
587 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
588
589 /* Extract the information we're interested in. */
590 for (size_t c = 0; c < attr.size(); c++) {
591 /* First character of a run is always a valid word break. */
592 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
593 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
594 }
595 }
596 }
597
598 /* End-of-string is always a valid stopping point. */
599 this->str_info.back().char_stop = true;
600 this->str_info.back().word_stop = true;
601}
602
603/* virtual */ size_t UniscribeStringIterator::SetCurPosition(size_t pos)
604{
605 /* Convert incoming position to an UTF-16 string index. */
606 size_t utf16_pos = 0;
607 for (size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
608 if (this->utf16_to_utf8[i] == pos) {
609 utf16_pos = i;
610 break;
611 }
612 }
613
614 /* Sanitize in case we get a position inside a grapheme cluster. */
615 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
616 this->cur_pos = utf16_pos;
617
618 return this->utf16_to_utf8[this->cur_pos];
619}
620
621/* virtual */ size_t UniscribeStringIterator::Next(IterType what)
622{
623 assert(this->cur_pos <= this->utf16_to_utf8.size());
625
626 if (this->cur_pos == this->utf16_to_utf8.size()) return END;
627
628 do {
629 this->cur_pos++;
630 } 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));
631
632 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
633}
634
636{
637 assert(this->cur_pos <= this->utf16_to_utf8.size());
639
640 if (this->cur_pos == 0) return END;
641
642 do {
643 this->cur_pos--;
644 } while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
645
646 return this->utf16_to_utf8[this->cur_pos];
647}
Font cache for basic fonts.
Definition fontcache.h:32
virtual std::string GetFontName()=0
Get the name of this font.
int GetHeight() const
Get the height of the font.
Definition fontcache.h:106
virtual const void * GetOSHandle()
Get the native OS font handle, if there is one.
Definition fontcache.h:160
virtual bool IsBuiltInFont()=0
Is this a built-in sprite font?
virtual uint GetGlyphWidth(GlyphID key)=0
Get the width of the glyph with the given key.
virtual GlyphID MapCharToGlyph(char32_t key)=0
Map a character into a glyph.
FontSize GetSize() const
Get the FontSize of the font.
Definition fontcache.h:96
Container with information about a font.
Definition gfx_layout.h:98
A single line worth of VisualRuns.
Definition gfx_layout.h:142
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:130
Interface to glue fallback and normal layouter into one.
Definition gfx_layout.h:112
IterType
Type of the iterator.
Definition string_base.h:17
@ ITER_WORD
Iterate over words.
Definition string_base.h:19
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
Definition string_base.h:18
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.
int GetLeading() const override
Get the height of the line.
Visual run contains data about the bit of text with the same font.
Wrapper for doing layouts with Uniscribe.
int cur_range_offset
Offset from the start of the current run from where to output.
std::vector< UniscribeRun > ranges
All runs of the text.
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.
void SetString(std::string_view s) override
Set a new iteration 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:30
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
uint32_t GlyphID
Glyphs are characters from a font.
Definition fontcache.h:18
std::vector< std::pair< int, Font > > FontMap
Mapping from index to font.
Definition gfx_layout.h:107
char32_t SwapRtlPairedCharacters(char32_t c)
Swap paired brackets for fallback RTL layouting.
FontSize
Available font sizes.
Definition gfx_type.h:248
const LanguageMetadata * _current_language
The currently loaded language.
Definition strings.cpp:54
char32_t Utf16DecodeChar(const uint16_t *c)
Decode an UTF-16 character.
Definition string_func.h:96
bool Utf16IsLeadSurrogate(uint c)
Is the given character a lead surrogate code point?
Definition string_func.h:65
static std::map< FontIndex, SCRIPT_CACHE > _script_cache
Uniscribe cache for internal font information, cleared when OTTD changes fonts.
static std::vector< SCRIPT_ITEM > UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
Break a string into language formatting ranges.
static HFONT HFontFromFont(const Font &font)
Load the matching native Windows font.
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
@ TD_RTL
Text is written right-to-left by default.
uint16_t winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition language.h:51
Contains all information about a run of characters.
void FallbackShape(const UniscribeParagraphLayoutFactory::CharType *buff)
Manually shape a run for built-in non-truetype fonts.
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:392
declarations of functions for MS windows systems