Artikel Teknis

Membangun penampil PDF di Delphi dengan PDFium Component

Penampil PDF di Delphi bermuara pada dua komponen dan pengkabelan di antaranya. TPdf memiliki dokumen: ia membuka file, mendekripsinya, dan menjawab pertanyaan tentang jumlah halaman dan metadata. TPdfView adalah kontrol visual yang mengecat halaman di layar dan menangani pengguliran, zoom, dan halaman yang sedang dilihat pengguna. PDFium Component membungkus mesin render yang sama yang dikirim di dalam Chrome, sehingga glyph, anti-aliasing, dan warna yang Anda dapatkan di kanvas cocok dengan yang sudah dilihat pengguna Anda di browser mereka. Pekerkerjaannya bukan pada rendering. Itu ada pada menghubungkan objek dokumen ke view, memuat tanpa crash pada file yang rusak atau dilindungi kata sandi, dan memberi pengguna segelintir kontrol yang membuat penampil terasa selesai: mengganti halaman, mengubah zoom, dan menyesuaikan halaman ke jendela

Ini menelusuri perakitan itu dalam urutan yang benar-benar Anda bangun. Semua di sini merender satu halaman pada satu waktu, yang diinginkan sebagian besar alur kerja dokumen. Jika Anda butuh halaman ditumpuk dalam satu kolom yang digulir terus-menerus, itu adalah keputusan tata letak yang berbeda dan bukan jalur di sini

Mengkabel TPdf ke TPdfView

Jatuhkan TPdf dan TPdfView di form, lalu beri tahu view dokumen mana yang akan ditampilkan. Penugasan tunggal itu adalah seluruh tautan antara dokumen non-visual dan kontrol yang mengecatnya

Arsitektur penampil PDF Delphi di mana TPdf memiliki dokumen, TPdfView melukisnya, dan satu assignment properti menghubungkan keduanya di atas DLL PDFium
TPdf memiliki dokumen sementara TPdfView melukisnya, dan satu assignment menyambung keduanya di atas engine PDFium bersama
procedure TFormMain.FormCreate(Sender: TObject);
begin
  // Pdf dan PdfView dijatuhkan saat desain.
  PdfView.Pdf := Pdf;                 // view mengecat apa pun yang disimpan dokumen ini
  PdfView.FitMode := pfmFitWidth;     // mulai pengguna pada zoom yang masuk akal
end;

Sebelum salah satu dari ini berjalan, pustaka native PDFium harus ada di mesin. PDFium Component memanggil ke pdfium32.dll atau pdfium64.dll tergantung platform target Anda, dan dokumen tersebut menolak dibuka jika DLL tidak ditemukan. Kirim DLL yang cocok di samping executable Anda, atau letakkan di tempat pemuat sistem akan menemukannya. Build V8-enabled hanya ada untuk PDF yang membawa JavaScript yang ingin Anda jalankan, yang tidak dilakukan penampil polos, jadi jangkau DLL standar kecuali Anda punya alasan konkret untuk tidak melakukannya

Memuat dokumen tanpa mempercayai input

Instingnya adalah membungkus pemuatan dalam try/except dan memperlakukan pengecualian yang dilemparkan sebagai kegagalan. Insting itu salah di sini, dan salah menghasilkan penampil yang terlihat baik-baik saja sampai seseorang memberinya file rusak. Menyetel Active := True tidak melempar pada kegagalan muat. PDFium Component menangkap kesalahan internal dan membiarkan Active duduk di False, jadi satu-satunya cara jujur untuk mengetahui apakah dokumen terbuka adalah membaca properti kembali setelah Anda menyetelnya

Alur keputusan pemuatan untuk penampil PDFium Delphi di mana mengatur Active tak pernah memunculkan exception, false yang senyap berarti kata sandi salah atau berkas rusak, dan satu percobaan ulang kata sandi mengikuti
Aktivasi tak pernah melempar exception saat gagal, sehingga viewer membaca ulang Active dan menjawab false senyap dengan satu percobaan ulang password
procedure TFormMain.OpenDocument(const FileName: string);
begin
  Pdf.FileName := FileName;
  Pdf.Active := True;                 // tidak pernah melempar; kegagalan membiarkan Active = False
  if not Pdf.Active then
  begin
    ShowMessage('Could not open ' + FileName);
    Exit;
  end;
  PdfView.PageNumber := 1;            // view melacak halaman saat ini miliknya sendiri
  UpdatePageLabel;
