mirror of
https://github.com/UglyToad/PdfPig.git
synced 2026-01-09 14:04:35 +08:00
cleanup font usage, fix some build system issues with older c# version
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user