#21 change dictionarytoken to use explicit key type, finish os/2 table for truetype, first file creation using embedded truetype font

This commit is contained in:
Eliot Jones
2018-12-08 14:38:27 +00:00
parent c2879ed4b4
commit 3a4b7b79d1
25 changed files with 383 additions and 207 deletions

View File

@@ -11,7 +11,7 @@
{
private readonly Ascii85Filter filter = new Ascii85Filter();
private readonly DictionaryToken dictionary = new DictionaryToken(new Dictionary<IToken, IToken>());
private readonly DictionaryToken dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>());
[Fact]
public void DecodesWikipediaExample()

View File

@@ -9,7 +9,7 @@
public class AsciiHexDecodeFilterTests
{
private readonly DictionaryToken dictionary = new DictionaryToken(new Dictionary<IToken, IToken>());
private readonly DictionaryToken dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>());
[Fact]
public void DecodesEncodedTextProperly()

View File

@@ -21,7 +21,7 @@
[Fact]
public void NegativeIndex_Throws()
{
Action action = () => resolver.GetFilterParameters(new DictionaryToken(new Dictionary<IToken, IToken>()), -1);
Action action = () => resolver.GetFilterParameters(new DictionaryToken(new Dictionary<NameToken, IToken>()), -1);
Assert.Throws<ArgumentOutOfRangeException>(action);
}
@@ -29,7 +29,7 @@
[Fact]
public void EmptyDictionary_ReturnsEmptyDictionary()
{
var result = resolver.GetFilterParameters(new DictionaryToken(new Dictionary<IToken, IToken>()), 0);
var result = resolver.GetFilterParameters(new DictionaryToken(new Dictionary<NameToken, IToken>()), 0);
Assert.Empty(result.Data);
}

View File

@@ -26,7 +26,7 @@
1, 10, 19
};
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<IToken, IToken>()), 1);
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<NameToken, IToken>()), 1);
var expectedResult = new byte[]
{
@@ -55,7 +55,7 @@
90, 6, 7
};
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<IToken, IToken>()), 0);
var decoded = filter.Decode(data, new DictionaryToken(new Dictionary<NameToken, IToken>()), 0);
var expectedResult = new byte[]
{

View File

@@ -88,13 +88,13 @@
var streamBytes = BytesBetween(streamPosition.end + 1, endStreamPosition.start - 1, bytes);
var paramsDict = new DictionaryToken(new Dictionary<IToken, IToken>
var paramsDict = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Predictor, new NumericToken(12) },
{ NameToken.Columns, new NumericToken(4) }
});
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{NameToken.Filter, NameToken.FlateDecode},
{NameToken.DecodeParms, paramsDict}
@@ -162,7 +162,7 @@
return result.ToArray();
}
private static string GetNthFilename(int n = 0)
private static string GetNthFilename()
{
var documentFolder = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "Integration", "Documents"));

View File

@@ -20,7 +20,7 @@ namespace UglyToad.PdfPig.Tests.Tokens
[Fact]
public void EmptyDictionaryValid()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>());
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>());
Assert.Empty(dictionary.Data);
}
@@ -28,7 +28,7 @@ namespace UglyToad.PdfPig.Tests.Tokens
[Fact]
public void TryGetByName_EmptyDictionary()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>());
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>());
var result = dictionary.TryGet(NameToken.ActualText, out var token);
@@ -39,7 +39,7 @@ namespace UglyToad.PdfPig.Tests.Tokens
[Fact]
public void TryGetByName_NullName_Throws()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>());
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>());
Action action = () => dictionary.TryGet(null, out var _);
@@ -49,7 +49,7 @@ namespace UglyToad.PdfPig.Tests.Tokens
[Fact]
public void TryGetByName_NonEmptyDictionaryNotContainingKey()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Create("Registry"), new StringToken("None") }
});
@@ -63,7 +63,7 @@ namespace UglyToad.PdfPig.Tests.Tokens
[Fact]
public void TryGetByName_ContainingKey()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Create("Fish"), new NumericToken(420) },
{ NameToken.Create("Registry"), new StringToken("None") }
@@ -74,22 +74,11 @@ namespace UglyToad.PdfPig.Tests.Tokens
Assert.True(result);
Assert.Equal("None", Assert.IsType<StringToken>(token).Data);
}
[Fact]
public void CreateWithNonNameTokensThrows()
{
Action action = () => new DictionaryToken(new Dictionary<IToken, IToken>
{
{ new NumericToken(7), NameToken.N }
});
Assert.Throws<PdfDocumentFormatException>(action);
}
[Fact]
public void GetWithObjectNotOfTypeOrReferenceThrows()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Count, new StringToken("twelve") }
});
@@ -102,7 +91,7 @@ namespace UglyToad.PdfPig.Tests.Tokens
[Fact]
public void WithCorrectlyAddsKey()
{
var dictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var dictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Count, new StringToken("12") }
});

