PDFium Component provjerava PDF/A Info-XMP ekvivalentnost metapodataka s TPdf.InspectPdfAMetadata i popravlja je s TPdf.NormalizePdfAMetadata. ISO 19005-1 (kako ga ispravlja Cor.1) traži da svaki od osam mapiranih Info unosa, od Title do ModDate, nosi istu vrijednost kao njegovo XMP svojstvo, ne samo da postoji; provjera čita točan RDF oblik, usklađuje namespaceove po URI-ju i uspoređuje datume kao trenutke
Bug izvještaj koji obično pokreće ovaj razgovor izgleda bezopasno. Sustav za upravljanje dokumentima utiskuje novi /ModDate u Info rječnik pri svakom inkrementalnom spremanju, XMP paket ostavlja na miru, i šest mjeseci kasnije arhivski audit obilježi tisuće datoteka kao nesukladne. Oba datuma su tu. Samo su prestali izjednačavati se kod prve izmjene, a provjera prisutnosti to nikad nije primijetila. Izmjene Titlea učinjene kroz Info-only API, i Author string poput Finance; Controlling koji je neki alat razdvojio u dva dc:creator unosa, padaju isto
Zašto PDF/A odbija metapodatke koji postoje na oba mjesta?
PDF/A to odbija jer je ISO 19005-1 §6.7.3 pravilo o vrijednosti, ne pravilo o prisutnosti: Tablica 1 preslikava osam Info ključeva na XMP svojstva, i jednom kad je Info ključ prisutan, mapirano XMP svojstvo mora nositi ekvivalentnu vrijednost. Skener na razini bajtova opisan u PDF/A preflight validaciji s PDFium Componentom samo potvrđuje da xmp:CreateDate i xmp:ModifyDate postoje (pvaiMissingXmpDates). Od v3.72.0 TPdf.ValidatePdfA dodatno izvodi potpunu usporedbu vrijednosti i dodaje pvaiInfoXmpValueMismatch u skup problema kad XMP paket postoji ali se ne slaže s Info (paket koji se ne može parsirati računa se kao neslaganje). Nepostojeći paket ostaje prijavljen kao pvaiMissingXmpMetadata, pa ta dva problema nikad ne dvostruko broje isti defekt
Koji RDF oblik svako mapirano XMP svojstvo treba?
Svako od osam mapiranja ima fiksni XMP tip, i točna vrijednost u krivom spremniku i dalje pada. ComparePdfAInfoAndXmp u FPdfPdfa.pas traži svojstva po namespace URI-ju, pa paket koji veže http://purl.org/dc/elements/1.1/ na neobičan prefiks čita se točno kao onaj koji koristi dc. Traženi oblici su:
- Title →
dc:titlei Subject →dc:description:rdf:Altjezična alternativa, uspoređivana samo s njenimx-defaultunosom (jezična oznaka poklapa se neosjetljivo na velika i mala slova); Alt bezx-defaultračuna se kao nepostojeći - Author →
dc:creator:rdf:Seqs točno jednim tekstualnim unosom koji drži cijeli Info string, pa autor popis odvojen točka-zarezom ostaje jedan unos - Keywords →
pdf:Keywordsi Producer →pdf:Producer(namespacehttp://ns.adobe.com/pdf/1.3/): jednostavna tekstualna svojstva - Creator →
xmp:CreatorTool, CreationDate →xmp:CreateDate, ModDate →xmp:ModifyDate(namespacehttp://ns.adobe.com/xap/1.0/): jednostavna tekstualna svojstva
<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>
Tekstualne vrijednosti uspoređuju se kao točni slijedovi Unicode code pointova, bez odrezivanja, pretvaranja velikih i malih slova ili normalizacije. Prazno mjesto na kraju, ili sklopljeni é s jedne strane i razloženi e plus kombinirajući akcenat s druge, pravi je nesuklad. Info strana uvijek dolazi iz PDFium vlastitog dekodiranja PDFDocEncoding i UTF-16 teksta kroz FPDF_GetMetaText, što biblioteku odvraća od ponovne implementacije dekodiranja stringova i suptilne greške; XMP strana je čista onoliko koliko su čisti bajtovi koji su je proizveli, zato i codepage zamke koje kvare XMP metapodatke pod Free Pascalom ovdje imaju veze
Kada su PDF datum i XMP datum jednaki?
PDF datum i XMP datum jednaki su kad opisuju isti trenutak do sekunde, s istim znanjem o vremenskoj zoni na objema stranama. Oba parsera primaju zakonitu smanjenu preciznost, pa D:2026 i 2026 oba znače 1. siječnja 2026, 00:00:00. Kad obje vrijednosti nose zonu, pretvaraju se u UTC prije usporedbe: D:20260827093659+08'00' jednako je 2026-08-27T01:36:59Z. Kad nijedna ne nosi zonu, lokalne komponente uspoređuju se kako su zapisane. Kad samo jedna strana ima zonu, rezultat je pamsValueMismatch, jer bi izmišljanje pomaka bilo pogađanje. Nenulta decimalna sekunda poput .250 u XMP-u također nameće nesuklad, jer PDF datum nema načina izraziti je, a tiho bi je zaokruživanje sakrilo stvarno neslaganje; .000 se prima. Neparabilne vrijednosti javljaju se odvojeno kao pamsInvalidInfoDate ili pamsInvalidXmpDate
Prisutnost ima vlastito pravilo. TPdfAMetadataValues.Present je skup koji se puni šetnjom kroz /Info rječnik aktivnog trailera, i on drži "ključ izostao" odvojeno od "ključ prisutan s praznim stringom". Izostali ključ daje pamsNotRequired i od XMP-a ništa ne traži; /Title () je prisutan, pa XMP paket mora nositi i prazan x-default naslov
Kako pregledati Info i XMP metapodatke prije spremanja?
TPdf.InspectPdfAMetadata vraća TPdfAMetadataReport s jednom TPdfAMetadataComparison po polju, svaka drži Info vrijednost, XMP vrijednost i TPdfAMetadataState, pa se pad može objasniti bez reverse-engineeringa pojedinačne validacijske zastavice. MismatchFields sažima padajući skup, HasXmpPacket kaže je li paket nađen, a XmpParseError nosi parserovu poruku kad paket postoji ali se ne može čitati
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;
Što NormalizePdfAMetadata mijenja, i što odbija?
TPdf.NormalizePdfAMetadata tretira Info rječnik kao izvor istine i prepravlja samo XMP svojstva čije je polje završilo u MismatchFields; sve ostalo u paketu preživi. Title i Subject upisuju se u x-default unos dok druge jezične alternative ostaju netaknute, Author postaje rdf:Seq s jednim unosom, nepoznati namespaceovi i nesrodna svojstva čuvaju se, a XMP svojstva za izostale Info ključeve ostavljaju se na miru. Datum Info s zonom piše se kao kanonski UTC XMP datum sa sufiksom Z; onaj bez zone zadržava svoje lokalne komponente. Overload s datotekom sprema kroz privremenu datoteku i atomsku zamjenu, a sama XMP izmjena dodaje se kao inkrementalni update
Odbijanja su namjerna. Bez XMP paketa metoda diže EPdfError, jer je gradnja potpunog PDF/A identificiranja i skupa metapodataka posao SaveAsPdfA, obrađen u stvaranju PDF/A arhivskih datoteka s PDFium Componentom. Neispravan Info datum diže EPdfXmpError umjesto da napiše uvjerljivo izgledajuću krivu vrijednost, i ništa se ne sprema. Potpisani dokumenti odbijaju se osim ako pozivatelj ne proslijedi AllowSignedDocument = True. Ekvivalentnost je i jedna od pravila ISO 19005-1, pa normalizirana datoteka nije automatski sukladna
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; // već usklađeno, datoteku ostavite na miru
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 // neispravan Info datum ili nečitljiv paket
raise Exception.CreateFmt('Cannot normalize %s: %s', [Source, E.Message]);
end;
finally
Pdf.Free;
end;
end;
Pokretanje usporedbe na vlastitom XMP paketu
ComparePdfAInfoAndXmp i SynchronizePdfAInfoToXmp obične su funkcije u FPdfPdfa koje rade nad TPdfXmpPacket bez učitanog dokumenta, što odgovara jediničnim testovima i pipelineima koji sklapaju XMP iz predloška. Jedna je zamka Present: zapis inicijaliziran s Default(TPdfAMetadataValues) ima prazan skup, svako polje tada javlja pamsNotRequired, i usporedba prolazi prazno bez obzira na vrijednosti koje ste upisali
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 odlučuje koja su polja obavezna; same vrijednosti se ignoriraju
Info.Present := [pamfTitle, pamfAuthor, pamfModDate];
Packet := TPdfXmpPacket.Parse(TFile.ReadAllText(TemplateFile, TEncoding.UTF8));
try
Changed := SynchronizePdfAInfoToXmp(Info, Packet);
// xmp:ModifyDate je sada 2026-08-27T01:36:59Z, dc:creator rdf:Seq s jednim unosom
if Changed <> [] then
TFile.WriteAllBytes(TemplateFile, Packet.ToUtf8);
finally
Packet.Free;
end;
end;
Ako Vaš pipeline arhivira dokumente koje drugi sustavi i dalje uređuju, spojite noćni prolaz InspectPdfAMetadata s NormalizePdfAMetadata za datoteke koje odmu, a ValidatePdfA držite kao kapiju prije nego bilo što otputuje u dugoročno spremanje. Tipizirani izvještaj, putanja popravke i ostatak PDF/A alata isporučuju se u PDFium Componentu za Delphi i C++Builder