// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System; using System.Globalization; using System.IO; using SixLabors.Fonts.Tables; using SixLabors.Fonts.Tables.General; using SixLabors.Fonts.Tables.General.Name; using SixLabors.Fonts.WellKnownIds; namespace SixLabors.Fonts { /// /// Provides basic descriptive metadata for the font. /// public class FontDescription { private readonly NameTable nameTable; /// /// Initializes a new instance of the class. /// /// The name table. /// The os2 table. /// The head table. internal FontDescription(NameTable nameTable, OS2Table? os2, HeadTable? head) { this.nameTable = nameTable; this.Style = ConvertStyle(os2, head); this.FontNameInvariantCulture = this.FontName(CultureInfo.InvariantCulture); this.FontFamilyInvariantCulture = this.FontFamily(CultureInfo.InvariantCulture); this.FontSubFamilyNameInvariantCulture = this.FontSubFamilyName(CultureInfo.InvariantCulture); } /// /// Gets the style. /// public FontStyle Style { get; } /// /// Gets the name of the font in the invariant culture. /// public string FontNameInvariantCulture { get; } /// /// Gets the name of the font family in the invariant culture. /// public string FontFamilyInvariantCulture { get; } /// /// Gets the font sub family in the invariant culture. /// public string FontSubFamilyNameInvariantCulture { get; } /// /// Gets the name of the font. /// /// The culture to load metadata in. /// The font name. public string FontName(CultureInfo culture) => this.nameTable.FontName(culture); /// /// Gets the name of the font family. /// /// The culture to load metadata in. /// The font family name. public string FontFamily(CultureInfo culture) => this.nameTable.FontFamilyName(culture); /// /// Gets the font sub family. /// /// The culture to load metadata in. /// The font sub family name. public string FontSubFamilyName(CultureInfo culture) => this.nameTable.FontSubFamilyName(culture); /// /// Gets the name matching the given culture and id. /// If is passed this method will return the first name matching the id. /// /// The culture to load metadata in. /// The name id to match. /// The name. public string GetNameById(CultureInfo culture, KnownNameIds nameId) => this.nameTable.GetNameById(culture, nameId); /// /// Reads a from the specified stream. /// /// The file path. /// a . public static FontDescription LoadDescription(string path) { Guard.NotNullOrWhiteSpace(path, nameof(path)); using FileStream fs = File.OpenRead(path); using var reader = new FontReader(fs); return LoadDescription(reader); } /// /// Reads a from the specified stream. /// /// The stream. /// a . public static FontDescription LoadDescription(Stream stream) { Guard.NotNull(stream, nameof(stream)); // Only read the name tables. using var reader = new FontReader(stream); return LoadDescription(reader); } /// /// Reads a from the specified stream. /// /// The reader. /// /// a . /// internal static FontDescription LoadDescription(FontReader reader) { DebugGuard.NotNull(reader, nameof(reader)); // NOTE: These fields are read in their optimized order // https://docs.microsoft.com/en-gb/typography/opentype/spec/recom#optimized-table-ordering HeadTable? head = reader.TryGetTable(); OS2Table? os2 = reader.TryGetTable(); NameTable nameTable = reader.GetTable(); return new FontDescription(nameTable, os2, head); } /// /// Reads all the s from the file at the specified path (typically a .ttc file like simsun.ttc). /// /// The file path. /// A read-only memory region containing the font descriptions. public static ReadOnlyMemory LoadFontCollectionDescriptions(string path) { Guard.NotNullOrWhiteSpace(path, nameof(path)); using FileStream fs = File.OpenRead(path); return LoadFontCollectionDescriptions(fs); } /// /// Reads all the s from the specified stream (typically a .ttc file like simsun.ttc). /// /// The stream to read the font collection from. /// A read-only memory region containing the font descriptions. public static ReadOnlyMemory LoadFontCollectionDescriptions(Stream stream) { long startPos = stream.Position; using var reader = new BigEndianBinaryReader(stream, true); var ttcHeader = TtcHeader.Read(reader); var result = new FontDescription[(int)ttcHeader.NumFonts]; for (int i = 0; i < ttcHeader.NumFonts; ++i) { stream.Position = startPos + ttcHeader.OffsetTable[i]; using var fontReader = new FontReader(stream); result[i] = LoadDescription(fontReader); } return result; } private static FontStyle ConvertStyle(OS2Table? os2, HeadTable? head) { FontStyle style = FontStyle.Regular; if (os2 != null) { if ((os2.FontStyle & OS2Table.FontStyleSelection.BOLD) == OS2Table.FontStyleSelection.BOLD) { style |= FontStyle.Bold; } if ((os2.FontStyle & OS2Table.FontStyleSelection.ITALIC) == OS2Table.FontStyleSelection.ITALIC) { style |= FontStyle.Italic; } } else if (head != null) { if ((head.MacStyle & HeadTable.HeadMacStyle.Bold) == HeadTable.HeadMacStyle.Bold) { style |= FontStyle.Bold; } if ((head.MacStyle & HeadTable.HeadMacStyle.Italic) == HeadTable.HeadMacStyle.Italic) { style |= FontStyle.Italic; } } return style; } } }