19#include "3rdparty/icu/scriptrun.h"
21#include <unicode/ubidi.h>
22#include <unicode/brkiter.h>
64 std::vector<GlyphID> glyphs;
65 std::vector<Position> positions;
66 std::vector<int> glyph_to_char;
74 std::span<const GlyphID> GetGlyphs()
const override {
return this->glyphs; }
75 std::span<const Position> GetPositions()
const override {
return this->positions; }
76 std::span<const int> GetGlyphToCharMap()
const override {
return this->glyph_to_char; }
78 const Font *GetFont()
const override {
return this->font; }
79 int GetLeading()
const override {
return this->font->
fc->
GetHeight(); }
80 int GetGlyphCount()
const override {
return this->glyphs.size(); }
81 int GetAdvance()
const {
return this->total_advance; }
89 int CountRuns()
const override {
return (uint)this->size(); }
90 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
92 int GetInternalCharLength(
char32_t c)
const override
95 return c >= 0x010000U ? 2 : 1;
100 std::vector<ICURun> runs;
103 std::vector<ICURun>::iterator current_run;
107 ICUParagraphLayout(std::vector<ICURun> &&runs, UChar *buff,
size_t buff_length) : runs(std::move(runs)), buff(buff), buff_length(buff_length)
114 void Reflow()
override
116 this->current_run = this->runs.begin();
117 this->partial_offset = 0;
120 std::unique_ptr<const Line> NextLine(
int max_width)
override;
132 glyphs(run.glyphs), glyph_to_char(run.glyph_to_char), total_advance(run.total_advance), font(run.font)
136 this->positions.reserve(run.
positions.size());
140 this->positions.emplace_back(pos.left + x, pos.right + x, pos.top);
152 auto hbfont = hb_ft_font_create_referenced(*(
static_cast<const FT_Face *
>(font->fc->GetOSHandle())));
154 hb_ft_font_set_load_flags(hbfont, GetFontAAState() ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
157 auto hbbuf = hb_buffer_create();
158 hb_buffer_add_utf16(hbbuf,
reinterpret_cast<uint16_t *
>(buff), buff_length, this->start, this->length);
161 hb_buffer_set_direction(hbbuf, (this->level & 1) == 1 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
162 hb_buffer_set_script(hbbuf, hb_script_from_string(uscript_getShortName(this->script), -1));
164 hb_buffer_set_cluster_level(hbbuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES);
167 hb_shape(hbfont, hbbuf,
nullptr, 0);
169 unsigned int glyph_count;
170 auto glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count);
171 auto glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count);
174 this->glyphs.clear();
175 this->glyph_to_char.clear();
176 this->positions.clear();
177 this->advance.clear();
180 this->glyphs.reserve(glyph_count);
181 this->glyph_to_char.reserve(glyph_count);
182 this->positions.reserve(glyph_count);
183 this->advance.reserve(glyph_count);
186 hb_position_t advance = 0;
187 for (
unsigned int i = 0; i < glyph_count; i++) {
190 if (buff[glyph_info[i].cluster] >= SCC_SPRITE_START && buff[glyph_info[i].cluster] <= SCC_SPRITE_END && glyph_info[i].codepoint == 0) {
191 auto glyph = this->font->fc->MapCharToGlyph(buff[glyph_info[i].cluster]);
192 x_advance = this->font->fc->GetGlyphWidth(glyph);
193 this->glyphs.push_back(glyph);
194 this->positions.emplace_back(advance, advance + x_advance - 1, (this->font->fc->GetHeight() -
ScaleSpriteTrad(FontCache::GetDefaultFontHeight(this->font->fc->GetSize()))) / 2);
196 x_advance = glyph_pos[i].x_advance /
FONT_SCALE;
197 this->glyphs.push_back(glyph_info[i].codepoint);
198 this->positions.emplace_back(glyph_pos[i].x_offset /
FONT_SCALE + advance, glyph_pos[i].x_offset /
FONT_SCALE + advance + x_advance - 1, glyph_pos[i].y_offset /
FONT_SCALE);
201 this->glyph_to_char.push_back(glyph_info[i].cluster);
202 this->advance.push_back(x_advance);
203 advance += x_advance;
207 this->total_advance = advance;
209 hb_buffer_destroy(hbbuf);
210 hb_font_destroy(hbfont);
220 for (
const auto &run : *
this) {
221 leading = std::max(leading, run.GetLeading());
234 for (
const auto &run : *
this) {
235 length += run.GetAdvance();
252 auto ubidi = ubidi_open();
256 UErrorCode err = U_ZERO_ERROR;
257 ubidi_setPara(ubidi, buff, length, parLevel,
nullptr, &err);
258 if (U_FAILURE(err)) {
259 Debug(fontcache, 0,
"Failed to set paragraph: {}", u_errorName(err));
261 return std::vector<ICURun>();
264 int32_t count = ubidi_countRuns(ubidi, &err);
265 if (U_FAILURE(err)) {
266 Debug(fontcache, 0,
"Failed to count runs: {}", u_errorName(err));
268 return std::vector<ICURun>();
271 std::vector<ICURun> runs;
275 int32_t logical_pos = 0;
276 while (
static_cast<size_t>(logical_pos) < length) {
277 auto start_pos = logical_pos;
281 ubidi_getLogicalRun(ubidi, start_pos, &logical_pos, &level);
283 runs.emplace_back(start_pos, logical_pos - start_pos, level);
286 assert(
static_cast<size_t>(count) == runs.size());
302std::vector<ICURun>
ItemizeScript(UChar *buff,
size_t length, std::vector<ICURun> &runs_current)
304 std::vector<ICURun> runs;
305 icu::ScriptRun script_itemizer(buff, length);
308 auto cur_run = runs_current.begin();
310 while (cur_pos < script_itemizer.getScriptEnd() && cur_run != runs_current.end()) {
311 int stop_pos = std::min(script_itemizer.getScriptEnd(), cur_run->start + cur_run->length);
312 assert(stop_pos - cur_pos > 0);
314 runs.emplace_back(cur_pos, stop_pos - cur_pos, cur_run->level, script_itemizer.getScriptCode());
316 if (stop_pos == cur_run->start + cur_run->length) cur_run++;
320 if (!script_itemizer.next())
break;
337 std::vector<ICURun> runs;
340 auto cur_run = runs_current.begin();
341 for (
auto const &[position, font] : font_mapping) {
342 while (cur_pos < position && cur_run != runs_current.end()) {
343 int stop_pos = std::min(position, cur_run->start + cur_run->length);
344 assert(stop_pos - cur_pos > 0);
346 runs.emplace_back(cur_pos, stop_pos - cur_pos, cur_run->level, cur_run->script, font);
348 if (stop_pos == cur_run->start + cur_run->length) cur_run++;
356 std::unique_ptr<ParagraphLayouter> ICUParagraphLayoutFactory::GetParagraphLayout(UChar *buff, UChar *buff_end,
FontMap &font_mapping)
358 size_t length = buff_end - buff;
360 if (length == 0)
return nullptr;
363 for (
auto const &[position, font] : font_mapping) {
364 if (font->fc->IsBuiltInFont())
return nullptr;
371 if (runs.empty())
return nullptr;
373 for (
auto &run : runs) {
374 run.Shape(buff, length);
377 return std::make_unique<ICUParagraphLayout>(std::move(runs), buff, length);
380 std::unique_ptr<icu::BreakIterator> ICUParagraphLayoutFactory::break_iterator;
388 UErrorCode status = U_ZERO_ERROR;
389 ICUParagraphLayoutFactory::break_iterator.reset(icu::BreakIterator::createLineInstance(locale, status));
390 assert(U_SUCCESS(status));
399 assert(ICUParagraphLayoutFactory::break_iterator !=
nullptr);
401 return std::unique_ptr<icu::BreakIterator>(ICUParagraphLayoutFactory::break_iterator->clone());
404std::unique_ptr<const ICUParagraphLayout::Line> ICUParagraphLayout::NextLine(
int max_width)
406 std::vector<ICURun>::iterator start_run = this->current_run;
407 std::vector<ICURun>::iterator last_run = this->current_run;
409 if (start_run == this->runs.end())
return nullptr;
414 if (this->partial_offset > 0) {
415 if ((start_run->level & 1) == 0) {
416 for (
size_t i = this->partial_offset; i < start_run->advance.size(); i++) {
417 cur_width += start_run->advance[i];
420 for (
int i = 0; i < this->partial_offset; i++) {
421 cur_width += start_run->advance[i];
428 while (last_run != this->runs.end() && cur_width < max_width) {
429 cur_width += last_run->total_advance;
434 int new_partial_length = 0;
435 if (cur_width > max_width) {
438 break_iterator->setText(icu::UnicodeString(this->buff, this->buff_length));
440 auto overflow_run = last_run - 1;
444 if ((overflow_run->level & 1) == 0) {
446 for (index = overflow_run->glyphs.size(); index > 0; index--) {
447 cur_width -= overflow_run->advance[index - 1];
448 if (cur_width <= max_width)
break;
453 for (index = 0; index < overflow_run->glyphs.size(); index++) {
454 cur_width -= overflow_run->advance[index];
455 if (cur_width <= max_width)
break;
460 auto char_pos = overflow_run->glyph_to_char[index];
463 int32_t break_pos = break_iterator->preceding(char_pos + 1);
464 auto overflow_run_start = overflow_run->start;
465 if (overflow_run == start_run) overflow_run_start += this->partial_offset;
466 if (break_pos != icu::BreakIterator::DONE && break_pos > overflow_run_start) {
468 new_partial_length = break_pos - overflow_run_start;
469 }
else if (overflow_run != start_run) {
477 new_partial_length = char_pos - overflow_run_start;
482 std::vector<UBiDiLevel> bidi_level;
483 for (
auto run = start_run; run != last_run; run++) {
484 bidi_level.push_back(run->level);
486 std::vector<int32_t> vis_to_log(bidi_level.size());
487 ubidi_reorderVisual(bidi_level.data(), bidi_level.size(), vis_to_log.data());
490 std::unique_ptr<ICULine> line = std::make_unique<ICULine>();
493 for (
auto &i : vis_to_log) {
494 auto i_run = start_run + i;
498 if (i_run == last_run - 1 && new_partial_length > 0) {
499 if (i_run == start_run && this->partial_offset > 0) {
500 assert(run.
length > this->partial_offset);
501 run.
start += this->partial_offset;
502 run.
length -= this->partial_offset;
505 assert(run.
length > new_partial_length);
506 run.
length = new_partial_length;
508 run.
Shape(this->buff, this->buff_length);
509 }
else if (i_run == start_run && this->partial_offset > 0) {
510 assert(run.
length > this->partial_offset);
512 run.
start += this->partial_offset;
513 run.
length -= this->partial_offset;
515 run.
Shape(this->buff, this->buff_length);
519 line->emplace_back(std::move(run), cur_pos);
520 cur_pos += total_advance;
523 if (new_partial_length > 0) {
524 this->current_run = last_run - 1;
525 if (this->current_run != start_run) this->partial_offset = 0;
526 this->partial_offset += new_partial_length;
528 this->current_run = last_run;
529 this->partial_offset = 0;
535 size_t ICUParagraphLayoutFactory::AppendToBuffer(UChar *buff,
const UChar *buffer_last,
char32_t c)
537 assert(buff < buffer_last);
540 UErrorCode err = U_ZERO_ERROR;
541 u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
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 GetLeading() const override
Get the height of the line.
int GetWidth() const override
Get the width of this line.
Visual run contains data about the bit of text with the same font.
ICUVisualRun(const ICURun &run, int x)
Constructor for a new ICUVisualRun.
Wrapper for doing layouts with ICU.
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(category, level, format_string,...)
Ouptut a line of debugging information.
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.