Redaksi PDF yang sebenarnya di Delphi berarti menghapus byte yang mendasarinya, bukan sekadar mengecat di atasnya. losLab PDF Library menarik garis pemisah tersebut dengan dua API: RedactRegion mengedit stream konten halaman sehingga teks, vektor, dan gambar yang dihapus benar-benar hilang secara fisik, dan SanitizeDocument membersihkan metadata, skrip, dan tindakan tempat informasi yang diredaksi mungkin masih tersembunyi. Jika salah satu bagian di atas salah, Anda akan mengirimkan file yang tampak disensor di layar tetapi memberikan rahasia tersebut kembali kepada siapa saja yang menyalin teks atau membuka Properti Dokumen
Mode kegagalan ini sudah menjadi rahasia umum. Surat kabar, pengadilan, dan lembaga pemerintah semuanya pernah merilis PDF dengan persegi panjang hitam di atas nama atau angka, tetapi pembaca tetap dapat memilih teks di bawahnya, menyalinnya, dan menempelkan string yang seharusnya diredaksi tersebut ke tempat yang terlihat jelas. Persegi panjang tersebut hanyalah sebuah gambar. Teksnya masih ada, berada di dalam stream konten satu lapisan di bawah cat, sepenuhnya utuh dan dapat dipilih. losLab PDF Library, engine PDF Delphi dan C++Builder yang diakses melalui kelas TPDFlib, memperlakukan perbedaan tersebut sebagai inti dari redaksi dan bukan sebagai pemikiran sampingan
Mengapa menggambar kotak hitam di atas teks PDF bukan redaksi yang sebenarnya?
losLab PDF Library menyediakan MaskRect untuk kasus di mana penutupan adalah hal yang benar-benar Anda inginkan: tanda air, penutupan visual, atau stempel bukti. MaskRect(Page, Left, Top, Width, Height, Red, Green, Blue) menambahkan persegi panjang ke stream konten halaman dan mengecatnya dengan operator pembuatan jalur re diikuti oleh operator pengisian f (ISO 32000-1 §8.5). Setiap byte yang sudah ada di halaman — glyph nama yang Anda tutup, goresan vektor, gambar tersemat — tetap berada tepat di tempatnya, satu instruksi sebelumnya di stream yang sama. Viewer yang patuh akan menggambar kotak hitam terakhir, sehingga secara visual ia menang. Penutup tersebut tidak mengubah apa pun di bawahnya
Siapa pun dapat mengambil kembali konten asli dengan tiga cara: memilih dan menyalin teks langsung melalui kotak buram, menjalankan ekstraktor teks pada halaman, atau menghapus persegi panjang penutup dari stream konten. MaskRect jujur tentang hal ini dari namanya sendiri — ia memasker, bukan menghapus. Hasilnya seperti stiker di atas kata yang masih bisa Anda baca dengan melepas stiker tersebut
Bagaimana RedactRegion menghapus konten pada tingkat instruksi
RedactRegion(Page, Left, Top, Width, Height, Options) bekerja satu lapisan di bawah cat. losLab PDF Library mengurai setiap lapisan konten halaman menjadi TPDFContentProgram, menelusuri daftar instruksi dengan penganalisis pelacakan CTM (FindInstructionsInRect dalam model konten), dan menandai setiap operator penampilan teks, pengecatan jalur, dan gambar Do yang area catnya berpotongan dengan persegi panjang Anda. Instruksi tersebut dihapus dengan TPDFContentProgram.Delete, dan lapisan yang diedit ditulis kembali dengan WritePageContentLayer. Byte tidak bergeser ke belakang penutup — mereka keluar dari stream. Ekstraktor teks yang dijalankan setelahnya tidak akan menemukan apa pun, karena memang tidak ada yang bisa ditemukan
Dua detail implementasi patut Anda pertimbangkan. RedactRegion menerima persegi panjang kiri-atas dalam titik asal dan unit saat ini, lalu mengonversinya secara internal ke ruang pengguna kiri-bawah yang digunakan stream konten, sehingga Anda menentukan wilayah seperti yang Anda lihat di halaman. Ketika persegi panjang mencakup beberapa instruksi, RedactRegion menghapusnya dalam urutan indeks menurun, karena menghapus instruksi 4 sebelum instruksi 7 akan menggeser setiap indeks setelahnya dan merusak rentang. Berikan nilai Options = 0 dan losLab PDF Library juga akan membubuhkan kotak hitam pekat di atas wilayah yang dibersihkan sebagai tanda visual — tetapi tanda tersebut bersifat kosmetik, diterapkan setelah konten asli benar-benar hilang
Dalam praktiknya, Anda jarang menulis persegi panjang tersebut secara hard-coded. Anda mencari string sensitif pada halaman dan membaca kembali kotak pembatasnya (bounding box) — pencarian halaman dan enumerasi elemen yang sama yang dibahas dalam menemukan lokasi teks dan elemen halaman di Delphi — lalu menyerahkan persegi panjang tersebut ke RedactRegion:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', '') = 1 then
begin
// Delete everything painted inside a 220 x 14 pt box on page 1.
// Coordinates are top-left, in the current origin and units.
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // Options=0 stamps a black marker
PDF.SaveToFile('contract-redacted.pdf');
end;
finally
PDF.Free;
end;
end;
Di mana informasi yang diredaksi masih tersembunyi dalam PDF?
Redaksi yang hanya berhenti pada lapisan yang terlihat akan meninggalkan jejak, karena PDF menyimpan informasi di tempat-tempat yang tidak pernah dilihat pembaca. losLab PDF Library mengelompokkan kebocoran ini ke dalam beberapa saluran yang patut disebutkan. Document Information Dictionary dan stream metadata XMP secara rutin membawa string Author, Producer, atau Keywords yang menyebutkan nama orang atau kasus yang baru saja Anda hapus dari isi dokumen. JavaScript tingkat dokumen dan catalog OpenAction dapat dijalankan saat dokumen dibuka dan mengakses data yang Anda kira sudah hilang. Setiap anotasi membawa tindakan /A opsional, dan anotasi itu sendiri — catatan tempel, field formulir usang, tautan — dapat berada tepat di atas area yang diredaksi dengan salinan teksnya sendiri. Semua ini tidak tersentuh oleh enkripsi; file terenkripsi dengan paket XMP teks biasa masih membocorkan judulnya ke pengindeks mana pun, sebuah celah yang patut di-audit enkripsi dan izinnya secara tersendiri. Pembersihan yang defensif harus memeriksa setiap tempat persembunyian ini, bukan hanya halaman yang Anda lihat
Membersihkan dokumen dengan SanitizeDocument
losLab PDF Library menjawab penyebaran kebocoran tersebut dengan SanitizeDocument(Flags), panggilan orkestrasi yang menjalankan serangkaian pembersihan independen yang dipilih oleh bitmask dan mengembalikan jumlah pembersihan yang diterapkan. Bendera (flags) tersebut dapat digabungkan. SANITIZE_JAVASCRIPT menghapus setiap paket JavaScript tingkat dokumen dan skrip OpenAction apa pun. SANITIZE_ACTIONS menghapus kamus tindakan /A dari setiap anotasi, melalui TPDFAnnots.RemoveAnnotAction yang baru, dan membersihkan catalog OpenAction. SANITIZE_METADATA mengosongkan Author, Subject, Keywords, Creator, dan Producer di Document Information Dictionary (ISO 32000-1 §14.3.3) and stream metadata XMP (§14.3.2). SANITIZE_ANNOTATIONS menghapus setiap anotasi di seluruh dokumen, dan SANITIZE_OPEN_ACTION menghapus entri catalog /OpenAction. SANITIZE_ALL adalah gabungan bitwise dari kelimanya
Konvensi indeks di dalam pembersihan ini adalah hal yang dapat secara diam-diam merusak perulangan yang dibuat sendiri, itulah sebabnya SanitizeDocument hadir sebagai satu panggilan tunggal. GlobalJavaScriptPackageName berbasis 0, dan RemoveGlobalJavaScript memperkecil daftar saat berjalan, sehingga pembersihan JavaScript selalu membaca indeks paket 0 dan menghapusnya hingga jumlahnya mencapai nol. Sebaliknya, DeleteAnnotation berbasis 1. AnnotationCount melaporkan terhadap halaman yang saat ini dipilih, sehingga pembersihan anotasi memilih setiap halaman secara bergantian sebelum menghitung. losLab PDF Library menangani batas-batas ini dengan benar secara internal, sehingga Anda cukup meneruskan satu set bendera dan membaca satu jumlah kembali alih-alih mereproduksi lima aturan iterasi yang berbeda
var
Applied: Integer;
begin
// Strip metadata, scripts, and actions but keep the annotations:
Applied := PDF.SanitizeDocument(SANITIZE_METADATA or SANITIZE_JAVASCRIPT or
SANITIZE_ACTIONS or SANITIZE_OPEN_ACTION);
// Or clear every hidden channel, annotations included:
Applied := PDF.SanitizeDocument(SANITIZE_ALL);
end;
Alur kerja redaksi dan pembersihan yang lengkap
Gabungkan keduanya dan prosedur lengkapnya menjadi singkat. losLab PDF Library menghapus konten yang terlihat dengan RedactRegion, halaman demi halaman, kemudian membersihkan setiap saluran tersembunyi dengan SanitizeDocument(SANITIZE_ALL) sebelum file ditulis. Karena redaksi hampir selalu dijalankan pada dokumen yang berasal dari luar kendali Anda, ada baiknya untuk memuatnya melalui jalur yang diperkuat — posisi defensif yang sama yang dijelaskan dalam mengurai PDF yang tidak tepercaya dengan aman — sehingga input yang cacat gagal secara bersih alih-alih gagal di tengah jalan proses redaksi
var
PDF: TPDFlib;
Applied: Integer;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('incoming.pdf', '') = 1 then
begin
PDF.RedactRegion(1, 90, 705, 220, 14, 0); // delete the sensitive line
Applied := PDF.SanitizeDocument(SANITIZE_ALL); // clear metadata, JS, actions, annots
PDF.SaveToFile('published.pdf');
end;
finally
PDF.Free;
end;
end;
Di mana batas redaksi stream konten
RedactRegion beroperasi pada stream konten, yang merupakan tempat teks digital asli berada dan bukan tempat teks terpindai berada. Ketika halaman berupa gambar terpindai, kata-kata adalah piksel di dalam gambar XObject, bukan operator teks, dan losLab PDF Library tidak dapat memotong nama dari tengah raster seperti halnya ia menghapus glyph. Penanganan gambarnya sengaja dibuat konservatif: gambar apa pun yang penempatannya berpotongan dengan persegi panjang akan dihapus secara keseluruhan, sehingga kotak redaksi pada halaman terpindai akan menghapus seluruh hasil pemindaian dan bukan hanya sebagian darinya. Meredaksi wilayah di dalam gambar berarti melakukan rasterisasi, pengecatan, dan penanaman kembali pada tingkat piksel, yang merupakan operasi yang berbeda dari penghapusan stream konten. Peringatan yang sama berlaku untuk lapisan teks OCR di atas halaman terpindai: RedactRegion menghapus operator teks yang dapat dilihatnya, tetapi gambar di bawahnya tetap tidak tersentuh kecuali jika gambar XObject-nya juga berpotongan dengan kotak tersebut
Dua poin penting menjaga hasil tetap akurat. Uji perpotongan memperkirakan lebar setiap rangkaian teks dari jumlah karakter dan ukuran font-nya daripada mengukur metrik glyph yang tepat, sehingga kata yang sangat pendek dalam font proporsional dapat lolos dari persegi panjang yang digambar dengan ketat — berikan sedikit margin pada kotak tersebut. Penghapusan juga bersifat granular-instruksi: RedactRegion menghapus seluruh operator penampilan teks, sehingga persegi panjang yang memotong hanya sebagian dari rangkaian kata dapat ikut menghapus karakter yang berdekatan yang menggunakan operator yang sama dengannya. Kedua batasan tersebut bukan alasan untuk tidak mempercayai mekanisme ini, melainkan alasan untuk membatasi persegi panjang dengan hati-hati dan memeriksa hasilnya
Aturan yang menutup setiap alur kerja redaksi adalah verifikasi, bukan kepercayaan. Redaksi wilayah tersebut, bersihkan dokumen, simpan ke file baru, buka kembali, dan coba ambil kembali teks yang Anda hapus serta metadata yang Anda bersihkan; jika keduanya kembali kosong, maka redaksi telah berhasil. API RedactRegion dan SanitizeDocument yang ditunjukkan di sini disertakan dalam losLab PDF Library untuk Delphi dan C++Builder, bersama dengan referensi model konten dan pembersihan yang lengkap