end;

Dua hal patut diperhatikan. Yang pertama adalah PageNumber ada di kedua objek dan keduanya independen. Pdf.PageNumber adalah gagasan dokumen tentang halaman saat ini; PdfView.PageNumber adalah halaman yang sebenarnya ditampilkan kontrol, dan itulah yang Anda setel untuk menggerakkan pengguna melalui file. Menyetel satu tidak menggerakkan yang lain, jadi penampil selalu menggerakkan properti view. Yang kedua adalah pengindeksan berbasis-1: halaman berjalan dari 1 ke Pdf.PageCount, bukan dari 0, yang menjebak siapa pun yang terbiasa dengan array berbasis-nol

Menangani file terenkripsi

Dokumen terenkripsi terlipat ke dalam jalur muat yang sama. Jika kata sandi terbuka disetel sebelum aktivasi, dokumen didekripsi saat dibuka; jika salah atau hilang, Active tetap False persis seperti untuk file rusak. Jadi pemulihannya adalah meminta kata sandi dan mencoba aktivasi lagi

procedure TFormMain.OpenWithPassword(const FileName: string);
var
  Password: string;
begin
  Pdf.FileName := FileName;
  Pdf.Active := True;
  if not Pdf.Active then
  begin
    if InputQuery('Password required', 'Password:', Password) then
    begin
      Pdf.Password := Password;       // harus disetel sebelum Active := True
      Pdf.Active := True;
    end;
    if not Pdf.Active then
    begin
      ShowMessage('Unable to open the document.');
      Exit;
    end;
  end;
  PdfView.PageNumber := 1;
end;

Karena kegagalan diam untuk kata sandi buruk dan file rusak, Anda tidak bisa membedakan keduanya dari Active saja. Dalam praktik itu dapat diterima untuk penampil: pengguna baik menyediakan kata sandi yang benar atau belajar file tidak akan terbuka, dan pesan terbaca sama baiknya

Berhalaman melalui dokumen

Dengan dokumen terbuka, navigasi adalah aritmetika pada PdfView.PageNumber yang dibatasi oleh Pdf.PageCount. Satu-satunya pekerjaan nyata adalah penjepitan, sehingga tombol tidak pernah mendorong halaman keluar dari rentang dan tombol pertama dan terakhir tetap dinonaktifkan di ujung file

procedure TFormMain.GoToPage(NewPage: Integer);
begin
  if not Pdf.Active then
    Exit;
  if NewPage < 1 then
    NewPage := 1
  else if NewPage > Pdf.PageCount then
    NewPage := Pdf.PageCount;
  PdfView.PageNumber := NewPage;
  UpdatePageLabel;
end;

// keempat tombol navigasi menyederhanakan menjadi satu panggilan masing-masing
procedure TFormMain.FirstClick(Sender: TObject);  begin GoToPage(1); end;
procedure TFormMain.PrevClick(Sender: TObject);   begin GoToPage(PdfView.PageNumber - 1); end;
procedure TFormMain.NextClick(Sender: TObject);   begin GoToPage(PdfView.PageNumber + 1); end;
procedure TFormMain.LastClick(Sender: TObject);   begin GoToPage(Pdf.PageCount); end;

Kotak teks "go to page N" adalah panggilan GoToPage yang sama yang diberi makan dari integer yang diurai, dan penjepit mencakup kasus di mana pengguna mengetik 9999 ke dalam file sepuluh-halaman. Jaga UpdatePageLabel sebagai tempat tunggal yang menulis "Page 3 of 12" agar pembacaan tidak pernah menyimpang dari apa yang ditampilkan view

Zoom: persentase eksplisit dan mode fit

