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())));
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{};
254 SCRIPT_STATE state{};
257 std::vector<SCRIPT_ITEM> items(16);
261 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
265 items.resize(generated + 1);
269 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
271 items.resize(items.size() * 2);
279 int32_t length = buff_end - buff;
281 if (length == 0)
return nullptr;
284 for (
auto const &[position, font] : font_mapping) {
285 if (font->fc->IsBuiltInFont())
return nullptr;
290 if (items.empty())
return nullptr;
294 std::vector<UniscribeRun> ranges;
297 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
298 for (
auto const &[position, font] : font_mapping) {
299 while (cur_pos < position && cur_item != items.end() - 1) {
301 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
302 assert(stop_pos - cur_pos > 0);
303 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
311 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
316 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
319 std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(
int max_width)
321 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
322 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
324 if (start_run == this->ranges.end())
return nullptr;
328 if (this->cur_range_offset != 0) {
329 std::vector<int> dx(start_run->len);
330 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]);
332 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
339 while (last_run != this->ranges.end() && cur_width <= max_width) {
340 cur_width += last_run->total_advance;
345 int remaining_offset = (last_run - 1)->len + 1;
346 int whitespace_count = 0;
347 if (cur_width > max_width) {
348 std::vector<SCRIPT_LOGATTR> log_attribs;
351 int width_avail = max_width;
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 nullptr;
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 - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
382 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
385 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
386 num_chars -= run->len;
388 if (num_chars <= 0) {
389 remaining_offset = num_chars + run->len + 1;
391 assert(remaining_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],
nullptr)))
return nullptr;
406 std::unique_ptr<UniscribeLine> line = std::make_unique<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;
414 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
415 run.len = remaining_offset - 1;
419 if (i_run == start_run && this->cur_range_offset > 0) {
420 assert(run.len - this->cur_range_offset > 0);
427 line->emplace_back(run, cur_pos);
428 cur_pos += run.total_advance;
431 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
433 this->cur_range_offset = remaining_offset + whitespace_count - 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;
451 for (
const auto &run : *
this) {
452 leading = std::max(leading, run.GetLeading());
465 for (
const auto &run : *
this) {
466 length += run.GetAdvance();
472UniscribeParagraphLayout::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.reserve(this->num_glyphs);
478 for (
int i = 0; i < this->num_glyphs; i++) {
479 int x_advance = range.advances[i];
480 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
482 advance += x_advance;
486UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
487 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
488 start_pos(other.start_pos), total_advance(other.total_advance), num_glyphs(other.num_glyphs), font(other.font),
489 glyph_to_char(std::move(other.glyph_to_char))
493std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap()
const
495 if (this->glyph_to_char.empty()) {
496 this->glyph_to_char.resize(this->GetGlyphCount());
500 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
502 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
506 int last_char = this->glyph_to_char[0];
507 for (
int g = 0; g < this->GetGlyphCount(); g++) {
508 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
509 this->glyph_to_char[g] = last_char;
513 return this->glyph_to_char;
519 this->utf16_to_utf8.clear();
520 this->str_info.clear();
525 std::vector<wchar_t> utf16_str;
527 for (
auto it = view.begin(), end = view.end(); it != end; ++it) {
528 size_t idx = it.GetByteOffset();
531 utf16_str.push_back((
wchar_t)c);
534 utf16_str.push_back((
wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
535 utf16_str.push_back((
wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
536 this->utf16_to_utf8.push_back(idx);
538 this->utf16_to_utf8.push_back(idx);
540 this->utf16_to_utf8.push_back(s.size());
543 this->str_info.resize(utf16_to_utf8.size());
545 if (!utf16_str.empty()) {
549 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
551 int len = (run + 1)->iCharPos - run->iCharPos;
552 std::vector<SCRIPT_LOGATTR> attr(len);
553 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
556 for (
size_t c = 0; c < attr.size(); c++) {
558 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
559 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
565 this->str_info.back().char_stop =
true;
566 this->str_info.back().word_stop =
true;
572 size_t utf16_pos = 0;
573 for (
size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
574 if (this->utf16_to_utf8[i] == pos) {
581 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
582 this->cur_pos = utf16_pos;
584 return this->utf16_to_utf8[this->cur_pos];
589 assert(this->cur_pos <= this->utf16_to_utf8.size());
592 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
596 }
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));
598 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
603 assert(this->cur_pos <= this->utf16_to_utf8.size());
606 if (this->cur_pos == 0)
return END;
610 }
while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
612 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(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.