Temporary solution to read woff2

This commit is contained in:
ema
2024-12-30 04:21:24 +08:00
parent ffecab95be
commit 4eb4251db5
91 changed files with 34874 additions and 4 deletions

View File

@@ -0,0 +1,127 @@
//MIT, 2019-present, WinterDev
using System;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/avar
/// <summary>
/// avar — Axis Variations Table
/// </summary>
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 <reserved> 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
}
}
}

View File

@@ -0,0 +1,28 @@
//MIT, 2019-present, WinterDev
using System;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/cvar
/// <summary>
/// cvar — CVT Variations Table
/// </summary>
class CVar : TableEntry
{
public const string _N = "cvar";
public override string Name => _N;
public CVar()
{
//The control value table (CVT) variations table is used in variable fonts to provide variation data for CVT values.
//For a general overview of OpenType Font Variations
}
protected override void ReadContentFrom(BinaryReader reader)
{
}
}
}

View File

@@ -0,0 +1,119 @@
//MIT, 2019-present, WinterDev
using System;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
//Item variation stores are used for most variation data other than that used for TrueType glyph outlines,
//including the variation data in MVAR, HVAR, VVAR, BASE and GDEF tables.
//Note: For CFF2 glyph outlines, delta values are interleaved directly within the glyph outline description in the CFF2 table.
//The sets of regions which are associated with the delta sets are defined in an item variation store,
//contained as a subtable within the CFF2 table.
//See the CFF2 chapter for additional details.
//...
//The item variation store includes a variation region list and an array of item variation data subtables
class ItemVariationStoreTable
{
public VariationRegion[] variationRegions;
public void ReadContentFrom(BinaryReader reader)
{
//VariationRegionList:
//Type Name Description
//uint16 axisCount The number of variation axes for this font.This must be the same number as axisCount in the 'fvar' table.
//uint16 regionCount The number of variation region tables in the variation region list.
//VariationRegion variationRegions[regionCount] Array of variation regions.
//The regions can be in any order.
//The regions are defined using an array of RegionAxisCoordinates records, one for each axis defined in the 'fvar' table:
ushort axisCount = reader.ReadUInt16();
ushort regionCount = reader.ReadUInt16();
variationRegions = new VariationRegion[regionCount];
for (int i = 0; i < regionCount; ++i)
{
var variationRegion = new VariationRegion();
variationRegion.ReadContent(reader, axisCount);
variationRegions[i] = variationRegion;
}
}
}
class VariationRegion
{
//VariationRegion record:
//Type Name Description
//RegionAxisCoordinates regionAxes[axisCount] Array of region axis coordinates records, in the order of axes given in the 'fvar' table.
//Each RegionAxisCoordinates record provides coordinate values for a region along a single axis:
public RegionAxisCoordinate[] regionAxes;
public void ReadContent(BinaryReader reader, int axisCount)
{
regionAxes = new RegionAxisCoordinate[axisCount];
for (int i = 0; i < axisCount; ++i)
{
regionAxes[i] = new RegionAxisCoordinate(
reader.ReadF2Dot14(), //start
reader.ReadF2Dot14(), //peak
reader.ReadF2Dot14() //end
);
}
}
}
readonly struct RegionAxisCoordinate
{
//RegionAxisCoordinates record:
//Type Name Description
//F2DOT14 startCoord The region start coordinate value for the current axis.
//F2DOT14 peakCoord The region peak coordinate value for the current axis.
//F2DOT14 endCoord The region end coordinate value for the current axis.
public readonly float startCoord;
public readonly float peakCoord;
public readonly float endCoord;
public RegionAxisCoordinate(float startCoord, float peakCoord, float endCoord)
{
this.startCoord = startCoord;
this.peakCoord = peakCoord;
this.endCoord = endCoord;
//The three values must all be within the range - 1.0 to + 1.0.
//startCoord must be less than or equal to peakCoord,
//and peakCoord must be less than or equal to endCoord.
//The three values must be either all non-positive or all non-negative with one possible exception:
//if peakCoord is zero, then startCoord can be negative or 0 while endCoord can be positive or zero.
//...
//Note: The following guidelines are used for setting the three values in different scenarios:
//In the case of a non-intermediate region for which the given axis should factor into the scalar calculation for the region,
//either startCoord and peakCoord are set to a negative value(typically, -1.0)
//and endCoord is set to zero, or startCoord is set to zero and peakCoord and endCoord are set to a positive value(typically + 1.0).
//In the case of an intermediate region for which the given axis should factor into the scalar calculation for the region,
//startCoord, peakCoord and endCoord are all set to non - positive values or are all set to non - negative values.
//If the given axis should not factor into the scalar calculation for a region,
//then this is achieved by setting peakCoord to zero.
//In this case, startCoord can be any non - positive value, and endCoord can be any non - negative value.
//It is recommended either that all three be set to zero, or that startCoord be set to - 1.0 and endCoord be set to + 1.0.
}
#if DEBUG
public override string ToString()
{
return "start:" + startCoord + ",peak:" + peakCoord + ",end:" + endCoord;
}
#endif
}
}

View File

