mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-12 10:19:07 +00:00
416 lines
20 KiB
C#
416 lines
20 KiB
C#
//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;
|
||
}
|
||
}
|
||
}
|
||
|
||
|