ImageSharp/SixLabors.Fonts/TextMeasurer.cs
2026-08-03 22:31:27 +02:00

201 lines
9.1 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.Fonts {
/// <summary>
/// Encapsulates logic for laying out and then measuring text properties.
/// </summary>
public static class TextMeasurer
{
/// <inheritdoc cref="Measure(ReadOnlySpan{char}, TextOptions)"/>
public static TextMetrics Measure(string text, TextOptions options)
=> Measure(text.AsSpan(), options);
/// <summary>
/// Measures the full set of layout metrics for the supplied text in a single pass.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>A <see cref="TextMetrics"/> instance containing every measurement for the laid-out text.</returns>
public static TextMetrics Measure(ReadOnlySpan<char> text, TextOptions options)
{
TextBlock block = new(text, options);
return block.Measure(options.WrappingLength);
}
/// <inheritdoc cref="MeasureAdvance(ReadOnlySpan{char}, TextOptions)"/>
public static FontRectangle MeasureAdvance(string text, TextOptions options)
=> MeasureAdvance(text.AsSpan(), options);
/// <summary>
/// Measures the logical advance of the text in pixel units.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>The logical advance rectangle of the text if it was to be rendered.</returns>
public static FontRectangle MeasureAdvance(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return FontRectangle.Empty;
}
TextBlock block = new(text, options);
return block.MeasureAdvance(options.WrappingLength);
}
/// <inheritdoc cref="MeasureBounds(ReadOnlySpan{char}, TextOptions)"/>
public static FontRectangle MeasureBounds(string text, TextOptions options)
=> MeasureBounds(text.AsSpan(), options);
/// <inheritdoc cref="MeasureRenderableBounds(ReadOnlySpan{char}, TextOptions)"/>
public static FontRectangle MeasureRenderableBounds(string text, TextOptions options)
=> MeasureRenderableBounds(text.AsSpan(), options);
/// <summary>
/// Measures the rendered glyph bounds of the text in pixel units.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>The rendered glyph bounds of the text if it was to be rendered.</returns>
public static FontRectangle MeasureBounds(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return FontRectangle.Empty;
}
TextBlock block = new(text, options);
return block.MeasureBounds(options.WrappingLength);
}
/// <summary>
/// Measures the full renderable bounds of the text in pixel units.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>
/// The union of the logical advance rectangle and the rendered glyph bounds if the text was to be rendered.
/// </returns>
public static FontRectangle MeasureRenderableBounds(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return FontRectangle.Empty;
}
TextBlock block = new(text, options);
return block.MeasureRenderableBounds(options.WrappingLength);
}
/// <inheritdoc cref="GetGlyphMetrics(ReadOnlySpan{char}, TextOptions)"/>
public static ReadOnlyMemory<GlyphMetrics> GetGlyphMetrics(string text, TextOptions options)
=> GetGlyphMetrics(text.AsSpan(), options);
/// <summary>
/// Gets the positioned metrics of each laid-out glyph entry in pixel units.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>A read-only memory region containing the per-glyph metrics entries of the text if it was to be rendered.</returns>
public static ReadOnlyMemory<GlyphMetrics> GetGlyphMetrics(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return ReadOnlyMemory<GlyphMetrics>.Empty;
}
TextBlock block = new(text, options);
return block.GetGlyphMetrics(options.WrappingLength);
}
/// <inheritdoc cref="GetGraphemeMetrics(ReadOnlySpan{char}, TextOptions)"/>
public static ReadOnlyMemory<GraphemeMetrics> GetGraphemeMetrics(string text, TextOptions options)
=> GetGraphemeMetrics(text.AsSpan(), options);
/// <summary>
/// Gets the positioned metrics of each laid-out grapheme in pixel units.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>A read-only memory region containing the per-grapheme metrics entries of the text if it was to be rendered.</returns>
public static ReadOnlyMemory<GraphemeMetrics> GetGraphemeMetrics(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return ReadOnlyMemory<GraphemeMetrics>.Empty;
}
TextBlock block = new(text, options);
return block.GetGraphemeMetrics(options.WrappingLength);
}
/// <inheritdoc cref="GetWordMetrics(ReadOnlySpan{char}, TextOptions)"/>
public static ReadOnlyMemory<WordMetrics> GetWordMetrics(string text, TextOptions options)
=> GetWordMetrics(text.AsSpan(), options);
/// <summary>
/// Gets the positioned metrics of each Unicode word-boundary segment in pixel units.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>A read-only memory region containing the per-word-boundary segment metrics entries of the text if it was to be rendered.</returns>
public static ReadOnlyMemory<WordMetrics> GetWordMetrics(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return ReadOnlyMemory<WordMetrics>.Empty;
}
TextBlock block = new(text, options);
return block.GetWordMetrics(options.WrappingLength);
}
/// <inheritdoc cref="CountLines(ReadOnlySpan{char}, TextOptions)"/>
public static int CountLines(string text, TextOptions options)
=> CountLines(text.AsSpan(), options);
/// <summary>
/// Gets the number of laid-out lines contained within the text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>The laid-out line count.</returns>
public static int CountLines(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return 0;
}
TextBlock block = new(text, options);
return block.CountLines(options.WrappingLength);
}
/// <inheritdoc cref="GetLineMetrics(ReadOnlySpan{char}, TextOptions)"/>
public static ReadOnlyMemory<LineMetrics> GetLineMetrics(string text, TextOptions options)
=> GetLineMetrics(text.AsSpan(), options);
/// <summary>
/// Gets per-line layout metrics for the supplied text.
/// </summary>
/// <param name="text">The text to measure.</param>
/// <param name="options">The text options. <see cref="TextOptions.WrappingLength"/> controls wrapping; use <c>-1</c> to disable wrapping.</param>
/// <returns>
/// A read-only memory region containing <see cref="LineMetrics"/> in pixel units, one entry per laid-out line.
/// </returns>
public static ReadOnlyMemory<LineMetrics> GetLineMetrics(ReadOnlySpan<char> text, TextOptions options)
{
if (text.IsEmpty)
{
return ReadOnlyMemory<LineMetrics>.Empty;
}
TextBlock block = new(text, options);
return block.GetLineMetrics(options.WrappingLength);
}
}
}