Files
crtr/Assets/Cryville/Common/Font/FontTable.cs
2023-03-26 23:25:20 +08:00

223 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Cryville.Common.Font {
public abstract class FontTable<T> {
protected UInt32 Offset { get; private set; }
protected BinaryReader Reader { get; private set; }
protected FontTable(BinaryReader reader, UInt32 offset) {
Reader = reader;
Offset = offset;
reader.BaseStream.Position = offset;
}
public abstract IReadOnlyList<T> GetItems();
}
public abstract class FontTable<T, U> : FontTable<T> {
protected FontTable(BinaryReader reader, UInt32 offset) : base(reader, offset) { }
public abstract U GetSubTable(T item);
}
public sealed class TTCHeader : FontTable<UInt32, TableDirectory> {
readonly String ttcTag;
readonly UInt16 majorVersion;
readonly UInt16 minorVersion;
readonly UInt32 numFonts;
readonly List<UInt32> tableDirectoryOffsets = new List<UInt32>();
readonly String dsigTag;
readonly UInt32 dsigLength;
readonly UInt32 dsigOffset;
public TTCHeader(BinaryReader reader, UInt32 offset) : base(reader, offset) {
ttcTag = reader.ReadTag();
if (ttcTag != "ttcf") throw new NotImplementedException();
majorVersion = reader.ReadUInt16();
minorVersion = reader.ReadUInt16();
numFonts = reader.ReadUInt32();
for (UInt32 i = 0; i < numFonts; i++) tableDirectoryOffsets.Add(reader.ReadUInt32());
if (majorVersion == 2) {
dsigTag = reader.ReadTag();
dsigLength = reader.ReadUInt32();
dsigOffset = reader.ReadUInt32();
}
}
public override IReadOnlyList<UInt32> GetItems() {
return tableDirectoryOffsets;
}
public override TableDirectory GetSubTable(UInt32 item) {
var i = item;
return new TableDirectory(Reader, i);
}
}
public sealed class TableDirectory : FontTable<TableRecord, object> {
readonly UInt32 sfntVersion;
readonly UInt16 numTables;
readonly UInt16 searchRange;
readonly UInt16 entrySelector;
readonly UInt16 rangeShift;
readonly List<TableRecord> tableRecords = new List<TableRecord>();
public TableDirectory(BinaryReader reader, UInt32 offset) : base(reader, offset) {
sfntVersion = reader.ReadUInt32();
numTables = reader.ReadUInt16();
searchRange = reader.ReadUInt16();
entrySelector = reader.ReadUInt16();
rangeShift = reader.ReadUInt16();
for (int i = 0; i < numTables; i++)
tableRecords.Add(new TableRecord {
tableTag = reader.ReadTag(),
checksum = reader.ReadUInt32(),
offset = reader.ReadUInt32(),
length = reader.ReadUInt32(),
});
}
public override IReadOnlyList<TableRecord> GetItems() {
return tableRecords;
}
public override object GetSubTable(TableRecord item) {
switch (item.tableTag) {
case "name": return new NameTable(Reader, item.offset);
case "meta": return new MetaTable(Reader, item.offset);
default: throw new NotImplementedException();
}
}
}
public struct TableRecord {
public string tableTag;
public UInt32 checksum;
public UInt32 offset;
public UInt32 length;
}
public sealed class NameTable : FontTable<NameRecord> {
readonly UInt16 version;
readonly UInt16 count;
readonly UInt16 storageOffset;
readonly List<NameRecord> nameRecord = new List<NameRecord>();
readonly UInt16 langTagCount;
readonly List<LangTagRecord> langTagRecord = new List<LangTagRecord>();
public NameTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
version = reader.ReadUInt16();
count = reader.ReadUInt16();
storageOffset = reader.ReadUInt16();
for (UInt16 i = 0; i < count; i++)
nameRecord.Add(new NameRecord {
platformID = reader.ReadUInt16(),
encodingID = reader.ReadUInt16(),
languageID = reader.ReadUInt16(),
nameID = (NameID)reader.ReadUInt16(),
length = reader.ReadUInt16(),
stringOffset = reader.ReadUInt16(),
});
if (version == 1) {
langTagCount = reader.ReadUInt16();
for (UInt16 i = 0; i < langTagCount; i++)
langTagRecord.Add(new LangTagRecord {
length = reader.ReadUInt16(),
langTagOffset = reader.ReadUInt16(),
});
}
UInt32 origin = (UInt32)reader.BaseStream.Position;
for (int i = 0; i < nameRecord.Count; i++) nameRecord[i] = nameRecord[i].Load(reader, origin);
for (int i = 0; i < langTagRecord.Count; i++) langTagRecord[i] = langTagRecord[i].Load(reader, origin);
}
public sealed override IReadOnlyList<NameRecord> GetItems() {
return nameRecord;
}
}
public struct NameRecord {
public UInt16 platformID;
public UInt16 encodingID;
public UInt16 languageID;
public NameID nameID;
public UInt16 length;
public UInt16 stringOffset;
public String value { get; private set; }
public NameRecord Load(BinaryReader reader, UInt32 origin) {
reader.BaseStream.Position = origin + stringOffset;
Encoding encoding;
switch (platformID) {
case 0: encoding = Encoding.BigEndianUnicode; break;
case 3: encoding = Encoding.BigEndianUnicode; break;
default: return this;
}
value = encoding.GetString(reader.ReadBytes(length));
return this;
}
}
public enum NameID : UInt16 {
CopyrightNotice = 0,
FontFamilyName = 1,
FontSubfamilyName = 2,
UniqueFontIdentifier = 3,
FullFontName = 4,
VersionString = 5,
PostScriptName = 6,
Trademark = 7,
ManufacturerName = 8,
Designer = 9,
Description = 10,
URLVendor = 11,
URLDesigner = 12,
LicenseDescription = 13,
LicenseInfoURL = 14,
TypographicFamilyName = 16,
TypographicSubfamilyName = 17,
CompatibleFull = 18,
SampleText = 19,
PostScriptCIDFindfontName = 20,
WWSFamilyName = 21,
WWSSubfamilyName = 22,
LightBackgroundPalette = 23,
DarkBackgroundPalette = 24,
VariationsPostScriptNamePrefix = 25,
}
public struct LangTagRecord {
public UInt16 length;
public UInt16 langTagOffset;
public String value { get; private set; }
public LangTagRecord Load(BinaryReader reader, UInt32 origin) {
reader.BaseStream.Position = origin + langTagOffset;
value = Encoding.BigEndianUnicode.GetString(reader.ReadBytes(length));
return this;
}
}
public sealed class MetaTable : FontTable<DataMap> {
readonly UInt32 version;
readonly UInt32 flags;
readonly UInt32 dataMapCount;
readonly List<DataMap> dataMaps = new List<DataMap>();
public MetaTable(BinaryReader reader, UInt32 offset) : base(reader, offset) {
version = reader.ReadUInt32();
flags = reader.ReadUInt32();
reader.ReadUInt32();
dataMapCount = reader.ReadUInt32();
for (UInt32 i = 0; i < dataMapCount; i++)
dataMaps.Add(new DataMap {
tag = reader.ReadTag(),
dataOffset = reader.ReadUInt32(),
dataLength = reader.ReadUInt32(),
});
for (int i = 0; i < dataMaps.Count; i++) dataMaps[i] = dataMaps[i].Load(reader, offset);
}
public sealed override IReadOnlyList<DataMap> GetItems() {
return dataMaps;
}
}
public struct DataMap {
public String tag;
public UInt32 dataOffset;
public UInt32 dataLength;
public String value { get; private set; }
public DataMap Load(BinaryReader reader, UInt32 origin) {
reader.BaseStream.Position = origin + dataOffset;
value = Encoding.ASCII.GetString(reader.ReadBytes((int)dataLength));
return this;
}
}
public static class BinaryReaderExtensions {
public static string ReadTag(this BinaryReader reader) {
return Encoding.ASCII.GetString(reader.ReadBytes(4));
}
}
}