Refactor PDF handling; remove iText dependency
Replaced iText-based PDF processing with DevExpress PdfGraphics API. Removed `itext` and `itext.bouncy-castle-adapter` dependencies. Simplified `BuildReport` to burn signatures directly into PDFs and render all pages using `XRPdfContent` with `GenerateOwnPages = true`. Consolidated subreport logic into `BuildReport` and removed `BuildPageSubreport`. Eliminated unused constants and methods, including `GetPdfPageCount`. Updated XML documentation to reflect the new implementation. Placeholder implementation for `BurnSignaturesIntoPdf` added, pending further development.
This commit is contained in:
@@ -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,60 +467,33 @@
|
||||
|
||||
// ----- Report builder -----
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
static XtraReport BuildReport(
|
||||
byte[] pdfBytes,
|
||||
IReadOnlyList<SignatureDto> 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++)
|
||||
try
|
||||
{
|
||||
// Build a subreport for this PDF page
|
||||
var pageReport = BuildPageSubreport(pdfBytes, page, signatures, capturedSignature);
|
||||
|
||||
var subreport = new XRSubreport
|
||||
sourcePdf = BurnSignaturesIntoPdf(pdfBytes, signatures, capturedSignature);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ReportSource = pageReport,
|
||||
GenerateOwnPages = true,
|
||||
LocationF = new PointF(0f, 0f),
|
||||
SizeF = new SizeF(PageWidthDx, PageHeightDx),
|
||||
};
|
||||
|
||||
mainDetail.Controls.Add(subreport);
|
||||
// Fall back to unmodified PDF — non-critical
|
||||
sourcePdf = pdfBytes;
|
||||
}
|
||||
}
|
||||
|
||||
return mainReport;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
static XtraReport BuildPageSubreport(
|
||||
byte[] pdfBytes,
|
||||
int pageNumber,
|
||||
IReadOnlyList<SignatureDto> signatures,
|
||||
SignatureCaptureDto? capturedSignature)
|
||||
{
|
||||
var report = new XtraReport
|
||||
{
|
||||
PaperKind = DevExpress.Drawing.Printing.DXPaperKind.A4,
|
||||
@@ -539,74 +501,34 @@
|
||||
Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0),
|
||||
};
|
||||
|
||||
var detail = new DetailBand
|
||||
{
|
||||
HeightF = PageHeightDx,
|
||||
Name = $"DetailBand_Page{pageNumber}",
|
||||
};
|
||||
var detail = new DetailBand { HeightF = 0f };
|
||||
report.Bands.Add(detail);
|
||||
|
||||
// --- PDF content (embedded, no own pages) ---
|
||||
// GenerateOwnPages = true (default): each PDF page becomes a separate report page
|
||||
var pdfContent = new XRPdfContent
|
||||
{
|
||||
Source = pdfBytes,
|
||||
PageRange = pageNumber.ToString(),
|
||||
GenerateOwnPages = false,
|
||||
LocationF = new PointF(0f, 0f),
|
||||
SizeF = new SizeF(PageWidthDx, PageHeightDx),
|
||||
Source = sourcePdf,
|
||||
GenerateOwnPages = true,
|
||||
};
|
||||
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)
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
/// <summary>Reads the page count of a PDF using iText7 (already referenced in the server project).</summary>
|
||||
static int GetPdfPageCount(byte[] pdfBytes)
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
static byte[] BurnSignaturesIntoPdf(
|
||||
byte[] pdfBytes,
|
||||
IReadOnlyList<SignatureDto> 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;
|
||||
}
|
||||
|
||||
/// <summary>Converts a base64 data URL (data:image/...;base64,...) to raw bytes.</summary>
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
<PackageReference Include="DigitalData.Core.API" Version="2.2.1" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="9.0.892" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="8.0.11" />
|
||||
<PackageReference Include="itext" Version="8.0.5" />
|
||||
<PackageReference Include="itext.bouncy-castle-adapter" Version="8.0.5" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.17" />
|
||||
<PackageReference Include="Microsoft.Identity.Client" Version="4.82.1" />
|
||||
|
||||
Reference in New Issue
Block a user