From b15a3a9b57826f445f62e1e0f18bf507eefa4346 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sat, 4 Jan 2020 09:16:08 +0000 Subject: [PATCH] tidy up truetype tables * improves the naming of truetype related classes. * uses correct numeric type for the loca table. * makes a few related classes public. --- .../PublicApiScannerTests.cs | 3 + .../TrueType/Parser/ITrueTypeTableParser.cs | 2 +- .../Fonts/TrueType/Parser/TableParser.cs | 2 +- .../Fonts/TrueType/Tables/CMapTable.cs | 2 +- .../Fonts/TrueType/Tables/GlyphDataTable.cs | 4 +- .../Fonts/TrueType/Tables/HeaderTable.cs | 2 +- .../TrueType/Tables/HorizontalHeaderTable.cs | 2 +- .../TrueType/Tables/HorizontalMetricsTable.cs | 2 +- .../Fonts/TrueType/Tables/ITable.cs | 9 --- .../Fonts/TrueType/Tables/ITrueTypeTable.cs | 18 +++++ .../TrueType/Tables/IndexToLocationTable.cs | 65 +++++++++++++++---- .../TrueType/Tables/MaximumProfileTable.cs | 2 +- .../Fonts/TrueType/Tables/NameTable.cs | 2 +- .../Fonts/TrueType/Tables/Os2Table.cs | 2 +- .../Fonts/TrueType/Tables/PostScriptTable.cs | 2 +- .../Fonts/TrueType/TrueTypeDataBytes.cs | 6 +- .../Fonts/TrueType/TrueTypeHeaderTable.cs | 43 ++++++++---- .../Subsetting/TrueTypeGlyphTableSubsetter.cs | 2 +- .../Fonts/Subsetting/TrueTypeSubsetter.cs | 3 +- 19 files changed, 124 insertions(+), 49 deletions(-) delete mode 100644 src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITable.cs create mode 100644 src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITrueTypeTable.cs diff --git a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs index 125f24ba..63db494b 100644 --- a/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs +++ b/src/UglyToad.PdfPig.Tests/PublicApiScannerTests.cs @@ -109,6 +109,9 @@ "UglyToad.PdfPig.Fonts.FontDescriptorFlags", "UglyToad.PdfPig.Fonts.FontStretch", "UglyToad.PdfPig.Fonts.Standard14Font", + "UglyToad.PdfPig.Fonts.TrueType.Tables.IndexToLocationTable", + "UglyToad.PdfPig.Fonts.TrueType.Tables.ITrueTypeTable", + "UglyToad.PdfPig.Fonts.TrueType.TrueTypeHeaderTable", "UglyToad.PdfPig.Geometry.GeometryExtensions", "UglyToad.PdfPig.Geometry.PdfPath", "UglyToad.PdfPig.Geometry.PdfPoint", diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Parser/ITrueTypeTableParser.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Parser/ITrueTypeTableParser.cs index e5d88ae0..3044545f 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Parser/ITrueTypeTableParser.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Parser/ITrueTypeTableParser.cs @@ -2,7 +2,7 @@ { using Tables; - internal interface ITrueTypeTableParser where T : ITable + internal interface ITrueTypeTableParser where T : ITrueTypeTable { T Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register); } diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Parser/TableParser.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Parser/TableParser.cs index 8b3a9d8e..57e03fa0 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Parser/TableParser.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Parser/TableParser.cs @@ -11,7 +11,7 @@ private static readonly NameTableParser NameTableParser = new NameTableParser(); private static readonly Os2TableParser Os2TableParser = new Os2TableParser(); - public static T Parse(TrueTypeHeaderTable table, TrueTypeDataBytes data, TableRegister.Builder register) where T : ITable + public static T Parse(TrueTypeHeaderTable table, TrueTypeDataBytes data, TableRegister.Builder register) where T : ITrueTypeTable { if (typeof(T) == typeof(CMapTable)) { diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/CMapTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/CMapTable.cs index b1e293d3..f8c0ffb8 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/CMapTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/CMapTable.cs @@ -7,7 +7,7 @@ using IO; using Util; - internal class CMapTable : ITable, IWriteable + internal class CMapTable : ITrueTypeTable, IWriteable { public IReadOnlyList SubTables { get; } diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs index 63fc825b..2e58be5e 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/GlyphDataTable.cs @@ -11,7 +11,7 @@ /// The 'glyf' table contains the data that defines the appearance of the glyphs in the font. /// This includes specification of the points that describe the contours that make up a glyph outline and the instructions that grid-fit that glyph. /// - internal class GlyphDataTable : ITable + internal class GlyphDataTable : ITrueTypeTable { public string Tag => TrueTypeHeaderTable.Glyf; @@ -34,7 +34,7 @@ var offsets = indexToLocationTable.GlyphOffsets; - var entryCount = offsets.Length; + var entryCount = offsets.Count; var glyphCount = entryCount - 1; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HeaderTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HeaderTable.cs index a08954a6..ceabc69a 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HeaderTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HeaderTable.cs @@ -8,7 +8,7 @@ /// The 'head' table contains global information about the font. /// It contains things like as the font version number, the creation and modification dates, revision number and basic typographic data that applies to the font as a whole. /// - internal class HeaderTable : ITable + internal class HeaderTable : ITrueTypeTable { public string Tag => TrueTypeHeaderTable.Head; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalHeaderTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalHeaderTable.cs index c4b03b77..f443999b 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalHeaderTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalHeaderTable.cs @@ -4,7 +4,7 @@ /// The 'hhea' table contains information needed to layout fonts whose characters are written horizontally, that is, either left to right or right to left. /// This table contains information that is general to the font as a whole. /// - internal class HorizontalHeaderTable : ITable + internal class HorizontalHeaderTable : ITrueTypeTable { public string Tag => TrueTypeHeaderTable.Hhea; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalMetricsTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalMetricsTable.cs index afeaad2e..232dceb9 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalMetricsTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/HorizontalMetricsTable.cs @@ -8,7 +8,7 @@ /// /// The 'hmtx' table contains metric information for the horizontal layout each of the glyphs in the font. /// - internal class HorizontalMetricsTable : ITable, IWriteable + internal class HorizontalMetricsTable : ITrueTypeTable, IWriteable { public string Tag => TrueTypeHeaderTable.Hmtx; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITable.cs deleted file mode 100644 index 0645a193..00000000 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITable.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UglyToad.PdfPig.Fonts.TrueType.Tables -{ - internal interface ITable - { - string Tag { get; } - - TrueTypeHeaderTable DirectoryTable { get; } - } -} diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITrueTypeTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITrueTypeTable.cs new file mode 100644 index 00000000..d3b20a1c --- /dev/null +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/ITrueTypeTable.cs @@ -0,0 +1,18 @@ +namespace UglyToad.PdfPig.Fonts.TrueType.Tables +{ + /// + /// A table in a TrueType font. + /// + public interface ITrueTypeTable + { + /// + /// The tag, a 4 letter/byte code, used to identify this table in a TrueType font. + /// + string Tag { get; } + + /// + /// The directory entry from the font's offset subtable which indicates the length, offset, type and checksum of a table. + /// + TrueTypeHeaderTable DirectoryTable { get; } + } +} diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/IndexToLocationTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/IndexToLocationTable.cs index 8b5a2506..a1f1adff 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/IndexToLocationTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/IndexToLocationTable.cs @@ -1,56 +1,95 @@ namespace UglyToad.PdfPig.Fonts.TrueType.Tables { using System; + using System.Collections.Generic; using System.IO; + using Exceptions; using IO; using Parser; using Util; + /// /// - /// Stores the offset to the glyph locations relative to the start of the glyph data table. + /// Stores the offset to the glyph locations relative to the start of the . /// Index zero points to the "missing character" which is used for characters not provided by the font. - /// The number of glpyhs in this table should match the maximum profile table. + /// The number of glpyhs in this table should match the maximum profile table. The glyph offsets contains + /// an extra entry at the last index which points to the end of the glyph data, this makes it possible to compute + /// the length of the last glyph entry and supports empty glyphs. /// - internal class IndexToLocationTable : ITable, IWriteable + public class IndexToLocationTable : ITrueTypeTable, IWriteable { + /// public string Tag => TrueTypeHeaderTable.Loca; + /// public TrueTypeHeaderTable DirectoryTable { get; } + /// + /// Indicates the format the offsets were stored in in the underlying file, for + /// the values are divided by 2. The values in are the real offsets, with any format + /// changes removed. + /// public EntryFormat Format { get; } /// /// The glyph offsets relative to the start of the glyph data table. /// - public long[] GlyphOffsets { get; } + public IReadOnlyList GlyphOffsets { get; } - public IndexToLocationTable(TrueTypeHeaderTable directoryTable, EntryFormat format, long[] glyphOffsets) + /// + /// Create a new . + /// + public IndexToLocationTable(TrueTypeHeaderTable directoryTable, EntryFormat format, IReadOnlyList glyphOffsets) { DirectoryTable = directoryTable; Format = format; - GlyphOffsets = glyphOffsets; + GlyphOffsets = glyphOffsets ?? throw new ArgumentNullException(nameof(glyphOffsets)); } - public static IndexToLocationTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, TableRegister.Builder tableRegister) + /// + /// Load the index to location (loca) table from the TrueType font. Requires the maximum profile (maxp) and header (head) table + /// to have been parsed. + /// + 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; - var offsets = new long[glyphCount]; + var offsets = new uint[glyphCount]; switch (format) { case EntryFormat.Short: { // The local offset divided by 2 is stored. - for (int i = 0; i < glyphCount; i++) + for (var i = 0; i < glyphCount; i++) { - offsets[i] = data.ReadUnsignedShort() * 2; + offsets[i] = (uint)(data.ReadUnsignedShort() * 2); } break; } @@ -68,9 +107,10 @@ return new IndexToLocationTable(table, format, offsets); } + /// public void Write(Stream stream) { - for (var i = 0; i < GlyphOffsets.Length; i++) + for (var i = 0; i < GlyphOffsets.Count; i++) { var offset = GlyphOffsets[i]; switch (Format) @@ -87,6 +127,9 @@ } } + /// + /// The format of glyph offset entries stored in the raw TrueType data. + /// public enum EntryFormat : short { /// diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/MaximumProfileTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/MaximumProfileTable.cs index 75c2c2b0..ec44ba60 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/MaximumProfileTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/MaximumProfileTable.cs @@ -5,7 +5,7 @@ /// /// This table establishes the memory requirements for the font. /// - internal class BasicMaximumProfileTable : ITable + internal class BasicMaximumProfileTable : ITrueTypeTable { public string Tag => TrueTypeHeaderTable.Maxp; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/NameTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/NameTable.cs index a5b1c5f5..cee084a4 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/NameTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/NameTable.cs @@ -4,7 +4,7 @@ using Names; using Util.JetBrains.Annotations; - internal class NameTable : ITable + internal class NameTable : ITrueTypeTable { public string Tag => TrueTypeHeaderTable.Name; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Os2Table.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Os2Table.cs index f233f9b1..e17186fc 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Os2Table.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/Os2Table.cs @@ -9,7 +9,7 @@ /// /// The most basic format of the OS/2 table, excluding the fields not included in the Apple version of the specification. /// - internal class Os2Table : ITable, IWriteable + internal class Os2Table : ITrueTypeTable, IWriteable { public string Tag => TrueTypeHeaderTable.Os2; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/PostScriptTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/PostScriptTable.cs index 8305938e..059b9add 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/Tables/PostScriptTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/Tables/PostScriptTable.cs @@ -7,7 +7,7 @@ /// This table contains information for TrueType fonts on PostScript printers. /// This includes data for the FontInfo dictionary and the PostScript glyph names. /// - internal class PostScriptTable : ITable + internal class PostScriptTable : ITrueTypeTable { public string Tag => TrueTypeHeaderTable.Post; diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeDataBytes.cs b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeDataBytes.cs index 460d9c4d..26bb1149 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeDataBytes.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeDataBytes.cs @@ -147,11 +147,11 @@ return result; } - public void ReadUnsignedIntArray(long[] offsets, int length) + public void ReadUnsignedIntArray(uint[] offsets, int length) { - for (int i = 0; i < length; i++) + for (var i = 0; i < length; i++) { - offsets[i] = ReadUnsignedInt(); + offsets[i] = (uint)ReadUnsignedInt(); } } diff --git a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeHeaderTable.cs b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeHeaderTable.cs index e804efea..3fa4e646 100644 --- a/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeHeaderTable.cs +++ b/src/UglyToad.PdfPig/Fonts/TrueType/TrueTypeHeaderTable.cs @@ -6,10 +6,12 @@ using Util; using Util.JetBrains.Annotations; + /// /// - /// A table directory entry from the TrueType font file. + /// A table directory entry from the TrueType font file. Indicates the position of the corresponding table + /// data in the TrueType font. /// - internal struct TrueTypeHeaderTable : IWriteable + public struct TrueTypeHeaderTable : IWriteable { #region RequiredTableTags /// @@ -160,7 +162,11 @@ #endregion #region PostScriptTableTags - + /// + /// Compact font format table. The corresponding table contains a Compact Font Format font representation + /// (also known as a PostScript Type 1, or CIDFont). + /// + /// Optional public const string Cff = "cff "; #endregion @@ -176,18 +182,31 @@ public uint CheckSum { get; } /// - /// Offset of the table from the beginning of the file. + /// Offset of the table data from the beginning of the file in bytes. /// public uint Offset { get; } /// - /// The length of the table. + /// The length of the table data in bytes. /// public uint Length { get; } + /// + /// Create a new . + /// public TrueTypeHeaderTable(string tag, uint checkSum, uint offset, uint length) { - Tag = tag ?? throw new ArgumentNullException(nameof(tag)); + if (tag == null) + { + throw new ArgumentNullException(nameof(tag)); + } + + if (tag.Length != 4) + { + throw new ArgumentException($"A TrueType table tag must be a uint32, 4 bytes long, instead got: {tag}.", nameof(tag)); + } + + Tag = tag; CheckSum = checkSum; Offset = offset; Length = length; @@ -201,11 +220,7 @@ return new TrueTypeHeaderTable(tag, 0, 0, 0); } - public override string ToString() - { - return $"{Tag} {Offset} {Length} {CheckSum}"; - } - + /// public void Write(Stream stream) { for (var i = 0; i < Tag.Length; i++) @@ -217,5 +232,11 @@ stream.WriteUInt(Offset); stream.WriteUInt(Length); } + + /// + public override string ToString() + { + return $"{Tag} - Offset: {Offset} Length: {Length} Checksum: {CheckSum}"; + } } } diff --git a/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeGlyphTableSubsetter.cs b/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeGlyphTableSubsetter.cs index a522d56c..74cf5869 100644 --- a/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeGlyphTableSubsetter.cs +++ b/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeGlyphTableSubsetter.cs @@ -150,7 +150,7 @@ { var indexToLocationTable = font.TableRegister.IndexToLocationTable; - var numGlyphs = indexToLocationTable.GlyphOffsets.Length - 1; + var numGlyphs = indexToLocationTable.GlyphOffsets.Count - 1; var glyphDirectory = font.TableRegister.GlyphTable.DirectoryTable; diff --git a/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeSubsetter.cs b/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeSubsetter.cs index 9055ea78..fadbf6de 100644 --- a/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeSubsetter.cs +++ b/src/UglyToad.PdfPig/Writer/Fonts/Subsetting/TrueTypeSubsetter.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; - using System.Linq; using IO; using PdfPig.Fonts.Exceptions; using PdfPig.Fonts.TrueType; @@ -117,7 +116,7 @@ else if (entry.Tag == TrueTypeHeaderTable.Loca) { var table = new IndexToLocationTable(entry.DummyHeader, IndexToLocationTable.EntryFormat.Long, - trueTypeSubsetGlyphTable.GlyphOffsets.Select(x => (long)x).ToArray()); + trueTypeSubsetGlyphTable.GlyphOffsets); table.Write(stream); } else if (entry.Tag == TrueTypeHeaderTable.Head)