@@ -0,0 +1,504 @@
//MIT, 2019-present, WinterDev
using System;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
//Tuple variation stores are used in the 'gvar' and 'cvar' tables,
//and organize sets of variation data into groupings,
//each of which is associated with a particular region of applicability within the variation space.
//Within the 'gvar' table, there is a separate variation store for each glyph.
//Within the 'cvar' table, there is one variation store providing variations for all CVT values.
//There is a minor difference in the top-level structure of the store in these two contexts.
//Within the 'cvar' table,
//it is the entire 'cvar' table that comprises the specific variation store format,
//with a header that begins with major/minor version fields.
//The specific variation store format for glyph-specific data within the 'gvar' table is
//the **GlyphVariationData table(one per glyph ID)**,which does not include any version fields.
//In other respects, the 'cvar' table and GlyphVariationData table formats are the same.
//There is also a minor difference in certain data that can occur in a GlyphVariationData table versus a 'cvar' table.
//Differences between the 'gvar' and 'cvar' tables will be summarized later in this section
//In terms of logical information content,
//the GlyphVariationData and 'cvar' tables consist of a set of logical, tuple variation data tables,
//each for a particular region of the variation space.
//In physical layout, however, the logical tuple variation tables are divided
//into separate parts that get stored separately: a header portion, and a serialized-data portion.
//In terms of overall structure, the GlyphVariationData table and the 'cvar' table each begin with a header,
//which is followed by serialized data.
//The header includes an array with all of the tuple variation headers.
//The serialized data include deltas and other data that will be explained below.
//---------------------------------------------------
// GlyphVariationData table / 'cvar' table
// ---- header -----------
// (include tuple variation headers)
//
// ---- serialized data---
// (adjustment deltas and other data)
//
//---------------------------------------------------
//_fig: High-level organization of tuple variation stores_
//Tuple Records
//The tuple variation store formats make reference to regions within the fonts variation space using tuple records.
//These references identify positions in terms of normalized coordinates, which use F2DOT14 values.
//Tuple record(F2DOT14):
//Type Name Description
//F2DOT14 coordinates[axisCount] Coordinate array specifying a position within the fonts variation space.
// The number of elements must match the axisCount specified in the 'fvar' table.
//----------------------------------------------------------------
//Tuple Variation Store Header
//The two variants of a tuple variation store header,
//the GlyphVariationData table header and the 'cvar' header,
//are only slightly different.The formats of each are as follows:
//GlyphVariationData header:
//Type Name Description
//uint16 tupleVariationCount A packed field. The high 4 bits are flags (see below), and the low 12 bits are the number of tuple variation tables for this glyph.The count can be any number between 1 and 4095.
//Offset16 dataOffset Offset from the start of the GlyphVariationData table to the serialized data.
//TupleVariationHeader tupleVariationHeaders[tupleVariationCount] Array of tuple variation headers.
//'cvar' table header:
//Type Name Description
//uint16 majorVersion Major version number of the 'cvar' table — set to 1.
//uint16 minorVersion Minor version number of the 'cvar' table — set to 0.
//uint16 tupleVariationCount A packed field.The high 4 bits are flags (see below), and the low 12 bits are the number of tuple variation tables. The count can be any number between 1 and 4095.
//Offset16 dataOffset Offset from the start of the 'cvar' table to the serialized data.
//TupleVariationHeader tupleVariationHeaders[tupleVariationCount] Array of tuple variation headers.
//The tupleVariationCount field contains a packed value that includes flags and the number of logical tuple variation tables — which is also the number of physical tuple variation headers.The format of the tupleVariationCount value is as follows:
//Mask Name Description
//0x8000 SHARED_POINT_NUMBERS Flag indicating that some or all tuple variation tables reference a shared set of “point” numbers.
// These shared numbers are represented as packed point number data at the start of the serialized data.
//0x7000 Reserved Reserved for future use — set to 0.
//0x0FFF COUNT_MASK Mask for the low bits to give the number of tuple variation tables.
//If the sharedPointNumbers flag is set,
//then the serialized data following the header begins with packed “point” number data.
//In the context of a GlyphVariationData table within the 'gvar' table,
//these identify outline point numbers for which deltas are explicitly provided.
//In the context of the 'cvar' table, these are interpreted as CVT indices rather than point indices.
//The format of packed point number data is described below.
//TupleVariationHeader
//The GlyphVariationData and 'cvar' header formats
//include an array of tuple variation headers.
//The TupleVariationHeader format is as follows.
class TupleVariationHeader
{
//TupleVariationHeader:
//Type Name Description
//uint16 variationDataSize The size in bytes of the serialized data for this tuple variation table.
//uint16 tupleIndex A packed field.
// The high 4 bits are flags(see below).
// The low 12 bits are an index into a shared tuple records array.
//Tuple peakTuple Peak tuple record for this tuple variation table — optional, determined by flags in the tupleIndex value.
// Note that this must always be included in the 'cvar' table.
//Tuple intermediateStartTuple Intermediate start tuple record for this tuple variation table — optional, determined by flags in the tupleIndex value.
//Tuple intermediateEndTuple Intermediate end tuple record for this tuple variation table — optional, determined by flags in the tupleIndex value.
public ushort variableDataSize;
public int flags;
public ushort indexToSharedTupleRecArray;
public TupleRecord peakTuple;
public TupleRecord intermediateStartTuple;
public TupleRecord intermediateEndTuple;
public static TupleVariationHeader Read(BinaryReader reader, int axisCount)
{
TupleVariationHeader header = new TupleVariationHeader();
header.variableDataSize = reader.ReadUInt16();
ushort tupleIndex = reader.ReadUInt16();
int flags = (tupleIndex >> 12) & 0xF; //The high 4 bits are flags(see below).
header.flags = flags; //The high 4 bits are flags(see below).
header.indexToSharedTupleRecArray = (ushort)(tupleIndex & 0x0FFF); // The low 12 bits are an index into a shared tuple records array.
if ((flags & ((int)TupleIndexFormat.EMBEDDED_PEAK_TUPLE >> 12)) == ((int)TupleIndexFormat.EMBEDDED_PEAK_TUPLE >> 12))
{
//TODO:...
header.peakTuple = TupleRecord.ReadTupleRecord(reader, axisCount);
}
if ((flags & ((int)TupleIndexFormat.INTERMEDIATE_REGION >> 12)) == ((int)TupleIndexFormat.INTERMEDIATE_REGION >> 12))
{
//TODO:...
header.intermediateStartTuple = TupleRecord.ReadTupleRecord(reader, axisCount);
header.intermediateEndTuple = TupleRecord.ReadTupleRecord(reader, axisCount);
}
return header;
}
//---------
public ushort[] PrivatePoints;
public short[] PackedDeltasXY;
//Note that the size of the TupleVariationHeader is variable,
//depending on whether peak or intermediate tuple records are included. (See below for more information.)
//The variationDataSize value indicates the size of serialized data
//for the given tuple variation table that is contained in the serialized data.
//**It does not include the size of the TupleVariationHeader.**
//Every tuple variation table has an associated peak tuple record.
//Most tuple variation tables use non-intermediate regions,
//and so require only the peak tuple record to define the region.
//- In the 'cvar' table, there is only one variation store,
// and so any given region will only need to be referenced once.
//- Within the 'gvar' table, however, there is a GlyphVariationData table for each glyph ID,
// and so any region may be referenced numerous times;
// in fact, most regions will be referenced within the GlyphVariationData tables for most glyphs.
//To provide a more efficient representation,
//the tuple variation store formats allow for an array of tuple records,
//stored outside the tuple variation store structures,
//that can be shared across many tuple variation stores.
//This is used only within the 'gvar' table; it is not needed or supported in the 'cvar' table.
//The formats alternately allow for a peak tuple record that is non-shared,
//specific to the given tuple variation table, to be embedded directly within a TupleVariationHeader.
//This is optional within the 'gvar' table,
//but required in the 'cvar' table, which does not use shared peak tuple records.
//The tupleIndex field contains a packed value that includes flags and
//an index into a shared tuple records array(not used in the 'cvar' table).
//The format of the tupleIndex field is as follows.
}
[Flags]
enum TupleIndexFormat
{
//tupleIndex format:
//Mask Name Description
//0x8000 EMBEDDED_PEAK_TUPLE Flag indicating that this tuple variation header includes an embedded peak tuple record,
// immediately after the tupleIndex field.
// If set, the low 12 bits of the tupleIndex value are ignored.
// Note that this must always be set within the 'cvar' table.
//0x4000 INTERMEDIATE_REGION Flag indicating that this tuple variation table applies to an intermediate region within the variation space.
// If set, the header includes the two intermediate - region, start and end tuple records,
// immediately after the peak tuple record(if present).
//0x2000 PRIVATE_POINT_NUMBERS Flag indicating that the serialized data for this tuple variation table includes packed “point” number data.
// If set, this tuple variation table uses that number data;
// if clear, this tuple variation table uses shared number data found at the start of the serialized data
// for this glyph variation data or 'cvar' table.
//0x1000 Reserved Reserved for future use — set to 0.
//0x0FFF TUPLE_INDEX_MASK Mask for the low 12 bits to give the shared tuple records index.
EMBEDDED_PEAK_TUPLE = 0x8000,
INTERMEDIATE_REGION = 0x4000,
PRIVATE_POINT_NUMBERS = 0x2000,
Reserved = 0x1000,
TUPLE_INDEX_MASK = 0x0FFF
//Note that the intermediateRegion flag is independent of the embeddedPeakTuple flag or
//the shared tuple records index.
//Every tuple variation table has a peak n-tuple indicated either by an embedded tuple record (always true in the 'cvar' table) or
//by an index into a shared tuple records array (only in the 'gvar' table).
//An intermediate-region tuple variation table additionally has start and end n-tuples that also get used in the interpolation process;
//these are always represented using embedded tuple records.
//Also note that the privatePointNumbers flag is independent of the sharedPointNumbers flag in the tupleVariationCount field of
//the GlyphVariationData or 'cvar' header.
//A GlyphVariationData or 'cvar' table may have shared point number data used by multiple tuple variation tables,
//but any given tuple variation table may have private point number data that it uses instead.
//As noted, the size of tuple variation headers is variable. The next TupleVariationHeader can be calculated as follows:
// const TupleVariationHeader*
// NextHeader( const TupleVariationHeader* currentHeader, int axisCount )
// {
// int bump = 2 * sizeof( uint16 );
// int tupleIndex = currentHeader->tupleIndex;
// if ( tupleIndex & embeddedPeakTuple )
// bump += axisCount * sizeof( F2DOT14 );
// if ( tupleIndex & intermediateRegion )
// bump += 2 * axisCount * sizeof( F2DOT14 );
// return (const TupleVariationHeader*)((char*)currentHeader + bump);
// }
}
readonly struct TupleRecord
{
public readonly float[] coords;
public TupleRecord(float[] coords) => this.coords = coords;
#if DEBUG
public override string ToString() => coords?.Length.ToString() ?? "0";
#endif
public static TupleRecord ReadTupleRecord(BinaryReader reader, int count)
{
float[] coords = new float[count];
for (int n = 0; n < coords.Length; ++n)
{
coords[n] = reader.ReadF2Dot14();
}
return new TupleRecord(coords);
}
}
//----------------------------------------------------------------
//Serialized Data
//After the GlyphVariationData or 'cvar' header(including the TupleVariationHeader array) is
//a block of serialized data.The offset to this block of data is provided in the header.
//The serialized data block begins with shared “point” number data,
//followed by the variation data for the tuple variation tables.
//The shared point number data is optional:
//it is present if the corresponding flag is set in the tupleVariationCount field of the header.
//If present, the shared number data is represented as packed point numbers, described below.
//---------------------------------------------------
// Serialized data block
// ---- Shared "point" numbers -----------
// (optional per flag in the header)
//
// ---- Per-tuple-variation data---
//
//---------------------------------------------------
//_fig: Organization of serialized data_
//The remaining data contains runs of data specific to individual tuple variation tables,
//in order of the tuple variation headers.
//Each TupleVariationHeader indicates the data size for the corresponding run of data for that tuple variation table.
//The per-tuple-variation-table data optionally begins with private “point” numbers,
//present if the privatePointNumbers flag is set in the tupleIndex field of the TupleVariationHeader.
//Private point numbers are represented as packed point numbers, described below.
//After the private point number data(if present),
//the tuple variation data will include packed delta data.
//The format for packed deltas is given below.
//- Within the 'gvar' table,
// there are packed deltas for X coordinates, followed by packed deltas for Y coordinates.
//---------------------------------------------------
// Per-tuple-variation data (gvar)
// ---- Private point numbers -----------
// (optional per flag in tupleVariationHeader)
//
// ---- X coordinate packed deltas---
//
// ---- Y coordinate packed deltas---
//---------------------------------------------------
//_fig: Organization 'gvar' per-tuple variation data_
//- Within the 'cvar' table, there is one set of packed deltas
//---------------------------------------------------
// Per-tuple-variation data (gvar)
// ---- Private point numbers -----------
// (optional per flag in tupleVariationHeader)
//
// ---- X coordinate packed deltas---
//
// ---- Y coordinate packed deltas---
//---------------------------------------------------
//_fig: Organization 'cvar' per-tuple variation data_
//The data size indicated in the TupleVariationHeader includes the size of the private point number data,
//if present, plus the size of the packed deltas.
//---------------------------------------------------
//Packed “Point” Numbers
//Tuple variation data specify deltas to be applied to specific items:
//X and Y coordinates for glyph outline points within the 'gvar' table, and CVT values in the 'cvar' table.
//For a given glyph, deltas may be provided for any or all of a glyphs points,
//including “phantom” points generated within the rasterizer that represent glyph side bearing points.
//(See the chapter Instructing TrueType Glyphs for more background on phantom points.)
//Similarly, within the 'cvar' table, deltas may be provided for any or all CVTs.
//The set of glyph points or CVTs for which deltas are provided is specified by packed point numbers.
//**Note: If a glyph is a composite glyph,
//then “point” numbers are component indices for the components that make up the composite glyph.
//See the 'gvar' table chapter for complete details.
//Likewise, in the context of the 'cvar' table, “point” numbers are indices for CVT entries.
//Note: Within the 'gvar' table,
//if deltas are not provided explicitly for some points,
// then inferred delta values may need to be calculated — see the 'gvar' table chapter for details.
//This does not apply to the 'cvar' table, however:
// if deltas are not provided for some CVT values,
// then no adjustments are made to those CVTs in connection with the particular tuple variation table.
//Packed point numbers are stored as a count followed by one or more runs of point number data.
//The count may be stored in one or two bytes.
//After reading the first byte, the need for a second byte can be determined.
//The count bytes are processed as follows:
// If the first byte is 0, then ...
// a second count byte is not used.
// This value has a special meaning:
// the tuple variation data provides deltas for all glyph points (including the “phantom” points), or for all CVTs.
// If the first byte is non-zero and the high bit is clear (value is 1 to 127), then ...
// a second count byte is **not used**.
// The point count is equal to the value of the first byte.
// If the high bit of the first byte is set, then ...
// a second byte is used.
// The count is read from interpreting the two bytes as a big-endian uint16 value with the high-order bit masked out.
//Thus, if the count fits in 7 bits,
//it is stored in a single byte,
//with the value 0 having a special interpretation.
//If the count does not fit in 7 bits,
//then the count is stored in the first two bytes with the high bit of the first byte set as a flag that is not part of the count — the count uses 15 bits.
//For example, a count of 0x00 indicates that deltas are provided for all point numbers / all CVTs,
//with no additional point number data required;
//a count of 0x32 indicates that there are a total of 50 point numbers specified;
//a count of 0x81 0x22 indicates that there are a total of 290 (= 0x0122) point numbers specified.
//Point number data runs follow after the count.
//Each data run begins with a control byte that specifies
//the number of point numbers defined in the run,
//and a flag bit indicating the format of the run data.
//The control bytes high bit specifies whether the run is represented in 8-bit or 16-bit values.
//The low 7 bits specify the number of elements in the run minus 1.
//The format of the control byte is as follows:
//Mask Name Description
//0x80 POINTS_ARE_WORDS Flag indicating the data type used for point numbers in this run.
// If set, the point numbers are stored as unsigned 16-bit values (uint16);
// if clear, the point numbers are stored as unsigned bytes (uint8).
//0x7F POINT_RUN_COUNT_MASK Mask for the low 7 bits of the control byte to give the number of point number elements, minus 1.
//For example, a control byte of 0x02 indicates that the run has three elements represented as uint8 values;
//a control byte of 0xD4 indicates that the run has 0x54 + 1 = 85 elements represented as uint16 values.
//In the first point run,..
// the first point number is represented directly (that is, as a difference from zero).
// Each subsequent point number in that run is stored as the difference between it and the previous point number.
//In subsequent runs,...
// all elements, including the first, represent a difference from the last point number.
//Since the values in the packed data are all unsigned,
//point numbers will be given in increasing order.
//Since the packed representation can include zero values,
//it is possible for a given point number to be repeated in the derived point number list.
//In that case, there will be multiple delta values in the deltas data associated with that point number.
//All of these deltas must be applied cumulatively to the given point.
//Packed Deltas
//Tuple variation data specify deltas to be applied to glyph point coordinates or to CVT values.
//As in the case of point number data, deltas are stored in a packed format.
//Packed delta data does not include the total number of delta values within the data.
//Logically, there are deltas for every point number or CVT index specified in the point-number data.
//Thus, the count of logical deltas is equal to the count of point numbers specified for that tuple variation table.
//But since the deltas are represented in a packed format,
//the actual count of stored values is typically less than the logical count.
//The data is read until the expected logic count of deltas is obtained.
// Note: In the 'gvar' table,
// there will be two logical deltas for each point number:
// one that applies to the X coordinate, and one that applies to the Y coordinate.
// Therefore, the total logical delta count is two times the point number count.
// The packed deltas are arranged with all of the deltas for X coordinates first, followed by the deltas for Y coordinates.
//Packed deltas are stored as a series of runs.
//Each delta run consists of a control byte followed by the actual delta values of that run.
//The control byte is a packed value with flags in the high two bits and a count in the low six bits.
//The flags specify the data size of the delta values in the run.
//The format of the control byte is as follows:
//Mask Name Description
//0x80 DELTAS_ARE_ZERO Flag indicating that this run contains no data (no explicit delta values are stored), and that all of the deltas for this run are zero.
//0x40 DELTAS_ARE_WORDS Flag indicating the data type for delta values in the run. If set, the run contains 16-bit signed deltas (int16); if clear, the run contains 8-bit signed deltas (int8).
//0x3F DELTA_RUN_COUNT_MASK Mask for the low 6 bits to provide the number of delta values in the run, minus one.
//...
//...
//-------------------------------------------------
//Differences Between 'gvar' and 'cvar' Tables
//The following is a summary of key differences between tuple variation stores in the 'gvar' and 'cvar' tables.
//- The 'gvar' table is a parent table for tuple variation stores,
// and contains one tuple variation store(the glyph variation data table) for each glyph ID.
// In contrast, the entire 'cvar' table is comprised of a single,
// slightly-extended(with version fields) tuple variation store.
//- Because the 'gvar' table contains multiple tuple variation stores,
// sharing of data between tuple variation stores is possible,
// and is used for shared tuple records.
// Because the 'cvar' table has a single tuple variation store, no possibility of shared data arises.
//- The tupleIndex field of TupleVariationHeader structures within a tuple variation store includes a flag
// that indicates whether the structure instance includes an embedded peak tuple record.
// In the 'gvar' table, this is optional.In the 'cvar' table, a peak tuple record is mandatory.
//- The serialized data includes packed “point” numbers.
// In the 'gvar' table, these refer to glyph contour point numbers or,
// in the case of a composite glyph, to component indices.
// In the context of the 'cvar' table, these are indices for CVT entries.
//- In the 'gvar' table,
// point numbers cover the points or components defined in a 'glyf' entry plus four additional “phantom” points that
// represent the glyphs horizontal and vertical advance and side bearings.
// (See the chapter, Instructing TrueType Glyphs for more background on phantom points.)
// The last four point numbers for any glyph, including composite glyphs, are for the phantom points.
//- In the 'gvar' table,
// if deltas are not provided for some points and the point indices are not represented in the point number data,
// then interpolated deltas for those points will in some cases be inferred.
// This is not done in the 'cvar' table, however.
//- In the 'gvar' table,
// the serialized data for a given region has two logical deltas for each point number:
// one for the X coordinate, and one for the Y coordinate.
// Hence the total number of deltas is twice the count of control points.
// In the 'cvar' table, however, there is only one delta for each point number.
}

