@@ -116,6 +116,11 @@ impl<'a, Coord, T: Borrow<str>> MultiLineText<'a, Coord, T> {
116116 }
117117}
118118
119+
120+ // Rewrite of the layout function for multiline-text. It crashes when UTF-8 is used
121+ // instead of ASCII. Solution taken from:
122+ // https://stackoverflow.com/questions/68122526/splitting-a-utf-8-string-into-chunks
123+ // and modified for our purposes.
119124fn layout_multiline_text < ' a , F : FnMut ( & ' a str ) > (
120125 text : & ' a str ,
121126 max_width : u32 ,
@@ -126,32 +131,60 @@ fn layout_multiline_text<'a, F: FnMut(&'a str)>(
126131 if max_width == 0 || line. is_empty ( ) {
127132 func ( line) ;
128133 } else {
129- let mut remaining = & line[ 0 ..] ;
134+ let mut indices = line. char_indices ( ) . map ( |( idx, _) | idx) . peekable ( ) ;
135+ let font2 = font. clone ( ) ;
130136
131- while !remaining. is_empty ( ) {
132- let mut left = 0 ;
133- while left < remaining. len ( ) {
134- let width = font. box_size ( & remaining[ 0 ..=left] ) . unwrap_or ( ( 0 , 0 ) ) . 0 as i32 ;
137+ let it = std:: iter:: from_fn ( || {
138+ let start_idx = match indices. next ( ) {
139+ Some ( idx) => idx,
140+ None => return None ,
141+ } ;
135142
143+ // iterate over indices
144+ while let Some ( idx) = indices. next ( ) {
145+ let substring = & line[ start_idx..idx] ;
146+ let width = font2. box_size ( substring) . unwrap_or ( ( 0 , 0 ) ) . 0 as i32 ;
136147 if width > max_width as i32 {
137148 break ;
138149 }
139- left += 1 ;
140150 }
141151
142- if left == 0 {
143- left += 1 ;
144- }
152+ let end_idx = match indices. peek ( ) {
153+ Some ( idx) => * idx,
154+ None => line. bytes ( ) . len ( ) ,
155+ } ;
145156
146- let cur_line = & remaining [ ..left ] ;
147- remaining = & remaining [ left.. ] ;
157+ Some ( & line [ start_idx..end_idx ] )
158+ } ) ;
148159
149- func ( cur_line) ;
160+ for chunk in it {
161+ func ( chunk) ;
150162 }
151163 }
152164 }
153165}
154166
167+ #[ cfg( feature = "ttf" ) ]
168+ #[ test]
169+ fn test_multi_layout ( ) {
170+ use plotters_backend:: { FontFamily , FontStyle } ;
171+
172+ let font = FontDesc :: new ( FontFamily :: SansSerif , 20 as f64 , FontStyle :: Bold ) ;
173+
174+ layout_multiline_text ( "öäabcde" , 40 , font, |txt| {
175+ println ! ( "Got: {}" , txt) ;
176+ assert ! ( txt == "öäabc" || txt == "de" ) ;
177+ } ) ;
178+
179+ let font = FontDesc :: new ( FontFamily :: SansSerif , 20 as f64 , FontStyle :: Bold ) ;
180+ layout_multiline_text ( "öä" , 100 , font, |txt| {
181+ // This does not divide the line, but still crashed in the previous implementation
182+ // of layout_multiline_text. So this test should be reliable
183+ println ! ( "Got: {}" , txt) ;
184+ assert_eq ! ( txt, "öä" )
185+ } ) ;
186+ }
187+
155188impl < ' a , T : Borrow < str > > MultiLineText < ' a , BackendCoord , T > {
156189 /// Compute the line layout
157190 pub fn compute_line_layout ( & self ) -> FontResult < Vec < LayoutBox > > {
0 commit comments