View File

@@ -101,6 +101,18 @@
var b = builder.Build();
Assert.NotEmpty(b);
using (var document = PdfDocument.Open(b))
{
var page1 = document.GetPage(1);
Assert.Equal("Hello World!", page1.Text);
var h = page1.Letters[0];
Assert.Equal("H", h.Value);
Assert.Equal("Andada-Regular", h.FontName);
}
}
}
}

View File

@@ -3,8 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ContentStream;
using Cos;
using Logging;
using Tokens;
@@ -32,7 +30,7 @@
public CrossReferenceTable Build(long firstCrossReferenceOffset, ILog log)
{
CrossReferenceType type = CrossReferenceType.Table;
DictionaryToken trailerDictionary = new DictionaryToken(new Dictionary<IToken, IToken>());
DictionaryToken trailerDictionary = new DictionaryToken(new Dictionary<NameToken, IToken>());
Dictionary<IndirectReference, long> objectOffsets = new Dictionary<IndirectReference, long>();
List<long> xrefSeqBytePos = new List<long>();

View File

@@ -55,7 +55,7 @@
break;
}
return new DictionaryToken(new Dictionary<IToken, IToken>());
return new DictionaryToken(new Dictionary<NameToken, IToken>());
}
private static IToken GetDictionaryObject(DictionaryToken dictionary, NameToken first, NameToken second)

View File

@@ -26,5 +26,10 @@
Offset = offset;
Value = value;
}
public override string ToString()
{
return $"({PlatformId}, {NameId}) - {Value}";
}
}
}

View File

@@ -0,0 +1,151 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Parser
{
using System.Text;
using Names;
using Tables;
using Util;
using Util.JetBrains.Annotations;
internal class NameTableParser : ITrueTypeTableParser<NameTable>
{
public NameTable Parse(TrueTypeHeaderTable header, TrueTypeDataBytes data, TableRegister.Builder register)
{
data.Seek(header.Offset);
// ReSharper disable once UnusedVariable
var format = data.ReadUnsignedShort();
var count = data.ReadUnsignedShort();
var stringOffset = data.ReadUnsignedShort();
var names = new NameRecordBuilder[count];
for (var i = 0; i < count; i++)
{
names[i] = NameRecordBuilder.Read(data);
}
var strings = new TrueTypeNameRecord[count];
for (var i = 0; i < count; i++)
{
var nameRecord = names[i];
var encoding = OtherEncodings.Iso88591;
switch (nameRecord.PlatformId)
{
case TrueTypePlatformIdentifier.Windows:
{
var platformEncoding = (TrueTypeWindowsEncodingIdentifier)nameRecord.PlatformEncodingId;
if (platformEncoding == TrueTypeWindowsEncodingIdentifier.Symbol
|| platformEncoding == TrueTypeWindowsEncodingIdentifier.UnicodeBmp)
{
encoding = Encoding.BigEndianUnicode;
}
break;
}
case TrueTypePlatformIdentifier.Unicode:
{
encoding = Encoding.BigEndianUnicode;
break;
}
case TrueTypePlatformIdentifier.Iso:
{
switch (nameRecord.PlatformEncodingId)
{
case 0:
encoding = Encoding.GetEncoding("US-ASCII");
break;
case 1:
encoding = Encoding.GetEncoding("ISO-10646-UCS-2");
break;
}
break;
}
}
var position = header.Offset + stringOffset + nameRecord.Offset;
data.Seek(position);
var str = data.ReadString(nameRecord.Length, encoding);
strings[i] = nameRecord.ToNameRecord(str);
}
return new NameTable(header, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings);
}
[CanBeNull]
private static string GetName(int nameId, TrueTypeNameRecord[] names)
{
const int windowsEnUs = 409;
string windows = null;
string any = null;
for (var i = 0; i < names.Length; i++)
{
var name = names[i];
if (name.NameId != nameId)
{
continue;
}
if (name.PlatformId == TrueTypePlatformIdentifier.Windows && name.LanguageId == windowsEnUs)
{
return name.Value;
}
if (name.PlatformId == TrueTypePlatformIdentifier.Windows)
{
windows = name.Value;
}
any = name.Value;
}
return windows ?? any;
}
private struct NameRecordBuilder
{
public TrueTypePlatformIdentifier PlatformId { get; }
public int PlatformEncodingId { get; }
private int LanguageId { get; }
private int NameId { get; }
public int Length { get; }
public int Offset { get; }
private NameRecordBuilder(int platformId, int platformEncodingId, int languageId, int nameId, int length, int offset)
{
PlatformId = (TrueTypePlatformIdentifier)platformId;
PlatformEncodingId = platformEncodingId;
LanguageId = languageId;
NameId = nameId;
Length = length;
Offset = offset;
}
public TrueTypeNameRecord ToNameRecord(string s)
{
return new TrueTypeNameRecord(PlatformId, PlatformEncodingId,
LanguageId, NameId, Length, Offset, s);
}
public static NameRecordBuilder Read(TrueTypeDataBytes data)
{
return new NameRecordBuilder(data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort());
}
}
}
}

