OpenTTD Source 20250524-master-gc366e6a48e
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 "../../core/utf8.hpp"
17#include "../../table/control_codes.h"
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
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
66private:
68
69 std::vector<UniscribeRun> ranges;
70 std::vector<UniscribeRun>::iterator cur_range;
72
73public:
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 int num_glyphs;
84 Font *font;
85
86 mutable std::vector<int> glyph_to_char;
87
88 public:
89 UniscribeVisualRun(const UniscribeRun &range, int x);
90 UniscribeVisualRun(UniscribeVisualRun &&other) noexcept;
91
92 std::span<const GlyphID> GetGlyphs() const override { return this->glyphs; }
93 std::span<const Position> GetPositions() const override { return this->positions; }
94 std::span<const int> GetGlyphToCharMap() const override;
95
96 const Font *GetFont() const override { return this->font; }
97 int GetLeading() const override { return this->font->fc->GetHeight(); }
98 int GetGlyphCount() const override { return this->num_glyphs; }
99 int GetAdvance() const { return this->total_advance; }
100 };
101
103 class UniscribeLine : public std::vector<UniscribeVisualRun>, public ParagraphLayouter::Line {
104 public:
105 int GetLeading() const override;
106 int GetWidth() const override;
107 int CountRuns() const override { return (uint)this->size(); }
108 const VisualRun &GetVisualRun(int run) const override { return this->at(run); }
109
110 int GetInternalCharLength(char32_t c) const override
111 {
112 /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */
113 return c >= 0x010000U ? 2 : 1;
114 }
115 };
116
117 UniscribeParagraphLayout(std::vector<UniscribeRun> &&ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(std::move(ranges))
118 {
119 this->Reflow();
120 }
121
122 ~UniscribeParagraphLayout() override {}
123
124 void Reflow() override
125 {
126 this->cur_range = this->ranges.begin();
127 this->cur_range_offset = 0;
128 }
129
130 std::unique_ptr<const Line> NextLine(int max_width) override;
131};
132
133void UniscribeResetScriptCache(FontSize size)
134{
135 if (_script_cache[size] != nullptr) {
136 ScriptFreeCache(&_script_cache[size]);
137 _script_cache[size] = nullptr;
138 }
139}
140
142static HFONT HFontFromFont(Font *font)
143{
144 if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect(reinterpret_cast<PLOGFONT>(const_cast<void *>(font->fc->GetOSHandle())));
145
146 LOGFONT 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 control.uDefaultLanguage = _current_language->winlangid;
253
254 SCRIPT_STATE state{};
255 state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0;
256
257 std::vector<SCRIPT_ITEM> items(16);
258 while (true) {
259 /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */
260 int generated = 0;
261 HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated);
262
263 if (SUCCEEDED(hr)) {
264 /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */
265 items.resize(generated + 1);
266 break;
267 }
268 /* Some kind of error except item buffer too small. */
269 if (hr != E_OUTOFMEMORY) return std::vector<SCRIPT_ITEM>();
270
271 items.resize(items.size() * 2);
272 }
273
274 return items;
275}
276
277/* static */ std::unique_ptr<ParagraphLayouter> UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
278{
279 int32_t length = buff_end - buff;
280 /* Can't layout an empty string. */
281 if (length == 0) return nullptr;
282
283 /* Can't layout our in-built sprite fonts. */
284 for (auto const &[position, font] : font_mapping) {
285 if (font->fc->IsBuiltInFont()) return nullptr;
286 }
287
288 /* Itemize text. */
289 std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
290 if (items.empty()) return nullptr;
291
292 /* Build ranges from the items and the font map. A range is a run of text
293 * that is part of a single item and formatted using a single font style. */
294 std::vector<UniscribeRun> ranges;
295
296 int cur_pos = 0;
297 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
298 for (auto const &[position, font] : font_mapping) {
299 while (cur_pos < position && cur_item != items.end() - 1) {
300 /* Add a range that spans the intersection of the remaining item and font run. */
301 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
302 assert(stop_pos - cur_pos > 0);
303 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
304
305 /* Shape the range. */
306 if (!UniscribeShapeRun(buff, ranges.back())) {
307 return nullptr;
308 }
309
310 /* If we are at the end of the current item, advance to the next item. */
311 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
312 cur_pos = stop_pos;
313 }
314 }
315
316 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
317}
318
319/* virtual */ std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(int max_width)
320{
321 std::vector<UniscribeRun>::iterator start_run = this->cur_range;
322 std::vector<UniscribeRun>::iterator last_run = this->cur_range;
323
324 if (start_run == this->ranges.end()) return nullptr;
325
326 /* Add remaining width of the first run if it is a broken run. */
327 int cur_width = 0;
328 if (this->cur_range_offset != 0) {
329 std::vector<int> dx(start_run->len);
330 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]);
331
332 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
333 cur_width += *c;
334 }
335 ++last_run;
336 }
337
338 /* Gather runs until the line is full. */
339 while (last_run != this->ranges.end() && cur_width <= max_width) {
340 cur_width += last_run->total_advance;
341 ++last_run;
342 }
343
344 /* If the text does not fit into the available width, find a suitable breaking point. */
345 int remaining_offset = (last_run - 1)->len + 1;
346 int whitespace_count = 0;
347 if (cur_width > max_width) {
348 std::vector<SCRIPT_LOGATTR> log_attribs;
349
350 /* Get word break information. */
351 int width_avail = max_width;
352 int num_chars = this->cur_range_offset;
353 int start_offs = this->cur_range_offset;
354 int last_cluster = this->cur_range_offset + 1;
355 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
356 log_attribs.resize(r->pos - start_run->pos + r->len);
357 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;
358
359 std::vector<int> dx(r->len);
360 ScriptGetLogicalWidths(&r->sa, r->len, (int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
361
362 /* Count absolute max character count on the line. */
363 for (int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
364 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
365 width_avail -= dx[c];
366 }
367
368 start_offs = 0;
369 }
370
371 /* Walk backwards to find the last suitable breaking point. */
372 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
373
374 if (num_chars == this->cur_range_offset) {
375 /* Didn't find any suitable word break point, just break on the last cluster boundary. */
376 num_chars = last_cluster;
377 }
378
379 /* Eat any whitespace characters before the breaking point. */
380 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
381 /* Count whitespace after the breaking point. */
382 while (num_chars + whitespace_count < (int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
383
384 /* Get last run that corresponds to the number of characters to show. */
385 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
386 num_chars -= run->len;
387
388 if (num_chars <= 0) {
389 remaining_offset = num_chars + run->len + 1;
390 last_run = run + 1;
391 assert(remaining_offset - 1 > 0);
392 break;
393 }
394 }
395 }
396
397 /* Build display order from the runs. */
398 std::vector<BYTE> bidi_level;
399 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
400 bidi_level.push_back(r->sa.s.uBidiLevel);
401 }
402 std::vector<INT> vis_to_log(bidi_level.size());
403 if (FAILED(ScriptLayout((int)bidi_level.size(), &bidi_level[0], &vis_to_log[0], nullptr))) return nullptr;
404
405 /* Create line. */
406 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
407
408 int cur_pos = 0;
409 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
410 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
411 UniscribeRun run = *i_run;
412
413 /* Partial run after line break (either start or end)? Reshape run to get the first/last glyphs right. */
414 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
415 run.len = remaining_offset - 1;
416
417 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
418 }
419 if (i_run == start_run && this->cur_range_offset > 0) {
420 assert(run.len - this->cur_range_offset > 0);
421 run.pos += this->cur_range_offset;
422 run.len -= this->cur_range_offset;
423
424 if (!UniscribeShapeRun(this->text_buffer, run)) return nullptr;
425 }
426
427 line->emplace_back(run, cur_pos);
428 cur_pos += run.total_advance;
429 }
430
431 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
432 /* We didn't use up all of the last run, store remainder for the next line. */
433 this->cur_range_offset = remaining_offset + whitespace_count - 1;
434 this->cur_range = last_run - 1;
435 assert(this->cur_range->len > this->cur_range_offset);
436 } else {
437 this->cur_range_offset = 0;
438 this->cur_range = last_run;
439 }
440
441 return line;
442}
443
449{
450 int leading = 0;
451 for (const auto &run : *this) {
452 leading = std::max(leading, run.GetLeading());
453 }
454
455 return leading;
456}
457
463{
464 int length = 0;
465 for (const auto &run : *this) {
466 length += run.GetAdvance();
467 }
468
469 return length;
470}
471
472UniscribeParagraphLayout::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)
473{
474 this->num_glyphs = (int)glyphs.size();
475 this->positions.reserve(this->num_glyphs);
476
477 int advance = x;
478 for (int i = 0; i < this->num_glyphs; i++) {
479 int x_advance = range.advances[i];
480 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
481
482 advance += x_advance;
483 }
484}
485
486UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
487 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
488 start_pos(other.start_pos), total_advance(other.total_advance), num_glyphs(other.num_glyphs), font(other.font),
489 glyph_to_char(std::move(other.glyph_to_char))
490{
491}
492
493std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap() const
494{
495 if (this->glyph_to_char.empty()) {
496 this->glyph_to_char.resize(this->GetGlyphCount());
497
498 /* The char to glyph array contains the first glyph index of the cluster that is associated
499 * with each character. It is possible for a cluster to be formed of several chars. */
500 for (int c = 0; c < (int)this->char_to_glyph.size(); c++) {
501 /* If multiple chars map to one glyph, only refer back to the first character. */
502 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
503 }
504
505 /* We only marked the first glyph of each cluster in the loop above. Fill the gaps. */
506 int last_char = this->glyph_to_char[0];
507 for (int g = 0; g < this->GetGlyphCount(); g++) {
508 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
509 this->glyph_to_char[g] = last_char;
510 }
511 }
512
513 return this->glyph_to_char;
514}
515
516
517/* virtual */ void UniscribeStringIterator::SetString(std::string_view s)
518{
519 this->utf16_to_utf8.clear();
520 this->str_info.clear();
521 this->cur_pos = 0;
522
523 /* Uniscribe operates on UTF-16, thus we have to convert the input string.
524 * To be able to return proper offsets, we have to create a mapping at the same time. */
525 std::vector<wchar_t> utf16_str;
526 Utf8View view(s);
527 for (auto it = view.begin(), end = view.end(); it != end; ++it) {
528 size_t idx = it.GetByteOffset();
529 char32_t c = *it;
530 if (c < 0x10000) {
531 utf16_str.push_back((wchar_t)c);
532 } else {
533 /* Make a surrogate pair. */
534 utf16_str.push_back((wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
535 utf16_str.push_back((wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
536 this->utf16_to_utf8.push_back(idx);
537 }
538 this->utf16_to_utf8.push_back(idx);
539 }
540 this->utf16_to_utf8.push_back(s.size());
541
542 /* Query Uniscribe for word and cluster break information. */
543 this->str_info.resize(utf16_to_utf8.size());
544
545 if (!utf16_str.empty()) {
546 /* Itemize string into language runs. */
547 std::vector<SCRIPT_ITEM> runs = UniscribeItemizeString(&utf16_str[0], (int32_t)utf16_str.size());
548
549 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
550 /* Get information on valid word and character break.s */
551 int len = (run + 1)->iCharPos - run->iCharPos;
552 std::vector<SCRIPT_LOGATTR> attr(len);
553 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
554
555 /* Extract the information we're interested in. */
556 for (size_t c = 0; c < attr.size(); c++) {
557 /* First character of a run is always a valid word break. */
558 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
559 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
560 }
561 }
562 }
563
564 /* End-of-string is always a valid stopping point. */
565 this->str_info.back().char_stop = true;
566 this->str_info.back().word_stop = true;
567}
568
569/* virtual */ size_t UniscribeStringIterator::SetCurPosition(size_t pos)
570{
571 /* Convert incoming position to an UTF-16 string index. */
572 size_t utf16_pos = 0;
573 for (size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
574 if (this->utf16_to_utf8[i] == pos) {
575 utf16_pos = i;
576 break;
577 }
578 }
579
580 /* Sanitize in case we get a position inside a grapheme cluster. */
581 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
582 this->cur_pos = utf16_pos;
583
584 return this->utf16_to_utf8[this->cur_pos];
585}
586
587/* virtual */ size_t UniscribeStringIterator::Next(IterType what)
588{
589 assert(this->cur_pos <= this->utf16_to_utf8.size());
591
592 if (this->cur_pos == this->utf16_to_utf8.size()) return END;
593
594 do {
595 this->cur_pos++;
596 } 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));
597
598 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
599}
600
602{
603 assert(this->cur_pos <= this->utf16_to_utf8.size());
605
606 if (this->cur_pos == 0) return END;
607
608 do {
609 this->cur_pos--;
610 } while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
611
612 return this->utf16_to_utf8[this->cur_pos];
613}
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:97
FontCache * fc
The font we are using.
Definition gfx_layout.h:99
A single line worth of VisualRuns.
Definition gfx_layout.h:141
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:129
Interface to glue fallback and normal layouter into one.
Definition gfx_layout.h:111
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
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:250
const LanguageMetadata * _current_language
The currently loaded language.
Definition strings.cpp:55
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:57
@ 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(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