10#include "../../stdafx.h"
11#include "../../debug.h"
13#include "../../gfx_func.h"
14#include "../../gfx_layout_fallback.h"
15#include "../../language.h"
16#include "../../strings_func.h"
17#include "../../string_func.h"
18#include "../../core/utf8.hpp"
19#include "../../table/control_codes.h"
20#include "../../zoom_func.h"
26#include "../../safeguards.h"
29# pragma comment(lib, "usp10")
45 std::vector<GlyphID> ft_glyphs;
48 std::vector<WORD> char_to_glyph;
50 std::vector<SCRIPT_VISATTR> vis_attribs;
51 std::vector<WORD> glyphs;
52 std::vector<int> advances;
53 std::vector<GOFFSET> offsets;
56 UniscribeRun(
int pos,
int len,
const Font &font, SCRIPT_ANALYSIS &sa) : pos(pos), len(len), font(font), sa(sa) {}
81 std::vector<GlyphID> glyphs;
82 std::vector<Position> positions;
83 std::vector<WORD> char_to_glyph;
90 mutable std::vector<int> glyph_to_char;
96 std::span<const GlyphID> GetGlyphs()
const override {
return this->glyphs; }
97 std::span<const Position> GetPositions()
const override {
return this->positions; }
98 std::span<const int> GetGlyphToCharMap()
const override;
100 const Font &GetFont()
const override {
return this->font; }
102 int GetGlyphCount()
const override {
return this->num_glyphs; }
103 int GetAdvance()
const {
return this->total_advance; }
111 int CountRuns()
const override {
return (uint)this->size(); }
112 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
114 int GetInternalCharLength(
char32_t c)
const override
117 return c >= 0x010000U ? 2 : 1;
128 void Reflow()
override
130 this->cur_range = this->ranges.begin();
131 this->cur_range_offset = 0;
134 std::unique_ptr<const Line> NextLine(
int max_width)
override;
137void UniscribeResetScriptCache(
FontSize)
140 ScriptFreeCache(&sc.second);
150 if (fc.
GetOSHandle() !=
nullptr)
return CreateFontIndirect(
reinterpret_cast<PLOGFONT
>(
const_cast<void *
>(fc.
GetOSHandle())));
154 logfont.lfWeight = FW_NORMAL;
155 logfont.lfCharSet = DEFAULT_CHARSET;
158 return CreateFontIndirect(&logfont);
168 FontCache &fc = this->font.GetFontCache();
170 this->glyphs.reserve(this->len);
173 for (
int i = this->pos; i < this->pos + this->len; i +=
Utf16IsLeadSurrogate(buff[i]) ? 2 : 1) {
174 char32_t c =
Utf16DecodeChar(
reinterpret_cast<const uint16_t *
>(buff + i));
181 std::reverse(std::begin(this->glyphs), std::end(this->glyphs));
184 this->offsets.reserve(this->glyphs.size());
187 int y_offset = fc.GetGlyphYOffset();
189 for (
const GlyphID glyph : this->glyphs) {
190 this->offsets.emplace_back(advance, y_offset);
192 this->advances.push_back(x_advance);
193 advance += x_advance;
200 FontCache &fc = range.font.GetFontCache();
203 range.glyphs.resize(range.len * 3 / 2 + 16);
204 range.vis_attribs.resize(range.glyphs.size());
207 range.char_to_glyph.resize(range.len);
209 HDC temp_dc =
nullptr;
210 HFONT old_font =
nullptr;
211 HFONT cur_font =
nullptr;
221 HRESULT hr = ScriptShape(temp_dc, &
_script_cache[fc.GetIndex()], 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);
224 range.glyphs.resize(glyphs_used);
225 range.vis_attribs.resize(glyphs_used);
229 range.advances.resize(range.glyphs.size());
230 range.offsets.resize(range.glyphs.size());
231 hr = ScriptPlace(temp_dc, &
_script_cache[fc.GetIndex()], &range.glyphs[0], (
int)range.glyphs.size(), &range.vis_attribs[0], &range.sa, &range.advances[0], &range.offsets[0], &abc);
235 range.ft_glyphs.resize(range.glyphs.size());
236 for (
size_t g_id = 0; g_id < range.glyphs.size(); g_id++) {
237 range.ft_glyphs[g_id] = range.glyphs[g_id];
240 range.total_advance = 0;
241 for (
size_t i = 0; i < range.advances.size(); i++) {
244 if (range.advances[i] > 0 && range.glyphs[i] != 0xFFFF) range.advances[i] = fc.
GetGlyphWidth(range.glyphs[i]);
246 range.total_advance += range.advances[i];
252 if (hr == E_OUTOFMEMORY) {
254 range.glyphs.resize(range.glyphs.size() * 2);
255 range.vis_attribs.resize(range.vis_attribs.size() * 2);
256 }
else if (hr == E_PENDING) {
259 if (cur_font ==
nullptr)
return false;
261 temp_dc = CreateCompatibleDC(
nullptr);
262 SetMapMode(temp_dc, MM_TEXT);
263 old_font = (HFONT)SelectObject(temp_dc, cur_font);
264 }
else if (hr == USP_E_SCRIPT_NOT_IN_FONT && range.sa.eScript != SCRIPT_UNDEFINED) {
266 range.sa.eScript = SCRIPT_UNDEFINED;
269 if (temp_dc !=
nullptr) {
270 SelectObject(temp_dc, old_font);
271 DeleteObject(cur_font);
272 ReleaseDC(
nullptr, temp_dc);
278 if (temp_dc !=
nullptr) {
279 SelectObject(temp_dc, old_font);
280 DeleteObject(cur_font);
281 ReleaseDC(
nullptr, temp_dc);
290 SCRIPT_CONTROL control{};
293 SCRIPT_STATE state{};
296 std::vector<SCRIPT_ITEM> items(16);
300 HRESULT hr = ScriptItemize(buff, length, (
int)items.size() - 1, &control, &state, &items[0], &generated);
304 items.resize(generated + 1);
308 if (hr != E_OUTOFMEMORY)
return std::vector<SCRIPT_ITEM>();
310 items.resize(items.size() * 2);
318 int32_t length = buff_end - buff;
320 if (length == 0)
return nullptr;
324 if (items.empty())
return nullptr;
328 std::vector<UniscribeRun> ranges;
331 std::vector<SCRIPT_ITEM>::iterator cur_item = items.begin();
332 for (
auto const &[position, font] : font_mapping) {
333 while (cur_pos < position && cur_item != items.end() - 1) {
335 int stop_pos = std::min(position, (cur_item + 1)->iCharPos);
336 assert(stop_pos - cur_pos > 0);
337 ranges.emplace_back(cur_pos, stop_pos - cur_pos, font, cur_item->a);
345 if (stop_pos == (cur_item + 1)->iCharPos) cur_item++;
350 return std::make_unique<UniscribeParagraphLayout>(std::move(ranges), buff);
353 std::unique_ptr<const ParagraphLayouter::Line> UniscribeParagraphLayout::NextLine(
int max_width)
355 std::vector<UniscribeRun>::iterator start_run = this->
cur_range;
356 std::vector<UniscribeRun>::iterator last_run = this->
cur_range;
358 if (start_run == this->ranges.end())
return nullptr;
362 if (this->cur_range_offset != 0) {
363 std::vector<int> dx(start_run->len);
364 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]);
366 for (std::vector<int>::const_iterator c = dx.begin() + this->cur_range_offset; c != dx.end(); c++) {
373 while (last_run != this->ranges.end() && cur_width <= max_width) {
374 cur_width += last_run->total_advance;
379 int remaining_offset = (last_run - 1)->len + 1;
380 int whitespace_count = 0;
381 if (cur_width > max_width) {
382 std::vector<SCRIPT_LOGATTR> log_attribs;
385 int width_avail = max_width;
388 int last_cluster = this->cur_range_offset + 1;
389 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
390 log_attribs.resize(r->pos - start_run->pos + r->len);
391 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;
393 std::vector<int> dx(r->len);
394 ScriptGetLogicalWidths(&r->sa, r->len, (
int)r->glyphs.size(), &r->advances[0], &r->char_to_glyph[0], &r->vis_attribs[0], &dx[0]);
397 for (
int c = start_offs; c < r->len && width_avail > 0; c++, num_chars++) {
398 if (c > start_offs && log_attribs[num_chars].fCharStop) last_cluster = num_chars;
399 width_avail -= dx[c];
406 while (--num_chars > this->cur_range_offset && !log_attribs[num_chars].fSoftBreak && !log_attribs[num_chars].fWhiteSpace) {}
408 if (num_chars == this->cur_range_offset) {
410 num_chars = last_cluster;
414 while (num_chars - 1 > this->cur_range_offset && log_attribs[num_chars - 1].fWhiteSpace) num_chars--;
416 while (num_chars + whitespace_count < (
int)log_attribs.size() && log_attribs[num_chars + whitespace_count].fWhiteSpace) whitespace_count++;
419 for (std::vector<UniscribeRun>::iterator run = start_run; run != last_run; run++) {
420 num_chars -= run->len;
422 if (num_chars <= 0) {
423 remaining_offset = num_chars + run->len + 1;
425 assert(remaining_offset - 1 > 0);
432 std::vector<BYTE> bidi_level;
433 for (std::vector<UniscribeRun>::iterator r = start_run; r != last_run; r++) {
434 bidi_level.push_back(r->sa.s.uBidiLevel);
436 std::vector<INT> vis_to_log(bidi_level.size());
437 if (FAILED(ScriptLayout((
int)bidi_level.size(), &bidi_level[0], &vis_to_log[0],
nullptr)))
return nullptr;
440 std::unique_ptr<UniscribeLine> line = std::make_unique<UniscribeLine>();
443 for (std::vector<INT>::iterator l = vis_to_log.begin(); l != vis_to_log.end(); l++) {
444 std::vector<UniscribeRun>::iterator i_run = start_run + *l;
448 if (i_run == last_run - 1 && remaining_offset <= (last_run - 1)->len) {
449 run.len = remaining_offset - 1;
453 if (i_run == start_run && this->cur_range_offset > 0) {
454 assert(run.len - this->cur_range_offset > 0);
461 line->emplace_back(run, cur_pos);
462 cur_pos += run.total_advance;
465 if (remaining_offset + whitespace_count - 1 < (last_run - 1)->len) {
467 this->cur_range_offset = remaining_offset + whitespace_count - 1;
468 this->cur_range = last_run - 1;
469 assert(this->cur_range->len > this->cur_range_offset);
471 this->cur_range_offset = 0;
472 this->cur_range = last_run;
485 for (
const auto &run : *
this) {
486 leading = std::max(leading, run.GetLeading());
499 for (
const auto &run : *
this) {
500 length += run.GetAdvance();
506UniscribeParagraphLayout::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)
508 this->num_glyphs = (int)glyphs.size();
509 this->positions.reserve(this->num_glyphs);
512 for (
int i = 0; i < this->num_glyphs; i++) {
513 int x_advance = range.advances[i];
514 this->positions.emplace_back(range.offsets[i].du + advance, range.offsets[i].du + advance + x_advance - 1, range.offsets[i].dv);
516 advance += x_advance;
520UniscribeParagraphLayout::UniscribeVisualRun::UniscribeVisualRun(UniscribeVisualRun&& other) noexcept
521 : glyphs(std::move(other.glyphs)), positions(std::move(other.positions)), char_to_glyph(std::move(other.char_to_glyph)),
522 start_pos(other.start_pos), total_advance(other.total_advance), num_glyphs(other.num_glyphs), font(other.font),
523 glyph_to_char(std::move(other.glyph_to_char))
527std::span<const int> UniscribeParagraphLayout::UniscribeVisualRun::GetGlyphToCharMap()
const
529 if (this->glyph_to_char.empty()) {
530 this->glyph_to_char.resize(this->GetGlyphCount());
534 for (
int c = 0; c < (int)this->char_to_glyph.size(); c++) {
536 if (this->glyph_to_char[this->char_to_glyph[c]] == 0) this->glyph_to_char[this->char_to_glyph[c]] = c + this->start_pos;
540 int last_char = this->glyph_to_char[0];
541 for (
int g = 0; g < this->GetGlyphCount(); g++) {
542 if (this->glyph_to_char[g] != 0) last_char = this->glyph_to_char[g];
543 this->glyph_to_char[g] = last_char;
547 return this->glyph_to_char;
553 this->utf16_to_utf8.clear();
554 this->str_info.clear();
559 std::vector<wchar_t> utf16_str;
561 for (
auto it = view.begin(), end = view.end(); it != end; ++it) {
562 size_t idx = it.GetByteOffset();
565 utf16_str.push_back((
wchar_t)c);
568 utf16_str.push_back((
wchar_t)(0xD800 + ((c - 0x10000) >> 10)));
569 utf16_str.push_back((
wchar_t)(0xDC00 + ((c - 0x10000) & 0x3FF)));
570 this->utf16_to_utf8.push_back(idx);
572 this->utf16_to_utf8.push_back(idx);
574 this->utf16_to_utf8.push_back(s.size());
577 this->str_info.resize(utf16_to_utf8.size());
579 if (!utf16_str.empty()) {
583 for (std::vector<SCRIPT_ITEM>::const_iterator run = runs.begin(); !runs.empty() && run != runs.end() - 1; run++) {
585 int len = (run + 1)->iCharPos - run->iCharPos;
586 std::vector<SCRIPT_LOGATTR> attr(len);
587 ScriptBreak(&utf16_str[run->iCharPos], len, &run->a, &attr[0]);
590 for (
size_t c = 0; c < attr.size(); c++) {
592 this->str_info[c + run->iCharPos].word_stop = attr[c].fWordStop || c == 0;
593 this->str_info[c + run->iCharPos].char_stop = attr[c].fCharStop;
599 this->str_info.back().char_stop =
true;
600 this->str_info.back().word_stop =
true;
606 size_t utf16_pos = 0;
607 for (
size_t i = 0; i < this->utf16_to_utf8.size(); i++) {
608 if (this->utf16_to_utf8[i] == pos) {
615 while (utf16_pos > 0 && !this->str_info[utf16_pos].char_stop) utf16_pos--;
616 this->cur_pos = utf16_pos;
618 return this->utf16_to_utf8[this->cur_pos];
623 assert(this->cur_pos <= this->utf16_to_utf8.size());
626 if (this->cur_pos == this->utf16_to_utf8.size())
return END;
630 }
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));
632 return this->cur_pos == this->utf16_to_utf8.size() ? END : this->utf16_to_utf8[this->cur_pos];
637 assert(this->cur_pos <= this->utf16_to_utf8.size());
640 if (this->cur_pos == 0)
return END;
644 }
while (this->cur_pos > 0 && (what == ITER_WORD ? !this->str_info[this->cur_pos].word_stop : !this->str_info[this->cur_pos].char_stop));
646 return this->utf16_to_utf8[this->cur_pos];
Font cache for basic fonts.
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 bool IsBuiltInFont()=0
Is this a built-in sprite font?
virtual uint GetGlyphWidth(GlyphID key)=0
Get the width of the glyph with the given key.
virtual GlyphID MapCharToGlyph(char32_t key)=0
Map a character into a glyph.
FontSize GetSize() const
Get the FontSize of the font.
Container with information about a font.
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.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
uint32_t GlyphID
Glyphs are characters from a font.
std::vector< std::pair< int, Font > > FontMap
Mapping from index to font.
char32_t SwapRtlPairedCharacters(char32_t c)
Swap paired brackets for fallback RTL layouting.
FontSize
Available font sizes.
const LanguageMetadata * _current_language
The currently loaded language.
char32_t Utf16DecodeChar(const uint16_t *c)
Decode an UTF-16 character.
bool Utf16IsLeadSurrogate(uint c)
Is the given character a lead surrogate code point?
static std::map< FontIndex, SCRIPT_CACHE > _script_cache
Uniscribe cache for internal font information, cleared when OTTD changes fonts.
static std::vector< SCRIPT_ITEM > UniscribeItemizeString(UniscribeParagraphLayoutFactory::CharType *buff, int32_t length)
Break a string into language formatting ranges.
static HFONT HFontFromFont(const Font &font)
Load the matching native Windows font.
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.
void FallbackShape(const UniscribeParagraphLayoutFactory::CharType *buff)
Manually shape a run for built-in non-truetype fonts.
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