Dua dokumen terbuka sekaligus, nomor halaman yang sama, masing-masing di dalam panel yang bisa digulir sendiri: itulah inti sebuah viewer pembanding. PDFium Component menghadirkan ini lewat model objek yang lugas, tempat TPdf memiliki file-nya dan TPdfView memiliki tampilannya. Satu dokumen, satu TPdf, satu TPdfView. Anda ingin tiga panel, maka Anda punya tiga pasang. Bagian yang sulit bukanlah panggilan API-nya; melainkan aritmetika tata letak saat jendelanya diubah ukuran dan logika sinkronisasi halaman saat Anda memutuskan view mana yang harus mengikuti view mana
Tata Letak Form
Form VCL-nya memuat tiga container TScrollBox yang berdampingan, masing-masing dengan sebuah TPdfView di dalamnya dan disejajarkan ke alClient supaya memenuhi kotaknya. Dua component TSplitter duduk di antara kotak-kotak itu supaya pengguna bisa menyesuaikan lebar kolomnya saat program berjalan. Sebuah toolbar di atas panelnya membawa tombol buka, kendali zoom, dan sakelar dua-view / tiga-view
Mode tiga view adalah sebuah boolean yang dilacak form itu secara internal. Ketika ia berbalik, Anda menghitung ulang lebarnya lalu menampilkan atau menyembunyikan kolom ketiga. Pendekatan yang paling sederhana adalah membersihkan semua properti Align, menyembunyikan splitter-nya, lalu menyetel posisi absolutnya:
procedure TFormMain.UpdateLayout;
var
TotalWidth: Integer;
begin
TotalWidth := ClientWidth;
if ThreeViewMode then
begin
ScrollBox3.Visible := True;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 3;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth div 3;
ScrollBox3.Left := ScrollBox2.Left + ScrollBox2.Width;
ScrollBox3.Width := TotalWidth - ScrollBox3.Left;
// Terapkan (ClientHeight - tinggi toolbar) yang sama ke ketiga nilai Height
end
else
begin
ScrollBox3.Visible := False;
ScrollBox1.Left := 0;
ScrollBox1.Width := TotalWidth div 2;
ScrollBox2.Left := ScrollBox1.Width;
ScrollBox2.Width := TotalWidth - ScrollBox2.Left;
end;
end;
Menyetel Align := alNone pada ketiga kotak sebelum aritmetika bilangan bulatnya mencegah mesin constraint VCL bertengkar dengan penugasan Anda. Pulihkan kembali kelihatan-tidaknya splitter setelah pemosisian bila Anda menginginkan penyesuaian ukuran dengan seret pada mode dua view
Tinggi setiap scroll box adalah area klien dikurangi tinggi panel toolbar-nya. Karena toolbar-nya ditambatkan di atas dengan alTop, ClientHeight - PanelButtons.Height memberi Anda ruang vertikal yang bisa dipakai. Tugaskan nilai ini ke ketiga kotak di dalam panggilan UpdateLayout yang sama supaya tidak pernah ada satu frame pun tempat sebuah kotak lebih tinggi daripada yang lain dan menyebabkan tata letaknya berkedip
Membuka Sebuah Dokumen
Setiap pasangan panel membutuhkan prosedur pembukanya sendiri. Polanya pendek: nonaktifkan component-nya, setel nama file-nya, aktifkan, lalu periksa Active; jika ia tetap False, mintalah password lalu coba lagi. Perhatikan bahwa TPdfView.Active adalah yang mengendalikan rendering, sementara TPdf.Active adalah yang benar-benar membuka file-nya; keduanya saling bebas. Menyetel PdfView.Active := True ketika TPdf yang tertaut padanya belum aktif tidak berbahaya namun tidak menampilkan apa pun
procedure TFormMain.OpenPdfFile(PdfComponent: TPdf;
PdfViewComponent: TPdfView);
var
Password: string;
begin
if not OpenDialog.Execute then
Exit;
PdfComponent.Active := False;
PdfComponent.FileName := OpenDialog.FileName;
PdfComponent.Password := '';
PdfComponent.Active := True;
// Kegagalan muat itu senyap: Active tetap False alih-alih melempar error.
if not PdfComponent.Active then
begin
// Kemungkinan besar file berpassword; beri pengguna satu kali coba lagi.
if InputQuery('Password', 'Enter document password:', Password) then
begin
PdfComponent.Password := Password;
PdfComponent.Active := True;
end;
end;
if not PdfComponent.Active then
begin
ShowMessage('Could not open ' + OpenDialog.FileName +
' (damaged file or wrong password)');
Exit;
end;
PdfViewComponent.PageNumber := 1;
SetActivePdfView(PdfViewComponent);
end;
Selalu periksa PdfComponent.Active setelah penugasannya; file yang rusak atau password yang salah membuat pemuatannya gagal secara senyap tanpa melempar exception pada jalur bawaannya. Menyetel PdfViewComponent.PageNumber := 1 secara eksplisit setelah pembukaan yang berhasil mencegah nomor halaman basi dari dokumen sebelumnya
Dialog pesan di akhirnya memang disengaja: Anda ingin file yang rusak atau tidak didukung muncul ke permukaan seketika alih-alih ditelan menjadi panel kosong yang diam. Pengguna yang tidak melihat apa pun tidak punya gambaran apakah file-nya termuat lalu memang kosong, atau apakah component-nya menolaknya. Melaporkan kegagalannya menjaga error-nya tetap terlihat
Pelacakan Panel Aktif
Ketika pengguna mengklik di dalam sebuah panel, panel itu menjadi aktif. Form-nya melacak sebuah field privat FActivePdfView: TPdfView. Umpan balik visualnya adalah perubahan warna batas pada TScrollBox yang memuatnya: setel ke clHighlight untuk yang aktif dan clWindow untuk yang lain. Kaitkan ini ke setiap TPdfView.OnClick dan ke prosedur pembukanya supaya fokusnya mengikuti dokumen yang baru saja Anda buka
Sebagian operasi berlaku untuk semua panel yang terlihat alih-alih hanya yang aktif. Sebuah boolean FAllViewsMode pada form-nya menggerakkan percabangan itu. Ketika ia bernilai true, perubahan zoom dan navigasi halaman menyebar ke setiap panel yang punya dokumen aktif:
procedure TFormMain.ApplyZoomToAll(NewZoom: Double);
begin
if PdfView1.Active then PdfView1.Zoom := NewZoom;
if PdfView2.Active then PdfView2.Zoom := NewZoom;
if ThreeViewMode and PdfView3.Active then PdfView3.Zoom := NewZoom;
end;
Navigasi Halaman Tersinkron
Navigasi tersinkron itu opsional namun berguna untuk workflow revisi dokumen tempat kedua file mencakup rentang halaman yang sama. Logikanya berada di dalam sebuah event handler yang menyala setelah pengguna menavigasi salah satu view. Ketika sebuah view sumber mengubah PageNumber-nya, handler-nya menyebarkan nomor itu ke view yang lain, dengan satu penjaga: view sasarannya harus punya setidaknya sebanyak itu halaman, kalau tidak lewati saja
PageNumber pada TPdfView dan pada TPdf saling bebas. TPdf.PageNumber melacak halaman mana yang dianggap aktif oleh component dokumennya; TPdfView.PageNumber melacak apa yang ditampilkan di layar. Untuk keperluan navigasi, Anda menginginkan properti view-nya, bukan properti dokumennya
Sebuah checkbox berlabel semacam "Sync pages" memberi kendali kepada pengguna. Ketika ia tidak dicentang, setiap panel bernavigasi sendiri-sendiri dan handler-nya langsung keluar. Kebebasan itu penting untuk kasus pemakaian ketika kedua dokumennya punya jumlah halaman berbeda, atau ketika pengguna ingin menemukan bagian yang setara di dalam sebuah terjemahan yang dimulai pada halaman berbeda. Memaksa sinkron selamanya justru akan membuat tool-nya lebih sulit dipakai daripada susunan dua jendela desktop yang sederhana
Satu hal yang perlu diawasi: menyetel PdfView.PageNumber secara terprogram di dalam handler sinkronisasinya akan memicu event perubahan pada view itu sendiri. Jagalah dari rekursi tak berujung dengan sebuah flag boolean yang Anda setel sebelum penugasan lalu Anda bersihkan segera sesudahnya. Flag itu berlaku per form, bukan per view, karena ketiga view berbagi handler yang sama
Zoom Per Panel
Setiap TPdfView membawa properti Zoom-nya sendiri, sebuah Double dalam persen tempat Zoom := 100 berarti ukuran sebenarnya (100%). Menyetelnya menimpa FitMode apa pun yang sedang aktif. Untuk sebuah tombol paskan-ke-lebar pada panel yang aktif, baca zoom paskannya dari PdfView.PageWidthZoom[PdfView.PageNumber] lalu tugaskan. Untuk paskan-ke-halaman, pakai PageZoom[PageNumber]. Keduanya adalah properti array yang diindeks dengan nomor halaman berbasis 1, jadi jagalah dari nomor halaman nol sebelum mengaksesnya
Ketika Anda mengekspor halaman aktif ke sebuah gambar, baca rotasinya dari view-nya namun panggil RenderPage pada component TPdf, bukan pada view-nya. Bentuk bitmap dari TPdf.RenderPage menerima dimensi piksel yang eksplisit ditambah sebuah nilai TRotation dan sebuah himpunan TRenderOptions. Varian fungsinya mengembalikan sebuah TBitmap yang dimiliki pemanggil dan Anda bebaskan sendiri setelah menyimpannya:
procedure TFormMain.SaveActiveViewAsImage;
var
Pdf: TPdf;
Bmp: TBitmap;
Jpeg: TJpegImage;
begin
if not Assigned(FActivePdfView) or not FActivePdfView.Active then
Exit;
Pdf := FActivePdfView.Pdf;
Pdf.PageNumber := FActivePdfView.PageNumber;
Bmp := Pdf.RenderPage(
0, 0,
Round(Pdf.PageWidth * 2),
Round(Pdf.PageHeight * 2),
FActivePdfView.Rotation, [], clWhite);
try
if SavePictureDialog.Execute then
begin
Jpeg := TJpegImage.Create;
try
Jpeg.Assign(Bmp);
Jpeg.CompressionQuality := 90;
Jpeg.SaveToFile(SavePictureDialog.FileName);
finally
Jpeg.Free;
end;
end;
finally
Bmp.Free;
end;
end;
Pengali 2x pada lebar dan tingginya memberi keluaran yang lebih tajam untuk dokumen berteks halus. try/finally di sekeliling pembebasan bitmap-nya bukanlah pilihan; sebuah pembatalan TSaveDialog tetap mendarat di blok finally, dan Anda ingin bitmap-nya dilepas apa pun yang dilakukan pengguna
Kebutuhan DLL
PDFium Component membungkus library pdfium native. Proses host 32 bit membutuhkan pdfium32.dll; host 64 bit membutuhkan pdfium64.dll. Varian dengan engine JavaScript V8 menambahkan akhiran v8 dan berbobot kira-kira 23-27 MB dibanding build standar yang 5-6 MB. Untuk sebuah viewer pembanding yang menonaktifkan pengisian form (Pdf.FormFill := False), build standar tanpa V8 sudah memadai dan menjaga distribusinya tetap kecil
Tempatkan DLL-nya di direktori yang sama dengan executable-nya, atau di direktori mana pun yang ada pada PATH sistem. Component-nya memuatnya sesuai kebutuhan ketika TPdf pertama diaktifkan, sehingga DLL yang hilang baru muncul pada titik itu alih-alih pada saat aplikasinya dimulai. Jika Anda mengirimkan sebuah installer, pendekatan yang paling andal adalah menyalin DLL-nya ke folder aplikasinya saat pemasangan alih-alih bersandar pada direktori sistem yang kelak mungkin dibersihkan seorang administrator
Build V8 terutama berguna ketika Anda perlu berinteraksi dengan action JavaScript pada PDF, misalnya untuk memicu field kalkulasi atau handler submit. Sebuah viewer pembanding yang pasif tidak punya alasan menjalankan JavaScript; menyetel Pdf.FormFill := False sebelum Active := True melewatkan lingkungan pengisian form sepenuhnya, yang juga berarti tidak ada engine JS yang diinisialisasi bahkan jika build standarnya yang dipakai. Itulah default yang benar untuk sebuah viewer hanya-baca tanpa peduli varian DLL mana yang Anda kirimkan
Untuk detail lebih lanjut tentang PDFium Component dan API lengkapnya, kunjungi halaman produk Delphi PDFium Component