20#include "3rdparty/icu/scriptrun.h"
22#include <unicode/ubidi.h>
23#include <unicode/brkiter.h>
65 std::vector<GlyphID> glyphs;
66 std::vector<Position> positions;
67 std::vector<int> glyph_to_char;
75 std::span<const GlyphID>
GetGlyphs()
const override {
return this->glyphs; }
76 std::span<const Position>
GetPositions()
const override {
return this->positions; }
82 int GetAdvance()
const {
return this->total_advance; }
90 size_t CountRuns()
const override {
return this->size(); }
96 return c >= 0x010000U ? 2 : 1;
101 std::vector<ICURun> runs;
104 std::vector<ICURun>::iterator current_run;
108 ICUParagraphLayout(std::vector<ICURun> &&runs, UChar *buff,
size_t buff_length) : runs(std::move(runs)), buff(buff), buff_length(buff_length)
113 ~ICUParagraphLayout()
override =
default;
117 this->current_run = this->runs.begin();
118 this->partial_offset = 0;
121 std::unique_ptr<const Line>
NextLine(
int max_width)
override;
133 glyphs(run.glyphs), glyph_to_char(run.glyph_to_char), total_advance(run.total_advance), font(run.font)
137 this->positions.reserve(run.
positions.size());
141 this->positions.emplace_back(pos.left + x, pos.right + x, pos.top);
153 auto hbfont = hb_ft_font_create_referenced(*(
static_cast<const FT_Face *
>(
font->fc->GetOSHandle())));
155 hb_ft_font_set_load_flags(hbfont, GetFontAAState() ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
158 auto hbbuf = hb_buffer_create();
159 hb_buffer_add_utf16(hbbuf,
reinterpret_cast<uint16_t *
>(buff), buff_length, this->
start, this->
length);
162 hb_buffer_set_direction(hbbuf, (this->
level & 1) == 1 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
163 hb_buffer_set_script(hbbuf, hb_script_from_string(uscript_getShortName(this->
script), -1));
164 hb_buffer_set_language(hbbuf, hb_language_from_string(
_current_language->isocode, -1));
165 hb_buffer_set_cluster_level(hbbuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES);
168 hb_shape(hbfont, hbbuf,
nullptr, 0);
170 unsigned int glyph_count;
171 auto glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count);
172 auto glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count);
181 this->
glyphs.reserve(glyph_count);
184 this->
advance.reserve(glyph_count);
188 for (
unsigned int i = 0; i < glyph_count; i++) {
191 if (buff[glyph_info[i].cluster] >= SCC_SPRITE_START && buff[glyph_info[i].cluster] <= SCC_SPRITE_END && glyph_info[i].codepoint == 0) {
192 auto glyph = this->
font->fc->MapCharToGlyph(buff[glyph_info[i].cluster]);
193 x_advance = this->
font->fc->GetGlyphWidth(glyph);
194 this->
glyphs.push_back(glyph);
197 x_advance = glyph_pos[i].x_advance /
FONT_SCALE;
198 this->
glyphs.push_back(glyph_info[i].codepoint);
203 this->advance.push_back(x_advance);
210 hb_buffer_destroy(hbbuf);
211 hb_font_destroy(hbfont);
221 for (
const auto &run : *
this) {
222 leading = std::max(leading, run.GetLeading());
235 for (
const auto &run : *
this) {
236 length += run.GetAdvance();
257 UErrorCode err = U_ZERO_ERROR;
258 ubidi_setPara(ubidi.get(), buff, length, parLevel,
nullptr, &err);
259 if (U_FAILURE(err)) {
264 int32_t count = ubidi_countRuns(ubidi.get(), &err);
265 if (U_FAILURE(err)) {
270 std::vector<ICURun> runs;
274 int32_t logical_pos = 0;
275 while (
static_cast<size_t>(logical_pos) < length) {
276 auto start_pos = logical_pos;
280 ubidi_getLogicalRun(ubidi.get(), start_pos, &logical_pos, &level);
282 runs.emplace_back(start_pos, logical_pos - start_pos, level);
285 assert(
static_cast<size_t>(count) == runs.size());
300std::vector<ICURun>
ItemizeScript(UChar *buff,
size_t length, std::vector<ICURun> &runs_current)
302 std::vector<ICURun> runs;
303 icu::ScriptRun script_itemizer(buff, length);
306 auto cur_run = runs_current.begin();
308 while (cur_pos < script_itemizer.getScriptEnd() && cur_run != runs_current.end()) {
309 int stop_pos = std::min(script_itemizer.getScriptEnd(), cur_run->start + cur_run->length);
310 assert(stop_pos - cur_pos > 0);
312 runs.emplace_back(cur_pos, stop_pos - cur_pos, cur_run->level, script_itemizer.getScriptCode());
314 if (stop_pos == cur_run->start + cur_run->length) cur_run++;
318 if (!script_itemizer.next())
break;
335 std::vector<ICURun> runs;
338 auto cur_run = runs_current.begin();
339 for (
auto const &[position, font] : font_mapping) {
340 while (cur_pos < position && cur_run != runs_current.end()) {
341 int stop_pos = std::min(position, cur_run->start + cur_run->length);
342 assert(stop_pos - cur_pos > 0);
344 runs.emplace_back(cur_pos, stop_pos - cur_pos, cur_run->level, cur_run->script, font);
346 if (stop_pos == cur_run->start + cur_run->length) cur_run++;
354 std::unique_ptr<ParagraphLayouter> ICUParagraphLayoutFactory::GetParagraphLayout(UChar *buff, UChar *buff_end,
FontMap &font_mapping)
356 size_t length = buff_end - buff;
358 if (length == 0)
return nullptr;
361 for (
auto const &[position, font] : font_mapping) {
362 if (font->fc->IsBuiltInFont())
return nullptr;
369 if (runs.empty())
return nullptr;
371 for (
auto &run : runs) {
372 run.Shape(buff, length);
375 return std::make_unique<ICUParagraphLayout>(std::move(runs), buff, length);
378 std::unique_ptr<icu::BreakIterator> ICUParagraphLayoutFactory::break_iterator;
386 UErrorCode status = U_ZERO_ERROR;
387 ICUParagraphLayoutFactory::break_iterator.reset(icu::BreakIterator::createLineInstance(locale, status));
388 assert(U_SUCCESS(status));
397 assert(ICUParagraphLayoutFactory::break_iterator !=
nullptr);
399 return std::unique_ptr<icu::BreakIterator>(ICUParagraphLayoutFactory::break_iterator->clone());
404 std::vector<ICURun>::iterator start_run = this->current_run;
405 std::vector<ICURun>::iterator last_run = this->current_run;
407 if (start_run == this->runs.end())
return nullptr;
412 if (this->partial_offset > 0) {
413 if ((start_run->level & 1) == 0) {
414 for (
size_t i = this->partial_offset; i < start_run->advance.size(); i++) {
415 cur_width += start_run->advance[i];
418 for (
int i = 0; i < this->partial_offset; i++) {
419 cur_width += start_run->advance[i];
426 while (last_run != this->runs.end() && cur_width < max_width) {
427 cur_width += last_run->total_advance;
432 int new_partial_length = 0;
433 if (cur_width > max_width) {
436 icu::UnicodeString text(this->buff, this->buff_length);
437 break_iterator->setText(text);
439 auto overflow_run = last_run - 1;
443 if ((overflow_run->level & 1) == 0) {
445 for (index = overflow_run->glyphs.size(); index > 0; ) {
447 cur_width -= overflow_run->advance[index];
448 if (cur_width <= max_width)
break;
452 for (index = 0; index < overflow_run->glyphs.size(); index++) {
453 cur_width -= overflow_run->advance[index];
454 if (cur_width <= max_width)
break;
459 auto char_pos = overflow_run->glyph_to_char[index];
462 int32_t break_pos = break_iterator->preceding(char_pos + 1);
463 auto overflow_run_start = overflow_run->start;
464 if (overflow_run == start_run) overflow_run_start += this->partial_offset;
465 if (break_pos != icu::BreakIterator::DONE && break_pos > overflow_run_start) {
467 new_partial_length = break_pos - overflow_run_start;
468 }
else if (overflow_run != start_run) {
476 new_partial_length = char_pos - overflow_run_start;
481 std::vector<UBiDiLevel> bidi_level;
482 for (
auto run = start_run; run != last_run; run++) {
483 bidi_level.push_back(run->level);
485 std::vector<int32_t> vis_to_log(bidi_level.size());
486 ubidi_reorderVisual(bidi_level.data(), bidi_level.size(), vis_to_log.data());
489 std::unique_ptr<ICULine> line = std::make_unique<ICULine>();
492 for (
auto &i : vis_to_log) {
493 auto i_run = start_run + i;
497 if (i_run == last_run - 1 && new_partial_length > 0) {
498 if (i_run == start_run && this->partial_offset > 0) {
499 assert(run.
length > this->partial_offset);
500 run.
start += this->partial_offset;
501 run.
length -= this->partial_offset;
504 assert(run.
length > new_partial_length);
505 run.
length = new_partial_length;
507 run.
Shape(this->buff, this->buff_length);
508 }
else if (i_run == start_run && this->partial_offset > 0) {
509 assert(run.
length > this->partial_offset);
511 run.
start += this->partial_offset;
512 run.
length -= this->partial_offset;
514 run.
Shape(this->buff, this->buff_length);
518 line->emplace_back(std::move(run), cur_pos);
519 cur_pos += total_advance;
522 if (new_partial_length > 0) {
523 this->current_run = last_run - 1;
524 if (this->current_run != start_run) this->partial_offset = 0;
525 this->partial_offset += new_partial_length;
527 this->current_run = last_run;
528 this->partial_offset = 0;
534 size_t ICUParagraphLayoutFactory::AppendToBuffer(UChar *buff,
const UChar *buffer_last,
char32_t c)
536 assert(buff < buffer_last);
539 UErrorCode err = U_ZERO_ERROR;
540 u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
Helper for std::unique_ptr to use an arbitrary function as the deleter.
std::unique_ptr< T, DeleterFromFunc< Tfunc > > AutoRelease
Specialisation of std::unique_ptr for objects which must be deleted by calling a function.
int GetHeight() const
Get the height of the font.
Container with information about a font.
FontCache * fc
The font we are using.
static std::unique_ptr< icu::BreakIterator > GetBreakIterator()
Get a thread-safe line break iterator.
static void InitializeLayouter()
Initialize data needed for the ICU layouter.
A single line worth of VisualRuns.
int GetInternalCharLength(char32_t c) const override
Get the number of elements the given character occupies in the underlying text buffer of the Layouter...
const VisualRun & GetVisualRun(size_t run) const override
Get a reference to the given run.
int GetLeading() const override
Get the height of the line.
int GetWidth() const override
Get the width of this line.
size_t CountRuns() const override
Get the number of runs in this line.
std::span< const Position > GetPositions() const override
Get the positions for each of the glyphs.
ICUVisualRun(const ICURun &run, int x)
Constructor for a new ICUVisualRun.
int GetLeading() const override
Get the font leading, or distance between the baselines of consecutive lines.
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.
Wrapper for doing layouts with ICU.
void Reflow() override
Reset the position to the start of the paragraph.
std::unique_ptr< const Line > NextLine(int max_width) override
Construct a new line with a maximum width.
Helper class to store the information of all the runs of a paragraph in.
UScriptCode script
Script of the run.
std::vector< int > glyph_to_char
The mapping from glyphs to characters. Valid after Shape() is called.
void Shape(UChar *buff, size_t length)
Shape a single run.
std::vector< GlyphID > glyphs
The glyphs of the run. Valid after Shape() is called.
int total_advance
The total advance of the run. Valid after Shape() is called.
Font * font
Font of the run.
std::vector< int > advance
The advance (width) of the glyphs. Valid after Shape() is called.
int length
Length of the run in the buffer.
int start
Start of the run in the buffer.
std::vector< ParagraphLayouter::Position > positions
The positions of the glyphs. Valid after Shape() is called.
UBiDiLevel level
Embedding level of the run.
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.
Control codes that are embedded in the translation strings.
Functions related to debugging.
#define Debug(facility, severity, format_string,...)
Output a line of debugging information.
@ Fontcache
Fontcache message facility.
@ Critical
Critical, user should know about this.
std::vector< std::pair< int, Font * > > FontMap
Mapping from index to font.
std::vector< ICURun > ItemizeStyle(std::vector< ICURun > &runs_current, FontMap &font_mapping)
Itemize the string into runs per style, based on the previous created runs.
constexpr float FONT_SCALE
HarfBuzz FreeType integration sets the font scaling, which is always in 1/64th of a pixel.
std::vector< ICURun > ItemizeBidi(UChar *buff, size_t length)
Itemize the string into runs per embedding level.
std::vector< ICURun > ItemizeScript(UChar *buff, size_t length, std::vector< ICURun > &runs_current)
Itemize the string into runs per script, based on the previous created runs.
Functions related to laying out the texts with ICU.
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.
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.
Functions related to zooming.
int ScaleSpriteTrad(int value)
Scale traditional pixel dimensions to GUI zoom level, for drawing sprites.