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