@@ -34,6 +34,9 @@ impl TextDisplay {
3434 /// [Requires status][Self#status-of-preparation]: level runs have been
3535 /// prepared and are valid in all ways except size (`dpem`).
3636 ///
37+ /// Parameters must match those passed to [`Self::prepare_runs`] for the
38+ /// result to be valid.
39+ ///
3740 /// This updates the result of [`TextDisplay::prepare_runs`] due to change
3841 /// in font size.
3942 pub fn resize_runs ( & mut self , text : & str , mut font_tokens : impl Iterator < Item = FontToken > ) {
@@ -145,7 +148,12 @@ impl TextDisplay {
145148 ///
146149 /// [Requires status][Self#status-of-preparation]: none.
147150 ///
148- /// Must be called again if any of `text`, `direction` or `font` change.
151+ /// The `font_tokens` iterator must not be empty and the first token yielded
152+ /// must have [`FontToken::start`] == 0. (Failure to do so will result in an
153+ /// error on debug builds and usage of default values on release builds.)
154+ ///
155+ /// Must be called again if any of `text`, `direction` or `font_tokens`
156+ /// change.
149157 /// If only `dpem` changes, [`Self::resize_runs`] may be called instead.
150158 ///
151159 /// The text is broken into a set of contiguous "level runs". These runs are
@@ -285,13 +293,8 @@ impl TextDisplay {
285293 require_break = true ;
286294 }
287295
288- let mut new_script = None ;
289296 if is_real ( script) {
290- if first_real. is_none ( ) {
291- first_real = Some ( c) ;
292- }
293297 if script != input. script {
294- new_script = Some ( script) ;
295298 require_break |= is_real ( input. script ) ;
296299 }
297300 }
@@ -324,8 +327,22 @@ impl TextDisplay {
324327 }
325328 input. script = script;
326329 breaks = Default :: default ( ) ;
327- } else if is_break && !is_control {
328- breaks. push ( shaper:: GlyphBreak :: new ( to_u32 ( index) ) ) ;
330+ } else {
331+ if is_break && !is_control && index > start {
332+ breaks. push ( shaper:: GlyphBreak :: new ( to_u32 ( index) ) ) ;
333+ }
334+
335+ if input. script == Script :: Unknown
336+ || matches ! ( input. script, Script :: Common | Script :: Inherited ) && is_real ( script)
337+ {
338+ input. script = script;
339+ }
340+ }
341+
342+ if is_real ( script) {
343+ if first_real. is_none ( ) {
344+ first_real = Some ( c) ;
345+ }
329346 }
330347
331348 if let Some ( token) = next_token. as_ref ( )
@@ -345,9 +362,6 @@ impl TextDisplay {
345362 last_is_control = is_control;
346363 last_is_htab = is_htab;
347364 emoji_start = new_emoji_start;
348- if let Some ( script) = new_script {
349- input. script = script;
350- }
351365 }
352366
353367 let hard_break = ends_with_hard_break ( text) ;
@@ -540,3 +554,118 @@ impl EmojiState {
540554 b
541555 }
542556}
557+
558+ /// Warning: test results may depend on system fonts
559+ #[ cfg( test) ]
560+ mod test {
561+ use super :: * ;
562+ use std:: iter;
563+ use std:: ops:: Range ;
564+ use unicode_bidi:: Level ;
565+
566+ type Expected < ' a > = & ' a [ ( Range < usize > , RunSpecial , Level , Script , & ' a [ u32 ] ) ] ;
567+
568+ fn test_breaking ( text : & str , dir : Direction , expected : Expected ) {
569+ let fonts = iter:: once ( FontToken {
570+ start : 0 ,
571+ dpem : 16.0 ,
572+ font : Default :: default ( ) ,
573+ } ) ;
574+
575+ let mut display = TextDisplay :: default ( ) ;
576+ assert ! ( display. prepare_runs( text, dir, fonts) . is_ok( ) ) ;
577+
578+ for ( i, ( run, expected) ) in display. runs . iter ( ) . zip ( expected. iter ( ) ) . enumerate ( ) {
579+ assert_eq ! (
580+ run. range. to_std( ) ,
581+ expected. 0 ,
582+ "text range for text \" {text}\" , run {i}"
583+ ) ;
584+ assert_eq ! (
585+ run. special, expected. 1 ,
586+ "RunSpecial for text \" {text}\" , run {i}"
587+ ) ;
588+ assert_eq ! ( run. level, expected. 2 , "for text \" {text}\" , run {i}" ) ;
589+ assert_eq ! ( run. script, expected. 3 , "for text \" {text}\" , run {i}" ) ;
590+ assert_eq ! (
591+ run. breaks. iter( ) . map( |b| b. index) . collect:: <Vec <_>>( ) ,
592+ expected. 4 ,
593+ "wrap-points for text \" {text}\" , run {i}"
594+ ) ;
595+ }
596+ assert_eq ! ( display. runs. len( ) , expected. len( ) , "number of runs" ) ;
597+ }
598+
599+ #[ test]
600+ fn test_breaking_simple ( ) {
601+ let sample = "Layout demo 123" ;
602+ test_breaking (
603+ sample,
604+ Direction :: Auto ,
605+ & [ (
606+ 0 ..sample. len ( ) ,
607+ RunSpecial :: None ,
608+ Level :: ltr ( ) ,
609+ Script :: Latin ,
610+ & [ 7 , 12 ] ,
611+ ) ] ,
612+ ) ;
613+ }
614+
615+ #[ test]
616+ fn test_breaking_bidi ( ) {
617+ let sample = "abc אבג def" ;
618+ test_breaking (
619+ sample,
620+ Direction :: Auto ,
621+ & [
622+ ( 0 ..4 , RunSpecial :: None , Level :: ltr ( ) , Script :: Latin , & [ ] ) ,
623+ (
624+ 4 ..10 ,
625+ RunSpecial :: NoBreak ,
626+ Level :: rtl ( ) ,
627+ Script :: Hebrew ,
628+ & [ ] ,
629+ ) ,
630+ ( 10 ..14 , RunSpecial :: None , Level :: ltr ( ) , Script :: Latin , & [ 11 ] ) ,
631+ ] ,
632+ ) ;
633+ }
634+
635+ #[ test]
636+ fn test_breaking_weak_bidi ( ) {
637+ let sample = "123 (1-2)" ;
638+
639+ let expected_ltr: Expected =
640+ & [ ( 0 ..9 , RunSpecial :: None , Level :: ltr ( ) , Script :: Common , & [ 4 ] ) ] ;
641+ test_breaking ( sample, Direction :: Auto , & expected_ltr[ ..] ) ;
642+ test_breaking ( sample, Direction :: Ltr , & expected_ltr[ ..] ) ;
643+
644+ let expected_rtl: Expected = & [
645+ (
646+ 0 ..3 ,
647+ RunSpecial :: NoBreak ,
648+ Level :: new ( 2 ) . unwrap ( ) ,
649+ Script :: Common ,
650+ & [ ] ,
651+ ) ,
652+ (
653+ 3 ..5 ,
654+ RunSpecial :: NoBreak ,
655+ Level :: rtl ( ) ,
656+ Script :: Common ,
657+ & [ 4 ] ,
658+ ) ,
659+ (
660+ 5 ..8 ,
661+ RunSpecial :: NoBreak ,
662+ Level :: new ( 2 ) . unwrap ( ) ,
663+ Script :: Common ,
664+ & [ ] ,
665+ ) ,
666+ ( 8 ..9 , RunSpecial :: None , Level :: rtl ( ) , Script :: Common , & [ ] ) ,
667+ ] ;
668+ test_breaking ( sample, Direction :: AutoRtl , & expected_rtl[ ..] ) ;
669+ test_breaking ( sample, Direction :: Rtl , & expected_rtl[ ..] ) ;
670+ }
671+ }
0 commit comments