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;
85 mutable std::vector<int> glyph_to_char;
89 UniscribeVisualRun(UniscribeVisualRun &&other)
noexcept;
91 std::span<const GlyphID>
GetGlyphs()
const override {
return this->glyphs; }
92 std::span<const Position>
GetPositions()
const override {
return this->positions; }
98 int GetAdvance()
const {
return this->total_advance; }
106 size_t CountRuns()
const override {
return this->size(); }
112 return c >= 0x010000U ? 2 : 1;
121 ~UniscribeParagraphLayout()
override =
default;
125 this->cur_range = this->ranges.begin();
126 this->cur_range_offset = 0;
129 std::unique_ptr<const Line>
NextLine(
int max_width)
override;
132void UniscribeResetScriptCache(
FontSize size)
143 if (font->
fc->
GetOSHandle() !=
nullptr)
return CreateFontIndirect(
reinterpret_cast<PLOGFONT
>(
const_cast<void *
>(font->
fc->
GetOSHandle())));
147 logfont.lfWeight = FW_NORMAL;
148 logfont.lfCharSet = DEFAULT_CHARSET;
151 return CreateFontIndirect(&logfont);
158 range.glyphs.resize(range.len * 3 / 2 + 16);
159 range.vis_attribs.resize(range.glyphs.size());
162 range.char_to_glyph.resize(range.len);
164 HDC temp_dc =
nullptr;
165 HFONT old_font =
nullptr;
166 HFONT cur_font =
nullptr;
171 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);
174 range.glyphs.resize(glyphs_used);
175 range.vis_attribs.resize(glyphs_used);
179 range.advances.resize(range.glyphs.size());
180 range.offsets.resize(range.glyphs.size());
181 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);
185 range.ft_glyphs.resize(range.glyphs.size());
186 for (
size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
187 range.ft_glyphs[g_id] = range.glyphs[g_id];
189 for (
int i = 0; i < range.len; i++) {
190 if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
191 auto pos = range.char_to_glyph[i];
192 if (range.ft_glyphs[pos] == 0) {
195 range.advances[pos] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[pos]);
200 range.total_advance = 0;
201 for (
size_t i = 0; i < range.advances.size(); i++) {
204 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) {
219 if (cur_font ==
nullptr)
return false;
221 temp_dc = CreateCompatibleDC(
nullptr);
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 !=
nullptr) {
230 SelectObject(temp_dc, old_font);
231 DeleteObject(cur_font);
232 ReleaseDC(
nullptr, temp_dc);
238 if (temp_dc !=
nullptr) {
239 SelectObject(temp_dc, old_font);
240 DeleteObject(cur_font);
241 ReleaseDC(
nullptr, temp_dc);
250 SCRIPT_CONTROL control{};
253 SCRIPT_STATE state{};
256 std::vector<SCRIPT_ITEM> items(16);
260 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
264 items.resize(generated + 1);
268 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
270 items.resize(items.size() * 2);
278 int32_t length = buff_end - buff;
280 if (length == 0)
return nullptr;
283 for (
auto const &[position, font] : font_mapping) {
284 if (font->fc->IsBuiltInFont())
return nullptr;
289 if (items.empty())
return nullptr;
293 std::vector<UniscribeRun> ranges;
296 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
297 for (
auto const &[position, font] : font_mapping) {
298 while (cur_pos < position && cur_item != items.end() - 1) {
300 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
301 assert(stop_pos - cur_pos > 0);
302 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
310 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
315 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
320 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
321 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
323 if (start_run == this->
ranges.end())
return nullptr;
328 std::vector<int> dx(start_run->len);
329 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]);
331 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
338 while (last_run != this->
ranges.end() && cur_width <= max_width) {
339 cur_width += last_run->total_advance;
344 int remaining_offset = (last_run - 1)->len + 1;
345 int whitespace_count = 0;
346 if (cur_width > max_width) {
347 std::vector<SCRIPT_LOGATTR> log_attribs;
350 int width_avail = max_width;
354 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
355 log_attribs.resize(r->pos - start_run->pos + r->len);
356 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;
358 std::vector<int> dx(r->len);
359 ScriptGetLogicalWidths(&r->sa, r->len, (
int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
362 for (
int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
363 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
364 width_avail -= dx[c];
371 while (--num_chars > this->
cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
375 num_chars = last_cluster;
379 while (num_chars - 1 > this->
cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
381 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
384 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
385 num_chars -= run->len;
387 if (num_chars <= 0) {
388 remaining_offset = num_chars + run->len + 1;
390 assert(remaining_offset - 1 > 0);
397 std::vector<BYTE> bidi_level;
398 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
399 bidi_level.push_back(r->sa.s.uBidiLevel);
401 std::vector<INT> vis_to_log(bidi_level.size());
402 if (FAILED(ScriptLayout((
int)bidi_level.size(), &bidi_level[0], &vis_to_log[0],
nullptr)))
return nullptr;
405 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
408 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
409 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
413 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
414 run.len = remaining_offset - 1;
419 assert(run.len - this->cur_range_offset > 0);
426 line->emplace_back(run, cur_pos);
427 cur_pos += run.total_advance;
430 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
434 assert(this->
cur_range->len > this->cur_range_offset);
450 for (
const auto &run : *
this) {
451 leading = std::max(leading, run.GetLeading());
464 for (
const auto &run : *
this) {
465 length += run.GetAdvance();
471UniscribeParagraphLayout::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 this->positions.reserve(this->GetGlyphCount());
476 for (
size_t i = 0; i < this->GetGlyphCount(); i++) {
477 int x_advance = range.advances[i];
478 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
480 advance += x_advance;
484UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(
UniscribeVisualRun&& other) noexcept
485 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
486 start_pos(other.start_pos), total_advance(other.total_advance), font(other.font),
487 glyph_to_char(std::move(other.glyph_to_char))
493 if (this->glyph_to_char.empty()) {
498 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
500 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
504 int last_char = this->glyph_to_char[0];
506 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
507 this->glyph_to_char[g] = last_char;
511 return this->glyph_to_char;
523 std::vector<wchar_t> utf16_str;
525 for (
auto it = view.begin(), end = view.end(); it != end; ++it) {
526 size_t idx = it.GetByteOffset();
529 utf16_str.push_back((
wchar_t)c);
532 utf16_str.push_back((
wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
533 utf16_str.push_back((
wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
543 if (!utf16_str.empty()) {
547 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
549 int len = (run + 1)->iCharPos - run->iCharPos;
550 std::vector<SCRIPT_LOGATTR> attr(len);
551 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
554 for (
size_t c = 0; c < attr.size(); c++) {
556 this->
str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
557 this->
str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
563 this->
str_info.back().char_stop =
true;
564 this->
str_info.back().word_stop =
true;
570 size_t utf16_pos = 0;
579 while (utf16_pos > 0 && !this->
str_info[utf16_pos].char_stop) utf16_pos--;
594 }
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));
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.
static const size_t END
Sentinel to indicate end-of-iteration.
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.
size_t CountRuns() const override
Get the number of runs in this line.
int GetInternalCharLength(char32_t c) const override
Get the number of elements the given character occupies in the underlying text buffer of the Layouter...
int GetLeading() const override
Get the height of the line.
const VisualRun & GetVisualRun(size_t run) const override
Get a reference to the given run.
Visual run contains data about the bit of text with the same font.
const Font * GetFont() const override
Get the font.
std::span< const GlyphID > GetGlyphs() const override
Get the glyphs to draw.
size_t GetGlyphCount() const override
Get the number of glyphs.
std::span< const int > GetGlyphToCharMap() const override
The offset for each of the glyphs to the character run that was passed to the Layouter.
std::span< const Position > GetPositions() const override
Get the positions for each of the glyphs.
int GetLeading() const override
Get the font leading, or distance between the baselines of consecutive lines.
Wrapper for doing layouts with Uniscribe.
int cur_range_offset
Offset from the start of the current run from where to output.
void Reflow() override
Reset the position to the start of the paragraph.
std::vector< UniscribeRun > ranges
All runs of the text.
std::unique_ptr< const Line > NextLine(int max_width) override
Construct a new line with a maximum width.
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.
std::vector< CharInfo > str_info
Break information for each code point.
void SetString(std::string_view s) override
Set a new iteration string.
size_t cur_pos
Current iteration position.
std::vector< size_t > utf16_to_utf8
Mapping from UTF-16 code point position to index in the UTF-8 source 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.
Control codes that are embedded in the translation strings.
Functions related to debugging.
std::vector< std::pair< int, Font * > > FontMap
Mapping from index to font.
FontSize
Available font sizes.
Information about languages and their files.
const LanguageMetadata * _current_language
The currently loaded language.
A number of safeguards to prevent using unsafe methods.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
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.
Functions related to OTTD's strings.
@ TD_RTL
Text is written right-to-left by default.
Contains all information about a run of characters.
Handling of UTF-8 encoded data.
wchar_t * convert_to_fs(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.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.