Imate deset hiljada ugovornih PDF-ova iz tuceta različitih generatora, a pravni tim želi da svaki od njih nosi tačan Author, ispravljeni Producer string i režim čitanja koji pri pokretanju otvara panel obeleživača. Naivno rešenje je da učitate svaku datoteku, ponovo rasporedite stranice i zapišete novi dokument. Ako to uradite, bacili ste svaki postojeći broj objekta, istoriju inkrementalnih ažuriranja, svaki digitalni potpis i pažljivo podešen xref koji je originalni alat izdao. Stranice izgledaju isto, a datoteka je strukturalno stranac. Za uređivanje metapodataka to je potpuno pogrešna razmena
Pravi potez je da učitani dokument tretirate kao graf objekata koji menjate na mestu: posegnite u Info rečnik, /Metadata stream i Catalog, izmenite nekoliko unosa koji vas zanimaju i vratite rezultat nazad. HotPDF, nativna VCL PDF komponenta za Delphi i C++Builder, izlaže upravo taj sloj kroz svoj loaded-document write API. Ovaj članak govori o tome kako da ga koristite ispravno, i o jednoj grešci koju gotovo svi prave: uređuju Info rečnik i zaborave da druga kopija istih metapodataka živi u XMP-u
Dve lokacije čuvaju iste metapodatke i ne slažu se
PDF čuva informacije o dokumentu na dve paralelne lokacije, i to je koren većine prijava tipa "Promenio sam naslov, ali Acrobat i dalje prikazuje stari". Prva je rečnik informacija o dokumentu, klasični /Info objekt sa /Title, /Author, /Subject, /Keywords, /Creator i /Producer ključevima, definisanim u ISO 32000-1 §14.3.3. Druga je XMP paket, XML dokument sačuvan kao stream nakačen na Catalog pod /Metadata, definisan u §14.3.2 i zasnovan na Adobe XMP modelu podataka
Oba mogu da sadrže naslov. Ništa u specifikaciji ih ne tera da se slažu. Savremeni pregledači i većina PDF/A validatora daju prednost XMP paketu kada je prisutan, a vraćaju se na Info rečnik kada nije. Zato, ako ažurirate samo /Info - što radi velika većina koda za „postavljanje PDF metapodataka“ - čitač koji veruje XMP-u i dalje će prikazivati zastarelu vrednost, a PDF/A provera će prijaviti neusklađenost. Ispravna operacija na svakoj datoteci koja već ima XMP paket jeste dvostruko pisanje: promenite Info unos i regenerišite XMP, tako da ta dva prikaza ostanu usklađena. HotPDF vam daje obe polovine; disciplina da ih koristite zajedno je na vama
Uređivanje Info rečnika
Info pomoćne funkcije su tanke i predvidljive. SetLoadedTitle, SetLoadedAuthor, SetLoadedSubject, SetLoadedKeywords, SetLoadedCreator, i SetLoadedProducer svaka uzima jedan AnsiString i upisuje odgovarajući ključ u učitani Info rečnik, zamenjujući vrednost ako ključ postoji i dodajući je ako ne postoji. Da biste potpuno uklonili ključ - recimo curavi /Creator koji imenuje vaše interne alate - pozovite RemoveLoadedInfoKey sa samim nazivom ključa. Nijedna od ovih funkcija ne dira XMP; one rade isključivo nad /Info objektom koji je LoadFromFile pronašao kada je parsirao datoteku
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('contract-in.pdf', '') > 0 then
begin
Pdf.SetLoadedTitle('Master Services Agreement 2026');
Pdf.SetLoadedAuthor('Legal Department');
Pdf.SetLoadedSubject('Executed contract, retention 7 years');
Pdf.SetLoadedKeywords('contract; MSA; 2026; executed');
Pdf.SetLoadedProducer('Acme Document Pipeline');
Pdf.RemoveLoadedInfoKey('Creator'); // drop the originating tool name
Pdf.SaveLoadedDocument('contract-out.pdf');
end;
finally
Pdf.Free;
end;
end;
Jedan detalj treba držati ispravnim: ove funkcije uzimaju AnsiString. Za ASCII naslove to nije problem, ali PDF tekstualni stringovi kojima trebaju nelatinični znakovi moraju biti kodirani kako specifikacija nalaže - UTF-16BE sa BOM-om ili PDFDocEncoding - pre nego što ih prosledite. Biblioteka upisuje bajtove koje joj date u string objekat; ne pogađa kodiranje umesto vas. Ako su vaši naslovi običan engleski, ovo zanemarite. Ako sadrže akcentovane ili CJK znakove, kodirajte ih namerno i testirajte u pravom pregledaču
Prepisivanje XMP paketa
SetLoadedXMPMetadata je druga polovina dvostrukog upisa. Prosledite mu ceo XMP paket kao AnsiString i on radi jednu od dve stvari: ako se Catalog već poziva na /Metadata stream, on mu sadržaj zamenjuje na mestu, uz zadržavanje istog broja objekta; ako metadata stream ne postoji, kreira ga, označava ga /Type /Metadata i /Subtype /XML, dodeljuje broj objekta i povezuje ga iz Catalog-a. U oba slučaja završavate sa ispravnim metapodatkovnim objektom koji će pregledači pročitati
Vi obezbeđujete XML, što znači da vi kontrolišete šemu - dc:title, dc:creator, xmp:CreatorTool, i tako dalje. To je i moć i odgovornost u jednom: biblioteka ne parsira niti validira vaš paket, a bajtove upisuje nekompresovane, bez primenjenog stream filtera. Neispravan paket će proći kroz poziv i kasnije isplivati kao žalba na pokvarene metapodatke. Gradite XML pažljivo i tačno preslikajte vrednosti koje ste upisali u Info rečnik, da se ta dva prikaza nikada ne protivreče jedan drugom
const
XMP_TEMPLATE =
'<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>' +
'<x:xmpmeta xmlns:x="adobe:ns:meta/">' +
'<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' +
'<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">' +
'<dc:title><rdf:Alt><rdf:li xml:lang="x-default">%s</rdf:li></rdf:Alt></dc:title>' +
'<dc:creator><rdf:Seq><rdf:li>%s</rdf:li></rdf:Seq></dc:creator>' +
'</rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="w"?>';
begin
// After setting the Info dictionary, mirror the same values into XMP:
Pdf.SetLoadedTitle('Master Services Agreement 2026');
Pdf.SetLoadedAuthor('Legal Department');
Pdf.SetLoadedXMPMetadata(
AnsiString(Format(XMP_TEMPLATE,
['Master Services Agreement 2026', 'Legal Department'])));
Pdf.SaveLoadedDocument('contract-out.pdf');
end;
Taj redosled - Info prvo, XMP drugo, pa čuvanje - obrazac je koji treba zapamtiti. Ta dva poziva su nezavisna; usklađenost postoji samo zato što ste im dali iste stringove. Preskočite XMP poziv na datoteci koja ima XMP paket i vraćate se na grešku tihog zastarevanja koju ceo ovaj odeljak postoji da spreči

