HotPDF skapar, detekterar och validerar elektroniska ZUGFeRD- och Factur-X-fakturor från Delphi och C++Builder. AddFacturXAssociatedFile bäddar in faktura-XML enligt EN 16931 i en PDF/A-3-behållare, DetectFacturXInvoice känner igen en inkommande hybridfaktura och HPDFValidateEInvoice kör de officiella Schematron-affärsreglerna för EN 16931 mot den inbäddade XML-koden. Den här artikeln går igenom alla tre stegen och redogör ärligt för var valideringsmotorn sätter stopp
Drivkraften bakom dessa funktioner är lagstiftning, inte teknik. Tyskland och Frankrike fasar in obligatorisk strukturerad e-fakturering för inhemska B2B-transaktioner, och båda länders regelverk sammanstrålar i samma hybridmodell: den PDF-faktura som ditt affärssystem (ERP) redan skapar måste innehålla en maskinläsbar XML-tvilling. En Delphi-baserad redovisnings- eller ERP-applikation som idag skickar vanliga PDF-fakturor har en skarp deadline för att börja skicka Factur-X eller ZUGFeRD, och skillnaden mellan de två namnen är mindre än vad marknadsföringen antyder — Factur-X och ZUGFeRD är samma fransk-tyska standard under två olika etiketter
Vad gör en PDF till en giltig Factur-X-faktura?
En giltig Factur-X-faktura är en PDF/A-3-fil (ISO 19005-3) som bäddar in exakt en strukturerad faktura-XML som en associerad fil, länkar den från dokumentkatalogen och deklarerar den i XMP-tilläggsmetadata. Tre skikt måste stämma överens. För det första algebra-behållaren uppfylla kraven för PDF/A-3, det arkiveringsformat som — till skillnad från PDF/A-1 och PDF/A-2 — tillåter inbäddade filer av vilken typ som helst. För det andra måste faktura-XML-koden, namngiven factur-x.xml för standardprofiler eller xrechnung.xml för den tyska XRechnung-varianten, registreras i katalogens /AF-array och trädet /Names /EmbeddedFiles, med en AFRelationship-post som anger vad bilagan är: Alternative innebär att XML-koden är en likvärdig representation av den synliga fakturan, vilket är den semantik som de flesta profiler kräver. För det tredje måste Factur-X XMP-tilläggsschemat namnge den inbäddade filen, dess överensstämmelsenivå och dokumenttyp, så att ett mottagande system kan klassificera fakturan utan att ens behöva tolka XML-koden
Själva XML-innehållet följer den semantiska modellen EN 16931, den europeiska standarden som definierar kärnfakturan: köpare, säljare, radobjekt, momsredovisning, betalningsbelopp, var och en med ett affärstermnummer (BT-1 för fakturanummer, BT-5 för valuta, och så vidare). EN 16931-validering sker därför på två olika skikt. XML-schemavalidering kontrollerar att dokumentet är välformad CII med rätt elementnamn. Schematron-affärsreglerna kontrollerar att innehållet är sammanhängande — till exempel att BR-CO-15 uppfylls och att fakturans totalsumma faktiskt är lika med summan av radernas nettobelopp plus moms, samt att obligatoriska termer finns med för den deklarerade profilen. En fil kan klara schemavalidering och ändå vara en ogiltig faktura, vilket är anledningen till att affärsregelskiktet finns
Varför ZUGFeRD 2.5 inte behöver en ny behållare
ZUGFeRD 2.5 ändrar ingenting på PDF-behållarnivån: riktlinjernas URN, XMP-schemat och versionstoken är identiska med ZUGFeRD 2.3 / Factur-X 1.0. Detta överraskar de flesta implementörer, så det är värt att citera källan. Specifikationen för Factur-X 1.09 slår fast att versionsnumret i URI för PDF/A-tilläggsschemat inte är relaterat till versionsnumret för XML-dataspecifikationen, och att versionshanteringen för Factur-X-fakturainstanser förblir 1.0 — både i XMP fx:Version och i BT-24 av factur-x.xml. Det officiella ZUGFeRD 2.5-exempelmaterialet bekräftar detta: varje prov bäddar in factur-x.xml under URN-riktlinjen urn:factur-x.eu:1p0 med fx:Version som 1.0. Det som 2.5 faktiskt tillför finns inuti XML-datamodellen — nya element som underfakturarader och batch-identifierare — vilket är det affärsskikt som din applikation genererar, inte behållarskiktet som PDF-biblioteket hanterar
HotPDF kodar denna slutsats direkt. AddFacturXAssociatedFile accepterar ett Version-argument och normaliserar det via HPDFFacturXNormalizeVersion innan XMP skrivs. En anropare som skickar aliaset 2p5 får därmed fortfarande den token som uppfyller specifikationen, istället för att uppfinna en 2p5 XMP-version som ingen validerare skulle acceptera. Detekteringshjälparna behandlar 2p5 som likvärdigt vid läsning, vilket gör att operationerna förblir symmetriska. Om ett bibliotek eller verktyg hävdar att det skriver en speciell ZUGFeRD 2.5-behållare, specifikationen själv säger att något sådant inte existerar
Bädda in faktura-XML från Delphi
THotPDF.AddFacturXAssociatedFile förvandlar en vanlig PDF/A-3-skapsel till en Factur-X-faktura med ett enda anrop: den bäddar in XML-byten som en associerad fil, registrerar factur-x.xml i katalogens /AF-array och trädet /Names /EmbeddedFiles samt genererar matchande tilläggsmetadata för Factur-X XMP. Du ritar den människo-läsbara fakturasidan med HotPDF:s vanliga canvas-API; arbetet med behållaren är en enda rad mellan BeginDoc och EndDoc
var
Pdf: THotPDF;
InvoiceXML, UBLXML: TBytes;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice-2026-0042.pdf';
Pdf.PDFACompliance := '3B'; // PDF/A-3b container
Pdf.BeginDoc;
// embed the EN 16931 CII invoice XML as factur-x.xml
InvoiceXML := BuildInvoiceXML; // produced by your business layer
Pdf.AddFacturXAssociatedFile(InvoiceXML, 'EN 16931');
// optional: attach a UBL view as a supplementary representation
UBLXML := BuildUBLXML;
Pdf.AddUBLSupplementaryFile(UBLXML); // factur-xubl.xml, Alternative
// ... draw the visible invoice page here ...
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Standardvärdena följer specifikationen: filnamn factur-x.xml, relation Alternative, version 1.0. AddUBLSupplementaryFile implementerar avsnitt 6.4 i Factur-X 1.09-specifikationen, vilket tillåter att en UBL-representation av samma faktura skickas med som en andra bilaga med namnet factur-xubl.xml, MIME-typ application/xml och relationen Alternative. Ordningsföljden är viktig här, och HotPDF upprätthåller den begränsning som specifikationen antyder: UBL-filen är en kompletterande vy, aldrig den primära fakturan, så den måste läggas till efter att AddFacturXAssociatedFile har registrerat CII-mastern. Observera att den ritade sidan och den inbäddade XML-koden måste visa samma fakturainformation — en PDF som visar en totalsumma men kodar en annan är precis det felscenarie som hybridfakturering är utformad för att förhindra. PDF/A-3-sidan i detta arbetsflöde, inklusive OutputIntent och typsnittsinbäddning, behandlas mer i detalj i artikeln om validering av PDF/A, PDF/X och PDF/UA i Delphi
Hur upptäcker du en Factur-X-faktura i en inkommande PDF?
THotPDF.DetectFacturXInvoice svarar mot mottagarsidan av mandatet: läs in valfri PDF och få reda på i ett enda anrop om det är en hybridfaktura, vilken profil den deklarerar och vilka bilagor den bär med sig. Funktionen returnerar en THPDFFacturXInvoiceInfo-post med det inbäddade XML-filnamnet, XMP-deklarerat filnamn och typ för dokumentet, versionstoken, överensstämmelsenivå, riktlinje-URN samt AFRelationship-värdet. Ytterligare två fält rapporterar kompletterande vyer: UBLFileName fylls i när en factur-xubl.xml-bilaga finns, och EDIFACTFileName när en EDIFACT-vy följer med — ingetdera påverkar detekteringsbeslutet, eftersom en kompletterande representation inte kan existera utan den primära CII-fakturan
var
Reader: THotPDF;
Info: THPDFFacturXInvoiceInfo;
begin
Reader := THotPDF.Create(nil);
try
if Reader.LoadFromFile('incoming-invoice.pdf') <= 0 then
raise Exception.Create('No pages loaded.');
if Reader.DetectFacturXInvoice(Info) then
begin
Writeln('Invoice XML: ', string(Info.FileName));
Writeln('Conformance: ', string(Info.ConformanceLevel));
Writeln('Guideline URN: ', string(Info.GuidelineID));
Writeln('Relationship: ', string(Info.AFRelationship));
if Info.UBLFileName <> '' then
Writeln('UBL view: ', string(Info.UBLFileName));
end
else
Writeln('Not a Factur-X / ZUGFeRD hybrid invoice.');
finally
Reader.Free;
end;
end;
Detektering läser samma signaler som en kompatibel validerare läser — XMP-tilläggsschemat och registret över associerade filer — snarare än att gissa enbart utifrån filnamn. Samma inlästa dokumentobjekt exponerar också XMP- och Info-ordlistans metadata för inspektion eller korrigering, ett arbetsflöde som beskrivs i artikeln om redigering av metadata i inlästa PDF-dokument
Affärsregler eller schema: vad innebär EN 16931-validering?
HPDFValidateEInvoice, en fristående funktion i enheten HPDFEInvoiceValidator, kör båda valideringsskikten i sekvens: först kontrollen av behållarstruktur via ValidateFacturXInvoice, sedan EN 16931 Schematron-affärsreglerna mot den extraherade faktura-XML-koden. HotPDF levereras med de fem officiella Factur-X 1.09-regelfilerna — MINIMUM, BASIC WL, BASIC, EN 16931 och EXTENDED — under Lib/resources/Schematron/, och HPDFSchematronFileForProfile väljer den fil som matchar den detekterade överensstämmelsenivån. Det innebär att en EN 16931-faktura automatiskt kontrolleras mot Factur-X_1.09_EN16931.sch. Funktionen returnerar True endast när behållaren är giltig och ingen affärsregel utlöstes
uses HPDFEInvoiceValidator;
var
Reader: THotPDF;
Report: THPDFEInvoiceValidationReport;
I: Integer;
begin
Reader := THotPDF.Create(nil);
try
Reader.LoadFromFile('incoming-invoice.pdf');
if HPDFValidateEInvoice(Reader, SchematronDir, Report) then
Writeln('Container and business rules: OK')
else
Writeln('Validation found issues.');
if Report.BusinessRulesEvaluated then
begin
Writeln(Report.BusinessSummary.Evaluated, ' rules evaluated, ',
Report.BusinessSummary.Skipped, ' skipped (XPath 2.0)');
for I := 0 to High(Report.BusinessRules) do
if (not Report.BusinessRules[I].Skipped) and
(Report.BusinessRules[I].Severity = stsError) then
Writeln(string(Report.BusinessRules[I].RuleID), ': ',
string(Report.BusinessRules[I].Message));
end;
finally
Reader.Free;
end;
end;
Varje post i BusinessRules bär på regelidentifieraren som tolkats från påståendetexten — BR-45, BR-CO-17 och resten av EN 16931-namnschemat — plus regelns sammanhangs-XPath, testuttrycket och en allvarlighetsgrad (severity) som speglar Schematrons flaggattribut. Det gör rapporten användbar: istället för ett enkelt godkänd/underkänd får du reda på exakt vilken affärsterm som är inkonsekvent, vilket är vad din supportavdelning behöver när en kund fråga varför en faktura avvisades. Utvecklare som redan genererar strukturerade överensstämmelserapporter för arkiverings-PDF:er kan integrera dessa resultat i samma pipeline som beskrivs i artikeln om automatisering av PDF-preflight-rapporter
Var valideraren sätter stopp, ärligt talat
Schematron-motorn bygger på MSXML, som utvärderar XPath 1.0. EN 16931-regelfilen deklarerar queryBinding="xslt2" och innehåller 424 påståenden (assertions), varav de flesta endast använder XPath 1.0-funktioner som string-length och substring-after — dessa körs inbyggt. En minoritet förlitar sig på XPath 2.0-konstruktioner som xs:decimal-typomvandlingar eller exists(). HotPDF förhandsskannar varje testuttryck efter kända XPath 2.0-tokens och markerar dessa regler som Skipped med allvarlighetsgraden info, snarare än att försöka göra en utvärdering som tyst skulle misslyckas. Sammanfattningen rapporterar antalet utvärderade respektive hoppade regler separat, så att din logg alltid anger exakt hur stor del av regeluppsättningen som har körts. I Delphi 7, där MSXML-bindningar saknas, enheten kompilerar en stubb som rapporterar motorn som ej tillgänglig utan att störa behållarbeslutet; den fullständiga motorn kräver Delphi XE2 eller senare
Den andra begränsningen är ännu viktigare. Ett grönt resultat från HPDFValidateEInvoice innebär att behållarens struktur stämmer överens och att ingen utvärderad affärsregel utlöstes — det är inte ett godkännande av skattemässig efterlevnad. Nationella tilläggsregler, mottagarspecifika krav och de juridiska skyldigheterna kring fakturainnehåll ligger ovanför kärnan i EN 16931 och faller helt utanför alla programbibliotek. Behandla valideraren som den grindvakt som håller strukturellt felaktiga fakturor borta från din utskorg och flaggar inkonsekventa i din inkorg, kör en fullständig XPath 2.0-validerare i din release-pipeline när certifiering kräver det, och låt dina skatterådgivare ansvara för efterlevnadsfrågan. API:erna för inbäddning, detektering och validering som visas här är en del av standardversionen av HotPDF Component för Delphi och C++Builder, med ett komplett e-fakturaexempel som täcker alla sju ZUGFeRD- och Factur-X-profiler