mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-12-21 11:13:55 +08:00
cleanup stream writing to only write multiple when needed
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
using Integration;
|
||||
using PdfPig.Core;
|
||||
using PdfPig.Fonts.Standard14Fonts;
|
||||
using PdfPig.Tokens;
|
||||
using PdfPig.Writer;
|
||||
using Tests.Fonts.TrueType;
|
||||
using Xunit;
|
||||
@@ -854,6 +855,82 @@
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWriteEmptyContentStream()
|
||||
{
|
||||
using (var builder = new PdfDocumentBuilder())
|
||||
{
|
||||
builder.AddPage(PageSize.A4);
|
||||
var result = builder.Build();
|
||||
using (var document = PdfDocument.Open(result, ParsingOptions.LenientParsingOff))
|
||||
{
|
||||
Assert.Equal(1, document.NumberOfPages);
|
||||
var pg = document.GetPage(1);
|
||||
// single empty page should result in single content stream
|
||||
Assert.NotNull(pg.Dictionary.Data[NameToken.Contents] as IndirectReferenceToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWriteSingleContentStream()
|
||||
{
|
||||
using (var builder = new PdfDocumentBuilder())
|
||||
{
|
||||
var pb = builder.AddPage(PageSize.A4);
|
||||
pb.DrawLine(new PdfPoint(1, 1), new PdfPoint(2, 2));
|
||||
var result = builder.Build();
|
||||
using (var document = PdfDocument.Open(result, ParsingOptions.LenientParsingOff))
|
||||
{
|
||||
Assert.Equal(1, document.NumberOfPages);
|
||||
var pg = document.GetPage(1);
|
||||
// single empty page should result in single content stream
|
||||
Assert.NotNull(pg.Dictionary.Data[NameToken.Contents] as IndirectReferenceToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWriteAndIgnoreEmptyContentStream()
|
||||
{
|
||||
using (var builder = new PdfDocumentBuilder())
|
||||
{
|
||||
var pb = builder.AddPage(PageSize.A4);
|
||||
pb.DrawLine(new PdfPoint(1, 1), new PdfPoint(2, 2));
|
||||
pb.NewContentStreamAfter();
|
||||
var result = builder.Build();
|
||||
using (var document = PdfDocument.Open(result, ParsingOptions.LenientParsingOff))
|
||||
{
|
||||
Assert.Equal(1, document.NumberOfPages);
|
||||
var pg = document.GetPage(1);
|
||||
// empty stream should be ignored and resulting single stream should be written
|
||||
Assert.NotNull(pg.Dictionary.Data[NameToken.Contents] as IndirectReferenceToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanWriteMultipleContentStream()
|
||||
{
|
||||
using (var builder = new PdfDocumentBuilder())
|
||||
{
|
||||
var pb = builder.AddPage(PageSize.A4);
|
||||
pb.DrawLine(new PdfPoint(1, 1), new PdfPoint(2, 2));
|
||||
pb.NewContentStreamAfter();
|
||||
pb.DrawLine(new PdfPoint(1, 1), new PdfPoint(2, 2));
|
||||
var result = builder.Build();
|
||||
using (var document = PdfDocument.Open(result, ParsingOptions.LenientParsingOff))
|
||||
{
|
||||
Assert.Equal(1, document.NumberOfPages);
|
||||
var pg = document.GetPage(1);
|
||||
// multiple streams should be written to array
|
||||
var streams = pg.Dictionary.Data[NameToken.Contents] as ArrayToken;
|
||||
Assert.NotNull(streams);
|
||||
Assert.Equal(2, streams.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[InlineData("Single Page Simple - from google drive.pdf")]
|
||||
[InlineData("Old Gutnish Internet Explorer.pdf")]
|
||||
[InlineData("68-1990-01_A.pdf")]
|
||||
|
||||
@@ -9,43 +9,44 @@
|
||||
internal interface IPdfStreamWriter : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// The underlying stream used by the writer.
|
||||
/// </summary>
|
||||
Stream Stream { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Writes a single token to the stream.
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="token">Token to write.</param>
|
||||
/// <returns>Indirect reference to the token.</returns>
|
||||
IndirectReferenceToken WriteToken(IToken token);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Writes a token to a reserved object number.
|
||||
/// </summary>
|
||||
/// <param name="token"></param>
|
||||
/// <param name="indirectReference"></param>
|
||||
/// <returns></returns>
|
||||
/// <param name="token">Token to write.</param>
|
||||
/// <param name="indirectReference">Reserved indirect reference.</param>
|
||||
/// <returns>Reserved indirect reference.</returns>
|
||||
IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Reserves an object number for an object to be written.
|
||||
/// Useful with cyclic references where object number must be known before
|
||||
/// writing.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <returns>A reserved indirect reference.</returns>
|
||||
IndirectReferenceToken ReserveObjectNumber();
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Initializes the PDF stream with pdf header.
|
||||
/// </summary>
|
||||
/// <param name="version"></param>
|
||||
/// <param name="version">Version of PDF.</param>
|
||||
void InitializePdf(decimal version);
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Completes the PDF writing trailing PDF information.
|
||||
/// </summary>
|
||||
/// <param name="catalogReference"></param>
|
||||
/// <param name="documentInformationReference"></param>
|
||||
/// <param name="catalogReference">Indirect reference of catalog.</param>
|
||||
/// <param name="documentInformationReference">Reference to document information (optional)</param>
|
||||
void CompletePdf(IndirectReferenceToken catalogReference, IndirectReferenceToken documentInformationReference=null);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,7 +492,6 @@ namespace UglyToad.PdfPig.Writer
|
||||
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)
|
||||
@@ -503,18 +502,25 @@ namespace UglyToad.PdfPig.Writer
|
||||
pageResources[NameToken.Font] = new DictionaryToken(page.Value.fontDictionary);
|
||||
pageDictionary[NameToken.Resources] = new DictionaryToken(pageResources);
|
||||
|
||||
if (page.Value.contentStreams.Count == 1)
|
||||
var toWrite = page.Value.contentStreams.Where(x => x.HasContent).ToList();
|
||||
if (toWrite.Count == 0)
|
||||
{
|
||||
pageDictionary[NameToken.Contents] = page.Value.contentStreams[0].Write(context);
|
||||
// write empty
|
||||
pageDictionary[NameToken.Contents] = new PdfPageBuilder.DefaultContentStream().Write(context);
|
||||
}
|
||||
else if (toWrite.Count == 1)
|
||||
{
|
||||
// write single
|
||||
pageDictionary[NameToken.Contents] = toWrite[0].Write(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// write array
|
||||
var streams = new List<IToken>();
|
||||
foreach (var stream in page.Value.contentStreams)
|
||||
foreach (var stream in toWrite)
|
||||
{
|
||||
streams.Add(stream.Write(context));
|
||||
}
|
||||
|
||||
pageDictionary[NameToken.Contents] = new ArrayToken(streams);
|
||||
}
|
||||
|
||||
|
||||
@@ -783,8 +783,10 @@
|
||||
internal interface IPageContentStream : IContentStream
|
||||
{
|
||||
bool ReadOnly { get; }
|
||||
bool HasContent { get; }
|
||||
void Add(IGraphicsStateOperation operation);
|
||||
IndirectReferenceToken Write(IPdfStreamWriter writer);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -812,6 +814,7 @@
|
||||
}
|
||||
|
||||
public bool ReadOnly => false;
|
||||
public bool HasContent => operations.Any();
|
||||
|
||||
public void Add(IGraphicsStateOperation operation)
|
||||
{
|
||||
@@ -842,12 +845,13 @@
|
||||
internal class CopiedContentStream : IPageContentStream
|
||||
{
|
||||
private readonly IndirectReferenceToken token;
|
||||
public bool ReadOnly => true;
|
||||
public bool HasContent => true;
|
||||
|
||||
public CopiedContentStream(IndirectReferenceToken indirectReferenceToken)
|
||||
{
|
||||
token = indirectReferenceToken;
|
||||
}
|
||||
public bool ReadOnly => true;
|
||||
|
||||
|
||||
public IndirectReferenceToken Write(IPdfStreamWriter writer)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user