using iText.Kernel.Pdf; using iText.Kernel.Pdf.Canvas; using EnvelopeGenerator.Domain.Interfaces; using iText.Kernel.Colors; using iText.Kernel.Geom; #if NETFRAMEWORK using System; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Collections.Generic; #endif namespace EnvelopeGenerator.PdfEditor { public static class Pdf { public static Pdf FromMemory(byte[] documentBytes) { return new Pdf(new MemoryStream(documentBytes), new MemoryStream()); } } public class Pdf : 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; public Pdf(TInputStream inputStream, TOutputStream outputStream) { _inputStream = inputStream; _outputStream = outputStream; _reader = new PdfReader(inputStream); _writer = new PdfWriter(outputStream); _doc = new PdfDocument(_reader, _writer); } /// /// Gets the output stream containing the edited PDF document. /// /// /// Accessing this property will close the underlying to ensure /// all changes are flushed to the stream. After accessing this property, the PDF document /// can no longer be modified using this instance. /// /// /// The instance that contains the updated PDF bytes. /// public TOutputStream ExportStream() { _doc.Close(); return _outputStream; } #region Edit methods public Pdf Document(Action edit) { edit(_doc); return this; } public Pdf Page(int pageIndex, Action design) => Document(doc => { var page = doc.GetPage(pageIndex); design(doc.GetPage(pageIndex)); }); public Pdf Page(Action design) { for (int i = 1; i <= _doc.GetNumberOfPages(); i++) Page(i, design); return this; } public Pdf Design(int pageIndex, Action design) => Document(doc => { var page = doc.GetPage(pageIndex); var canvas = new PdfCanvas(page); design(canvas); }); #endregion public Pdf Background(IEnumerable signatures) where TSignature : ISignature { // once per page Page(page => { var canvas = new PdfCanvas(page); canvas.ConcatMatrix(1, 0, 0, -1, 0, page.GetPageSize().GetHeight()); }); foreach (var signature in signatures) Page(signature.Page, page => { var canvas = new PdfCanvas(page); double inchFactor = 72; double magin = .2; double x = (signature.X - .7 - magin) * inchFactor; double y = (signature.Y - .5 - magin) * inchFactor; double width = 1.9500000000000002 * inchFactor; double height = 2.52 * inchFactor; double bottomLineLength = 2.5; // draw background canvas.SetFillColor(new DeviceRgb(222, 220, 215)) .Rectangle(x, y, width, height) .Fill(); // draw bottom line canvas.SetFillColor(new DeviceRgb(204, 202, 198)) .Rectangle(x, y + height - bottomLineLength, width, bottomLineLength) .Fill(); }); return this; } #region Finalizer private bool _disposed = false; ~Pdf() { // 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); } #endregion } }