12 #if defined(WITH_UNISCRIBE) 14 #include "../../stdafx.h" 15 #include "../../debug.h" 17 #include "../../language.h" 18 #include "../../strings_func.h" 19 #include "../../string_func.h" 20 #include "../../table/control_codes.h" 27 #include "../../safeguards.h" 30 # pragma comment(lib, "usp10") 35 static SCRIPT_CACHE _script_cache[FS_END];
46 std::vector<GlyphID> ft_glyphs;
49 std::vector<WORD> char_to_glyph;
51 std::vector<SCRIPT_VISATTR> vis_attribs;
52 std::vector<WORD> glyphs;
53 std::vector<int> advances;
54 std::vector<GOFFSET> offsets;
57 UniscribeRun(
int pos,
int len, Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
61 static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length);
63 static bool UniscribeShapeRun(
const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range);
70 const UniscribeParagraphLayoutFactory::CharType *text_buffer;
72 std::vector<UniscribeRun> ranges;
73 std::vector<UniscribeRun>::iterator cur_range;
74 int cur_range_offset = 0;
80 std::vector<GlyphID> glyphs;
81 std::vector<float> positions;
82 std::vector<WORD> char_to_glyph;
89 mutable int *glyph_to_char = NULL;
92 UniscribeVisualRun(
const UniscribeRun &range,
int x);
93 virtual ~UniscribeVisualRun()
95 free(this->glyph_to_char);
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;
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; }
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); }
116 int GetInternalCharLength(
WChar c)
const 119 return c >= 0x010000U ? 2 : 1;
123 UniscribeParagraphLayout(std::vector<UniscribeRun> &ranges,
const UniscribeParagraphLayoutFactory::CharType *buffer) : text_buffer(buffer), ranges(ranges)
128 virtual ~UniscribeParagraphLayout() {}
130 virtual void Reflow()
132 this->cur_range = this->ranges.begin();
133 this->cur_range_offset = 0;
136 virtual const Line *NextLine(
int max_width);
139 void UniscribeResetScriptCache(
FontSize size)
141 if (_script_cache[size] != NULL) {
142 ScriptFreeCache(&_script_cache[size]);
143 _script_cache[size] = NULL;
148 static HFONT HFontFromFont(Font *font)
151 ZeroMemory(&logfont,
sizeof(LOGFONT));
152 logfont.lfHeight = font->fc->GetHeight();
153 logfont.lfWeight = FW_NORMAL;
154 logfont.lfCharSet = DEFAULT_CHARSET;
157 return CreateFontIndirect(&logfont);
161 static bool UniscribeShapeRun(
const UniscribeParagraphLayoutFactory::CharType *buff, UniscribeRun &range)
164 range.glyphs.resize(range.len * 3 / 2 + 16);
165 range.vis_attribs.resize(range.glyphs.size());
168 range.char_to_glyph.resize(range.len);
171 HFONT old_font = NULL;
172 HFONT cur_font = NULL;
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);
180 range.glyphs.resize(glyphs_used);
181 range.vis_attribs.resize(glyphs_used);
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);
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];
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;
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];
212 if (hr == E_OUTOFMEMORY) {
214 range.glyphs.resize(range.glyphs.size() * 2);
215 range.vis_attribs.resize(range.vis_attribs.size() * 2);
216 }
else if (hr == E_PENDING) {
218 cur_font = HFontFromFont(range.font);
219 if (cur_font == NULL)
return false;
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) {
226 range.sa.eScript = SCRIPT_UNDEFINED;
229 if (temp_dc != NULL) {
230 SelectObject(temp_dc, old_font);
231 DeleteObject(cur_font);
232 ReleaseDC(NULL, temp_dc);
238 if (temp_dc != NULL) {
239 SelectObject(temp_dc, old_font);
240 DeleteObject(cur_font);
241 ReleaseDC(NULL, temp_dc);
247 static std::vector<SCRIPT_ITEM> UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32 length)
250 SCRIPT_CONTROL control;
251 ZeroMemory(&control,
sizeof(SCRIPT_CONTROL));
255 ZeroMemory(&state,
sizeof(SCRIPT_STATE));
258 std::vector<SCRIPT_ITEM> items(16);
262 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
266 items.resize(generated + 1);
270 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
272 items.resize(items.size() * 2);
278 ParagraphLayouter *UniscribeParagraphLayoutFactory::GetParagraphLayout(CharType *buff, CharType *buff_end,
FontMap &fontMapping)
280 int32 length = buff_end - buff;
282 if (length == 0)
return NULL;
286 if (i->second->fc->IsBuiltInFont())
return NULL;
290 std::vector<SCRIPT_ITEM> items = UniscribeItemizeString(buff, length);
291 if (items.size() == 0)
return NULL;
295 std::vector<UniscribeRun> ranges;
298 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
300 while (cur_pos < i->first && cur_item != items.end() - 1) {
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));
307 if (!UniscribeShapeRun(buff, ranges.back())) {
312 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
317 return new UniscribeParagraphLayout(ranges, buff);
322 std::vector<UniscribeRun>::iterator start_run = this->cur_range;
323 std::vector<UniscribeRun>::iterator last_run = this->cur_range;
325 if (start_run == this->ranges.end())
return NULL;
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]);
333 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
340 while (last_run != this->ranges.end() && cur_width < max_width) {
341 cur_width += last_run->total_advance;
346 int remaing_offset = (last_run - 1)->len;
347 if (cur_width > max_width) {
348 std::vector<SCRIPT_LOGATTR> log_attribs;
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;
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]);
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];
372 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
374 if (num_chars == this->cur_range_offset) {
376 num_chars = last_cluster;
380 while (num_chars < (
int)log_attribs.size() && log_attribs[num_chars].fWhiteSpace) {
385 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
386 num_chars -= run->len;
388 if (num_chars <= 0) {
389 remaing_offset = num_chars + run->len + 1;
391 assert(remaing_offset - 1 > 0);
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);
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;
406 UniscribeLine *line =
new UniscribeLine();
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;
414 if (i_run == last_run - 1 && remaing_offset < (last_run - 1)->len) {
415 run.len = remaing_offset - 1;
417 if (!UniscribeShapeRun(this->text_buffer, run))
return NULL;
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;
424 if (!UniscribeShapeRun(this->text_buffer, run))
return NULL;
427 *line->Append() =
new UniscribeVisualRun(run, cur_pos);
428 cur_pos += run.total_advance;
431 if (remaing_offset < (last_run - 1)->len) {
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);
437 this->cur_range_offset = 0;
438 this->cur_range = last_run;
448 int UniscribeParagraphLayout::UniscribeLine::GetLeading()
const 451 for (
const UniscribeVisualRun *
const *run = this->Begin(); run != this->End(); run++) {
452 leading =
max(leading, (*run)->GetLeading());
462 int UniscribeParagraphLayout::UniscribeLine::GetWidth()
const 465 for (
const UniscribeVisualRun *
const *run = this->Begin(); run != this->End(); run++) {
466 length += (*run)->GetAdvance();
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)
474 this->num_glyphs = (int)glyphs.size();
475 this->positions.resize(this->num_glyphs * 2 + 2);
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;
482 advance += range.advances[i];
484 this->positions[this->num_glyphs * 2] = advance + x;
487 const int *UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap()
const 489 if (this->glyph_to_char == NULL) {
490 this->glyph_to_char = CallocT<int>(this->GetGlyphCount());
494 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
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;
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;
507 return this->glyph_to_char;
511 void UniscribeStringIterator::SetString(
const char *s)
513 const char *string_base = s;
515 this->utf16_to_utf8.clear();
516 this->str_info.clear();
521 std::vector<wchar_t> utf16_str;
523 size_t idx = s - string_base;
525 WChar c = Utf8Consume(&s);
527 utf16_str.push_back((
wchar_t)c);
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);
534 this->utf16_to_utf8.push_back(idx);
536 this->utf16_to_utf8.push_back(s - string_base);
539 this->str_info.resize(utf16_to_utf8.size());
541 if (utf16_str.size() > 0) {
543 std::vector<SCRIPT_ITEM> runs = UniscribeItemizeString(&utf16_str[0], (int32)utf16_str.size());
545 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); runs.size() > 0 && run != runs.end() - 1; run++) {
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]);
552 for (
size_t c = 0; c < attr.size(); c++) {
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;
561 this->str_info.back().char_stop =
true;
562 this->str_info.back().word_stop =
true;
565 size_t UniscribeStringIterator::SetCurPosition(
size_t pos)
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) {
577 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
578 this->cur_pos = utf16_pos;
580 return this->utf16_to_utf8[this->cur_pos];
583 size_t UniscribeStringIterator::Next(IterType what)
585 assert(this->cur_pos <= this->utf16_to_utf8.size());
588 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
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));
594 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
597 size_t UniscribeStringIterator::Prev(IterType what)
599 assert(this->cur_pos <= this->utf16_to_utf8.size());
602 if (this->cur_pos == 0)
return END;
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));
608 return this->utf16_to_utf8[this->cur_pos];
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's encoding to that of the environment in UNICODE.
const LanguageMetadata * _current_language
The currently loaded language.
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.
static T max(const T a, const T b)
Returns the maximum of two values.
const T * End() const
Get the pointer behind the last valid item (const)
Iterate over characters (or more exactly grapheme clusters).
A single line worth of VisualRuns.
Interface to glue fallback and normal layouter into one.
Simple vector template class, with automatic delete.
#define lengthof(x)
Return the length of an fixed size array.
static T min(const T a, const T b)
Returns the minimum of two values.
TextDirection _current_text_dir
Text direction of the currently selected language.
FontSize
Available font sizes.
static void free(const void *ptr)
Version of the standard free that accepts const pointers.
Text is written right-to-left by default.
uint32 GlyphID
Glyphs are characters from a font.
uint32 WChar
Type for wide characters, i.e.
declarations of functions for MS windows systems