PDFium Component preverja enakovrednost metapodatkov Info-XMP za PDF/A s TPdf.InspectPdfAMetadata in jo popravlja s TPdf.NormalizePdfAMetadata. ISO 19005-1 (kot jo je popravil Cor.1) zahteva, da vsak od osmih preslikanih vnosov Info, od Title do ModDate, nosi isto vrednost kot njegova lastnost XMP, ne le da obstaja; preizkus prebere pravilno obliko RDF, imenske prostore ujema po URI in datume primerja kot trenutke
Poročilo o hrošču, ki ponavadi začne ta pogovor, izgleda nenevarno. Sistem za upravljanje dokumentov žigi nov /ModDate v slovar Info ob vsakem inkrementalnem shranjevanju, paket XMP pusti pri miru, pol leta pozneje pa arhivska revizija označi tisoče datotek kot neskladne. Oba datuma sta tam. Pravkar sta prenehala sovpadati ob prvi spremembi in preizkus prisotnosti tega nikoli ni opazil. Spremembe naslova, narejene prek API-ja samo za Info, in niz Author, kot je Finance; Controlling, ki ga je kakšno orodje razcepilo v dva vnosa dc:creator, spodletijo enako
Zakaj PDF/A zavrne metapodatke, ki obstajajo na obeh mestih?
PDF/A jo zavrne, ker je ISO 19005-1 §6.7.3 pravilo vrednosti in ne pravilo prisotnosti: Tabela 1 preslika osem ključev Info na lastnosti XMP in ko je ključ Info enkrat prisoten, mora preslikana lastnost XMP nositi enakovredno vrednost. Bajtni pregledovalnik, opisan v validaciji PDF/A preflight s komponento PDFium Component, le potrdi, da xmp:CreateDate in xmp:ModifyDate obstajata (pvaiMissingXmpDates). Od v3.72.0 TPdf.ValidatePdfA dodatno požene polno primerjavo vrednosti in doda pvaiInfoXmpValueMismatch k nizu težav, kadar paket XMP obstaja, a se ne strinja z Info (paket, ki ga ni mogoče razčleniti, šteje kot nesoglasje). Manjkajoči paket ostane prijavljen kot pvaiMissingXmpMetadata, tako da ti dve težavi nikoli ne preštejeta iste napake
Katero obliko RDF potrebuje vsaka preslikana lastnost XMP?
Vsaka od osmih preslikav ima fiksni tip XMP in pravilna vrednost v napačnem vsebniku vseeno spodleti. ComparePdfAInfoAndXmp v FPdfPdfa.pas lastnosti poišče po URI imenskega prostora, zato je paket, ki veže http://purl.org/dc/elements/1.1/ na nenavadno predpono, prebran točno tako kot tisti, ki uporablja dc. Zahtevane oblike so:
- Title →
dc:titlein Subject →dc:description: jezikovna alternativardf:Alt, primerjana le s svojim vnosomx-default(jezikovna oznaka se ujema brez razlikovanja velikih in malih črk); Alt brezx-defaultse šteje za manjkajočega - Author →
dc:creator:rdf:Seqs točno enim besedilnim vnosom, ki nosi celoten niz Info, tako da seznam avtorjev, ločenih s podpičji, ostane en sam vnos - Keywords →
pdf:Keywordsin Producer →pdf:Producer(namespacehttp://ns.adobe.com/pdf/1.3/): preproste besedilne lastnosti - Creator →
xmp:CreatorTool, CreationDate →xmp:CreateDate, ModDate →xmp:ModifyDate(namespacehttp://ns.adobe.com/xap/1.0/): preproste besedilne lastnosti
<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>
Besedilne vrednosti so primerjane kot točna zaporedja kodnih točk Unicode, brez obrezovanja, zlaganja velikosti ali normalizacije. Presledek na koncu ali vnaprej sestavljeno é na eni strani in razstavljeno e z združevalnim naglasom na drugi je pravo nesorazmerje. Stran Info vedno prihaja iz lastnega dekodiranja PDFium besedil PDFDocEncoding in UTF-16 prek FPDF_GetMetaText, kar knjižnici odvzame, da bi znova implementirala dekodiranje nizov in se subtilno zmotila; stran XMP je čista le toliko, kot so bajti, ki so jo ustvarili, zato pasti kodnih strani, ki pod Free Pascal pokvarijo metapodatke XMP, tu štejejo prav tako
Kdaj sta datum PDF in datum XMP enaka?
Datum PDF in datum XMP sta enaka, ko opisujeta isti trenutek do sekunde, z istim znanjem o časovnem pasu na obeh straneh. Oba razčlenjevalnika sprejmeta zakonito zmanjšano natančnost, zato D:2026 in 2026 oba pomenita 1. januar 2026, 00:00:00. Ko obe vrednosti nosita pas, sta pred primerjavo pretvorjeni v UTC: D:20260827093659+08'00' je enako 2026-08-27T01:36:59Z. Ko nobena ne nosi pasu, so lokalne komponente primerjane tako, kot so zapisane. Ko le ena stran ima pas, je rezultat pamsValueMismatch, ker bi izmišljanje odmika bilo ugibanje. Necel del sekunde, ki ni nič, kot je .250 v XMP, prav tako vsili nesorazmerje, ker ga datum PDF ne zna izraziti in bi ga tiho zaokroževanje proč skrilo pravo nesoglasje; .000 je sprejet. Nerazčlenljive vrednosti so prijavljene ločeno kot pamsInvalidInfoDate ali pamsInvalidXmpDate
Prisotnost ima svoje pravilo. TPdfAMetadataValues.Present je množica, napolnjena z obhodom slovarja /Info dejavnega trailerja, in ločuje »ključ manjka« od »ključ prisoten s praznim nizom«. Manjkajoči ključ da pamsNotRequired in od XMP ne zahteva ničesar; /Title () je prisoten, zato mora paket XMP nositi tudi prazen naslov x-default
Kako pregledate metapodatke Info in XMP, preden shranite?
TPdf.InspectPdfAMetadata vrne TPdfAMetadataReport z enim TPdfAMetadataComparison na polje, vsak nosi vrednost Info, vrednost XMP in TPdfAMetadataState, tako da spodletelost lahko razložite brez povratnega inženiringa posamezne validacijske zastavice. MismatchFields povzame spodleteli nabor, HasXmpPacket pove, ali je bil paket najden, XmpParseError pa nosi sporočilo razčlenjevalnika, kadar paket obstaja, a ga ni mogoče brati
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;
Kaj NormalizePdfAMetadata spremeni in česa odklanja?
TPdf.NormalizePdfAMetadata slovar Info obravnava kot vir resnice in prepisuje le lastnosti XMP, katerih polje je pristalo v MismatchFields; vse ostalo v paketu preživi. Title in Subject sta zapisana v vnos x-default, medtem ko ostanejo druge jezikovne alternative nedotaknjene, Author postane enovnosni rdf:Seq, neznani imenski prostori in nesorodne lastnosti so ohranjene, lastnosti XMP za manjkajoče ključe Info pa ostanejo pri miru. Datum Info s časovnim pasom je zapisan kot kanonični datum XMP UTC s pripono Z; tisti brez pasu obdrži svoje lokalne komponente. Preobremenitev za datoteko shrani skozi začasno datoteko in atomsko zamenjavo, posodobitev XMP sama pa se pripne kot inkrementalna posodobitev
Zavrnitve so bile namerne. Brez paketa XMP metoda sproži EPdfError, ker je gradnja popolne identifikacije PDF/A in nabora metapodatkov naloga SaveAsPdfA, pokrite v ustvarjanju arhivskih datotek PDF/A s komponento PDFium Component. Nepravilno oblikovan datum Info sproži EPdfXmpError, namesto da bi zapisal verjetno videčo napačno vrednost, in nič ni shranjeno. Podpisani dokumenti so zavrnjeni, razen če klicnik podate AllowSignedDocument = True. Enakovrednost je eno izmed pravil ISO 19005-1, normalizirana datoteka pa zato ni samodejno skladna
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; // že usklajeno, datoteko pustite pri 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 // nepravilno oblikovan datum Info ali neberljiv paket
raise Exception.CreateFmt('Cannot normalize %s: %s', [Source, E.Message]);
end;
finally
Pdf.Free;
end;
end;
Zagon primerjave na lastnem paketu XMP
ComparePdfAInfoAndXmp in SynchronizePdfAInfoToXmp sta navadni funkciji v FPdfPdfa, ki delujeta na TPdfXmpPacket brez naloženega dokumenta, kar ustreza enotskim testom in cevovodom, ki sestavljajo XMP iz predloge. Ena sama past je Present: zapis, inicializiran s Default(TPdfAMetadataValues), ima prazno množico, vsako polje potem poroča pamsNotRequired in primerjava gre skozi povsem prazno, ne glede na to, katere vrednosti ste vnesli
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 odloča, katera polja so obvezna; same vrednosti so ignorirane
Info.Present := [pamfTitle, pamfAuthor, pamfModDate];
Packet := TPdfXmpPacket.Parse(TFile.ReadAllText(TemplateFile, TEncoding.UTF8));
try
Changed := SynchronizePdfAInfoToXmp(Info, Packet);
// xmp:ModifyDate je zdaj 2026-08-27T01:36:59Z, dc:creator pa enovnosni rdf:Seq
if Changed <> [] then
TFile.WriteAllBytes(TemplateFile, Packet.ToUtf8);
finally
Packet.Free;
end;
end;
Če Vaš cevovod arhivira dokumente, ki jih drugi sistemi kar naprej urejajo, združite nočni prehod InspectPdfAMetadata s NormalizePdfAMetadata za datoteke, ki zdrsnijo, ValidatePdfA pa ohranite kot pregrado, preden karkoli odpotuje v dolgoročno hrambo. Tipizirano poročilo, pot popravila in preostalo orodjarino PDF/A pošilja PDFium Component za Delphi in C++Builder