Skip to content

[WIP]Fix line height calculation for lines that contain fallback fonts #19013

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
2 changes: 1 addition & 1 deletion src/Avalonia.Base/Media/GlyphRun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ private GlyphRunMetrics CreateGlyphRunMetrics()

return new GlyphRunMetrics
{
Baseline = (-GlyphTypeface.Metrics.Ascent + GlyphTypeface.Metrics.LineGap) * Scale,
Baseline = -GlyphTypeface.Metrics.Ascent * Scale,
Width = width,
WidthIncludingTrailingWhitespace = widthIncludingTrailingWhitespace,
Height = height,
Expand Down
96 changes: 84 additions & 12 deletions src/Avalonia.Base/Media/TextFormatting/TextFormatterImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,16 @@ internal sealed class TextFormatterImpl : TextFormatter
&& wrappingTextLineBreak.AcquireRemainingRuns() is { } remainingRuns
&& paragraphProperties.TextWrapping != TextWrapping.NoWrap)
{
return PerformTextWrapping(remainingRuns, true, firstTextSourceIndex, paragraphWidth,
paragraphProperties, previousLineBreak.FlowDirection, previousLineBreak, objectPool);
return PerformTextWrapping(
remainingRuns,
true,
firstTextSourceIndex,
paragraphWidth,
paragraphProperties,
wrappingTextLineBreak.FontMetrics,
previousLineBreak.FlowDirection,
previousLineBreak,
objectPool);
}

RentedList<TextRun>? fetchedRuns = null;
Expand All @@ -46,6 +54,8 @@ internal sealed class TextFormatterImpl : TextFormatter
return null;
}

var fontMetrics = CreateFontMetrics(paragraphProperties, fetchedRuns);

shapedTextRuns = ShapeTextRuns(fetchedRuns, paragraphProperties, objectPool, fontManager,
out var resolvedFlowDirection);

Expand All @@ -60,7 +70,7 @@ internal sealed class TextFormatterImpl : TextFormatter
{
var textLine = new TextLineImpl(shapedTextRuns.ToArray(), firstTextSourceIndex,
textSourceLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection, nextLineBreak);
paragraphWidth, paragraphProperties, fontMetrics, resolvedFlowDirection, nextLineBreak);

textLine.FinalizeLine();

Expand All @@ -69,8 +79,16 @@ internal sealed class TextFormatterImpl : TextFormatter
case TextWrapping.WrapWithOverflow:
case TextWrapping.Wrap:
{
return PerformTextWrapping(shapedTextRuns, false, firstTextSourceIndex, paragraphWidth,
paragraphProperties, resolvedFlowDirection, nextLineBreak, objectPool);
return PerformTextWrapping(
shapedTextRuns,
false,
firstTextSourceIndex,
paragraphWidth,
paragraphProperties,
fontMetrics,
resolvedFlowDirection,
nextLineBreak,
objectPool);
}
default:
throw new ArgumentOutOfRangeException(nameof(paragraphProperties.TextWrapping));
Expand All @@ -83,6 +101,44 @@ internal sealed class TextFormatterImpl : TextFormatter
}
}

private static FontMetrics CreateFontMetrics(TextParagraphProperties paragraphProperties, IReadOnlyList<TextRun> fetchedRuns)
{
var textMetrics = paragraphProperties.DefaultTextRunProperties.Typeface.GlyphTypeface.Metrics;

var ascent = textMetrics.Ascent;
var descent = textMetrics.Descent;
var lineGap = textMetrics.LineGap;

for (int i = 0; i < fetchedRuns.Count; i++)
{
var textRun = fetchedRuns[i];

if(textRun.Properties is null)
{
continue;
}

var fontMetrics = textRun.Properties.Typeface.GlyphTypeface.Metrics;

if (ascent > fontMetrics.Ascent)
{
ascent = fontMetrics.Ascent;
}

if (descent < fontMetrics.Descent)
{
descent = fontMetrics.Descent;
}

if (lineGap < fontMetrics.LineGap)
{
lineGap = fontMetrics.LineGap;
}
}

return textMetrics with { Ascent = ascent, Descent = descent, LineGap = lineGap };
}

/// <summary>
/// Split a sequence of runs into two segments at specified length.
/// </summary>
Expand Down Expand Up @@ -693,7 +749,9 @@ public static TextLineImpl CreateEmptyTextLine(int firstTextSourceIndex, double

var textRuns = new TextRun[] { new ShapedTextRun(shapedBuffer, properties) };

var line = new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, flowDirection);
var fontMetrics = CreateFontMetrics(paragraphProperties, textRuns);

var line = new TextLineImpl(textRuns, firstTextSourceIndex, 0, paragraphWidth, paragraphProperties, fontMetrics, flowDirection);

line.FinalizeLine();

Expand All @@ -708,13 +766,21 @@ public static TextLineImpl CreateEmptyTextLine(int firstTextSourceIndex, double
/// <param name="firstTextSourceIndex">The first text source index.</param>
/// <param name="paragraphWidth">The paragraph width.</param>
/// <param name="paragraphProperties">The text paragraph properties.</param>
/// <param name="fontMetrics">The overall font metrics.</param>
/// <param name="resolvedFlowDirection"></param>
/// <param name="currentLineBreak">The current line break if the line was explicitly broken.</param>
/// <param name="objectPool">A pool used to get reusable formatting objects.</param>
/// <returns>The wrapped text line.</returns>
private static TextLineImpl PerformTextWrapping(List<TextRun> textRuns, bool canReuseTextRunList,
int firstTextSourceIndex, double paragraphWidth, TextParagraphProperties paragraphProperties,
FlowDirection resolvedFlowDirection, TextLineBreak? currentLineBreak, FormattingObjectPool objectPool)
private static TextLineImpl PerformTextWrapping(
List<TextRun> textRuns,
bool canReuseTextRunList,
int firstTextSourceIndex,
double paragraphWidth,
TextParagraphProperties paragraphProperties,
FontMetrics fontMetrics,
FlowDirection resolvedFlowDirection,
TextLineBreak? currentLineBreak,
FormattingObjectPool objectPool)
{
if (textRuns.Count == 0)
{
Expand Down Expand Up @@ -905,7 +971,7 @@ private static TextLineImpl PerformTextWrapping(List<TextRun> textRuns, bool can
remainingRuns.Add(postSplitRuns[i]);
}

textLineBreak = new WrappingTextLineBreak(null, resolvedFlowDirection, remainingRuns);
textLineBreak = new WrappingTextLineBreak(null, resolvedFlowDirection, fontMetrics, remainingRuns);
}
else if (currentLineBreak?.TextEndOfLine is { } textEndOfLine)
{
Expand All @@ -921,8 +987,14 @@ private static TextLineImpl PerformTextWrapping(List<TextRun> textRuns, bool can
ResetTrailingWhitespaceBidiLevels(preSplitRuns, paragraphProperties.FlowDirection, objectPool);
}

var textLine = new TextLineImpl(preSplitRuns.ToArray(), firstTextSourceIndex, measuredLength,
paragraphWidth, paragraphProperties, resolvedFlowDirection,
var textLine = new TextLineImpl(
preSplitRuns.ToArray(),
firstTextSourceIndex,
measuredLength,
paragraphWidth,
paragraphProperties,
fontMetrics,
resolvedFlowDirection,
textLineBreak);

textLine.FinalizeLine();
Expand Down
Loading
Loading