View File

@@ -0,0 +1,200 @@
//MIT, 2019-present, WinterDev
using System;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/fvar
//'fvar' Header
//The format of the font variations table header is as follows.
//Note: The 'fvar' table describes a fonts variation space,
//and other variation tables provide variation data to describe
//how different data items are varied across the fonts variation space
/// <summary>
/// fvar font variations
/// </summary>
class FVar : TableEntry
{
public const string _N = "fvar";
public override string Name => _N;
public VariableAxisRecord[] variableAxisRecords;
public InstanceRecord[] instanceRecords;
//
protected override void ReadContentFrom(BinaryReader reader)
{
//Font variations header:
//Type Name Description
//uint16 majorVersion Major version number of the font variations table — set to 1.
//uint16 minorVersion Minor version number of the font variations table — set to 0.
//Offset16 axesArrayOffset Offset in bytes from the beginning of the table to the start of the VariationAxisRecord array.
//uint16 (reserved) This field is permanently reserved.Set to 2.
//uint16 axisCount The number of variation axes in the font (the number of records in the axes array).
//uint16 axisSize The size in bytes of each VariationAxisRecord — set to 20 (0x0014) for this version.
//uint16 instanceCount The number of named instances defined in the font (the number of records in the instances array).
//uint16 instanceSize The size in bytes of each InstanceRecord — set to either axisCount * sizeof(Fixed) + 4,
// or to axisCount * sizeof(Fixed) + 6.
long beginAt = reader.BaseStream.Position;
//header:
ushort majorVersion = reader.ReadUInt16();
ushort minorVersion = reader.ReadUInt16();
ushort axesArrayOffset = reader.ReadUInt16();
ushort reserved = reader.ReadUInt16();//set to 2
ushort axisCount = reader.ReadUInt16();
ushort axisSize = reader.ReadUInt16();
ushort instanceCount = reader.ReadUInt16();
ushort instanceSize = reader.ReadUInt16();
//The header is followed by axes and instances arrays.
//The location of the axes array is specified in the axesArrayOffset field;
//the instances array directly follows the axes array.
//Type Name Description
//VariationAxisRecord axes[axisCount] The variation axis array.
//InstanceRecord instances[instanceCount] The named instance array.
//Note: The axisSize and instanceSize fields indicate
//the size of the VariationAxisRecord and InstanceRecord structures.
//In this version of the 'fvar' table, the InstanceRecord structure has an optional field,
//and so two different size formulations are possible.
//Future minor-version updates of the 'fvar' table may define compatible extensions to either formats.
//***Implementations must use the axisSize and instanceSize fields to determine the start of each record.***
//The set of axes that make up the fonts variation space are defined by an array of variation axis records.
//The number of records, and the number of axes, is determined by the axisCount field.
//A functional variable font must have an axisCount value that is greater than zero.
//If axisCount is zero, then the font is not functional as a variable font, ***
//and must be treated as a non-variable font; ***
//any variation-specific tables or data is ignored.
variableAxisRecords = new VariableAxisRecord[axisCount];
for (int i = 0; i < axisCount; ++i)
{
long pos = reader.BaseStream.Position;
VariableAxisRecord varAxisRecord = new VariableAxisRecord();
varAxisRecord.ReadContent(reader);
variableAxisRecords[i] = varAxisRecord;
if (reader.BaseStream.Position != pos + axisSize)
{
//***Implementations must use the axisSize and instanceSize fields to determine the start of each record.***
reader.BaseStream.Position = pos + axisSize;
}
}
instanceRecords = new InstanceRecord[instanceCount];
for (int i = 0; i < instanceCount; ++i)
{
long pos = reader.BaseStream.Position;
InstanceRecord instanecRec = new InstanceRecord();
instanecRec.ReadContent(reader, axisCount, instanceSize);
if (reader.BaseStream.Position != pos + instanceSize)
{
//***Implementations must use the axisSize and instanceSize fields to determine the start of each record.***
reader.BaseStream.Position = pos + instanceSize;
}
}
}
public class VariableAxisRecord
{
//VariationAxisRecord
//The format of the variation axis record is as follows:
//VariationAxisRecord
//Type Name Description
//Tag axisTag Tag identifying the design variation for the axis.
//Fixed minValue The minimum coordinate value for the axis.
//Fixed defaultValue The default coordinate value for the axis.
//Fixed maxValue The maximum coordinate value for the axis.
//uint16 flags Axis qualifiers — see details below.
//uint16 axisNameID The name ID for entries in the 'name' table that provide a display name for this axis.
public string axisTag;
public float minValue;
public float defaultValue;
public float maxValue;
public ushort flags;
public ushort axisNameID;
public void ReadContent(BinaryReader reader)
{
axisTag = Utils.TagToString(reader.ReadUInt32());//4
minValue = reader.ReadFixed();//4
defaultValue = reader.ReadFixed();//4
maxValue = reader.ReadFixed();//4
flags = reader.ReadUInt16();//2
axisNameID = reader.ReadUInt16();//2
//
}
}
public class InstanceRecord
{
//InstanceRecord
//The instance record format includes an array of n-tuple coordinate arrays
//that define position within the fonts variation space.
//The n-tuple array has the following format:
//Tuple Record(Fixed):
//Type Name Description
//Fixed coordinates[axisCount] Coordinate array specifying a position within the fonts variation space.
//The format of the instance record is as follows.
//InstanceRecord:
//Type Name Description
//uint16 subfamilyNameID The name ID for entries in the 'name' table that provide subfamily names for this instance.
//uint16 flags Reserved for future use — set to 0.
//Tuple coordinates The coordinates array for this instance.
//uint16 postScriptNameID Optional.The name ID for entries in the 'name' table that provide PostScript names for this instance.
public ushort subfamilyNameID;//point to name table, will be resolved later
public ushort flags;
public TupleRecord coordinates;
public ushort postScriptNameID;//point to name table, will be resolved later
public void ReadContent(BinaryReader reader, int axisCount, int instanceRecordSize)
{
long expectedEndPos = reader.BaseStream.Position + instanceRecordSize;
subfamilyNameID = reader.ReadUInt16();
flags = reader.ReadUInt16();
float[] coords = new float[axisCount];
for (int i = 0; i < axisCount; ++i)
{
coords[i] = reader.ReadFixed();
}
coordinates = new TupleRecord(coords);
if (reader.BaseStream.Position < expectedEndPos)
{
//optional field
postScriptNameID = reader.ReadUInt16();
}
}
}
}
}

