//MIT, 2019-present, WinterDev using System; using System.IO; namespace Typography.OpenFont.Tables { //https://docs.microsoft.com/en-us/typography/opentype/spec/avar /// /// avar — Axis Variations Table /// class AVar : TableEntry { public const string _N = "avar"; public override string Name => _N; //The axis variations table('avar') is an optional table //used in variable fonts that use OpenType Font Variations mechanisms. //It can be used to modify aspects of how a design varies for different instances along a particular design-variation axis. //Specifically, it allows modification of the coordinate normalization that is used when processing variation data for a particular variation instance. //... //The 'avar' table must be used in combination with a font variations('fvar') table and //other required or optional tables used in variable fonts. SegmentMapRecord[] _axisSegmentMaps; protected override void ReadContentFrom(BinaryReader reader) { //The 'avar' table is comprised of a small header plus segment maps for each axis. //Axis variation table: //Type Name Description //uint16 majorVersion Major version number of the axis variations table — set to 1. //uint16 minorVersion Minor version number of the axis variations table — set to 0. //uint16 Permanently reserved; set to zero. //uint16 axisCount The number of variation axes for this font. // This must be the same number as axisCount in the 'fvar' table. //SegmentMaps axisSegmentMaps[axisCount] The segment maps array—one segment map for each axis, // in the order of axes specified in the 'fvar' table. //-------------- //There must be one segment map for each axis defined in the 'fvar' table, //and the segment maps for the different axes must be given in the order of axes specified in the 'fvar' table. //The segment map for each axis is comprised of a list of axis - value mapping records. ushort majorVersion = reader.ReadUInt16(); ushort minorVersion = reader.ReadUInt16(); ushort reserved = reader.ReadUInt16(); ushort axisCount = reader.ReadUInt16(); //Each axis value map record provides a single axis-value mapping correspondence. _axisSegmentMaps = new SegmentMapRecord[axisCount]; for (int i = 0; i < axisCount; ++i) { SegmentMapRecord segmentMap = new SegmentMapRecord(); segmentMap.ReadContent(reader); _axisSegmentMaps[i] = segmentMap; } } public class SegmentMapRecord { //SegmentMaps record: //Type Name Description //uint16 positionMapCount The number of correspondence pairs for this axis. //AxisValueMap axisValueMaps[positionMapCount] The array of axis value map records for this axis. public AxisValueMap[] axisValueMaps; public void ReadContent(BinaryReader reader) { ushort positionMapCount = reader.ReadUInt16(); axisValueMaps = new AxisValueMap[positionMapCount]; for (int i = 0; i < positionMapCount; ++i) { axisValueMaps[i] = new AxisValueMap( reader.ReadF2Dot14(), reader.ReadF2Dot14() ); } } } public readonly struct AxisValueMap { //AxisValueMap record: //Type Name Description //F2DOT14 fromCoordinate A normalized coordinate value obtained using default normalization. //F2DOT14 toCoordinate The modified, normalized coordinate value. //Axis value maps can be provided for any axis, //but are required only if the normalization mapping for an axis is being modified. //If the segment map for a given axis has any value maps, //then it must include at least three value maps: -1 to - 1, 0 to 0, and 1 to 1. //These value mappings are essential to the design of the variation mechanisms and //are required even if no additional maps are specified for a given axis. //If any of these is missing, then no modification to axis coordinate values will be made for that axis. //All of the axis value map records for a given axis must have different fromCoordinate values, //and axis value map records must be arranged in increasing order of the fromCoordinate value. //If the fromCoordinate value of a record is less than or equal to the fromCoordinate value of a previous record in the array, //then the given record may be ignored. //Also, for any given record except the first, //the toCoordinate value must be greater than or equal to the toCoordinate value of the preceding record. //This requirement ensures that there are no retrograde behaviors as the user-scale value range is traversed. //If a toCoordinate value of a record is less than that of the previous record, then the given record may be ignored. public readonly float fromCoordinate; public readonly float toCoordinate; public AxisValueMap(float fromCoordinate, float toCoordinate) { this.fromCoordinate = fromCoordinate; this.toCoordinate = toCoordinate; } #if DEBUG public override string ToString() { return "from:" + fromCoordinate + " to:" + toCoordinate; } #endif } } }