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

OpenTTD Source 13.2.1

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