mirror of
https://github.com/QL-Win/QuickLook.git
synced 2025-09-12 18:39:45 +00:00
206 lines
9.2 KiB
C#
206 lines
9.2 KiB
C#
//Apache2, 2016-present, WinterDev
|
|
using System;
|
|
using System.IO;
|
|
|
|
namespace Typography.OpenFont.Tables
|
|
{
|
|
//https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2
|
|
//----------------------------
|
|
//Class Definition Table
|
|
//----------------------------
|
|
//
|
|
//In OpenType Layout, index values identify glyphs. For efficiency and ease of representation, a font developer can group glyph indices to form glyph classes.
|
|
//Class assignments vary in meaning from one lookup subtable to another.
|
|
//For example, in the GSUB and GPOS tables, classes are used to describe glyph contexts.
|
|
//GDEF tables also use the idea of glyph classes.
|
|
|
|
//Consider a substitution action that replaces only the lowercase ascender glyphs in a glyph string.
|
|
//To more easily describe the appropriate context for the substitution,
|
|
//the font developer might divide the font's lowercase glyphs into two classes,
|
|
//one that contains the ascenders and one that contains the glyphs without ascenders.
|
|
|
|
//A font developer can assign any glyph to any class, each identified with an integer called a class value.
|
|
//A Class Definition table (ClassDef) groups glyph indices by class,
|
|
//beginning with Class 1, then Class 2, and so on.
|
|
//All glyphs not assigned to a class fall into Class 0.
|
|
//Within a given class definition table, each glyph in the font belongs to exactly one class.
|
|
|
|
//The ClassDef table can have either of two formats:
|
|
//one that assigns a range of consecutive glyph indices to different classes,
|
|
//or one that puts groups of consecutive glyph indices into the same class.
|
|
//
|
|
//
|
|
//Class Definition Table Format 1
|
|
//
|
|
//
|
|
//The first class definition format (ClassDefFormat1) specifies
|
|
//a range of consecutive glyph indices and a list of corresponding glyph class values.
|
|
//This table is useful for assigning each glyph to a different class because
|
|
//the glyph indices in each class are not grouped together.
|
|
|
|
//A ClassDef Format 1 table begins with a format identifier (ClassFormat).
|
|
//The range of glyph indices (GlyphIDs) covered by the table is identified by two values: the GlyphID of the first glyph (StartGlyph),
|
|
//and the number of consecutive GlyphIDs (including the first one) that will be assigned class values (GlyphCount).
|
|
//The ClassValueArray lists the class value assigned to each GlyphID, starting with the class value for StartGlyph and
|
|
//following the same order as the GlyphIDs.
|
|
//Any glyph not included in the range of covered GlyphIDs automatically belongs to Class 0.
|
|
|
|
//Example 7 at the end of this chapter uses Format 1 to assign class values to the lowercase, x-height, ascender, and descender glyphs in a font.
|
|
//
|
|
//----------------------------
|
|
//ClassDefFormat1 table: Class array
|
|
//----------------------------
|
|
//Type Name Description
|
|
//uint16 ClassFormat Format identifier-format = 1
|
|
//uint16 StartGlyph First glyph ID of the ClassValueArray
|
|
//uint16 GlyphCount Size of the ClassValueArray
|
|
//uint16 ClassValueArray[GlyphCount] Array of Class Values-one per GlyphID
|
|
//----------------------------------
|
|
//
|
|
//
|
|
//Class Definition Table Format 2
|
|
//
|
|
//
|
|
//The second class definition format (ClassDefFormat2) defines multiple groups of glyph indices that belong to the same class.
|
|
//Each group consists of a discrete range of glyph indices in consecutive order (ranges cannot overlap).
|
|
|
|
//The ClassDef Format 2 table contains a format identifier (ClassFormat),
|
|
//a count of ClassRangeRecords that define the groups and assign class values (ClassRangeCount),
|
|
//and an array of ClassRangeRecords ordered by the GlyphID of the first glyph in each record (ClassRangeRecord).
|
|
|
|
//Each ClassRangeRecord consists of a Start glyph index, an End glyph index, and a Class value.
|
|
//All GlyphIDs in a range, from Start to End inclusive,
|
|
//constitute the class identified by the Class value.
|
|
//Any glyph not covered by a ClassRangeRecord is assumed to belong to Class 0.
|
|
|
|
//Example 8 at the end of this chapter uses Format 2 to assign class values to four types of glyphs in the Arabic script.
|
|
//---------------------------------------
|
|
//ClassDefFormat2 table: Class ranges
|
|
//---------------------------------------
|
|
//Type Name Description
|
|
//uint16 ClassFormat Format identifier-format = 2
|
|
//uint16 ClassRangeCount Number of ClassRangeRecords
|
|
//struct ClassRangeRecord[ClassRangeCount] Array of ClassRangeRecords-ordered by Start GlyphID
|
|
//---------------------------------------
|
|
//
|
|
//ClassRangeRecord
|
|
//---------------------------------------
|
|
//Type Name Descriptionc
|
|
//uint16 Start First glyph ID in the range
|
|
//uint16 End Last glyph ID in the range
|
|
//uint16 Class Applied to all glyphs in the range
|
|
//---------------------------------------
|
|
class ClassDefTable
|
|
{
|
|
public int Format { get; internal set; }
|
|
//----------------
|
|
//format 1
|
|
public ushort startGlyph;
|
|
public ushort[] classValueArray;
|
|
//---------------
|
|
//format2
|
|
public ClassRangeRecord[] records;
|
|
public static ClassDefTable CreateFrom(BinaryReader reader, long beginAt)
|
|
{
|
|
|
|
reader.BaseStream.Seek(beginAt, SeekOrigin.Begin);
|
|
|
|
//---------
|
|
ClassDefTable classDefTable = new ClassDefTable();
|
|
switch (classDefTable.Format = reader.ReadUInt16())
|
|
{
|
|
default: throw new OpenFontNotSupportedException();
|
|
case 1:
|
|
{
|
|
classDefTable.startGlyph = reader.ReadUInt16();
|
|
ushort glyphCount = reader.ReadUInt16();
|
|
classDefTable.classValueArray = Utils.ReadUInt16Array(reader, glyphCount);
|
|
}
|
|
break;
|
|
case 2:
|
|
{
|
|
ushort classRangeCount = reader.ReadUInt16();
|
|
ClassRangeRecord[] records = new ClassRangeRecord[classRangeCount];
|
|
for (int i = 0; i < classRangeCount; ++i)
|
|
{
|
|
records[i] = new ClassRangeRecord(
|
|
reader.ReadUInt16(), //start glyph id
|
|
reader.ReadUInt16(), //end glyph id
|
|
reader.ReadUInt16()); //classNo
|
|
}
|
|
classDefTable.records = records;
|
|
}
|
|
break;
|
|
}
|
|
return classDefTable;
|
|
}
|
|
internal readonly struct ClassRangeRecord
|
|
{
|
|
//---------------------------------------
|
|
//
|
|
//ClassRangeRecord
|
|
//---------------------------------------
|
|
//Type Name Descriptionc
|
|
//uint16 Start First glyph ID in the range
|
|
//uint16 End Last glyph ID in the range
|
|
//uint16 Class Applied to all glyphs in the range
|
|
//---------------------------------------
|
|
public readonly ushort startGlyphId;
|
|
public readonly ushort endGlyphId;
|
|
public readonly ushort classNo;
|
|
public ClassRangeRecord(ushort startGlyphId, ushort endGlyphId, ushort classNo)
|
|
{
|
|
this.startGlyphId = startGlyphId;
|
|
this.endGlyphId = endGlyphId;
|
|
this.classNo = classNo;
|
|
}
|
|
#if DEBUG
|
|
public override string ToString()
|
|
{
|
|
return "class=" + classNo + " [" + startGlyphId + "," + endGlyphId + "]";
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
public int GetClassValue(ushort glyphIndex)
|
|
{
|
|
switch (Format)
|
|
{
|
|
default: throw new OpenFontNotSupportedException();
|
|
case 1:
|
|
{
|
|
if (glyphIndex >= startGlyph &&
|
|
glyphIndex < classValueArray.Length)
|
|
{
|
|
return classValueArray[glyphIndex - startGlyph];
|
|
}
|
|
return -1;
|
|
}
|
|
case 2:
|
|
{
|
|
|
|
for (int i = 0; i < records.Length; ++i)
|
|
{
|
|
//TODO: review a proper method here again
|
|
//esp. binary search
|
|
ClassRangeRecord rec = records[i];
|
|
if (rec.startGlyphId <= glyphIndex)
|
|
{
|
|
if (glyphIndex <= rec.endGlyphId)
|
|
{
|
|
return rec.classNo;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return -1;//no need to go further
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} |