//MIT, 2019-present, WinterDev
using System;
using System.Collections.Generic;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/mvar
///
/// MVAR — Metrics Variations Table
///
class MVar : TableEntry
{
public const string _N = "MVAR";
public override string Name => _N;
//The metrics variations table is used in variable fonts
//to provide variations for font-wide metric values
//found in the OS/2 table and other font tables
//The metrics variations table contains an item variation store structure to represent variation data.
//The item variation store and constituent formats are described in the chapter,
//OpenType Font Variations Common Table Formats.
//The item variation store is also used in the HVAR and GDEF tables,
//**BUT** is different from the formats for variation data used in the 'cvar' or 'gvar' tables.
//The item variation store format uses delta-set indices to reference variation delta data
//for particular target font-data items to which they are applied.
//Data external to the item variation store identifies the delta-set index to be used for each given target item.
//Within the MVAR table, an array of value tag records identifies a set of target items,
//and provides the delta-set index used for each.
//The target items are identified by four-byte tags,
//with a given tag representing some font-wide value found in another table
//The item variation store format uses a two-level organization for variation data:
//a store can have multiple item variation data subtables, and each subtable has multiple delta-set rows.
//A delta-set index is a two-part index:
//an outer index that selects a particular item variation data subtable,
//and an inner index that selects a particular delta-set row within that subtable.
//A value record specifies both the outer and inner portions of the delta-set index.
public ValueRecord[] valueRecords;
public ItemVariationStoreTable itemVariationStore;
public MVar()
{
}
protected override void ReadContentFrom(BinaryReader reader)
{
long startAt = reader.BaseStream.Position;
//Metrics variations table:
//Type Name Description
//uint16 majorVersion Major version number of the metrics variations table — set to 1.
//uint16 minorVersion Minor version number of the metrics variations table — set to 0.
//uint16 (reserved) Not used; set to 0.
//uint16 valueRecordSize The size in bytes of each value record — must be greater than zero.
//uint16 valueRecordCount The number of value records — may be zero.
//Offset16 itemVariationStoreOffset Offset in bytes from the start of this table to the item variation store table.
// If valueRecordCount is zero, set to zero;
// if valueRecordCount is greater than zero, must be greater than zero.
//ValueRecord valueRecords[valueRecordCount] Array of value records that identify target items and the associated delta-set index for each.
// The valueTag records must be in binary order of their valueTag field.
//-----------
//
//The valueRecordSize field indicates the size of each value record.
//Future, minor version updates of the MVAR table may define compatible extensions to the value record format with additional fields.
//**Implementations must use the valueRecordSize field to determine the start of each record.**
//The valueRecords array is an array of value records that identify the target,
//font -wide measures for which variation adjustment data is provided (target items),
//and outer and inner delta-set indices for each item into the item variation store data.
ushort majorVersion = reader.ReadUInt16();
ushort minorVersion = reader.ReadUInt16();
ushort reserved = reader.ReadUInt16();
ushort valueRecordSize = reader.ReadUInt16();
ushort valueRecordCount = reader.ReadUInt16();
ushort itemVariationStoreOffset = reader.ReadUInt16();
valueRecords = new ValueRecord[valueRecordCount];
for (int i = 0; i < valueRecordCount; ++i)
{
long recStartAt = reader.BaseStream.Position;
valueRecords[i] = new ValueRecord(
reader.ReadUInt32(),
reader.ReadUInt16(),
reader.ReadUInt16()
);
reader.BaseStream.Position = recStartAt + valueRecordSize;//**Implementations must use the valueRecordSize field to determine the start of each record.**
}
//
//item variation store table
if (valueRecordCount > 0)
{
reader.BaseStream.Position = startAt + itemVariationStoreOffset;
itemVariationStore = new ItemVariationStoreTable();
itemVariationStore.ReadContentFrom(reader);
}
}
public readonly struct ValueRecord
{
//ValueRecord:
//Type Name Description
//Tag valueTag Four-byte tag identifying a font-wide measure.
//uint16 deltaSetOuterIndex A delta-set outer index — used to select an item variation data subtable within the item variation store.
//uint16 deltaSetInnerIndex A delta-set inner index — used to select a delta-set row within an item variation data subtable.
//The value records must be given in binary order of the valueTag values.
//Each tag identifies a font-wide measure found in some other font table.
//For example, if a value record has a value tag of 'hasc',
//this corresponds to the OS/2.sTypoAscender field. Details on the tags used within the MVAR table are provided below.
public readonly uint tag;
public readonly ushort deltaSetOuterIndex;
public readonly ushort deltaSetInnerIndex;
public ValueRecord(uint tag, ushort deltaSetOuterIndex, ushort deltaSetInnerIndex)
{
this.tag = tag;
this.deltaSetOuterIndex = deltaSetOuterIndex;
this.deltaSetInnerIndex = deltaSetInnerIndex;
//Tags in the metrics variations table are case sensitive.
//Tags defined in this table use only lowercase letters or digits.
//Tags that are used in a font’s metrics variations table should be those that are documented in this table specification.
//A font may also use privately-defined tags, which have semantics known only by private agreement.
//Private-use tags must use begin with an uppercase letter and use only uppercase letters or digits.
//If a private-use tag is used in a given font, any application that does not recognize that tag should ignore it.
}
public string TranslatedTag => Utils.TagToString(tag);
#if DEBUG
public override string ToString()
{
return Utils.TagToString(tag) + ",outer:" + deltaSetOuterIndex + ",inner:" + deltaSetInnerIndex;
}
#endif
}
class ValueTagInfo
{
public readonly string Tag;
public readonly string Mnemonic;
public readonly string ValueRepresented;
public ValueTagInfo(string tag, string mnemonic, string valueRepresented)
{
this.Tag = tag;
this.Mnemonic = mnemonic;
this.ValueRepresented = valueRepresented;
}
#if DEBUG
public override string ToString()
{
return Tag + ", " + Mnemonic + ", " + ValueRepresented;
}
#endif
}
static class ValueTags
{
static Dictionary s_registerTags = new Dictionary();
public static bool TryGetValueTagInfo(string tag, out ValueTagInfo valueTagInfo)
{
return s_registerTags.TryGetValue(tag, out valueTagInfo);
}
static void RegisterValueTagInfo(string tag, string mnemonic, string valueRepresented)
{
s_registerTags.Add(tag, new ValueTagInfo(tag, mnemonic, valueRepresented));
}
static ValueTags()
{
//Value tags, ordered by logical grouping:
//Tag Mnemonic Value represented
//'hasc' horizontal ascender OS/2.sTypoAscender
//'hdsc' horizontal descender OS/2.sTypoDescender
//'hlgp' horizontal line gap OS/2.sTypoLineGap
//'hcla' horizontal clipping ascent OS/2.usWinAscent
//'hcld' horizontal clipping descent OS/2.usWinDescent
RegisterValueTagInfo("hasc", "horizontal ascender", "OS/2.sTypoAscender");
RegisterValueTagInfo("hdsc", "horizontal descender", "OS/2.sTypoDescender");
RegisterValueTagInfo("hlgp", "horizontal line gap", "OS/2.sTypoLineGap");
RegisterValueTagInfo("hcla", "horizontal clipping ascent", "OS/2.usWinAscent");
RegisterValueTagInfo("hcld", "horizontal clipping descent", "OS/2.usWinDescent");
//Tag Mnemonic Value represented
//'vasc' vertical ascender vhea.ascent
//'vdsc' vertical descender vhea.descent
//'vlgp' vertical line gap vhea.lineGap
RegisterValueTagInfo("vasc", "vertical ascender", "vhea.ascent");
RegisterValueTagInfo("vdsc", "vertical descender", "vhea.descent");
RegisterValueTagInfo("vlgp", "vertical line gap", "vhea.lineGap");
//Tag Mnemonic Value represented
//'hcrs' horizontal caret rise hhea.caretSlopeRise
//'hcrn' horizontal caret run hhea.caretSlopeRun
//'hcof' horizontal caret offset hhea.caretOffset
RegisterValueTagInfo("hcrs", "horizontal caret rise", "hhea.caretSlopeRise");
RegisterValueTagInfo("hcrn", "horizontal caret run", "hhea.caretSlopeRun");
RegisterValueTagInfo("hcof", "horizontal caret offset", "hhea.caretOffset");
//Tag Mnemonic Value represented
//'vcrs' vertical caret rise vhea.caretSlopeRise
//'vcrn' vertical caret run vhea.caretSlopeRun
//'vcof' vertical caret offset vhea.caretOffset
RegisterValueTagInfo("vcrs", "vertical caret rise", "vhea.caretSlopeRise");
RegisterValueTagInfo("vcrn", "vertical caret run", "vhea.caretSlopeRun");
RegisterValueTagInfo("vcof", "vertical caret offset", "vhea.caretOffset");
//Tag Mnemonic Value represented
//'xhgt' x height OS/2.sxHeight
//'cpht' cap height OS/2.sCapHeight
//'sbxs' subscript em x size OS/2.ySubscriptXSize
//'sbys' subscript em y size OS/2.ySubscriptYSize
//'sbxo' subscript em x offset OS/2.ySubscriptXOffset
//'sbyo' subscript em y offset OS/2.ySubscriptYOffset
//'spxs' superscript em x size OS/2.ySuperscriptXSize
//'spys' superscript em y size OS/2.ySuperscriptYSize
//'spxo' superscript em x offset OS/2.ySuperscriptXOffset
//'spyo' superscript em y offset OS/2.ySuperscriptYOffset
//'strs' strikeout size OS/2.yStrikeoutSize
//'stro' strikeout offset OS/2.yStrikeoutPosition
RegisterValueTagInfo("xhgt", "x height", "OS/2.sTypoAscender");
RegisterValueTagInfo("cpht", "cap height", "OS/2.sTypoDescender");
RegisterValueTagInfo("sbxs", "subscript em x size", "OS/2.ySubscriptXSize");
RegisterValueTagInfo("sbys", "subscript em y size", "OS/2.ySubscriptYSize");
RegisterValueTagInfo("sbxo", "subscript em x offset", "OS/2.ySubscriptXOffset");
RegisterValueTagInfo("sbyo", "subscript em y offset", "OS/2.ySubscriptYOffset");
RegisterValueTagInfo("spxs", "superscript em x size", "OS/2.ySuperscriptXSize");
RegisterValueTagInfo("spys", "superscript em y size", "OS/2.ySuperscriptYSize");
RegisterValueTagInfo("spxo", "superscript em x offset", "OS/2.ySuperscriptXOffset");
RegisterValueTagInfo("spyo", "superscript em y offset", "OS/2.ySuperscriptYOffset");
RegisterValueTagInfo("strs", "strikeout size", "OS/2.yStrikeoutSize");
RegisterValueTagInfo("stro", "strikeout offset", "OS/2.yStrikeoutPosition");
//Tag Mnemonic Value represented
//'unds' underline size post.underlineThickness
//'undo' underline offset post.underlinePosition
RegisterValueTagInfo("unds", "underline size", "post.underlineThickness");
RegisterValueTagInfo("undo", "underline offset", "post.underlinePosition");
//Tag Mnemonic Value represented
//'gsp0' gaspRange[0] gasp.gaspRange[0].rangeMaxPPEM
//'gsp1' gaspRange[1] gasp.gaspRange[1].rangeMaxPPEM
//'gsp2' gaspRange[2] gasp.gaspRange[2].rangeMaxPPEM
//'gsp3' gaspRange[3] gasp.gaspRange[3].rangeMaxPPEM
//'gsp4' gaspRange[4] gasp.gaspRange[4].rangeMaxPPEM
//'gsp5' gaspRange[5] gasp.gaspRange[5].rangeMaxPPEM
//'gsp6' gaspRange[6] gasp.gaspRange[6].rangeMaxPPEM
//'gsp7' gaspRange[7] gasp.gaspRange[7].rangeMaxPPEM
//'gsp8' gaspRange[8] gasp.gaspRange[8].rangeMaxPPEM
//'gsp9' gaspRange[9] gasp.gaspRange[9].rangeMaxPPEM
RegisterValueTagInfo("gsp0", "gaspRange[0]", "gasp.gaspRange[0].rangeMaxPPEM");
RegisterValueTagInfo("gsp1", "gaspRange[1]", "gasp.gaspRange[1].rangeMaxPPEM");
RegisterValueTagInfo("gsp2", "gaspRange[2]", "gasp.gaspRange[2].rangeMaxPPEM");
RegisterValueTagInfo("gsp3", "gaspRange[3]", "gasp.gaspRange[3].rangeMaxPPEM");
RegisterValueTagInfo("gsp4", "gaspRange[4]", "gasp.gaspRange[4].rangeMaxPPEM");
RegisterValueTagInfo("gsp5", "gaspRange[5]", "gasp.gaspRange[5].rangeMaxPPEM");
RegisterValueTagInfo("gsp6", "gaspRange[6]", "gasp.gaspRange[6].rangeMaxPPEM");
RegisterValueTagInfo("gsp7", "gaspRange[7]", "gasp.gaspRange[7].rangeMaxPPEM");
RegisterValueTagInfo("gsp8", "gaspRange[8]", "gasp.gaspRange[8].rangeMaxPPEM");
RegisterValueTagInfo("gsp9", "gaspRange[9]", "gasp.gaspRange[9].rangeMaxPPEM");
}
}
}
}