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) {}
75 std::vector<GlyphID> glyphs;
76 std::vector<Position> positions;
77 std::vector<WORD> char_to_glyph;
83 mutable std::vector<int> glyph_to_char;
87 UniscribeVisualRun(UniscribeVisualRun &&other)
noexcept;
89 std::span<const GlyphID>
GetGlyphs()
const override {
return this->glyphs; }
90 std::span<const Position>
GetPositions()
const override {
return this->positions; }
96 int GetAdvance()
const {
return this->total_advance; }
104 size_t CountRuns()
const override {
return this->size(); }
110 return c >= 0x010000U ? 2 : 1;
119 ~UniscribeParagraphLayout()
override =
default;
123 this->cur_range = this->ranges.begin();
124 this->cur_range_offset = 0;
127 std::unique_ptr<const Line>
NextLine(
int max_width)
override;
130void UniscribeResetScriptCache(
FontSize size)
145 if (font->
fc->
GetOSHandle() !=
nullptr)
return CreateFontIndirect(
reinterpret_cast<PLOGFONT
>(
const_cast<void *
>(font->
fc->
GetOSHandle())));
149 logfont.lfWeight = FW_NORMAL;
150 logfont.lfCharSet = DEFAULT_CHARSET;
153 return CreateFontIndirect(&logfont);
165 range.glyphs.resize(range.len * 3 / 2 + 16);
166 range.vis_attribs.resize(range.glyphs.size());
169 range.char_to_glyph.resize(range.len);
171 HDC temp_dc =
nullptr;
172 HFONT old_font =
nullptr;
173 HFONT cur_font =
nullptr;
178 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);
181 range.glyphs.resize(glyphs_used);
182 range.vis_attribs.resize(glyphs_used);
186 range.advances.resize(range.glyphs.size());
187 range.offsets.resize(range.glyphs.size());
188 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);
192 range.ft_glyphs.resize(range.glyphs.size());
193 for (
size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
194 range.ft_glyphs[g_id] = range.glyphs[g_id];
196 for (
int i = 0; i < range.len; i++) {
197 if (buff[range.pos + i] >= SCC_SPRITE_START && buff[range.pos + i] <= SCC_SPRITE_END) {
198 auto pos = range.char_to_glyph[i];
199 if (range.ft_glyphs[pos] == 0) {
202 range.advances[pos] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[pos]);
207 range.total_advance = 0;
208 for (
size_t i = 0; i < range.advances.size(); i++) {
211 if (range.advances[i] > 0 && range.ft_glyphs[i] != 0xFFFF) range.advances[i] = range.font->
fc->
GetGlyphWidth(range.ft_glyphs[i]);
213 range.total_advance += range.advances[i];
219 if (hr == E_OUTOFMEMORY) {
221 range.glyphs.resize(range.glyphs.size() * 2);
222 range.vis_attribs.resize(range.vis_attribs.size() * 2);
223 }
else if (hr == E_PENDING) {
226 if (cur_font ==
nullptr)
return false;
228 temp_dc = CreateCompatibleDC(
nullptr);
229 SetMapMode(temp_dc, MM_TEXT);
230 old_font = (HFONT)SelectObject(temp_dc, cur_font);
231 }
else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
233 range.sa.eScript = SCRIPT_UNDEFINED;
236 if (temp_dc !=
nullptr) {
237 SelectObject(temp_dc, old_font);
238 DeleteObject(cur_font);
239 ReleaseDC(
nullptr, temp_dc);
245 if (temp_dc !=
nullptr) {
246 SelectObject(temp_dc, old_font);
247 DeleteObject(cur_font);
248 ReleaseDC(
nullptr, temp_dc);
263 SCRIPT_CONTROL control{};
266 SCRIPT_STATE state{};
269 std::vector<SCRIPT_ITEM> items(16);
273 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
277 items.resize(generated + 1);
281 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
283 items.resize(items.size() * 2);
291 int32_t length = buff_end - buff;
293 if (length == 0)
return nullptr;
296 for (
auto const &[position, font] : font_mapping) {
297 if (font->fc->IsBuiltInFont())
return nullptr;
302 if (items.empty())
return nullptr;
306 std::vector<UniscribeRun> ranges;
309 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
310 for (
auto const &[position, font] : font_mapping) {
311 while (cur_pos < position && cur_item != items.end() - 1) {
313 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
314 assert(stop_pos - cur_pos > 0);
315 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
323 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
328 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
333 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
334 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
336 if (start_run == this->
ranges.end())
return nullptr;
341 std::vector<int> dx(start_run->len);
342 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]);
344 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
351 while (last_run != this->
ranges.end() && cur_width <= max_width) {
352 cur_width += last_run->total_advance;
357 int remaining_offset = (last_run - 1)->len + 1;
358 int whitespace_count = 0;
359 if (cur_width > max_width) {
360 std::vector<SCRIPT_LOGATTR> log_attribs;
363 int width_avail = max_width;
367 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
368 log_attribs.resize(r->pos - start_run->pos + r->len);
369 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;
371 std::vector<int> dx(r->len);
372 ScriptGetLogicalWidths(&r->sa, r->len, (
int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
375 for (
int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
376 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
377 width_avail -= dx[c];
384 while (--num_chars > this->
cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
388 num_chars = last_cluster;
392 while (num_chars - 1 > this->
cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
394 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
397 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
398 num_chars -= run->len;
400 if (num_chars <= 0) {
401 remaining_offset = num_chars + run->len + 1;
403 assert(remaining_offset - 1 > 0);
410 std::vector<BYTE> bidi_level;
411 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
412 bidi_level.push_back(r->sa.s.uBidiLevel);
414 std::vector<INT> vis_to_log(bidi_level.size());
415 if (FAILED(ScriptLayout((
int)bidi_level.size(), &bidi_level[0], &vis_to_log[0],
nullptr)))
return nullptr;
418 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
421 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
422 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
426 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
427 run.len = remaining_offset - 1;
432 assert(run.len - this->cur_range_offset > 0);
439 line->emplace_back(run, cur_pos);
440 cur_pos += run.total_advance;
443 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
447 assert(this->
cur_range->len > this->cur_range_offset);
463 for (
const auto &run : *
this) {
464 leading = std::max(leading, run.GetLeading());
477 for (
const auto &run : *
this) {
478 length += run.GetAdvance();
484UniscribeParagraphLayout::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)
486 this->positions.reserve(this->GetGlyphCount());
489 for (
size_t i = 0; i < this->GetGlyphCount(); i++) {
490 int x_advance = range.advances[i];
491 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
493 advance += x_advance;
497UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(
UniscribeVisualRun&& other) noexcept
498 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
499 start_pos(other.start_pos), total_advance(other.total_advance), font(other.font),
500 glyph_to_char(std::move(other.glyph_to_char))
506 if (this->glyph_to_char.empty()) {
511 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
513 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
517 int last_char = this->glyph_to_char[0];
519 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
520 this->glyph_to_char[g] = last_char;
524 return this->glyph_to_char;
536 std::vector<wchar_t> utf16_str;
538 for (
auto it = view.begin(), end = view.end(); it != end; ++it) {
539 size_t idx = it.GetByteOffset();
542 utf16_str.push_back((
wchar_t)c);
545 utf16_str.push_back((
wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
546 utf16_str.push_back((
wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
556 if (!utf16_str.empty()) {
560 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
562 int len = (run + 1)->iCharPos - run->iCharPos;
563 std::vector<SCRIPT_LOGATTR> attr(len);
564 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
567 for (
size_t c = 0; c < attr.size(); c++) {
569 this->
str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
570 this->
str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
576 this->
str_info.back().char_stop =
true;
577 this->
str_info.back().word_stop =
true;
583 size_t utf16_pos = 0;
592 while (utf16_pos > 0 && !this->
str_info[utf16_pos].char_stop) utf16_pos--;
607 }
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)
Determine the glyph positions for a run.
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.