OpenTTD
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 #if defined(WITH_UNISCRIBE)
11 
12 #include "../../stdafx.h"
13 #include "../../debug.h"
14 #include "string_uniscribe.h"
15 #include "../../language.h"
16 #include "../../strings_func.h"
17 #include "../../string_func.h"
18 #include "../../table/control_codes.h"
19 #include "win32.h"
20 #include <vector>
21 
22 #include <windows.h>
23 #include <usp10.h>
24 
25 #include "../../safeguards.h"
26 
27 #ifdef _MSC_VER
28 # pragma comment(lib, "usp10")
29 #endif
30 
31 
33 static SCRIPT_CACHE _script_cache[FS_END];
34 
39 struct UniscribeRun {
40  int pos;
41  int len;
42  Font *font;
43 
44  std::vector<GlyphID> ft_glyphs;
45 
46  SCRIPT_ANALYSIS sa;
47  std::vector<WORD> char_to_glyph;
48 
49  std::vector<SCRIPT_VISATTR> vis_attribs;
50  std::vector<WORD> glyphs;
51  std::vector<int> advances;
52  std::vector<GOFFSET> offsets;
53  int total_advance;
54 
55  UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
56 };
57 
59 static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length);
61 static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range);
62 
66 class UniscribeParagraphLayout : public ParagraphLayouter {
67 private:
68  const UniscribeParagraphLayoutFactory::CharType *text_buffer;
69 
70  std::vector<UniscribeRun> ranges;
71  std::vector<UniscribeRun>::iterator cur_range;
72  int cur_range_offset = 0;
73 
74 public:
76  class UniscribeVisualRun : public ParagraphLayouter::VisualRun {
77  private:
78  std::vector<GlyphID> glyphs;
79  std::vector<float> positions;
80  std::vector<WORD> char_to_glyph;
81 
82  int start_pos;
83  int total_advance;
84  int num_glyphs;
85  Font *font;
86 
87  mutable int *glyph_to_char = nullptr;
88 
89  public:
90  UniscribeVisualRun(const UniscribeRun &range, int x);
91  UniscribeVisualRun(UniscribeVisualRun &&other) noexcept;
92  ~UniscribeVisualRun() override
93  {
94  free(this->glyph_to_char);
95  }
96 
97  const GlyphID *GetGlyphs() const override { return &this->glyphs[0]; }
98  const float *GetPositions() const override { return &this->positions[0]; }
99  const int *GetGlyphToCharMap() const override;
100 
101  const Font *GetFont() const override { return this->font; }
102  int GetLeading() const override { return this->font->fc->GetHeight(); }
103  int GetGlyphCount() const override { return this->num_glyphs; }
104  int GetAdvance() const { return this->total_advance; }
105  };
106 
108  class UniscribeLine : public std::vector<UniscribeVisualRun>, public ParagraphLayouter::Line {
109  public:
110  int GetLeading() const override;
111  int GetWidth() const override;
112  int CountRuns() const override { return (uint)this->size(); }
113  const VisualRun &GetVisualRun(int run) const override { return this->at(run); }
114 
115  int GetInternalCharLength(WChar c) const override
116  {
117  /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */
118  return c >= 0x010000U ? 2 : 1;
119  }
120  };
121 
122  UniscribeParagraphLayout(std::vector<UniscribeRun> &ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(ranges)
123  {
124  this->Reflow();
125  }
126 
127  ~UniscribeParagraphLayout() override {}
128 
129  void Reflow() override
130  {
131  this->cur_range = this->ranges.begin();
132  this->cur_range_offset = 0;
133  }
134 
135  std::unique_ptr<const Line> NextLine(int max_width) override;
136 };
137 
138 void UniscribeResetScriptCache(FontSize size)
139 {
140  if (_script_cache[size] != nullptr) {
141  ScriptFreeCache(&_script_cache[size]);
142  _script_cache[size] = nullptr;
143  }
144 }
145 
147 static HFONT HFontFromFont(Font *font)
148 {
149  if (font->fc->GetOSHandle() != nullptr) return CreateFontIndirect((PLOGFONT)font->fc->GetOSHandle());
150 
151  LOGFONT logfont;
152  ZeroMemory(&logfont, sizeof(LOGFONT));
153  logfont.lfHeight = font->fc->GetHeight();
154  logfont.lfWeight = FW_NORMAL;
155  logfont.lfCharSet = DEFAULT_CHARSET;
156  convert_to_fs(font->fc->GetFontName(), logfont.lfFaceName, lengthof(logfont.lfFaceName));
157 
158  return CreateFontIndirect(&logfont);
159 }
160 
162 static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
163 {
164  /* Initial size guess for the number of glyphs recommended by Uniscribe. */
165  range.glyphs.resize(range.len * 3 / 2 + 16);
166  range.vis_attribs.resize(range.glyphs.size());
167 
168  /* The char-to-glyph array is the same size as the input. */
169  range.char_to_glyph.resize(range.len);
170 
171  HDC temp_dc = nullptr;
172  HFONT old_font = nullptr;
173  HFONT cur_font = nullptr;
174 
175  while (true) {
176  /* Shape the text run by determining the glyphs needed for display. */
177  int glyphs_used = 0;
178  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);
179 
180  if (SUCCEEDED(hr)) {
181  range.glyphs.resize(glyphs_used);
182  range.vis_attribs.resize(glyphs_used);
183 
184  /* Calculate the glyph positions. */
185  ABC abc;
186  range.advances.resize(range.glyphs.size());
187  range.offsets.resize(range.glyphs.size());
188  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);
189  if (SUCCEEDED(hr)) {
190  /* We map our special sprite chars to values that don't fit into a WORD. Copy the glyphs
191  * into a new vector and query the real glyph to use for these special chars. */
192  range.ft_glyphs.resize(range.glyphs.size());
193  for (size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
194  range.ft_glyphs[g_id] = range.glyphs[g_id];
195  }
196  for (int i = 0; i < range.len; i++) {
197  if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
198  auto pos = range.char_to_glyph[i];
199  range.ft_glyphs[pos] = range.font->fc->MapCharToGlyph(buff[range.pos + i]);
200  range.offsets[pos].dv = range.font->fc->GetAscender() - range.font->fc->GetGlyph(range.ft_glyphs[pos])->height - 1; // Align sprite glyphs to font baseline.
201  range.advances[pos] = range.font->fc->GetGlyphWidth(range.ft_glyphs[pos]);
202  }
203  }
204 
205  range.total_advance = 0;
206  for (size_t i = 0; i < range.advances.size(); i++) {
207 #ifdef WITH_FREETYPE
208  /* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
209  if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.ft_glyphs[i]);
210 #endif
211  range.total_advance += range.advances[i];
212  }
213  break;
214  }
215  }
216 
217  if (hr == E_OUTOFMEMORY) {
218  /* The glyph buffer needs to be larger. Just double it every time. */
219  range.glyphs.resize(range.glyphs.size() * 2);
220  range.vis_attribs.resize(range.vis_attribs.size() * 2);
221  } else if (hr == E_PENDING) {
222  /* Glyph data is not in cache, load native font. */
223  cur_font = HFontFromFont(range.font);
224  if (cur_font == nullptr) return false; // Sorry, no dice.
225 
226  temp_dc = CreateCompatibleDC(nullptr);
227  SetMapMode(temp_dc, MM_TEXT);
228  old_font = (HFONT)SelectObject(temp_dc, cur_font);
229  } else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
230  /* Try again with the generic shaping engine. */
231  range.sa.eScript = SCRIPT_UNDEFINED;
232  } else {
233  /* Some unknown other error. */
234  if (temp_dc != nullptr) {
235  SelectObject(temp_dc, old_font);
236  DeleteObject(cur_font);
237  ReleaseDC(nullptr, temp_dc);
238  }
239  return false;
240  }
241  }
242 
243  if (temp_dc != nullptr) {
244  SelectObject(temp_dc, old_font);
245  DeleteObject(cur_font);
246  ReleaseDC(nullptr, temp_dc);
247  }
248 
249  return true;
250 }
251 
252 static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length)
253 {
254  /* Itemize text. */
255  SCRIPT_CONTROL control;
256  ZeroMemory(&control, sizeof(SCRIPT_CONTROL));
257  control.uDefaultLanguage = _current_language->winlangid;
258 
259  SCRIPT_STATE state;
260  ZeroMemory(&state, sizeof(SCRIPT_STATE));
261  state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0;
262 
263  std::vector<SCRIPT_ITEM> items(16);
264  while (true) {
265  /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */
266  int generated = 0;
267  HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated);
268 
269  if (SUCCEEDED(hr)) {
270  /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */
271  items.resize(generated + 1);
272  break;
273  }
274  /* Some kind of error except item buffer too small. */
275  if (hr != E_OUTOFMEMORY) return std::vector<SCRIPT_ITEM>();
276 
277  items.resize(items.size() * 2);
278  }
279 
280  return items;
281 }
282 
283 /* static */ ParagraphLayouter *UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping)
284 {
285  int32 length = buff_end - buff;
286  /* Can't layout an empty string. */
287  if (length == 0) return nullptr;
288 
289  /* Can't layout our in-built sprite fonts. */
290  for (auto const &pair : fontMapping) {
291  if (pair.second->fc->IsBuiltInFont()) return nullptr;
292  }
293 
294  /* Itemize text. */
295  std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
296  if (items.size() == 0) return nullptr;
297 
298  /* Build ranges from the items and the font map. A range is a run of text
299  * that is part of a single item and formatted using a single font style. */
300  std::vector<UniscribeRun> ranges;
301 
302  int cur_pos = 0;
303  std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
304  for (auto const &i : fontMapping) {
305  while (cur_pos < i.first && cur_item != items.end() - 1) {
306  /* Add a range that spans the intersection of the remaining item and font run. */
307  int stop_pos = min(i.first, (cur_item + 1)->iCharPos);
308  assert(stop_pos - cur_pos > 0);
309  ranges.push_back(UniscribeRun(cur_pos, stop_pos - cur_pos, i.second, cur_item->a));
310 
311  /* Shape the range. */
312  if (!UniscribeShapeRun(buff, ranges.back())) {
313  return nullptr;
314  }
315 
316  /* If we are at the end of the current item, advance to the next item. */
317  if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
318  cur_pos = stop_pos;
319  }
320  }
321 
322  return new UniscribeParagraphLayout(ranges, buff);
323 }
324 
325 /* virtual */ std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(int max_width)
326 {
327  std::vector<UniscribeRun>::iterator start_run = this->cur_range;
328  std::vector<UniscribeRun>::iterator last_run = this->cur_range;
329 
330  if (start_run == this->ranges.end()) return nullptr;
331 
332  /* Add remaining width of the first run if it is a broken run. */
333  int cur_width = 0;
334  if (this->cur_range_offset != 0) {
335  std::vector<int> dx(start_run->len);
336  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]);
337 
338  for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
339  cur_width += *c;
340  }
341  ++last_run;
342  }
343 
344  /* Gather runs until the line is full. */
345  while (last_run != this->ranges.end() && cur_width < max_width) {
346  cur_width += last_run->total_advance;
347  ++last_run;
348  }
349 
350  /* If the text does not fit into the available width, find a suitable breaking point. */
351  int remaing_offset = (last_run - 1)->len;
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  /* Include whitespace characters after the breaking point. */
385  while (num_chars < (int)log_attribs.size() && log_attribs[num_chars].fWhiteSpace) {
386  num_chars++;
387  }
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  remaing_offset = num_chars + run->len + 1;
395  last_run = run + 1;
396  assert(remaing_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 && remaing_offset < (last_run - 1)->len) {
420  run.len = remaing_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 (remaing_offset < (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 = remaing_offset - 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 = 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());
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());
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 }
623 
624 #endif /* defined(WITH_UNISCRIBE) */
Functions related to laying out text on Win32.
TCHAR * convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
Convert from OpenTTD&#39;s encoding to that of the environment in UNICODE.
Definition: win32.cpp:625
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:46
Implementation of simple mapping class.
Visual run contains data about the bit of text with the same font.
Definition: gfx_layout.h:120
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:24
uint16 winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition: language.h:51
Iterate over characters (or more exactly grapheme clusters).
Definition: string_base.h:18
A single line worth of VisualRuns.
Definition: gfx_layout.h:132
Interface to glue fallback and normal layouter into one.
Definition: gfx_layout.h:115
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:40
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:40
Iterate over words.
Definition: string_base.h:19
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:48
FontSize
Available font sizes.
Definition: gfx_type.h:201
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:129
Text is written right-to-left by default.
Definition: strings_type.h:24
uint32 GlyphID
Glyphs are characters from a font.
Definition: fontcache.h:17
uint32 WChar
Type for wide characters, i.e.
Definition: string_type.h:35
declarations of functions for MS windows systems