10 #include "../../stdafx.h"
11 #include "../../debug.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"
23 #include "../../safeguards.h"
26 # pragma comment(lib, "usp10")
42 std::vector<GlyphID> ft_glyphs;
45 std::vector<WORD> char_to_glyph;
47 std::vector<SCRIPT_VISATTR> vis_attribs;
48 std::vector<WORD> glyphs;
49 std::vector<int> advances;
50 std::vector<GOFFSET> offsets;
53 UniscribeRun(
int pos,
int len,
Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
76 std::vector<GlyphID> glyphs;
77 std::vector<Position> positions;
78 std::vector<WORD> char_to_glyph;
85 mutable std::vector<int> glyph_to_char;
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;
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; }
106 int CountRuns()
const override {
return (uint)this->size(); }
107 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
109 int GetInternalCharLength(char32_t c)
const override
112 return c >= 0x010000U ? 2 : 1;
123 void Reflow()
override
125 this->cur_range = this->ranges.begin();
126 this->cur_range_offset = 0;
129 std::unique_ptr<const Line> NextLine(
int max_width)
override;
132 void UniscribeResetScriptCache(
FontSize size)
143 if (font->
fc->
GetOSHandle() !=
nullptr)
return CreateFontIndirect(
reinterpret_cast<PLOGFONT
>(
const_cast<void *
>(font->
fc->
GetOSHandle())));
146 ZeroMemory(&logfont,
sizeof(LOGFONT));
148 logfont.lfWeight = FW_NORMAL;
149 logfont.lfCharSet = DEFAULT_CHARSET;
152 return CreateFontIndirect(&logfont);
159 range.glyphs.resize(range.len * 3 / 2 + 16);
160 range.vis_attribs.resize(range.glyphs.size());
163 range.char_to_glyph.resize(range.len);
165 HDC temp_dc =
nullptr;
166 HFONT old_font =
nullptr;
167 HFONT cur_font =
nullptr;
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);
175 range.glyphs.resize(glyphs_used);
176 range.vis_attribs.resize(glyphs_used);
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);
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];
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) {
196 range.advances[pos] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[pos]);
201 range.total_advance = 0;
202 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]);
207 range.total_advance += range.advances[i];
213 if (hr == E_OUTOFMEMORY) {
215 range.glyphs.resize(range.glyphs.size() * 2);
216 range.vis_attribs.resize(range.vis_attribs.size() * 2);
217 }
else if (hr == E_PENDING) {
220 if (cur_font ==
nullptr)
return false;
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) {
227 range.sa.eScript = SCRIPT_UNDEFINED;
230 if (temp_dc !=
nullptr) {
231 SelectObject(temp_dc, old_font);
232 DeleteObject(cur_font);
233 ReleaseDC(
nullptr, temp_dc);
239 if (temp_dc !=
nullptr) {
240 SelectObject(temp_dc, old_font);
241 DeleteObject(cur_font);
242 ReleaseDC(
nullptr, temp_dc);
251 SCRIPT_CONTROL control;
252 ZeroMemory(&control,
sizeof(SCRIPT_CONTROL));
256 ZeroMemory(&state,
sizeof(SCRIPT_STATE));
259 std::vector<SCRIPT_ITEM> items(16);
263 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
267 items.resize(generated + 1);
271 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
273 items.resize(items.size() * 2);
281 int32_t length = buff_end - buff;
283 if (length == 0)
return nullptr;
286 for (
auto const &pair : fontMapping) {
287 if (pair.second->fc->IsBuiltInFont())
return nullptr;
292 if (items.empty())
return nullptr;
296 std::vector<UniscribeRun> ranges;
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) {
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);
313 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
321 std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(
int max_width)
323 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
324 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
326 if (start_run == this->ranges.end())
return nullptr;
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]);
334 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
341 while (last_run != this->ranges.end() && cur_width <= max_width) {
342 cur_width += last_run->total_advance;
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;
353 int width_avail = max_width;
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;
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]);
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];
374 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
376 if (num_chars == this->cur_range_offset) {
378 num_chars = last_cluster;
382 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
384 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
387 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
388 num_chars -= run->len;
390 if (num_chars <= 0) {
391 remaining_offset = num_chars + run->len + 1;
393 assert(remaining_offset - 1 > 0);
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);
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;
408 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
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;
416 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
417 run.len = remaining_offset - 1;
421 if (i_run == start_run && this->cur_range_offset > 0) {
422 assert(run.len - this->cur_range_offset > 0);
429 line->emplace_back(run, cur_pos);
430 cur_pos += run.total_advance;
433 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
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);
439 this->cur_range_offset = 0;
440 this->cur_range = last_run;
453 for (
const auto &run : *
this) {
454 leading = std::max(leading, run.GetLeading());
467 for (
const auto &run : *
this) {
468 length += run.GetAdvance();
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)
476 this->num_glyphs = (int)glyphs.size();
477 this->positions.reserve(this->num_glyphs);
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);
484 advance += x_advance;
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))
495 std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap()
const
497 if (this->glyph_to_char.empty()) {
498 this->glyph_to_char.resize(this->GetGlyphCount());
502 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
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;
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;
515 return this->glyph_to_char;
521 const char *string_base = s;
523 this->utf16_to_utf8.clear();
524 this->str_info.clear();
529 std::vector<wchar_t> utf16_str;
531 size_t idx = s - string_base;
533 char32_t c = Utf8Consume(&s);
535 utf16_str.push_back((
wchar_t)c);
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);
542 this->utf16_to_utf8.push_back(idx);
544 this->utf16_to_utf8.push_back(s - string_base);
547 this->str_info.resize(utf16_to_utf8.size());
549 if (!utf16_str.empty()) {
553 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
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]);
560 for (
size_t c = 0; c < attr.size(); c++) {
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;
569 this->str_info.back().char_stop =
true;
570 this->str_info.back().word_stop =
true;
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) {
585 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
586 this->cur_pos = utf16_pos;
588 return this->utf16_to_utf8[this->cur_pos];
593 assert(this->cur_pos <= this->utf16_to_utf8.size());
596 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
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));
602 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
607 assert(this->cur_pos <= this->utf16_to_utf8.size());
610 if (this->cur_pos == 0)
return END;
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));
616 return this->utf16_to_utf8[this->cur_pos];
virtual std::string GetFontName()=0
Get the name of this font.
int GetHeight() const
Get the height of the font.
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.
virtual const void * GetOSHandle()
Get the native OS font handle, if there is one.
Container with information about a font.
FontCache * fc
The font we are using.
A single line worth of VisualRuns.
Visual run contains data about the bit of text with the same font.
Interface to glue fallback and normal layouter into one.
IterType
Type of the iterator.
@ ITER_WORD
Iterate over words.
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
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.
FontSize
Available font sizes.
const LanguageMetadata * _current_language
The currently loaded language.
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.
@ TD_RTL
Text is written right-to-left by default.
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.
declarations of functions for MS windows systems
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.