Affected Versionitext7 (9.1.0) and itext7.bouncy-castle-adapter (9.1.0) via NuGet on Windows 11.
Actual BehaviorAt present, PdfWriter takes in a Stream for output and it closes that stream when finished. If the provided stream is a FileStream and the user wants the stream to be closed when PdfWriter is finished then there's no problem.
Why It's BadIf the provided stream is a MemoryStream or something else instead of a FileStream and if the user wants the stream to stay open (such as if providing the contents of the PDF to the browser in a Blazor or other web application or if some additional encryption or compression needs to be applied to the stream before closing) then closing the stream in that case is very bad. When a MemoryStream is closed, it contents are just thrown away by the garbage collector so that they can no longer be used by the program. This makes PdfWriter incompatible with (directly) using MemoryStream for output: a common and very desirable use case because we don't want to engage our storage device whenever doing the operation in RAM instead would suffice.
Expected BehaviorPdfWriter should have a boolean leaveOpen parameter with a default value of false in its constructor and then only close the stream in the case that leaveOpen is false. This is common practice for handling streams in C#, for example in
BinaryWriter. It would fix the issue while not breaking any existing user code due to leaveOpen's default value reflecting the current behavior.
That way, when using PdfWriter, my code would look like this:
Code:
using PdfWriter writer = new(os: outputStream, leaveOpen: true);
If I wanted the stream closed then I could just not specify leaveOpen.
Another option might be to include leaveOpen in the WriterProperties class instead, but this would not be following the common practice for stream handling in C# so users might have trouble finding it in there. I certainly don't see anything about leaving the stream open when looking at WriterProperties right now.
PdfReader may also benefit from a similar change if it is closing its input stream although I haven't personally tested to find out whether it has such an issue.
A WorkaroundThis is what I'm actually doing to work around this issue at present. I have an entire class which exists only to take the ability to close the stream away from PdfWriter by ignoring its command to close the stream:
Code:
public class NonClosingStreamWrapper(Stream innerStream) : Stream
{
public override void Close() { }
protected override void Dispose(bool disposing) { }
public override bool CanRead => innerStream.CanRead;
public override bool CanSeek => innerStream.CanSeek;
public override bool CanWrite => innerStream.CanWrite;
public override long Length => innerStream.Length;
public override long Position
{
get => innerStream.Position;
set => innerStream.Position = value;
}
public override void Flush() => innerStream.Flush();
public override int Read(byte[] buffer, int offset, int count) => innerStream.Read(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => innerStream.Seek(offset, origin);
public override void SetLength(long value) => innerStream.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => innerStream.Write(buffer, offset, count);
}
I then use it like this:
Code:
using PdfWriter writer = new(new NonClosingStreamWrapper(outputStream));
This works, but I shouldn't have to do this because PdfWriter should handle its stream properly with a boolean leaveOpen parameter as is common practice in C#.
I believe this issue should be trivial to fix but please let me know if there's any way I can help. Thanks for considering this issue.