PDFium Component mengendalikan layer optional content PDF (OCG) di Delphi lewat dua metode TPdf: InspectOptionalContent mendata setiap layer beserta visibilitas yang benar-benar akan dirender PDFium, dan SaveAsOptionalContentConfigured menulis salinan terverifikasi di mana layer pilihan Anda dinyalakan atau dimatikan. Metode kedua juga menetral aturan Usage dan /AS yang kalau dibiarkan diam-diam membatalkan suntingan Anda. Keduanya bekerja pada dokumen yang sudah terbuka di TPdf, jadi tidak ada parser kedua yang harus disinkronkan dengan apa yang ditampilkan viewer
Permintaannya biasanya datang dari shop CAD atau GIS: set gambar dikirim dengan dimensi, anotasi, dan title block di layer terpisah, dan pelanggan ingin salinan dengan dimensi tersembunyi sebelum berpindah ke supplier. PDFium merender optional content dengan benar, tapi ABI publiknya tak punya fungsi untuk mendata OCG, memilih konfigurasi, atau membalik state layer. Jadi Anda turun ke level objek, menyunting /OCProperties, menyimpan, memuat ulang, dan layer itu masih di sana. Sebabnya adalah logika visibilitas PDFium, dan layak dipahami sebelum menyentuh byte mana pun
Mengapa menyunting /ON dan /OFF tidak mengubah yang dirender PDFium?
Menyunting array /ON dan /OFF milik dictionary konfigurasi tidak cukup, karena PDFium membiarkan state eksplisit di dalam dictionary /Usage milik OCG sendiri mengungguli array-array itu, dan aturan auto-state /AS bisa lalu mengungguli keduanya. ISO 32000-1 §8.11.4 mendeskripsikan konfigurasi dan dictionary usage sebagai mekanisme terpisah; renderer PDFium melipatnya menjadi satu keputusan, dan InspectOptionalContent mereproduksinya dalam urutan ini:
- Mulai dari
/BaseStatemilik konfigurasi, tempat/ONdan/Unchangedsama-sama dihitung terlihat dan hanya/OFFyang menyembunyikan - Terapkan array
/ONmilik konfigurasi, lalu array/OFF-nya, sehingga group yang terdaftar di keduanya berakhir tersembunyi - Terapkan state Usage eksplisit milik group untuk usage yang diminta, misalnya
/Usage << /View << /ViewState /OFF >> >>, yang mengungguli semua di atasnya - Perlakukan group yang
/Intent-nya tak memuat/Viewmaupun/Allsebagai terlihat, karena ia tak ikut serta dalam visibilitas view-intent - Terakhir jalankan array
/ASmilik konfigurasi terpilih, yang entri-entrinya untuk event yang cocok menyetel state group yang mereka daftarkan
Langkah ketiga adalah yang paling sering membakar orang. Berkas yang disimpan tool layout sering membawa /ViewState /ON di setiap OCG, dan PDFium lalu mengabaikan array /OFF suntingan Anda yang hati-hati itu: simpanan sukses, berkas terbuka kembali mulus, dan layer tetap tergambar. Untuk Print dan Export, OcExplicitUsageState membaca PrintState atau ExportState lebih dulu dan jatuh kembali ke ViewState ketika entri spesifiknya absen, sehingga ViewState /ON sebatas satu itu memaku layer untuk pencetakan juga. Marked content yang mereferensikan OCMD (§8.11.2.2) lalu di-resolve terhadap hasil per-group ini, lewat kebijakan /P atau, bila ada, ekspresi visibilitas /VE
Bagaimana mendata layer yang benar-benar akan ditampilkan PDFium?
TPdf.InspectOptionalContent mengembalikan TPdfOptionalContentInventory yang array Groups-nya membawa nomor objek tiap OCG, nama, intent, tiga state Usage, bahasa, rentang zoom, flag Locked, indeks radio-group, dan EffectiveVisible hasil hitungan. Metode ini lebih dulu menyuruh PDFium menyimpan dokumen in-memory saat ini, membentangkan object stream, dan memindai hasilnya, sehingga suntingan yang dibuat lebih awal di sesi ikut tercermin. Indeks konfigurasi 0 selalu dictionary /D default dan entri-entri /Configs menyusul dari indeks 1; argumen default -1 memilih indeks 0. Dokumen tanpa /OCProperties membuat metode mengembalikan False dengan alasannya di ErrorMessage alih-alih melempar exception
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage default-nya ocuView; -1 memilih konfigurasi 0, dictionary /D
if not Pdf.InspectOptionalContent(Inv) then
begin
Memo1.Lines.Add('No usable layers: ' + Inv.ErrorMessage);
Exit;
end;
Memo1.Lines.Add(Format('Configuration %d: %s',
[Inv.SelectedConfigurationIndex,
string(Inv.Configurations[Inv.SelectedConfigurationIndex].Name)]));
for G in Inv.Groups do
Memo1.Lines.Add(Format('obj %d %s visible=%s locked=%s radio=%d',
[G.ObjectNumber, string(G.Name),
BoolToStr(G.EffectiveVisible, True),
BoolToStr(G.Locked, True), G.RadioGroupIndex]));
end;
Array Memberships melaporkan setiap OCMD dengan Policy-nya (ocmpAnyOn, ocmpAllOn, ocmpAnyOff, ocmpAllOff), teks VisibilityExpression mentah, dan EffectiveVisible-nya sendiri. Beberapa aturan pinggiran disengaja. /P default-nya /AnyOn, dan OCMD tanpa group dihitung terlihat. Referensi ke nomor objek yang bukan OCG yang dikenal diperlakukan terlihat alih-alih menggagalkan seluruh ekspresi. Evaluasi /VE berhenti di kedalaman nesting 32 dan memperlakukan yang lebih dalam sebagai tersembunyi, sehingga ekspresi yang bermusuhan atau mereferensi dirinya tak mengubah inspeksi menjadi stack overflow
Menulis state layer baru dengan SaveAsOptionalContentConfigured
TPdf.SaveAsOptionalContentConfigured menerima array record TPdfOptionalContentStateChange (nomor objek group plus Visible) dan menulis dokumen yang konfigurasi terpilihnya menghasilkan persis state itu. Konfigurasi terpilih mendapat /BaseState /ON plus array /ON dan /OFF lengkap yang mencakup setiap group, dan setiap OCG yang sudah punya dictionary Usage menerima ViewState eksplisit (atau PrintState / ExportState, mengikuti Options.Usage) yang cocok dengan state barunya. Dengan TPdfOptionalContentConfigureOptions.Default, key /AS milik konfigurasi terpilih dibuang agar event open, print, atau export tak bisa membalik layer kembali
procedure TFormMain.SaveWithoutDimensions(DimensionsObj, NotesObj: Integer);
var
Changes: TPdfOptionalContentStateChanges;
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
SetLength(Changes, 2);
Changes[0].GroupObjectNumber := DimensionsObj;
Changes[0].Visible := False;
Changes[1].GroupObjectNumber := NotesObj;
Changes[1].Visible := True;
// Konfigurasi 0, ocuView, DisableAutomaticState dan EnforceRadioGroups True
Options := TPdfOptionalContentConfigureOptions.Default;
if not Pdf.SaveAsOptionalContentConfigured('C:\Out\Drawing-NoDims.pdf',
Changes, Options, Report) then
raise Exception.Create('Layer update rejected: ' + Report.ErrorMessage);
Log(Format('%d of %d groups changed, %d Usage states rewritten, /AS removed: %s',
[Report.ChangedGroupCount, Report.GroupCount,
Report.UpdatedUsageStateCount,
BoolToStr(Report.RemovedAutomaticState, True)]));
end;
Jalur tulis mempertahankan output simpanan PDFium sendiri sebagai prefiks byte demi byte dan hanya menambahkan pemilik konfigurasi hasil penulisan ulang serta objek OCG yang membawa dictionary Usage, diikuti seksi xref dan trailer baru. Sebelum satu byte pun mencapai tujuan Anda, hasilnya dibuka kembali di TPdf terpisah di bawah kebijakan muat ketat, dan metode gagal bila tabel cross-reference tidak tervalidasi. Overload berkas melangkah satu langkah lagi: ia menulis ke berkas sementara di samping target dan mengganti target hanya setelah verifikasi sukses, sehingga pembaruan yang ditolak tak pernah meninggalkan gambar setengah jadi. Ini pendekatan revisi inkremental terverifikasi yang sama yang dipakai editor name tree dan number tree PDF di PDFium Component
Apa yang enggan dilakukan simpanan terkonfigurasi?
Simpanan terkonfigurasi menolak perubahan apa pun yang dilarang dokumen sendiri atau tak mampu ia wakili dengan aman, dan setiap penolakan terjadi sebelum tujuan disentuh. Nomor objek yang tidak ada di /OCGs langsung gagal. Mengubah group yang terdaftar di array /Locked milik konfigurasi gagal, meski menuliskan ulang nilainya yang sekarang diizinkan. Dengan EnforceRadioGroups aktif, set /RBGroups apa pun yang akan berakhir dengan lebih dari satu anggota terlihat ditolak alih-alih diam-diam mematikan yang lain. Dokumen terenkripsi ditolak karena objek inkremental plaintext tak mampu membawa security handler yang aktif. Dokumen bertanda tangan melempar EPdfError kecuali Anda memberikan AllowSignedDocument = True, karena mengubah apa yang ditampilkan halaman bisa merusak cakupan tanda tangan atau kebijakan sertifikasi
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // menulis /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // entri pertama /Configs, bukan /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // AllowSignedDocument tetap False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // berkas bertanda tangan: tak ada yang ditulis ke Target
Result := False;
end;
end;
end;
Ketahui trade-off-nya sebelum memasang ini ke batch job. Revisi yang ditambahkan duduk di atas re-save penuh PDFium, bukan byte berkas asli Anda, dan persis itulah kenapa masukan bertanda tangan butuh persetujuan eksplisit. Penulisan ulang juga menormalisasi konfigurasi terpilih ke /BaseState /ON, sehingga baseline /Unchanged atau /OFF milik penulis diganti array eksplisit dengan visibilitas hasil yang sama. Membuang /AS menghapus trik khusus cetak seperti layer watermark yang muncul hanya di atas kertas; setel DisableAutomaticState ke False untuk mempertahankan aturan itu, dengan menerima bahwa aturan boleh mengungguli state yang Anda minta untuk event tersebut. Di sisi positif, PDF/A-2 (ISO 19005-2 klausul 6.9) dan PDF/UA (ISO 14289-1 klausul 7.10) sama-sama melarang /AS di dictionary konfigurasi, jadi output default menghilangkan satu isu yang seharusnya akan dilaporkan validasi preflight PDF/A dengan PDFium Component Anda
Di mana kendali layer berada di viewer PDF Delphi
Di viewer, kendali layer adalah checklist yang digerakkan inventory plus muat ulang hasil simpanan. Isi checklist dari Groups, nonaktifkan entri yang Locked, perlakukan anggota yang berbagi RadioGroupIndex sebagai saling eksklusif, dan saat menerapkan, tulis ke TMemoryStream lalu muat stream itu kembali ke TPdf agar view menggambar state baru. Pengawatan antara TPdf dan TPdfView dibahas di membangun viewer PDF kaya fitur dengan PDFium VCL di Delphi. Lisensi, unduhan trial, dan sisa set fitur ada di halaman produk PDFium Component untuk Delphi