// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Numerics; using SixLabors.Fonts.Tables; using SixLabors.Fonts.Tables.AdvancedTypographic; using SixLabors.Fonts.Tables.AdvancedTypographic.Variations; using SixLabors.Fonts.Unicode; namespace SixLabors.Fonts { /// /// /// Represents a font face with metrics, which is a set of glyphs with a specific style (regular, italic, bold etc). /// /// The font source is a filesystem path. /// internal sealed class FileFontMetrics : FontMetrics { private readonly Lazy fontMetrics; public FileFontMetrics(string path) : this(path, 0) { } public FileFontMetrics(string path, long offset) : this(FontDescription.LoadDescription(path), path, offset) { } internal FileFontMetrics(FontDescription description, string path, long offset) { this.Description = description; this.Path = path; this.fontMetrics = new Lazy(() => StreamFontMetrics.LoadFont(path, offset), true); } /// public override FontDescription Description { get; } /// /// Gets the filesystem path to the font face source. /// public string Path { get; } /// /// Gets the underlying that this file-backed instance delegates to. /// internal StreamFontMetrics StreamFontMetrics => this.fontMetrics.Value; /// public override ushort UnitsPerEm => this.fontMetrics.Value.UnitsPerEm; /// public override float ScaleFactor => this.fontMetrics.Value.ScaleFactor; /// public override HorizontalMetrics HorizontalMetrics => this.fontMetrics.Value.HorizontalMetrics; /// public override VerticalMetrics VerticalMetrics => this.fontMetrics.Value.VerticalMetrics; /// public override short SubscriptXSize => this.fontMetrics.Value.SubscriptXSize; /// public override short SubscriptYSize => this.fontMetrics.Value.SubscriptYSize; /// public override short SubscriptXOffset => this.fontMetrics.Value.SubscriptXOffset; /// public override short SubscriptYOffset => this.fontMetrics.Value.SubscriptYOffset; /// public override short SuperscriptXSize => this.fontMetrics.Value.SuperscriptXSize; /// public override short SuperscriptYSize => this.fontMetrics.Value.SuperscriptYSize; /// public override short SuperscriptXOffset => this.fontMetrics.Value.SuperscriptXOffset; /// public override short SuperscriptYOffset => this.fontMetrics.Value.SuperscriptYOffset; /// public override short StrikeoutSize => this.fontMetrics.Value.StrikeoutSize; /// public override short StrikeoutPosition => this.fontMetrics.Value.StrikeoutPosition; /// public override short UnderlinePosition => this.fontMetrics.Value.UnderlinePosition; /// public override short UnderlineThickness => this.fontMetrics.Value.UnderlineThickness; /// public override float ItalicAngle => this.fontMetrics.Value.ItalicAngle; /// internal override bool TryGetGlyphId(CodePoint codePoint, out ushort glyphId) => this.fontMetrics.Value.TryGetGlyphId(codePoint, out glyphId); /// internal override bool TryGetGlyphId( CodePoint codePoint, CodePoint? nextCodePoint, out ushort glyphId, out bool skipNextCodePoint) => this.fontMetrics.Value.TryGetGlyphId(codePoint, nextCodePoint, out glyphId, out skipNextCodePoint); /// internal override bool TryGetCodePoint(ushort glyphId, out CodePoint codePoint) => this.fontMetrics.Value.TryGetCodePoint(glyphId, out codePoint); /// internal override bool TryGetGlyphClass(ushort glyphId, [NotNullWhen(true)] out GlyphClassDef? glyphClass) => this.fontMetrics.Value.TryGetGlyphClass(glyphId, out glyphClass); /// internal override bool TryGetMarkAttachmentClass(ushort glyphId, [NotNullWhen(true)] out GlyphClassDef? markAttachmentClass) => this.fontMetrics.Value.TryGetMarkAttachmentClass(glyphId, out markAttachmentClass); /// public override bool TryGetVariationAxes(out ReadOnlyMemory variationAxes) => this.fontMetrics.Value.TryGetVariationAxes(out variationAxes); /// internal override bool IsInMarkFilteringSet(ushort markGlyphSetIndex, ushort glyphId) => this.fontMetrics.Value.IsInMarkFilteringSet(markGlyphSetIndex, glyphId); /// public override bool TryGetGlyphMetrics( CodePoint codePoint, TextAttributes textAttributes, TextDecorations textDecorations, LayoutMode layoutMode, ColorFontSupport support, [NotNullWhen(true)] out FontGlyphMetrics? metrics) => this.fontMetrics.Value.TryGetGlyphMetrics(codePoint, textAttributes, textDecorations, layoutMode, support, out metrics); /// internal override FontGlyphMetrics GetGlyphMetrics( CodePoint codePoint, ushort glyphId, TextAttributes textAttributes, TextDecorations textDecorations, LayoutMode layoutMode, ColorFontSupport support) => this.fontMetrics.Value.GetGlyphMetrics(codePoint, glyphId, textAttributes, textDecorations, layoutMode, support); /// public override ReadOnlyMemory GetAvailableCodePoints() => this.fontMetrics.Value.GetAvailableCodePoints(); /// internal override bool TryGetGSubTable([NotNullWhen(true)] out GSubTable? gSubTable) => this.fontMetrics.Value.TryGetGSubTable(out gSubTable); /// internal override void ApplySubstitution(GlyphSubstitutionCollection collection) => this.fontMetrics.Value.ApplySubstitution(collection); /// internal override bool TryGetKerningOffset(ushort currentId, ushort nextId, out Vector2 vector) => this.fontMetrics.Value.TryGetKerningOffset(currentId, nextId, out vector); /// internal override void UpdatePositions(GlyphPositioningCollection collection) => this.fontMetrics.Value.UpdatePositions(collection); /// internal override float GetGDefVariationDelta(uint packedVariationIndex) => this.fontMetrics.Value.GetGDefVariationDelta(packedVariationIndex); /// internal override ReadOnlySpan GetNormalizedCoordinates() => this.fontMetrics.Value.GetNormalizedCoordinates(); /// /// Reads a from the specified stream. /// /// The file path. /// A read-only memory region containing the font metrics. public static ReadOnlyMemory LoadFontCollection(string path) { using FileStream fs = File.OpenRead(path); long startPos = fs.Position; using BigEndianBinaryReader reader = new(fs, true); TtcHeader ttcHeader = TtcHeader.Read(reader); FileFontMetrics[] fonts = new FileFontMetrics[(int)ttcHeader.NumFonts]; for (int i = 0; i < ttcHeader.NumFonts; ++i) { fs.Position = startPos + ttcHeader.OffsetTable[i]; FontDescription description = FontDescription.LoadDescription(fs); fonts[i] = new FileFontMetrics(description, path, ttcHeader.OffsetTable[i]); } return fonts; } } }