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