Anda memiliki ribuan PDF kontrak dari berbagai generator, dan tim legal ingin setiap file memiliki Author yang benar, string Producer yang sudah diperbaiki, dan mode baca yang membuka panel bookmark ketika dibuka. Cara paling mudah biasanya memuat setiap file, menyusun ulang halaman, lalu menulis dokumen baru. Jika Anda melakukannya, semua nomor objek, sejarah pembaruan inkremental, tanda tangan digital, dan xref yang ditata oleh alat asli ikut terhapus. Halamannya tampak sama, tetapi struktur file menjadi berbeda. Untuk pengeditan metadata, ini keputusan yang salah.
Langkah yang tepat adalah memperlakukan dokumen yang dimuat sebagai graph objek yang dimutasi langsung. Masuk ke Info dictionary, stream /Metadata, dan Catalog, lalu ubah beberapa entri yang dibutuhkan dan tulis hasilnya kembali. HotPDF, komponen PDF VCL asli untuk Delphi dan C++Builder, mengekspos permukaan ini melalui loaded-document write API. Artikel ini menjelaskan penggunaan yang benar, termasuk kesalahan umum yang sering terjadi: mengedit Info dictionary tapi lupa bahwa salinan metadata yang sama juga ada di XMP.
Dua lokasi menyimpan metadata yang sama dan dapat berbeda
PDF menyimpan informasi dokumen di dua lokasi paralel, dan ini sumber utama tiket seperti "judul saya sudah berubah tetapi Acrobat tetap menampilkan yang lama". Yang pertama adalah document information dictionary, yaitu objek klasik /Info dengan kunci /Title, /Author, /Subject, /Keywords, /Creator, dan /Producer menurut ISO 32000-1 §14.3.3. Yang kedua adalah paket XMP, yaitu dokumen XML yang disimpan sebagai stream di bawah Catalog pada /Metadata sesuai §14.3.2 dengan model data Adobe XMP.
Keduanya dapat menyimpan judul, tetapi tidak ada ketentuan yang memaksa mereka konsisten. Viewer modern dan sebagian besar validator PDF/A lebih memilih paket XMP saat ada, lalu kembali ke Info dictionary jika tidak ada. Jadi jika Anda hanya memperbarui /Info yang adalah pola paling umum pada kode "set PDF metadata", pembaca yang mempercayai XMP tetap menampilkan nilai lama, dan pemeriksa PDF/A akan menandai ketidaksesuaian. Pada file yang sudah memiliki paket XMP, operasi yang benar adalah tulis ganda: perbarui entri Info dan regenerasi XMP agar tetap sinkron. HotPDF menyediakan kedua bagian, dan Anda wajib memakainya berpasangan.
Mengedit Info dictionary
Helper Info memiliki struktur yang tipis dan konsisten. SetLoadedTitle, SetLoadedAuthor, SetLoadedSubject, SetLoadedKeywords, SetLoadedCreator, dan SetLoadedProducer masing-masing menerima satu AnsiString lalu menulis kunci terkait ke Info dictionary yang dimuat, mengganti nilainya jika kunci sudah ada dan menambahkannya bila belum ada. Untuk menghapus kunci sepenuhnya, misalnya /Creator yang bocor karena nama tooling internal, panggil RemoveLoadedInfoKey dengan nama kunci mentah. Semua metode ini tidak menyentuh XMP; mereka hanya beroperasi pada objek /Info yang ditemukan LoadFromFile saat mem-parsing file.
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;
Detail penting berikutnya adalah parameter AnsiString. Untuk judul ASCII ini bukan masalah. Tetapi string teks PDF yang memerlukan karakter non-Latin harus dikodekan seperti yang diminta spesifikasi, yaitu UTF-16BE dengan byte-order mark atau PDFDocEncoding, sebelum Anda kirimkan. Library hanya menulis byte yang diberikan ke string object, tanpa menebak encoding. Jika judul Anda berisi kata Inggris biasa, bagian ini bisa diabaikan. Jika ada karakter beraksen atau CJK, lakukan encoding secara eksplisit lalu uji di viewer nyata.
Menulis ulang paket XMP
SetLoadedXMPMetadata adalah bagian kedua dari penulisan ganda ini. Berikan paket XMP lengkap sebagai AnsiString. Jika Catalog sudah mereferensikan stream /Metadata, isi stream tersebut diganti di tempat dengan menjaga nomor objek yang sama. Jika stream metadata belum ada, metode ini membuatnya, memberi /Type /Metadata dan /Subtype /XML, mengalokasikan nomor objek, serta menghubungkannya dari Catalog. Hasilnya adalah objek metadata valid yang dapat dibaca viewer.
Anda menyediakan XML dan secara langsung mengatur skema seperti dc:title, dc:creator, xmp:CreatorTool, dan sebagainya. Inilah sisi kuat sekaligus tanggung jawabnya: library tidak mem-parsing atau memvalidasi paket Anda, dan menulis byte Anda ke sebuah string object apa adanya tanpa menebak encoding. Paket yang salah format akan lolos panggilan dan baru muncul sebagai kesalahan metadata di tahap selanjutnya. Buat XML dengan cermat, dan samakan nilainya persis dengan yang Anda tulis ke Info dictionary supaya kedua tampilan tidak bertentangan.
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;
Urutan yang perlu dipahami adalah Info terlebih dahulu, XMP kedua, lalu simpan. Keduanya berjalan terpisah, dan konsistensi hanya ada ketika Anda memberi nilai string yang sama ke keduanya. Lewatkan pemanggilan XMP pada file yang memiliki paket XMP maka bug "stale" tetap muncul, yaitu nilai lama yang tetap terlihat.

