2023-05-01 12:42:51 +00:00
/*
* This file is part of OpenTTD .
* OpenTTD is free software ; you can redistribute it and / or modify it under the terms of the GNU General Public License as published by the Free Software Foundation , version 2.
* OpenTTD is distributed in the hope that it will be useful , but WITHOUT ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
* See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with OpenTTD . If not , see < http : //www.gnu.org/licenses/>.
*/
2023-04-30 18:37:40 +00:00
/** @file gfx_layout_icu.cpp Handling of laying out with ICU / Harfbuzz. */
2023-05-01 12:42:51 +00:00
# include "stdafx.h"
# include "gfx_layout_icu.h"
2023-04-30 18:37:40 +00:00
# include "debug.h"
# include "strings_func.h"
# include "language.h"
# include "table/control_codes.h"
# include "zoom_func.h"
# include "3rdparty/icu/scriptrun.h"
# include <unicode/ubidi.h>
# include <unicode/brkiter.h>
# include <hb.h>
# include <hb-ft.h>
2023-05-01 12:42:51 +00:00
# include "safeguards.h"
2024-01-18 08:36:48 +00:00
/** HarfBuzz FreeType integration sets the font scaling, which is always in 1/64th of a pixel. */
2023-04-30 18:37:40 +00:00
constexpr float FONT_SCALE = 64.0 ;
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
/**
* Helper class to store the information of all the runs of a paragraph in .
*
* During itemization , more and more information is filled in .
*/
class ICURun {
public :
int start ; ///< Start of the run in the buffer.
int length ; ///< Length of the run in the buffer.
UBiDiLevel level ; ///< Embedding level of the run.
UScriptCode script ; ///< Script of the run.
Font * font ; ///< Font of the run.
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
std : : vector < GlyphID > glyphs ; ///< The glyphs of the run. Valid after Shape() is called.
std : : vector < int > advance ; ///< The advance (width) of the glyphs. Valid after Shape() is called.
std : : vector < int > glyph_to_char ; ///< The mapping from glyphs to characters. Valid after Shape() is called.
2024-01-18 21:06:43 +00:00
std : : vector < Point > positions ; ///< The positions of the glyphs. Valid after Shape() is called.
2023-05-30 19:17:30 +00:00
int total_advance = 0 ; ///< The total advance of the run. Valid after Shape() is called.
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
ICURun ( int start , int length , UBiDiLevel level , UScriptCode script = USCRIPT_UNKNOWN , Font * font = nullptr ) : start ( start ) , length ( length ) , level ( level ) , script ( script ) , font ( font ) { }
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
void Shape ( UChar * buff , size_t length ) ;
} ;
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
/**
* Wrapper for doing layouts with ICU .
*/
class ICUParagraphLayout : public ParagraphLayouter {
public :
/** Visual run contains data about the bit of text with the same font. */
class ICUVisualRun : public ParagraphLayouter : : VisualRun {
private :
std : : vector < GlyphID > glyphs ;
2024-01-18 21:06:43 +00:00
std : : vector < Point > positions ;
2023-04-30 18:37:40 +00:00
std : : vector < int > glyph_to_char ;
int total_advance ;
const Font * font ;
public :
ICUVisualRun ( const ICURun & run , int x ) ;
2024-01-12 01:39:44 +00:00
const std : : vector < GlyphID > & GetGlyphs ( ) const override { return this - > glyphs ; }
2024-01-18 21:06:43 +00:00
const std : : vector < Point > & GetPositions ( ) const override { return this - > positions ; }
2024-01-12 01:39:44 +00:00
const std : : vector < int > & GetGlyphToCharMap ( ) const override { return this - > glyph_to_char ; }
2023-04-30 18:37:40 +00:00
const Font * GetFont ( ) const override { return this - > font ; }
int GetLeading ( ) const override { return this - > font - > fc - > GetHeight ( ) ; }
int GetGlyphCount ( ) const override { return this - > glyphs . size ( ) ; }
int GetAdvance ( ) const { return this - > total_advance ; }
} ;
/** A single line worth of VisualRuns. */
class ICULine : public std : : vector < ICUVisualRun > , public ParagraphLayouter : : Line {
public :
int GetLeading ( ) const override ;
int GetWidth ( ) const override ;
int CountRuns ( ) const override { return ( uint ) this - > size ( ) ; }
const VisualRun & GetVisualRun ( int run ) const override { return this - > at ( run ) ; }
2023-05-08 17:01:06 +00:00
int GetInternalCharLength ( char32_t c ) const override
2023-04-30 18:37:40 +00:00
{
/* ICU uses UTF-16 internally which means we need to account for surrogate pairs. */
return c > = 0x010000U ? 2 : 1 ;
}
} ;
private :
std : : vector < ICURun > runs ;
UChar * buff ;
size_t buff_length ;
std : : vector < ICURun > : : iterator current_run ;
int partial_offset ;
public :
ICUParagraphLayout ( std : : vector < ICURun > & runs , UChar * buff , size_t buff_length ) : runs ( runs ) , buff ( buff ) , buff_length ( buff_length )
{
this - > Reflow ( ) ;
}
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
~ ICUParagraphLayout ( ) override { }
void Reflow ( ) override
{
this - > current_run = this - > runs . begin ( ) ;
this - > partial_offset = 0 ;
}
std : : unique_ptr < const Line > NextLine ( int max_width ) override ;
} ;
/**
* Constructor for a new ICUVisualRun .
*
* It bases all information on the ICURun , which should already be shaped .
*
* @ param run The ICURun to base the visual run on .
* @ param x The offset of the run on the line .
*/
ICUParagraphLayout : : ICUVisualRun : : ICUVisualRun ( const ICURun & run , int x ) :
glyphs ( run . glyphs ) , glyph_to_char ( run . glyph_to_char ) , total_advance ( run . total_advance ) , font ( run . font )
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
/* If there are no positions, the ICURun was not Shaped; that should never happen. */
2023-10-20 18:22:49 +00:00
assert ( ! run . positions . empty ( ) ) ;
2023-04-30 18:37:40 +00:00
this - > positions . reserve ( run . positions . size ( ) ) ;
2024-01-18 21:06:43 +00:00
/* Copy positions, moving x coordinate by x offset. */
for ( const Point & pt : run . positions ) {
this - > positions . emplace_back ( pt . x + x , pt . y ) ;
2023-04-30 18:37:40 +00:00
}
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/**
* Shape a single run .
*
* @ param buff The buffer of which a partial ( depending on start / length of the run ) will be shaped .
* @ param length The length of the buffer .
*/
2023-11-09 19:20:41 +00:00
void ICURun : : Shape ( UChar * buff , size_t buff_length )
{
2023-04-30 18:37:40 +00:00
auto hbfont = hb_ft_font_create_referenced ( * ( static_cast < const FT_Face * > ( font - > fc - > GetOSHandle ( ) ) ) ) ;
2024-01-18 08:34:56 +00:00
/* Match the flags with how we render the glyphs. */
hb_ft_font_set_load_flags ( hbfont , GetFontAAState ( this - > font - > fc - > GetSize ( ) ) ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO ) ;
2023-04-30 18:37:40 +00:00
/* ICU buffer is in UTF-16. */
auto hbbuf = hb_buffer_create ( ) ;
2023-05-08 17:01:06 +00:00
hb_buffer_add_utf16 ( hbbuf , reinterpret_cast < uint16_t * > ( buff ) , buff_length , this - > start , this - > length ) ;
2023-04-30 18:37:40 +00:00
/* Set all the properties of this segment. */
hb_buffer_set_direction ( hbbuf , ( this - > level & 1 ) = = 1 ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ) ;
hb_buffer_set_script ( hbbuf , hb_script_from_string ( uscript_getShortName ( this - > script ) , - 1 ) ) ;
hb_buffer_set_language ( hbbuf , hb_language_from_string ( _current_language - > isocode , - 1 ) ) ;
hb_buffer_set_cluster_level ( hbbuf , HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES ) ;
/* Shape the segment. */
hb_shape ( hbfont , hbbuf , nullptr , 0 ) ;
unsigned int glyph_count ;
auto glyph_info = hb_buffer_get_glyph_infos ( hbbuf , & glyph_count ) ;
auto glyph_pos = hb_buffer_get_glyph_positions ( hbbuf , & glyph_count ) ;
/* Make sure any former run is lost. */
this - > glyphs . clear ( ) ;
this - > glyph_to_char . clear ( ) ;
this - > positions . clear ( ) ;
this - > advance . clear ( ) ;
/* Reserve space, as we already know the size. */
this - > glyphs . reserve ( glyph_count ) ;
this - > glyph_to_char . reserve ( glyph_count ) ;
2024-01-18 21:06:43 +00:00
this - > positions . reserve ( glyph_count + 1 ) ;
2023-04-30 18:37:40 +00:00
this - > advance . reserve ( glyph_count ) ;
/* Prepare the glyphs/position. ICUVisualRun will give the position an offset if needed. */
hb_position_t advance = 0 ;
for ( unsigned int i = 0 ; i < glyph_count ; i + + ) {
int x_advance ;
2023-12-15 23:06:19 +00:00
if ( buff [ glyph_info [ i ] . cluster ] > = SCC_SPRITE_START & & buff [ glyph_info [ i ] . cluster ] < = SCC_SPRITE_END & & glyph_info [ i ] . codepoint = = 0 ) {
2023-04-30 18:37:40 +00:00
auto glyph = this - > font - > fc - > MapCharToGlyph ( buff [ glyph_info [ i ] . cluster ] ) ;
this - > glyphs . push_back ( glyph ) ;
2024-01-18 21:06:43 +00:00
this - > positions . emplace_back ( advance , ( this - > font - > fc - > GetHeight ( ) - ScaleSpriteTrad ( FontCache : : GetDefaultFontHeight ( this - > font - > fc - > GetSize ( ) ) ) ) / 2 ) ; // Align sprite font to centre
2023-04-30 18:37:40 +00:00
x_advance = this - > font - > fc - > GetGlyphWidth ( glyph ) ;
} else {
this - > glyphs . push_back ( glyph_info [ i ] . codepoint ) ;
2024-01-18 21:06:43 +00:00
this - > positions . emplace_back ( glyph_pos [ i ] . x_offset / FONT_SCALE + advance , glyph_pos [ i ] . y_offset / FONT_SCALE ) ;
2023-04-30 18:37:40 +00:00
x_advance = glyph_pos [ i ] . x_advance / FONT_SCALE ;
}
2023-08-16 19:17:02 +00:00
this - > glyph_to_char . push_back ( glyph_info [ i ] . cluster ) ;
2023-04-30 18:37:40 +00:00
this - > advance . push_back ( x_advance ) ;
advance + = x_advance ;
}
2024-01-18 21:06:43 +00:00
/* End-of-run position. */
this - > positions . emplace_back ( advance , 0 ) ;
2023-04-30 18:37:40 +00:00
/* Track the total advancement we made. */
this - > total_advance = advance ;
hb_buffer_destroy ( hbbuf ) ;
hb_font_destroy ( hbfont ) ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/**
* Get the height of the line .
* @ return The maximum height of the line .
*/
int ICUParagraphLayout : : ICULine : : GetLeading ( ) const
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
int leading = 0 ;
for ( const auto & run : * this ) {
leading = std : : max ( leading , run . GetLeading ( ) ) ;
}
return leading ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/**
* Get the width of this line .
* @ return The width of the line .
*/
int ICUParagraphLayout : : ICULine : : GetWidth ( ) const
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
int length = 0 ;
for ( const auto & run : * this ) {
length + = run . GetAdvance ( ) ;
}
return length ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/**
* Itemize the string into runs per embedding level .
*
* Later on , based on the levels , we can deduce the order of a subset of runs .
*
* @ param buff The string to itemize .
* @ param length The length of the string .
* @ return The runs .
*/
std : : vector < ICURun > ItemizeBidi ( UChar * buff , size_t length )
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
auto ubidi = ubidi_open ( ) ;
auto parLevel = _current_text_dir = = TD_RTL ? UBIDI_RTL : UBIDI_LTR ;
UErrorCode err = U_ZERO_ERROR ;
ubidi_setPara ( ubidi , buff , length , parLevel , nullptr , & err ) ;
if ( U_FAILURE ( err ) ) {
Debug ( fontcache , 0 , " Failed to set paragraph: %s " , u_errorName ( err ) ) ;
ubidi_close ( ubidi ) ;
return std : : vector < ICURun > ( ) ;
}
int32_t count = ubidi_countRuns ( ubidi , & err ) ;
if ( U_FAILURE ( err ) ) {
Debug ( fontcache , 0 , " Failed to count runs: %s " , u_errorName ( err ) ) ;
ubidi_close ( ubidi ) ;
return std : : vector < ICURun > ( ) ;
}
std : : vector < ICURun > runs ;
runs . reserve ( count ) ;
/* Find the breakpoints for the logical runs. So we get runs that say "from START to END". */
int32_t logical_pos = 0 ;
while ( static_cast < size_t > ( logical_pos ) < length ) {
auto start_pos = logical_pos ;
/* Fetch the embedding level, so we can order bidi correctly later on. */
UBiDiLevel level ;
ubidi_getLogicalRun ( ubidi , start_pos , & logical_pos , & level ) ;
runs . emplace_back ( ICURun ( start_pos , logical_pos - start_pos , level ) ) ;
}
assert ( static_cast < size_t > ( count ) = = runs . size ( ) ) ;
ubidi_close ( ubidi ) ;
return runs ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/**
* Itemize the string into runs per script , based on the previous created runs .
*
* Basically , this always returns the same or more runs than given .
*
* @ param buff The string to itemize .
* @ param length The length of the string .
* @ param runs_current The current runs .
* @ return The runs .
*/
std : : vector < ICURun > ItemizeScript ( UChar * buff , size_t length , std : : vector < ICURun > & runs_current )
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
std : : vector < ICURun > runs ;
icu : : ScriptRun script_itemizer ( buff , length ) ;
int cur_pos = 0 ;
auto cur_run = runs_current . begin ( ) ;
while ( true ) {
while ( cur_pos < script_itemizer . getScriptEnd ( ) & & cur_run ! = runs_current . end ( ) ) {
int stop_pos = std : : min ( script_itemizer . getScriptEnd ( ) , cur_run - > start + cur_run - > length ) ;
assert ( stop_pos - cur_pos > 0 ) ;
runs . push_back ( ICURun ( cur_pos , stop_pos - cur_pos , cur_run - > level , script_itemizer . getScriptCode ( ) ) ) ;
if ( stop_pos = = cur_run - > start + cur_run - > length ) cur_run + + ;
cur_pos = stop_pos ;
}
if ( ! script_itemizer . next ( ) ) break ;
}
return runs ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/**
* Itemize the string into runs per style , based on the previous created runs .
*
* Basically , this always returns the same or more runs than given .
*
* @ param runs_current The current runs .
* @ param font_mapping The font mapping .
* @ return The runs .
*/
2023-09-16 21:27:16 +00:00
std : : vector < ICURun > ItemizeStyle ( std : : vector < ICURun > & runs_current , FontMap & font_mapping )
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
std : : vector < ICURun > runs ;
int cur_pos = 0 ;
auto cur_run = runs_current . begin ( ) ;
for ( auto const & font_map : font_mapping ) {
while ( cur_pos < font_map . first & & cur_run ! = runs_current . end ( ) ) {
int stop_pos = std : : min ( font_map . first , cur_run - > start + cur_run - > length ) ;
assert ( stop_pos - cur_pos > 0 ) ;
runs . push_back ( ICURun ( cur_pos , stop_pos - cur_pos , cur_run - > level , cur_run - > script , font_map . second ) ) ;
if ( stop_pos = = cur_run - > start + cur_run - > length ) cur_run + + ;
cur_pos = stop_pos ;
}
}
return runs ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/* static */ ParagraphLayouter * ICUParagraphLayoutFactory : : GetParagraphLayout ( UChar * buff , UChar * buff_end , FontMap & font_mapping )
2023-05-01 12:42:51 +00:00
{
2023-04-30 18:37:40 +00:00
size_t length = buff_end - buff ;
/* Can't layout an empty string. */
if ( length = = 0 ) return nullptr ;
/* Can't layout our in-built sprite fonts. */
for ( auto const & pair : font_mapping ) {
if ( pair . second - > fc - > IsBuiltInFont ( ) ) return nullptr ;
}
auto runs = ItemizeBidi ( buff , length ) ;
runs = ItemizeScript ( buff , length , runs ) ;
2023-09-16 21:27:16 +00:00
runs = ItemizeStyle ( runs , font_mapping ) ;
2023-04-30 18:37:40 +00:00
2023-10-20 18:09:58 +00:00
if ( runs . empty ( ) ) return nullptr ;
2023-04-30 18:37:40 +00:00
for ( auto & run : runs ) {
run . Shape ( buff , length ) ;
}
return new ICUParagraphLayout ( runs , buff , length ) ;
2023-05-01 12:42:51 +00:00
}
2024-01-27 20:13:42 +00:00
/* static */ std : : unique_ptr < icu : : BreakIterator > ICUParagraphLayoutFactory : : break_iterator ;
/**
* Initialize data needed for the ICU layouter .
*/
/* static */ void ICUParagraphLayoutFactory : : InitializeLayouter ( )
{
auto locale = icu : : Locale ( _current_language - > isocode ) ;
UErrorCode status = U_ZERO_ERROR ;
ICUParagraphLayoutFactory : : break_iterator . reset ( icu : : BreakIterator : : createLineInstance ( locale , status ) ) ;
assert ( U_SUCCESS ( status ) ) ;
}
/**
* Get a thread - safe line break iterator .
* @ returns unique_ptr managed BreakIterator instance .
*/
/* static */ std : : unique_ptr < icu : : BreakIterator > ICUParagraphLayoutFactory : : GetBreakIterator ( )
{
assert ( ICUParagraphLayoutFactory : : break_iterator ! = nullptr ) ;
return std : : unique_ptr < icu : : BreakIterator > ( ICUParagraphLayoutFactory : : break_iterator - > clone ( ) ) ;
}
2023-04-30 18:37:40 +00:00
std : : unique_ptr < const ICUParagraphLayout : : Line > ICUParagraphLayout : : NextLine ( int max_width )
{
std : : vector < ICURun > : : iterator start_run = this - > current_run ;
std : : vector < ICURun > : : iterator last_run = this - > current_run ;
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
if ( start_run = = this - > runs . end ( ) ) return nullptr ;
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
int cur_width = 0 ;
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
/* Add remaining width of the first run if it is a broken run. */
if ( this - > partial_offset > 0 ) {
if ( ( start_run - > level & 1 ) = = 0 ) {
for ( size_t i = this - > partial_offset ; i < start_run - > advance . size ( ) ; i + + ) {
cur_width + = start_run - > advance [ i ] ;
}
} else {
for ( int i = 0 ; i < this - > partial_offset ; i + + ) {
cur_width + = start_run - > advance [ i ] ;
2023-05-01 12:42:51 +00:00
}
}
2023-04-30 18:37:40 +00:00
last_run + + ;
}
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
/* Gather runs until the line is full. */
while ( last_run ! = this - > runs . end ( ) & & cur_width < max_width ) {
cur_width + = last_run - > total_advance ;
last_run + + ;
}
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
/* If the text does not fit into the available width, find a suitable breaking point. */
int new_partial_length = 0 ;
if ( cur_width > max_width ) {
/* Create a break-iterator to find a good place to break lines. */
2024-01-27 20:13:42 +00:00
auto break_iterator = ICUParagraphLayoutFactory : : GetBreakIterator ( ) ;
2023-04-30 18:37:40 +00:00
break_iterator - > setText ( icu : : UnicodeString ( this - > buff , this - > buff_length ) ) ;
auto overflow_run = last_run - 1 ;
/* Find the last glyph that fits. */
size_t index ;
if ( ( overflow_run - > level & 1 ) = = 0 ) {
/* LTR */
for ( index = overflow_run - > glyphs . size ( ) ; index > 0 ; index - - ) {
cur_width - = overflow_run - > advance [ index - 1 ] ;
if ( cur_width < = max_width ) break ;
}
index - - ;
} else {
/* RTL */
for ( index = 0 ; index < overflow_run - > glyphs . size ( ) ; index + + ) {
cur_width - = overflow_run - > advance [ index ] ;
if ( cur_width < = max_width ) break ;
}
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
/* Find the character that matches; this is the start of the cluster. */
auto char_pos = overflow_run - > glyph_to_char [ index ] ;
/* See if there is a good breakpoint inside this run. */
int32_t break_pos = break_iterator - > preceding ( char_pos + 1 ) ;
if ( break_pos ! = icu : : BreakIterator : : DONE & & break_pos > overflow_run - > start + this - > partial_offset ) {
/* There is a line-break inside this run that is suitable. */
new_partial_length = break_pos - overflow_run - > start - this - > partial_offset ;
} else if ( overflow_run ! = start_run ) {
/* There is no suitable line-break in this run, but it is also not
* the only run on this line . So we remove the run . */
last_run - - ;
} else {
/* There is no suitable line-break and this is the only run on the
* line . So we break at the cluster . This is not pretty , but the
* best we can do . */
2023-08-16 19:17:02 +00:00
new_partial_length = char_pos - overflow_run - > start - this - > partial_offset ;
2023-04-30 18:37:40 +00:00
}
}
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
/* Reorder the runs on this line for display. */
std : : vector < UBiDiLevel > bidi_level ;
for ( auto run = start_run ; run ! = last_run ; run + + ) {
bidi_level . push_back ( run - > level ) ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
std : : vector < int32_t > vis_to_log ( bidi_level . size ( ) ) ;
ubidi_reorderVisual ( bidi_level . data ( ) , bidi_level . size ( ) , vis_to_log . data ( ) ) ;
/* Create line. */
std : : unique_ptr < ICULine > line ( new ICULine ( ) ) ;
int cur_pos = 0 ;
for ( auto & i : vis_to_log ) {
auto i_run = start_run + i ;
/* Copy the ICURun here, so we can modify it in case of a partial. */
ICURun run = * i_run ;
if ( i_run = = last_run - 1 & & new_partial_length > 0 ) {
if ( i_run = = start_run & & this - > partial_offset > 0 ) {
assert ( run . length > this - > partial_offset ) ;
run . start + = this - > partial_offset ;
run . length - = this - > partial_offset ;
}
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
assert ( run . length > new_partial_length ) ;
run . length = new_partial_length ;
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
run . Shape ( this - > buff , this - > buff_length ) ;
} else if ( i_run = = start_run & & this - > partial_offset > 0 ) {
assert ( run . length > this - > partial_offset ) ;
run . start + = this - > partial_offset ;
run . length - = this - > partial_offset ;
run . Shape ( this - > buff , this - > buff_length ) ;
}
2023-05-01 12:42:51 +00:00
2023-04-30 18:37:40 +00:00
auto total_advance = run . total_advance ;
line - > emplace_back ( std : : move ( run ) , cur_pos ) ;
cur_pos + = total_advance ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
if ( new_partial_length > 0 ) {
this - > current_run = last_run - 1 ;
this - > partial_offset + = new_partial_length ;
} else {
this - > current_run = last_run ;
this - > partial_offset = 0 ;
2023-05-01 12:42:51 +00:00
}
2023-04-30 18:37:40 +00:00
return line ;
2023-05-01 12:42:51 +00:00
}
2023-05-08 17:01:06 +00:00
/* static */ size_t ICUParagraphLayoutFactory : : AppendToBuffer ( UChar * buff , const UChar * buffer_last , char32_t c )
2023-05-01 12:42:51 +00:00
{
2023-09-16 22:33:35 +00:00
assert ( buff < buffer_last ) ;
2023-05-01 12:42:51 +00:00
/* Transform from UTF-32 to internal ICU format of UTF-16. */
2023-05-08 17:01:06 +00:00
int32_t length = 0 ;
2023-05-01 12:42:51 +00:00
UErrorCode err = U_ZERO_ERROR ;
u_strFromUTF32 ( buff , buffer_last - buff , & length , ( UChar32 * ) & c , 1 , & err ) ;
return length ;
}