// 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}"; } } }