Mengatur cara viewer membuka file
Ada tiga entri Catalog yang menentukan tampilan saat file dibuka, dan ketiganya adalah perubahan satu baris pada graph yang dimuat. SetLoadedPageMode menulis /PageMode sebagai name object. Gunakan 'UseOutlines' untuk menampilkan panel bookmark, 'UseThumbs' untuk rail thumbnail, 'FullScreen' untuk mode presentasi, atau 'UseAttachments' untuk panel lampiran sesuai ISO 32000-1 §7.7.3.1, Tabel 28. SetLoadedPageLayout menulis /PageLayout dengan nilai yang sama: 'SinglePage', 'OneColumn', 'TwoColumnLeft', dan lain-lain. Keduanya menulis nama tanpa slash di depan; library yang menambahkannya saat output.
SetLoadedLanguage menulis entri Catalog /Lang, yaitu tag bahasa alami untuk dokumen, seperti 'en-US', 'de-DE', atau tag BCP 47. Ada perbedaan tipe yang sering membuat bingung: /PageMode dan /PageLayout adalah objek name, sedangkan /Lang adalah string. HotPDF memang memprosesnya dengan benar secara internal, tetapi jika Anda periksa hasilnya, Anda akan melihat /PageMode /UseOutlines bertemu dengan /Lang (en-US), dan sekarang Anda paham alasannya. Entri /Lang lebih penting dari yang terlihat: ini yang dibaca teknologi bantu untuk menentukan pengucapan, dan merupakan syarat wajib untuk aksesibilitas 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;
Mengubah judul bookmark tanpa mengganggu struktur
Judul bookmark biasanya dipakai untuk pembersihan rutin, misalnya typo pada heading atau penomoran chapter yang berubah setelah outline selesai. SetLoadedOutlineTitle menerima indeks nol berbasis awal untuk entri outline top-level dan judul baru, lalu menelusuri rantai Catalog → /Outlines → /First → /Next sampai posisi itu dan mengganti /Title di sana. Yang diubah hanya judul; destinasi, status buka tutup, dan struktur anak tidak berubah.
if Pdf.LoadFromFile('report.pdf', '') > 0 then
begin
Pdf.SetLoadedOutlineTitle(0, 'Executive Summary');
Pdf.SetLoadedOutlineTitle(1, 'Financial Results');
Pdf.SaveLoadedDocument('report-renamed.pdf');
end;
Mengubah judul aman karena tidak menyentuh counter struktural. Menghapus entri outline lebih berisiko dan tetap penting dipahami meskipun tugas Anda hanya mengubah nama, karena hal itu memberi tahu bagian mana yang tidak boleh diedit manual. Setiap node outline memiliki /Count. Sesuai ISO 32000-1 §12.3.3, /Count bukan jumlah anak langsung, melainkan jumlah visible descendants total. Nilai /Count positif N berarti N turunannya sedang terlihat, sementara nilai negatif berarti node memiliki turunan tetapi ditutup. Jika entry tingkat atas dihapus, nilai /Count di root /Outlines tidak cukup hanya dikurangi satu; harus dihitung ulang dengan menjumlahkan "satu untuk node itu sendiri plus /Count positif" pada setiap node level atas, melewati turunan dari node tertutup. Jika salah, total bookmark yang ditampilkan pembaca akan melompat lebih dari satu saat penghapusan. Mengubah judul menghindari masalah ini, sebab itu salah satu alasan memakai helper terarah daripada mengubah dictionary sendiri.
Bagaimana penyimpanan tetap di tempat
Setiap pengeditan di atas memutasikan objek di memori dan tidak ada yang menyentuh disk sampai SaveLoadedDocument dipanggil. Alasan pendekatan ini ringan adalah proses save tidak meregenerasi dokumen. Ia mempertahankan nomor objek yang sudah ada dan struktur yang diparse saat load, lalu menulis kembali graph yang sama dengan sedikit objek yang berubah atau baru dialokasikan. Inilah yang membuat operasi metadata tidak menulis ulang seluruh file, dan mekanisme in-place update yang sama dipakai untuk object streams and incremental updates. Jika file sumber berasal dari Word atau paket office lain, tata letak objeknya memiliki karakteristik sendiri yang baiknya dipahami sebelum mengedit; artikel hybrid-reference cross-reference streams in Office PDFs menjelaskan struktur file tersebut dan apa yang aman saat round trip.
Ada dua batas yang harus dihormati. Pertama, ini adalah model edit in-place, bukan redaction atau sanitization. Menghapus kunci Info memang menghapus kunci itu, tetapi tidak membersihkan nilai lama yang masih tersimpan di generasi pembaruan inkremental sebelumnya. Jika Anda membutuhkan penghapusan metadata yang benar-benar sensitif, itu operasi yang lebih berat dan berbeda. Kedua, penulisan XMP bersifat literal karena library mempercayai XML Anda dan tidak memvalidasinya, jadi jika targetnya PDF/A atau validator ketat, bangun paket dari template yang sudah valid dan verifikasi output. Jika dipakai pada batas yang tepat, edit in-place ini hanya memperbaiki beberapa byte yang salah dan membiarkan sembilan puluh sembilan persen file yang sudah benar tetap sama persis seperti saat dibuat oleh produsen asal.
Loaded-document write API yang dibahas di sini dikirim bersama HotPDF Component standar untuk Delphi dan C++Builder, bersama metode metadata, outline, dan Catalog editing yang lengkap.