View File

@@ -73,14 +73,60 @@
if (version == 0)
{
return null;
return new Os2RevisedVersion0Table(header, version, xAvgCharWidth,
weightClass, widthClass, typeFlags, ySubscriptXSize,
ySubscriptYSize,
ySubscriptXOffset,
ySubscriptYOffset,
ySuperscriptXSize,
ySuperscriptYSize,
ySuperscriptXOffset,
ySuperscriptYOffset,
yStrikeoutSize,
yStrikeoutPosition,
familyClass,
panose,
unicodeCharRange,
Encoding.Unicode.GetString(vendorId),
selectionFlags,
firstCharacterIndex,
lastCharacterIndex,
sTypoAscender,
sTypoDescender,
sTypoLineGap,
usWinAscent,
usWinDescent);
}
var ulCodePageRange1 = data.ReadUnsignedInt();
var ulCodePageRange2 = data.ReadUnsignedInt();
var ulCodePageRange1 = (uint)data.ReadUnsignedInt();
var ulCodePageRange2 = (uint)data.ReadUnsignedInt();
if (version == 1)
{
return null;
return new Os2Version1Table(header, version, xAvgCharWidth,
weightClass, widthClass, typeFlags, ySubscriptXSize,
ySubscriptYSize,
ySubscriptXOffset,
ySubscriptYOffset,
ySuperscriptXSize,
ySuperscriptYSize,
ySuperscriptXOffset,
ySuperscriptYOffset,
yStrikeoutSize,
yStrikeoutPosition,
familyClass,
panose,
unicodeCharRange,
Encoding.Unicode.GetString(vendorId),
selectionFlags,
firstCharacterIndex,
lastCharacterIndex,
sTypoAscender,
sTypoDescender,
sTypoLineGap,
usWinAscent,
usWinDescent,
ulCodePageRange1,
ulCodePageRange2);
}
var sxHeight = data.ReadSignedShort();
@@ -91,14 +137,73 @@
if (version < 5)
{
return null;
return new Os2Version2To4OpenTypeTable(header, version, xAvgCharWidth,
weightClass, widthClass, typeFlags, ySubscriptXSize,
ySubscriptYSize,
ySubscriptXOffset,
ySubscriptYOffset,
ySuperscriptXSize,
ySuperscriptYSize,
ySuperscriptXOffset,
ySuperscriptYOffset,
yStrikeoutSize,
yStrikeoutPosition,
familyClass,
panose,
unicodeCharRange,
Encoding.Unicode.GetString(vendorId),
selectionFlags,
firstCharacterIndex,
lastCharacterIndex,
sTypoAscender,
sTypoDescender,
sTypoLineGap,
usWinAscent,
usWinDescent,
ulCodePageRange1,
ulCodePageRange2,
sxHeight,
sCapHeight,
usDefaultChar,
usBreakChar,
usMaxContext);
}
var usLowerOpticalPointSize = data.ReadUnsignedShort();
var usUpperOpticalPointSize = data.ReadUnsignedShort();
return null;
return new Os2Version5OpenTypeTable(header, version, xAvgCharWidth,
weightClass, widthClass, typeFlags, ySubscriptXSize,
ySubscriptYSize,
ySubscriptXOffset,
ySubscriptYOffset,
ySuperscriptXSize,
ySuperscriptYSize,
ySuperscriptXOffset,
ySuperscriptYOffset,
yStrikeoutSize,
yStrikeoutPosition,
familyClass,
panose,
unicodeCharRange,
Encoding.Unicode.GetString(vendorId),
selectionFlags,
firstCharacterIndex,
lastCharacterIndex,
sTypoAscender,
sTypoDescender,
sTypoLineGap,
usWinAscent,
usWinDescent,
ulCodePageRange1,
ulCodePageRange2,
sxHeight,
sCapHeight,
usDefaultChar,
usBreakChar,
usMaxContext,
usLowerOpticalPointSize,
usUpperOpticalPointSize);
}
}
}

