cleanup font usage, fix some build system issues with older c# version

This commit is contained in:
Plaisted
2021-02-06 15:13:22 -06:00
parent 7f42ad0af9
commit 44ee6d394f
9 changed files with 134 additions and 76 deletions

View File

@@ -719,7 +719,7 @@
var b = builder.Build();
WriteFile(nameof(CanWriteSinglePageHelloWorld), b);
WriteFile(nameof(CanAddHelloWorldToSimplePage), b);
Assert.NotEmpty(b);
@@ -859,10 +859,12 @@
{
foreach (var letter in page.Letters)
{
unchecked { letters += 1; }
unchecked {
location += letter.Location.X;
location += letter.Location.Y;
location += letter.Location.Y;
location += letter.Font.Name.Length;
}
}
}

View File

@@ -3,6 +3,7 @@
using System.IO;
using Core;
using Tokens;
using Util.JetBrains.Annotations;
internal interface IWritingFont
{
@@ -16,7 +17,7 @@
TransformationMatrix GetFontMatrix();
IndirectReferenceToken WriteFont(IPdfStreamWriter writer, NameToken fontKeyName);
IndirectReferenceToken WriteFont(IPdfStreamWriter writer, [CanBeNull]IndirectReferenceToken reservedIndirect=null);
byte GetValueForCharacter(char character);
}

View File

@@ -8,6 +8,7 @@
using PdfPig.Fonts.AdobeFontMetrics;
using PdfPig.Fonts.Encodings;
using Tokens;
using Util.JetBrains.Annotations;
internal class Standard14WritingFont : IWritingFont
{
@@ -55,18 +56,22 @@
return TransformationMatrix.FromValues(1/1000.0, 0, 0, 1/1000.0, 0, 0);
}
public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, NameToken fontKeyName)
public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, IndirectReferenceToken reservedIndirect=null)
{
var dictionary = new Dictionary<NameToken, IToken>
{
{ NameToken.Type, NameToken.Font },
{ NameToken.Subtype, NameToken.Type1 },
{ NameToken.BaseFont, NameToken.Create(metrics.FontName) },
{ NameToken.Encoding, NameToken.MacRomanEncoding },
{ NameToken.Name, fontKeyName }
{ NameToken.Encoding, NameToken.MacRomanEncoding }
};
var token = new DictionaryToken(dictionary);
if (reservedIndirect != null)
{
return writer.WriteToken(token, reservedIndirect);
}
var result = writer.WriteToken(token);

View File

@@ -47,7 +47,7 @@
return TransformationMatrix.FromValues(1.0 / unitsPerEm, 0, 0, 1.0 / unitsPerEm, 0, 0);
}
public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, NameToken fontKeyName)
public IndirectReferenceToken WriteFont(IPdfStreamWriter writer, IndirectReferenceToken reservedIndirect=null)
{
var newEncoding = new TrueTypeSubsetEncoding(characterMapping.Keys.ToList());
var subsetBytes = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), newEncoding);
@@ -128,9 +128,12 @@
var token = new DictionaryToken(dictionary);
var result = writer.WriteToken(token);
if (reservedIndirect != null)
{
return writer.WriteToken(token, reservedIndirect);
}
return result;
return writer.WriteToken(token);
}
public byte GetValueForCharacter(char character)

View File

