10#include "../../stdafx.h"
11#include "../../debug.h"
13#include "../../language.h"
14#include "../../strings_func.h"
15#include "../../string_func.h"
16#include "../../core/utf8.hpp"
17#include "../../table/control_codes.h"
18#include "../../zoom_func.h"
24#include "../../safeguards.h"
27# pragma comment(lib, "usp10")
43 std::vector<GlyphID> ft_glyphs;
46 std::vector<WORD> char_to_glyph;
48 std::vector<SCRIPT_VISATTR> vis_attribs;
49 std::vector<WORD> glyphs;
50 std::vector<int> advances;
51 std::vector<GOFFSET> offsets;
54 UniscribeRun(
int pos,
int len,
Font *font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
77 std::vector<GlyphID> glyphs;
78 std::vector<Position> positions;
79 std::vector<WORD> char_to_glyph;
86 mutable std::vector<int> glyph_to_char;
92 std::span<const GlyphID> GetGlyphs()
const override {
return this->glyphs; }
93 std::span<const Position> GetPositions()
const override {
return this->positions; }
94 std::span<const int> GetGlyphToCharMap()
const override;
96 const Font *GetFont()
const override {
return this->font; }
97 int GetLeading()
const override {
return this->font->
fc->
GetHeight(); }
98 int GetGlyphCount()
const override {
return this->num_glyphs; }
99 int GetAdvance()
const {
return this->total_advance; }
107 int CountRuns()
const override {
return (uint)this->size(); }
108 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
110 int GetInternalCharLength(
char32_t c)
const override
113 return c >= 0x010000U ? 2 : 1;
124 void Reflow()
override
126 this->cur_range = this->ranges.begin();
127 this->cur_range_offset = 0;
130 std::unique_ptr<const Line> NextLine(
int max_width)
override;
133void UniscribeResetScriptCache(
FontSize size)
144 if (font->
fc->
GetOSHandle() !=
nullptr)
return CreateFontIndirect(
reinterpret_cast<PLOGFONT
>(
const_cast<void *
>(font->
fc->
GetOSHandle())));
147 ZeroMemory(&logfont,
sizeof(LOGFONT));
149 logfont.lfWeight = FW_NORMAL;
150 logfont.lfCharSet = DEFAULT_CHARSET;
153 return CreateFontIndirect(&logfont);
160 range.glyphs.resize(range.len * 3 / 2 + 16);
161 range.vis_attribs.resize(range.glyphs.size());
164 range.char_to_glyph.resize(range.len);
166 HDC temp_dc =
nullptr;
167 HFONT old_font =
nullptr;
168 HFONT cur_font =
nullptr;
173 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);
176 range.glyphs.resize(glyphs_used);
177 range.vis_attribs.resize(glyphs_used);
181 range.advances.resize(range.glyphs.size());
182 range.offsets.resize(range.glyphs.size());
183 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);
187 range.ft_glyphs.resize(range.glyphs.size());
188 for (
size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
189 range.ft_glyphs[g_id] = range.glyphs[g_id];
191 for (
int i = 0; i < range.len; i++) {
192 if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
193 auto pos = range.char_to_glyph[i];
194 if (range.ft_glyphs[pos] == 0) {
197 range.advances[pos] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[pos]);
202 range.total_advance = 0;
203 for (
size_t i = 0; i < range.advances.size(); i++) {
206 if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[i]);
208 range.total_advance += range.advances[i];
214 if (hr == E_OUTOFMEMORY) {
216 range.glyphs.resize(range.glyphs.size() * 2);
217 range.vis_attribs.resize(range.vis_attribs.size() * 2);
218 }
else if (hr == E_PENDING) {
221 if (cur_font ==
nullptr)
return false;
223 temp_dc = CreateCompatibleDC(
nullptr);
224 SetMapMode(temp_dc, MM_TEXT);
225 old_font = (HFONT)SelectObject(temp_dc, cur_font);
226 }
else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
228 range.sa.eScript = SCRIPT_UNDEFINED;
231 if (temp_dc !=
nullptr) {
232 SelectObject(temp_dc, old_font);
233 DeleteObject(cur_font);
234 ReleaseDC(
nullptr, temp_dc);
240 if (temp_dc !=
nullptr) {
241 SelectObject(temp_dc, old_font);
242 DeleteObject(cur_font);
243 ReleaseDC(
nullptr, temp_dc);
252 SCRIPT_CONTROL control;
253 ZeroMemory(&control,
sizeof(SCRIPT_CONTROL));
257 ZeroMemory(&state,
sizeof(SCRIPT_STATE));
260 std::vector<SCRIPT_ITEM> items(16);
264 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
268 items.resize(generated + 1);
272 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
274 items.resize(items.size() * 2);
282 int32_t length = buff_end - buff;
284 if (length == 0)
return nullptr;
287 for (
auto const &[position, font] : font_mapping) {
288 if (font->fc->IsBuiltInFont())
return nullptr;
293 if (items.empty())
return nullptr;
297 std::vector<UniscribeRun> ranges;
300 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
301 for (
auto const &[position, font] : font_mapping) {
302 while (cur_pos < position && cur_item != items.end() - 1) {
304 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
305 assert(stop_pos - cur_pos > 0);
306 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
314 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
319 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
322 std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(
int max_width)
324 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
325 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
327 if (start_run == this->ranges.end())
return nullptr;
331 if (this->cur_range_offset != 0) {
332 std::vector<int> dx(start_run->len);
333 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]);
335 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
342 while (last_run != this->ranges.end() && cur_width <= max_width) {
343 cur_width += last_run->total_advance;
348 int remaining_offset = (last_run - 1)->len + 1;
349 int whitespace_count = 0;
350 if (cur_width > max_width) {
351 std::vector<SCRIPT_LOGATTR> log_attribs;
354 int width_avail = max_width;
357 int last_cluster = this->cur_range_offset + 1;
358 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
359 log_attribs.resize(r->pos - start_run->pos + r->len);
360 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;
362 std::vector<int> dx(r->len);
363 ScriptGetLogicalWidths(&r->sa, r->len, (
int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
366 for (
int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
367 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
368 width_avail -= dx[c];
375 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
377 if (num_chars == this->cur_range_offset) {
379 num_chars = last_cluster;
383 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
385 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
388 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
389 num_chars -= run->len;
391 if (num_chars <= 0) {
392 remaining_offset = num_chars + run->len + 1;
394 assert(remaining_offset - 1 > 0);
401 std::vector<BYTE> bidi_level;
402 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
403 bidi_level.push_back(r->sa.s.uBidiLevel);
405 std::vector<INT> vis_to_log(bidi_level.size());
406 if (FAILED(ScriptLayout((
int)bidi_level.size(), &bidi_level[0], &vis_to_log[0],
nullptr)))
return nullptr;
409 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
412 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
413 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
417 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
418 run.len = remaining_offset - 1;
422 if (i_run == start_run && this->cur_range_offset > 0) {
423 assert(run.len - this->cur_range_offset > 0);
430 line->emplace_back(run, cur_pos);
431 cur_pos += run.total_advance;
434 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
436 this->cur_range_offset = remaining_offset + whitespace_count - 1;
437 this->cur_range = last_run - 1;
438 assert(this->cur_range->len > this->cur_range_offset);
440 this->cur_range_offset = 0;
441 this->cur_range = last_run;
454 for (
const auto &run : *
this) {
455 leading = std::max(leading, run.GetLeading());
468 for (
const auto &run : *
this) {
469 length += run.GetAdvance();
475UniscribeParagraphLayout::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)
477 this->num_glyphs = (int)glyphs.size();
478 this->positions.reserve(this->num_glyphs);
481 for (
int i = 0; i < this->num_glyphs; i++) {
482 int x_advance = range.advances[i];
483 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
485 advance += x_advance;
489UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
490 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
491 start_pos(other.start_pos), total_advance(other.total_advance), num_glyphs(other.num_glyphs), font(other.font),
492 glyph_to_char(std::move(other.glyph_to_char))
496std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap()
const
498 if (this->glyph_to_char.empty()) {
499 this->glyph_to_char.resize(this->GetGlyphCount());
503 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
505 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
509 int last_char = this->glyph_to_char[0];
510 for (
int g = 0; g < this->GetGlyphCount(); g++) {
511 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
512 this->glyph_to_char[g] = last_char;
516 return this->glyph_to_char;
522 this->utf16_to_utf8.clear();
523 this->str_info.clear();
528 std::vector<wchar_t> utf16_str;
530 for (
auto it = view.begin(), end = view.end(); it != end; ++it) {
531 size_t idx = it.GetByteOffset();
534 utf16_str.push_back((
wchar_t)c);
537 utf16_str.push_back((
wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
538 utf16_str.push_back((
wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
539 this->utf16_to_utf8.push_back(idx);
541 this->utf16_to_utf8.push_back(idx);
543 this->utf16_to_utf8.push_back(s.size());
546 this->str_info.resize(utf16_to_utf8.size());
548 if (!utf16_str.empty()) {
552 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
554 int len = (run + 1)->iCharPos - run->iCharPos;
555 std::vector<SCRIPT_LOGATTR> attr(len);
556 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
559 for (
size_t c = 0; c < attr.size(); c++) {
561 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
562 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
568 this->str_info.back().char_stop =
true;
569 this->str_info.back().word_stop =
true;
575 size_t utf16_pos = 0;
576 for (
size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
577 if (this->utf16_to_utf8[i] == pos) {
584 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
585 this->cur_pos = utf16_pos;
587 return this->utf16_to_utf8[this->cur_pos];
592 assert(this->cur_pos <= this->utf16_to_utf8.size());
595 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
599 }
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 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
606 assert(this->cur_pos <= this->utf16_to_utf8.size());
609 if (this->cur_pos == 0)
return END;
613 }
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 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 const void * GetOSHandle()
Get the native OS font handle, if there is one.
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.
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 std::unique_ptr< ParagraphLayouter > GetParagraphLayout(CharType *buff, CharType *buff_end, FontMap &font_mapping)
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(std::string_view 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.
Constant span of UTF-8 encoded data.
std::vector< std::pair< 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.