Zoom pada TPdfView tiba dalam dua rasa yang berinteraksi, dan memahami interaksi adalah perbedaan antara kontrol zoom yang berperilaku baik dan yang melawan pengguna. Rute langsung adalah properti Zoom, persentase di mana 100 berarti ukuran aktual. Rute lain adalah FitMode, yang memberi tahu view untuk menghitung zoom untuk Anda dan terus menghitungnya saat jendela diubah ukurannya

Interaksi Zoom dan FitMode dalam penampil Delphi PDFium di mana mengassign Zoom persis mengosongkan FitMode menjadi pfmNone dan memilih mode fit menyerahkan zoom kembali ke tampilan
Menetapkan zoom eksak membersihkan fit mode, dan memilih fit mode menyerahkan perhitungan zoom kembali ke view
// pembesaran tetap
PdfView.Zoom := 100;     // ukuran aktual
PdfView.Zoom := 50;      // setengah
PdfView.Zoom := 200;     // dua kali

// biarkan view menyesuaikan halaman ke jendela, dan jaga ukurannya saat resize
PdfView.FitMode := pfmFitWidth;   // lebar halaman memenuhi kontrol
PdfView.FitMode := pfmFitPage;    // seluruh halaman terlihat
PdfView.FitMode := pfmActualSize; // 1:1 dengan titik-titik dokumen

Inilah bagian yang menjebak orang. Menetapkan Zoom secara langsung mereset FitMode ke pfmNone. Itu adalah perilaku yang benar, bukan bug: saat pengguna memilih 150% yang persis, view tidak bisa lagi juga menghormati "fit to width," karena dua permintaan bertentangan. Konsekuensinya untuk UI Anda adalah tombol zoom-in dan tombol fit-to-page adalah status yang saling eksklusif, dan toolbar harus membuat mode aktif terlihat. Saat pengguna mengklik fit-to-page, setel FitMode; saat mereka mengklik zoom numerik, setel Zoom dan biarkan membersihkan mode fit sendiri

Jika Anda lebih suka menghitung nilai fit sendiri, mungkin untuk menyemai slider zoom dengan persentase fit saat ini, pembantu per-halaman memberi Anda angka tanpa mengubah mode. PageWidthZoom[N], PageZoom[N], dan ActualSizeZoom[N] mengembalikan persentase yang akan menyesuaikan halaman N ke lebar, menyesuaikannya utuh, atau merendernya pada ukuran aktual

// bibit penunjuk zoom dari nilai fit-to-width halaman saat ini
var
  FitPercent: Double;
begin
  FitPercent := PdfView.PageWidthZoom[PdfView.PageNumber];
  ZoomEdit.Text := Format('%.0f%%', [FitPercent]);
end;

Apa yang benar-benar dibutuhkan penampil yang selesai

Penampil di atas adalah beberapa lusin baris, dan sudah melakukan pekerjaan yang dibutuhkan alur kerja dokumen: membuka file, bertahan dari yang buruk, menampilkan halaman, berpindah antar halaman, dan mengubah pembesaran dengan tangan atau dengan fit. PDFium melakukan bagian yang sulit secara diam-diam. Font yang tersemat terselesaikan, anotasi dan kolom formulir mengecat di tempat dokumen menempatkannya, dan halaman yang Anda lihat cocok dengan yang akan dilihat pengguna Chrome, karena ini mesin yang sama menggambar keduanya

Dari basis ini tambahan adalah inkremental bukan struktural. Pemilihan dan pencarian teks dibaca dari lapisan teks yang sama yang sudah dibangun PDFium; metadata seperti Pdf.Title dan Pdf.Author adalah satu baca properti jauhnya; rotasi dan skala abu-abu adalah opsi render yang Anda lewatkan saat menggambar halaman ke bitmap. Tidak satu pun dari itu mengubah tulang punggung yang Anda miliki di sini, yaitu objek dokumen, view, dan alur muat-lalu-navigasi yang menghubungkannya. Dapatkan tulang punggung itu dengan benar dan sisanya adalah dekorasi

Komponen TPdf dan TPdfView yang digunakan di sepanjang adalah bagian dari PDFium Component untuk Delphi dan C++Builder, yang membawa referensi penampil lengkap pada halaman produknya