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.
This commit is contained in:
Eliot Jones
2020-01-04 09:16:08 +00:00
parent 0b103ce8ad
commit b15a3a9b57
19 changed files with 124 additions and 49 deletions

View File

@@ -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",

View File

@@ -2,7 +2,7 @@
{
using Tables;
internal interface ITrueTypeTableParser<out T> where T : ITable
internal interface ITrueTypeTableParser<out T> where T : ITrueTypeTable
{
T Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register);
}

View File

@@ -11,7 +11,7 @@
private static readonly NameTableParser NameTableParser = new NameTableParser();
private static readonly Os2TableParser Os2TableParser = new Os2TableParser();
public static T Parse<T>(TrueTypeHeaderTable table, TrueTypeDataBytes data, TableRegister.Builder register) where T : ITable
public static T Parse<T>(TrueTypeHeaderTable table, TrueTypeDataBytes data, TableRegister.Builder register) where T : ITrueTypeTable
{
if (typeof(T) == typeof(CMapTable))
{

View File

@@ -7,7 +7,7 @@
using IO;
using Util;
internal class CMapTable : ITable, IWriteable
internal class CMapTable : ITrueTypeTable, IWriteable
{
public IReadOnlyList<ICMapSubTable> SubTables { get; }

View File

@@ -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.
/// </summary>
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;

View File

@@ -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.
/// </summary>
internal class HeaderTable : ITable
internal class HeaderTable : ITrueTypeTable
{
public string Tag => TrueTypeHeaderTable.Head;

View File

@@ -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.
/// </summary>
internal class HorizontalHeaderTable : ITable
internal class HorizontalHeaderTable : ITrueTypeTable
{
public string Tag => TrueTypeHeaderTable.Hhea;

View File

@@ -8,7 +8,7 @@
/// <summary>
/// The 'hmtx' table contains metric information for the horizontal layout each of the glyphs in the font.
/// </summary>
internal class HorizontalMetricsTable : ITable, IWriteable
internal class HorizontalMetricsTable : ITrueTypeTable, IWriteable
{
public string Tag => TrueTypeHeaderTable.Hmtx;

View File

@@ -1,9 +0,0 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
{
internal interface ITable
{
string Tag { get; }
TrueTypeHeaderTable DirectoryTable { get; }
}
}

View File

@@ -0,0 +1,18 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
{
/// <summary>
/// A table in a TrueType font.
/// </summary>
public interface ITrueTypeTable
{
/// <summary>
/// The tag, a 4 letter/byte code, used to identify this table in a TrueType font.
/// </summary>
string Tag { get; }
/// <summary>
/// The directory entry from the font's offset subtable which indicates the length, offset, type and checksum of a table.
/// </summary>
TrueTypeHeaderTable DirectoryTable { get; }
}
}

View File

@@ -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;
/// <inheritdoc cref="ITrueTypeTable"/>
/// <summary>
/// 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 <see cref="GlyphDataTable"/>.
/// 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.
/// </summary>
internal class IndexToLocationTable : ITable, IWriteable
public class IndexToLocationTable : ITrueTypeTable, IWriteable
{
/// <inheritdoc />
public string Tag => TrueTypeHeaderTable.Loca;
/// <inheritdoc />
public TrueTypeHeaderTable DirectoryTable { get; }
/// <summary>
/// Indicates the format the offsets were stored in in the underlying file, for <see cref="EntryFormat.Short"/>
/// the values are divided by 2. The values in <see cref="GlyphOffsets"/> are the real offsets, with any format
/// changes removed.
/// </summary>
public EntryFormat Format { get; }
/// <summary>
/// The glyph offsets relative to the start of the glyph data table.
/// </summary>
public long[] GlyphOffsets { get; }
public IReadOnlyList<uint> GlyphOffsets { get; }
public IndexToLocationTable(TrueTypeHeaderTable directoryTable, EntryFormat format, long[] glyphOffsets)
/// <summary>
/// Create a new <see cref="IndexToLocationTable"/>.
/// </summary>
public IndexToLocationTable(TrueTypeHeaderTable directoryTable, EntryFormat format, IReadOnlyList<uint> 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)
/// <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;
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);
}
/// <inheritdoc />
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 @@
}
}
/// <summary>
/// The format of glyph offset entries stored in the raw TrueType data.
/// </summary>
public enum EntryFormat : short
{
/// <summary>

View File

@@ -5,7 +5,7 @@
/// <summary>
/// This table establishes the memory requirements for the font.
/// </summary>
internal class BasicMaximumProfileTable : ITable
internal class BasicMaximumProfileTable : ITrueTypeTable
{
public string Tag => TrueTypeHeaderTable.Maxp;

View File

@@ -4,7 +4,7 @@
using Names;
using Util.JetBrains.Annotations;
internal class NameTable : ITable
internal class NameTable : ITrueTypeTable
{
public string Tag => TrueTypeHeaderTable.Name;

View File

@@ -9,7 +9,7 @@
/// <summary>
/// The most basic format of the OS/2 table, excluding the fields not included in the Apple version of the specification.
/// </summary>
internal class Os2Table : ITable, IWriteable
internal class Os2Table : ITrueTypeTable, IWriteable
{
public string Tag => TrueTypeHeaderTable.Os2;

View File

@@ -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.
/// </summary>
internal class PostScriptTable : ITable
internal class PostScriptTable : ITrueTypeTable
{
public string Tag => TrueTypeHeaderTable.Post;

View File

@@ -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();
}
}

View File

@@ -6,10 +6,12 @@
using Util;
using Util.JetBrains.Annotations;
/// <inheritdoc cref="IWriteable" />
/// <summary>
/// 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.
/// </summary>
internal struct TrueTypeHeaderTable : IWriteable
public struct TrueTypeHeaderTable : IWriteable
{
#region RequiredTableTags
/// <summary>
@@ -160,7 +162,11 @@
#endregion
#region PostScriptTableTags
/// <summary>
/// Compact font format table. The corresponding table contains a Compact Font Format font representation
/// (also known as a PostScript Type 1, or CIDFont).
/// </summary>
/// <remarks>Optional</remarks>
public const string Cff = "cff ";
#endregion
@@ -176,18 +182,31 @@
public uint CheckSum { get; }
/// <summary>
/// Offset of the table from the beginning of the file.
/// Offset of the table data from the beginning of the file in bytes.
/// </summary>
public uint Offset { get; }
/// <summary>
/// The length of the table.
/// The length of the table data in bytes.
/// </summary>
public uint Length { get; }
/// <summary>
/// Create a new <see cref="TrueTypeHeaderTable"/>.
/// </summary>
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}";
}
/// <inheritdoc />
public void Write(Stream stream)
{
for (var i = 0; i < Tag.Length; i++)
@@ -217,5 +232,11 @@
stream.WriteUInt(Offset);
stream.WriteUInt(Length);
}
/// <inheritdoc />
public override string ToString()
{
return $"{Tag} - Offset: {Offset} Length: {Length} Checksum: {CheckSum}";
}
}
}

View File

@@ -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;

View File

@@ -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)