Imate deset tisuća ugovornih PDF-ova iz desetak različitih generatora, a pravni odjel želi da svaki od njih nosi ispravan Author, ispravljeni Producer string i način otvaranja koji pri pokretanju otvara panel knjižnih oznaka. Naivno rješenje je učitati svaku datoteku, ponovno složiti stranice i zapisati novi dokument. To znači da ste upravo odbacili svaki postojeći broj objekta, povijest inkrementalnih ažuriranja, svaki digitalni potpis i pažljivo podešeni xref koji je izvorni alat proizveo. Stranice izgledaju isto, a datoteka je strukturno tuđa. Za uređivanje metapodataka to je posve pogrešan kompromis
Ispravan je potez tretirati učitani dokument kao graf objekata koji mijenjate na mjestu: posegnuti u Info rječnik, u /Metadata tok i katalog, promijeniti nekoliko unosa koji vas zanimaju i zapisati rezultat natrag. HotPDF, izvorna VCL PDF komponenta za Delphi i C++Builder, izlaže upravo to sučelje kroz svoj API za zapis učitanog dokumenta. Ovaj članak govori o ispravnoj upotrebi i o jednoj pogrešci koju gotovo svi rade: uređuju Info rječnik i zaborave da druga kopija istih metapodataka živi u XMP-u
Dvije lokacije pohranjuju iste metapodatke i ne slažu se
PDF nosi informacije o dokumentu na dvije paralelne lokacije, i to je korijen većine "Promijenio sam naslov, ali Acrobat i dalje prikazuje stari" prijava. Prva je rječnik informacija o dokumentu, klasični /Info objekt s /Title, /Author, /Subject, /Keywords, /Creator, and /Producer ključevi, definirani u ISO 32000-1 §14.3.3. Drugi je XMP paket, XML dokument pohranjen kao tok koji visi s kataloga pod /Metadata, definiran u §14.3.2 i temeljen na Adobe XMP modelu podataka
Oba mogu sadržavati naslov. Ništa u specifikaciji ne prisiljava da budu usklađeni. Suvremeni preglednici i većina PDF/A validatora preferiraju XMP paket kada je prisutan, a vraćaju se na Info rječnik kada nije. Ako dakle ažurirate samo /Info - što radi golema većina koda za "postavljanje PDF metapodataka" - čitač koji vjeruje XMP-u i dalje će prikazivati zastarjelu vrijednost, a PDF/A provjera označit će nesklad. Ispravna operacija na svakoj datoteci koja već ima XMP paket jest dvostruko pisanje: promijeniti unos u Info rječniku i regenerirati XMP, tako da oba ostanu usklađena. HotPDF vam daje obje polovice; disciplina njihove zajedničke upotrebe na vama je
Uređivanje Info rječnika
Pomoćne funkcije na strani Info rječnika tanke su i predvidljive. SetLoadedTitle, SetLoadedAuthor, SetLoadedSubject, SetLoadedKeywords, SetLoadedCreator, and SetLoadedProducer svaki uzima jedan AnsiString i zapisuje odgovarajući ključ u učitani Info rječnik, zamjenjujući vrijednost ako ključ postoji i dodajući je ako ne postoji. Za potpuno uklanjanje ključa - recimo curenja /Creator koji naziva vaše interno oruđe - pozovite RemoveLoadedInfoKey s čistim nazivom ključa. Ništa od ovoga ne dira XMP; rade isključivo na /Info objektu koji je LoadFromFile pronašao pri parsiranju datoteke
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 točnim: ove funkcije uzimaju AnsiString. Za ASCII naslove to nije problem, ali PDF tekstualni nizovi koji trebaju nelatinične znakove moraju biti kodirani kako specifikacija zahtijeva - UTF-16BE s oznakom reda bajtova ili PDFDocEncoding - prije nego što ih predate. Knjižnica upisuje bajtove koje joj date u string objekt; ne pogađa kodiranje umjesto vas. Ako su vaši naslovi običan engleski, zanemarite ovo. Ako sadrže naglašene ili CJK znakove, kodirajte ih namjerno i testirajte u stvarnom pregledniku
Prepisivanje XMP paketa
SetLoadedXMPMetadata je druga polovica dvostrukog pisanja. Predajte mu cijeli XMP paket kao AnsiString i on radi jednu od dviju stvari: ako se katalog već poziva na /Metadata tok metapodataka, zamjenjuje sadržaj tog toka na mjestu, zadržavajući isti broj objekta; ako nema metadata toka, stvara ga, označava ga /Type /Metadata i /Subtype /XML, dodjeljuje broj objekta i povezuje ga iz kataloga. U oba slučaja završite s valjanim objektom metapodataka koji će preglednici čitati
Vi dajete XML, što znači da vi kontrolirate shemu - dc:title, dc:creator, xmp:CreatorTool i tako dalje. To je istodobno i moć i odgovornost: knjižnica ne parsira i ne provjerava vaš paket, a bajtove zapisuje nekomprimirano, bez primijenjenog filtera toka. Neispravan paket će proći kroz poziv i kasnije se pojaviti kao prigovor o oštećenim metapodacima. XML gradite pažljivo i točno preslikajte vrijednosti koje ste upisali u Info rječnik kako se ta dva pogleda nikad ne bi proturječila
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 redoslijed - Info prvo, XMP drugo, zatim spremanje - obrazac je koji treba usvojiti. Dva poziva su neovisna; usklađenost postoji samo zato što ste im dali iste nizove. Preskočite XMP poziv na datoteci koja već ima XMP paket i vraćate se na bug tihe zastarjelosti koji cijeli ovaj odjeljak želi spriječiti

