feat(Edit): created to handle pdf edit operations fluently

This commit is contained in:
tekh 2025-09-24 16:07:51 +02:00
parent 0fa641c15d
commit ed4fd6ce96
3 changed files with 118 additions and 2 deletions

View File

@ -0,0 +1,92 @@
using iText.Kernel.Pdf;
#if NETFRAMEWORK
using System.IO;
using System;
using System.Threading.Tasks;
#endif
namespace EnvelopeGenerator.PdfEditor
{
public class Edit<TInputStream, TOutputStream> : IDisposable, IAsyncDisposable
where TInputStream : Stream
where TOutputStream : Stream
{
private readonly PdfDocument _doc;
private readonly TInputStream _inputStream;
private readonly TOutputStream _outputStream;
private readonly PdfReader _reader;
private readonly PdfWriter _writer;
private bool _disposed = false;
public Edit(TInputStream inputStream, TOutputStream outputStream)
{
_inputStream = inputStream;
_outputStream = outputStream;
_reader = new PdfReader(inputStream);
_writer = new PdfWriter(outputStream);
_doc = new PdfDocument(_reader, _writer);
}
public static Edit<MemoryStream, MemoryStream> OnByte(byte[] documentBytes)
{
return new Edit<MemoryStream, MemoryStream>(new MemoryStream(documentBytes), new MemoryStream());
}
// Finalizer
~Edit()
{
// If Dispose is not called, clean up unmanaged resources
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
// Managed resources
_doc?.Close();
_inputStream?.Dispose();
_outputStream?.Dispose();
}
else
{
// When called by the finalizer, clean up only unmanaged resources
// Unmanaged resources such as PdfDocument, PdfReader, and PdfWriter are already IDisposable; we close them here
try { _doc?.Close(); } catch { }
try { _inputStream?.Dispose(); } catch { }
try { _outputStream?.Dispose(); } catch { }
}
_disposed = true;
}
public async ValueTask DisposeAsync()
{
if (_disposed)
return;
_doc?.Close();
if (_inputStream is IAsyncDisposable asyncInput)
await asyncInput.DisposeAsync();
else
_inputStream.Dispose();
if (_outputStream is IAsyncDisposable asyncOutput)
await asyncOutput.DisposeAsync();
else
_outputStream?.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
}
}

View File

@ -1,5 +1,4 @@
using iText.Kernel.Colors;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Canvas;
#if NETFRAMEWORK
@ -16,6 +15,7 @@ namespace EnvelopeGenerator.PdfEditor
public static class Extensions
{
#region Edit PDF document
public static TStream Edit<TStream>(this TStream inputStream, Action<PdfDocument> edit)
where TStream : Stream, new()
{
@ -36,6 +36,18 @@ namespace EnvelopeGenerator.PdfEditor
return inputStream.Edit(edit).ToArray();
}
}
#endregion
public static byte[] Design(this byte[] pdfBytes, params (int pageIndex, Action<PdfCanvas> design)[] pageDesign)
=> pdfBytes.Edit(doc =>
{
foreach((int pageIndex, Action<PdfCanvas> design) in pageDesign)
{
var page = doc.GetPage(pageIndex);
var canvas = new PdfCanvas(page);
design(canvas);
}
});
}
#if NETFRAMEWORK
}

View File

@ -236,6 +236,18 @@ public class EnvelopeController : ViewControllerBase
canvas.FillStroke();
});
doc.ByteData = doc.ByteData.Edit(doc =>
{
var page = doc.GetFirstPage();
var canvas = new PdfCanvas(page);
canvas.SetStrokeColor(ColorConstants.RED);
canvas.SetFillColor(ColorConstants.CYAN);
canvas.SetFillColorRgb(222, 220, 215);
canvas.SetLineWidth(2);
canvas.Rectangle(100, 500, 200, 100);
canvas.FillStroke();
});
ViewData["DocumentBytes"] = doc.ByteData;
}
else