OpenTTD Source: src/os/windows/string_uniscribe.cpp Source File

OpenTTD Source 15.3

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 "../../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
32 static SCRIPT_CACHE _script_cache[FS_END];
33
38 struct UniscribeRun {
39 int pos;
40 int len;
41 Font *font;
42
43 std::vector<GlyphID> ft_glyphs;
44
45 SCRIPT_ANALYSIS sa;
46 std::vector<WORD> char_to_glyph;
47
48 std::vector<SCRIPT_VISATTR> vis_attribs;
49 std::vector<WORD> glyphs;
50 std::vector<int> advances;
51 std::vector<GOFFSET> offsets;
52 int total_advance;
53
54 UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
55};
56
58static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length);
60static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range);
61
65 class UniscribeParagraphLayout : public ParagraphLayouter {
66private:
67 const UniscribeParagraphLayoutFactory::CharType *text_buffer;
68
69 std::vector<UniscribeRun> ranges;
70 std::vector<UniscribeRun>::iterator cur_range;
71 int cur_range_offset = 0;
72
73public:
75 class UniscribeVisualRun : public ParagraphLayouter::VisualRun {
76 private:
77 std::vector<GlyphID> glyphs;
78 std::vector<Position> positions;
79 std::vector<WORD> char_to_glyph;
80
81 int start_pos;
82 int total_advance;
83 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
142 static 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
156 static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
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
248 static 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
448 int UniscribeParagraphLayout::UniscribeLine::GetLeading() const
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
462 int UniscribeParagraphLayout::UniscribeLine::GetWidth() const
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());
590 assert(what == StringIterator::ITER_CHARACTER || what == StringIterator::ITER_WORD);
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
601/*virtual */ size_t UniscribeStringIterator::Prev(IterType what)
602{
603 assert(this->cur_pos <= this->utf16_to_utf8.size());
604 assert(what == StringIterator::ITER_CHARACTER || what == StringIterator::ITER_WORD);
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}
FontCache::GetFontName
virtual std::string GetFontName()=0
Get the name of this font.
FontCache::GetHeight
int GetHeight() const
Get the height of the font.
Definition fontcache.h:59
FontCache::GetOSHandle
virtual const void * GetOSHandle()
Get the native OS font handle, if there is one.
Definition fontcache.h:114
FontCache::MapCharToGlyph
virtual GlyphID MapCharToGlyph(char32_t key, bool fallback=true)=0
Map a character into a glyph.
FontCache::GetGlyphWidth
virtual uint GetGlyphWidth(GlyphID key)=0
Get the width of the glyph with the given key.
FontCache::GetSize
FontSize GetSize() const
Get the FontSize of the font.
Definition fontcache.h:53
Font
Container with information about a font.
Definition gfx_layout.h:97
Font::fc
FontCache * fc
The font we are using.
Definition gfx_layout.h:99
ParagraphLayouter::Line
A single line worth of VisualRuns.
Definition gfx_layout.h:141
ParagraphLayouter::VisualRun
Visual run contains data about the bit of text with the same font.
Definition gfx_layout.h:129
ParagraphLayouter
Interface to glue fallback and normal layouter into one.
Definition gfx_layout.h:111
StringIterator::IterType
IterType
Type of the iterator.
Definition string_base.h:17
StringIterator::ITER_WORD
@ ITER_WORD
Iterate over words.
Definition string_base.h:19
StringIterator::ITER_CHARACTER
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
Definition string_base.h:18
UniscribeParagraphLayoutFactory::GetParagraphLayout
static std::unique_ptr< ParagraphLayouter > GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
Get the actual ParagraphLayout for the given buffer.
Definition string_uniscribe.cpp:277
UniscribeParagraphLayoutFactory::CharType
wchar_t CharType
Helper for GetLayouter, to get the right type.
Definition string_uniscribe.h:26
UniscribeParagraphLayout::UniscribeLine
A single line worth of VisualRuns.
Definition string_uniscribe.cpp:103
UniscribeParagraphLayout::UniscribeLine::GetWidth
int GetWidth() const override
Get the width of this line.
Definition string_uniscribe.cpp:462
UniscribeParagraphLayout::UniscribeLine::GetLeading
int GetLeading() const override
Get the height of the line.
Definition string_uniscribe.cpp:448
UniscribeParagraphLayout::UniscribeVisualRun
Visual run contains data about the bit of text with the same font.
Definition string_uniscribe.cpp:75
UniscribeParagraphLayout
Wrapper for doing layouts with Uniscribe.
Definition string_uniscribe.cpp:65
UniscribeParagraphLayout::cur_range_offset
int cur_range_offset
Offset from the start of the current run from where to output.
Definition string_uniscribe.cpp:71
UniscribeParagraphLayout::ranges
std::vector< UniscribeRun > ranges
All runs of the text.
Definition string_uniscribe.cpp:69
UniscribeParagraphLayout::cur_range
std::vector< UniscribeRun >::iterator cur_range
The next run to be output.
Definition string_uniscribe.cpp:70
UniscribeStringIterator::Prev
size_t Prev(IterType what) override
Move the cursor back by one iteration unit.
Definition string_uniscribe.cpp:601
UniscribeStringIterator::SetString
void SetString(std::string_view s) override
Set a new iteration string.
Definition string_uniscribe.cpp:517
UniscribeStringIterator::Next
size_t Next(IterType what) override
Advance the cursor by one iteration unit.
Definition string_uniscribe.cpp:587
UniscribeStringIterator::SetCurPosition
size_t SetCurPosition(size_t pos) override
Change the current string cursor.
Definition string_uniscribe.cpp:569
Utf8View
Constant span of UTF-8 encoded data.
Definition utf8.hpp:30
FontMap
std::vector< std::pair< int, Font * > > FontMap
Mapping from index to font.
Definition gfx_layout.h:106
FontSize
FontSize
Available font sizes.
Definition gfx_type.h:248
_current_language
const LanguageMetadata * _current_language
The currently loaded language.
Definition strings.cpp:54
HFontFromFont
static HFONT HFontFromFont(Font *font)
Load the matching native Windows font.
Definition string_uniscribe.cpp:142
UniscribeItemizeString
static std::vector< SCRIPT_ITEM > UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
Break a string into language formatting ranges.
Definition string_uniscribe.cpp:248
_script_cache
static SCRIPT_CACHE _script_cache[FS_END]
Uniscribe cache for internal font information, cleared when OTTD changes fonts.
Definition string_uniscribe.cpp:32
UniscribeShapeRun
static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
Generate and place glyphs for a run of characters.
Definition string_uniscribe.cpp:156
string_uniscribe.h
Functions related to laying out text on Win32.
_current_text_dir
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition strings.cpp:56
TD_RTL
@ TD_RTL
Text is written right-to-left by default.
Definition strings_type.h:27
LanguagePackHeader::winlangid
uint16_t winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition language.h:51
UniscribeRun
Contains all information about a run of characters.
Definition string_uniscribe.cpp:38
convert_to_fs
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
win32.h
declarations of functions for MS windows systems
ScaleSpriteTrad
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.
Definition zoom_func.h:107