add postscript table

This commit is contained in:
Eliot Jones 2017-12-04 23:00:57 +00:00
parent c0618a303f
commit 8d9960ef98
4 changed files with 319 additions and 2 deletions

View File

@ -74,6 +74,20 @@
var maximumProfile = BasicMaximumProfileTable.Load(data, maxHeaderTable);
var postScriptTable = default(PostScriptTable);
if (tables.TryGetValue(TrueTypeHeaderTable.Post, out var postscriptHeaderTable))
{
postScriptTable = PostScriptTable.Load(data, table, maximumProfile);
}
if (!isPostScript)
{
if (!tables.TryGetValue(TrueTypeHeaderTable.Loca, out var indexToLocationHeaderTable))
{
throw new InvalidOperationException("The location to index table is required for non-PostScript fonts.");
}
}
return new TrueTypeFont(version, header);
}
}

View File

@ -0,0 +1,212 @@
namespace UglyToad.Pdf.Fonts.TrueType.Tables
{
using System;
using System.Text;
/// <summary>
/// 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
{
public string Tag => TrueTypeHeaderTable.Post;
public TrueTypeHeaderTable DirectoryTable { get; }
/// <summary>
/// Format 1 contains the 258 standard Mac TrueType font file.<br/>
/// Format 2 is the Microsoft font format.<br/>
/// Format 2.5 is a space optimised subset of the standard Mac glyph set.<br/>
/// Format 3 enables a special font type which provides no PostScript information.<br/>
/// </summary>
public decimal FormatType { get; }
/// <summary>
/// Angle in counter-clockwise degrees from vertical. 0 for upright text, negative for right-leaning text.
/// </summary>
public decimal ItalicAngle { get; }
/// <summary>
/// Suggested values for the underline position with negative values below the baseline.
/// </summary>
public short UnderlinePosition { get; }
/// <summary>
/// Suggested values for the underline thickness.
/// </summary>
public short UnderlineThickness { get; }
/// <summary>
/// 0 if the font is proportionally spaced, non-zero for monospace or other
/// non-proportional spacing.
/// </summary>
public long IsFixedPitch { get; }
/// <summary>
/// Minimum memory usage when the TrueType font is downloaded.
/// </summary>
public long MinimumMemoryType42 { get; }
/// <summary>
/// Maximum memory usage when the TrueType font is downloaded.
/// </summary>
public long MaximumMemoryType42 { get; }
/// <summary>
/// Minimum memory usage when the TrueType font is downloaded as a Type 1 font.
/// </summary>
public long MinimumMemoryType1 { get; }
/// <summary>
/// Maximum memory usage when the TrueType font is downloaded as a Type 1 font.
/// </summary>
public long MaximumMemoryType1 { get; }
public string[] GlyphNames { get; }
public PostScriptTable(TrueTypeHeaderTable directoryTable, decimal formatType, decimal italicAngle, short underlinePosition, short underlineThickness, long isFixedPitch, long minimumMemoryType42, long maximumMemoryType42, long minimumMemoryType1, long maximumMemoryType1, string[] glyphNames)
{
DirectoryTable = directoryTable;
FormatType = formatType;
ItalicAngle = italicAngle;
UnderlinePosition = underlinePosition;
UnderlineThickness = underlineThickness;
IsFixedPitch = isFixedPitch;
MinimumMemoryType42 = minimumMemoryType42;
MaximumMemoryType42 = maximumMemoryType42;
MinimumMemoryType1 = minimumMemoryType1;
MaximumMemoryType1 = maximumMemoryType1;
GlyphNames = glyphNames ?? throw new ArgumentNullException(nameof(glyphNames));
}
public static PostScriptTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table, BasicMaximumProfileTable maximumProfileTable)
{
data.Seek(table.Offset - 1);
var formatType = data.Read32Fixed();
var italicAngle = data.Read32Fixed();
var underlinePosition = data.ReadSignedShort();
var underlineThickness = data.ReadSignedShort();
var isFixedPitch = data.ReadUnsignedInt();
var minMemType42 = data.ReadUnsignedInt();
var maxMemType42 = data.ReadUnsignedInt();
var mimMemType1 = data.ReadUnsignedInt();
var maxMemType1 = data.ReadUnsignedInt();
var glyphNames = GetGlyphNamesByFormat(data, maximumProfileTable, formatType);
return new PostScriptTable(table, (decimal)formatType, (decimal)italicAngle,
underlinePosition, underlineThickness, isFixedPitch,
minMemType42, maxMemType42, mimMemType1,
maxMemType1, glyphNames);
}
private static string[] GetGlyphNamesByFormat(TrueTypeDataBytes data, BasicMaximumProfileTable maximumProfileTable,
float formatType)
{
string[] glyphNames;
if (Math.Abs(formatType - 1) < float.Epsilon)
{
glyphNames = new string[WindowsGlyphList4.NumberOfMacGlyphs];
Array.Copy(WindowsGlyphList4.MacGlyphNames, glyphNames, WindowsGlyphList4.NumberOfMacGlyphs);
}
else if (Math.Abs(formatType - 2) < float.Epsilon)
{
glyphNames = GetFormat2GlyphNames(data);
}
else if (Math.Abs(formatType - 2.5) < float.Epsilon)
{
var glyphNameIndex = new int[maximumProfileTable?.NumberOfGlyphs ?? 0];
for (var i = 0; i < glyphNameIndex.Length; i++)
{
var offset = data.ReadSignedByte();
glyphNameIndex[i] = i + 1 + offset;
}
glyphNames = new string[glyphNameIndex.Length];
for (var i = 0; i < glyphNames.Length; i++)
{
var name = WindowsGlyphList4.MacGlyphNames[glyphNameIndex[i]];
if (name != null)
{
glyphNames[i] = name;
}
}
}
else if (Math.Abs(formatType - 3) < float.Epsilon)
{
glyphNames = new string[0];
}
else
{
throw new InvalidOperationException($"Format type {formatType} is not supported for the PostScript table.");
}
return glyphNames;
}
private static string[] GetFormat2GlyphNames(TrueTypeDataBytes data)
{
const int reservedIndexStart = 32768;
var numberOfGlyphs = data.ReadUnsignedShort();
var glyphNameIndex = new int[numberOfGlyphs];
var glyphNames = new string[numberOfGlyphs];
var maxIndex = int.MinValue;
for (var i = 0; i < numberOfGlyphs; i++)
{
var index = data.ReadUnsignedShort();
glyphNameIndex[i] = index;
if (index < reservedIndexStart)
{
maxIndex = Math.Max(maxIndex, index);
}
}
var nameArray = default(string[]);
if (maxIndex >= WindowsGlyphList4.NumberOfMacGlyphs)
{
var namesLength = maxIndex - WindowsGlyphList4.NumberOfMacGlyphs + 1;
nameArray = new string[namesLength];
for (var i = 0; i < namesLength; i++)
{
var numberOfCharacters = data.ReadUnsignedByte();
nameArray[i] = data.ReadString(numberOfCharacters, Encoding.UTF8);
}
}
for (int i = 0; i < numberOfGlyphs; i++)
{
var index = glyphNameIndex[i];
if (index < WindowsGlyphList4.NumberOfMacGlyphs)
{
glyphNames[i] = WindowsGlyphList4.MacGlyphNames[index];
}
else if (index >= WindowsGlyphList4.NumberOfMacGlyphs && index < reservedIndexStart)
{
if (nameArray == null)
{
throw new InvalidOperationException("The name array was null despite the number of glyphs exceeding the maximum Mac Glyphs.");
}
glyphNames[i] = nameArray[index - WindowsGlyphList4.NumberOfMacGlyphs];
}
else
{
glyphNames[i] = ".undefined";
}
}
return glyphNames;
}
}
}