View File

@@ -0,0 +1,415 @@
//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/gvar
class GlyphVariableData
{
public List<ushort> _sharedPoints;
public TupleVariationHeader[] tupleHeaders;
}
class GVar : TableEntry
{
public const string _N = "gvar";
public override string Name => _N;
public ushort axisCount;
internal TupleRecord[] _sharedTuples;
internal GlyphVariableData[] _glyphVarDataArr; //TODO: lazy load !
public GVar()
{
}
//
protected override void ReadContentFrom(BinaryReader reader)
{
//'gvar' header
//The glyph variations table header format is as follows:
//'gvar' header:
//Type Name Description
//uint16 majorVersion Major version number of the glyph variations table — set to 1.
//uint16 minorVersion Minor version number of the glyph variations table — set to 0.
//uint16 axisCount The number of variation axes for this font.
// This must be the same number as axisCount in the 'fvar' table.
//uint16 sharedTupleCount The number of shared tuple records.
// Shared tuple records can be referenced within glyph variation data tables for multiple glyphs,
// as opposed to other tuple records stored directly within a glyph variation data table.
//Offset32 sharedTuplesOffset Offset from the start of this table to the shared tuple records.
//uint16 glyphCount The number of glyphs in this font.This must match the number of glyphs stored elsewhere in the font.
//uint16 flags Bit-field that gives the format of the offset array that follows.
// If bit 0 is clear, the offsets are uint16;
// if bit 0 is set, the offsets are uint32.
//Offset32 glyphVariationDataArrayOffset Offset from the start of this table to the array of GlyphVariationData tables.
//
//Offset16-
//-or- Offset32 glyphVariationDataOffsets[glyphCount + 1] Offsets from the start of the GlyphVariationData array to each GlyphVariationData table.
//
//-------------
long beginAt = reader.BaseStream.Position;
ushort majorVersion = reader.ReadUInt16();
ushort minorVersion = reader.ReadUInt16();
#if DEBUG
if (majorVersion != 1 && minorVersion != 0)
{
//WARN
}
#endif
axisCount = reader.ReadUInt16(); //This must be the same number as axisCount in the 'fvar' table
ushort sharedTupleCount = reader.ReadUInt16();
uint sharedTuplesOffset = reader.ReadUInt32();
ushort glyphCount = reader.ReadUInt16();
ushort flags = reader.ReadUInt16();
uint glyphVariationDataArrayOffset = reader.ReadUInt32();
uint[] glyphVariationDataOffsets = null;
if ((flags & 0x1) == 0)
{
//bit 0 is clear-> use Offset16
glyphVariationDataOffsets = reader.ReadUInt16ArrayAsUInt32Array(glyphCount);
//
//***If the short format (Offset16) is used for offsets,
//the value stored is the offset divided by 2.
//Hence, the actual offset for the location of the GlyphVariationData table within the font
//will be the value stored in the offsets array multiplied by 2.
for (int i = 0; i < glyphVariationDataOffsets.Length; ++i)
{
glyphVariationDataOffsets[i] *= 2;
}
}
else
{
//Offset32
glyphVariationDataOffsets = reader.ReadUInt32Array(glyphCount);
}
reader.BaseStream.Position = beginAt + sharedTuplesOffset;
ReadSharedTupleArray(reader, sharedTupleCount);
//GlyphVariationData array ...
long glyphVariableData_startAt = beginAt + glyphVariationDataArrayOffset;
reader.BaseStream.Position = glyphVariableData_startAt;
_glyphVarDataArr = new GlyphVariableData[glyphVariationDataOffsets.Length];
for (int i = 0; i < glyphVariationDataOffsets.Length; ++i)
{
reader.BaseStream.Position = glyphVariableData_startAt + glyphVariationDataOffsets[i];
_glyphVarDataArr[i] = ReadGlyphVariationData(reader);
}
}
void ReadSharedTupleArray(BinaryReader reader, ushort sharedTupleCount)
{
//-------------
//Shared tuples array
//-------------
//The shared tuples array provides a set of variation-space positions
//that can be referenced by variation data for any glyph.
//The shared tuples array follows the GlyphVariationData offsets array
//at the end of the 'gvar' header.
//This data is simply an array of tuple records, each representing a position in the fonts variation space.
//Shared tuples array:
//Type Name Description
//TupleRecord sharedTuples[sharedTupleCount] Array of tuple records shared across all glyph variation data tables.
//Tuple records that are in the shared array or
//that are contained directly within a given glyph variation data table
//use 2.14 values to represent normalized coordinate values.
//See the Common Table Formats chapter for details.
//
//Tuple Records
//https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
//The tuple variation store formats make reference to regions within the fonts variation space using tuple records.
//These references identify positions in terms of normalized coordinates, which use F2DOT14 values.
//Tuple record(F2DOT14):
//Type Name Description
//F2DOT14 coordinates[axisCount] Coordinate array specifying a position within the fonts variation space.
// The number of elements must match the axisCount specified in the 'fvar' table.
TupleRecord[] tupleRecords = new TupleRecord[sharedTupleCount];
for (int t = 0; t < sharedTupleCount; ++t)
{
tupleRecords[t] = TupleRecord.ReadTupleRecord(reader, axisCount);
}
_sharedTuples = tupleRecords;
}
static void ReadPackedPoints(BinaryReader reader, List<ushort> packPoints)
{
byte b0 = reader.ReadByte();
if (b0 == 0)
{
//If the first byte is 0, then a second count byte is not used.
//This value has a special meaning: the tuple variation data provides deltas for all glyph points (including the “phantom” points), or for all CVTs.
}
else if (b0 > 0 && b0 <= 127)
{
//If the first byte is non-zero and the high bit is clear (value is 1 to 127),
//then a second count byte is not used.
//The point count is equal to the value of the first byte.
ReadPackedPoints(reader, b0, packPoints);
}
else
{
//If the high bit of the first byte is set, then a second byte is used.
//The count is read from interpreting the two bytes as a big-endian uint16 value with the high-order bit masked out.
//Thus, if the count fits in 7 bits, it is stored in a single byte, with the value 0 having a special interpretation.
//If the count does not fit in 7 bits, then the count is stored in the first two bytes with the high bit of the first byte set as a flag
//that is not part of the count — the count uses 15 bits.
byte b1 = reader.ReadByte();
ReadPackedPoints(reader, ((b0 & 0x7F) << 8) | b1, packPoints);
}
}
static void ReadPackedPoints(BinaryReader reader, int point_count, List<ushort> packPoints)
{
int point_read = 0;
//for (int n = 0; n < point_count ; ++n)
while (point_read < point_count)
{
//Point number data runs follow after the count.
//Each data run begins with a control byte that specifies the number of point numbers defined in the run,
//and a flag bit indicating the format of the run data.
//The control bytes high bit specifies whether the run is represented in 8-bit or 16-bit values.
//The low 7 bits specify the number of elements in the run minus 1.
//The format of the control byte is as follows:
byte controlByte = reader.ReadByte();
//Mask Name Description
//0x80 POINTS_ARE_WORDS Flag indicating the data type used for point numbers in this run.
// If set, the point numbers are stored as unsigned 16-bit values (uint16);
// if clear, the point numbers are stored as unsigned bytes (uint8).
//0x7F POINT_RUN_COUNT_MASK Mask for the low 7 bits of the control byte to give the number of point number elements, minus 1.
int point_run_count = (controlByte & 0x7F) + 1;
//In the first point run, the first point number is represented directly (that is, as a difference from zero).
//Each subsequent point number in that run is stored as the difference between it and the previous point number.
//In subsequent runs, all elements, including the first, represent a difference from the last point number.
if (((controlByte & 0x80) == 0x80)) //point_are_uint16
{
for (int i = 0; i < point_run_count; ++i)
{
point_read++;
packPoints.Add(reader.ReadUInt16());
}
}
else
{
for (int i = 0; i < point_run_count; ++i)
{
point_read++;
packPoints.Add(reader.ReadByte());
}
}
}
}
GlyphVariableData ReadGlyphVariationData(BinaryReader reader)
{
//https://docs.microsoft.com/en-gb/typography/opentype/spec/otvarcommonformats#tuple-records
//------------
//The glyphVariationData table array
//The glyphVariationData table array follows the 'gvar' header and shared tuples array.
//Each glyphVariationData table describes the variation data for a single glyph in the font.
//GlyphVariationData header:
//Type Name Description
//uint16 tupleVariationCount A packed field.
// The high 4 bits are flags,
// and the low 12 bits are the number of tuple variation tables for this glyph.
// The number of tuple variation tables can be any number between 1 and 4095.
//Offset16 dataOffset Offset from the start of the GlyphVariationData table to the serialized data
//TupleVariationHeader tupleVariationHeaders[tupleCount] Array of tuple variation headers.
GlyphVariableData glyphVarData = new GlyphVariableData();
long beginAt = reader.BaseStream.Position;
ushort tupleVariationCount = reader.ReadUInt16();
ushort dataOffset = reader.ReadUInt16();
//The tupleVariationCount field contains a packed value that includes flags and the number of
//logical tuple variation tables — which is also the number of physical tuple variation headers.
//The format of the tupleVariationCount value is as follows:
//Table 4
//Mask Name Description
//0x8000 SHARED_POINT_NUMBERS Flag indicating that some or all tuple variation tables reference a shared set of “point” numbers.
// These shared numbers are represented as packed point number data at the start of the serialized data.***
//0x7000 Reserved Reserved for future use — set to 0.
//0x0FFF COUNT_MASK Mask for the low bits to give the number of tuple variation tables.
int tupleCount = tupleVariationCount & 0xFFF;//low 12 bits are the number of tuple variation tables for this glyph
TupleVariationHeader[] tupleHaders = new TupleVariationHeader[tupleCount];
glyphVarData.tupleHeaders = tupleHaders;
for (int i = 0; i < tupleCount; ++i)
{
tupleHaders[i] = TupleVariationHeader.Read(reader, axisCount);
}
//read glyph serialized data (https://docs.microsoft.com/en-gb/typography/opentype/spec/otvarcommonformats#serialized-data)
reader.BaseStream.Position = beginAt + dataOffset;
//
//If the sharedPointNumbers flag is set,
//then the serialized data following the header begins with packed “point” number data.
//In the context of a GlyphVariationData table within the 'gvar' table,
//these identify outline point numbers for which deltas are explicitly provided.
//In the context of the 'cvar' table, these are interpreted as CVT indices rather than point indices.
//The format of packed point number data is described below.
//....
int flags = tupleVariationCount >> 12;//The high 4 bits are flags,
if ((flags & 0x8) == 0x8)//check the flags has SHARED_POINT_NUMBERS or not
{
//The serialized data block begins with shared “point” number data,
//followed by the variation data for the tuple variation tables.
//The shared point number data is optional:
//it is present if the corresponding flag is set in the tupleVariationCount field of the header.
//If present, the shared number data is represented as packed point numbers, described below.
//https://docs.microsoft.com/en-gb/typography/opentype/spec/otvarcommonformats#packed-point-numbers
//...
//Packed point numbers are stored as a count followed by one or more runs of point number data.
//The count may be stored in one or two bytes.
//After reading the first byte, the need for a second byte can be determined.
//The count bytes are processed as follows:
glyphVarData._sharedPoints = new List<ushort>();
ReadPackedPoints(reader, glyphVarData._sharedPoints);
}
for (int i = 0; i < tupleCount; ++i)
{
TupleVariationHeader header = tupleHaders[i];
ushort dataSize = header.variableDataSize;
long expect_endAt = reader.BaseStream.Position + dataSize;
#if DEBUG
if (expect_endAt > reader.BaseStream.Length)
{
}
#endif
//The variationDataSize value indicates the size of serialized data for the given tuple variation table that is contained in the serialized data.
//It does not include the size of the TupleVariationHeader.
if ((header.flags & ((int)TupleIndexFormat.PRIVATE_POINT_NUMBERS >> 12)) == ((int)TupleIndexFormat.PRIVATE_POINT_NUMBERS) >> 12)
{
List<ushort> privatePoints = new List<ushort>();
ReadPackedPoints(reader, privatePoints);
header.PrivatePoints = privatePoints.ToArray();
}
else if (header.flags != 0)
{
}
//Packed Deltas
//Packed deltas are stored as a series of runs. Each delta run consists of a control byte followed by the actual delta values of that run.
//The control byte is a packed value with flags in the high two bits and a count in the low six bits.
//The flags specify the data size of the delta values in the run. The format of the control byte is as follows:
//Packed Deltas
//Mask Name Description
//0x80 DELTAS_ARE_ZERO Flag indicating that this run contains no data (no explicit delta values are stored), and that all of the deltas for this run are zero.
//0x40 DELTAS_ARE_WORDS Flag indicating the data type for delta values in the run. If set, the run contains 16-bit signed deltas (int16); if clear, the run contains 8-bit signed deltas (int8).
//0x3F DELTA_RUN_COUNT_MASK Mask for the low 6 bits to provide the number of delta values in the run, minus one.
List<short> packedDeltasXY = new List<short>();
while (reader.BaseStream.Position < expect_endAt)
{
byte controlByte = reader.ReadByte();
int number_in_run = (controlByte & 0x3F) + 1;
int flags01 = (controlByte >> 6) << 6;
if (flags01 == 0x80)
{
for (int nn = 0; nn < number_in_run; ++nn)
{
packedDeltasXY.Add(0);
}
}
else if (flags01 == 0x40)
{
//DELTAS_ARE_WORDS Flag indicating the data type for delta values in the run.If set,
//the run contains 16 - bit signed deltas(int16);
//if clear, the run contains 8 - bit signed deltas(int8).
for (int nn = 0; nn < number_in_run; ++nn)
{
packedDeltasXY.Add(reader.ReadInt16());
}
}
else if (flags01 == 0)
{
for (int nn = 0; nn < number_in_run; ++nn)
{
packedDeltasXY.Add(reader.ReadByte());
}
}
else
{
}
}
//---
header.PackedDeltasXY = packedDeltasXY.ToArray();
#if DEBUG
//ensure!
if ((packedDeltasXY.Count % 2) != 0)
{
System.Diagnostics.Debugger.Break();
}
//ensure!
if (reader.BaseStream.Position != expect_endAt)
{
System.Diagnostics.Debugger.Break();
}
#endif
}
return glyphVarData;
}
}
}

