public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) { data.Seek(table.Offset); var indexToLocationTable = tableRegister.IndexToLocationTable; var offsets = indexToLocationTable.GlyphOffsets; var entryCount = offsets.Length; var glyphCount = entryCount - 1; var glyphs = new IGlyphDescription[glyphCount]; var emptyGlyph = Glyph.Empty(tableRegister.HeaderTable.Bounds); var compositeLocations = new Dictionary <int, TemporaryCompositeLocation>(); for (var i = 0; i < glyphCount; i++) { if (offsets[i] == offsets[i + 1]) { // empty glyph glyphs[i] = emptyGlyph; continue; } data.Seek(offsets[i] + table.Offset); var contourCount = data.ReadSignedShort(); var minX = data.ReadSignedShort(); var minY = data.ReadSignedShort(); var maxX = data.ReadSignedShort(); var maxY = data.ReadSignedShort(); var bounds = new PdfRectangle(minX, minY, maxX, maxY); // If the number of contours is greater than or equal zero it's a simple glyph. if (contourCount >= 0) { glyphs[i] = ReadSimpleGlyph(data, contourCount, bounds); } else { compositeLocations.Add(i, new TemporaryCompositeLocation(data.Position, bounds, contourCount)); } } // Build composite glyphs by combining simple and other composite glyphs. foreach (var compositeLocation in compositeLocations) { glyphs[compositeLocation.Key] = ReadCompositeGlyph(data, compositeLocation.Value, compositeLocations, glyphs, emptyGlyph); } return(new GlyphDataTable(table, glyphs)); }
public static HorizontalMetricsTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) { var glyphCount = tableRegister.MaximumProfileTable.NumberOfGlyphs; var metricCount = tableRegister.HorizontalHeaderTable.NumberOfHeaderMetrics; data.Seek(table.Offset); // The number of entries in the left side bearing field per entry is number of glyphs - number of metrics var additionalLeftSideBearingLength = glyphCount - metricCount; var advancedWidths = new int[metricCount]; // For bearings over the metric count, the width is the same as the last width in advanced widths. var leftSideBearings = new short[glyphCount]; for (var i = 0; i < metricCount; i++) { advancedWidths[i] = data.ReadUnsignedShort(); leftSideBearings[i] = data.ReadSignedShort(); } for (var i = 0; i < additionalLeftSideBearingLength; i++) { leftSideBearings[metricCount + i] = data.ReadSignedShort(); } return(new HorizontalMetricsTable(table, advancedWidths, leftSideBearings, metricCount)); }
public static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) { const short shortFormat = 0; const short longFormat = 1; data.Seek(table.Offset); var headerTable = tableRegister.HeaderTable; var maximumProfileTable = tableRegister.MaximumProfileTable; var format = headerTable.IndexToLocFormat; var glyphCount = maximumProfileTable.NumberOfGlyphs + 1; var offsets = new long[glyphCount]; switch (format) { case shortFormat: { // The local offset divided by 2 is stored. for (int i = 0; i < glyphCount; i++) { offsets[i] = data.ReadUnsignedShort() * 2; } break; } case longFormat: { // The actual offset is stored. data.ReadUnsignedIntArray(offsets, glyphCount); break; } default: throw new InvalidOperationException($"The format {format} was invalid for the index to location (loca) table."); } return(new IndexToLocationTable(table, offsets)); }
public static GlyphDataTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) { data.Seek(table.Offset); var bytes = data.ReadByteArray((int)table.Length); return(new GlyphDataTable(table, tableRegister.IndexToLocationTable.GlyphOffsets, tableRegister.HeaderTable.Bounds, new TrueTypeDataBytes(bytes))); }
/// <summary> /// Load the index to location (loca) table from the TrueType font. Requires the maximum profile (maxp) and header (head) table /// to have been parsed. /// </summary> internal static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) { if (data == null) { throw new ArgumentNullException(nameof(data)); } if (tableRegister == null) { throw new ArgumentNullException(nameof(tableRegister)); } data.Seek(table.Offset); var headerTable = tableRegister.HeaderTable; var maximumProfileTable = tableRegister.MaximumProfileTable; if (headerTable == null) { throw new InvalidFontFormatException("No header (head) table was defined in this font."); } if (maximumProfileTable == null) { throw new InvalidFontFormatException("No maximum profile (maxp) table was defined in this font."); } var format = (EntryFormat)headerTable.IndexToLocFormat; var glyphCount = maximumProfileTable.NumberOfGlyphs + 1; uint[] offsets; switch (format) { case EntryFormat.Short: { // The local offset divided by 2 is stored. offsets = new uint[glyphCount]; for (var i = 0; i < glyphCount; i++) { offsets[i] = (uint)(data.ReadUnsignedShort() * 2); } break; } case EntryFormat.Long: { // The actual offset is stored. offsets = data.ReadUnsignedIntArray(glyphCount); break; } default: throw new InvalidOperationException($"The format {format} was invalid for the index to location (loca) table."); } return(new IndexToLocationTable(table, format, offsets)); }
public static CMapTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) { data.Seek(table.Offset); var tableVersionNumber = data.ReadUnsignedShort(); var numberOfEncodingTables = data.ReadUnsignedShort(); var subTableHeaders = new SubTableHeaderEntry[numberOfEncodingTables]; for (int i = 0; i < numberOfEncodingTables; i++) { var platformId = data.ReadUnsignedShort(); var encodingId = data.ReadUnsignedShort(); var offset = data.ReadUnsignedInt(); subTableHeaders[i] = new SubTableHeaderEntry(platformId, encodingId, offset); } var tables = new List <ICMapSubTable>(numberOfEncodingTables); var numberofGlyphs = tableRegister.MaximumProfileTable.NumberOfGlyphs; for (var i = 0; i < subTableHeaders.Length; i++) { var header = subTableHeaders[i]; data.Seek(table.Offset + header.Offset); var format = data.ReadUnsignedShort(); /* * There are 9 currently available formats: * 0: Character code and glyph indices are restricted to a single byte. Rare. * 2: Suitable for CJK characters. Contain mixed 8/16 byte encoding. * 4: 2 byte encoding format. Used when character codes fall into (gappy) contiguous ranges. * 6: 'Trimmed table mapping', used when character codes fall into a single contiguous range. This is dense mapping. * 8: 16/32 bit coverage. Uses mixed length character codes. * 10: Similar to format 6, trimmed table/array for 32 bits. * 12: Segmented coverage, similar to format 4 but for 32 bit/4 byte. * 13: Many to one mappings. Used by Apple for the LastResort font. * 14: Unicode variation sequences. * * Many of the formats are obsolete or not really used. Modern fonts will tend to use formats 4, 6 and 12. * For PDF we will support 0, 2 and 4 since these are in the original TrueType spec. */ switch (format) { case 0: { // Simple 1 to 1 mapping of character codes to glyph codes. var item = ByteEncodingCMapTable.Load(data, header.PlatformId, header.EncodingId); tables.Add(item); break; } case 2: { // Useful for CJK characters. Use mixed 8/16 bit encoding. var item = HighByteMappingCMapTable.Load(data, numberofGlyphs, header.PlatformId, header.EncodingId); tables.Add(item); break; } case 4: { // Microsoft's standard mapping table. var item = Format4CMapTable.Load(data, header.PlatformId, header.EncodingId); tables.Add(item); break; } case 6: { var item = TrimmedTableMappingCMapTable.Load(data, header.PlatformId, header.EncodingId); tables.Add(item); break; } } } return(new CMapTable(tableVersionNumber, table, tables)); }