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