Podešavanje načina na koji se datoteka otvara u pregledaču
Tri Catalog unosa određuju šta čitalac vidi čim se dokument otvori, i sva tri su jednostruke izmene na učitanom grafu. SetLoadedPageMode upisuje /PageMode kao name object: prosledite 'UseOutlines' da biste otvorili panel obeleživača, 'UseThumbs' za traku s minijaturama, 'FullScreen' za režim prezentacije, ili 'UseAttachments' da prikažete panel sa prilozima (ISO 32000-1 §7.7.3.1, Tabela 28). SetLoadedPageLayout upisuje /PageLayout na isti način - 'SinglePage', 'OneColumn', 'TwoColumnLeft' i ostalo. Obe funkcije primaju naziv bez vodeće kose crte; biblioteka je dodaje pri izlazu
SetLoadedLanguage upisuje Catalog /Lang unos, prirodnojezičku oznaku za ceo dokument - 'en-US', 'de-DE', oznaku BCP 47. Obratite pažnju na razliku u tipu koja ljude zbunjuje: /PageMode i /PageLayout su PDF name objekti, dok je /Lang string. HotPDF to interno radi ispravno, ali ako ikad pregledate izlaz, videćete /PageMode /UseOutlines naspram /Lang (en-US), i sada znate zašto. /Lang unos je važniji nego što izgleda: to je ono što asistivna tehnologija čita da bi izabrala izgovor, i to je strogi uslov za PDF/UA usklađenost pristupačnosti
if Pdf.LoadFromFile('handbook.pdf', '') > 0 then
begin
Pdf.SetLoadedPageMode('UseOutlines'); // /PageMode, a name
Pdf.SetLoadedPageLayout('TwoColumnLeft'); // /PageLayout, a name
Pdf.SetLoadedLanguage('en-US'); // /Lang, a string
Pdf.SaveLoadedDocument('handbook-tagged.pdf');
end;
Preimenovanje obeleživača bez uznemiravanja stabla
Naslovi obeleživača su rutinsko sređivanje - tipfeler u zaglavlju, poglavlje prebrojano nakon što je outline napravljen. SetLoadedOutlineTitle uzima indeks od nule u gornjeg nivoa outline unosa i novi naslov, prolazi kroz Catalog → /Outlines → /First → /Next lanac do te pozicije i zamenjuje string unosa /Title. Menja samo naslov; odredište, stanje otvorenosti/zatvorenosti i struktura dece ostaju netaknuti
if Pdf.LoadFromFile('report.pdf', '') > 0 then
begin
Pdf.SetLoadedOutlineTitle(0, 'Executive Summary');
Pdf.SetLoadedOutlineTitle(1, 'Financial Results');
Pdf.SaveLoadedDocument('report-renamed.pdf');
end;
Preimenovanje je bezbedno upravo zato što nikada ne dira strukturalne brojače. Brisanje outline unosa je slučaj koji pravi probleme i vredno ga je razumeti čak i kada samo preimenujete, jer pokazuje šta ne treba ručno uređivati. Svaki outline čvor nosi /Count, a - po ISO 32000-1 §12.3.3 - taj broj nije broj neposredne dece. To je ukupan broj vidljivih potomaka: pozitivan /Count od N znači da je N potomaka trenutno prikazano, dok negativna vrednost znači da čvor ima potomke ali je skupljen. Kada se gornji unos ukloni, /Outlines korenski broj ne može jednostavno da se umanji za jedan; mora da se preračuna sabiranjem, za svaki preostali unos gornjeg nivoa, "jedan za sam čvor plus njegov pozitivan /Count," uz preskakanje potomaka bilo kog skupljenog (negativnog) čvora. Ako to pogrešite, ukupan broj obeleživača koji čitalac prikazuje odluta - skoči za više od jedan po brisanju. Preimenovanje zaobilazi sve ovo, što je još jedan razlog da se odlučite za ciljanu pomoćnu funkciju umesto da sami čačkate rečnik
Kako čuvanje ostaje na mestu
Svaka izmena iznad menja objekte u memoriji; ništa ne stiže na disk sve dok SaveLoadedDocument ne krene. Razlog što je ovaj pristup jeftin jeste to što save ne regeneriše dokument - čuva postojeće brojeve objekata i strukturu koju je HotPDF parsirao pri učitavanju, vraćajući isti graf sa vašom šakom izmenjenih i novo dodeljenih objekata. To je ono što sprečava da prolaz metapodataka prepiše celu datoteku, i to je ista mašinerija za in-place update koja omogućava object streams and incremental updates da rade. Ako vam izvorne datoteke dolaze iz Word-a ili nekog drugog office paketa, njihov raspored objekata ima svoje specifičnosti koje vredi znati pre nego što ih uređujete; članak o hybrid-reference cross-reference streams in Office PDFs objašnjava kako su te datoteke strukturirane i šta preživljava round trip
Dve granice treba poštovati. Prvo, ovo je model uređivanja na mestu, a ne alat za redakciju ili sanitizaciju: uklanjanje Info ključa uklanja taj ključ, ali ne čisti starije vrednosti koje mogu ostati u prethodnoj generaciji inkrementalnog ažuriranja iste datoteke. Ako vam je potreban istinski nestanak osetljivih metapodataka, to je druga, teža operacija. Drugo, XMP upis je doslovan - biblioteka veruje vašem XML-u i ne validira ga - pa za sve što je namenjeno PDF/A ili strogom validatoru, generišite paket iz poznato dobrog šablona i proverite izlaz. Kada se koristi u tim okvirima, uređivanje metapodataka na mestu je alat prave veličine: popravlja nekoliko bajtova koji nisu ispravni i ostavlja devedeset devet procenata datoteke koji je već bio ispravan tačno onako kako ga je originalni proizvođač zapisao
API za upis učitanog dokumenta prikazan ovde isporučuje se sa standardnim HotPDF Component za Delphi i C++Builder, zajedno sa punim skupom metoda za uređivanje metapodataka, outline-a i Catalog-a