View File

@@ -5,9 +5,16 @@
internal static class TableParser
{
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
{
if (typeof(T) == typeof(NameTable))
{
return (T) (object) NameTableParser.Parse(table, data, register);
}
if (typeof(T) == typeof(Os2Table))
{
return (T)(object)Os2TableParser.Parse(table, data, register);

View File

@@ -92,7 +92,7 @@
if (tables.TryGetValue(TrueTypeHeaderTable.Name, out var nameTable))
{
builder.NameTable = NameTable.Load(data, nameTable);
builder.NameTable = TableParser.Parse<NameTable>(nameTable, data, builder);
}
if (tables.TryGetValue(TrueTypeHeaderTable.Os2, out var os2Table))

View File

@@ -1,12 +1,10 @@
namespace UglyToad.PdfPig.Fonts.TrueType.Tables
{
using System.Collections.Generic;
using System.Text;
using Names;
using Util;
using Util.JetBrains.Annotations;
internal class NameTable
internal class NameTable : ITable
{
public string Tag => TrueTypeHeaderTable.Name;
@@ -33,144 +31,30 @@
NameRecords = nameRecords;
}
internal static NameTable Load(TrueTypeDataBytes data, TrueTypeHeaderTable table)
{
data.Seek(table.Offset);
// ReSharper disable once UnusedVariable
var format = data.ReadUnsignedShort();
var count = data.ReadUnsignedShort();
var stringOffset = data.ReadUnsignedShort();
var names = new NameRecordBuilder[count];
for (var i = 0; i < count; i++)
{
names[i] = NameRecordBuilder.Read(data);
}
var strings = new TrueTypeNameRecord[count];
for (var i = 0; i < count; i++)
{
var nameRecord = names[i];
var encoding = OtherEncodings.Iso88591;
switch (nameRecord.PlatformId)
{
case TrueTypePlatformIdentifier.Windows:
{
var platformEncoding = (TrueTypeWindowsEncodingIdentifier) nameRecord.PlatformEncodingId;
if (platformEncoding == TrueTypeWindowsEncodingIdentifier.Symbol
|| platformEncoding == TrueTypeWindowsEncodingIdentifier.UnicodeBmp)
{
encoding = Encoding.BigEndianUnicode;
}
break;
}
case TrueTypePlatformIdentifier.Unicode:
{
encoding = Encoding.BigEndianUnicode;
break;
}
case TrueTypePlatformIdentifier.Iso:
{
switch (nameRecord.PlatformEncodingId)
{
case 0:
encoding = Encoding.GetEncoding("US-ASCII");
break;
case 1:
encoding = Encoding.GetEncoding("ISO-10646-UCS-2");
break;
}
break;
}
}
var position = table.Offset + stringOffset + nameRecord.Offset;
data.Seek(position);
var str = data.ReadString(nameRecord.Length, encoding);
strings[i] = nameRecord.ToNameRecord(str);
}
return new NameTable(table, GetName(4, strings), GetName(1, strings), GetName(2, strings), strings);
}
/// <summary>
/// Gets the PostScript name for the font if specified, preferring the Windows platform name if present.
/// </summary>
/// <returns>The PostScript name for the font if found or <see langword="null"/>.</returns>
[CanBeNull]
private static string GetName(int nameId, TrueTypeNameRecord[] names)
public string GetPostscriptName()
{
const int windowsEnUs = 409;
string windows = null;
string any = null;
for (var i = 0; i < names.Length; i++)
foreach (var nameRecord in NameRecords)
{
var name = names[i];
if (name.NameId != nameId)
if (nameRecord.NameId != 6)
{
continue;
}
if (name.PlatformId == TrueTypePlatformIdentifier.Windows && name.LanguageId == windowsEnUs)
if (nameRecord.PlatformId == TrueTypePlatformIdentifier.Windows)
{
return name.Value;
return nameRecord.Value;
}
if (name.PlatformId == TrueTypePlatformIdentifier.Windows)
{
windows = name.Value;
}
any = name.Value;
any = nameRecord.Value;
}
return windows ?? any;
}
private struct NameRecordBuilder
{
public TrueTypePlatformIdentifier PlatformId { get; }
public int PlatformEncodingId { get; }
private int LanguageId { get; }
private int NameId { get; }
public int Length { get; }
public int Offset { get; }
private NameRecordBuilder(int platformId, int platformEncodingId, int languageId, int nameId, int length, int offset)
{
PlatformId = (TrueTypePlatformIdentifier)platformId;
PlatformEncodingId = platformEncodingId;
LanguageId = languageId;
NameId = nameId;
Length = length;
Offset = offset;
}
public TrueTypeNameRecord ToNameRecord(string s)
{
return new TrueTypeNameRecord(PlatformId, PlatformEncodingId,
LanguageId, NameId, Length, Offset, s);
}
public static NameRecordBuilder Read(TrueTypeDataBytes data)
{
return new NameRecordBuilder(data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort(),
data.ReadUnsignedShort());
}
return any;
}
}
}

View File

@@ -10,12 +10,12 @@
/// <summary>
/// This field is used to specify the code pages encompassed by the font file in the 'cmap' subtable for the Microsoft platform(3), Unicode BMP encoding (1).
/// </summary>
public byte CodePage1 { get; }
public uint CodePage1 { get; }
/// <summary>
/// This field is the second byte used to specify the code pages encompassed by the font file in the 'cmap' subtable for the Microsoft platform(3), Unicode BMP encoding (1).
/// </summary>
public byte CodePage2 { get; }
public uint CodePage2 { get; }
public Os2Version1Table(TrueTypeHeaderTable directoryTable, ushort version, short xAverageCharacterWidth, ushort weightClass, ushort widthClass,
ushort typeFlags,
@@ -41,8 +41,8 @@
short typographicLineGap,
ushort windowsAscent,
ushort windowsDescent,
byte codePage1,
byte codePage2) : base(directoryTable, version, xAverageCharacterWidth, weightClass, widthClass,
uint codePage1,
uint codePage2) : base(directoryTable, version, xAverageCharacterWidth, weightClass, widthClass,
typeFlags, ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, ySubscriptYOffset, ySuperscriptXSize,
ySuperscriptYSize, ySuperscriptXOffset, ySuperscriptYOffset, yStrikeoutSize, yStrikeoutPosition,
familyClass, panose, unicodeRanges, vendorId, fontSelectionFlags, firstCharacterIndex, lastCharacterIndex,

View File

@@ -69,8 +69,8 @@
short typographicLineGap,
ushort windowsAscent,
ushort windowsDescent,
byte codePage1,
byte codePage2,
uint codePage1,
uint codePage2,
short xHeight,
short capHeight,
ushort defaultCharacter,

View File

@@ -52,8 +52,8 @@
short typographicLineGap,
ushort windowsAscent,
ushort windowsDescent,
byte codePage1,
byte codePage2,
uint codePage1,
uint codePage2,
short xHeight,
short capHeight,
ushort defaultCharacter,

View File

@@ -237,7 +237,7 @@
{
IToken previousToken = null;
var dictionary = new Dictionary<IToken, IToken>();
var dictionary = new Dictionary<NameToken, IToken>();
// Skip the operators "dup" etc to reach "begin".
while (scanner.MoveNext() && (!(scanner.CurrentToken is OperatorToken operatorToken) || operatorToken.Data != "begin"))

View File

@@ -1,7 +1,7 @@
namespace UglyToad.PdfPig.Tokenization
{
using System.Collections.Generic;
using ContentStream;
using Exceptions;
using IO;
using Parser.Parts;
using Scanner;
@@ -58,19 +58,24 @@
return true;
}
private static Dictionary<IToken, IToken> ConvertToDictionary(IReadOnlyList<IToken> tokens)
private static Dictionary<NameToken, IToken> ConvertToDictionary(IReadOnlyList<IToken> tokens)
{
var result = new Dictionary<IToken, IToken>();
var result = new Dictionary<NameToken, IToken>();
IToken key = null;
NameToken key = null;
for (var i = 0; i < tokens.Count; i++)
{
var token = tokens[i];
if (key == null)
{
key = token;
continue;
if (token is NameToken name)
{
key = name;
continue;
}
throw new PdfDocumentFormatException($"Expected name as dictionary key, instead got: " + token);
}
// Combine indirect references, e.g. 12 0 R

View File

@@ -24,26 +24,18 @@
/// Create a new <see cref="DictionaryToken"/>.
/// </summary>
/// <param name="data">The data this dictionary will contain.</param>
public DictionaryToken([NotNull]IReadOnlyDictionary<IToken, IToken> data)
public DictionaryToken([NotNull]IReadOnlyDictionary<NameToken, IToken> data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
var result = new Dictionary<string, IToken>(data.Count);
foreach (var keyValuePair in data)
{
if (keyValuePair.Key is NameToken name)
{
result[name.Data] = keyValuePair.Value;
}
else
{
// For now:
throw new PdfDocumentFormatException($"Key for dictionary token was not a name! {keyValuePair.Key}");
}
result[keyValuePair.Key.Data] = keyValuePair.Value;
}
Data = result;
@@ -68,7 +60,7 @@
return typedToken;
}
/// <summary>
/// Try and get the entry with a given name.
/// </summary>

View File

@@ -113,14 +113,14 @@
fontsWritten.Add(font.Key, fontObj);
}
var resources = new Dictionary<IToken, IToken>
var resources = new Dictionary<NameToken, IToken>
{
{ NameToken.ProcSet, new ArrayToken(new []{ NameToken.Create("PDF"), NameToken.Create("Text") }) }
};
if (fontsWritten.Count > 0)
{
var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => ((IToken)fonts[x.Key].FontKey.Name, (IToken)new IndirectReferenceToken(x.Value.Number)))
var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => (fonts[x.Key].FontKey.Name, (IToken)new IndirectReferenceToken(x.Value.Number)))
.ToDictionary(x => x.Item1, x => x.Item2));
var fontsDictionaryRef = context.WriteObject(memory, fontsDictionary);
@@ -131,7 +131,7 @@
var pageReferences = new List<IndirectReferenceToken>();
foreach (var page in pages)
{
var pageDictionary = new Dictionary<IToken, IToken>
var pageDictionary = new Dictionary<NameToken, IToken>
{
{NameToken.Type, NameToken.Page},
{
@@ -155,7 +155,7 @@
pageReferences.Add(new IndirectReferenceToken(pageRef.Number));
}
var pagesDictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var pagesDictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Type, NameToken.Pages },
{ NameToken.Kids, new ArrayToken(pageReferences) },
@@ -164,7 +164,7 @@
var pagesRef = context.WriteObject(memory, pagesDictionary);
var catalog = new DictionaryToken(new Dictionary<IToken, IToken>
var catalog = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Type, NameToken.Catalog },
{ NameToken.Pages, new IndirectReferenceToken(pagesRef.Number) }
@@ -189,7 +189,7 @@
var bytes = memoryStream.ToArray();
var streamDictionary = new Dictionary<IToken, IToken>
var streamDictionary = new Dictionary<NameToken, IToken>
{
{ NameToken.Length, new NumericToken(bytes.Length) }
};

View File

@@ -35,7 +35,7 @@
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
{
var dictionary = new Dictionary<IToken, IToken>
var dictionary = new Dictionary<NameToken, IToken>
{
{ NameToken.Type, NameToken.Font },
{ NameToken.Subtype, NameToken.Type1 },

View File

@@ -150,7 +150,7 @@
outputStream.Write(Trailer, 0, Trailer.Length);
WriteLineBreak(outputStream);
var trailerDictionary = new DictionaryToken(new Dictionary<IToken, IToken>
var trailerDictionary = new DictionaryToken(new Dictionary<NameToken, IToken>
{
{NameToken.Size, new NumericToken(objectOffsets.Count) },
{NameToken.Root, new IndirectReferenceToken(catalogToken.Number) }

View File

@@ -2,8 +2,11 @@
{
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Fonts;
using Fonts.Exceptions;
using Fonts.TrueType;
using Fonts.TrueType.Tables;
using Geometry;
using Tokens;
@@ -28,14 +31,14 @@
public ObjectToken WriteFont(NameToken fontKeyName, Stream outputStream, BuilderContext context)
{
var bytes = fontFileBytes;
var embeddedFile = new StreamToken(new DictionaryToken(new Dictionary<IToken, IToken>
var embeddedFile = new StreamToken(new DictionaryToken(new Dictionary<NameToken, IToken>
{
{ NameToken.Length, new NumericToken(bytes.Count) }
}), bytes);
var fileRef = context.WriteObject(outputStream, embeddedFile);
var baseFont = NameToken.Create(font.TableRegister.NameTable.FontName);
var baseFont = NameToken.Create(font.TableRegister.NameTable.GetPostscriptName());
var postscript = font.TableRegister.PostScriptTable;
var hhead = font.TableRegister.HorizontalHeaderTable;
@@ -60,11 +63,36 @@
{ NameToken.FontFile2, new IndirectReferenceToken(fileRef.Number) }
};
var dictionary = new Dictionary<IToken, IToken>
var os2 = font.TableRegister.Os2Table;
if (os2 == null)
{
throw new InvalidFontFormatException("Embedding TrueType font requires OS/2 table.");
}
if (os2 is Os2Version2To4OpenTypeTable twoPlus)
{
descriptorDictionary[NameToken.CapHeight] = new NumericToken(twoPlus.CapHeight);
descriptorDictionary[NameToken.Xheight] = new NumericToken(twoPlus.XHeight);
}
descriptorDictionary[NameToken.StemV] = new NumericToken(bbox.Width * scaling * 0.13m);
var widths = font.TableRegister.GlyphTable.Glyphs.Select(x => new NumericToken(x.Bounds.Width)).ToArray();
var widthsRef = context.WriteObject(outputStream, new ArrayToken(widths));
var descriptor = context.WriteObject(outputStream, new DictionaryToken(descriptorDictionary));
var dictionary = new Dictionary<NameToken, IToken>
{
{ NameToken.Type, NameToken.Font },
{ NameToken.Subtype, NameToken.TrueType },
{ NameToken.BaseFont, baseFont },
{ NameToken.FontDescriptor, new IndirectReferenceToken(descriptor.Number) },
{ NameToken.FirstChar, new NumericToken(0) },
{ NameToken.LastChar, new NumericToken(font.TableRegister.GlyphTable.Glyphs.Count - 1) },
{ NameToken.Widths, new IndirectReferenceToken(widthsRef.Number) },
{ NameToken.Encoding, NameToken.WinAnsiEncoding }
};
var token = new DictionaryToken(dictionary);