Files
QuickLook/QuickLook.Plugin/QuickLook.Plugin.FontViewer/Typography.OpenFont/Tables.Variations/Common.TupleVariationStore.cs
2024-12-30 04:21:24 +08:00

504 lines
27 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//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.
}