4
4
using System ;
5
5
using System . IO ;
6
6
using System . Reflection ;
7
+ using System . Threading . Tasks ;
7
8
8
9
namespace Synercoding . FileFormats . Pdf
9
10
{
@@ -19,6 +20,8 @@ public sealed class PdfWriter : IDisposable
19
20
private readonly PageTree _pageTree ;
20
21
private readonly Catalog _catalog ;
21
22
23
+ private bool _endingWritten = false ;
24
+
22
25
/// <summary>
23
26
/// Constructor for <see cref="PdfWriter"/>
24
27
/// </summary>
@@ -54,13 +57,21 @@ public PdfWriter(Stream stream, bool ownsStream)
54
57
/// </summary>
55
58
public DocumentInformation DocumentInformation { get ; }
56
59
60
+ /// <summary>
61
+ /// Returns the number of pages already added to the writer
62
+ /// </summary>
63
+ public int PageCount
64
+ => _pageTree . PageCount ;
65
+
57
66
/// <summary>
58
67
/// Set meta information for this document
59
68
/// </summary>
60
69
/// <param name="infoAction">Action used to set meta data</param>
61
70
/// <returns>Returns this <see cref="PdfWriter"/> to chain calls</returns>
62
71
public PdfWriter SetDocumentInfo ( Action < DocumentInformation > infoAction )
63
72
{
73
+ _throwWhenEndingWritten ( ) ;
74
+
64
75
infoAction ( DocumentInformation ) ;
65
76
66
77
return this ;
@@ -82,6 +93,8 @@ public PdfWriter AddPage(Action<PdfPage> pageAction)
82
93
/// <returns>Returns this <see cref="PdfWriter"/> to chain calls</returns>
83
94
public PdfWriter AddPage < T > ( T data , Action < T , PdfPage > pageAction )
84
95
{
96
+ _throwWhenEndingWritten ( ) ;
97
+
85
98
using ( var page = new PdfPage ( _tableBuilder , _pageTree ) )
86
99
{
87
100
pageAction ( data , page ) ;
@@ -92,13 +105,43 @@ public PdfWriter AddPage<T>(T data, Action<T, PdfPage> pageAction)
92
105
return this ;
93
106
}
94
107
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
+
95
136
/// <summary>
96
137
/// Add an <see cref="SixLabors.ImageSharp.Image"/> to the pdf file and get the <see cref="Image"/> reference returned
97
138
/// </summary>
98
139
/// <param name="image">The image that needs to be added.</param>
99
140
/// <returns>The image reference that can be used in pages</returns>
100
141
public Image AddImage ( SixLabors . ImageSharp . Image image )
101
142
{
143
+ _throwWhenEndingWritten ( ) ;
144
+
102
145
var id = _tableBuilder . ReserveId ( ) ;
103
146
104
147
var pdfImage = new Image ( id , image ) ;
@@ -123,6 +166,8 @@ public Image AddImage(SixLabors.ImageSharp.Image image)
123
166
/// <returns>The image reference that can be used in pages</returns>
124
167
public Image AddJpgImageUnsafe ( Stream jpgStream , int originalWidth , int originalHeight )
125
168
{
169
+ _throwWhenEndingWritten ( ) ;
170
+
126
171
var id = _tableBuilder . ReserveId ( ) ;
127
172
128
173
var pdfImage = new Image ( id , jpgStream , originalWidth , originalHeight ) ;
@@ -135,9 +180,17 @@ public Image AddJpgImageUnsafe(Stream jpgStream, int originalWidth, int original
135
180
return pdfImage ;
136
181
}
137
182
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 ( )
140
190
{
191
+ if ( _endingWritten )
192
+ return ;
193
+
141
194
_writePageTree ( ) ;
142
195
143
196
_writeCatalog ( ) ;
@@ -148,12 +201,27 @@ public void Dispose()
148
201
149
202
_stream . Flush ( ) ;
150
203
204
+ _endingWritten = true ;
205
+ }
206
+
207
+ /// <inheritdoc />
208
+ public void Dispose ( )
209
+ {
210
+ WriteTrailer ( ) ;
211
+
212
+ _stream . Flush ( ) ;
213
+
151
214
if ( _ownsStream )
152
215
{
153
216
_stream . Dispose ( ) ;
154
217
}
155
218
}
156
219
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
+
157
225
private static void _writeHeader ( PdfStream stream )
158
226
{
159
227
stream . WriteByte ( 0x25 ) ; // %
@@ -204,47 +272,27 @@ private void _writePdfEnding()
204
272
var xRefTable = _tableBuilder . GetXRefTable ( ) ;
205
273
uint xRefPosition = xRefTable . WriteToStream ( _stream ) ;
206
274
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 ) ;
209
276
}
210
277
211
- private readonly struct Trailer
278
+ private void _writeTrailer ( PdfStream stream , uint startXRef , int size , PdfReference root , PdfReference documentInfo )
212
279
{
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" ) ;
248
296
}
249
297
}
250
298
}
0 commit comments