Penggabungan halaman (page stitching) menyusun konten dari halaman-halaman yang ada ke atas kanvas bersama, yang merupakan operasi berbeda dari penggabungan berkas (merging). losLab PDF Library mengimplementasikan penggabungan halaman Form XObject dan templat watermark yang dapat disusun untuk Delphi: StitchPageOverlay, StitchPagesSideBySide, dan StitchPagesVertically menempatkan halaman yang ditangkap pada skala apa pun dengan ketepatan vektor penuh, dan TPDFlibCompositeWatermark menumpuk watermark teks dan gambar di seluruh rentang halaman dalam satu panggilan. Jika yang sebenarnya Anda butuhkan adalah menambahkan halaman utuh dari beberapa dokumen ke dalam satu berkas, itu termasuk dalam keluarga penggabungan (merge), yang dibahas dalam artikel tentang penggabungan PDF tingkat byte yang cepat; artikel ini membahas tentang menggabungkan konten halaman dalam satu ruang koordinat
Permintaan yang mengarah ke sini biasanya umum dan berulang. Sebuah sekolah ingin dua halaman ujian ditempatkan pada satu lembar lanskap untuk menghemat setengah penggunaan kertas. Pengulas lokalisasi menginginkan halaman bahasa Inggris dan terjemahannya berdampingan di satu halaman, digulir bersama. Tim hukum ingin setiap draf dicap dengan tiga lapisan sekaligus: logo di sudut, tulisan CONFIDENTIAL diagonal di tengah, dan baris kaki kecil, hanya pada halaman 1 hingga 3. Ketiganya merupakan masalah mendasar yang sama, yaitu menempatkan konten halaman yang sudah diatur tata letaknya ke tempat lain dengan ukuran lain tanpa menurunkannya menjadi bitmap
Mengapa menyambungkan aliran konten PDF bisa gagal?
Penggabungan naif, dengan menyalin aliran /Contents halaman sumber ke halaman target, gagal karena tiga alasan struktural. Pertama, benturan koordinat: kedua aliran konten mengasumsikan titik asal mereka sendiri di kiri bawah MediaBox mereka sendiri, sehingga konten yang ditempel mendarat di atas konten yang ada di tempat yang salah. Kedua, benturan nama sumber daya (resource): setiap halaman menyelesaikan operator seperti /F1 Tf dan /Im0 Do terhadap kamus /Resources miliknya sendiri, dan dua halaman secara rutin mengikat nama yang sama ke font atau gambar yang berbeda, sehingga salah satunya secara senyap merender dengan sumber daya yang salah. Ketiga, salinan aliran mentah tidak memberi Anda penskalaan sama sekali; hal terbaik yang bisa Anda lakukan hanyalah menerjemahkan (translate)
Form XObject memecahkan ketiganya sekaligus, dan itulah sebabnya losLab PDF Library membangun penggabungannya di atas objek tersebut. ISO 32000-1 §8.10 mendefinisikan form XObject sebagai aliran konten mandiri dengan kamus /Resources miliknya sendiri dan ruang koordinatnya sendiri, yang ditempatkan ke dalam halaman dengan memanggil namanya di bawah matriks transformasi saat ini yang aktif. CapturePage membungkus seluruh halaman yang ada, konten ditambah sumber daya, menjadi XObject /Subtype /Form, dan fungsi stitch kemudian memposisikannya dengan matriks cm: skala, translasi, rotasi, sesuka hati. Karena sumbernya tetap berupa vektor, teks tetap menjadi teks dan jalur tetap menjadi jalur pada tingkat zoom apa pun, sifat yang sama yang membuat grafis vektor dan pola bayangan (shading pattern) independen dari resolusi. Tidak ada yang dirasterisasi, dan kamus sumber daya yang terisolasi berarti halaman yang ditangkap tidak dapat berebut nama /F1 dengan halaman target
Bagaimana cara menggabungkan dua halaman PDF menjadi satu halaman?
StitchPagesSideBySide(Page1, Page2, Gap, TargetHeight) adalah jawaban langsungnya: fungsi ini menangkap kedua halaman sumber, membuat halaman baru yang lebih lebar, dan menempatkan kedua tangkapan tersebut di kiri dan kanan dengan selokan (gutter) di antara keduanya, masing-masing diskalakan agar sesuai dengan TargetHeight. Fungsi ini mengembalikan nomor halaman baru (berbasis 1) atau 0 jika gagal, dan kedua halaman sumber ditangkap serta disembunyikan sebagai bagian dari operasi. Kasus ujian dua halaman per lembar adalah pekerjaan tiga baris kode
var
Lib: TPDFlib;
NewPage: Integer;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('exam.pdf', '');
// Halaman 1 dan 2 pada satu halaman baru yang lebih lebar dengan selokan 18pt,
// keduanya diskalakan ke tinggi output 595pt (A4 lanskap)
NewPage := Lib.StitchPagesSideBySide(1, 2, 18, 595);
if NewPage > 0 then
Lib.SaveToFile('exam-2up.pdf');
finally
Lib.Free;
end;
end;
Untuk komposisi vertikal, StitchPagesVertically(PageRanges, Gap, TargetWidth) menumpuk seluruh rentang halaman menjadi satu halaman tinggi bergaya poster. PageRanges menggunakan sintaks rentang standar seperti '1-3,5'. Setiap halaman sumber diskalakan sehingga lebarnya sesuai dengan TargetWidth, sehingga kontribusi tinggi dari setiap halaman adalah tingginya sendiri dikalikan dengan faktor skalanya sendiri, dan tinggi halaman output adalah jumlah dari tinggi tersebut ditambah satu Gap di antara setiap pasangan. Ini adalah bentuk yang Anda inginkan untuk dokumen tinjauan gulir berkelanjutan (continuous-scroll) atau tanda terima panjang yang dirakit dari sumber ukuran letter
Bagaimana cara menumpuk satu halaman ke halaman lainnya?
StitchPageOverlay(SourcePage, TargetPage, Left, Top, Width, Height, Opacity) melukis satu halaman yang ada ke halaman lain yang ada, alih-alih membuat halaman baru. Halaman sumber ditangkap sebagai form XObject dan digambar ke target pada persegi panjang yang Anda berikan, dengan Opacity dari 0 hingga 1 (1 adalah buram). Lebar dan tinggi adalah parameter bebas, sehingga penempatan akan meregang jika rasio Anda berbeda dari rasio aspek halaman sumber itu sendiri; berikan pasangan proporsional jika Anda menginginkan miniatur sumber yang tidak terdistorsi. Penggunaan umum adalah dokumen yang blok persetujuannya dikelola sebagai halamannya sendiri dan dicap ke sampul
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('report.pdf', '');
// Halaman 5 berisi blok persetujuan; tempatkan di halaman 1
// dalam kotak 260 x 180 dengan opacity 45%. Halaman 5 dikonsumsi:
// halaman tersebut ditangkap dan disembunyikan, sehingga halaman 6..n menjadi 5..n-1
if Lib.StitchPageOverlay(5, 1, 300, 500, 260, 180, 0.45) = 1 then
Lib.SaveToFile('report-approved.pdf');
finally
Lib.Free;
end;
end;
Apa yang terjadi pada nomor halaman setelah penangkapan?
CapturePage mengonsumsi halaman sumber: kontennya dipindahkan ke XObject dan halaman yang sekarang kosong disembunyikan, sehingga setiap halaman dengan nomor di atas halaman sumber segera bergeser ke bawah satu tingkat. Ini adalah semantik terpenting dalam seluruh API penggabungan. losLab PDF Library menangani perhitungan aritmatika di dalam setiap panggilan penggabungan, StitchPageOverlay menyesuaikan indeks target ketika target berada di atas sumber, dan StitchPagesSideBySide menangkap halaman bernomor lebih tinggi terlebih dahulu sehingga indeks penangkapan kedua tetap valid, tetapi kode Anda sendiri harus memperhitungkan pergeseran di antara panggilan. Dua aturan menjaga kode multi-stitch tetap benar. Catat ukuran halaman atau nomor halaman apa pun yang Anda perlukan belom penangkapan pertama, karena nilainya akan berubah setelahnya. Dan ketika Anda menangkap beberapa halaman sendiri dalam sebuah loop, lakukan iterasi dalam urutan halaman menurun, sehingga menyembunyikan halaman yang lebih baru tidak akan mengganggu indeks halaman sebelumnya yang belum Anda tangkap
Bagaimana cara menerapkan watermark multi-lapisan ke rentang halaman?
Lapisan templat watermark dalam PDFlibWatermark.pas mengubah "definisikan sekali, terapkan ke banyak halaman" menjadi objek. TPDFlibWatermarkBase membawa kesamaan yang dimiliki setiap watermark: sembilan titik Position (wpTopLeft hingga wpBottomRight, ditambah wpCustom yang menggunakan koordinat X/Y mentah), Angle dalam derajat, Opacity dari 0 hingga 1, dan string PageRange di mana nilai kosong berarti semua halaman. TPDFlibTextWatermark menambahkan teks, ID font (default ke Helvetica), ukuran, dan warna RGB; TPDFlibImageWatermark menambahkan ID gambar dari gambar yang ditambahkan sebelumnya ditambah lebar dan tinggi penempatan. TPDFlibCompositeWatermark menyimpan daftar sub-watermark dan menerapkannya secara rekursif, dan TPDFlib.ApplyWatermark adalah titik masuk sekali panggilan yang menghormati rentang halaman templat itu sendiri dan mengembalikan jumlah halaman yang dicap. Cap hukum tiga lapisan terlihat seperti ini
var
Lib: TPDFlib;
Logo: TPDFlibImageWatermark;
Diagonal, Footer: TPDFlibTextWatermark;
Stack: TPDFlibCompositeWatermark;
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('draft.pdf', '');
Stack := TPDFlibCompositeWatermark.Create;
try
Logo := TPDFlibImageWatermark.Create;
Logo.ImageID := Lib.AddImageFromFile('logo.png', 0);
Logo.Position := wpTopRight;
Logo.Width := 90;
Logo.Height := 32;
Stack.Add(Logo);
Diagonal := TPDFlibTextWatermark.Create;
Diagonal.Text := 'CONFIDENTIAL';
Diagonal.Position := wpCenter;
Diagonal.Angle := 45;
Diagonal.FontSize := 60;
Diagonal.Opacity := 0.15;
Stack.Add(Diagonal);
Footer := TPDFlibTextWatermark.Create;
Footer.Text := 'Internal review copy';
Footer.Position := wpBottomCenter;
Footer.FontSize := 9;
Footer.Opacity := 0.6;
Stack.Add(Footer);
Stack.PageRange := '1-3'; // anak-anak mewarisi rentang ini
Lib.ApplyWatermark(Stack);
Lib.SaveToFile('draft-marked.pdf');
finally
Stack.Free; // komposit membebaskan anak-anaknya
end;
finally
Lib.Free;
end;
end;
Detail komposisi yang patut dijelaskan
Dua detail komposisi patut dijelaskan. Pewarisan rentang bersifat satu arah dan tidak merusak: anak dengan PageRange kosong untuk sementara mewarisi rentang komposit selama Apply, sedangkan anak yang menetapkan rentangnya sendiri akan tetap menyimpannya, sehingga Anda dapat memasang footer ke setiap halaman sementara cap diagonal hanya mencakup bagian draf. Dan kepemilikan mengikuti komposit: TPDFlibCompositeWatermark.Destroy membebaskan anak-anaknya, sehingga Anda membebaskan komposit sekali dan tidak ada yang lain, seperti pada contoh di atas
Batasan yang perlu diketahui
Dua batasan jujur berlaku, dan keduanya mengikuti cara kerja mekanisme tersebut. Pertama, penangkapan hanya membungkus aliran konten halaman dan sumber daya saja. Anotasi, tautan, bidang formulir, dan komentar hidup sebagai objek terpisah dalam kamus halaman, bukan dalam aliran konten, sehingga mereka tidak ikut berpindah ke dalam XObject; tata letak yang digabungkan membawa konten halaman yang terlihat tetapi tidak membawa lapisan interaktif dari sumbernya. Kedua, posisi teks sembilan titik adalah perkiraan: tanpa mesin tata letak lengkap, pustaka tidak dapat mengetahui lebar pasti dari string yang dirender, sehingga memperkirakan kotak teks sebesar 40% dari lebar halaman saat menyelesaikan posisi seperti wpCenter. Hal ini cukup akurat untuk memusatkan label pendek, tetapi untuk penempatan string panjang yang tepat hingga piksel, beralihlah ke wpCustom dan hitung sendiri koordinatnya
Skala bukan kendala praktis di sini, karena penangkapan dan penggabungan memanipulasi referensi objek alih-alih menyandikan ulang konten halaman; jika sumber Anda berukuran ratusan megabyte, disiplin yang sama yang dijelaskan dalam panduan penggabungan dan pemisahan berkas besar dengan akses langsung berlaku sebelum Anda menggabungkannya. Fungsi penggabungan dan kelas templat watermark yang ditunjukkan di sini dikirimkan dalam losLab PDF Library saat ini untuk Delphi, C#, dan VB.NET, bersama dengan primitif tingkat lebih rendah CapturePage, DrawCapturedPage, dan DrawCapturedPageMatrix saat Anda memerlukan tata letak yang tidak dicakup oleh tiga panggilan penggabungan bawaan