Files
DXApp/DXApp.TemplateKitProject/Services/PdfResultPackageService.cs
OlgunR 43d63e975d Enhance PDF processing and report integration
Refactor `PdfResultPackageService` to improve PDF handling:
- Load the main document into `PdfDocumentProcessor`.
- Remove original metadata to attribute the result to `DXApp`.
- Replace report file attachment with appending report pages.
- Use a temporary processor to determine report page count.
- Add logging for appended pages and total page count.
- Update save operation to include detailed logging.

Removed the functionality to attach the report file as a PDF attachment.
Improved overall usability, traceability, and document attribution.
2026-06-02 14:06:24 +02:00

179 lines
7.1 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: Hauptdokument laden
using var processor = new PdfDocumentProcessor();
processor.LoadDocument(convertedStream);
// Metadaten des Originals entfernen → Result-PDF gehört uns, nicht dem Ersteller
processor.Document.Author = "DXApp Verarbeitungssystem";
processor.Document.Creator = "DXApp";
processor.Document.Producer = "DXApp";
processor.Document.Title = string.Empty;
processor.Document.Subject = string.Empty;
processor.Document.Keywords = string.Empty;
// Schritt 3a: Berichts-Seiten ans Ende anhängen
int reportPageCount;
using (var reportStream = new FileStream(reportPath, FileMode.Open, FileAccess.Read))
{
// Temporäres Dokument öffnen um Seitenanzahl zu ermitteln
using var tempProcessor = new PdfDocumentProcessor();
tempProcessor.LoadDocument(reportStream);
reportPageCount = tempProcessor.Document.Pages.Count;
logger.LogDebug(
"Hänge {PageCount} Seite(n) aus Bericht an das Dokument an.",
reportPageCount);
// Stream zurücksetzen und ans Hauptdokument anhängen
reportStream.Position = 0;
processor.AppendDocument(reportStream);
}
// 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 5: Speichern
try
{
processor.SaveDocument(outputPath);
logger.LogInformation(
"Result-PDF erstellt mit {TotalPages} Seiten (Original + {ReportPages} Berichtseiten).",
processor.Document.Pages.Count,
reportPageCount);
}
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;
}
}