@@ -18,7 +18,7 @@ using Core;
private const decimal DefaultVersion = 1.2m;
private bool Initialized { get; set; }
private readonly Dictionary<IndirectReference, long> offsets = new Dictionary<IndirectReference, long>();
private readonly Dictionary<byte[], IndirectReferenceToken> hashes = new (new FNVByteComparison());
private readonly Dictionary<byte[], IndirectReferenceToken> hashes = new Dictionary<byte[], IndirectReferenceToken>(new FNVByteComparison());
public PdfDedupStreamWriter(Stream stream, bool dispose)
{

View File

@@ -158,9 +158,8 @@ namespace UglyToad.PdfPig.Writer
{
var font = TrueTypeFontParser.Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(fontFileBytes)));
var id = Guid.NewGuid();
var added = new AddedFont(id, NameToken.Create($"F{fontId++}"));
var added = new AddedFont(id, context.ReserveObjectNumber());
fonts[id] = new FontStored(added, new TrueTypeWritingFont(font, fontFileBytes));
return added;
}
catch (Exception ex)
@@ -183,9 +182,8 @@ namespace UglyToad.PdfPig.Writer
var id = Guid.NewGuid();
var name = NameToken.Create($"F{fontId++}");
var added = new AddedFont(id, name);
var added = new AddedFont(id, context.ReserveObjectNumber());
fonts[id] = new FontStored(added, new Standard14WritingFont(Standard14.GetAdobeFontMetrics(type)));
return added;
}
@@ -332,24 +330,36 @@ namespace UglyToad.PdfPig.Writer
}
foreach (var kvp in pageDict.Data)
{
if (kvp.Key == NameToken.Contents || kvp.Key == NameToken.Parent || kvp.Key == NameToken.Type)
{
continue;
}
if (kvp.Key == NameToken.Resources)
{
CopyResourceDict(kvp.Value, resources);
continue;
}
copiedPageDict[NameToken.Create(kvp.Key)] =
WriterUtil.CopyToken(context, kvp.Value, document.Structure.TokenScanner, refs);
foreach (var kvp in pageDict.Data)
{
if (kvp.Key == NameToken.Contents || kvp.Key == NameToken.Parent || kvp.Key == NameToken.Type)
{
continue;
}
if (kvp.Key == NameToken.Resources)
{
CopyResourceDict(kvp.Value, resources);
continue;
}
copiedPageDict[NameToken.Create(kvp.Key)] =
WriterUtil.CopyToken(context, kvp.Value, document.Structure.TokenScanner, refs);
}
var builder = new PdfPageBuilder(pages.Count + 1, this, streams, resources, copiedPageDict);
if (resources.TryGetValue(NameToken.Font, out var fonts))
{
var existingFontDict = fonts as DictionaryToken;
foreach (var item in existingFontDict.Data)
{
var key = NameToken.Create(item.Key);
builder.fontDictionary[key] = item.Value;
}
resources.Remove(NameToken.Font);
}
var builder = new PdfPageBuilder(pages.Count + 1, this, streams, resources, copiedPageDict);
pages[builder.PageNumber] = builder;
return builder;
}
@@ -422,7 +432,7 @@ namespace UglyToad.PdfPig.Writer
foreach (var font in fonts)
{
var fontObj = font.Value.FontProgram.WriteFont(context, font.Value.FontKey.Name);
var fontObj = font.Value.FontProgram.WriteFont(context, font.Value.FontKey.Reference);
fontsWritten.Add(font.Key, fontObj);
}
@@ -440,16 +450,20 @@ namespace UglyToad.PdfPig.Writer
{ NameToken.ProcSet, new ArrayToken(procSet) }
};
if (fontsWritten.Count > 0)
{
var fontsDictionary = new DictionaryToken(fontsWritten.Select(x =>
(fonts[x.Key].FontKey.Name, (IToken)x.Value))
.ToDictionary(x => x.Item1, x => x.Item2));
var fontsDictionaryRef = context.WriteToken(fontsDictionary);
resources.Add(NameToken.Font, fontsDictionaryRef);
}
// var fontDictionary = new DictionaryToken(fontsWritten.Select(x =>
// (fonts[x.Key].FontKey.Name, (IToken)x.Value))
// .ToDictionary(x => x.Item1, x => x.Item2));
// var fontsDictionaryRef = context.WriteToken(fontDictionary);
// if (fontsWritten.Count > 0)
// {
// var fontsDictionary = new DictionaryToken(fontsWritten.Select(x =>
// (fonts[x.Key].FontKey.Name, (IToken)x.Value))
// .ToDictionary(x => x.Item1, x => x.Item2));
//
// var fontsDictionaryRef = context.WriteToken(fontsDictionary);
//
// resources.Add(NameToken.Font, fontsDictionaryRef);
// }
var parentIndirect = context.ReserveObjectNumber();
@@ -458,13 +472,23 @@ namespace UglyToad.PdfPig.Writer
{
var pageDictionary = page.Value.additionalPageProperties;
pageDictionary[NameToken.Type] = NameToken.Page;
pageDictionary[NameToken.Parent] = parentIndirect;
if (!pageDictionary.ContainsKey(NameToken.MediaBox))
{
pageDictionary[NameToken.MediaBox] = RectangleToArray(page.Value.PageSize);
}
pageDictionary[NameToken.Parent] = parentIndirect;
pageDictionary[NameToken.ProcSet] = new ArrayToken(procSet);
if (!pageDictionary.ContainsKey(NameToken.MediaBox))
{
pageDictionary[NameToken.MediaBox] = RectangleToArray(page.Value.PageSize);
}
pageDictionary[NameToken.Resources] = new DictionaryToken(page.Value.Resources);
// combine existing resources (if any) with added
var pageResources = new Dictionary<NameToken, IToken>();
foreach (var existing in page.Value.Resources)
{
pageResources[existing.Key] = existing.Value;
}
pageResources[NameToken.Font] = new DictionaryToken(page.Value.fontDictionary);
pageDictionary[NameToken.Resources] = new DictionaryToken(pageResources);
if (page.Value.contentStreams.Count == 1)
{
@@ -579,15 +603,6 @@ namespace UglyToad.PdfPig.Writer
});
}
private static void WriteString(string text, MemoryStream stream, bool appendBreak = true)
{
var bytes = OtherEncodings.StringAsLatin1Bytes(text);
stream.Write(bytes, 0, bytes.Length);
if (appendBreak)
{
stream.WriteNewLine();
}
}
internal class FontStored
{
@@ -635,17 +650,17 @@ namespace UglyToad.PdfPig.Writer
internal Guid Id { get; }
/// <summary>
/// The name of this font.
/// Reference to the added font.
/// </summary>
public NameToken Name { get; }
internal IndirectReferenceToken Reference { get; }
/// <summary>
/// Create a new <see cref="AddedFont"/>.
/// </summary>
internal AddedFont(Guid id, NameToken name)
internal AddedFont(Guid id, IndirectReferenceToken reference)
{
Id = id;
Name = name ?? throw new ArgumentNullException(nameof(name));
Reference = reference;
}
}

