@@ -197,7 +197,7 @@ internal unsafe UnicodeText(
197197 }
198198 }
199199
200- _text = stringBuilder . ToString ( ) ;
200+ _text = SanitizeText ( stringBuilder . ToString ( ) ) ;
201201 if ( _text . Length == 0 )
202202 {
203203 _lines = [ ] ;
@@ -230,7 +230,7 @@ internal unsafe UnicodeText(
230230 Array . Fill ( embeddingLevels , ( byte ) level , start , count ) ;
231231 }
232232
233- using var _ = ICU . CreateBiDiAndSetPara ( _text , 0 , _text . Length , flowDirection is FlowDirection . RightToLeft ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR , out var bidi , embeddingLevels ) ;
233+ using var _ = CreateBiDiOrFallback ( _text , flowDirection , embeddingLevels , out var bidi ) ;
234234 var runCount = ICU . GetMethod < ICU . ubidi_countRuns > ( ) ( bidi , out var countRunsErrorCode ) ;
235235 ICU . CheckErrorCode < ICU . ubidi_countRuns > ( countRunsErrorCode ) ;
236236 _rtl = ICU . GetMethod < ICU . ubidi_getParaLevel > ( ) ( bidi ) is UBIDI_RTL ;
@@ -837,39 +837,63 @@ public void Draw(in Visual.PaintingSession session,
837837 var wordBoundariesIndex = 0 ;
838838 var highlighterSlices = highlighterSlicer . GetSegments ( ) ;
839839 var highlighterIndex = 0 ;
840+ var fallbackHighlight = ( background : ( CompositionBrush ? ) null , foreground : ( Brush ) _blackBrush ) ;
840841 for ( var clusterIndex = 0 ; clusterIndex < _clustersInLogicalOrder . Count ; clusterIndex ++ )
841842 {
842843 var cluster = _clustersInLogicalOrder [ clusterIndex ] ;
843- while ( highlighterSlices [ highlighterIndex ] . End <= cluster . Value . start )
844+ RangeSlicer < ( CompositionBrush ? background , Brush foreground ) > . Segment ? highlighter = null ;
845+ if ( highlighterSlices . Count > 0 )
844846 {
845- highlighterIndex ++ ;
846- }
847+ while ( highlighterIndex + 1 < highlighterSlices . Count && highlighterSlices [ highlighterIndex ] . End <= cluster . Value . start )
848+ {
849+ highlighterIndex ++ ;
850+ }
847851
848- var highlighter = highlighterSlices [ highlighterIndex ] ;
852+ highlighter = highlighterSlices [ highlighterIndex ] ;
853+ }
849854
850- while ( _runBreaks [ runBreakIndex ] . end <= cluster . Value . start )
855+ if ( _runBreaks . Count > 0 )
851856 {
852- runBreakIndex ++ ;
857+ while ( runBreakIndex + 1 < _runBreaks . Count && _runBreaks [ runBreakIndex ] . end <= cluster . Value . start )
858+ {
859+ runBreakIndex ++ ;
860+ }
853861 }
854862
855- while ( _wordBoundaries [ wordBoundariesIndex ] <= cluster . Value . start )
863+ if ( _wordBoundaries . Count > 0 )
856864 {
857- wordBoundariesIndex ++ ;
865+ while ( wordBoundariesIndex + 1 < _wordBoundaries . Count && _wordBoundaries [ wordBoundariesIndex ] <= cluster . Value . start )
866+ {
867+ wordBoundariesIndex ++ ;
868+ }
858869 }
859870
860871 var lineIndex = cluster . Value . lineIndex ;
872+ if ( ( uint ) lineIndex >= ( uint ) _lines . Count || ( uint ) lineIndex >= ( uint ) _xyTable . Count )
873+ {
874+ continue ;
875+ }
876+
877+ var lineMetrics = _xyTable [ lineIndex ] ;
878+ if ( cluster . Value . indexInLine > 0 && cluster . Value . indexInLine - 1 >= lineMetrics . prefixSummedWidths . Count )
879+ {
880+ continue ;
881+ }
882+
861883 var line = _lines [ lineIndex ] ;
862- var y = _xyTable [ lineIndex ] . prefixSummedHeight - line . lineHeight ;
884+ var y = lineMetrics . prefixSummedHeight - line . lineHeight ;
863885 var unalignedX = cluster . Value . indexInLine == 0
864886 ? 0
865- : _xyTable [ lineIndex ] . prefixSummedWidths [ cluster . Value . indexInLine - 1 ] . sumUntilAfterCluster ;
887+ : lineMetrics . prefixSummedWidths [ cluster . Value . indexInLine - 1 ] . sumUntilAfterCluster ;
866888 var alignmentOffset = GetAlignmentOffsetForLine ( line ) ;
867889 var positionAcc = new SKPoint ( unalignedX + alignmentOffset , y + line . baselineOffset ) ;
868890 var fontDetails = cluster . Value . fontDetails ;
869891
870892 if ( ! cluster . Value . containsTab )
871893 {
872- var color = BrushToColor ( highlighter . Value . foreground is { } h ? h : _runBreaks [ runBreakIndex ] . foreground , session . Opacity ) ;
894+ var runForeground = _runBreaks . Count > 0 ? _runBreaks [ runBreakIndex ] . foreground : _blackBrush ;
895+ var highlightValue = highlighter ? . Value ?? fallbackHighlight ;
896+ var color = BrushToColor ( highlightValue . foreground is { } h ? h : runForeground , session . Opacity ) ;
873897 if ( ! _colorToFontToGlyphs . TryGetValue ( color , out var fontToGlyphs ) )
874898 {
875899 _colorToFontToGlyphs [ color ] = fontToGlyphs = new Dictionary < SKFont , ( List < ushort > glyphs , List < SKPoint > positions ) > ( ) ;
@@ -896,9 +920,9 @@ public void Draw(in Visual.PaintingSession session,
896920 }
897921
898922 var backgroundRect = new SKRect ( unalignedX + alignmentOffset , y , unalignedX + alignmentOffset + cluster . Value . width , y + line . lineHeight ) ;
899- highlighter . Value . background ? . Paint ( session . Canvas , session . Opacity , backgroundRect ) ;
923+ ( highlighter ? . Value ?? fallbackHighlight ) . background ? . Paint ( session . Canvas , session . Opacity , backgroundRect ) ;
900924
901- if ( _corrections ? [ wordBoundariesIndex ] is { } correction )
925+ if ( _corrections is { } corrections && _wordBoundaries . Count > 0 && wordBoundariesIndex < corrections . Count && corrections [ wordBoundariesIndex ] is { } correction )
902926 {
903927 var correctionIndexBase = wordBoundariesIndex == 0 ? 0 : _wordBoundaries [ wordBoundariesIndex - 1 ] ;
904928 if ( correctionIndexBase + correction . correctionStart <= cluster . Value . start && correctionIndexBase + correction . correctionEnd >= cluster . Value . end )
@@ -1275,10 +1299,79 @@ public int GetIndexAt(Point p, bool ignoreEndingNewLine, bool extendedSelection)
12751299
12761300 public bool IsBaseDirectionRightToLeft => _rtl ;
12771301
1302+ private static string SanitizeText ( string text )
1303+ {
1304+ var hasInvalid = false ;
1305+ for ( var i = 0 ; i < text . Length ; i ++ )
1306+ {
1307+ var c = text [ i ] ;
1308+ if ( char . IsHighSurrogate ( c ) )
1309+ {
1310+ if ( i + 1 < text . Length && char . IsLowSurrogate ( text [ i + 1 ] ) )
1311+ {
1312+ i ++ ;
1313+ continue ;
1314+ }
1315+ hasInvalid = true ;
1316+ break ;
1317+ }
1318+ if ( char . IsLowSurrogate ( c ) )
1319+ {
1320+ hasInvalid = true ;
1321+ break ;
1322+ }
1323+ }
1324+
1325+ if ( ! hasInvalid )
1326+ {
1327+ return text ;
1328+ }
1329+
1330+ var chars = text . ToCharArray ( ) ;
1331+ for ( var i = 0 ; i < chars . Length ; i ++ )
1332+ {
1333+ var c = chars [ i ] ;
1334+ if ( char . IsHighSurrogate ( c ) )
1335+ {
1336+ if ( i + 1 < chars . Length && char . IsLowSurrogate ( chars [ i + 1 ] ) )
1337+ {
1338+ i ++ ;
1339+ continue ;
1340+ }
1341+ chars [ i ] = '\uFFFD ' ;
1342+ continue ;
1343+ }
1344+ if ( char . IsLowSurrogate ( c ) )
1345+ {
1346+ chars [ i ] = '\uFFFD ' ;
1347+ }
1348+ }
1349+
1350+ return new string ( chars ) ;
1351+ }
1352+
1353+ private static DisposableStruct < IntPtr > CreateBiDiOrFallback ( string text , FlowDirection flowDirection , byte [ ] ? embeddingLevels , out IntPtr bidi )
1354+ {
1355+ var paraLevel = flowDirection is FlowDirection . RightToLeft ? UBIDI_DEFAULT_RTL : UBIDI_DEFAULT_LTR ;
1356+ try
1357+ {
1358+ return ICU . CreateBiDiAndSetPara ( text , 0 , text . Length , paraLevel , out bidi , embeddingLevels ) ;
1359+ }
1360+ catch ( InvalidOperationException ex ) when ( embeddingLevels is not null )
1361+ {
1362+ typeof ( UnicodeText ) . LogError ( ) ? . Error ( "ubidi_setPara failed with embedding levels; retrying without them." , ex ) ;
1363+ return ICU . CreateBiDiAndSetPara ( text , 0 , text . Length , paraLevel , out bidi , embeddingLevels : null ) ;
1364+ }
1365+ }
1366+
12781367 private static List < int > GetWords ( string text )
12791368 {
12801369 var boundaries = new List < int > ( ) ;
12811370 AppendBoundaries ( /* Word */ 1 , text , 0 , boundaries ) ;
1371+ if ( boundaries . Count == 0 )
1372+ {
1373+ return new List < int > { text . Length } ;
1374+ }
12821375 var ret = new List < int > { boundaries [ 0 ] } ;
12831376 for ( var index = 1 ; index < boundaries . Count ; index ++ )
12841377 {
@@ -1294,6 +1387,10 @@ private static List<int> GetWords(string text)
12941387 }
12951388 ret . Add ( boundary ) ;
12961389 }
1390+ if ( ret [ ^ 1 ] != text . Length )
1391+ {
1392+ ret . Add ( text . Length ) ;
1393+ }
12971394
12981395 return ret ;
12991396 }
0 commit comments