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