View File

@@ -154,7 +154,7 @@
}
var inputBytes = files[fileIndex];
var (coreScanner, version) = infos[fileIndex];
var (coreScanner, version) = infos[fileIndex];
var crossReferenceParser = new CrossReferenceParser(Log, new XrefOffsetValidator(Log),
new Parser.Parts.CrossReference.CrossReferenceStreamParser(FilterProvider));

View File

@@ -33,6 +33,10 @@
internal readonly List<IPageContentStream> contentStreams;
internal readonly Dictionary<NameToken, IToken> additionalPageProperties = new Dictionary<NameToken, IToken>();
private readonly Dictionary<NameToken, IToken> resourcesDictionary = new Dictionary<NameToken, IToken>();
internal Dictionary<NameToken, IToken> fontDictionary = new Dictionary<NameToken, IToken>();
internal int nextFontId = 1;
private readonly Dictionary<Guid, NameToken> documentFonts = new Dictionary<Guid, NameToken>();
//a sequence number of ShowText operation to determine whether letters belong to same operation or not (letters that belong to different operations have less changes to belong to same word)
private int textSequence;
@@ -259,7 +263,7 @@
var textMatrix = TransformationMatrix.FromValues(1, 0, 0, 1, position.X, position.Y);
var letters = DrawLetters(text, fontProgram, fm, fontSize, textMatrix);
var letters = DrawLetters(null, text, fontProgram, fm, fontSize, textMatrix);
return letters;
}
@@ -298,16 +302,18 @@
throw new ArgumentOutOfRangeException(nameof(fontSize), "Font size must be greater than 0");
}
var fontName = GetAddedFont(font);
var fontProgram = fontStore.FontProgram;
var fm = fontProgram.GetFontMatrix();
var textMatrix = TransformationMatrix.FromValues(1, 0, 0, 1, position.X, position.Y);
var letters = DrawLetters(text, fontProgram, fm, fontSize, textMatrix);
var letters = DrawLetters(fontName, text, fontProgram, fm, fontSize, textMatrix);
currentStream.Add(BeginText.Value);
currentStream.Add(new SetFontAndSize(font.Name, fontSize));
currentStream.Add(new SetFontAndSize(fontName, fontSize));
currentStream.Add(new MoveToNextLineWithOffset((decimal)position.X, (decimal)position.Y));
var bytesPerShow = new List<byte>();
foreach (var letter in text)
@@ -330,6 +336,23 @@
currentStream.Add(EndText.Value);
return letters;
}
private NameToken GetAddedFont(PdfDocumentBuilder.AddedFont font)
{
if (!documentFonts.TryGetValue(font.Id, out NameToken value))
{
value = NameToken.Create($"F{nextFontId++}");
while (fontDictionary.ContainsKey(value))
{
value = NameToken.Create($"F{nextFontId++}");
}
documentFonts[font.Id] = value;
fontDictionary[value] = font.Reference;
}
return value;
}
/// <summary>
@@ -583,13 +606,15 @@
foreach (var fontSet in fontsDictionary.Data)
{
var fontName = fontSet.Key;
var addedFont = documentBuilder.Fonts.Values.FirstOrDefault(f => f.FontKey.Name.Data == fontName);
if (addedFont != default)
var fontName = NameToken.Create(fontSet.Key);
if (fontDictionary.ContainsKey(fontName))
{
// This would mean that the imported font collide with one of the added font. so we have to rename it
var newName = $"F{documentBuilder.fontId++}";
var newName = NameToken.Create($"F{nextFontId++}");
while (fontDictionary.ContainsKey(newName))
{
newName = NameToken.Create($"F{nextFontId++}");
}
// Set all the pertinent SetFontAndSize operations with the new name
operations = operations.Select(op =>
@@ -601,7 +626,7 @@
if (fontAndSizeOperation.Font.Data == fontName)
{
return new SetFontAndSize(NameToken.Create(newName), fontAndSizeOperation.Size);
return new SetFontAndSize(newName, fontAndSizeOperation.Size);
}
return op;
@@ -615,10 +640,13 @@
throw new PdfDocumentFormatException($"Expected a IndirectReferenceToken for the font, got a {fontSet.Value.GetType().Name}");
}
pageFontsDictionary.Add(NameToken.Create(fontName), documentBuilder.CopyToken(srcPage.pdfScanner, fontReferenceToken));
pageFontsDictionary.Add(fontName, documentBuilder.CopyToken(srcPage.pdfScanner, fontReferenceToken));
}
resourcesDictionary[NameToken.Font] = new DictionaryToken(pageFontsDictionary);
foreach (var item in pageFontsDictionary)
{
fontDictionary[item.Key] = item.Value;
}
}
// Since we don't directly add xobjects's to the pages resources, we have to go look at the document's xobjects
@@ -677,7 +705,7 @@
destinationStream.Operations.AddRange(operations);
}
private List<Letter> DrawLetters(string text, IWritingFont font, TransformationMatrix fontMatrix, decimal fontSize, TransformationMatrix textMatrix)
private List<Letter> DrawLetters(NameToken name, string text, IWritingFont font, TransformationMatrix fontMatrix, decimal fontSize, TransformationMatrix textMatrix)
{
var horizontalScaling = 1;
var rise = 0;
@@ -709,7 +737,7 @@
var documentSpace = textMatrix.Transform(renderingMatrix.Transform(fontMatrix.Transform(rect)));
var letter = new Letter(c.ToString(), documentSpace, advanceRect.BottomLeft, advanceRect.BottomRight, width, (double)fontSize, FontDetails.GetDefault(font.Name),
var letter = new Letter(c.ToString(), documentSpace, advanceRect.BottomLeft, advanceRect.BottomRight, width, (double)fontSize, FontDetails.GetDefault(name),
GrayColor.Black,
(double)fontSize,
textSequence);

View File

@@ -25,8 +25,12 @@
public static IToken CopyToken(IPdfStreamWriter writer, IToken tokenToCopy, IPdfTokenScanner tokenScanner,
IDictionary<IndirectReference, IndirectReferenceToken> referencesFromDocument, Dictionary<IndirectReference, IndirectReferenceToken> callstack=null)
{
callstack ??= new Dictionary<IndirectReference, IndirectReferenceToken>();
// This token need to be deep copied, because they could contain reference. So we have to update them.
if (callstack == null)
{
callstack = new Dictionary<IndirectReference, IndirectReferenceToken>();
}
// This token need to be deep copied, because they could contain reference. So we have to update them.
switch (tokenToCopy)
{
case DictionaryToken dictionaryToken: