perf improvement for copying lots of pages from large documents

This commit is contained in:
Plaisted
2021-02-06 18:04:13 -06:00
parent 92f9af613f
commit 1db481164c
2 changed files with 411 additions and 391 deletions

View File

@@ -689,76 +689,76 @@
Assert.Equal("Lorem ipsum dolor sit amet, consectetur adipiscing elit. ", page2.Text);
}
}
[Fact]
public void CanAddHelloWorldToSimplePage()
{
var path = IntegrationHelpers.GetDocumentPath("Single Page Simple - from open office.pdf");
var doc = PdfDocument.Open(path);
var builder = new PdfDocumentBuilder();
var page = builder.AddPage(doc, 1);
page.DrawLine(new PdfPoint(30, 520), new PdfPoint(360, 520));
page.DrawLine(new PdfPoint(360, 520), new PdfPoint(360, 250));
page.SetStrokeColor(250, 132, 131);
page.DrawLine(new PdfPoint(25, 70), new PdfPoint(100, 70), 3);
page.ResetColor();
page.DrawRectangle(new PdfPoint(30, 200), 250, 100, 0.5m);
page.DrawRectangle(new PdfPoint(30, 100), 250, 100, 0.5m);
var file = TrueTypeTestHelper.GetFileBytes("Andada-Regular.ttf");
var font = builder.AddTrueTypeFont(file);
var letters = page.AddText("Hello World!", 12, new PdfPoint(30, 50), font);
Assert.NotEmpty(page.CurrentStream.Operations);
var b = builder.Build();
WriteFile(nameof(CanAddHelloWorldToSimplePage), b);
Assert.NotEmpty(b);
using (var document = PdfDocument.Open(b))
{
var page1 = document.GetPage(1);
Assert.Equal("I am a simple pdf.Hello World!", page1.Text);
var h = page1.Letters[18];
Assert.Equal("H", h.Value);
Assert.Equal("Andada-Regular", h.FontName);
var comparer = new DoubleComparer(0.01);
var pointComparer = new PointComparer(comparer);
for (int i = 0; i < letters.Count; i++)
{
var readerLetter = page1.Letters[i+18];
var writerLetter = letters[i];
Assert.Equal(readerLetter.Value, writerLetter.Value);
Assert.Equal(readerLetter.Location, writerLetter.Location, pointComparer);
Assert.Equal(readerLetter.FontSize, writerLetter.FontSize, comparer);
Assert.Equal(readerLetter.GlyphRectangle.Width, writerLetter.GlyphRectangle.Width, comparer);
Assert.Equal(readerLetter.GlyphRectangle.Height, writerLetter.GlyphRectangle.Height, comparer);
Assert.Equal(readerLetter.GlyphRectangle.BottomLeft, writerLetter.GlyphRectangle.BottomLeft, pointComparer);
}
}
}
[Fact]
public void CanMerge2SimpleDocumentsReversed_Builder()
{
var one = IntegrationHelpers.GetDocumentPath("Single Page Simple - from open office.pdf");
var two = IntegrationHelpers.GetDocumentPath("Single Page Simple - from inkscape.pdf");
}
[Fact]
public void CanAddHelloWorldToSimplePage()
{
var path = IntegrationHelpers.GetDocumentPath("Single Page Simple - from open office.pdf");
var doc = PdfDocument.Open(path);
var builder = new PdfDocumentBuilder();
var page = builder.AddPage(doc, 1);
page.DrawLine(new PdfPoint(30, 520), new PdfPoint(360, 520));
page.DrawLine(new PdfPoint(360, 520), new PdfPoint(360, 250));
page.SetStrokeColor(250, 132, 131);
page.DrawLine(new PdfPoint(25, 70), new PdfPoint(100, 70), 3);
page.ResetColor();
page.DrawRectangle(new PdfPoint(30, 200), 250, 100, 0.5m);
page.DrawRectangle(new PdfPoint(30, 100), 250, 100, 0.5m);
var file = TrueTypeTestHelper.GetFileBytes("Andada-Regular.ttf");
var font = builder.AddTrueTypeFont(file);
var letters = page.AddText("Hello World!", 12, new PdfPoint(30, 50), font);
Assert.NotEmpty(page.CurrentStream.Operations);
var b = builder.Build();
WriteFile(nameof(CanAddHelloWorldToSimplePage), b);
Assert.NotEmpty(b);
using (var document = PdfDocument.Open(b))
{
var page1 = document.GetPage(1);
Assert.Equal("I am a simple pdf.Hello World!", page1.Text);
var h = page1.Letters[18];
Assert.Equal("H", h.Value);
Assert.Equal("Andada-Regular", h.FontName);
var comparer = new DoubleComparer(0.01);
var pointComparer = new PointComparer(comparer);
for (int i = 0; i < letters.Count; i++)
{
var readerLetter = page1.Letters[i+18];
var writerLetter = letters[i];
Assert.Equal(readerLetter.Value, writerLetter.Value);
Assert.Equal(readerLetter.Location, writerLetter.Location, pointComparer);
Assert.Equal(readerLetter.FontSize, writerLetter.FontSize, comparer);
Assert.Equal(readerLetter.GlyphRectangle.Width, writerLetter.GlyphRectangle.Width, comparer);
Assert.Equal(readerLetter.GlyphRectangle.Height, writerLetter.GlyphRectangle.Height, comparer);
Assert.Equal(readerLetter.GlyphRectangle.BottomLeft, writerLetter.GlyphRectangle.BottomLeft, pointComparer);
}
}
}
[Fact]
public void CanMerge2SimpleDocumentsReversed_Builder()
{
var one = IntegrationHelpers.GetDocumentPath("Single Page Simple - from open office.pdf");
var two = IntegrationHelpers.GetDocumentPath("Single Page Simple - from inkscape.pdf");
using (var docOne = PdfDocument.Open(one))
using (var docTwo = PdfDocument.Open(two))
{
@@ -768,12 +768,12 @@
var result = builder.Build();
PdfMergerTests.CanMerge2SimpleDocumentsAssertions(new MemoryStream(result), "I am a simple pdf.", "Write something inInkscape", false);
}
}
[Fact]
public void CanMerge2SimpleDocuments_Builder()
{
var one = IntegrationHelpers.GetDocumentPath("Single Page Simple - from inkscape.pdf");
}
[Fact]
public void CanMerge2SimpleDocuments_Builder()
{
var one = IntegrationHelpers.GetDocumentPath("Single Page Simple - from inkscape.pdf");
var two = IntegrationHelpers.GetDocumentPath("Single Page Simple - from open office.pdf");
using (var docOne = PdfDocument.Open(one))
@@ -787,12 +787,12 @@
PdfMergerTests.CanMerge2SimpleDocumentsAssertions(new MemoryStream(result), "Write something inInkscape", "I am a simple pdf.", false);
}
}
[Fact]
public void CanDedupObjectsFromSameDoc_Builder()
{
}
[Fact]
public void CanDedupObjectsFromSameDoc_Builder()
{
var one = IntegrationHelpers.GetDocumentPath("Multiple Page - from Mortality Statistics.pdf");
using (var doc = PdfDocument.Open(one))
@@ -810,11 +810,11 @@
"Expected object count to be lower than 30"); // 45 objects with duplicates, 29 with correct re-use
}
}
}
[Fact]
public void CanDedupObjectsFromDifferentDoc_HashBuilder()
{
}
[Fact]
public void CanDedupObjectsFromDifferentDoc_HashBuilder()
{
var one = IntegrationHelpers.GetDocumentPath("Multiple Page - from Mortality Statistics.pdf");
using (var doc = PdfDocument.Open(one))
using (var doc2 = PdfDocument.Open(one))
@@ -832,19 +832,19 @@
"Expected object count to be lower than 30"); // 45 objects with duplicates, 29 with correct re-use
}
}
}
[InlineData("Single Page Simple - from google drive.pdf")]
[InlineData("Old Gutnish Internet Explorer.pdf")]
[InlineData("68-1990-01_A.pdf")]
[InlineData("Multiple Page - from Mortality Statistics.pdf")]
[Theory]
public void CopiedPagesResultInSameData(string name)
{
var docPath = IntegrationHelpers.GetDocumentPath(name);
}
[InlineData("Single Page Simple - from google drive.pdf")]
[InlineData("Old Gutnish Internet Explorer.pdf")]
[InlineData("68-1990-01_A.pdf")]
[InlineData("Multiple Page - from Mortality Statistics.pdf")]
[Theory]
public void CopiedPagesResultInSameData(string name)
{
var docPath = IntegrationHelpers.GetDocumentPath(name);
using (var doc = PdfDocument.Open(docPath, ParsingOptions.LenientParsingOff))
using (var builder = new PdfDocumentBuilder())
using (var builder = new PdfDocumentBuilder())
{
var count1 = GetCounts(doc);
@@ -859,30 +859,30 @@
var count2 = GetCounts(doc2);
Assert.Equal(count1.Item1, count2.Item1);
Assert.Equal(count1.Item2, count2.Item2);
}
}
(int, double) GetCounts(PdfDocument toCount)
{
int letters = 0;
double location = 0;
foreach (var page in toCount.GetPages())
{
foreach (var letter in page.Letters)
{
unchecked { letters += 1; }
unchecked {
location += letter.Location.X;
}
}
(int, double) GetCounts(PdfDocument toCount)
{
int letters = 0;
double location = 0;
foreach (var page in toCount.GetPages())
{
foreach (var letter in page.Letters)
{
unchecked { letters += 1; }
unchecked {
location += letter.Location.X;
location += letter.Location.Y;
location += letter.Font.Name.Length;
}
}
}
return (letters, location);
}
}
}
}
return (letters, location);
}
}
private static void WriteFile(string name, byte[] bytes, string extension = "pdf")

View File

@@ -269,217 +269,237 @@ namespace UglyToad.PdfPig.Writer
}
return WriterUtil.CopyToken(context, token, source, refs);
}
}
internal class PageInfo
{
public DictionaryToken Page { get; set; }
public List<DictionaryToken> Parents { get; set; }
}
private readonly ConditionalWeakTable<IPdfTokenScanner, Dictionary<IndirectReference, IndirectReferenceToken>> existingCopies =
new ConditionalWeakTable<IPdfTokenScanner, Dictionary<IndirectReference, IndirectReferenceToken>>();
/// <summary>
/// Add a new page with the specified size, this page will be included in the output when <see cref="Build"/> is called.
/// </summary>
/// <param name="document">Source document.</param>
/// <param name="pageNumber">Page to copy.</param>
/// <returns>A builder for editing the page.</returns>
public PdfPageBuilder AddPage(PdfDocument document, int pageNumber)
{
if (!existingCopies.TryGetValue(document.Structure.TokenScanner, out var refs))
{
refs = new Dictionary<IndirectReference, IndirectReferenceToken>();
existingCopies.Add(document.Structure.TokenScanner, refs);
}
int i = 1;
foreach (var (pageDict, parents) in WriterUtil.WalkTree(document.Structure.Catalog.PageTree))
{
if (i == pageNumber)
{
// copy content streams
var streams = new List<PdfPageBuilder.CopiedContentStream>();
if (pageDict.ContainsKey(NameToken.Contents))
{
var token = pageDict.Data[NameToken.Contents];
if (token is ArrayToken array)
{
foreach (var item in array.Data)
{
if (item is IndirectReferenceToken ir)
{
streams.Add(new PdfPageBuilder.CopiedContentStream(
WriterUtil.CopyToken(context, ir, document.Structure.TokenScanner, refs) as IndirectReferenceToken));
}
}
}
else if (token is IndirectReferenceToken ir)
{
streams.Add(new PdfPageBuilder.CopiedContentStream(
WriterUtil.CopyToken(context, ir, document.Structure.TokenScanner, refs) as IndirectReferenceToken));
}
}
// manually copy page dict / resources as we need to modify some
var copiedPageDict = new Dictionary<NameToken, IToken>();
Dictionary<NameToken, IToken> resources = new Dictionary<NameToken, IToken>();
// just put all parent resources into new page
foreach (var dict in parents)
{
if (dict.TryGet(NameToken.Resources, out var token))
{
CopyResourceDict(token, resources);
}
}
foreach (var kvp in pageDict.Data)
new ConditionalWeakTable<IPdfTokenScanner, Dictionary<IndirectReference, IndirectReferenceToken>>();
private readonly ConditionalWeakTable<PdfDocument, Dictionary<int, PageInfo>> existingTrees =
new ConditionalWeakTable<PdfDocument, Dictionary<int, PageInfo>>();
/// <summary>
/// Add a new page with the specified size, this page will be included in the output when <see cref="Build"/> is called.
/// </summary>
/// <param name="document">Source document.</param>
/// <param name="pageNumber">Page to copy.</param>
/// <returns>A builder for editing the page.</returns>
public PdfPageBuilder AddPage(PdfDocument document, int pageNumber)
{
if (!existingCopies.TryGetValue(document.Structure.TokenScanner, out var refs))
{
refs = new Dictionary<IndirectReference, IndirectReferenceToken>();
existingCopies.Add(document.Structure.TokenScanner, refs);
}
if (!existingTrees.TryGetValue(document, out var pagesInfos))
{
pagesInfos = new Dictionary<int, PageInfo>();
int i = 1;
foreach (var (pageDict, parents) in WriterUtil.WalkTree(document.Structure.Catalog.PageTree))
{
pagesInfos[i] = new PageInfo
{
if (kvp.Key == NameToken.Contents || kvp.Key == NameToken.Parent || kvp.Key == NameToken.Type)
Page = pageDict, Parents = parents
};
i++;
}
existingTrees.Add(document, pagesInfos);
}
if (!pagesInfos.ContainsKey(pageNumber))
{
throw new KeyNotFoundException($"Page {pageNumber} was not found in the source document.");
}
var pageInfo = pagesInfos[pageNumber];
// copy content streams
var streams = new List<PdfPageBuilder.CopiedContentStream>();
if (pageInfo.Page.ContainsKey(NameToken.Contents))
{
var token = pageInfo.Page.Data[NameToken.Contents];
if (token is ArrayToken array)
{
foreach (var item in array.Data)
{
if (item is IndirectReferenceToken ir)
{
continue;
streams.Add(new PdfPageBuilder.CopiedContentStream(
WriterUtil.CopyToken(context, ir, document.Structure.TokenScanner, refs) as IndirectReferenceToken));
}
if (kvp.Key == NameToken.Resources)
}
}
else if (token is IndirectReferenceToken ir)
{
streams.Add(new PdfPageBuilder.CopiedContentStream(
WriterUtil.CopyToken(context, ir, document.Structure.TokenScanner, refs) as IndirectReferenceToken));
}
}
// manually copy page dict / resources as we need to modify some
var copiedPageDict = new Dictionary<NameToken, IToken>();
Dictionary<NameToken, IToken> resources = new Dictionary<NameToken, IToken>();
// just put all parent resources into new page
foreach (var dict in pageInfo.Parents)
{
if (dict.TryGet(NameToken.Resources, out var token))
{
CopyResourceDict(token, resources);
}
}
foreach (var kvp in pageInfo.Page.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);
}
pages[builder.PageNumber] = builder;
return builder;
void CopyResourceDict(IToken token, Dictionary<NameToken, IToken> destinationDict)
{
DictionaryToken dict = GetRemoteDict(token);
if (dict == null)
{
return;
}
foreach (var item in dict.Data)
{
if (!destinationDict.ContainsKey(NameToken.Create(item.Key)))
{
if (item.Value is IndirectReferenceToken ir)
{
CopyResourceDict(kvp.Value, resources);
continue;
destinationDict[NameToken.Create(item.Key)] = WriterUtil.CopyToken(context, document.Structure.TokenScanner.Get(ir.Data).Data, document.Structure.TokenScanner, refs);
}
else
{
destinationDict[NameToken.Create(item.Key)] = WriterUtil.CopyToken(context, item.Value, document.Structure.TokenScanner, refs);
}
copiedPageDict[NameToken.Create(kvp.Key)] =
WriterUtil.CopyToken(context, kvp.Value, document.Structure.TokenScanner, refs);
continue;
}
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;
return builder;
}
i++;
}
throw new KeyNotFoundException($"Page {pageNumber} was not found in the source document.");
void CopyResourceDict(IToken token, Dictionary<NameToken, IToken> destinationDict)
{
DictionaryToken dict = GetRemoteDict(token);
if (dict == null)
{
return;
}
foreach (var item in dict.Data)
{
if (!destinationDict.ContainsKey(NameToken.Create(item.Key)))
{
if (item.Value is IndirectReferenceToken ir)
{
destinationDict[NameToken.Create(item.Key)] = WriterUtil.CopyToken(context, document.Structure.TokenScanner.Get(ir.Data).Data, document.Structure.TokenScanner, refs);
}
else
{
destinationDict[NameToken.Create(item.Key)] = WriterUtil.CopyToken(context, item.Value, document.Structure.TokenScanner, refs);
}
continue;
}
var subDict = GetRemoteDict(item.Value);
var destSubDict = destinationDict[NameToken.Create(item.Key)] as DictionaryToken;
if (destSubDict == null || subDict == null)
{
// not a dict.. just overwrite with more important one? should maybe check arrays?
destinationDict[NameToken.Create(item.Key)] = WriterUtil.CopyToken(context, item.Value, document.Structure.TokenScanner, refs);
continue;
}
foreach (var subItem in subDict.Data)
{
// last copied most important important
destinationDict[NameToken.Create(subItem.Key)] = WriterUtil.CopyToken(context, subItem.Value,
document.Structure.TokenScanner, refs);
}
}
}
DictionaryToken GetRemoteDict(IToken token)
{
DictionaryToken dict = null;
if (token is IndirectReferenceToken ir)
{
dict = document.Structure.TokenScanner.Get(ir.Data).Data as DictionaryToken;
}
else if (token is DictionaryToken dt)
{
dict = dt;
}
return dict;
}
}
private void CompleteDocument()
{
var fontsWritten = new Dictionary<Guid, IndirectReferenceToken>();
foreach (var font in fonts)
{
var fontObj = font.Value.FontProgram.WriteFont(context, font.Value.FontKey.Reference);
fontsWritten.Add(font.Key, fontObj);
}
var procSet = new List<NameToken>
{
NameToken.Create("PDF"),
NameToken.Text,
NameToken.ImageB,
NameToken.ImageC,
NameToken.ImageI
};
var resources = new Dictionary<NameToken, IToken>
{
{ NameToken.ProcSet, new ArrayToken(procSet) }
};
var subDict = GetRemoteDict(item.Value);
var destSubDict = destinationDict[NameToken.Create(item.Key)] as DictionaryToken;
if (destSubDict == null || subDict == null)
{
// not a dict.. just overwrite with more important one? should maybe check arrays?
destinationDict[NameToken.Create(item.Key)] = WriterUtil.CopyToken(context, item.Value, document.Structure.TokenScanner, refs);
continue;
}
foreach (var subItem in subDict.Data)
{
// last copied most important important
destinationDict[NameToken.Create(subItem.Key)] = WriterUtil.CopyToken(context, subItem.Value,
document.Structure.TokenScanner, refs);
}
}
}
DictionaryToken GetRemoteDict(IToken token)
{
DictionaryToken dict = null;
if (token is IndirectReferenceToken ir)
{
dict = document.Structure.TokenScanner.Get(ir.Data).Data as DictionaryToken;
}
else if (token is DictionaryToken dt)
{
dict = dt;
}
return dict;
}
}
private void CompleteDocument()
{
var fontsWritten = new Dictionary<Guid, IndirectReferenceToken>();
foreach (var font in fonts)
{
var fontObj = font.Value.FontProgram.WriteFont(context, font.Value.FontKey.Reference);
fontsWritten.Add(font.Key, fontObj);
}
var procSet = new List<NameToken>
{
NameToken.Create("PDF"),
NameToken.Text,
NameToken.ImageB,
NameToken.ImageC,
NameToken.ImageI
};
var resources = new Dictionary<NameToken, IToken>
{
{ NameToken.ProcSet, new ArrayToken(procSet) }
};
// 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();
var pageReferences = new List<IndirectReferenceToken>();
foreach (var page in pages)
{
var pageDictionary = page.Value.additionalPageProperties;
pageDictionary[NameToken.Type] = NameToken.Page;
// 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();
var pageReferences = new List<IndirectReferenceToken>();
foreach (var page in pages)
{
var pageDictionary = page.Value.additionalPageProperties;
pageDictionary[NameToken.Type] = NameToken.Page;
pageDictionary[NameToken.Parent] = parentIndirect;
pageDictionary[NameToken.ProcSet] = new ArrayToken(procSet);
pageDictionary[NameToken.ProcSet] = new ArrayToken(procSet);
if (!pageDictionary.ContainsKey(NameToken.MediaBox))
{
pageDictionary[NameToken.MediaBox] = RectangleToArray(page.Value.PageSize);
}
// combine existing resources (if any) with added
var pageResources = new Dictionary<NameToken, IToken>();
foreach (var existing in page.Value.Resources)
@@ -488,82 +508,82 @@ namespace UglyToad.PdfPig.Writer
}
pageResources[NameToken.Font] = new DictionaryToken(page.Value.fontDictionary);
pageDictionary[NameToken.Resources] = new DictionaryToken(pageResources);
if (page.Value.contentStreams.Count == 1)
{
pageDictionary[NameToken.Contents] = page.Value.contentStreams[0].Write(context);
}
else
{
var streams = new List<IToken>();
foreach (var stream in page.Value.contentStreams)
{
streams.Add(stream.Write(context));
}
pageDictionary[NameToken.Contents] = new ArrayToken(streams);
}
var pageRef = context.WriteToken( new DictionaryToken(pageDictionary));
pageReferences.Add(pageRef);
}
var pagesDictionaryData = new Dictionary<NameToken, IToken>
{
{NameToken.Type, NameToken.Pages},
{NameToken.Kids, new ArrayToken(pageReferences)},
{NameToken.Resources, new DictionaryToken(resources)},
{NameToken.Count, new NumericToken(pageReferences.Count)}
};
var pagesDictionary = new DictionaryToken(pagesDictionaryData);
var pagesRef = context.WriteToken(pagesDictionary, parentIndirect);
var catalogDictionary = new Dictionary<NameToken, IToken>
{
{NameToken.Type, NameToken.Catalog},
{NameToken.Pages, pagesRef}
};
if (ArchiveStandard != PdfAStandard.None)
{
Func<IToken, IndirectReferenceToken> writerFunc = x => context.WriteToken(x);
PdfABaselineRuleBuilder.Obey(catalogDictionary, writerFunc, DocumentInformation, ArchiveStandard);
switch (ArchiveStandard)
{
case PdfAStandard.A1A:
PdfA1ARuleBuilder.Obey(catalogDictionary);
break;
case PdfAStandard.A2B:
break;
case PdfAStandard.A2A:
PdfA1ARuleBuilder.Obey(catalogDictionary);
break;
}
}
var catalog = new DictionaryToken(catalogDictionary);
var catalogRef = context.WriteToken(catalog);
var informationReference = default(IndirectReferenceToken);
if (IncludeDocumentInformation)
{
var informationDictionary = DocumentInformation.ToDictionary();
if (informationDictionary.Count > 0)
{
var dictionary = new DictionaryToken(informationDictionary);
informationReference = context.WriteToken(dictionary);
}
}
context.CompletePdf(catalogRef, informationReference);
pageDictionary[NameToken.Resources] = new DictionaryToken(pageResources);
if (page.Value.contentStreams.Count == 1)
{
pageDictionary[NameToken.Contents] = page.Value.contentStreams[0].Write(context);
}
else
{
var streams = new List<IToken>();
foreach (var stream in page.Value.contentStreams)
{
streams.Add(stream.Write(context));
}
pageDictionary[NameToken.Contents] = new ArrayToken(streams);
}
var pageRef = context.WriteToken( new DictionaryToken(pageDictionary));
pageReferences.Add(pageRef);
}
var pagesDictionaryData = new Dictionary<NameToken, IToken>
{
{NameToken.Type, NameToken.Pages},
{NameToken.Kids, new ArrayToken(pageReferences)},
{NameToken.Resources, new DictionaryToken(resources)},
{NameToken.Count, new NumericToken(pageReferences.Count)}
};
var pagesDictionary = new DictionaryToken(pagesDictionaryData);
var pagesRef = context.WriteToken(pagesDictionary, parentIndirect);
var catalogDictionary = new Dictionary<NameToken, IToken>
{
{NameToken.Type, NameToken.Catalog},
{NameToken.Pages, pagesRef}
};
if (ArchiveStandard != PdfAStandard.None)
{
Func<IToken, IndirectReferenceToken> writerFunc = x => context.WriteToken(x);
PdfABaselineRuleBuilder.Obey(catalogDictionary, writerFunc, DocumentInformation, ArchiveStandard);
switch (ArchiveStandard)
{
case PdfAStandard.A1A:
PdfA1ARuleBuilder.Obey(catalogDictionary);
break;
case PdfAStandard.A2B:
break;
case PdfAStandard.A2A:
PdfA1ARuleBuilder.Obey(catalogDictionary);
break;
}
}
var catalog = new DictionaryToken(catalogDictionary);
var catalogRef = context.WriteToken(catalog);
var informationReference = default(IndirectReferenceToken);
if (IncludeDocumentInformation)
{
var informationDictionary = DocumentInformation.ToDictionary();
if (informationDictionary.Count > 0)
{
var dictionary = new DictionaryToken(informationDictionary);
informationReference = context.WriteToken(dictionary);
}
}
context.CompletePdf(catalogRef, informationReference);
}
/// <summary>
@@ -737,12 +757,12 @@ namespace UglyToad.PdfPig.Writer
return result;
}
}
/// <summary>
/// Disposes underlying stream if set to do so.
/// <summary>
/// Disposes underlying stream if set to do so.
/// </summary>
public void Dispose()
{
{
context.Dispose();
}
}