public static void ReadMaxp(DataReader reader, TableRecord[] tables, ref FaceHeader header) { SeekToTable(reader, tables, FourCC.Maxp, required: true); if (reader.ReadInt32BE() != 0x00010000) throw new InvalidFontException("Font contains an old style maxp table."); header.GlyphCount = reader.ReadUInt16BE(); if (header.GlyphCount > MaxGlyphs) throw new InvalidFontException("Font contains too many glyphs."); // skip maxPoints, maxContours, maxCompositePoints, maxCompositeContours, maxZones reader.Skip(sizeof(short) * 5); header.MaxTwilightPoints = reader.ReadUInt16BE(); header.MaxStorageLocations = reader.ReadUInt16BE(); header.MaxFunctionDefs = reader.ReadUInt16BE(); header.MaxInstructionDefs = reader.ReadUInt16BE(); header.MaxStackSize = reader.ReadUInt16BE(); // sanity checking if (header.MaxTwilightPoints > MaxTwilightPoints || header.MaxStorageLocations > MaxStorageLocations || header.MaxFunctionDefs > MaxFunctionDefs || header.MaxInstructionDefs > MaxFunctionDefs || header.MaxStackSize > MaxStackSize) throw new InvalidFontException("Font programs have limits that are larger than built-in sanity checks."); }
public static TableRecord[] ReadFaceHeader(DataReader reader) { var tag = reader.ReadUInt32BE(); //if (tag != TTFv1 && tag != TTFv2 && tag != FourCC.True) // throw new InvalidFontException("Unknown or unsupported sfnt version."); var tableCount = reader.ReadUInt16BE(); reader.Skip(6); // skip the rest of the header // read each font table descriptor var tables = new TableRecord[tableCount]; for (int i = 0; i < tableCount; i++) { tables[i] = new TableRecord { Tag = reader.ReadUInt32(), CheckSum = reader.ReadUInt32BE(), Offset = reader.ReadUInt32BE(), Length = reader.ReadUInt32BE(), }; } return(tables); }
public unsafe static SbitTable Read(DataReader reader, TableRecord[] tables) { if (!SfntTables.SeekToTable(reader, tables, FourCC.Eblc)) return null; // skip version var baseOffset = reader.Position; reader.Skip(sizeof(int)); // load each strike table var count = reader.ReadInt32BE(); if (count > MaxBitmapStrikes) throw new InvalidFontException("Too many bitmap strikes in font."); var sizeTableHeaders = stackalloc BitmapSizeTable[count]; for (int i = 0; i < count; i++) { sizeTableHeaders[i].SubTableOffset = reader.ReadUInt32BE(); sizeTableHeaders[i].SubTableSize = reader.ReadUInt32BE(); sizeTableHeaders[i].SubTableCount = reader.ReadUInt32BE(); // skip colorRef, metrics entries, start and end glyph indices reader.Skip(sizeof(uint) + sizeof(ushort) * 2 + 12 * 2); sizeTableHeaders[i].PpemX = reader.ReadByte(); sizeTableHeaders[i].PpemY = reader.ReadByte(); sizeTableHeaders[i].BitDepth = reader.ReadByte(); sizeTableHeaders[i].Flags = (BitmapSizeFlags)reader.ReadByte(); } // read index subtables var indexSubTables = stackalloc IndexSubTable[count]; for (int i = 0; i < count; i++) { reader.Seek(baseOffset + sizeTableHeaders[i].SubTableOffset); indexSubTables[i] = new IndexSubTable { FirstGlyph = reader.ReadUInt16BE(), LastGlyph = reader.ReadUInt16BE(), Offset = reader.ReadUInt32BE() }; } // read the actual data for each strike table for (int i = 0; i < count; i++) { // read the subtable header reader.Seek(baseOffset + sizeTableHeaders[i].SubTableOffset + indexSubTables[i].Offset); var indexFormat = reader.ReadUInt16BE(); var imageFormat = reader.ReadUInt16BE(); var imageDataOffset = reader.ReadUInt32BE(); } return null; }
public static void ReadHead(DataReader reader, TableRecord[] tables, out FaceHeader header) { SeekToTable(reader, tables, FourCC.Head, required: true); // 'head' table contains global information for the font face // we only care about a few fields in it reader.Skip(sizeof(int) * 4); // version, revision, checksum, magic number header = new FaceHeader { Flags = (HeadFlags)reader.ReadUInt16BE(), UnitsPerEm = reader.ReadUInt16BE() }; if (header.UnitsPerEm == 0) throw new InvalidFontException("Invalid 'head' table."); // skip over created and modified times, bounding box, // deprecated style bits, direction hints, and size hints reader.Skip(sizeof(long) * 2 + sizeof(short) * 7); header.IndexFormat = (IndexFormat)reader.ReadInt16BE(); }
public static TableRecord[] ReadFaceHeader(DataReader reader) { var tag = reader.ReadUInt32BE(); if (tag != TTFv1 && tag != TTFv2 && tag != FourCC.True) throw new InvalidFontException("Unknown or unsupported sfnt version."); var tableCount = reader.ReadUInt16BE(); reader.Skip(6); // skip the rest of the header // read each font table descriptor var tables = new TableRecord[tableCount]; for (int i = 0; i < tableCount; i++) { tables[i] = new TableRecord { Tag = reader.ReadUInt32(), CheckSum = reader.ReadUInt32BE(), Offset = reader.ReadUInt32BE(), Length = reader.ReadUInt32BE(), }; } return tables; }
public static KerningTable ReadKern(DataReader reader, TableRecord[] tables) { // kern table is optional if (!SfntTables.SeekToTable(reader, tables, FourCC.Kern)) return null; // skip version reader.Skip(sizeof(short)); // read each subtable and accumulate kerning values var tableData = new Dictionary<uint, int>(); var subtableCount = reader.ReadUInt16BE(); for (int i = 0; i < subtableCount; i++) { // skip version var currentOffset = reader.Position; reader.Skip(sizeof(short)); var length = reader.ReadUInt16BE(); var coverage = reader.ReadUInt16BE(); // we (and Windows) only support Format 0 tables // only care about tables with horizontal kerning data var kc = (KernCoverage)coverage; if ((coverage & FormatMask) == 0 && (kc & KernCoverage.Horizontal) != 0 && (kc & KernCoverage.CrossStream) == 0) { // read the number of entries; skip over the rest of the header var entryCount = reader.ReadUInt16BE(); reader.Skip(sizeof(short) * 3); var isMin = (kc & KernCoverage.Minimum) != 0; var isOverride = (kc & KernCoverage.Override) != 0; // read in each entry and accumulate its kerning data for (int j = 0; j < entryCount; j++) { var left = reader.ReadUInt16BE(); var right = reader.ReadUInt16BE(); var value = reader.ReadInt16BE(); // look up the current value, if we have one; if not, start at zero int current = 0; var key = ((uint)left << 16) | right; tableData.TryGetValue(key, out current); if (isMin) { if (current < value) tableData[key] = value; } else if (isOverride) tableData[key] = value; else tableData[key] = current + value; } } // jump to the next subtable reader.Seek(currentOffset + length); } return new KerningTable(tableData); }
public static CharacterMap ReadCmap(DataReader reader, TableRecord[] tables) { SfntTables.SeekToTable(reader, tables, FourCC.Cmap, required: true); // skip version var cmapOffset = reader.Position; reader.Skip(sizeof(short)); // read all of the subtable headers var subtableCount = reader.ReadUInt16BE(); var subtableHeaders = new CmapSubtableHeader[subtableCount]; for (int i = 0; i < subtableHeaders.Length; i++) { subtableHeaders[i] = new CmapSubtableHeader { PlatformID = reader.ReadUInt16BE(), EncodingID = reader.ReadUInt16BE(), Offset = reader.ReadUInt32BE() }; } // search for a "full" Unicode table first var chosenSubtableOffset = 0u; for (int i = 0; i < subtableHeaders.Length; i++) { var platform = subtableHeaders[i].PlatformID; var encoding = subtableHeaders[i].EncodingID; if ((platform == PlatformID.Microsoft && encoding == WindowsEncoding.UnicodeFull) || (platform == PlatformID.Unicode && encoding == UnicodeEncoding.Unicode32)) { chosenSubtableOffset = subtableHeaders[i].Offset; break; } } // if no full unicode table, just grab the first // one that supports any flavor of Unicode if (chosenSubtableOffset == 0) { for (int i = 0; i < subtableHeaders.Length; i++) { var platform = subtableHeaders[i].PlatformID; var encoding = subtableHeaders[i].EncodingID; if ((platform == PlatformID.Microsoft && encoding == WindowsEncoding.UnicodeBmp) || platform == PlatformID.Unicode) { chosenSubtableOffset = subtableHeaders[i].Offset; break; } } } // no unicode support at all is an error if (chosenSubtableOffset == 0) throw new Exception("Font does not support Unicode."); // jump to our chosen table and find out what format it's in reader.Seek(cmapOffset + chosenSubtableOffset); var format = reader.ReadUInt16BE(); switch (format) { case 4: return ReadCmapFormat4(reader); default: throw new Exception("Unsupported cmap format."); } }
public static bool SeekToTable(DataReader reader, TableRecord[] tables, FourCC tag, bool required = false) { // check if we have the desired table and that it's not empty var index = FindTable(tables, tag); if (index == -1 || tables[index].Length == 0) { if (required) //throw new InvalidFontException($"Missing or empty '{tag}' table."); throw new InvalidFontException(string.Format( "Missing or empty '{0}' table.", tag)); return false; } // seek to the appropriate offset reader.Seek(tables[index].Offset); return true; }
public static byte[] ReadProgram(DataReader reader, TableRecord[] tables, FourCC tag) { var index = FindTable(tables, tag); if (index == -1) return null; reader.Seek(tables[index].Offset); return reader.ReadBytes((int)tables[index].Length); }
public static int FindTable(TableRecord[] tables, FourCC tag) { var index = -1; for (int i = 0; i < tables.Length; i++) { if (tables[i].Tag == tag) { index = i; break; } } return index; }
public static FUnit[] ReadCvt(DataReader reader, TableRecord[] tables) { var index = FindTable(tables, FourCC.Cvt); if (index == -1) return null; reader.Seek(tables[index].Offset); var results = new FUnit[tables[index].Length / sizeof(short)]; for (int i = 0; i < results.Length; i++) results[i] = (FUnit)reader.ReadInt16BE(); return results; }
public unsafe static NameData ReadNames(DataReader reader, TableRecord[] tables) { if (!SeekToTable(reader, tables, FourCC.Name)) return default(NameData); // read header var currentOffset = reader.Position; var format = reader.ReadUInt16BE(); var count = reader.ReadUInt16BE(); var dataOffset = currentOffset + reader.ReadUInt16BE(); // read name records, filtering out non-Unicode and platforms we don't know about var stringData = stackalloc StringData[count]; var stringDataCount = 0; for (int i = 0; i < count; i++) { var platform = reader.ReadUInt16BE(); var encoding = reader.ReadUInt16BE(); var language = reader.ReadUInt16BE(); var name = reader.ReadUInt16BE(); var length = reader.ReadUInt16BE(); var offset = reader.ReadUInt16BE(); // we only support Unicode strings if (platform == PlatformID.Microsoft) { if (encoding != WindowsEncoding.UnicodeBmp && encoding != WindowsEncoding.UnicodeFull) continue; if (language != CultureInfo.CurrentCulture.LCID) continue; } else if (platform != PlatformID.Unicode) continue; stringData[stringDataCount++] = new StringData { Name = name, Offset = offset, Length = length }; } // find strings we care about and extract them from the blob var nameData = new NameData(); for (int i = 0; i < stringDataCount; i++) { var data = stringData[i]; switch (data.Name) { case NameID.FamilyName: nameData.FamilyName = ExtractString(reader, dataOffset, data); break; case NameID.SubfamilyName: nameData.SubfamilyName = ExtractString(reader, dataOffset, data); break; case NameID.UniqueID: nameData.UniqueID = ExtractString(reader, dataOffset, data); break; case NameID.FullName: nameData.FullName = ExtractString(reader, dataOffset, data); break; case NameID.Version: nameData.Version = ExtractString(reader, dataOffset, data); break; case NameID.Description: nameData.Description = ExtractString(reader, dataOffset, data); break; case NameID.TypographicFamilyName: nameData.TypographicFamilyName = ExtractString(reader, dataOffset, data); break; case NameID.TypographicSubfamilyName: nameData.TypographicSubfamilyName = ExtractString(reader, dataOffset, data); break; } } return nameData; }
public static void ReadLoca(DataReader reader, TableRecord[] tables, IndexFormat format, uint* table, int count) { SeekToTable(reader, tables, FourCC.Loca, required: true); if (format == IndexFormat.Short) { // values are ushort, divided by 2, so we need to shift back for (int i = 0; i < count; i++) *table++ = (uint)(reader.ReadUInt16BE() << 1); } else { for (int i = 0; i < count; i++) *table++ = reader.ReadUInt32BE(); } }
public static void ReadPost(DataReader reader, TableRecord[] tables, ref FaceHeader header) { if (!SeekToTable(reader, tables, FourCC.Post)) return; // skip over version and italicAngle reader.Skip(sizeof(int) * 2); header.UnderlinePosition = reader.ReadInt16BE(); header.UnderlineThickness = reader.ReadInt16BE(); header.IsFixedPitch = reader.ReadUInt32BE() != 0; }
public static OS2Data ReadOS2(DataReader reader, TableRecord[] tables) { SeekToTable(reader, tables, FourCC.OS_2, required: true); // skip over version, xAvgCharWidth reader.Skip(sizeof(short) * 2); var result = new OS2Data { Weight = (FontWeight)reader.ReadUInt16BE(), Stretch = (FontStretch)reader.ReadUInt16BE() }; // skip over fsType, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset, // ySuperscriptXSize, ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptXOffset reader.Skip(sizeof(short) * 9); result.StrikeoutSize = reader.ReadInt16BE(); result.StrikeoutPosition = reader.ReadInt16BE(); // skip over sFamilyClass, panose[10], ulUnicodeRange1-4, achVendID[4] reader.Skip(sizeof(short) + sizeof(int) * 4 + 14); // check various style flags var fsSelection = (FsSelectionFlags)reader.ReadUInt16BE(); result.Style = (fsSelection & FsSelectionFlags.Italic) != 0 ? FontStyle.Italic : (fsSelection & FsSelectionFlags.Bold) != 0 ? FontStyle.Bold : (fsSelection & FsSelectionFlags.Oblique) != 0 ? FontStyle.Oblique : FontStyle.Regular; result.IsWWSFont = (fsSelection & FsSelectionFlags.WWS) != 0; result.UseTypographicMetrics = (fsSelection & FsSelectionFlags.UseTypoMetrics) != 0; // skip over usFirstCharIndex, usLastCharIndex reader.Skip(sizeof(short) * 2); result.TypographicAscender = reader.ReadInt16BE(); result.TypographicDescender = reader.ReadInt16BE(); result.TypographicLineGap = reader.ReadInt16BE(); result.WinAscent = reader.ReadUInt16BE(); result.WinDescent = reader.ReadUInt16BE(); // skip over ulCodePageRange1-2 reader.Skip(sizeof(int) * 2); result.XHeight = reader.ReadInt16BE(); result.CapHeight = reader.ReadInt16BE(); return result; }