Dimensi halaman PDF ditetapkan saat halaman dibuat, sehingga Anda tidak bisa sekadar mengubah skala konten di tempat seperti mengubah ukuran gambar. Model pustaka yang membuat penyusutan menjadi praktis adalah capture-and-redraw: angkat konten setiap halaman dari dokumen ke dalam sebuah handle, buat halaman baru yang kosong dengan ukuran media asli, lalu gambar kembali konten yang ditangkap ke dalam bounding box yang lebih kecil. Ruang putih di sekitarnya menjadi margin. Pada skala 70% untuk halaman A4 misalnya, 15% lebar berada di setiap sisi dan fraksi yang sama di atas dan bawah, yang persis itulah yang dihasilkan oleh aritmetika batas di bawah ini
Cara kerja CapturePage
CapturePage mengambil nomor halaman, mengangkat konten halaman tersebut ke dalam objek capture dalam memori, dan menghapus halaman dari pohon halaman dokumen. Penghapusan tersebut disengaja dan itulah alasan loop selalu memilih halaman 1 terlepas dari indeks iterasi: setelah halaman 1 ditangkap dan dihapus, halaman yang dulu halaman 2 menjadi halaman 1 baru, dan seterusnya. Jika Anda menambah selektor halaman bersama penghitung loop, Anda akan melewati setiap halaman lainnya dan mendapatkan setengah output yang diharapkan
Handle capture yang dikembalikan oleh CapturePage bukan referensi halaman; ia lebih seperti snapshot konten. Handle tersebut tetap valid sampai Anda memanggil DrawCapturedPage atau merilis secara eksplisit. DrawCapturedPage mengambil handle tersebut beserta persegi panjang tujuan yang diberikan sebagai offset kiri, offset bawah, lebar, dan tinggi, semuanya dalam poin. Pustaka menskalakan konten yang ditangkap agar pas tepat di persegi panjang tersebut, mempertahankan rasio aspek hanya jika persegi panjang Anda kebetulan cocok dengan proporsi aslinya. Untuk penskalaan seragam, Anda ingin persegi panjang memiliki ukuran asli dikalikan faktor skala, dipusatkan di halaman
Matematika pemusatan
Dengan faktor skala 70%, sisa 30% dari setiap dimensi dibagi rata di antara dua sisi. Jadi inset horizontal adalah pageWidth * (1.0 - 0.70) / 2, yaitu 15% dari lebar, dan inset vertikal mengikuti rumus yang sama menggunakan tinggi halaman. Persegi panjang tujuan untuk DrawCapturedPage kemudian dimulai dari (horizBorder, vertBorder) dan membentang pageWidth - 2 * horizBorder kali pageHeight - 2 * vertBorder. Aritmetika tersebut bukan spesifik pustaka; itu hanya geometri memasang persegi panjang lebih kecil secara simetris di dalam persegi panjang yang lebih besar
Satu hal yang perlu diperhatikan: SetOrigin(1) meletakkan asal koordinat di kiri atas alih-alih kiri bawah. Nilai batas yang Anda teruskan ke DrawCapturedPage diukur dari asal mana pun yang Anda atur, jadi jika Anda beralih mode asal antara pemuatan dan penggambaran, pemusatan akan meleset
Contoh C#
Kode berikut memproses setiap halaman dari Pages.pdf melalui siklus capture-and-redraw dan menulis hasilnya ke newpages.pdf. PDFL adalah objek wrapper ActiveX/COM yang ditambahkan ke proyek dari PDFlibDLL64.dll
private void ScalePages_Click(object sender, EventArgs e)
{
File.Delete("newpages.pdf");
double pageWidth, pageHeight, horizBorder, vertBorder;
double scaleFactor = 0.70;
int capturedPageId, ret;
PDFL.LoadFromFile("Pages.pdf", "");
PDFL.SetOrigin(1);
int numPages = PDFL.PageCount();
for (int i = 1; i <= numPages; i++)
{
// Always select page 1: CapturePage removes the page, so page 2
// becomes page 1 on the next iteration.
PDFL.SelectPage(1);
pageWidth = PDFL.PageWidth();
pageHeight = PDFL.PageHeight();
horizBorder = pageWidth * (1.0 - scaleFactor) / 2;
vertBorder = pageHeight * (1.0 - scaleFactor) / 2;
capturedPageId = PDFL.CapturePage(1);
PDFL.NewPage();
PDFL.SetPageDimensions(pageWidth, pageHeight);
ret = PDFL.DrawCapturedPage(
capturedPageId,
horizBorder, vertBorder,
pageWidth - 2 * horizBorder,
pageHeight - 2 * vertBorder);
}
PDFL.SaveToFile("newpages.pdf");
}
Contoh Delphi
Versi Delphi menggunakan TPDFlib secara langsung tanpa melalui lapisan COM, namun urutan pemanggilan identik. Satu perbedaan praktis adalah penjaga file output: FileExists ditambah DeleteFile alih-alih File.Delete, karena SaveToFile akan gagal jika tujuan dikunci oleh proses yang masih terbuka di viewer dari percobaan sebelumnya
procedure TForm1.ScalePagesClick(Sender: TObject);
var
PDFLib: TPDFlib;
pageWidth, pageHeight, horizBorder, vertBorder: Double;
scaleFactor: Double;
capturedPageId, ret, numPages, i: Integer;
begin
if FileExists('newpages.pdf') then
DeleteFile('newpages.pdf');
scaleFactor := 0.70;
PDFLib := TPDFlib.Create;
try
PDFLib.LoadFromFile('Pages.pdf', '');
PDFLib.SetOrigin(1);
numPages := PDFLib.PageCount();
for i := 1 to numPages do
begin
PDFLib.SelectPage(1);
pageWidth := PDFLib.PageWidth();
pageHeight := PDFLib.PageHeight();
horizBorder := pageWidth * (1.0 - scaleFactor) / 2;
vertBorder := pageHeight * (1.0 - scaleFactor) / 2;
capturedPageId := PDFLib.CapturePage(1);
PDFLib.NewPage();
PDFLib.SetPageDimensions(pageWidth, pageHeight);
ret := PDFLib.DrawCapturedPage(
capturedPageId,
horizBorder, vertBorder,
pageWidth - 2 * horizBorder,
pageHeight - 2 * vertBorder);
end;
PDFLib.SaveToFile('newpages.pdf');
finally
PDFLib.Free;
end;
end;
Apa yang sebenarnya dikendalikan faktor skala
Nilai 0,70 di sini berarti konten yang dirender menempati 70% dari setiap dimensi halaman, bukan bahwa file berukuran 70% dari ukuran byte aslinya. Ukuran file setelah operasi ini bergantung pada kompleksitas konten asli; halaman dengan gambar besar tidak akan menyusut secara proporsional karena data piksel digambar ulang dengan resolusi yang sama ke area yang lebih kecil. Jika kompresi pada tingkat byte adalah tujuannya, pendekatan yang tepat adalah LinearizeFile atau menyimpan ulang dengan kompresi aliran, bukan penskalaan geometris
Angka 70% juga bukan batas keras. Nilai apa pun antara 0,0 dan 1,0 dapat digunakan, dan nilai di atas 1,0 memperbesar konten melampaui batas halaman asli, yang akan terpotong di tepi media box kecuali jika Anda juga menambah dimensi halaman. Dokumen berukuran campuran ditangani secara alami karena PageWidth dan PageHeight ditanya per halaman sebelum perhitungan batas, sehingga dokumen di mana halaman ganjil adalah A4 dan halaman genap adalah A3 akan menghasilkan output yang dipusatkan dengan benar pada setiap ukuran halaman tanpa penanganan khusus apa pun
Di mana hal-hal bisa salah
Dua mode kegagalan muncul dalam praktik. Yang pertama adalah file output yang masih terbuka di viewer PDF dari percobaan sebelumnya: SaveToFile akan gagal atau menulis nol byte tergantung pada platform, dan output baru tidak pernah tersimpan. Penjaga hapus file di bagian atas fungsi menangani itu untuk pengembangan, tetapi dalam pipeline produksi menulis ke path sementara dan mengganti nama saat berhasil adalah cara yang lebih aman
Yang kedua adalah ketidaksesuaian jumlah halaman. Karena CapturePage menghapus halaman dari dokumen saat memprosesnya, jumlah yang Anda baca dari PageCount() sebelum loop adalah batas iterasi yang benar. Memanggil PageCount() di dalam loop akan mengembalikan angka yang semakin kecil di setiap iterasi dan keluar lebih awal, membiarkan halaman terakhir tidak diproses. Variabel loop dalam contoh hanya berfungsi sebagai penghitung iterasi yang tersisa; variabel tersebut tidak pernah digunakan untuk memilih halaman, karena halaman yang dipilih selalu 1 karena alasan yang dijelaskan sebelumnya
Panggilan manipulasi halaman yang ditampilkan di sini, termasuk CapturePage, DrawCapturedPage, dan SetPageDimensions, adalah bagian dari losLab PDF Library untuk Delphi, C#, VB.NET, dan C++