PDFium Component sjekker PDF/A Info-til-XMP-metadataekvivalens med TPdf.InspectPdfAMetadata og reparerer den med TPdf.NormalizePdfAMetadata. ISO 19005-1 (som korrigert av Cor.1) krever at hver av de åtte kartlagte Info-oppføringene, Title gjennom ModDate, bærer samme verdi som sin XMP-egenskap, ikke bare at den finnes; sjekken leser den korrekte RDF-formen, matcher navnerom etter URI og sammenligner datoer som tidspunkter
Feilrapporten som vanligvis starter denne samtalen, ser ufarlig ut. Et dokumenthåndteringssystem stempler en ny /ModDate inn i Info-ordboken ved hver inkrementelle lagring, lar XMP-pakken være i fred, og et halvt år senere flagger en arkivrevisjon tusenvis av filer som ikke-konforme. Begge datoene er der. De sluttet bare å være enige ved den første redigeringen, og en tilstedeværelsessjekk la aldri merke til det. Tittelredigeringer gjort gjennom et kun-Info-API, og en Author-streng som Finance; Controlling som et eller annet verktøy delte i to dc:creator-elementer, feiler på samme måte
Hvorfor avviser PDF/A-metadata som finnes begge steder?
PDF/A avviser den fordi ISO 19005-1 §6.7.3 er en verdiregel, ikke en tilstedeværelsesregel: Tabell 1 mapper åtte Info-nøkler til XMP-egenskaper, og så snart en Info-nøkkel er til stede, må den kartlagte XMP-egenskapen holde en tilsvarende verdi. Byteskanneren beskrevet i PDF/A-preflight-validering med PDFium Component bekrefter bare at xmp:CreateDate og xmp:ModifyDate finnes (pvaiMissingXmpDates). Siden v3.72.0 kjører TPdf.ValidatePdfA i tillegg den fulle verdisammenligningen og legger pvaiInfoXmpValueMismatch til problemsettet når en XMP-pakke finnes, men er uenig med Info (en pakke som ikke kan parses, teller som uenig). En manglende pakke forblir rapportert som pvaiMissingXmpMetadata, så de to problemene teller aldri samme defekt dobbelt
Hvilken RDF-form trenger hver kartlagt XMP-egenskap?
Hver av de åtte mappene har en fast XMP-type, og en korrekt verdi i feil beholder feiler fortsatt. ComparePdfAInfoAndXmp i FPdfPdfa.pas slår egenskaper opp etter navneroms-URI, så en pakke som binder http://purl.org/dc/elements/1.1/ til en uvanlig prefiks, leses nøyaktig som én som bruker dc. De påkrevde formene er:
- Title →
dc:titleog Subject →dc:description: etrdf:Altspråkalternativ, sammenlignet bare motx-default-elementet sitt (språktaggen matches uten hensyn til store og små bokstaver); en Alt utenx-defaultteller som manglende - Author →
dc:creator: enrdf:Seqmed nøyaktig ett tekstelement som holder hele Info-strengen, så en semikolonadskilt forfatterliste forblir én enkelt oppføring - Keywords →
pdf:Keywordsog Producer →pdf:Producer(navneromhttp://ns.adobe.com/pdf/1.3/): enkle teksteienskaper - Creator →
xmp:CreatorTool, CreationDate →xmp:CreateDate, ModDate →xmp:ModifyDate(navneromhttp://ns.adobe.com/xap/1.0/): enkle teksteienskaper
<dc:title><rdf:Alt><rdf:li xml:lang="x-default">Quarterly Report 2026</rdf:li></rdf:Alt></dc:title>
<dc:creator><rdf:Seq><rdf:li>Finance; Controlling</rdf:li></rdf:Seq></dc:creator>
<pdf:Producer>PDFium Component</pdf:Producer>
Tekstverdier sammenlignes som eksakte Unicode-kodesekvenser, uten trimming, caseneutralisering eller normalisering. Et avsluttende mellomrom, eller en prekomponert é på den ene siden og en dekomponert e pluss kombinerende aksent på den andre, er et genuint avvik. Info-siden kommer alltid fra PDFiums egen dekoding av PDFDocEncoding og UTF-16-tekst gjennom FPDF_GetMetaText, noe som hindrer biblioteket i å re-implementere strengdekoding og ta feil på en subtil måte; XMP-siden er bare like ren som bytene som produserte den, og det er derfor kode sidefellene som korruperer XMP-metadata under Free Pascal også gjelder her
Når er en PDF-dato og en XMP-dato like?
En PDF-dato og en XMP-dato er like når de beskriver samme tidspunkt ned til sekundet, med samme tidssonekunnskap på begge sider. Begge parserne godtar lovlig redusert presisjon, så D:2026 og 2026 betyr begge 1. januar 2026, 00:00:00. Når begge verdier bærer en sone, konverteres de til UTC før sammenligning: D:20260827093659+08'00' er lik 2026-08-27T01:36:59Z. Når ingen av dem bærer en sone, sammenlignes de lokale komponentene som skrevet. Når bare den ene siden har en sone, blir resultatet pamsValueMismatch, fordi å finne opp en offset ville være å gjette. En ikkesnull fraksjonell sekund som .250 i XMP tvinger også frem et avvik, siden en PDF-dato ikke har noen måte å uttrykke det på, og å runde det stille bort ville skjule en reell uenighet; .000 godtas. Uparsebare verdier rapporteres separat som pamsInvalidInfoDate eller pamsInvalidXmpDate
Tilstedeværelse har sin egen regel. TPdfAMetadataValues.Present er et sett fylt ved å gå gjennom den aktive traileren /Info-ordbok, og det holder «nøkkel fraværende» adskilt fra «nøkkel til stede med en tom streng». En fraværende nøkkel gir pamsNotRequired og krever ingenting fra XMP; /Title () er til stede, så XMP-pakken må bære en tom x-default-tittel også
Hvordan inspiserer du Info- og XMP-metadata før lagring?
TPdf.InspectPdfAMetadata returnerer en TPdfAMetadataReport med én TPdfAMetadataComparison per felt, hver med Info-verdien, XMP-verdien og en TPdfAMetadataState, så et feil kan forklares uten å reversere en enkelt valideringsflagg. MismatchFields oppsummerer det feilende settet, HasXmpPacket forteller deg om en pakke ble funnet, og XmpParseError bærer parsermeldingen når pakken finnes, men ikke kan leses
uses
System.SysUtils, PDFium, FPdfPdfa;
const
FieldNames: array[TPdfAMetadataField] of string = (
'Title', 'Author', 'Subject', 'Keywords',
'Creator', 'Producer', 'CreationDate', 'ModDate');
StateNames: array[TPdfAMetadataState] of string = (
'not required', 'equivalent', 'XMP missing', 'XMP type mismatch',
'value mismatch', 'invalid Info date', 'invalid XMP date');
procedure ReportMetadata(Pdf: TPdf);
var
Report: TPdfAMetadataReport;
Item: TPdfAMetadataComparison;
begin
Report := Pdf.InspectPdfAMetadata;
if Report.XmpParseError <> '' then
Writeln('XMP packet unreadable: ', Report.XmpParseError)
else if not Report.HasXmpPacket then
Writeln('No XMP packet at all');
for Item in Report.Comparisons do
if not Item.IsEquivalent then
Writeln(Format('%-12s %-18s Info="%s" XMP="%s"',
[FieldNames[Item.Field], StateNames[Item.State],
Item.InfoValue, Item.XmpValue]));
end;
Hva endrer NormalizePdfAMetadata, og hva nekter den?
TPdf.NormalizePdfAMetadata behandler Info-ordboken som sannhetskilden og skriver bare om XMP-egenskapene hvis felt havnet i MismatchFields; alt annet i pakken overlever. Title og Subject skrives inn i x-default-elementet mens andre språkalternativer forblir intakte, Author blir en rdf:Seq med ett element, ukjente navnerom og urelaterte egenskaper bevares, og XMP-egenskaper for fraværende Info-nøkler røres ikke. En datert Info-dato med sone skrives som en kanonisk UTC XMP-dato med Z-suffiks; en soneløs beholder de lokale komponentene sine. Fil-overloaden lagrer gjennom en midlertidig fil og en atomær erstatning, og selve XMP-oppdateringen legges til som en inkrementell oppdatering
Avslagene er bevisste. Uten XMP-pakke kaster metoden EPdfError, fordi å bygge et komplett PDF/A-identifiserings- og metadatasett er SaveAsPdfA sin jobb, dekket i å lage PDF/A-arkivfiler med PDFium Component. En feilformet Info-dato kaster EPdfXmpError i stedet for å skrive en plausibel utseende feil verdi, og ingenting lagres. Signerte dokumenter avvises med mindre kalleren sender AllowSignedDocument = True. Ekvivalens er forresten selv én regel i ISO 19005-1, så en normalisert fil er ikke automatisk en konform en
uses
System.SysUtils, PDFium, FPdfPdfa, FPdfXmp;
procedure NormalizeArchive(const Source, Target: string);
var
Pdf: TPdf;
Report: TPdfAMetadataReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := Source;
Pdf.Active := True;
Report := Pdf.InspectPdfAMetadata;
if Report.IsEquivalent then
Exit; // allerede konsistent, la filen være i fred
if not Report.HasXmpPacket then
raise Exception.Create('No XMP packet: convert with SaveAsPdfA instead');
try
if not Pdf.NormalizePdfAMetadata(Target) then
raise Exception.Create('Normalized save failed');
except
on E: EPdfXmpError do // feilformet Info-dato eller uleselig pakke
raise Exception.CreateFmt('Cannot normalize %s: %s', [Source, E.Message]);
end;
finally
Pdf.Free;
end;
end;
Kjøre sammenligningen på din egen XMP-pakke
ComparePdfAInfoAndXmp og SynchronizePdfAInfoToXmp er rene funksjoner i FPdfPdfa som jobber på en TPdfXmpPacket uten dokument lastet, noe som passer enhetstester og pipelines som setter sammen XMP fra en mal. Den ene fellen er Present: en record initialisert med Default(TPdfAMetadataValues) har et tomt sett, hvert felt rapporterer da pamsNotRequired, og sammenligningen består vakuert uansett hvilke verdier du fylte inn
uses
System.SysUtils, System.IOUtils, FPdfPdfa, FPdfXmp;
procedure AlignTemplate(const TemplateFile: string);
var
Info: TPdfAMetadataValues;
Packet: TPdfXmpPacket;
Changed: TPdfAMetadataFields;
begin
Info := Default(TPdfAMetadataValues);
Info.Title := 'Quarterly Report 2026';
Info.Author := 'Finance; Controlling';
Info.ModDate := 'D:20260827093659+08''00''';
// Present avgjør hvilke felt som er obligatoriske; verdier alene ignoreres
Info.Present := [pamfTitle, pamfAuthor, pamfModDate];
Packet := TPdfXmpPacket.Parse(TFile.ReadAllText(TemplateFile, TEncoding.UTF8));
try
Changed := SynchronizePdfAInfoToXmp(Info, Packet);
// xmp:ModifyDate er nå 2026-08-27T01:36:59Z, dc:creator en rdf:Seq med ett element
if Changed <> [] then
TFile.WriteAllBytes(TemplateFile, Packet.ToUtf8);
finally
Packet.Free;
end;
end;
Arkiverer pipelinen din dokumenter andre systemer fortsetter å redigere, parr en nattlig InspectPdfAMetadata-svøp med NormalizePdfAMetadata for filene som driver, og hold ValidatePdfA som gaten før noe drar til langtidslagring. Det typede rapporten, reparasjonsstien og resten av PDF/A-verktøyene følger med i PDFium Component for Delphi og C++Builder