Du har bygd en Factur-X-faktura, og alle containerkontroller går igjennom. Katalogen bærer et /AF-array, navnetreet EmbeddedFiles peker til riktig filspesifikasjon, den innebygde factur-x.xml har korrekt /AFRelationship lik Alternative, og den innebygde ValidateFacturXInvoice returnerer 1. Så kjører du den samme filen gjennom veraPDF, referansekontrolløren som skatteportaler bruker, og den slår fast at hele dokumentet ikke er en gyldig PDF/A-3. Strukturen er riktig. Det er metadataene som er problemet, og feilen er en av de letteste å overse i hele e-fakturaarbeidsflyten
Det er verdt å forstå årsaken fullt ut, fordi den forklarer en klasse av PDF/A-feil som ikke har noe med den synlige siden eller vedlegget å gjøre, men alt med hvordan XMP beskriver seg selv. Dette er fellen som gjemmer seg bak en grønn containerkontroll
De fire egenskapene som får filen til å stryke
En Factur-X-faktura skriver fire egendefinerte egenskaper inn i XMP-pakken sin slik at nedstrøms programvare kan lese fakturaprofilen uten å parse den innebygde XML-en. De ligger i Factur-X-navnerommet under prefikset fx: fx:DocumentFileName, fx:DocumentType, fx:Version og fx:ConformanceLevel. De er nøyaktig de metadataene en leser trenger for å vite at denne PDF-en bærer en EN 16931-faktura med navnet factur-x.xml i versjon 1.0
Ingen av disse fire egenskapene er del av noe XMP-skjema som PDF/A definerer på forhånd. Identifikasjonsskjemaene Dublin Core, XMP Basic, PDF og PDF/A er kjent for en konform leser, men fx: er det ikke. Når veraPDF går gjennom XMP-en og når en egenskap hvis navnerom den ikke gjenkjenner, leter den etter en deklarasjon som forteller hva egenskapen betyr. Hvis den deklarasjonen mangler, rapporterer den en feil mot ISO 19005-3 punkt 6.6.2.3.1, som krever at enhver egenskap som ikke er hentet fra et forhåndsdefinert skjema, beskrives i et PDF/A-utvidelsesskjema. Fire udeklarerte egenskaper, fire måter filen kan bli avvist på, og ikke én av dem er synlig for en containerkontroll
Hvorfor PDF/A avviser en naken egendefinert egenskap
Regelen virker pedantisk helt til du husker hva PDF/A er til for. Formatet finnes for at en fil skal kunne åpnes og forstås tiår fram i tid, av programvare som aldri ble fortalt om konvensjonene fra 2026. En konform leser forventes å forstå dokumentet ut fra dokumentet alene, uten noe eksternt register å slå opp i
Egendefinerte metadata bryter det løftet med mindre filen bærer sin egen beskrivelse. Gitt en naken fx:ConformanceLevel-egenskap kan en framtidig leser ikke vite hvilken navneroms-URI prefikset fx binder til, om verdien er tekst, en dato eller et heltall, eller om egenskapen beskriver selve dokumentet eller en ekstern ressurs. Mekanismen med PDF/A-utvidelsesskjema lukker det gapet. Den lar filen deklarere, i en fast XMP-struktur, navnerommet, prefikset og for hver egenskap en verditype og en kategori av internal eller external. Når den deklarasjonen er til stede, er egenskapen selvbeskrivende, og punkt 6.6.2.3.1 er oppfylt. Uten den har validatoren ikke noe valg annet enn å behandle egenskapen som uforståelig og la filen stryke. Kategoriskillet er viktig her: fakturaegenskaper som disse beskriver data som kommer utenfra PDF-prosessoren, så de deklareres som external i stedet for internal
Hva utvidelsesskjema-deklarasjonen inneholder
Deklarasjonen er en rdf:Description i XMP-pakken som bruker de tre AIIM-definerte navnerommene pdfaExtension, pdfaSchema og pdfaProperty. Inne i en pdfaExtension:schemas-bag ligger én skjemaoppføring som navngir Factur-X-skjemaet, oppgir dets pdfaSchema:namespaceURI og pdfaSchema:prefix, og deretter lister opp de fire egenskapene i en pdfaSchema:property-sekvens. Hver egenskap bærer et navn, en pdfaProperty:valueType lik Text og en pdfaProperty:category lik external. Den illustrerende markeringen nedenfor viser formen på den blokken
<rdf:Description rdf:about=""
xmlns:pdfaExtension="http://www.aiim.org/pdfa/ns/extension/"
xmlns:pdfaSchema="http://www.aiim.org/pdfa/ns/schema#"
xmlns:pdfaProperty="http://www.aiim.org/pdfa/ns/property#">
<pdfaExtension:schemas>
<rdf:Bag>
<rdf:li rdf:parseType="Resource">
<pdfaSchema:schema>Factur-X PDFA Extension Schema</pdfaSchema:schema>
<pdfaSchema:namespaceURI>urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#</pdfaSchema:namespaceURI>
<pdfaSchema:prefix>fx</pdfaSchema:prefix>
<pdfaSchema:property>
<rdf:Seq>
<rdf:li rdf:parseType="Resource">
<pdfaProperty:name>DocumentFileName</pdfaProperty:name>
<pdfaProperty:valueType>Text</pdfaProperty:valueType>
<pdfaProperty:category>external</pdfaProperty:category>
<pdfaProperty:description>name of the embedded XML invoice file</pdfaProperty:description>
</rdf:li>
<!-- DocumentType, Version og ConformanceLevel deklareres på samme måte -->
</rdf:Seq>
</pdfaSchema:property>
</rdf:li>
</rdf:Bag>
</pdfaExtension:schemas>
</rdf:Description>
Navneroms-URI-en og prefikset er ikke faste strenger. De følger profilen. Et Factur-X-dokument bruker urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0# med prefikset fx, mens en ZUGFeRD 2.0-fil valgt gjennom zugferd-invoice.xml peker til en annen URI under sitt eget skjemanavn. Utvidelsesskjemaet må deklarere den samme navneroms-URI-en som egenskapsblokken faktisk bruker, ellers kan validatoren fortsatt ikke koble de to sammen. PDFlibPas utleder begge verdiene fra filnavnet og versjonen du sender inn, slik at deklarasjonen og egenskapsblokken alltid stemmer overens
Hvordan hjelperen skriver begge halvdelene samtidig
I PDFlibPas setter du ikke den XML-en sammen for hånd. Du setter dokumentet i en PDF/A-3-modus og kaller én metode. Det første som må avklares er konformansflagget, fordi Factur-X krever PDF/A-3. Å kalle SetPDFAMode(7) velger PDF/A-3u-nivået, som setter pdfaid:part til 3 og pdfaid:conformance til U i identifikasjonsskjemaet. XMP-pakken bærer nå riktig del og konformans før noen fakturametadata legges til
var
FileID: Integer;
begin
PDF.SetPDFAMode(7); // PDF/A-3u: pdfaid:part=3, conformance=U
PDF.NewDocument;
// tegn den menneskelesbare fakturasiden her
FileID := PDF.AddFacturXAssociatedFileFromString(
InvoiceXML, // rå UTF-8 XML-byte
'EN16931', // ConformanceLevel
'factur-x.xml', // innebygd filnavn
'Factur-X invoice XML', // /Desc-tekst
'Alternative', // /AFRelationship
'1.0', // profilversjon
''); // valgfri landkode
if FileID = 0 then
Exit; // ikke PDF/A-3, eller XML/profil stemmer ikke
PDF.SaveToFile('factur-x.pdf');
end;
Et enkelt kall til AddFacturXAssociatedFileFromString gjør arbeidet som den feilende filen manglet. Det bygger inn XML-en som en PDF/A-3-tilknyttet fil med relasjonen du navnga, og registrerer de fire fx-egenskapene sammen med skjemanavnet, navneroms-URI-en og prefikset for den valgte profilen. Når dokumentet lagres, injiserer et internt trinn ved navn ApplyFacturXMetadata både egenskapsblokken og den matchende pdfaExtension:schemas-deklarasjonen inn i XMP-pakken, slik at de egendefinerte egenskapene ankommer ferdig beskrevet. Metoden returnerer 0 hvis dokumentet ikke er i en PDF/A-3-modus, eller hvis XML-en ikke samsvarer med den deklarerte profilen, som er den samme vakten som hindrer en feilformet faktura i å nå filen i utgangspunktet
Den blindsonen containerkontrollen ikke kan se
Dette er delen som bør sies rett ut, fordi det er grunnen til at feilen gjemmer seg. ValidateFacturXInvoice kontrollerer containeren. Den bekrefter at katalogen har en /AF-oppføring, at navnetreet EmbeddedFiles er til stede, at faktura-XML-en finnes, at det innebygde filnavnet samsvarer med profilen, at retningslinje-ID-en i XML-en stemmer med konformansnivået, og at /AFRelationship er en som PDF/A-3 tillater. Dette er reelle kontroller, og de fanger reelle feil. GetFacturXValidationIssues rapporterer dem ved navn, med identifikatorer som MissingCatalogAF, NotPDFA3, ConformanceGuidelineMismatch, InvalidAFRelationship og InvalidFileNameProfile
Det den ikke kontrollerer, er om XMP-utvidelsesskjemaet er til stede og korrekt. En fil hvis container er feilfri, men hvis fx-egenskaper er udeklarerte, passerer hver eneste problemkontroll og returnerer 1, fordi ingenting i den listen inspiserer pdfaExtension:schemas-blokken. Det er nettopp derfor en håndbygd faktura, eller en produsert av en pipeline som skrev egenskapsblokken uten deklarasjonen, kan seile gjennom den innebygde validatoren og likevel stryke i veraPDF på punkt 6.6.2.3.1. Containervalidatoren og PDF/A-metadatavalidatoren svarer på ulike spørsmål, og bare den fullstendige PDF/A-kontrolløren svarer på det andre
Å lese problemer slik at du vet hvilket lag som sviktet
Fordi de to lagene svikter uavhengig av hverandre, er den riktige diagnosevanen å lese containerproblemene først og behandle et rent resultat som en uttalelse om containeren alene, aldri om PDF/A-metadata. Kjør den innebygde valideringen, samle inn problemlisten og handle på den før du griper til et eksternt verktøy
var
Issues: WideString;
begin
if PDF.ValidateFacturXInvoice = 0 then
begin
Issues := PDF.GetFacturXValidationIssues('|');
// identifikatorer på containernivå, for eksempel:
// MissingCatalogAF, NotPDFA3, MissingEmbeddedFilesNameTree,
// ConformanceGuidelineMismatch, InvalidAFRelationship
WriteLn('Container issues: ', Issues);
end
else
WriteLn('Container OK; verify XMP extension schema with a PDF/A checker.');
end;
Når det kallet returnerer et problemnavn, ligger feilen i containeren, og meldingen forteller deg hvilken del. Når det returnerer rent og veraPDF likevel avviser filen, ligger feilen nesten alltid i XMP-utvidelsesskjemaet, og løsningen er å la AddFacturXAssociatedFileFromString skrive metadataene i stedet for å konstruere egenskapsblokken selv. Å holde de to spørsmålene fra hverandre i ditt eget hode er det som gjør en forvirrende avvisning om til en éntimers diagnose: containerproblemer kommer til syne gjennom problemlisten, problemer med skjemadeklarasjonen kommer bare til syne gjennom en PDF/A-validator, og å blande de to er det som lar feilen gjemme seg
Det bredere bildet av PDF/A- og PDF/UA-konformans, inkludert hvordan du kjører en preflight-gjennomgang før en fil forlater bygget ditt, dekkes i gjennomgangen av PDF/A- og PDF/UA-preflight. Hvis fakturaen din også må være tilgjengelig, er strukturtreet som PDF/A-3a og tagget PDF avhenger av, emnet for artikkelen om tagget PDF og tilgjengelighet. Håndteringen av utvidelsesskjema som beskrives her, leveres som del av PDFlibPas Delphi PDF Library sammen med profilstøtten for Factur-X, ZUGFeRD og XRechnung som er dokumentert på tvers av denne bloggen