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(); var b = builder.Build();
WriteFile(nameof(CanWriteSinglePageHelloWorld), b); WriteFile(nameof(CanAddHelloWorldToSimplePage), b);
Assert.NotEmpty(b); Assert.NotEmpty(b);
@@ -859,10 +859,12 @@
{ {
foreach (var letter in page.Letters) foreach (var letter in page.Letters)
{ {
unchecked { letters += 1; } unchecked { letters += 1; }
unchecked { unchecked {
location += letter.Location.X; 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 System.IO;
using Core; using Core;
using Tokens; using Tokens;
using Util.JetBrains.Annotations;
internal interface IWritingFont internal interface IWritingFont
{ {
@@ -16,7 +17,7 @@
TransformationMatrix GetFontMatrix(); TransformationMatrix GetFontMatrix();
IndirectReferenceToken WriteFont(IPdfStreamWriter writer, NameToken fontKeyName); IndirectReferenceToken WriteFont(IPdfStreamWriter writer, [CanBeNull]IndirectReferenceToken reservedIndirect=null);
byte GetValueForCharacter(char character); byte GetValueForCharacter(char character);
} }

View File

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

View File

@@ -47,7 +47,7 @@
return TransformationMatrix.FromValues(1.0 / unitsPerEm, 0, 0, 1.0 / unitsPerEm, 0, 0); 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 newEncoding = new TrueTypeSubsetEncoding(characterMapping.Keys.ToList());
var subsetBytes = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), newEncoding); var subsetBytes = TrueTypeSubsetter.Subset(fontFileBytes.ToArray(), newEncoding);
@@ -128,9 +128,12 @@
var token = new DictionaryToken(dictionary); 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) public byte GetValueForCharacter(char character)

View File

@@ -18,7 +18,7 @@ using Core;
private const decimal DefaultVersion = 1.2m; private const decimal DefaultVersion = 1.2m;
private bool Initialized { get; set; } private bool Initialized { get; set; }
private readonly Dictionary<IndirectReference, long> offsets = new Dictionary<IndirectReference, long>(); 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) 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 font = TrueTypeFontParser.Parse(new TrueTypeDataBytes(new ByteArrayInputBytes(fontFileBytes)));
var id = Guid.NewGuid(); 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)); fonts[id] = new FontStored(added, new TrueTypeWritingFont(font, fontFileBytes));
return added; return added;
} }
catch (Exception ex) catch (Exception ex)
@@ -183,9 +182,8 @@ namespace UglyToad.PdfPig.Writer
var id = Guid.NewGuid(); var id = Guid.NewGuid();
var name = NameToken.Create($"F{fontId++}"); 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))); fonts[id] = new FontStored(added, new Standard14WritingFont(Standard14.GetAdobeFontMetrics(type)));
return added; return added;
} }
@@ -350,6 +348,18 @@ namespace UglyToad.PdfPig.Writer
} }
var builder = new PdfPageBuilder(pages.Count + 1, this, streams, resources, copiedPageDict); 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);
}
pages[builder.PageNumber] = builder; pages[builder.PageNumber] = builder;
return builder; return builder;
} }
@@ -422,7 +432,7 @@ namespace UglyToad.PdfPig.Writer
foreach (var font in fonts) 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); fontsWritten.Add(font.Key, fontObj);
} }
@@ -440,16 +450,20 @@ namespace UglyToad.PdfPig.Writer
{ NameToken.ProcSet, new ArrayToken(procSet) } { NameToken.ProcSet, new ArrayToken(procSet) }
}; };
if (fontsWritten.Count > 0) // var fontDictionary = new DictionaryToken(fontsWritten.Select(x =>
{ // (fonts[x.Key].FontKey.Name, (IToken)x.Value))
var fontsDictionary = new DictionaryToken(fontsWritten.Select(x => // .ToDictionary(x => x.Item1, x => x.Item2));
(fonts[x.Key].FontKey.Name, (IToken)x.Value)) // var fontsDictionaryRef = context.WriteToken(fontDictionary);
.ToDictionary(x => x.Item1, x => x.Item2)); // if (fontsWritten.Count > 0)
// {
var fontsDictionaryRef = context.WriteToken(fontsDictionary); // var fontsDictionary = new DictionaryToken(fontsWritten.Select(x =>
// (fonts[x.Key].FontKey.Name, (IToken)x.Value))
resources.Add(NameToken.Font, fontsDictionaryRef); // .ToDictionary(x => x.Item1, x => x.Item2));
} //
// var fontsDictionaryRef = context.WriteToken(fontsDictionary);
//
// resources.Add(NameToken.Font, fontsDictionaryRef);
// }
var parentIndirect = context.ReserveObjectNumber(); var parentIndirect = context.ReserveObjectNumber();
@@ -459,12 +473,22 @@ namespace UglyToad.PdfPig.Writer
var pageDictionary = page.Value.additionalPageProperties; var pageDictionary = page.Value.additionalPageProperties;
pageDictionary[NameToken.Type] = NameToken.Page; pageDictionary[NameToken.Type] = NameToken.Page;
pageDictionary[NameToken.Parent] = parentIndirect; pageDictionary[NameToken.Parent] = parentIndirect;
pageDictionary[NameToken.ProcSet] = new ArrayToken(procSet);
if (!pageDictionary.ContainsKey(NameToken.MediaBox)) if (!pageDictionary.ContainsKey(NameToken.MediaBox))
{ {
pageDictionary[NameToken.MediaBox] = RectangleToArray(page.Value.PageSize); 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) 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 internal class FontStored
{ {
@@ -635,17 +650,17 @@ namespace UglyToad.PdfPig.Writer
internal Guid Id { get; } internal Guid Id { get; }
/// <summary> /// <summary>
/// The name of this font. /// Reference to the added font.
/// </summary> /// </summary>
public NameToken Name { get; } internal IndirectReferenceToken Reference { get; }
/// <summary> /// <summary>
/// Create a new <see cref="AddedFont"/>. /// Create a new <see cref="AddedFont"/>.
/// </summary> /// </summary>
internal AddedFont(Guid id, NameToken name) internal AddedFont(Guid id, IndirectReferenceToken reference)
{ {
Id = id; Id = id;
Name = name ?? throw new ArgumentNullException(nameof(name)); Reference = reference;
} }
} }

View File

@@ -33,6 +33,10 @@
internal readonly List<IPageContentStream> contentStreams; internal readonly List<IPageContentStream> contentStreams;
internal readonly Dictionary<NameToken, IToken> additionalPageProperties = new Dictionary<NameToken, IToken>(); internal readonly Dictionary<NameToken, IToken> additionalPageProperties = new Dictionary<NameToken, IToken>();
private readonly Dictionary<NameToken, IToken> resourcesDictionary = 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) //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; private int textSequence;
@@ -259,7 +263,7 @@
var textMatrix = TransformationMatrix.FromValues(1, 0, 0, 1, position.X, position.Y); 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; return letters;
} }
@@ -298,16 +302,18 @@
throw new ArgumentOutOfRangeException(nameof(fontSize), "Font size must be greater than 0"); throw new ArgumentOutOfRangeException(nameof(fontSize), "Font size must be greater than 0");
} }
var fontName = GetAddedFont(font);
var fontProgram = fontStore.FontProgram; var fontProgram = fontStore.FontProgram;
var fm = fontProgram.GetFontMatrix(); var fm = fontProgram.GetFontMatrix();
var textMatrix = TransformationMatrix.FromValues(1, 0, 0, 1, position.X, position.Y); 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(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)); currentStream.Add(new MoveToNextLineWithOffset((decimal)position.X, (decimal)position.Y));
var bytesPerShow = new List<byte>(); var bytesPerShow = new List<byte>();
foreach (var letter in text) foreach (var letter in text)
@@ -332,6 +338,23 @@
return letters; 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> /// <summary>
/// Adds the JPEG image represented by the input bytes at the specified location. /// Adds the JPEG image represented by the input bytes at the specified location.
/// </summary> /// </summary>
@@ -583,13 +606,15 @@
foreach (var fontSet in fontsDictionary.Data) foreach (var fontSet in fontsDictionary.Data)
{ {
var fontName = fontSet.Key; var fontName = NameToken.Create(fontSet.Key);
var addedFont = documentBuilder.Fonts.Values.FirstOrDefault(f => f.FontKey.Name.Data == fontName); if (fontDictionary.ContainsKey(fontName))
if (addedFont != default)
{ {
// This would mean that the imported font collide with one of the added font. so we have to rename it // This would mean that the imported font collide with one of the added font. so we have to rename it
var newName = NameToken.Create($"F{nextFontId++}");
var newName = $"F{documentBuilder.fontId++}"; while (fontDictionary.ContainsKey(newName))
{
newName = NameToken.Create($"F{nextFontId++}");
}
// Set all the pertinent SetFontAndSize operations with the new name // Set all the pertinent SetFontAndSize operations with the new name
operations = operations.Select(op => operations = operations.Select(op =>
@@ -601,7 +626,7 @@
if (fontAndSizeOperation.Font.Data == fontName) if (fontAndSizeOperation.Font.Data == fontName)
{ {
return new SetFontAndSize(NameToken.Create(newName), fontAndSizeOperation.Size); return new SetFontAndSize(newName, fontAndSizeOperation.Size);
} }
return op; return op;
@@ -615,10 +640,13 @@
throw new PdfDocumentFormatException($"Expected a IndirectReferenceToken for the font, got a {fontSet.Value.GetType().Name}"); 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 // 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); 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 horizontalScaling = 1;
var rise = 0; var rise = 0;
@@ -709,7 +737,7 @@
var documentSpace = textMatrix.Transform(renderingMatrix.Transform(fontMatrix.Transform(rect))); 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, GrayColor.Black,
(double)fontSize, (double)fontSize,
textSequence); textSequence);

View File

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