mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-23 18:54:51 +00:00
Temporary solution to read woff2
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
||||
}
|
@@ -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 font’s 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 font’s 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 glyph’s 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 byte’s 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 glyph’s 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.
|
||||
|
||||
|
||||
|
||||
}
|
@@ -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 font’s variation space,
|
||||
//and other variation tables provide variation data to describe
|
||||
//how different data items are varied across the font’s 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 font’s 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 font’s 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 font’s 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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -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 font’s 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 font’s 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 font’s 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 byte’s 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -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 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<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");
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
//TODO: implement this
|
||||
//https://docs.microsoft.com/en-us/typography/opentype/spec/vvar
|
Reference in New Issue
Block a user