@@ -116,6 +116,11 @@ impl<'a, Coord, T: Borrow<str>> MultiLineText<'a, Coord, T> {
116
116
}
117
117
}
118
118
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.
119
124
fn layout_multiline_text < ' a , F : FnMut ( & ' a str ) > (
120
125
text : & ' a str ,
121
126
max_width : u32 ,
@@ -126,32 +131,60 @@ fn layout_multiline_text<'a, F: FnMut(&'a str)>(
126
131
if max_width == 0 || line. is_empty ( ) {
127
132
func ( line) ;
128
133
} else {
129
- let mut remaining = & line[ 0 ..] ;
134
+ let mut indices = line. char_indices ( ) . map ( |( idx, _) | idx) . peekable ( ) ;
135
+ let font2 = font. clone ( ) ;
130
136
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
+ } ;
135
142
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 ;
136
147
if width > max_width as i32 {
137
148
break ;
138
149
}
139
- left += 1 ;
140
150
}
141
151
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
+ } ;
145
156
146
- let cur_line = & remaining [ ..left ] ;
147
- remaining = & remaining [ left.. ] ;
157
+ Some ( & line [ start_idx..end_idx ] )
158
+ } ) ;
148
159
149
- func ( cur_line) ;
160
+ for chunk in it {
161
+ func ( chunk) ;
150
162
}
151
163
}
152
164
}
153
165
}
154
166
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
+
155
188
impl < ' a , T : Borrow < str > > MultiLineText < ' a , BackendCoord , T > {
156
189
/// Compute the line layout
157
190
pub fn compute_line_layout ( & self ) -> FontResult < Vec < LayoutBox > > {
0 commit comments