Introduce functionality to add a stamp to the top-right corner of the first page of a PDF. The stamp includes a white background, a green border, and two lines of text: "✔ VERARBEITET" in bold green font and the current date/time in gray font. The stamp is drawn using `DevExpress.Pdf.PdfDocumentProcessor` and saved to the document's foreground.
158 lines
6.0 KiB
C#
158 lines
6.0 KiB
C#
using DevExpress.Pdf;
|
|
using DXApp.TemplateKitProject.Models;
|
|
|
|
namespace DXApp.TemplateKitProject.Services;
|
|
|
|
public class PdfResultPackageService(
|
|
IConfiguration configuration,
|
|
ILogger<PdfResultPackageService> logger)
|
|
{
|
|
public async Task<string?> CreateResultPackageAsync(
|
|
byte[] originalPdfBytes,
|
|
string originalFileName,
|
|
ZugferdInvoice invoice)
|
|
{
|
|
// 1. Bericht-PDF suchen
|
|
var reportPath = FindReportFile(originalFileName);
|
|
if (reportPath is null)
|
|
{
|
|
logger.LogWarning(
|
|
"Kein Ergebnisbericht gefunden für '{FileName}'.", originalFileName);
|
|
return null;
|
|
}
|
|
|
|
logger.LogDebug(
|
|
"Ergebnisbericht gefunden: '{ReportPath}'.", reportPath);
|
|
|
|
// 2. Ausgabepfad bestimmen
|
|
var outputDir = configuration["PdfResults:OutputDirectory"]
|
|
?? Path.Combine(Path.GetTempPath(), "PdfResults");
|
|
Directory.CreateDirectory(outputDir);
|
|
|
|
var baseName = Path.GetFileNameWithoutExtension(originalFileName);
|
|
var outputPath = Path.Combine(outputDir, $"{baseName}_result.pdf");
|
|
|
|
// 3. Original auf PDF/A-3b hochstufen + Bericht anhängen
|
|
await Task.Run(() =>
|
|
{
|
|
using var inputStream = new MemoryStream(originalPdfBytes);
|
|
|
|
// Schritt 1: PDF/A-3b Konvertierung
|
|
PdfDocumentConverter converter;
|
|
try
|
|
{
|
|
converter = new PdfDocumentConverter(inputStream);
|
|
converter.Convert(PdfCompatibility.PdfA3b);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException(
|
|
"Konvertierung nach PDF/A-3b fehlgeschlagen. " +
|
|
"Die Originaldatei ist möglicherweise beschädigt oder nicht konvertierbar.", ex);
|
|
}
|
|
|
|
// Schritt 2: Konvertiertes PDF puffern
|
|
using var convertedStream = new MemoryStream();
|
|
converter.SaveDocument(convertedStream);
|
|
convertedStream.Position = 0;
|
|
|
|
// Schritt 3: Anhang einbetten
|
|
using var processor = new PdfDocumentProcessor();
|
|
processor.LoadDocument(convertedStream);
|
|
|
|
processor.AttachFile(new PdfFileAttachment
|
|
{
|
|
FileName = Path.GetFileName(reportPath),
|
|
Description = "Ergebnisbericht",
|
|
MimeType = "application/pdf",
|
|
Relationship = PdfAssociatedFileRelationship.Supplement,
|
|
CreationDate = DateTime.Now,
|
|
Data = File.ReadAllBytes(reportPath)
|
|
});
|
|
|
|
// Schritt 4: Stempel auf Seite 1 zeichnen
|
|
var firstPage = processor.Document.Pages[0];
|
|
using (var graphics = processor.CreateGraphicsWorldSystem())
|
|
{
|
|
// Stempel-Position: oben rechts
|
|
// Welt-Koordinaten: Ursprung oben links, X nach rechts, Y nach unten
|
|
var stampX = (float)firstPage.CropBox.Width - 200;
|
|
var stampY = 20f;
|
|
var stampWidth = 175f;
|
|
var stampHeight = 50f;
|
|
|
|
// Hintergrund weiß
|
|
using var whiteBrush = new DevExpress.Drawing.DXSolidBrush(
|
|
System.Drawing.Color.White);
|
|
graphics.FillRectangle(whiteBrush,
|
|
new System.Drawing.RectangleF(stampX, stampY, stampWidth, stampHeight));
|
|
|
|
// Rahmen grün
|
|
using var greenPen = new DevExpress.Drawing.DXPen(
|
|
System.Drawing.Color.Green, 1.5f);
|
|
graphics.DrawRectangle(greenPen,
|
|
new System.Drawing.RectangleF(stampX, stampY, stampWidth, stampHeight));
|
|
|
|
// Text Zeile 1: VERARBEITET
|
|
using var greenBrush = new DevExpress.Drawing.DXSolidBrush(
|
|
System.Drawing.Color.Green);
|
|
var fontBold = new DevExpress.Drawing.DXFont(
|
|
"Arial", 11, DevExpress.Drawing.DXFontStyle.Bold);
|
|
graphics.DrawString(
|
|
"✔ VERARBEITET",
|
|
fontBold,
|
|
greenBrush,
|
|
new System.Drawing.PointF(stampX + 8, stampY + 6));
|
|
|
|
// Text Zeile 2: Datum/Uhrzeit
|
|
using var grayBrush = new DevExpress.Drawing.DXSolidBrush(
|
|
System.Drawing.Color.DimGray);
|
|
var fontNormal = new DevExpress.Drawing.DXFont("Arial", 9);
|
|
graphics.DrawString(
|
|
DateTime.Now.ToString("dd.MM.yyyy HH:mm"),
|
|
fontNormal,
|
|
grayBrush,
|
|
new System.Drawing.PointF(stampX + 8, stampY + 28));
|
|
|
|
graphics.AddToPageForeground(
|
|
processor.Document.Pages[0], 96, 96);
|
|
}
|
|
|
|
// Schritt 4: Speichern
|
|
try
|
|
{
|
|
processor.SaveDocument(outputPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new InvalidOperationException(
|
|
$"Result-PDF konnte nicht gespeichert werden unter '{outputPath}'. " +
|
|
"Prüfe ob das Verzeichnis existiert und beschreibbar ist.", ex);
|
|
}
|
|
});
|
|
|
|
logger.LogInformation(
|
|
"Result-PDF gespeichert: '{OutputPath}'.", outputPath);
|
|
|
|
return outputPath;
|
|
}
|
|
|
|
private string? FindReportFile(string originalFileName)
|
|
{
|
|
var inputDir = configuration["PdfResultReports:InputDirectory"]
|
|
?? Path.Combine(Path.GetTempPath(), "PdfResultReports");
|
|
|
|
if (!Directory.Exists(inputDir))
|
|
{
|
|
logger.LogWarning("Berichtsverzeichnis nicht gefunden: '{Dir}'.", inputDir);
|
|
return null;
|
|
}
|
|
|
|
// Konvention Option A: {originalname}_report.pdf
|
|
var baseName = Path.GetFileNameWithoutExtension(originalFileName);
|
|
var reportName = $"{baseName}_report.pdf";
|
|
var reportPath = Path.Combine(inputDir, reportName);
|
|
|
|
return File.Exists(reportPath) ? reportPath : null;
|
|
}
|
|
} |