@@ -580,7 +580,7 @@ private static FlowDirection GetRunDirection(TextRun? textRun, FlowDirection cur
580580 /// </returns>
581581 private int GetLastDirectionalRunIndex ( int indexedRunIndex , FlowDirection flowDirection , ref double directionalWidth )
582582 {
583- if ( _indexedTextRuns is null )
583+ if ( _indexedTextRuns is null )
584584 {
585585 return - 1 ;
586586 }
@@ -624,7 +624,7 @@ private int GetLastDirectionalRunIndex(int indexedRunIndex, FlowDirection flowDi
624624
625625 public override IReadOnlyList < TextBounds > GetTextBounds ( int firstTextSourceIndex , int textLength )
626626 {
627- if ( textLength == 0 )
627+ if ( textLength == 0 )
628628 {
629629 throw new ArgumentOutOfRangeException ( nameof ( textLength ) , textLength , $ "{ nameof ( textLength ) } ('0') must be a non-zero value. ") ;
630630 }
@@ -643,7 +643,7 @@ public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex
643643 var indexedTextRun = _indexedTextRuns [ 0 ] ;
644644 var currentDirection = GetRunDirection ( indexedTextRun . TextRun , _resolvedFlowDirection ) ;
645645
646- return [ new TextBounds ( new Rect ( 0 , 0 , 0 , Height ) , currentDirection , [ ] ) ] ;
646+ return [ new TextBounds ( new Rect ( 0 , 0 , 0 , Height ) , currentDirection , [ ] ) ] ;
647647 }
648648
649649 //We can return early if the requested text range is after the line's text range.
@@ -667,7 +667,7 @@ public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex
667667 {
668668 break ;
669669 }
670-
670+
671671 var currentTextRun = currentIndexedRun . TextRun ;
672672
673673 if ( currentTextRun == null )
@@ -691,7 +691,7 @@ public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex
691691 {
692692 directionalWidth = currentDrawable . Size . Width ;
693693 }
694-
694+
695695 var firstRunIndex = currentIndexedRun . RunIndex ;
696696 var lastRunIndex = GetLastDirectionalRunIndex ( indexedRunIndex , currentDirection , ref directionalWidth ) ;
697697
@@ -709,8 +709,8 @@ public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex
709709 }
710710 default :
711711 {
712- currentBounds = GetTextBoundsLeftToRight ( firstRunIndex , lastRunIndex , currentX , firstTextSourceIndex ,
713- currentPosition , remainingLength , out coveredLength , out currentPosition ) ;
712+ currentBounds = GetTextBoundsLeftToRight ( firstRunIndex , lastRunIndex , currentX , firstTextSourceIndex ,
713+ currentPosition , remainingLength , out coveredLength , out currentPosition ) ;
714714
715715 break ;
716716 }
@@ -729,7 +729,7 @@ public override IReadOnlyList<TextBounds> GetTextBounds(int firstTextSourceIndex
729729
730730 lastBounds = currentBounds ;
731731
732- if ( coveredLength <= 0 )
732+ if ( coveredLength <= 0 )
733733 {
734734 throw new InvalidOperationException ( "Covered length must be greater than zero." ) ;
735735 }
@@ -988,14 +988,14 @@ private TextBounds GetTextBoundsLeftToRight(int firstRunIndex, int lastRunIndex,
988988 {
989989 var runBounds = GetRunBounds ( shapedTextRun , endX , firstTextSourceIndex , remainingLength , currentPosition ) ;
990990
991- if ( runBounds . TextSourceCharacterIndex < FirstTextSourceIndex + Length )
991+ if ( runBounds . TextSourceCharacterIndex < FirstTextSourceIndex + Length )
992992 {
993993 textRunBounds . Add ( runBounds ) ;
994994 }
995995
996996 currentPosition = runBounds . TextSourceCharacterIndex + runBounds . Length ;
997997
998- if ( i == firstRunIndex )
998+ if ( i == firstRunIndex )
999999 {
10001000 startX = runBounds . Rectangle . Left ;
10011001 }
@@ -1112,7 +1112,7 @@ private TextRunBounds GetRunBounds(ShapedTextRun currentRun, double currentX, in
11121112 var startHitIndex = startHit . FirstCharacterIndex ;
11131113
11141114 //If the requested text range starts at the trailing edge we need to move at the end of the hit
1115- if ( startHitIndex < startIndex )
1115+ if ( startHitIndex < startIndex )
11161116 {
11171117 startHitIndex += startHit . TrailingLength ;
11181118 }
@@ -1230,7 +1230,7 @@ public void FinalizeLine()
12301230 }
12311231 case not null :
12321232 {
1233- if ( direction == LogicalDirection . Forward )
1233+ if ( direction == LogicalDirection . Forward )
12341234 {
12351235 if ( textPosition == codepointIndex )
12361236 {
@@ -1316,40 +1316,60 @@ private TextLineMetrics CreateLineMetrics()
13161316 }
13171317 }
13181318
1319- var height = descent - ascent + lineGap ;
1320-
13211319 var inkBounds = new Rect ( ) ;
13221320
13231321 for ( var index = 0 ; index < _textRuns . Length ; index ++ )
13241322 {
13251323 switch ( _textRuns [ index ] )
13261324 {
13271325 case ShapedTextRun textRun :
1328- {
1329- var glyphRun = textRun . GlyphRun ;
1330- //Align the ink bounds at the common baseline
1331- var offsetY = - ascent - textRun . Baseline ;
1326+ {
1327+ var glyphRun = textRun . GlyphRun ;
1328+ //Align the ink bounds at the common baseline
1329+ var offsetY = - ascent - textRun . Baseline ;
13321330
1333- var runBounds = glyphRun . InkBounds . Translate ( new Vector ( widthIncludingWhitespace , offsetY ) ) ;
1331+ var runBounds = glyphRun . InkBounds . Translate ( new Vector ( widthIncludingWhitespace , offsetY ) ) ;
13341332
1335- inkBounds = inkBounds . Union ( runBounds ) ;
1333+ inkBounds = inkBounds . Union ( runBounds ) ;
13361334
1337- widthIncludingWhitespace += textRun . Size . Width ;
1335+ widthIncludingWhitespace += textRun . Size . Width ;
13381336
1339- break ;
1340- }
1337+ break ;
1338+ }
13411339
13421340 case DrawableTextRun drawableTextRun :
1343- {
1344- //Align the bounds at the common baseline
1345- var offsetY = - ascent - drawableTextRun . Baseline ;
1341+ {
1342+ //Align the bounds at the common baseline
1343+ var offsetY = - ascent - drawableTextRun . Baseline ;
13461344
1347- inkBounds = inkBounds . Union ( new Rect ( new Point ( widthIncludingWhitespace , offsetY ) , drawableTextRun . Size ) ) ;
1345+ inkBounds = inkBounds . Union ( new Rect ( new Point ( widthIncludingWhitespace , offsetY ) , drawableTextRun . Size ) ) ;
13481346
1349- widthIncludingWhitespace += drawableTextRun . Size . Width ;
1350-
1351- break ;
1352- }
1347+ widthIncludingWhitespace += drawableTextRun . Size . Width ;
1348+
1349+ break ;
1350+ }
1351+ }
1352+ }
1353+
1354+ var halfLineGap = lineGap * 0.5 ;
1355+ var naturalHeight = descent - ascent + lineGap ;
1356+ var baseline = - ascent + halfLineGap ;
1357+ var height = naturalHeight ;
1358+
1359+ if ( ! double . IsNaN ( lineHeight ) && ! MathUtilities . IsZero ( lineHeight ) )
1360+ {
1361+ if ( lineHeight <= naturalHeight )
1362+ {
1363+ //Clamp to the specified line height
1364+ height = lineHeight ;
1365+ baseline = - ascent ;
1366+ }
1367+ else
1368+ {
1369+ // Center the text vertically within the specified line height
1370+ height = lineHeight ;
1371+ var extra = lineHeight - ( descent - ascent ) ;
1372+ baseline = - ascent + extra / 2 ;
13531373 }
13541374 }
13551375
@@ -1385,24 +1405,14 @@ private TextLineMetrics CreateLineMetrics()
13851405 }
13861406
13871407 var extent = inkBounds . Height ;
1388- //The width of overhanging pixels at the bottom
1389- var overhangAfter = inkBounds . Bottom - height ;
1408+ //The height of overhanging pixels at the bottom
1409+ var overhangAfter = inkBounds . Bottom - height + halfLineGap ;
13901410 //The width of overhanging pixels at the natural alignment point. Positive value means we are inside.
13911411 var overhangLeading = inkBounds . Left ;
13921412 //The width of overhanging pixels at the end of the natural bounds. Positive value means we are inside.
13931413 var overhangTrailing = widthIncludingWhitespace - inkBounds . Right ;
13941414 var hasOverflowed = width > _paragraphWidth ;
13951415
1396- if ( ! double . IsNaN ( lineHeight ) && ! MathUtilities . IsZero ( lineHeight ) )
1397- {
1398- //Center the line
1399- var offset = ( height - lineHeight ) / 2 ;
1400-
1401- ascent += offset ;
1402-
1403- height = lineHeight ;
1404- }
1405-
14061416 var start = GetParagraphOffsetX ( width , widthIncludingWhitespace ) ;
14071417
14081418 _inkBounds = inkBounds . Translate ( new Vector ( start , 0 ) ) ;
@@ -1416,7 +1426,7 @@ private TextLineMetrics CreateLineMetrics()
14161426 Extent = extent ,
14171427 NewlineLength = newLineLength ,
14181428 Start = start ,
1419- TextBaseline = - ascent ,
1429+ TextBaseline = baseline ,
14201430 TrailingWhitespaceLength = trailingWhitespaceLength ,
14211431 Width = width ,
14221432 WidthIncludingTrailingWhitespace = widthIncludingWhitespace ,
0 commit comments