Merender halaman PDF ke device context Windows untuk pratinjau cetak menaruh tiga sistem koordinat dalam satu baris kode, dan mereka jarang sepakat. Halaman PDF diukur dalam poin dengan titik asal di kiri bawah. DC layar diukur dalam piksel dengan titik asal di kiri atas dan faktor zoom yang Anda pilih. DC printer, yang seharusnya diramalkan pratinjau itu, mengukur piksel pada resolusi perangkat tetapi menempatkan titik asalnya di sudut area tercetak, bukan di sudut lembarnya. Salah pada salah satu saja dan pratinjaunya tampak baik-baik saja sementara halaman tercetak keluar bergeser, terskala, atau terpotong di satu tepi. Gejala lazimnya adalah formulir bergaris tepi yang terpratinjau di tengah lalu tercetak dengan garis atas dan kirinya teriris, karena printer laser tidak bisa menaruh tinta di beberapa milimeter terluar dan tak ada yang memberi tahu pratinjaunya. losLab PDF Library (PDF Library for Delphi) menutupi seluruh jalur itu dengan pemanggilan rendering ke device context, sebuah lapisan konfigurasi printer virtual, dan bitmap pratinjau yang dihasilkan dari metrik printer itu sendiri, dan bagian terakhir inilah yang membuat pratinjaunya jujur soal margin tersebut
Geometri kertas bukan geometri area tercetak
Dua persegi panjang menggambarkan target cetak mana pun, dan selisih di antara keduanya adalah tempat sebagian besar bug pratinjau bersarang. Persegi panjang kertas adalah lembar fisiknya. Persegi panjang area tercetak adalah wilayah yang lebih kecil yang benar-benar dapat dijangkau mesin cetak, tersorok ke dalam oleh margin perangkat keras yang berbeda per model printer dan kadang per baki. Lapisan pencetakan pustaka ini mengukur keduanya. Kelas TPLPrinter di bawahnya memaparkan PageWidth dan PageHeight untuk area tercetak, FullPageWidth dan FullPageHeight untuk lembar penuh, serta PrintOffsetX bersama PrintOffsetY untuk jarak antara titik asal keduanya, semuanya dalam piksel perangkat pada resolusi yang dilaporkan GetDPI. Pratinjau yang jujur menyusutkan angka-angka yang sama itu ke resolusi layar alih-alih melukis halaman ke persegi panjang apa pun yang kebetulan dimiliki kontrolnya. Lewatkan langkah itu dan pratinjaunya diam-diam mengandaikan margin nol, satu-satunya nilai yang tidak dipakai printer sungguhan mana pun
Pratinjau layar lewat RenderPageToDC
Untuk kontrol pratinjau di layar, RenderPageToDC(DPI, Page, DC) menggambar halaman dari dokumen yang dimuat langsung ke device context GDI mana pun, entah itu kanvas TPaintBox, bitmap di luar layar, atau DC metafile. Argumen DPI menentukan zoom-nya. 96 mendekati tampilan 100% pada layar klasik, dan menggandakannya menggandakan ukuran hasil render
procedure TPreviewForm.PreviewBoxPaint(Sender: TObject);
begin
// ketiganya adalah keadaan pustaka yang lengket, bukan parameter per pemanggilan:
FPdf.SetRenderDCOffset(FOffsetX, FOffsetY);
FPdf.SetRenderDCErasePage(1);
FPdf.SetRenderCropType(0);
FPdf.RenderPageToDC(FPreviewDpi, FCurrentPage, PreviewBox.Canvas.Handle);
end;
Jebakannya, jalur render DC dikemudikan oleh keadaan pustaka yang lengket, bukan oleh parameter per pemanggilan. SetRenderDCOffset, SetRenderDCErasePage, dan SetRenderCropType masing-masing bertahan sampai ada yang mengubahnya, sehingga loop thumbnail yang berjalan setelah pengguna menyetel tampilan terzoom akan mewarisi offset atau crop apa pun yang ditinggalkan jalur kode sebelumnya. Gejalanya adalah pratinjau yang melenceng hanya pada urutan navigasi tertentu, dan itu kira-kira jenis bug yang paling menyiksa untuk direproduksi. Menetapkan semua keadaan yang relevan di awal handler paint, seperti di atas, tidak berbiaya apa pun dan melenyapkan seluruh kelas masalah itu. Ada pengali kedua yang bersembunyi di dekatnya. Resolusi keluaran efektifnya adalah skala render dikali argumen DPI, dan meski SetRenderScale berdefault 1.0, ia juga bertahan begitu diubah, sehingga fitur ekspor yang sempat menaikkannya diam-diam menskalakan ulang setiap pratinjau berikutnya sampai ada yang mengembalikannya
Penampil yang bisa digulir dan pengecatan ulang sebagian punya varian khusus. RenderPageToDCClip menerima spesifikasi clip berikut device context-nya, sehingga membatalkan satu pita jendela hanya mengecat ulang pita itu alih-alih meraster ulang seluruh halaman. Pada zoom tinggi di halaman berformat besar, itulah bedanya antara penampil yang mengikuti scrollbar dan penampil yang mengekor berlepotan di belakangnya
Pekerjaan cetak yang cocok dengan pratinjaunya
Sisi pencetakan bekerja lewat printer virtual. NewCustomPrinter mengkloning printer sistem menjadi konfigurasi privat milik pustaka, dan SetupPrinter menyesuaikan klon itu tanpa menyentuh DevMode seluruh mesin: kertas masuk sebagai setting 1 (sebuah konstanta DMPAPER_*) dan orientasi sebagai setting 11. Imbalannya adalah isolasi. Sebuah layanan dapat mencetak label A4 sementara printer default host tetap di Letter, dan tidak ada yang perlu dipulihkan sesudahnya
var
Pdf: TPDFlib;
Virt: WideString;
Opt: Integer;
begin
Pdf := TPDFlib.Create;
try
if Pdf.LoadFromFile('report.pdf', '') <> 1 then
raise Exception.Create('load failed');
Virt := Pdf.NewCustomPrinter(Pdf.GetDefaultPrinterName);
Pdf.SetupPrinter(Virt, 1, 9); // setting 1 = kertas, DMPAPER_A4
Pdf.SetupPrinter(Virt, 11, 1); // setting 11 = orientasi, 1 = potret
Opt := Pdf.PrintOptions(1, 1, 'Monthly Report'); // pas ke kertas, auto-rotate + tengah
Pdf.PrintDocument(Virt, 1, Pdf.PageCount, Opt);
finally
Pdf.Free;
end;
end;
PrintOptions pantas dibaca dengan saksama. Ia mengembalikan handle opsi yang wajib Anda oper ke PrintDocument atau PrintPages; ia bukan keadaan ambien. Membangun opsinya lalu lupa mengoper handle-nya gagal secara senyap. Pekerjaannya tetap tercetak dengan nilai default, dan tak seorang pun menyadarinya sampai kebijakan pas-ke-kertas yang diharapkan ternyata menghasilkan halaman kebesaran yang terpotong. Argumen penskalaan halaman adalah tempat kebijakan itu tinggal. Tanpa penskalaan, akurasi dimensinya terjaga, dan itu penting untuk formulir yang diukur dengan penggaris. Pas-ke-kertas menskalakan ulang segalanya ke lembarnya. Perkecil-halaman-besar membiarkan halaman normal apa adanya dan hanya turun tangan ketika sebuah halaman melampaui area tercetak, dan itu biasanya default yang tepat untuk kumpulan dokumen campuran. Flag auto-rotate-and-center menangani halaman lanskap tanpa jalur kode kedua
Aplikasi yang sudah mengelola TPrinter lewat alur dialog VCL dapat menyerahkannya langsung. PrintDocumentToPrinterObject dan PrintPagesToPrinterObject menerima instance TPrinter yang sudah dikonfigurasi, sehingga dialog cetak standar tetap menjadi permukaan konfigurasi bagi pengguna sementara pustaka menangani rendering halamannya. Mencampur kedua pendekatan itu dalam satu jalur kode cenderung memunculkan kembali pergeseran geometri yang justru hendak dibunuh seluruh pekerjaan ini, jadi pilih satu. Rute printer virtual cocok untuk layanan tanpa penunggu; rute TPrinter cocok untuk aplikasi interaktif
Keluaran selektif bekerja dengan cara yang sama. PrintPages menerima string rentang, jadi mengoper nama printer virtual, '2-5,12', dan handle opsinya akan mencetak halaman 2 sampai 5 dan 12 dengan kontrak geometrinya utuh, dan sintaks yang sama menggerakkan varian cetak-ke-berkas. Varian berkas itulah jawaban praktis untuk lingkungan tanpa penunggu yang tidak punya perangkat fisik terpasang: menguji regresi geometri cetak di server build yang sama sekali tidak punya antrean driver. Render dokumen yang sama lewat opsi yang sama menjadi artefak berkas pada setiap build, dan regresi geometri berubah menjadi sebuah diff alih-alih laporan pelanggan tiga pekan kemudian
Bitmap pratinjau dengan metrik printer itu sendiri
Pratinjau yang dirender pada 96 DPI terhadap ukuran halaman yang diandaikan menjawab pertanyaan yang salah. Ia memperlihatkan seperti apa halamannya, bukan apa yang akan ditaruh printer ini di kertas ini. GetPrintPreviewBitmapToString menutup celah itu dengan membangun pratinjau dari printer kustom yang sama dan handle opsi yang sama dengan pekerjaan cetak nantinya, sehingga ukuran kertas, orientasi, kebijakan penskalaan, rotasi, dan offset perangkat keras semuanya ikut memasok bitmap-nya. Apa yang kembali adalah apa yang akan tampak di lembar itu
procedure ShowPrinterTruePreview(Pdf: TPDFlib; const Virt: WideString; Opt: Integer);
var
Data: AnsiString;
Strm: TMemoryStream;
Bmp: TBitmap;
begin
Data := Pdf.GetPrintPreviewBitmapToString(Virt, 1, Opt, 1200, 0);
Strm := TMemoryStream.Create;
try
Strm.WriteBuffer(PAnsiChar(Data)^, Length(Data));
Strm.Position := 0;
Bmp := TBitmap.Create;
try
Bmp.LoadFromStream(Strm);
PreviewImage.Picture.Assign(Bmp);
finally
Bmp.Free;
end;
finally
Strm.Free;
end;
end;
Argumen MaxDimension membatasi sisi panjang bitmap. 1200 piksel tetap tajam untuk dialog pratinjau dan menjaga pemakaian memori tetap sederhana bahkan untuk gambar teknik berukuran E, di mana render resolusi penuh pada 600 DPI milik printer akan membengkak sampai bergigabyte-gigabyte
Mengingat pilihan printer pengguna
Dialog cetak yang lupa pengaturannya antar sesi melahirkan tiket dukungannya sendiri. Pasangan DevMode, GetPrinterDevModeToString dan SetPrinterDevModeFromString, menyerialkan konfigurasi driver lengkap sebuah printer menjadi string legap yang bisa Anda simpan di preferensi pengguna dan pulihkan pada sesi berikutnya, termasuk opsi khusus driver yang tidak repot dimodelkan API generik mana pun. Simpan printer berdasarkan namanya dari GetPrinterNames, jangan pernah berdasarkan indeks daftar. Urutan indeks berubah setiap kali sebuah printer ditambahkan atau dihapus, sehingga indeks yang tersimpan diam-diam menunjuk perangkat yang salah ketika daftarnya bergeser. GetDefaultPrinterName menutupi kasus cadangan ketika perangkat yang diingat sudah lenyap sama sekali
Pemilihan baki melengkapi kisah persistensi ini. GetPrinterBins melaporkan sumber kertas yang dipaparkan sebuah driver, dan itu penting untuk alur kerja kop surat di mana halaman satu ditarik dari baki kop surat dan sisanya dari kertas polos. Itu kebijakan yang pengguna harapkan diingat aplikasi bersama segala hal lain, dan pekerjaan cetak yang mendarat di kertas yang salah terbaca sebagai bug bahkan ketika setiap byte PDF-nya sudah benar
Pertahankan satu mesin untuk pratinjau dan cetak
Satu keputusan terakhir diam-diam mengatur kesetiaan hasil. Pemilihan mesin rendering berlaku untuk tujuan layar maupun printer, sehingga godaannya adalah mempratinjau dengan mesin cepat dan mencetak dengan mesin presisi. Tahan godaan itu. Menggerakkan pratinjau dan pekerjaan cetak lewat mesin yang berbeda memunculkan kembali persis pergeseran kesetiaan yang hendak dihapus oleh pratinjau yang setia pada printer, dan ia melakukannya dengan cara yang baru tampak di atas kertas. Pertukaran antara mesin bawaan, Cairo, dan PDFium ditimbang di rendering PDF multi-mesin di Delphi; pilih satu dan pakai di kedua sisi
Dokumen yang terlalu besar untuk dimuat dengan nyaman sebelum dicetak dapat dibuka lewat jalur direct-access yang diuraikan di gabung, pecah, dan direct access untuk PDF besar, yang merender halaman ke device context dari sebuah file handle tanpa membangun pohon dokumen. Referensi API pencetakan selengkapnya ada di halaman produk losLab PDF Library for Delphi