22#include "3rdparty/icu/scriptrun.h"
24#include <unicode/ubidi.h>
25#include <unicode/brkiter.h>
68 std::vector<GlyphID> glyphs;
69 std::vector<Position> positions;
70 std::vector<int> glyph_to_char;
78 std::span<const GlyphID> GetGlyphs()
const override {
return this->glyphs; }
79 std::span<const Position> GetPositions()
const override {
return this->positions; }
80 std::span<const int> GetGlyphToCharMap()
const override {
return this->glyph_to_char; }
82 const Font &GetFont()
const override {
return this->font; }
84 int GetGlyphCount()
const override {
return this->glyphs.size(); }
85 int GetAdvance()
const {
return this->total_advance; }
93 int CountRuns()
const override {
return (uint)this->size(); }
94 const VisualRun &GetVisualRun(
int run)
const override {
return this->at(run); }
96 int GetInternalCharLength(
char32_t c)
const override
99 return c >= 0x010000U ? 2 : 1;
104 std::vector<ICURun> runs;
107 std::vector<ICURun>::iterator current_run;
111 ICUParagraphLayout(std::vector<ICURun> &&runs, UChar *buff,
size_t buff_length) : runs(std::move(runs)), buff(buff), buff_length(buff_length)
118 void Reflow()
override
120 this->current_run = this->runs.begin();
121 this->partial_offset = 0;
124 std::unique_ptr<const Line> NextLine(
int max_width)
override;
136 glyphs(run.glyphs), glyph_to_char(run.glyph_to_char), total_advance(run.total_advance), font(run.font)
140 this->positions.reserve(run.
positions.size());
144 this->positions.emplace_back(pos.left + x, pos.right + x, pos.top);
155 FontCache &fc = this->font.GetFontCache();
157 this->glyphs.reserve(this->length);
158 this->glyph_to_char.reserve(this->length);
161 for (
int i = this->start; i < this->start + this->length; i +=
Utf16IsLeadSurrogate(buff[i]) ? 2 : 1) {
165 this->glyph_to_char.push_back(i);
169 if (this->level & 1) {
170 std::reverse(std::begin(this->glyphs), std::end(this->glyphs));
171 std::reverse(std::begin(this->glyph_to_char), std::end(this->glyph_to_char));
174 this->positions.reserve(this->glyphs.size());
177 int y_offset = fc.GetGlyphYOffset();
179 for (
const GlyphID glyph : this->glyphs) {
181 this->positions.emplace_back(advance, advance + x_advance - 1, y_offset);
182 this->advance.push_back(x_advance);
183 advance += x_advance;
185 this->total_advance = advance;
196 FontCache &fc = this->font.GetFontCache();
199 this->glyphs.clear();
200 this->glyph_to_char.clear();
201 this->positions.clear();
202 this->advance.clear();
205 this->FallbackShape(buff);
209 auto hbfont = hb_ft_font_create_referenced(*(
static_cast<const FT_Face *
>(fc.
GetOSHandle())));
211 hb_ft_font_set_load_flags(hbfont, GetFontAAState() ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
214 auto hbbuf = hb_buffer_create();
215 hb_buffer_add_utf16(hbbuf,
reinterpret_cast<uint16_t *
>(buff), buff_length, this->start, this->length);
218 hb_buffer_set_direction(hbbuf, (this->level & 1) == 1 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
219 hb_buffer_set_script(hbbuf, hb_script_from_string(uscript_getShortName(this->script), -1));
221 hb_buffer_set_cluster_level(hbbuf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES);
224 hb_shape(hbfont, hbbuf,
nullptr, 0);
226 unsigned int glyph_count;
227 auto glyph_info = hb_buffer_get_glyph_infos(hbbuf, &glyph_count);
228 auto glyph_pos = hb_buffer_get_glyph_positions(hbbuf, &glyph_count);
231 this->glyphs.reserve(glyph_count);
232 this->glyph_to_char.reserve(glyph_count);
233 this->positions.reserve(glyph_count);
234 this->advance.reserve(glyph_count);
237 int y_offset = fc.GetGlyphYOffset();
238 hb_position_t advance = 0;
239 for (
unsigned int i = 0; i < glyph_count; i++) {
240 int x_advance = glyph_pos[i].x_advance /
FONT_SCALE;
241 this->glyphs.push_back(glyph_info[i].codepoint);
242 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 + y_offset);
244 this->glyph_to_char.push_back(glyph_info[i].cluster);
245 this->advance.push_back(x_advance);
246 advance += x_advance;
250 this->total_advance = advance;
252 hb_buffer_destroy(hbbuf);
253 hb_font_destroy(hbfont);
263 for (
const auto &run : *
this) {
264 leading = std::max(leading, run.GetLeading());
277 for (
const auto &run : *
this) {
278 length += run.GetAdvance();
295 auto ubidi = ubidi_open();
299 UErrorCode err = U_ZERO_ERROR;
300 ubidi_setPara(ubidi, buff, length, parLevel,
nullptr, &err);
301 if (U_FAILURE(err)) {
302 Debug(fontcache, 0,
"Failed to set paragraph: {}", u_errorName(err));
304 return std::vector<ICURun>();
307 int32_t count = ubidi_countRuns(ubidi, &err);
308 if (U_FAILURE(err)) {
309 Debug(fontcache, 0,
"Failed to count runs: {}", u_errorName(err));
311 return std::vector<ICURun>();
314 std::vector<ICURun> runs;
318 int32_t logical_pos = 0;
319 while (
static_cast<size_t>(logical_pos) < length) {
320 auto start_pos = logical_pos;
324 ubidi_getLogicalRun(ubidi, start_pos, &logical_pos, &level);
326 runs.emplace_back(start_pos, logical_pos - start_pos, level, USCRIPT_UNKNOWN,
Font{});
329 assert(
static_cast<size_t>(count) == runs.size());
345std::vector<ICURun>
ItemizeScript(UChar *buff,
size_t length, std::vector<ICURun> &runs_current)
347 std::vector<ICURun> runs;
348 icu::ScriptRun script_itemizer(buff, length);
351 auto cur_run = runs_current.begin();
353 while (cur_pos < script_itemizer.getScriptEnd() && cur_run != runs_current.end()) {
354 int stop_pos = std::min(script_itemizer.getScriptEnd(), cur_run->start + cur_run->length);
355 assert(stop_pos - cur_pos > 0);
357 runs.emplace_back(cur_pos, stop_pos - cur_pos, cur_run->level, script_itemizer.getScriptCode(),
Font{});
359 if (stop_pos == cur_run->start + cur_run->length) cur_run++;
363 if (!script_itemizer.next())
break;
380 std::vector<ICURun> runs;
383 auto cur_run = runs_current.begin();
384 for (
auto const &[position, font] : font_mapping) {
385 while (cur_pos < position && cur_run != runs_current.end()) {
386 int stop_pos = std::min(position, cur_run->start + cur_run->length);
387 assert(stop_pos - cur_pos > 0);
389 runs.emplace_back(cur_pos, stop_pos - cur_pos, cur_run->level, cur_run->script, font);
391 if (stop_pos == cur_run->start + cur_run->length) cur_run++;
399 std::unique_ptr<ParagraphLayouter> ICUParagraphLayoutFactory::GetParagraphLayout(UChar *buff, UChar *buff_end,
FontMap &font_mapping)
401 size_t length = buff_end - buff;
403 if (length == 0)
return nullptr;
409 if (runs.empty())
return nullptr;
411 for (
auto &run : runs) {
412 run.Shape(buff, length);
415 return std::make_unique<ICUParagraphLayout>(std::move(runs), buff, length);
418 std::unique_ptr<icu::BreakIterator> ICUParagraphLayoutFactory::break_iterator;
426 UErrorCode status = U_ZERO_ERROR;
427 ICUParagraphLayoutFactory::break_iterator.reset(icu::BreakIterator::createLineInstance(locale, status));
428 assert(U_SUCCESS(status));
437 assert(ICUParagraphLayoutFactory::break_iterator !=
nullptr);
439 return std::unique_ptr<icu::BreakIterator>(ICUParagraphLayoutFactory::break_iterator->clone());
442std::unique_ptr<const ICUParagraphLayout::Line> ICUParagraphLayout::NextLine(
int max_width)
444 std::vector<ICURun>::iterator start_run = this->current_run;
445 std::vector<ICURun>::iterator last_run = this->current_run;
447 if (start_run == this->runs.end())
return nullptr;
452 if (this->partial_offset > 0) {
453 if ((start_run->level & 1) == 0) {
454 for (
size_t i = this->partial_offset; i < start_run->advance.size(); i++) {
455 cur_width += start_run->advance[i];
458 for (
int i = 0; i < this->partial_offset; i++) {
459 cur_width += start_run->advance[i];
466 while (last_run != this->runs.end() && cur_width < max_width) {
467 cur_width += last_run->total_advance;
472 int new_partial_length = 0;
473 if (cur_width > max_width) {
476 break_iterator->setText(icu::UnicodeString(this->buff, this->buff_length));
478 auto overflow_run = last_run - 1;
482 if ((overflow_run->level & 1) == 0) {
484 for (index = overflow_run->glyphs.size(); index > 0; ) {
486 cur_width -= overflow_run->advance[index];
487 if (cur_width <= max_width)
break;
491 for (index = 0; index < overflow_run->glyphs.size(); index++) {
492 cur_width -= overflow_run->advance[index];
493 if (cur_width <= max_width)
break;
498 auto char_pos = overflow_run->glyph_to_char[index];
501 int32_t break_pos = break_iterator->preceding(char_pos + 1);
502 auto overflow_run_start = overflow_run->start;
503 if (overflow_run == start_run) overflow_run_start += this->partial_offset;
504 if (break_pos != icu::BreakIterator::DONE && break_pos > overflow_run_start) {
506 new_partial_length = break_pos - overflow_run_start;
507 }
else if (overflow_run != start_run) {
515 new_partial_length = char_pos - overflow_run_start;
520 std::vector<UBiDiLevel> bidi_level;
521 for (
auto run = start_run; run != last_run; run++) {
522 bidi_level.push_back(run->level);
524 std::vector<int32_t> vis_to_log(bidi_level.size());
525 ubidi_reorderVisual(bidi_level.data(), bidi_level.size(), vis_to_log.data());
528 std::unique_ptr<ICULine> line = std::make_unique<ICULine>();
531 for (
auto &i : vis_to_log) {
532 auto i_run = start_run + i;
536 if (i_run == last_run - 1 && new_partial_length > 0) {
537 if (i_run == start_run && this->partial_offset > 0) {
538 assert(run.
length > this->partial_offset);
539 run.
start += this->partial_offset;
540 run.
length -= this->partial_offset;
543 assert(run.
length > new_partial_length);
544 run.
length = new_partial_length;
546 run.
Shape(this->buff, this->buff_length);
547 }
else if (i_run == start_run && this->partial_offset > 0) {
548 assert(run.
length > this->partial_offset);
550 run.
start += this->partial_offset;
551 run.
length -= this->partial_offset;
553 run.
Shape(this->buff, this->buff_length);
557 line->emplace_back(std::move(run), cur_pos);
558 cur_pos += total_advance;
561 if (new_partial_length > 0) {
562 this->current_run = last_run - 1;
563 if (this->current_run != start_run) this->partial_offset = 0;
564 this->partial_offset += new_partial_length;
566 this->current_run = last_run;
567 this->partial_offset = 0;
573 size_t ICUParagraphLayoutFactory::AppendToBuffer(UChar *buff,
const UChar *buffer_last,
char32_t c)
575 assert(buff < buffer_last);
578 UErrorCode err = U_ZERO_ERROR;
579 u_strFromUTF32(buff, buffer_last - buff, &length, (UChar32*)&c, 1, &err);
Font cache for basic fonts.
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.
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.
Font font
Font 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.
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.
void FallbackShape(UChar *buff)
Manually shape a run for built-in non-truetype fonts.
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,...)
Output a line of debugging information.
int GetCharacterHeight(FontSize size)
Get height of a character for a given font size.
uint32_t GlyphID
Glyphs are characters from a font.
Functions related to the gfx engine.
std::vector< std::pair< int, Font > > FontMap
Mapping from index to font.
Functions related to laying out the texts as fallback.
char32_t SwapRtlPairedCharacters(char32_t c)
Swap paired brackets for fallback RTL layouting.
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.
Functions related to low-level strings.
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?
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.