Zugferd, etc.

This commit is contained in:
2026-04-28 17:11:48 +02:00
parent 77370be55d
commit 674532460a
5 changed files with 709 additions and 321 deletions

View File

@@ -6,6 +6,7 @@ Imports System.Net
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Windows.Forms
Imports System.Xml
Imports DocumentFormat.OpenXml
Imports iText.Kernel.Pdf
Imports iText.Kernel.Pdf.Filespec
@@ -13,11 +14,13 @@ Imports iText.Kernel.XMP
Imports iText.Kernel.XMP.Options
Imports iText.Pdfa
Imports iText.Pdfa.Exceptions
Imports iTextSharp.text
Imports iTextSharp.text.pdf
Imports Spire.Pdf
Imports Spire.Pdf.Attachments
Imports Spire.Pdf.Graphics
Imports Spire.Pdf.Print
Imports Font = iTextSharp.text.Font
Imports PdfName = iTextSharp.text.pdf.PdfName
@@ -851,13 +854,13 @@ Public Class cFormularManager
Public Function fillPDF(vorlagenname As String, list As List(Of VERAG_PROG_ALLGEMEIN.MyListItem), listToWrite As List(Of cPDFWriteValues), Optional editierbar As Boolean = True, Optional autoDruck As Boolean = False, Optional printerName As String = "", Optional barcode As Image = Nothing, Optional barcodeLKW As Image = Nothing, Optional uo2 As String = "", Optional openfile As Boolean = True) As String
Public Function fillPDF(vorlagenname As String, list As List(Of VERAG_PROG_ALLGEMEIN.MyListItem), listToWrite As List(Of cPDFWriteValues), Optional editierbar As Boolean = True, Optional autoDruck As Boolean = False, Optional printerName As String = "", Optional barcode As System.Drawing.Image = Nothing, Optional barcodeLKW As System.Drawing.Image = Nothing, Optional uo2 As String = "", Optional openfile As Boolean = True) As String
Return fillPDF_Editierbar("DOKUMENTE", "VORLAGEN", "", uo2, "", vorlagenname, list, listToWrite, editierbar, autoDruck, printerName, barcode, barcodeLKW)
End Function
Public Function fillPDF_Editierbar(da_kategorie As String, da_ordner As String, da_uOrdner1 As String, da_uOrdner2 As String, da_uOrdner3 As String, da_name As String, list As List(Of VERAG_PROG_ALLGEMEIN.MyListItem), listToWrite As List(Of cPDFWriteValues), Optional editierbar As Boolean = True, Optional autoDruck As Boolean = False, Optional printerName As String = "", Optional barcode As Image = Nothing, Optional barcodeLKW As Image = Nothing, Optional openfile As Boolean = True) As String
Public Function fillPDF_Editierbar(da_kategorie As String, da_ordner As String, da_uOrdner1 As String, da_uOrdner2 As String, da_uOrdner3 As String, da_name As String, list As List(Of VERAG_PROG_ALLGEMEIN.MyListItem), listToWrite As List(Of cPDFWriteValues), Optional editierbar As Boolean = True, Optional autoDruck As Boolean = False, Optional printerName As String = "", Optional barcode As System.Drawing.Image = Nothing, Optional barcodeLKW As System.Drawing.Image = Nothing, Optional openfile As Boolean = True) As String
Try
Dim DS As New cDATENSERVER(da_kategorie, da_ordner, da_uOrdner1, da_uOrdner2, da_uOrdner3, da_name)
If Not DS.hasITEMS Then Return ""
@@ -1455,7 +1458,7 @@ Public Class cFormularManager
Dim printer As PrintDocument = New PrintDocument
AddHandler printer.PrintPage, Sub(snd As Object, ev As PrintPageEventArgs)
Dim img As Image = Image.FromFile(pdfPath)
Dim img As System.Drawing.Image = System.Drawing.Image.FromFile(pdfPath)
ev.Graphics.DrawImage(img, ev.PageBounds)
End Sub
' printer.PrinterSettings.DefaultPageSettings.PrintableArea.
@@ -1815,7 +1818,7 @@ Public Class cFormularManager
Dim rasterizer = New Ghostscript.NET.Rasterizer.GhostscriptRasterizer
rasterizer.Open(fileName)
'//Image page = rasterizer.GetPage(96,96); <-- this one prints ok
Dim page As Image = rasterizer.GetPage(600, 600, 1)
Dim page As System.Drawing.Image = rasterizer.GetPage(600, 600, 1)
' doc.DocumentName = fileName
Dim doc As New PrintDocument
@@ -2526,21 +2529,20 @@ Public Class DATENVERVER_OPTIONS
Private Shared Sub SetZugferdXmp_NEW(pdf As iText.Kernel.Pdf.PdfDocument, xmlFileName As String)
Dim xmp As XMPMeta = XMPMetaFactory.Create()
Dim xmp = iText.Kernel.XMP.XMPMetaFactory.Create()
' =========================
' PDF/A
' PDF/A Pflichtfelder
' =========================
Dim pdfaNs As String = "http://www.aiim.org/pdfa/ns/id/"
xmp.SetProperty(pdfaNs, "part", "3")
xmp.SetProperty(pdfaNs, "conformance", "B")
xmp.SetProperty("http://www.aiim.org/pdfa/ns/id/", "part", "3")
xmp.SetProperty("http://www.aiim.org/pdfa/ns/id/", "conformance", "B")
' =========================
' Factur-X Namespace
' =========================
Dim fxNs As String = "urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#"
XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(fxNs, "fx")
iText.Kernel.XMP.XMPMetaFactory.GetSchemaRegistry().RegisterNamespace(fxNs, "fx")
xmp.SetProperty(fxNs, "DocumentType", "INVOICE")
xmp.SetProperty(fxNs, "DocumentFileName", xmlFileName)
@@ -2548,12 +2550,76 @@ Public Class DATENVERVER_OPTIONS
xmp.SetProperty(fxNs, "ConformanceLevel", "EN 16931")
' =========================
' WICHTIG: KEIN XML-MANIPULATION
' PDF/A Extension Schema
' =========================
Dim pdfaExt = "http://www.aiim.org/pdfa/ns/extension/"
Dim pdfaSchema = "http://www.aiim.org/pdfa/ns/schema#"
Dim pdfaProp = "http://www.aiim.org/pdfa/ns/property#"
' schemas Array (rdf:Bag ist hier OK für schemas selbst)
xmp.SetProperty(pdfaExt, "schemas", Nothing,
New iText.Kernel.XMP.Options.PropertyOptions(iText.Kernel.XMP.Options.PropertyOptions.ARRAY))
' Schema-Struct hinzufügen
xmp.AppendArrayItem(
pdfaExt,
"schemas",
New iText.Kernel.XMP.Options.PropertyOptions(iText.Kernel.XMP.Options.PropertyOptions.ARRAY),
Nothing,
New iText.Kernel.XMP.Options.PropertyOptions(iText.Kernel.XMP.Options.PropertyOptions.STRUCT)
)
' Schema Beschreibung
xmp.SetStructField(pdfaExt, "schemas[1]", pdfaSchema, "schema", "Factur-X PDFA Extension Schema")
xmp.SetStructField(pdfaExt, "schemas[1]", pdfaSchema, "namespaceURI", fxNs)
xmp.SetStructField(pdfaExt, "schemas[1]", pdfaSchema, "prefix", "fx")
' 🔥 property MUSS rdf:Seq sein!
xmp.SetStructField(
pdfaExt,
"schemas[1]",
pdfaSchema,
"property",
Nothing,
New iText.Kernel.XMP.Options.PropertyOptions(
iText.Kernel.XMP.Options.PropertyOptions.ARRAY Or
iText.Kernel.XMP.Options.PropertyOptions.ARRAY_ORDERED)
)
Dim props = {"DocumentType", "DocumentFileName", "Version", "ConformanceLevel"}
For i = 0 To props.Length - 1
Dim arrayPath = "schemas[1]/pdfaSchema:property"
' 🔥 Item in rdf:Seq hinzufügen
xmp.AppendArrayItem(
pdfaExt,
arrayPath,
New iText.Kernel.XMP.Options.PropertyOptions(
iText.Kernel.XMP.Options.PropertyOptions.ARRAY Or
iText.Kernel.XMP.Options.PropertyOptions.ARRAY_ORDERED),
Nothing,
New iText.Kernel.XMP.Options.PropertyOptions(iText.Kernel.XMP.Options.PropertyOptions.STRUCT)
)
Dim idx = i + 1
Dim basePath = $"schemas[1]/pdfaSchema:property[{idx}]"
xmp.SetStructField(pdfaExt, basePath, pdfaProp, "name", props(i))
xmp.SetStructField(pdfaExt, basePath, pdfaProp, "valueType", "Text")
xmp.SetStructField(pdfaExt, basePath, pdfaProp, "category", "external")
xmp.SetStructField(pdfaExt, basePath, pdfaProp, "description", props(i))
Next
' =========================
' XMP ins PDF schreiben
' =========================
pdf.SetXmpMetadata(xmp)
End Sub
Public Shared Function addAttachementToPDF(File As String, attPath As String, Optional renameFile As String = "") As String
Try
@@ -2603,11 +2669,323 @@ Public Class DATENVERVER_OPTIONS
End Function
Shared Function ExtractMustangResult(xmlLog As String, invoiceFile As String, RK_ID As Integer) As MustangResult
Dim doc As New XmlDocument()
doc.LoadXml(xmlLog)
' =========================
' PDF STATUS
' =========================
Dim pdfNode = doc.SelectSingleNode("//pdf/summary")
Dim pdfStatus As String =
If(pdfNode?.Attributes("status")?.Value = "valid", "VALID", "INVALID")
' =========================
' XML STATUS
' =========================
Dim xmlNode = doc.SelectSingleNode("//xml/summary")
Dim xmlStatus As String =
If(xmlNode?.Attributes("status")?.Value = "valid", "VALID", "INVALID")
' =========================
' PROFILE
' =========================
Dim profileNode = doc.SelectSingleNode("//xml/info/profile")
Dim profile As String =
If(profileNode IsNot Nothing, profileNode.InnerText, "UNKNOWN")
' =========================
' VERSION
' =========================
Dim xmlVersionNode = doc.SelectSingleNode("//xml/info/version")
Dim xmlVersion As String =
If(xmlVersionNode IsNot Nothing, xmlVersionNode.InnerText, "-")
Dim validatorNode = doc.SelectSingleNode("//xml/info/validator")
Dim validatorVersion As String =
validatorNode?.Attributes("version")?.Value
' =========================
' DURATION (SAFE)
' =========================
Dim pdfDuration = doc.SelectSingleNode("//pdf/info/duration")?.InnerText
Dim xmlDuration = doc.SelectSingleNode("//xml/info/duration")?.InnerText
Dim duration As String =
$"{If(pdfDuration, "-")} ms (PDF)" & " / " &
$"{If(xmlDuration, "-")} ms (XML)"
' =========================
' RULES
' =========================
Dim firedNode = doc.SelectSingleNode("//xml/info/rules/fired")
Dim failedNode = doc.SelectSingleNode("//xml/info/rules/failed")
Dim fired As Integer = If(firedNode IsNot Nothing, Integer.Parse(firedNode.InnerText), 0)
Dim failed As Integer = If(failedNode IsNot Nothing, Integer.Parse(failedNode.InnerText), 0)
Dim passed As Integer = fired - failed
' =========================
' RESULT DTO
' =========================
Return New MustangResult With {
.PdfValid = (pdfStatus = "VALID"),
.XmlValid = (xmlStatus = "VALID"),
.PdfStatus = pdfStatus,
.XmlStatus = xmlStatus,
.Profile = profile,
.Fired = fired,
.Failed = failed,
.Passed = passed,
.ValidatorVersion = validatorVersion,
.XMLVersion = xmlVersion,
.Duration = duration
}
End Function
Shared Function CreateValidationPdf(log As String, Invoice_file As String, RK_ID As Integer, outputPath As String) As Boolean
Dim result = ExtractMustangResult(log, Invoice_file, RK_ID)
Dim docXml As New XmlDocument()
docXml.LoadXml(log)
Using fs As New FileStream(outputPath, FileMode.Create)
Using doc As New iTextSharp.text.Document(iTextSharp.text.PageSize.A4, 36, 36, 36, 36)
Dim writer = pdf.PdfWriter.GetInstance(doc, fs)
doc.Open()
' =========================
' FONTS
' =========================
Dim titleFont = setFont(18.0F, Font.BOLD)
Dim normal = setFont(9.0F, iTextSharp.text.Font.NORMAL)
Dim errorFont = setFont(11.0F, Font.BOLD, BaseColor.RED)
' =========================
' HEADER
' =========================
Dim title As New iTextSharp.text.Paragraph("MUSTANG VALIDATION DASHBOARD", titleFont)
title.Alignment = Element.ALIGN_CENTER
title.SpacingAfter = 15
doc.Add(title)
doc.Add(New iTextSharp.text.Paragraph(" ", normal))
' =========================
' KPI 1
' =========================
Dim kpiTable As New PdfPTable(3)
kpiTable.WidthPercentage = 100
kpiTable.SetWidths(New Single() {33, 33, 34})
kpiTable.AddCell(CreateKpiCell("PDF STATUS", result.PdfStatus, If(result.PdfValid, BaseColor.GREEN, BaseColor.RED)))
kpiTable.AddCell(CreateKpiCell("XML STATUS", result.XmlStatus, If(result.XmlValid, BaseColor.GREEN, BaseColor.RED)))
kpiTable.AddCell(CreateKpiCell("PROFILE", result.Profile, BaseColor.DARK_GRAY))
doc.Add(kpiTable)
' =========================
' KPI 2
' =========================
Dim kpi2 As New PdfPTable(3)
kpi2.WidthPercentage = 100
kpi2.SpacingAfter = 15
kpi2.SetWidths(New Single() {33, 33, 34})
kpi2.AddCell(CreateKpiCell("TESTS PASSED", result.Passed.ToString(), BaseColor.DARK_GRAY))
kpi2.AddCell(CreateKpiCell("FAILED", result.Failed.ToString(), If(result.Failed > 0, BaseColor.RED, BaseColor.GREEN)))
kpi2.AddCell(CreateKpiCell("TOTAL", result.Fired.ToString(), BaseColor.DARK_GRAY))
doc.Add(kpi2)
' =========================
' KPI 3
' =========================
'Dim kpi3 As New PdfPTable(3)
'kpi3.WidthPercentage = 100
'kpi3.SetWidths(New Single() {33, 33, 34})
'kpi3.AddCell(CreateKpiCell("VALIDATOR", result.ValidatorVersion, BaseColor.DARK_GRAY))
'kpi3.AddCell(CreateKpiCell("XML VERSION", result.XMLVersion, BaseColor.DARK_GRAY))
'kpi3.AddCell(CreateKpiCell("DURATION", result.Duration, BaseColor.DARK_GRAY))
'doc.Add(kpi3)
' =========================
' DETAILS
' =========================
Dim detailHeader As New iTextSharp.text.Paragraph("DETAILS", normal)
detailHeader.SpacingAfter = 8
doc.Add(New iTextSharp.text.Paragraph(ToFlatText(result, Invoice_file, RK_ID), normal))
' =========================
' ERROR SECTION
' =========================
Dim failedAssertions = docXml.SelectNodes("//pdf//TestAssertion[@status='failed']")
Dim MessagesXML = docXml.SelectNodes("//xml/messages/*")
Dim MessagesPDF = docXml.SelectNodes("//pdf/messages/*")
Dim errorHeader As New iTextSharp.text.Paragraph("VALIDATION DETAILS", normal)
errorHeader.SpacingAfter = 8
doc.Add(errorHeader)
Dim errorTable As New PdfPTable(1)
errorTable.WidthPercentage = 100
Dim hasErrors As Boolean = (failedAssertions IsNot Nothing AndAlso failedAssertions.Count > 0) OrElse (MessagesXML IsNot Nothing AndAlso MessagesXML.Count > 0) OrElse (MessagesPDF IsNot Nothing AndAlso MessagesPDF.Count > 0)
If hasErrors Then
If failedAssertions IsNot Nothing Then
For i = 0 To failedAssertions.Count - 1
Dim msg = failedAssertions(i).Attributes("message")?.Value
Dim rule = failedAssertions(i).Attributes("ruleId")?.Value
Dim text = If(rule IsNot Nothing, "[" & rule & "] ", "") &
If(msg IsNot Nothing, msg, "Unknown error")
Dim cell As New PdfPCell(New Phrase(text, normal))
cell.BackgroundColor = New BaseColor(245, 245, 245)
cell.Padding = 8
errorTable.AddCell(cell)
Next
End If
' XML Messages
If MessagesXML IsNot Nothing Then
For i = 0 To MessagesXML.Count - 1
Dim text = MessagesXML(i).InnerText
errorTable.AddCell(New PdfPCell(New Phrase(text, normal)) With {
.BackgroundColor = New BaseColor(245, 245, 245),
.Padding = 8
})
Next
End If
' PDF Messages
If MessagesPDF IsNot Nothing Then
For i = 0 To MessagesPDF.Count - 1
Dim text = MessagesPDF(i).InnerText
errorTable.AddCell(New PdfPCell(New Phrase(text, normal)) With {
.BackgroundColor = New BaseColor(245, 245, 245),
.Padding = 8
})
Next
End If
doc.Add(errorTable)
Else
Dim ok As New iTextSharp.text.Paragraph("No validation errors found.", normal)
ok.Font.Color = BaseColor.GREEN
doc.Add(ok)
End If
' =========================
' FOOTER
' =========================
doc.Add(New iTextSharp.text.Paragraph(" "))
Dim footer As New iTextSharp.text.Paragraph("Generated by Mustang Validator", normal)
footer.Alignment = Element.ALIGN_RIGHT
footer.Font.Color = BaseColor.BLACK
doc.Add(footer)
doc.Close()
Return result.PdfValid AndAlso result.XmlValid
End Using
End Using
End Function
Public Shared Function ToFlatText(r As MustangResult, fileName As String, RK_ID As String) As String
Dim sb As New StringBuilder()
Dim fileInfo As New FileInfo(fileName)
Dim invoice As New cRechnungsausgang(RK_ID)
sb.AppendLine("RESULT")
sb.AppendLine("================================")
sb.AppendLine($"PDF File : {fileInfo.Name}")
sb.AppendLine($"Invoice No : {invoice.RechnungsNr}")
sb.AppendLine($"Invoice Date : {CDate(invoice.RechnungsDatum).ToShortDateString}")
sb.AppendLine("--------------------------------")
sb.AppendLine($"XML Version : {r.XMLVersion}")
sb.AppendLine($"Validator : {r.ValidatorVersion}")
sb.AppendLine("--------------------------------")
sb.AppendLine($"Tests Passed : {r.Passed} / {r.Fired}")
sb.AppendLine($"XML Errors : {r.Failed}")
sb.AppendLine($"PDF Errors : {If(r.PdfValid AndAlso r.XmlValid, 0, 1)}")
sb.AppendLine($"Duration : {r.Duration}")
sb.AppendLine($"Created : {Now()}")
sb.AppendLine("================================")
Return sb.ToString()
End Function
Private Shared Function CreateKpiCell(title As String, value As String, color As iTextSharp.text.BaseColor) As iTextSharp.text.pdf.PdfPCell
Dim titleFont = iTextSharp.text.FontFactory.GetFont("Helvetica", 9, iTextSharp.text.Font.BOLD)
Dim valueFont As iTextSharp.text.Font = iTextSharp.text.FontFactory.GetFont(iTextSharp.text.FontFactory.HELVETICA, 11.0F, iTextSharp.text.Font.BOLD, color)
Dim p As New iTextSharp.text.Paragraph()
p.Add(New iTextSharp.text.Phrase(title & vbCrLf, titleFont))
p.Add(New iTextSharp.text.Phrase(value, valueFont))
Dim cell As New iTextSharp.text.pdf.PdfPCell(p)
cell.Padding = 10
cell.HorizontalAlignment = iTextSharp.text.Element.ALIGN_CENTER
cell.VerticalAlignment = iTextSharp.text.Element.ALIGN_MIDDLE
Return cell
End Function
Private Shared Function setFont(size As Single, style As Integer, Optional color As BaseColor = Nothing) As Font
If color Is Nothing Then
Return FontFactory.GetFont(FontFactory.HELVETICA, size, style)
End If
Return FontFactory.GetFont(FontFactory.HELVETICA, size, style, color)
End Function
End Class
Public Class MustangResult
Public Property PdfValid As Boolean
Public Property XmlValid As Boolean
Public Property PdfStatus As String
Public Property XmlStatus As String
Public Property Profile As String
Public Property Fired As Integer
Public Property Failed As Integer
Public Property Passed As Integer
Public Property ValidatorVersion As String
Public Property XMLVersion As String
Public Property Duration As String
End Class
Public Class barcodeToPdf
Public image As Image = Nothing
Public image As System.Drawing.Image = Nothing
Public x As Integer
Public y As Integer
Public width As Integer
@@ -2615,7 +2993,7 @@ Public Class barcodeToPdf
Public onpage = 1
Public rotate As System.Drawing.RotateFlipType = RotateFlipType.RotateNoneFlipNone
Sub New(image As Image, x As Integer, y As Integer, width As Integer, height As Integer, onpage As Integer, rotate As System.Drawing.RotateFlipType)
Sub New(image As System.Drawing.Image, x As Integer, y As Integer, width As Integer, height As Integer, onpage As Integer, rotate As System.Drawing.RotateFlipType)
Me.image = image
Me.x = x
Me.y = y