// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Numerics;
using SixLabors.Fonts.Unicode;
namespace SixLabors.Fonts {
///
/// Represents the layout positions of a glyph entry emitted from a laid-out .
///
internal readonly struct GlyphLayout
{
internal GlyphLayout(
Glyph glyph,
Font font,
Vector2 advanceOrigin,
Vector2 glyphOrigin,
Vector2 decorationOrigin,
float advanceWidth,
float advanceHeight,
GlyphLayoutMode layoutMode,
int bidiLevel,
bool isStartOfLine,
int graphemeIndex,
int stringIndex)
{
this.Glyph = glyph;
this.Font = font;
this.CodePoint = glyph.GlyphMetrics.CodePoint;
this.AdvanceOrigin = advanceOrigin;
this.GlyphOrigin = glyphOrigin;
this.DecorationOrigin = decorationOrigin;
this.AdvanceX = advanceWidth;
this.AdvanceY = advanceHeight;
this.LayoutMode = layoutMode;
this.BidiLevel = bidiLevel;
this.IsStartOfLine = isStartOfLine;
this.GraphemeIndex = graphemeIndex;
this.StringIndex = stringIndex;
}
///
/// Gets the font-specific glyph for this laid-out glyph entry.
///
public Glyph Glyph { get; }
///
/// Gets the font used to shape and render this laid-out glyph entry.
///
public Font Font { get; }
///
/// Gets the code point represented by this glyph.
///
public CodePoint CodePoint { get; }
///
/// Gets the origin of the logical advance box in DPI-normalized layout units.
///
///
/// Multiply by the target DPI to convert to device pixels.
///
public Vector2 AdvanceOrigin { get; }
///
/// Gets the origin used to render the glyph outline in DPI-normalized layout units.
///
///
/// Multiply by the target DPI to convert to device pixels.
///
public Vector2 GlyphOrigin { get; }
///
/// Gets the origin used to render text decorations in DPI-normalized layout units.
///
///
/// Multiply by the target DPI to convert to device pixels.
///
public Vector2 DecorationOrigin { get; }
///
/// Gets the advance in the x direction in DPI-normalized layout units.
///
///
/// Multiply by the target DPI to convert to device pixels.
///
public float AdvanceX { get; }
///
/// Gets the advance in the y direction in DPI-normalized layout units.
///
///
/// Multiply by the target DPI to convert to device pixels.
///
public float AdvanceY { get; }
///
/// Gets the glyph layout mode.
///
public GlyphLayoutMode LayoutMode { get; }
///
/// Gets the resolved bidi embedding level.
///
internal int BidiLevel { get; }
///
/// Gets a value indicating whether this glyph is the first glyph on a new line.
///
public bool IsStartOfLine { get; }
///
/// Gets the zero-based grapheme index in the original text.
///
public int GraphemeIndex { get; }
///
/// Gets the zero-based UTF-16 code unit index in the original text.
///
public int StringIndex { get; }
///
/// Gets a value indicating whether the glyph represents a whitespace character.
///
/// The .
public bool IsWhiteSpace() => UnicodeUtility.ShouldRenderWhiteSpaceOnly(this.CodePoint);
///
/// Measures the positioned logical advance rectangle in pixel units.
///
/// The target DPI.
/// The measured advance rectangle.
internal FontRectangle MeasureAdvance(float dpi)
=> new(
this.AdvanceOrigin.X * dpi,
this.AdvanceOrigin.Y * dpi,
this.AdvanceX * dpi,
this.AdvanceY * dpi);
///
/// Measures the rendered glyph bounds in pixel units.
///
/// The target DPI.
/// The measured rendered bounds.
internal FontRectangle MeasureBounds(float dpi)
{
// Same logic as in GlyphMetrics.RenderTo.
Vector2 glyphOrigin = this.GlyphOrigin * dpi;
FontRectangle box = this.Glyph.BoundingBox(this.LayoutMode, glyphOrigin, dpi);
// Whitespace uses the layout advance because it occupies measurable
// text space even though the renderer suppresses its outline.
if (this.IsWhiteSpace())
{
if (this.LayoutMode == GlyphLayoutMode.Vertical)
{
return new FontRectangle(
box.X,
box.Y,
box.Width,
this.AdvanceY * dpi);
}
if (this.LayoutMode == GlyphLayoutMode.VerticalRotated)
{
return new FontRectangle(
box.X,
box.Y,
0,
this.AdvanceY * dpi);
}
return new FontRectangle(
box.X,
box.Y,
this.AdvanceX * dpi,
box.Height);
}
return box;
}
///
public override string ToString()
{
string s = this.IsStartOfLine ? "@ " : string.Empty;
string ws = this.IsWhiteSpace() ? "!" : string.Empty;
Vector2 l = this.GlyphOrigin;
return $"{s}{ws}{this.CodePoint.ToDebuggerDisplay()} {l.X},{l.Y} {this.AdvanceX}x{this.AdvanceY}";
}
}
}