View File

@@ -0,0 +1,153 @@
//MIT, 2019-present, WinterDev
using System;
using System.IO;
namespace Typography.OpenFont.Tables
{
//https://docs.microsoft.com/en-us/typography/opentype/spec/hvar
/// <summary>
/// HVAR — Horizontal Metrics Variations Table
/// </summary>
class HVar : TableEntry
{
public const string _N = "HVAR";
public override string Name => _N;
internal ItemVariationStoreTable _itemVartionStore;
internal DeltaSetIndexMap[] _advanceWidthMapping;
internal DeltaSetIndexMap[] _lsbMapping;
internal DeltaSetIndexMap[] _rsbMapping;
public HVar()
{
//The HVAR table is used in variable fonts to provide variations for horizontal glyph metrics values.
//This can be used to provide variation data for advance widths in the 'hmtx' table.
//In fonts with TrueType outlines, it can also be used to provide variation data for left and right side
//bearings obtained from the 'hmtx' table and glyph bounding box.
}
protected override void ReadContentFrom(BinaryReader reader)
{
long beginAt = reader.BaseStream.Position;
//Horizontal metrics variations table:
//Type Name Description
//uint16 majorVersion Major version number of the horizontal metrics variations table — set to 1.
//uint16 minorVersion Minor version number of the horizontal metrics variations table — set to 0.
//Offset32 itemVariationStoreOffset Offset in bytes from the start of this table to the item variation store table.
//Offset32 advanceWidthMappingOffset Offset in bytes from the start of this table to the delta-set index mapping for advance widths (may be NULL).
//Offset32 lsbMappingOffset Offset in bytes from the start of this table to the delta-set index mapping for left side bearings(may be NULL).
//Offset32 rsbMappingOffset Offset in bytes from the start of this table to the delta-set index mapping for right side bearings(may be NULL).
ushort majorVersion = reader.ReadUInt16();
ushort minorVersion = reader.ReadUInt16();
uint itemVariationStoreOffset = reader.ReadUInt32();
uint advanceWidthMappingOffset = reader.ReadUInt32();
uint lsbMappingOffset = reader.ReadUInt32();
uint rsbMappingOffset = reader.ReadUInt32();
//
//itemVariationStore
reader.BaseStream.Position = beginAt + itemVariationStoreOffset;
_itemVartionStore = new ItemVariationStoreTable();
_itemVartionStore.ReadContentFrom(reader);
//-----------------------------------------
if (advanceWidthMappingOffset > 0)
{
reader.BaseStream.Position = beginAt + advanceWidthMappingOffset;
_advanceWidthMapping = ReadDeltaSetIndexMapTable(reader);
}
if (lsbMappingOffset > 0)
{
reader.BaseStream.Position = beginAt + lsbMappingOffset;
_lsbMapping = ReadDeltaSetIndexMapTable(reader);
}
if (rsbMappingOffset > 0)
{
reader.BaseStream.Position = beginAt + rsbMappingOffset;
_rsbMapping = ReadDeltaSetIndexMapTable(reader);
}
}
const int INNER_INDEX_BIT_COUNT_MASK = 0x000F;
const int MAP_ENTRY_SIZE_MASK = 0x0030;
const int MAP_ENTRY_SIZE_SHIFT = 4;
public readonly struct DeltaSetIndexMap
{
public readonly ushort innerIndex;
public readonly ushort outerIndex;
public DeltaSetIndexMap(ushort innerIndex, ushort outerIndex)
{
this.innerIndex = innerIndex;
this.outerIndex = outerIndex;
}
}
static DeltaSetIndexMap[] ReadDeltaSetIndexMapTable(BinaryReader reader)
{
//DeltaSetIndexMap table:
//Table 2
//Type Name Description
//uint16 entryFormat A packed field that describes the compressed representation of delta-set indices. See details below.
//uint16 mapCount The number of mapping entries.
//uint8 mapData[variable] The delta-set index mapping data. See details below.
ushort entryFormat = reader.ReadUInt16();
ushort mapCount = reader.ReadUInt16();
//The mapCount field indicates the number of delta-set index mapping entries.
//Glyph IDs are used as the index into the mapping array.
//If a given glyph ID is greater than mapCount - 1, then the last entry is used.
//Each mapping entry represents a delta-set outer-level index and inner-level index combination.
//Logically, each of these indices is a 16-bit, unsigned value.
//These are represented in a packed format that uses one, two, three or four bytes.
//The entryFormat field is a packed bitfield that describes the compressed representation used in
//the mapData field of the given deltaSetIndexMap table.
//The format of the entryFormat field is as follows:
//EntryFormat Field Masks
//Table 3
//Mask Name Description
//0x000F INNER_INDEX_BIT_COUNT_MASK Mask for the low 4 bits, which give the count of bits minus one that are used in each entry for the inner-level index.
//0x0030 MAP_ENTRY_SIZE_MASK Mask for bits that indicate the size in bytes minus one of each entry.
//0xFFC0 Reserved Reserved for future use — set to 0.
//see also: afdko\c\public\lib\source\varread\varread.c (Apache2)
int entrySize = ((entryFormat & MAP_ENTRY_SIZE_MASK) >> MAP_ENTRY_SIZE_SHIFT) + 1;
int innerIndexEntryMask = (1 << ((entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1)) - 1;
int outerIndexEntryShift = (entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1;
int mapDataSize = mapCount * entrySize;
DeltaSetIndexMap[] deltaSetIndexMaps = new DeltaSetIndexMap[mapCount];
for (int i = 0; i < mapCount; ++i)
{
int entry;
switch (entrySize)
{
default: throw new OpenFontNotSupportedException();
case 1: entry = reader.ReadByte(); break;
case 2: entry = (reader.ReadByte() << 8) | reader.ReadByte(); break;
case 3: entry = (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(); break;
case 4: entry = (reader.ReadByte() << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(); break;
}
//***
deltaSetIndexMaps[i] = new DeltaSetIndexMap((ushort)(entry & innerIndexEntryMask), (ushort)(entry >> outerIndexEntryShift));
}
return deltaSetIndexMaps;
}
}
}

View File

@@ -0,0 +1,307 @@
//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
/// <summary>
/// MVAR — Metrics Variations Table
/// </summary>
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 fonts 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<string, ValueTagInfo> s_registerTags = new Dictionary<string, ValueTagInfo>();
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");
}
}
}
}

View File

@@ -0,0 +1,2 @@
//TODO: implement this
//https://docs.microsoft.com/en-us/typography/opentype/spec/vvar