Compare commits

...

3 Commits

Author SHA1 Message Date
OlgunR
a087baa089 Add stamp to first page of PDF documents
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.
2026-05-29 11:26:25 +02:00
OlgunR
98226f239b Adjust logging levels to reduce verbosity
Changed logging levels from LogInformation to LogDebug in
PdfAttachmentExtractorService and PdfResultPackageService.
This includes logs for PDF conformity levels, ZUGFeRD
Guideline-IDs, saved attachment details, and result report
discovery. These changes aim to reduce log verbosity in
production environments by moving less critical information
to the debug level.
2026-05-29 09:52:05 +02:00
OlgunR
1144c45826 Improve error handling in PDF processing workflow
Refactored the PDF processing workflow to enhance error handling
and provide more descriptive exception messages. Added `try-catch`
blocks around the PDF/A-3b conversion and document saving steps
to handle potential failures.

Labeled the workflow steps for clarity:
- Step 1: Convert to PDF/A-3b
- Step 2: Buffer converted PDF
- Step 3: Embed attachment
- Step 4: Save document

Removed redundant `MemoryStream` initialization for the original
PDF and updated comments to improve code readability and
maintainability.
2026-05-29 09:20:34 +02:00
2 changed files with 78 additions and 13 deletions

View File

@@ -38,7 +38,7 @@ public class PdfAttachmentExtractorService(
}; };
result.PdfAWarning = compatibility == PdfACompatibility.None; result.PdfAWarning = compatibility == PdfACompatibility.None;
logger.LogInformation( logger.LogDebug(
"PDF '{FileName}': Konformität = {Level}", "PDF '{FileName}': Konformität = {Level}",
sourceFileName, result.PdfALevel); sourceFileName, result.PdfALevel);
@@ -50,7 +50,7 @@ public class PdfAttachmentExtractorService(
{ {
result.ZugferdGuidelineId = ExtractGuidelineId(xmpData); result.ZugferdGuidelineId = ExtractGuidelineId(xmpData);
if (!string.IsNullOrEmpty(result.ZugferdGuidelineId)) if (!string.IsNullOrEmpty(result.ZugferdGuidelineId))
logger.LogInformation( logger.LogDebug(
"PDF '{FileName}': Guideline-ID = {GuidelineId}", "PDF '{FileName}': Guideline-ID = {GuidelineId}",
sourceFileName, result.ZugferdGuidelineId); sourceFileName, result.ZugferdGuidelineId);
} }
@@ -105,7 +105,7 @@ public class PdfAttachmentExtractorService(
var isZugferd = IsZugferdXml(attachment.FileName); var isZugferd = IsZugferdXml(attachment.FileName);
logger.LogInformation( logger.LogDebug(
" → Gespeichert: '{FileName}' ({Bytes} Bytes){Zugferd}", " → Gespeichert: '{FileName}' ({Bytes} Bytes){Zugferd}",
safeFileName, data.Length, safeFileName, data.Length,
isZugferd ? " [ZUGFeRD/Factur-X XML]" : string.Empty); isZugferd ? " [ZUGFeRD/Factur-X XML]" : string.Empty);

View File

@@ -21,7 +21,7 @@ public class PdfResultPackageService(
return null; return null;
} }
logger.LogInformation( logger.LogDebug(
"Ergebnisbericht gefunden: '{ReportPath}'.", reportPath); "Ergebnisbericht gefunden: '{ReportPath}'.", reportPath);
// 2. Ausgabepfad bestimmen // 2. Ausgabepfad bestimmen
@@ -35,20 +35,28 @@ public class PdfResultPackageService(
// 3. Original auf PDF/A-3b hochstufen + Bericht anhängen // 3. Original auf PDF/A-3b hochstufen + Bericht anhängen
await Task.Run(() => await Task.Run(() =>
{ {
// Original in MemoryStream laden
using var inputStream = new MemoryStream(originalPdfBytes); using var inputStream = new MemoryStream(originalPdfBytes);
using var outputStream = new MemoryStream();
// PDF/A-3b Konvertierung // Schritt 1: PDF/A-3b Konvertierung
var converter = new PdfDocumentConverter(inputStream); PdfDocumentConverter converter;
try
{
converter = new PdfDocumentConverter(inputStream);
converter.Convert(PdfCompatibility.PdfA3b); 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);
}
// Konvertiertes PDF in MemoryStream speichern // Schritt 2: Konvertiertes PDF puffern
using var convertedStream = new MemoryStream(); using var convertedStream = new MemoryStream();
converter.SaveDocument(convertedStream); converter.SaveDocument(convertedStream);
convertedStream.Position = 0; convertedStream.Position = 0;
// Bericht als Anhang einbetten // Schritt 3: Anhang einbetten
using var processor = new PdfDocumentProcessor(); using var processor = new PdfDocumentProcessor();
processor.LoadDocument(convertedStream); processor.LoadDocument(convertedStream);
@@ -62,8 +70,65 @@ public class PdfResultPackageService(
Data = File.ReadAllBytes(reportPath) Data = File.ReadAllBytes(reportPath)
}); });
// Speichern // 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); 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( logger.LogInformation(