OpenTTD
string_uniscribe.cpp
Go to the documentation of this file.
1 /* $Id$ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
12 #if defined(WITH_UNISCRIBE)
13 
14 #include "../../stdafx.h"
15 #include "../../debug.h"
16 #include "string_uniscribe.h"
17 #include "../../language.h"
18 #include "../../strings_func.h"
19 #include "../../string_func.h"
20 #include "../../table/control_codes.h"
21 #include "win32.h"
22 #include <vector>
23 
24 #include <windows.h>
25 #include <usp10.h>
26 
27 #include "../../safeguards.h"
28 
29 #ifdef _MSC_VER
30 # pragma comment(lib, "usp10")
31 #endif
32 
33 
35 static SCRIPT_CACHE _script_cache[FS_END];
36 
41 struct UniscribeRun {
42  int pos;
43  int len;
44  Font *font;
45 
46  std::vector<GlyphID> ft_glyphs;
47 
48  SCRIPT_ANALYSIS sa;
49  std::vector<WORD> char_to_glyph;
50 
51  std::vector<SCRIPT_VISATTR> vis_attribs;
52  std::vector<WORD> glyphs;
53  std::vector<int> advances;
54  std::vector<GOFFSET> offsets;
55  int total_advance;
56 
57  UniscribeRun(int pos, int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
58 };
59 
61 static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length);
63 static bool UniscribeShapeRun(const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range);
64 
68 class UniscribeParagraphLayout : public ParagraphLayouter {
69 private:
70  const UniscribeParagraphLayoutFactory::CharType *text_buffer;
71 
72  std::vector<UniscribeRun> ranges;
73  std::vector<UniscribeRun>::iterator cur_range;
74  int cur_range_offset = 0;
75 
76 public:
78  class UniscribeVisualRun : public ParagraphLayouter::VisualRun {
79  private:
80  std::vector<GlyphID> glyphs;
81  std::vector<float> positions;
82  std::vector<WORD> char_to_glyph;
83 
84  int start_pos;
85  int total_advance;
86  int num_glyphs;
87  Font *font;
88 
89  mutable int *glyph_to_char = NULL;
90 
91  public:
92  UniscribeVisualRun(const UniscribeRun &range, int x);
93  virtual ~UniscribeVisualRun()
94  {
95  free(this->glyph_to_char);
96  }
97 
98  virtual const GlyphID *GetGlyphs() const { return &this->glyphs[0]; }
99  virtual const float *GetPositions() const { return &this->positions[0]; }
100  virtual const int *GetGlyphToCharMap() const;
101 
102  virtual const Font *GetFont() const { return this->font; }
103  virtual int GetLeading() const { return this->font->fc->GetHeight(); }
104  virtual int GetGlyphCount() const { return this->num_glyphs; }
105  int GetAdvance() const { return this->total_advance; }
106  };
107 
109  class UniscribeLine : public AutoDeleteSmallVector<UniscribeVisualRun *, 4>, public ParagraphLayouter::Line {
110  public:
111  virtual int GetLeading() const;
112  virtual int GetWidth() const;
113  virtual int CountRuns() const { return this->Length(); }
114  virtual const VisualRun *GetVisualRun(int run) const { return *this->Get(run); }
115 
116  int GetInternalCharLength(WChar c) const
117  {
118  /* Uniscribe uses UTF-16 internally which means we need to account for surrogate pairs. */
119  return c >= 0x010000U ? 2 : 1;
120  }
121  };
122 
123  UniscribeParagraphLayout(std::vector<UniscribeRun> &ranges, const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(ranges)
124  {
125  this->Reflow();
126  }
127 
128  virtual ~UniscribeParagraphLayout() {}
129 
130  virtual void Reflow()
131  {
132  this->cur_range = this->ranges.begin();
133  this->cur_range_offset = 0;
134  }
135 
136  virtual const Line *NextLine(int max_width);
137 };
138 
139 void UniscribeResetScriptCache(FontSize size)
140 {
141  if (_script_cache[size] != NULL) {
142  ScriptFreeCache(&_script_cache[size]);
143  _script_cache[size] = NULL;
144  }
145 }
146 
148 static HFONT HFontFromFont(Font *font)
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(), 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 = NULL;
171  HFONT old_font = NULL;
172  HFONT cur_font = NULL;
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  range.ft_glyphs[range.char_to_glyph[i]] = range.font->fc->MapCharToGlyph(buff[range.pos + i]);
198  range.offsets[range.char_to_glyph[i]].dv = range.font->fc->GetAscender() - range.font->fc->GetGlyph(range.ft_glyphs[range.char_to_glyph[i]])->height - 1; // Align sprite glyphs to font baseline.
199  }
200  }
201 
202  /* FreeType and GDI/Uniscribe seems to occasionally disagree over the width of a glyph. */
203  range.total_advance = 0;
204  for (size_t i = 0; i < range.advances.size(); i++) {
205  if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->fc->GetGlyphWidth(range.ft_glyphs[i]);
206  range.total_advance += range.advances[i];
207  }
208  break;
209  }
210  }
211 
212  if (hr == E_OUTOFMEMORY) {
213  /* The glyph buffer needs to be larger. Just double it every time. */
214  range.glyphs.resize(range.glyphs.size() * 2);
215  range.vis_attribs.resize(range.vis_attribs.size() * 2);
216  } else if (hr == E_PENDING) {
217  /* Glyph data is not in cache, load native font. */
218  cur_font = HFontFromFont(range.font);
219  if (cur_font == NULL) return false; // Sorry, no dice.
220 
221  temp_dc = CreateCompatibleDC(NULL);
222  SetMapMode(temp_dc, MM_TEXT);
223  old_font = (HFONT)SelectObject(temp_dc, cur_font);
224  } else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
225  /* Try again with the generic shaping engine. */
226  range.sa.eScript = SCRIPT_UNDEFINED;
227  } else {
228  /* Some unknown other error. */
229  if (temp_dc != NULL) {
230  SelectObject(temp_dc, old_font);
231  DeleteObject(cur_font);
232  ReleaseDC(NULL, temp_dc);
233  }
234  return false;
235  }
236  }
237 
238  if (temp_dc != NULL) {
239  SelectObject(temp_dc, old_font);
240  DeleteObject(cur_font);
241  ReleaseDC(NULL, temp_dc);
242  }
243 
244  return true;
245 }
246 
247 static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length)
248 {
249  /* Itemize text. */
250  SCRIPT_CONTROL control;
251  ZeroMemory(&control, sizeof(SCRIPT_CONTROL));
252  control.uDefaultLanguage = _current_language->winlangid;
253 
254  SCRIPT_STATE state;
255  ZeroMemory(&state, sizeof(SCRIPT_STATE));
256  state.uBidiLevel = _current_text_dir == TD_RTL ? 1 : 0;
257 
258  std::vector<SCRIPT_ITEM> items(16);
259  while (true) {
260  /* We subtract one from max_items to work around a buffer overflow on some older versions of Windows. */
261  int generated = 0;
262  HRESULT hr = ScriptItemize(buff, length, (int)items.size() - 1, &control, &state, &items[0], &generated);
263 
264  if (SUCCEEDED(hr)) {
265  /* Resize the item buffer. Note that Uniscribe will always add an additional end sentinel item. */
266  items.resize(generated + 1);
267  break;
268  }
269  /* Some kind of error except item buffer too small. */
270  if (hr != E_OUTOFMEMORY) return std::vector<SCRIPT_ITEM>();
271 
272  items.resize(items.size() * 2);
273  }
274 
275  return items;
276 }
277 
278 /* static */ ParagraphLayouter *UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &fontMapping)
279 {
280  int32 length = buff_end - buff;
281  /* Can't layout an empty string. */
282  if (length == 0) return NULL;
283 
284  /* Can't layout our in-built sprite fonts. */
285  for (FontMap::const_iterator i = fontMapping.Begin(); i != fontMapping.End(); i++) {
286  if (i->second->fc->IsBuiltInFont()) return NULL;
287  }
288 
289  /* Itemize text. */
290  std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
291  if (items.size() == 0) return NULL;
292 
293  /* Build ranges from the items and the font map. A range is a run of text
294  * that is part of a single item and formatted using a single font style. */
295  std::vector<UniscribeRun> ranges;
296 
297  int cur_pos = 0;
298  std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
299  for (FontMap::const_iterator i = fontMapping.Begin(); i != fontMapping.End(); i++) {
300  while (cur_pos < i->first && cur_item != items.end() - 1) {
301  /* Add a range that spans the intersection of the remaining item and font run. */
302  int stop_pos = min(i->first, (cur_item + 1)->iCharPos);
303  assert(stop_pos - cur_pos > 0);
304  ranges.push_back(UniscribeRun(cur_pos, stop_pos - cur_pos, i->second, cur_item->a));
305 
306  /* Shape the range. */
307  if (!UniscribeShapeRun(buff, ranges.back())) {
308  return NULL;
309  }
310 
311  /* If we are at the end of the current item, advance to the next item. */
312  if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
313  cur_pos = stop_pos;
314  }
315  }
316 
317  return new UniscribeParagraphLayout(ranges, buff);
318 }
319 
320 /* virtual */ const ParagraphLayouter::Line *UniscribeParagraphLayout::NextLine(int max_width)
321 {
322  std::vector<UniscribeRun>::iterator start_run = this->cur_range;
323  std::vector<UniscribeRun>::iterator last_run = this->cur_range;
324 
325  if (start_run == this->ranges.end()) return NULL;
326 
327  /* Add remaining width of the first run if it is a broken run. */
328  int cur_width = 0;
329  if (this->cur_range_offset != 0) {
330  std::vector<int> dx(start_run->len);
331  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]);
332 
333  for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
334  cur_width += *c;
335  }
336  ++last_run;
337  }
338 
339  /* Gather runs until the line is full. */
340  while (last_run != this->ranges.end() && cur_width < max_width) {
341  cur_width += last_run->total_advance;
342  ++last_run;
343  }
344 
345  /* If the text does not fit into the available width, find a suitable breaking point. */
346  int remaing_offset = (last_run - 1)->len;
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 NULL;
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  /* Include whitespace characters after the breaking point. */
380  while (num_chars < (int)log_attribs.size() && log_attribs[num_chars].fWhiteSpace) {
381  num_chars++;
382  }
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  remaing_offset = num_chars + run->len + 1;
390  last_run = run + 1;
391  assert(remaing_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], NULL))) return NULL;
404 
405  /* Create line. */
406  UniscribeLine *line = new 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 && remaing_offset < (last_run - 1)->len) {
415  run.len = remaing_offset - 1;
416 
417  if (!UniscribeShapeRun(this->text_buffer, run)) return NULL;
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 NULL;
425  }
426 
427  *line->Append() = new UniscribeVisualRun(run, cur_pos);
428  cur_pos += run.total_advance;
429  }
430 
431  if (remaing_offset < (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 = remaing_offset - 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 UniscribeVisualRun * const *run = this->Begin(); run != this->End(); run++) {
452  leading = 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 UniscribeVisualRun * const *run = this->Begin(); run != this->End(); run++) {
466  length += (*run)->GetAdvance();
467  }
468 
469  return length;
470 }
471 
472 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)
473 {
474  this->num_glyphs = (int)glyphs.size();
475  this->positions.resize(this->num_glyphs * 2 + 2);
476 
477  int advance = 0;
478  for (int i = 0; i < this->num_glyphs; i++) {
479  this->positions[i * 2 + 0] = range.offsets[i].du + advance + x;
480  this->positions[i * 2 + 1] = range.offsets[i].dv;
481 
482  advance += range.advances[i];
483  }
484  this->positions[this->num_glyphs * 2] = advance + x;
485 }
486 
487 const int *UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap() const
488 {
489  if (this->glyph_to_char == NULL) {
490  this->glyph_to_char = CallocT<int>(this->GetGlyphCount());
491 
492  /* The char to glyph array contains the first glyph index of the cluster that is associated
493  * with each character. It is possible for a cluster to be formed of several chars. */
494  for (int c = 0; c < (int)this->char_to_glyph.size(); c++) {
495  /* If multiple chars map to one glyph, only refer back to the first character. */
496  if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
497  }
498 
499  /* We only marked the first glyph of each cluster in the loop above. Fill the gaps. */
500  int last_char = this->glyph_to_char[0];
501  for (int g = 0; g < this->GetGlyphCount(); g++) {
502  if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
503  this->glyph_to_char[g] = last_char;
504  }
505  }
506 
507  return this->glyph_to_char;
508 }
509 
510 
511 /* virtual */ void UniscribeStringIterator::SetString(const char *s)
512 {
513  const char *string_base = s;
514 
515  this->utf16_to_utf8.clear();
516  this->str_info.clear();
517  this->cur_pos = 0;
518 
519  /* Uniscribe operates on UTF-16, thus we have to convert the input string.
520  * To be able to return proper offsets, we have to create a mapping at the same time. */
521  std::vector<wchar_t> utf16_str;
522  while (*s != '\0') {
523  size_t idx = s - string_base;
524 
525  WChar c = Utf8Consume(&s);
526  if (c < 0x10000) {
527  utf16_str.push_back((wchar_t)c);
528  } else {
529  /* Make a surrogate pair. */
530  utf16_str.push_back((wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
531  utf16_str.push_back((wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
532  this->utf16_to_utf8.push_back(idx);
533  }
534  this->utf16_to_utf8.push_back(idx);
535  }
536  this->utf16_to_utf8.push_back(s - string_base);
537 
538  /* Query Uniscribe for word and cluster break information. */
539  this->str_info.resize(utf16_to_utf8.size());
540 
541  if (utf16_str.size() > 0) {
542  /* Itemize string into language runs. */
543  std::vector<SCRIPT_ITEM> runs = UniscribeItemizeString(&utf16_str[0], (int32)utf16_str.size());
544 
545  for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); runs.size() > 0 && run != runs.end() - 1; run++) {
546  /* Get information on valid word and character break.s */
547  int len = (run + 1)->iCharPos - run->iCharPos;
548  std::vector<SCRIPT_LOGATTR> attr(len);
549  ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
550 
551  /* Extract the information we're interested in. */
552  for (size_t c = 0; c < attr.size(); c++) {
553  /* First character of a run is always a valid word break. */
554  this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
555  this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
556  }
557  }
558  }
559 
560  /* End-of-string is always a valid stopping point. */
561  this->str_info.back().char_stop = true;
562  this->str_info.back().word_stop = true;
563 }
564 
565 /* virtual */ size_t UniscribeStringIterator::SetCurPosition(size_t pos)
566 {
567  /* Convert incoming position to an UTF-16 string index. */
568  size_t utf16_pos = 0;
569  for (size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
570  if (this->utf16_to_utf8[i] == pos) {
571  utf16_pos = i;
572  break;
573  }
574  }
575 
576  /* Sanitize in case we get a position inside a grapheme cluster. */
577  while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
578  this->cur_pos = utf16_pos;
579 
580  return this->utf16_to_utf8[this->cur_pos];
581 }
582 
583 /* virtual */ size_t UniscribeStringIterator::Next(IterType what)
584 {
585  assert(this->cur_pos <= this->utf16_to_utf8.size());
587 
588  if (this->cur_pos == this->utf16_to_utf8.size()) return END;
589 
590  do {
591  this->cur_pos++;
592  } 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));
593 
594  return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
595 }
596 
597 /*virtual */ size_t UniscribeStringIterator::Prev(IterType what)
598 {
599  assert(this->cur_pos <= this->utf16_to_utf8.size());
601 
602  if (this->cur_pos == 0) return END;
603 
604  do {
605  this->cur_pos--;
606  } while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
607 
608  return this->utf16_to_utf8[this->cur_pos];
609 }
610 
611 #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:632
const LanguageMetadata * _current_language
The currently loaded language.
Definition: strings.cpp:50
Implementation of simple mapping class.
const T * Begin() const
Get the pointer to the first item (const)
Visual run contains data about the bit of text with the same font.
Definition: gfx_layout.h:122
static T max(const T a, const T b)
Returns the maximum of two values.
Definition: math_func.hpp:26
const T * End() const
Get the pointer behind the last valid item (const)
uint16 winlangid
Windows language ID: Windows cannot and will not convert isocodes to something it can use to determin...
Definition: language.h:53
Iterate over characters (or more exactly grapheme clusters).
Definition: string_base.h:20
A single line worth of VisualRuns.
Definition: gfx_layout.h:134
Simple pair of data.
Interface to glue fallback and normal layouter into one.
Definition: gfx_layout.h:117
Simple vector template class, with automatic delete.
#define lengthof(x)
Return the length of an fixed size array.
Definition: depend.cpp:42
static T min(const T a, const T b)
Returns the minimum of two values.
Definition: math_func.hpp:42
Iterate over words.
Definition: string_base.h:21
TextDirection _current_text_dir
Text direction of the currently selected language.
Definition: strings.cpp:52
FontSize
Available font sizes.
Definition: gfx_type.h:203
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Definition: depend.cpp:114
Text is written right-to-left by default.
Definition: strings_type.h:26
uint32 GlyphID
Glyphs are characters from a font.
Definition: fontcache.h:19
uint32 WChar
Type for wide characters, i.e.
Definition: string_type.h:35
declarations of functions for MS windows systems