diff --git a/src/SixLabors.Fonts/TextMeasurer.cs b/src/SixLabors.Fonts/TextMeasurer.cs index fdc6a36a..41785917 100644 --- a/src/SixLabors.Fonts/TextMeasurer.cs +++ b/src/SixLabors.Fonts/TextMeasurer.cs @@ -59,6 +59,24 @@ public static FontRectangle MeasureBounds(ReadOnlySpan text, TextOptions o public static bool TryMeasureCharacterBounds(ReadOnlySpan text, TextOptions options, out GlyphBounds[] characterBounds) => TextMeasurerInt.Default.TryMeasureCharacterBounds(text, options, out characterBounds); + /// + /// Gets the number of lines contained within the text. + /// + /// The text. + /// The style. + /// The line count. + public static int CountLines(string text, TextOptions options) + => TextMeasurerInt.Default.CountLines(text.AsSpan(), options); + + /// + /// Gets the number of lines contained within the text. + /// + /// The text. + /// The style. + /// The line count. + public static int CountLines(ReadOnlySpan text, TextOptions options) + => TextMeasurerInt.Default.CountLines(text, options); + internal static FontRectangle GetSize(IReadOnlyList glyphLayouts, float dpi) { if (glyphLayouts.Count == 0) @@ -195,6 +213,20 @@ internal FontRectangle Measure(ReadOnlySpan text, TextOptions options) return GetSize(glyphsToRender, options.Dpi); } + + /// + /// Gets the number of lines contained within the text. + /// + /// The text. + /// The style. + /// The line count. + internal int CountLines(ReadOnlySpan text, TextOptions options) + { + IReadOnlyList glyphsToRender = this.layoutEngine.GenerateLayout(text, options); + int usedLines = glyphsToRender.Count(x => x.IsStartOfLine); + + return usedLines; + } } } } diff --git a/tests/SixLabors.Fonts.Tests/TextLayoutTests.cs b/tests/SixLabors.Fonts.Tests/TextLayoutTests.cs index 8b124c4d..00c318ca 100644 --- a/tests/SixLabors.Fonts.Tests/TextLayoutTests.cs +++ b/tests/SixLabors.Fonts.Tests/TextLayoutTests.cs @@ -334,6 +334,51 @@ public void MeasureTextLeadingFraction() Assert.NotEqual(FontRectangle.Empty, measurement); } + [Theory] + [InlineData("hello world", 1)] + [InlineData("hello world\nhello world", 2)] + [InlineData("hello world\nhello world\nhello world", 3)] + public void CountLines(string text, int usedLines) + { + Font font = CreateFont(text); + int count = TextMeasurer.CountLines(text, new TextOptions(font) { Dpi = font.FontMetrics.ScaleFactor }); + + Assert.Equal(usedLines, count); + } + + [Fact] + public void CountLinesWithSpan() + { + Font font = CreateFont("hello\n!"); + + Span text = stackalloc char[] + { + 'h', + 'e', + 'l', + 'l', + 'o', + '\n', + '!' + }; + int count = TextMeasurer.CountLines(text, new TextOptions(font) { Dpi = font.FontMetrics.ScaleFactor }); + + Assert.Equal(2, count); + } + + [Theory] + [InlineData("This is a long and Honorificabilitudinitatibus califragilisticexpialidocious", 25, 7)] + [InlineData("This is a long and Honorificabilitudinitatibus califragilisticexpialidocious", 50, 7)] + [InlineData("This is a long and Honorificabilitudinitatibus califragilisticexpialidocious", 100, 6)] + [InlineData("This is a long and Honorificabilitudinitatibus califragilisticexpialidocious", 200, 6)] + public void CountLinesWrappingLength(string text, int wrappingLength, int usedLines) + { + Font font = CreateFont(text); + int count = TextMeasurer.CountLines(text, new TextOptions(font) { Dpi = font.FontMetrics.ScaleFactor, WrappingLength = wrappingLength }); + + Assert.Equal(usedLines, count); + } + public static Font CreateFont(string text) { var fc = (IFontMetricsCollection)new FontCollection();