Upravljanje time kako preglednik otvara datoteku
Tri unosa kataloga odlučuju što će čitatelj vidjeti istog trenutka kad se dokument otvori, a sva tri su jednolinijske izmjene na učitanom grafu. SetLoadedPageMode zapisuje /PageMode kao name objekt: proslijedite 'UseOutlines' da biste otvorili panel knjižnih oznaka, 'UseThumbs' za traku minijatura, 'FullScreen' za prezentacijski način, ili 'UseAttachments' da biste prikazali panel privitaka (ISO 32000-1 §7.7.3.1, Tablica 28). SetLoadedPageLayout zapisuje /PageLayout na isti način - 'SinglePage', 'OneColumn', 'TwoColumnLeft' i ostalo. Oba uzimaju ime bez vodeće kose crte; knjižnica ga dodaje pri izlazu
SetLoadedLanguage upisuje unos u katalogu /Lang, prirodnojezičnu oznaku za dokument kao cjelinu - 'en-US', 'de-DE' oznaku BCP 47. Primijetite razliku u tipu koja zbunjuje ljude: /PageMode i /PageLayout su PDF naziv objekti, dok je /Lang niz znakova. HotPDF to interno radi ispravno, ali ako ikad pregledate izlaz vidjet ćete /PageMode /UseOutlines nasuprot /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 odabrala izgovor, i to je strogi uvjet za usklađenost s pristupačnošću PDF/UA
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 knjižnih oznaka bez narušavanja stabla
Naslovi knjižnih oznaka su rutinsko čišćenje - tipfeler u naslovu, poglavlje prenumerirano nakon što je outline izgrađen. SetLoadedOutlineTitle uzima indeks od nule u vršnim unosima outlinea i novi naslov, prolazi kroz lanac Catalog → /Outlines → /First → /Next do te pozicije i zamjenjuje /Title string. Mijenja samo naslov; odredište, stanje otvoreno/zatvoreno i struktura djece 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 sigurno upravo zato što nikad ne dira strukturne brojače. Brisanje outline unosa je slučaj koji grize, i vrijedi ga razumjeti čak i kad samo preimenujete, jer vam govori što ne ručno uređivati. Svaki outline čvor nosi /Count broj, a - prema ISO 32000-1 §12.3.3 - taj broj nije broj neposredne djece. To je ukupan broj vidljivih potomaka: pozitivan /Count od N znači da je N potomaka trenutno prikazano, dok negativna vrijednost znači da čvor ima potomke, ali je sažet. Kada se vršni unos ukloni, /Outlines broj korijena outlinea ne može se jednostavno smanjiti za jedan; mora se ponovno izračunati zbrajanjem, za svaki preživjeli vršni čvor, "jedan za sam čvor plus njegov pozitivan /Count," preskačući potomke svakog sažetog čvora (s negativnim brojem). Ako to pogrešno napravite, ukupni broj knjižnih oznaka koji čitatelj prikazuje odluta - skoči za više od jedan po svakom brisanju. Preimenovanje zaobilazi sve to, što je još jedan razlog da radije koristite ciljanu pomoćnu funkciju nego da čačkate po rječniku sami
Kako spremanje ostaje na mjestu
Svaka izmjena iznad mijenja objekte u memoriji; ništa ne stiže na disk dok SaveLoadedDocument ne radi. Razlog zašto je ovaj pristup jeftin jest to što spremanje ne ponovno generira dokument - čuva postojeće brojeve objekata i strukturu koju je HotPDF pročitao pri učitavanju, vraćajući isti graf s vašim nekolicinom promijenjenih i novo dodijeljenih objekata. To je ono što sprječava da prolaz metapodataka prepiše cijelu datoteku, i to je isti mehanizam ažuriranja na mjestu koji omogućuje object streams i inkrementalna ažuriranja rade. Ako vaše izvorne datoteke dolaze iz Worda ili nekog drugog uredskog paketa, njihov raspored objekata ima vlastite osobitosti koje vrijedi znati prije nego ih uređujete; članak o hibridno-referentni cross-reference tokovi u Office PDF-ovima objašnjava kako su te datoteke strukturirane i što preživljava put tamo-amo
Postoje dvije granice koje treba poštovati. Prvo, ovo je model uređivanja na mjestu, a ne alat za redakciju ili sanaciju: uklanjanje Info ključa uklanja taj ključ, ali ne briše starije vrijednosti koje bi mogle ostati u prethodnoj generaciji inkrementalnog ažuriranja iste datoteke. Ako je vaš zahtjev stvarno uklanjanje osjetljivih metapodataka, to je drugačija i teža operacija. Drugo, XMP zapis je doslovan - knjižnica vjeruje vašem XML-u i ne provjerava ga - pa za sve što je namijenjeno PDF/A ili strogom validatoru, generirajte paket iz provjerenog predloška i provjerite izlaz. Korišten unutar tih granica, uređivanje metapodataka na mjestu je pravi alat odgovarajuće veličine: popravlja nekoliko bajtova koji su pogrešni i ostavlja devedeset devet posto datoteke koji je već bio ispravan točno onakvim kakvim ga je izvorni proizvođač zapisao
API za zapis učitanog dokumenta prikazan ovdje dolazi uz standardni HotPDF Component za Delphi i C++Builder, zajedno s cjelokupnim skupom metoda za uređivanje metapodataka, outlinea i Cataloga