// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
namespace SixLabors.Fonts.Tables.General {
///
/// Represents the OS/2 and Windows metrics table, which contains metrics required for Windows and OS/2.
///
///
internal sealed class OS2Table : Table
{
///
/// The table name identifier.
///
internal const string TableName = "OS/2";
///
/// The font embedding licensing rights (fsType).
///
private readonly ushort styleType;
///
/// The PANOSE classification number.
///
private readonly byte[] panose;
///
/// The cap height in font design units.
///
private readonly short capHeight;
///
/// The font family class and subclass (sFamilyClass).
///
private readonly short familyClass;
///
/// The x-height in font design units.
///
private readonly short heightX;
///
/// The four-character font vendor identification tag.
///
private readonly string tag;
///
/// The code page range bits 0-31.
///
private readonly ushort codePageRange1;
///
/// The code page range bits 32-63.
///
private readonly ushort codePageRange2;
///
/// The Unicode range bits 0-31.
///
private readonly uint unicodeRange1;
///
/// The Unicode range bits 32-63.
///
private readonly uint unicodeRange2;
///
/// The Unicode range bits 64-95.
///
private readonly uint unicodeRange3;
///
/// The Unicode range bits 96-127.
///
private readonly uint unicodeRange4;
///
/// The break character (usBreakChar).
///
private readonly ushort breakChar;
///
/// The default character displayed when a requested character is not in the font.
///
private readonly ushort defaultChar;
///
/// The minimum Unicode index in this font.
///
private readonly ushort firstCharIndex;
///
/// The maximum Unicode index in this font.
///
private readonly ushort lastCharIndex;
///
/// The lower value of the size range for which this font is designed (version 5+).
///
private readonly ushort lowerOpticalPointSize;
///
/// The maximum length of a target glyph context for any feature in this font.
///
private readonly ushort maxContext;
///
/// The upper value of the size range for which this font is designed (version 5+).
///
private readonly ushort upperOpticalPointSize;
///
/// The visual weight class of the font (usWeightClass).
///
private readonly ushort weightClass;
///
/// The relative change from the normal aspect ratio (usWidthClass).
///
private readonly ushort widthClass;
///
/// The average weighted width of the lower case letters and space.
///
private readonly short averageCharWidth;
///
/// Initializes a new instance of the class with version 0 fields.
///
/// The average character width.
/// The visual weight class.
/// The relative width class.
/// The embedding licensing rights.
/// The horizontal size for subscripts.
/// The vertical size for subscripts.
/// The horizontal offset for subscripts.
/// The vertical offset for subscripts.
/// The horizontal size for superscripts.
/// The vertical size for superscripts.
/// The horizontal offset for superscripts.
/// The vertical offset for superscripts.
/// The width of the strikeout stroke.
/// The position of the strikeout stroke relative to the baseline.
/// The font family class and subclass.
/// The PANOSE classification bytes.
/// Unicode range bits 0-31.
/// Unicode range bits 32-63.
/// Unicode range bits 64-95.
/// Unicode range bits 96-127.
/// The four-character vendor identification tag.
/// The font style selection flags.
/// The minimum Unicode index.
/// The maximum Unicode index.
/// The typographic ascender.
/// The typographic descender.
/// The typographic line gap.
/// The Windows ascent metric.
/// The Windows descent metric.
public OS2Table(
short averageCharWidth,
ushort weightClass,
ushort widthClass,
ushort styleType,
short subscriptXSize,
short subscriptYSize,
short subscriptXOffset,
short subscriptYOffset,
short superscriptXSize,
short superscriptYSize,
short superscriptXOffset,
short superscriptYOffset,
short strikeoutSize,
short strikeoutPosition,
short familyClass,
byte[] panose,
uint unicodeRange1,
uint unicodeRange2,
uint unicodeRange3,
uint unicodeRange4,
string tag,
FontStyleSelection fontStyle,
ushort firstCharIndex,
ushort lastCharIndex,
short typoAscender,
short typoDescender,
short typoLineGap,
ushort winAscent,
ushort winDescent)
{
this.averageCharWidth = averageCharWidth;
this.weightClass = weightClass;
this.widthClass = widthClass;
this.styleType = styleType;
this.SubscriptXSize = subscriptXSize;
this.SubscriptYSize = subscriptYSize;
this.SubscriptXOffset = subscriptXOffset;
this.SubscriptYOffset = subscriptYOffset;
this.SuperscriptXSize = superscriptXSize;
this.SuperscriptYSize = superscriptYSize;
this.SuperscriptXOffset = superscriptXOffset;
this.SuperscriptYOffset = superscriptYOffset;
this.StrikeoutSize = strikeoutSize;
this.StrikeoutPosition = strikeoutPosition;
this.familyClass = familyClass;
this.panose = panose;
this.unicodeRange1 = unicodeRange1;
this.unicodeRange2 = unicodeRange2;
this.unicodeRange3 = unicodeRange3;
this.unicodeRange4 = unicodeRange4;
this.tag = tag;
this.FontStyle = fontStyle;
this.firstCharIndex = firstCharIndex;
this.lastCharIndex = lastCharIndex;
this.TypoAscender = typoAscender;
this.TypoDescender = typoDescender;
this.TypoLineGap = typoLineGap;
this.WinAscent = winAscent;
this.WinDescent = winDescent;
}
///
/// Initializes a new instance of the class with version 1-4 fields.
///
/// The base version 0 table to extend.
/// Code page range bits 0-31.
/// Code page range bits 32-63.
/// The x-height.
/// The cap height.
/// The default character index.
/// The break character index.
/// The maximum target glyph context length.
public OS2Table(
OS2Table version0Table,
ushort codePageRange1,
ushort codePageRange2,
short heightX,
short capHeight,
ushort defaultChar,
ushort breakChar,
ushort maxContext)
: this(
version0Table.averageCharWidth,
version0Table.weightClass,
version0Table.widthClass,
version0Table.styleType,
version0Table.SubscriptXSize,
version0Table.SubscriptYSize,
version0Table.SubscriptXOffset,
version0Table.SubscriptYOffset,
version0Table.SuperscriptXSize,
version0Table.SuperscriptYSize,
version0Table.SuperscriptXOffset,
version0Table.SuperscriptYOffset,
version0Table.StrikeoutSize,
version0Table.StrikeoutPosition,
version0Table.familyClass,
version0Table.panose,
version0Table.unicodeRange1,
version0Table.unicodeRange2,
version0Table.unicodeRange3,
version0Table.unicodeRange4,
version0Table.tag,
version0Table.FontStyle,
version0Table.firstCharIndex,
version0Table.lastCharIndex,
version0Table.TypoAscender,
version0Table.TypoDescender,
version0Table.TypoLineGap,
version0Table.WinAscent,
version0Table.WinDescent)
{
this.codePageRange1 = codePageRange1;
this.codePageRange2 = codePageRange2;
this.heightX = heightX;
this.capHeight = capHeight;
this.defaultChar = defaultChar;
this.breakChar = breakChar;
this.maxContext = maxContext;
}
///
/// Initializes a new instance of the class with version 5 fields.
///
/// The base table (version < 5) to extend.
/// The lower optical point size.
/// The upper optical point size.
public OS2Table(OS2Table versionLessThan5Table, ushort lowerOpticalPointSize, ushort upperOpticalPointSize)
: this(
versionLessThan5Table,
versionLessThan5Table.codePageRange1,
versionLessThan5Table.codePageRange2,
versionLessThan5Table.heightX,
versionLessThan5Table.capHeight,
versionLessThan5Table.defaultChar,
versionLessThan5Table.breakChar,
versionLessThan5Table.maxContext)
{
this.lowerOpticalPointSize = lowerOpticalPointSize;
this.upperOpticalPointSize = upperOpticalPointSize;
}
///
/// Font style selection flags (fsSelection).
///
[Flags]
internal enum FontStyleSelection : ushort
{
///
/// No style flags set.
///
NONE = 0,
///
/// Font contains italic or oblique characters.
///
ITALIC = 1,
///
/// Characters are underscored.
///
UNDERSCORE = 1 << 1,
///
/// Characters have their foreground and background reversed.
///
NEGATIVE = 1 << 2,
///
/// Outline (hollow) characters, otherwise they are solid.
///
OUTLINED = 1 << 3,
///
/// Characters are overstruck.
///
STRIKEOUT = 1 << 4,
///
/// Characters are emboldened.
///
BOLD = 1 << 5,
///
/// Characters are in the standard weight/style for the font.
///
REGULAR = 1 << 6,
///
/// If set, it is strongly recommended to use OS/2.typoAscender - OS/2.typoDescender + OS/2.typoLineGap
/// as a value for default line spacing.
///
USE_TYPO_METRICS = 1 << 7,
///
/// The font has ‘name’ table strings consistent with a weight/width/slope family
/// without requiring use of ‘name’ IDs 21 and 22.
///
WWS = 1 << 8,
///
/// Font contains oblique characters.
///
OBLIQUE = 1 << 9,
}
///
/// Gets the font style selection flags.
///
public FontStyleSelection FontStyle { get; }
///
/// Gets the typographic ascender value.
///
public short TypoAscender { get; }
///
/// Gets the typographic descender value.
///
public short TypoDescender { get; }
///
/// Gets the typographic line gap value.
///
public short TypoLineGap { get; }
///
/// Gets the Windows ascent metric used for clipping.
///
public ushort WinAscent { get; }
///
/// Gets the Windows descent metric used for clipping.
///
public ushort WinDescent { get; }
///
/// Gets the position of the strikeout stroke relative to the baseline.
///
public short StrikeoutPosition { get; }
///
/// Gets the width of the strikeout stroke in font design units.
///
public short StrikeoutSize { get; }
///
/// Gets the horizontal offset for subscript characters.
///
public short SubscriptXOffset { get; }
///
/// Gets the horizontal size for subscript characters.
///
public short SubscriptXSize { get; }
///
/// Gets the vertical offset for subscript characters.
///
public short SubscriptYOffset { get; }
///
/// Gets the vertical size for subscript characters.
///
public short SubscriptYSize { get; }
///
/// Gets the horizontal offset for superscript characters.
///
public short SuperscriptXOffset { get; }
///
/// Gets the horizontal size for superscript characters.
///
public short SuperscriptXSize { get; }
///
/// Gets the vertical offset for superscript characters.
///
public short SuperscriptYOffset { get; }
///
/// Gets the vertical size for superscript characters.
///
public short SuperscriptYSize { get; }
///
/// Loads the from the specified font reader.
///
/// The font reader.
/// The , or if the table is not present.
public static OS2Table? Load(FontReader fontReader)
{
if (!fontReader.TryGetReaderAtTablePosition(TableName, out BigEndianBinaryReader? binaryReader))
{
return null;
}
using (binaryReader)
{
return Load(binaryReader);
}
}
///
/// Loads the from the specified binary reader.
///
/// The big-endian binary reader.
/// The .
public static OS2Table Load(BigEndianBinaryReader reader)
{
// Version 1.0
// Type | Name | Comments
// -------|------------------------|-----------------------
// uint16 |version | 0x0005
// int16 |xAvgCharWidth |
// uint16 |usWeightClass |
// uint16 |usWidthClass |
// uint16 |fsType |
// int16 |ySubscriptXSize |
// int16 |ySubscriptYSize |
// int16 |ySubscriptXOffset |
// int16 |ySubscriptYOffset |
// int16 |ySuperscriptXSize |
// int16 |ySuperscriptYSize |
// int16 |ySuperscriptXOffset |
// int16 |ySuperscriptYOffset |
// int16 |yStrikeoutSize |
// int16 |yStrikeoutPosition |
// int16 |sFamilyClass |
// uint8 |panose[10] |
// uint32 |ulUnicodeRange1 | Bits 0–31
// uint32 |ulUnicodeRange2 | Bits 32–63
// uint32 |ulUnicodeRange3 | Bits 64–95
// uint32 |ulUnicodeRange4 | Bits 96–127
// Tag |achVendID |
// uint16 |fsSelection |
// uint16 |usFirstCharIndex |
// uint16 |usLastCharIndex |
// int16 |sTypoAscender |
// int16 |sTypoDescender |
// int16 |sTypoLineGap |
// uint16 |usWinAscent |
// uint16 |usWinDescent |
// uint32 |ulCodePageRange1 | Bits 0–31
// uint32 |ulCodePageRange2 | Bits 32–63
// int16 |sxHeight |
// int16 |sCapHeight |
// uint16 |usDefaultChar |
// uint16 |usBreakChar |
// uint16 |usMaxContext |
// uint16 |usLowerOpticalPointSize |
// uint16 |usUpperOpticalPointSize |
ushort version = reader.ReadUInt16(); // assert 0x0005
short averageCharWidth = reader.ReadInt16();
ushort weightClass = reader.ReadUInt16();
ushort widthClass = reader.ReadUInt16();
ushort styleType = reader.ReadUInt16();
short subscriptXSize = reader.ReadInt16();
short subscriptYSize = reader.ReadInt16();
short subscriptXOffset = reader.ReadInt16();
short subscriptYOffset = reader.ReadInt16();
short superscriptXSize = reader.ReadInt16();
short superscriptYSize = reader.ReadInt16();
short superscriptXOffset = reader.ReadInt16();
short superscriptYOffset = reader.ReadInt16();
short strikeoutSize = reader.ReadInt16();
short strikeoutPosition = reader.ReadInt16();
short familyClass = reader.ReadInt16();
byte[] panose = reader.ReadUInt8Array(10);
uint unicodeRange1 = reader.ReadUInt32(); // Bits 0–31
uint unicodeRange2 = reader.ReadUInt32(); // Bits 32–63
uint unicodeRange3 = reader.ReadUInt32(); // Bits 64–95
uint unicodeRange4 = reader.ReadUInt32(); // Bits 96–127
string tag = reader.ReadTag();
FontStyleSelection fontStyle = reader.ReadUInt16();
ushort firstCharIndex = reader.ReadUInt16();
ushort lastCharIndex = reader.ReadUInt16();
short typoAscender = reader.ReadInt16();
short typoDescender = reader.ReadInt16();
short typoLineGap = reader.ReadInt16();
ushort winAscent = reader.ReadUInt16();
ushort winDescent = reader.ReadUInt16();
var version0Table = new OS2Table(
averageCharWidth,
weightClass,
widthClass,
styleType,
subscriptXSize,
subscriptYSize,
subscriptXOffset,
subscriptYOffset,
superscriptXSize,
superscriptYSize,
superscriptXOffset,
superscriptYOffset,
strikeoutSize,
strikeoutPosition,
familyClass,
panose,
unicodeRange1,
unicodeRange2,
unicodeRange3,
unicodeRange4,
tag,
fontStyle,
firstCharIndex,
lastCharIndex,
typoAscender,
typoDescender,
typoLineGap,
winAscent,
winDescent);
if (version == 0)
{
return version0Table;
}
short heightX = 0;
short capHeight = 0;
ushort defaultChar = 0;
ushort breakChar = 0;
ushort maxContext = 0;
ushort codePageRange1 = reader.ReadUInt16(); // Bits 0–31
ushort codePageRange2 = reader.ReadUInt16(); // Bits 32–63
// fields exist only in > v1 https://docs.microsoft.com/en-us/typography/opentype/spec/os2
if (version > 1)
{
heightX = reader.ReadInt16();
capHeight = reader.ReadInt16();
defaultChar = reader.ReadUInt16();
breakChar = reader.ReadUInt16();
maxContext = reader.ReadUInt16();
}
var versionLessThan5Table = new OS2Table(
version0Table,
codePageRange1,
codePageRange2,
heightX,
capHeight,
defaultChar,
breakChar,
maxContext);
if (version < 5)
{
return versionLessThan5Table;
}
ushort lowerOpticalPointSize = reader.ReadUInt16();
ushort upperOpticalPointSize = reader.ReadUInt16();
return new OS2Table(
versionLessThan5Table,
lowerOpticalPointSize,
upperOpticalPointSize);
}
}
}