diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverReportPage.razor b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverReportPage.razor index 06b44f44..8a0a2601 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverReportPage.razor +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/Components/Pages/EnvelopeReceiverReportPage.razor @@ -2,8 +2,6 @@ @rendermode InteractiveServer @using DevExpress.Blazor.Reporting @using DevExpress.XtraReports.UI -@using DevExpress.XtraPrinting -@using DevExpress.XtraPrinting.Drawing @using EnvelopeGenerator.Server.Client.Models @using EnvelopeGenerator.Server.Client.Models.Constants @using EnvelopeGenerator.Server.Client.Services @@ -352,15 +350,6 @@ const string ImageInputId = "rp-signature-image-input"; const string ImageCanvasId = "rp-image-signature-pad"; - // A4 page dimensions in DX units (1/100 inch). - // 8.27" × 11.69" → 827 × 1169 - const float PageWidthDx = 827f; - const float PageHeightDx = 1169f; - - // Fixed signature field size in DX units: 1.77" × 1.96" - const float SigWidthDx = 177f; - const float SigHeightDx = 196f; - readonly (string Text, string Value)[] TypedSignatureFonts = [ ("Brush Script", "'Brush Script MT', cursive"), @@ -478,135 +467,68 @@ // ----- Report builder ----- /// - /// Builds an XtraReport that displays the PDF pages via XRPdfContent (embedded mode, - /// GenerateOwnPages = false). Each PDF page is wrapped in its own subreport so that - /// XRPictureBox overlays can be positioned accurately per page. + /// Builds an XtraReport wrapping the PDF bytes. + /// If a signature is captured and there are signature fields, the signature image is + /// first burned into the PDF via DevExpress PdfDocumentProcessor, then the modified + /// PDF is handed to XRPdfContent with GenerateOwnPages = true so that all pages appear. /// static XtraReport BuildReport( byte[] pdfBytes, IReadOnlyList signatures, SignatureCaptureDto? capturedSignature) { - // Determine the number of pages using DevExpress PDF processor - int pageCount = GetPdfPageCount(pdfBytes); - if (pageCount < 1) pageCount = 1; - - // Outer (main) report - acts as container for subreports - var mainReport = new XtraReport + // Burn signatures into PDF bytes when a captured signature is available + byte[] sourcePdf = pdfBytes; + if (capturedSignature is not null + && !string.IsNullOrWhiteSpace(capturedSignature.DataUrl) + && signatures.Count > 0) { - PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4, - Landscape = false, - Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0), - }; - - var mainDetail = new DetailBand { HeightF = 0f }; - mainReport.Bands.Add(mainDetail); - - for (int page = 1; page <= pageCount; page++) - { - // Build a subreport for this PDF page - var pageReport = BuildPageSubreport(pdfBytes, page, signatures, capturedSignature); - - var subreport = new XRSubreport + try { - ReportSource = pageReport, - GenerateOwnPages = true, - LocationF = new PointF(0f, 0f), - SizeF = new SizeF(PageWidthDx, PageHeightDx), - }; - - mainDetail.Controls.Add(subreport); - } - - return mainReport; - } - - /// - /// Builds a single-page subreport: one DetailBand containing the PDF page (via - /// XRPdfContent with GenerateOwnPages = false) plus XRPictureBox overlays for - /// any signatures placed on this page. - /// - static XtraReport BuildPageSubreport( - byte[] pdfBytes, - int pageNumber, - IReadOnlyList signatures, - SignatureCaptureDto? capturedSignature) - { - var report = new XtraReport - { - PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4, - Landscape = false, - Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0), - }; - - var detail = new DetailBand - { - HeightF = PageHeightDx, - Name = $"DetailBand_Page{pageNumber}", - }; - report.Bands.Add(detail); - - // --- PDF content (embedded, no own pages) --- - var pdfContent = new XRPdfContent - { - Source = pdfBytes, - PageRange = pageNumber.ToString(), - GenerateOwnPages = false, - LocationF = new PointF(0f, 0f), - SizeF = new SizeF(PageWidthDx, PageHeightDx), - }; - detail.Controls.Add(pdfContent); - - // --- Signature overlays --- - if (capturedSignature is not null && !string.IsNullOrWhiteSpace(capturedSignature.DataUrl)) - { - var signaturesOnPage = signatures.Where(s => s.Page == pageNumber).ToList(); - foreach (var sig in signaturesOnPage) + sourcePdf = BurnSignaturesIntoPdf(pdfBytes, signatures, capturedSignature); + } + catch { - try - { - var imgBytes = DataUrlToBytes(capturedSignature.DataUrl); - if (imgBytes is { Length: > 0 }) - { - using var imgStream = new System.IO.MemoryStream(imgBytes); - var img = System.Drawing.Image.FromStream(imgStream); - var picBox = new XRPictureBox - { - // DB stores INCHES; DX unit = 1/100 inch → multiply by 100 - LocationF = new PointF((float)(sig.X * 100), (float)(sig.Y * 100)), - SizeF = new SizeF(SigWidthDx, SigHeightDx), - Image = img, - Sizing = ImageSizeMode.Squeeze, - CanGrow = false, - CanShrink = false, - }; - detail.Controls.Add(picBox); - } - } - catch - { - // Non-critical: skip overlay on error - } + // Fall back to unmodified PDF — non-critical + sourcePdf = pdfBytes; } } + var report = new XtraReport + { + PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4, + Landscape = false, + Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0), + }; + + var detail = new DetailBand { HeightF = 0f }; + report.Bands.Add(detail); + + // GenerateOwnPages = true (default): each PDF page becomes a separate report page + var pdfContent = new XRPdfContent + { + Source = sourcePdf, + GenerateOwnPages = true, + }; + detail.Controls.Add(pdfContent); + return report; } - /// Reads the page count of a PDF using iText7 (already referenced in the server project). - static int GetPdfPageCount(byte[] pdfBytes) + /// + /// Burns signature images directly into the PDF using DevExpress PdfGraphics API. + /// Coordinates: DB stores INCHES with top-left origin, Y down. + /// PDF coordinate system: bottom-left origin, Y up, unit = points (1/72 inch). + /// Note: Implementation placeholder — requires DevExpress.Pdf.Drawing API wiring (Problem 2). + /// + static byte[] BurnSignaturesIntoPdf( + byte[] pdfBytes, + IReadOnlyList signatures, + SignatureCaptureDto capturedSignature) { - try - { - using var ms = new System.IO.MemoryStream(pdfBytes); - using var reader = new iText.Kernel.Pdf.PdfReader(ms); - using var pdfDoc = new iText.Kernel.Pdf.PdfDocument(reader); - return pdfDoc.GetNumberOfPages(); - } - catch - { - return 1; - } + // TODO: Implement with PdfGraphics when Problem 2 is addressed. + // For now return unmodified PDF so Problem 1 (all pages) can be verified first. + return pdfBytes; } /// Converts a base64 data URL (data:image/...;base64,...) to raw bytes. diff --git a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj index c27b4247..1d74f2b3 100644 --- a/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj +++ b/EnvelopeGenerator.Server/EnvelopeGenerator.Server/EnvelopeGenerator.Server.csproj @@ -28,8 +28,6 @@ - -