Skip to content

Commit 43a461f

Browse files
authored
Merge pull request #52 from synercoder/features/various-small-improvements
Features/various small improvements
2 parents b83be94 + f32b966 commit 43a461f

File tree

2 files changed

+91
-41
lines changed

2 files changed

+91
-41
lines changed

src/Synercoding.FileFormats.Pdf/LowLevel/PageTree.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using Synercoding.FileFormats.Pdf.LowLevel.Extensions;
2-
using Synercoding.FileFormats.Pdf.LowLevel.Text;
32
using Synercoding.FileFormats.Pdf.LowLevel.XRef;
43
using System;
54
using System.Collections.Generic;
@@ -25,6 +24,9 @@ public PageTree(PdfReference id, TableBuilder tableBuilder)
2524
public void AddPage(PdfPage pdfObject)
2625
=> _pages.Add(pdfObject);
2726

27+
public int PageCount
28+
=> _pages.Count;
29+
2830
internal uint WriteToStream(PdfStream stream)
2931
{
3032
if (_isWritten)

src/Synercoding.FileFormats.Pdf/PdfWriter.cs

Lines changed: 88 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.IO;
66
using System.Reflection;
7+
using System.Threading.Tasks;
78

89
namespace Synercoding.FileFormats.Pdf
910
{
@@ -19,6 +20,8 @@ public sealed class PdfWriter : IDisposable
1920
private readonly PageTree _pageTree;
2021
private readonly Catalog _catalog;
2122

23+
private bool _endingWritten = false;
24+
2225
/// <summary>
2326
/// Constructor for <see cref="PdfWriter"/>
2427
/// </summary>
@@ -54,13 +57,21 @@ public PdfWriter(Stream stream, bool ownsStream)
5457
/// </summary>
5558
public DocumentInformation DocumentInformation { get; }
5659

60+
/// <summary>
61+
/// Returns the number of pages already added to the writer
62+
/// </summary>
63+
public int PageCount
64+
=> _pageTree.PageCount;
65+
5766
/// <summary>
5867
/// Set meta information for this document
5968
/// </summary>
6069
/// <param name="infoAction">Action used to set meta data</param>
6170
/// <returns>Returns this <see cref="PdfWriter"/> to chain calls</returns>
6271
public PdfWriter SetDocumentInfo(Action<DocumentInformation> infoAction)
6372
{
73+
_throwWhenEndingWritten();
74+
6475
infoAction(DocumentInformation);
6576

6677
return this;
@@ -82,6 +93,8 @@ public PdfWriter AddPage(Action<PdfPage> pageAction)
8293
/// <returns>Returns this <see cref="PdfWriter"/> to chain calls</returns>
8394
public PdfWriter AddPage<T>(T data, Action<T, PdfPage> pageAction)
8495
{
96+
_throwWhenEndingWritten();
97+
8598
using (var page = new PdfPage(_tableBuilder, _pageTree))
8699
{
87100
pageAction(data, page);
@@ -92,13 +105,43 @@ public PdfWriter AddPage<T>(T data, Action<T, PdfPage> pageAction)
92105
return this;
93106
}
94107

108+
/// <summary>
109+
/// Add a page to the pdf file
110+
/// </summary>
111+
/// <param name="pageAction">Action used to setup the page</param>
112+
/// <returns>Returns an awaitable task that resolves into this <see cref="PdfWriter"/> to chain calls</returns>
113+
public async Task<PdfWriter> AddPageAsync(Func<PdfPage, Task> pageAction)
114+
=> await AddPageAsync(pageAction, static async (action, page) => await action(page));
115+
116+
/// <summary>
117+
/// Add a page to the pdf file
118+
/// </summary>
119+
/// <param name="data">Data passed into the action</param>
120+
/// <param name="pageAction">Action used to setup the page</param>
121+
/// <returns>Returns an awaitable task that resolves into this <see cref="PdfWriter"/> to chain calls</returns>
122+
public async Task<PdfWriter> AddPageAsync<T>(T data, Func<T, PdfPage, Task> pageAction)
123+
{
124+
_throwWhenEndingWritten();
125+
126+
using (var page = new PdfPage(_tableBuilder, _pageTree))
127+
{
128+
await pageAction(data, page);
129+
130+
page.WriteToStream(_stream);
131+
}
132+
133+
return this;
134+
}
135+
95136
/// <summary>
96137
/// Add an <see cref="SixLabors.ImageSharp.Image"/> to the pdf file and get the <see cref="Image"/> reference returned
97138
/// </summary>
98139
/// <param name="image">The image that needs to be added.</param>
99140
/// <returns>The image reference that can be used in pages</returns>
100141
public Image AddImage(SixLabors.ImageSharp.Image image)
101142
{
143+
_throwWhenEndingWritten();
144+
102145
var id = _tableBuilder.ReserveId();
103146

104147
var pdfImage = new Image(id, image);
@@ -123,6 +166,8 @@ public Image AddImage(SixLabors.ImageSharp.Image image)
123166
/// <returns>The image reference that can be used in pages</returns>
124167
public Image AddJpgImageUnsafe(Stream jpgStream, int originalWidth, int originalHeight)
125168
{
169+
_throwWhenEndingWritten();
170+
126171
var id = _tableBuilder.ReserveId();
127172

128173
var pdfImage = new Image(id, jpgStream, originalWidth, originalHeight);
@@ -135,9 +180,17 @@ public Image AddJpgImageUnsafe(Stream jpgStream, int originalWidth, int original
135180
return pdfImage;
136181
}
137182

138-
/// <inheritdoc />
139-
public void Dispose()
183+
/// <summary>
184+
/// Write the PDF trailer; indicates that the PDF is done.
185+
/// </summary>
186+
/// <remarks>
187+
/// Other calls to this <see cref="PdfWriter"/> will throw or have no effect after call this.
188+
/// </remarks>
189+
public void WriteTrailer()
140190
{
191+
if (_endingWritten)
192+
return;
193+
141194
_writePageTree();
142195

143196
_writeCatalog();
@@ -148,12 +201,27 @@ public void Dispose()
148201

149202
_stream.Flush();
150203

204+
_endingWritten = true;
205+
}
206+
207+
/// <inheritdoc />
208+
public void Dispose()
209+
{
210+
WriteTrailer();
211+
212+
_stream.Flush();
213+
151214
if (_ownsStream)
152215
{
153216
_stream.Dispose();
154217
}
155218
}
156219

220+
private void _throwWhenEndingWritten()
221+
{
222+
if (_endingWritten) throw new InvalidOperationException("Can't change document information when PDF trailer is written to the stream.");
223+
}
224+
157225
private static void _writeHeader(PdfStream stream)
158226
{
159227
stream.WriteByte(0x25); // %
@@ -204,47 +272,27 @@ private void _writePdfEnding()
204272
var xRefTable = _tableBuilder.GetXRefTable();
205273
uint xRefPosition = xRefTable.WriteToStream(_stream);
206274

207-
var trailer = new Trailer(xRefPosition, xRefTable.Section.ObjectCount, _catalog, DocumentInformation);
208-
trailer.WriteToStream(_stream);
275+
_writeTrailer(_stream, xRefPosition, xRefTable.Section.ObjectCount, _catalog.Reference, DocumentInformation.Reference);
209276
}
210277

211-
private readonly struct Trailer
278+
private void _writeTrailer(PdfStream stream, uint startXRef, int size, PdfReference root, PdfReference documentInfo)
212279
{
213-
public Trailer(uint startXRef, int size, Catalog root, DocumentInformation documentInfo)
214-
{
215-
StartXRef = startXRef;
216-
Size = size;
217-
Root = root.Reference;
218-
DocumentInfo = documentInfo.Reference;
219-
}
220-
221-
public uint StartXRef { get; }
222-
public int Size { get; }
223-
public PdfReference Root { get; }
224-
public PdfReference DocumentInfo { get; }
225-
226-
internal uint WriteToStream(PdfStream stream)
227-
{
228-
var position = (uint)stream.Position;
229-
230-
stream
231-
.Write("trailer")
232-
.NewLine()
233-
.Dictionary(this, static (trailer, dictionary) =>
234-
{
235-
dictionary
236-
.Write(PdfName.Get("Size"), trailer.Size)
237-
.Write(PdfName.Get("Root"), trailer.Root)
238-
.Write(PdfName.Get("Info"), trailer.DocumentInfo);
239-
})
240-
.Write("startxref")
241-
.NewLine()
242-
.Write(StartXRef)
243-
.NewLine()
244-
.Write("%%EOF");
245-
246-
return position;
247-
}
280+
stream
281+
.Write("trailer")
282+
.NewLine()
283+
.Dictionary((size, root, documentInfo), static (triple, dictionary) =>
284+
{
285+
var (size, root, documentInfo) = triple;
286+
dictionary
287+
.Write(PdfName.Get("Size"), size)
288+
.Write(PdfName.Get("Root"), root)
289+
.Write(PdfName.Get("Info"), documentInfo);
290+
})
291+
.Write("startxref")
292+
.NewLine()
293+
.Write(startXRef)
294+
.NewLine()
295+
.Write("%%EOF");
248296
}
249297
}
250298
}

0 commit comments

Comments
 (0)