View File

@ -36,6 +36,14 @@
return (internalBuffer[0] << 8) + (internalBuffer[1] << 0);
}
public int ReadUnsignedByte()
{
ReadBuffered(internalBuffer, 1);
// TODO: the cast from int -> byte -> int here suggest we are treating data incorrectly.
return internalBuffer[0];
}
private void ReadBuffered(byte[] buffer, int length)
{
var numberRead = 0;
@ -69,7 +77,7 @@
public long ReadUnsignedInt()
{
ReadBuffered(internalBuffer, 4);
return (internalBuffer[0] << 24) + (internalBuffer[1] << 16) + (internalBuffer[2] << 8) + (internalBuffer[3] << 0);
}
@ -93,7 +101,7 @@
{
// TODO: this returns the wrong value, investigate...
long secondsSince1904 = ReadLong();
var date = new DateTime(1904, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var result = date.AddSeconds(secondsSince1904);
@ -118,5 +126,12 @@
return ret;
}
public int ReadSignedByte()
{
ReadBuffered(internalBuffer, 1);
var signedByte = internalBuffer[0];
return signedByte < 127 ? signedByte : signedByte - 256;
}
}
}

View File

@ -0,0 +1,76 @@
namespace UglyToad.Pdf.Fonts.TrueType
{
using System.Collections.Generic;
internal static class WindowsGlyphList4
{
/// <summary>
/// The number of standard mac glyph names.
/// </summary>
public static readonly int NumberOfMacGlyphs = 258;
/// <summary>
/// The 258 standard mac glyph names used in 'post' format 1 and 2.
/// </summary>
public static readonly string[] MacGlyphNames = {
".notdef",".null", "nonmarkingreturn", "space", "exclam", "quotedbl",
"numbersign", "dollar", "percent", "ampersand", "quotesingle",
"parenleft", "parenright", "asterisk", "plus", "comma", "hyphen",
"period", "slash", "zero", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine", "colon", "semicolon", "less",
"equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S",
"T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash",
"bracketright", "asciicircum", "underscore", "grave", "a", "b",
"c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
"p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft",
"bar", "braceright", "asciitilde", "Adieresis", "Aring",
"Ccedilla", "Eacute", "Ntilde", "Odieresis", "Udieresis", "aacute",
"agrave", "acircumflex", "adieresis", "atilde", "aring",
"ccedilla", "eacute", "egrave", "ecircumflex", "edieresis",
"iacute", "igrave", "icircumflex", "idieresis", "ntilde", "oacute",
"ograve", "ocircumflex", "odieresis", "otilde", "uacute", "ugrave",
"ucircumflex", "udieresis", "dagger", "degree", "cent", "sterling",
"section", "bullet", "paragraph", "germandbls", "registered",
"copyright", "trademark", "acute", "dieresis", "notequal", "AE",
"Oslash", "infinity", "plusminus", "lessequal", "greaterequal",
"yen", "mu", "partialdiff", "summation", "product", "pi",
"integral", "ordfeminine", "ordmasculine", "Omega", "ae", "oslash",
"questiondown", "exclamdown", "logicalnot", "radical", "florin",
"approxequal", "Delta", "guillemotleft", "guillemotright",
"ellipsis", "nonbreakingspace", "Agrave", "Atilde", "Otilde", "OE",
"oe", "endash", "emdash", "quotedblleft", "quotedblright",
"quoteleft", "quoteright", "divide", "lozenge", "ydieresis",
"Ydieresis", "fraction", "currency", "guilsinglleft",
"guilsinglright", "fi", "fl", "daggerdbl", "periodcentered",
"quotesinglbase", "quotedblbase", "perthousand", "Acircumflex",
"Ecircumflex", "Aacute", "Edieresis", "Egrave", "Iacute",
"Icircumflex", "Idieresis", "Igrave", "Oacute", "Ocircumflex",
"apple", "Ograve", "Uacute", "Ucircumflex", "Ugrave", "dotlessi",
"circumflex", "tilde", "macron", "breve", "dotaccent", "ring",
"cedilla", "hungarumlaut", "ogonek", "caron", "Lslash", "lslash",
"Scaron", "scaron", "Zcaron", "zcaron", "brokenbar", "Eth", "eth",
"Yacute", "yacute", "Thorn", "thorn", "minus", "multiply",
"onesuperior", "twosuperior", "threesuperior", "onehalf",
"onequarter", "threequarters", "franc", "Gbreve", "gbreve",
"Idotaccent", "Scedilla", "scedilla", "Cacute", "cacute", "Ccaron",
"ccaron", "dcroat"
};
/// <summary>
/// The indices of the standard mac glyph names.
/// </summary>
public static readonly IReadOnlyDictionary<string, int> MacGlyphNamesIndices;
static WindowsGlyphList4()
{
var indices = new Dictionary<string, int>(NumberOfMacGlyphs);
for (int i = 0; i < NumberOfMacGlyphs; ++i)
{
indices[MacGlyphNames[i]] = i;
}
MacGlyphNamesIndices = indices;
}
}
}