cfdocument lanjutan: header, footer dan penomboran halaman
Belajar cfdocument lanjutan dalam Lucee: beza filename dan variable, margin inci atau sentimeter, header footer berbeza, nombor halaman dan fon terbenam.

Kandungan
cfdocument lanjutan: header, footer dan penomboran halaman
Tag cfdocument menukar HTML kepada PDF, dan artikel asas cara menjana PDF dengan cfdocument sudah membawa anda dari PDF pertama hingga saiz halaman dan margin. Artikel ini naik satu aras: apa yang anda buat apabila laporan memerlukan nombor halaman pada setiap muka surat, header yang berbeza antara bab, margin dalam sentimeter, dan fon yang tidak bertukar rupa apabila fail dibuka pada komputer lain. Semua contoh di bawah dijana dalam pelayan Lucee sebenar dan disahkan berfungsi, bukan dibaca daripada dokumentasi.
filename lawan variable: dua destinasi untuk PDF anda
cfdocument memberi dua cara menyimpan hasilnya, dan memilih yang salah adalah punca paling biasa PDF yang hilang atau kosong. Atribut filename menulis binari PDF terus ke fail pada cakera. Atribut variable pula menyimpan binari itu ke dalam pemboleh ubah memori tanpa menyentuh cakera.
Contoh dengan filename ialah bentuk paling ringkas kerana enjin melakukan semua kerja fail untuk anda:
<cfdocument format="pdf" filename="#expandPath('./laporan.pdf')#" overwrite="true">
<h1>Laporan Suku Tahunan</h1>
<p>Kandungan laporan.</p>
</cfdocument>
<cfset info = getFileInfo(expandPath('./laporan.pdf'))>
<cfoutput>Fail PDF dijana: #info.size# bait</cfoutput>
Contoh dengan variable memerlukan satu langkah tambahan, iaitu mengeluarkan pemboleh ubah itu sendiri:
<cfdocument format="pdf" variable="dok" pagetype="A4" fontembed="yes">
<h1>Invois Dalam Memori</h1>
<p>Kandungan disimpan ke pemboleh ubah, bukan ke fail.</p>
</cfdocument>
<cfset sasaran = expandPath('./invois.pdf')>
<cffile action="write" file="#sasaran#" output="#dok#">
<cfoutput>Panjang binari PDF: #len(dok)# bait</cfoutput>
Satu fakta yang mudah tersalah faham: variable ialah alias bagi name dalam dokumentasi Lucee. Kedua-duanya menyimpan PDF ke dalam pemboleh ubah, dan dalam rig ujian blok di atas memulangkan len(dok) sebanyak 1160 bait. Pilih filename apabila PDF terus dimuat turun atau diarkib, dan pilih variable apabila anda mahu memproses binari dahulu, contohnya melampirkannya ke e-mel atau menyimpannya ke pangkalan data. Jadual di bawah meringkaskan bezanya.
Ada satu lagi laluan yang sering dilupakan: apabila kedua-dua atribut itu tidak dinyatakan, cfdocument menghantar PDF terus ke strim respons, jadi pelayar membukanya sebagai muat turun atau paparan sebaris bergantung pada pengepala. Ini berguna untuk laporan yang dijana atas permintaan tanpa mahu meninggalkan fail sisa pada cakera, tetapi ia menjadikan setiap permintaan menjana semula PDF itu dari awal.
| Aspek | filename | variable (name) |
|---|---|---|
| Destinasi | Fail terus pada cakera | Pemboleh ubah dalam memori |
| Langkah tambahan | Tiada | Perlu cffile atau cfcontent untuk keluarkan |
| Sesuai bila | Muat turun terus atau arkib | Diproses dahulu, contohnya lampiran e-mel |
| Risiko biasa | Direktori tiada kebenaran tulis | Terlupa keluarkan, PDF kosong |
Perangkap "terlupa keluarkan" ini diterangkan secara mendalam dalam artikel PDF cfdocument kosong, kerana ia ialah punca nombor satu PDF kosong tanpa sebarang mesej ralat.
Margin dalam inci, sentimeter dan titik
Margin cfdocument diukur dalam inci secara lalai, tetapi atribut unit menukarnya kepada cm atau pt. Empat atribut margintop, marginbottom, marginleft dan marginright menetapkan setiap sisi secara berasingan, manakala atribut margin tunggal menetapkan keempat-empat sisi serentak.
Untuk nilai berbeza setiap sisi, anda boleh menghantar struktur margin. Satu perangkap yang perlu ditegaskan: struktur itu mesti dibina dahulu dengan cfset, bukan ditulis terus sebagai nilai atribut. Menulis margin="{top:2,bottom:2,left:1.5,right:1.5}" memulangkan ralat Can't cast String to a value of type Struct, kerana enjin membaca nilai dalam tanda petik sebagai rentetan:
<cfset m = {top: 2, bottom: 2, left: 1.5, right: 1.5}>
<cfdocument format="pdf" filename="#expandPath('./margin.pdf')#" overwrite="true"
unit="cm" margin="#m#" pagetype="A4">
<h1>Margin Sentimeter</h1>
<p>Margin atas dan bawah 2 cm, kiri dan kanan 1.5 cm.</p>
</cfdocument>
<cfset info = getFileInfo(expandPath('./margin.pdf'))>
<cfoutput>Fail PDF dijana: #info.size# bait</cfoutput>
Nilai unit="cm" mengubah unit untuk margin, pageheight dan pagewidth sekaligus, jadi pastikan saiz halaman anda dinyatakan dalam unit yang sama dengan margin.
Selain sentimeter, unit juga menerima pt untuk titik, satu unit yang berguna apabila anda mahu kawalan halus yang sepadan dengan saiz fon. Satu nilai margin="1" menetapkan keempat-empat sisi kepada satu unit yang sama, manakala struktur memberi anda empat nilai bebas. Apabila unit tidak dinyatakan, nilai margin dibaca sebagai inci, dan itulah punca biasa margin yang kelihatan terlalu lebar apabila pembangun menyangka nilainya dalam sentimeter.
Header dan footer berbeza mengikut halaman
Tag cfdocumentitem dengan type="header" dan type="footer" mengulang kandungan pada setiap halaman, tetapi ia hanya memberi satu header dan satu footer untuk seluruh dokumen. Apabila laporan anda mempunyai bab berbeza yang masing-masing perlu header sendiri, balut setiap bab dalam tag cfdocumentsection dan letakkan cfdocumentitem di dalamnya:
<cfdocument format="pdf" filename="#expandPath('./seksyen.pdf')#" overwrite="true">
<cfdocumentsection name="Ringkasan">
<cfdocumentitem type="header"><p align="left">Bahagian A: Ringkasan</p></cfdocumentitem>
<cfdocumentitem type="footer"><p align="center">Muka #cfdocument.currentsectionpagenumber#</p></cfdocumentitem>
<h1>Ringkasan</h1>
<p>Kandungan bahagian pertama.</p>
</cfdocumentsection>
<cfdocumentsection name="Perincian">
<cfdocumentitem type="header"><p align="left">Bahagian B: Perincian</p></cfdocumentitem>
<cfdocumentitem type="footer"><p align="center">Muka #cfdocument.currentsectionpagenumber#</p></cfdocumentitem>
<h1>Perincian</h1>
<p>Kandungan bahagian kedua.</p>
</cfdocumentsection>
</cfdocument>
<cfset info = getFileInfo(expandPath('./seksyen.pdf'))>
<cfoutput>Fail PDF dijana: #info.size# bait</cfoutput>
cfdocumentsection juga menerima atribut orientation untuk menjadikan satu bahagian landskap dan bahagian lain potret, dan atribut name menetapkan nama penanda buku bagi bahagian itu.
Header dan footer tidak terhad kepada teks sahaja. Anda boleh meletakkan imej logo, garisan pemisah, atau blok HTML biasa di dalamnya, dan ia akan diulang pada setiap halaman bahagian itu. Satu lagi nilai type yang patut diketahui ialah pagebreak, yang memaksa permulaan halaman baharu di tengah dokumen tanpa perlu bahagian berasingan.
Penomboran halaman
Penomboran halaman dilakukan dengan tiga pemboleh ubah khas yang hanya wujud di dalam badan cfdocument. cfdocument.currentpagenumber memulangkan nombor halaman semasa, cfdocument.totalpagecount memulangkan jumlah halaman keseluruhan dokumen, dan cfdocument.currentsectionpagenumber memulangkan nombor halaman dalam bahagian semasa:
<cfdocument format="pdf" filename="#expandPath('./berhalaman.pdf')#" overwrite="true"
margintop="1" marginbottom="1">
<cfdocumentitem type="header"><p align="right">Laporan Suku Tahunan</p></cfdocumentitem>
<cfdocumentitem type="footer"><p align="center">Halaman #cfdocument.currentpagenumber# daripada #cfdocument.totalpagecount#</p></cfdocumentitem>
<h1>Bab Satu</h1>
<p>Kandungan bab pertama.</p>
</cfdocument>
<cfset info = getFileInfo(expandPath('./berhalaman.pdf'))>
<cfoutput>Fail PDF dijana: #info.size# bait</cfoutput>
Pemboleh ubah ini ialah cara paling mudah untuk invois dan laporan yang memerlukan "Halaman 1 daripada 5" pada setiap muka surat tanpa anda mengira halaman sendiri. currentsectionpagenumber berguna apabila setiap bab bermula semula pada halaman satu. Apabila header atau footer anda membawa teks dinamik seperti nombor dokumen atau nama pelanggan, teks itu boleh dibina dengan fungsi rentetan CFML seperti yang diterangkan dalam panduan fungsi rentetan.
Satu peringatan penting: ketiga-tiga pemboleh ubah ini hanya wujud di dalam badan cfdocument, dan membacanya di luar tag memulangkan ralat kerana pemboleh ubah itu tidak ditakrifkan. Ia juga bersifat khusus enjin, jadi jangan andaikan gelagatnya sama persis dengan Adobe ColdFusion apabila anda memindahkan templat antara kedua-duanya.
Fon terbenam dan fontdirectory
Atribut fontembed ialah boolean yang menentukan sama ada fon dibenamkan ke dalam PDF. Dengan fontembed="yes", fail membawa fon bersamanya, jadi dokumen kelihatan sama pada setiap mesin yang membukanya. Tanpa pembenaman, PDF merujuk fon mengikut nama sahaja, dan mesin yang tiada fon itu akan menggantikannya dengan fon lain, menyebabkan susun atur berubah.
Untuk fon tersuai yang bukan fon lalai, atribut fontdirectory menunjuk kepada direktori yang mengandungi fail .ttf pada pelayan, dan enjin moden menggunakan nama fail fon itu sebagai nama font-family dalam HTML:
<cfdocument format="pdf" filename="#expandPath('./fon.pdf')#" overwrite="true"
fontdirectory="/usr/share/fonts/truetype/dejavu" fontembed="yes">
<h1 style="font-family: DejaVuSerif;">Tajuk dengan Fon Serif Tersuai</h1>
<p>Teks biasa menggunakan fon lalai.</p>
</cfdocument>
<cfset info = getFileInfo(expandPath('./fon.pdf'))>
<cfoutput>Fail PDF dijana: #info.size# bait</cfoutput>
Contoh di atas menggunakan direktori DejaVu yang lazim ada pada pelayan Linux. Pada pelayan anda, tunjuk fontdirectory ke direktori yang sebenarnya mengandungi fail .ttf, dan padankan nilai font-family dengan nama fail tanpa sambungan, dengan huruf besar kecil yang sama.
Padanan huruf besar kecil ini penting kerana enjin PDF moden Lucee membaca nama fon terus daripada nama fail .ttf, bukan daripada jadual nama dalaman fon. Fail DejaVuSerif.ttf hanya dikenali sebagai font-family: DejaVuSerif, dan menulis dejavuserif akan diabaikan secara senyap, menjadikan teks kembali kepada fon lalai tanpa sebarang ralat.
Had sebenar cfdocument
cfdocument ialah alat yang kuat untuk invois, laporan dan surat, tetapi ia bukan pelayar web penuh. Enjin PDF moden yang digunakannya memahami subset CSS sahaja, bukan keseluruhan CSS moden. Susun atur flexbox dan grid tidak berfungsi seperti dalam pelayar, animasi dan JavaScript tidak dijalankan langsung, dan kesan CSS lanjutan seperti bayang-bayang dan kecerunan sering diabaikan atau dirender berbeza.
Kekal dengan susun atur berasaskan blok dan jadual yang ringkas, gunakan sifat page-break-before dan page-break-after untuk memaksa pemisahan halaman, dan uji hasil PDF sebenar sebelum mempercayai apa yang anda lihat dalam pelayar. Imej besar juga menaikkan saiz PDF dengan cepat, jadi kecilkan imej dahulu seperti yang diterangkan dalam panduan memproses imej.
Engin moden ini ialah enjin lalai dalam Lucee, dikawal oleh tetapan this.pdf.type dalam Application.cfc. Ia lebih baik daripada enjin klasik lama dalam mematuhi CSS, tetapi kedua-duanya masih jauh daripada pelayar web. Untuk dokumen yang menuntut reka letak tepat seperti templat sijil atau borang kerajaan, jana HTML anda, buka PDF yang terhasil, dan laraskan markup sehingga rupa sebenar betul, kerana itulah satu-satunya cara untuk mengetahui hasilnya.
Apabila PDF sudah dijana dan anda perlu memanipulasinya, contohnya menggabungkan beberapa invois atau mengekstrak teks, itu kerja tag cfpdf, bukan cfdocument. Kedua-duanya saling melengkapi, dan sempadannya diterangkan dalam panduan cfpdf.
Soalan Lazim
Apakah perbezaan filename dan variable dalam cfdocument?
filename menulis PDF terus ke fail pada cakera, manakala variable menyimpan binari PDF ke dalam pemboleh ubah memori yang perlu anda keluarkan sendiri dengan cffile atau cfcontent. variable ialah alias bagi atribut name.
Bagaimana cara menetapkan margin dalam sentimeter?
Tambah atribut unit="cm" pada cfdocument, dan nyatakan margin melalui margintop, marginbottom, marginleft dan marginright, atau melalui struktur margin yang dibina dengan cfset terlebih dahulu.
Bagaimana cara memberi header berbeza pada setiap bab?
Balut setiap bab dalam tag cfdocumentsection dan letakkan cfdocumentitem type="header" di dalamnya. Setiap bahagian boleh membawa header dan footer sendiri, serta orientasi halaman sendiri.
Bagaimana cara memaparkan nombor halaman dalam PDF?
Guna #cfdocument.currentpagenumber# untuk nombor halaman semasa, #cfdocument.totalpagecount# untuk jumlah halaman, dan #cfdocument.currentsectionpagenumber# untuk nombor dalam bahagian semasa, di dalam cfdocumentitem type="footer".
Kenapa CSS flexbox dan grid tidak berfungsi dalam PDF saya?
Enjin PDF cfdocument memahami subset CSS sahaja, bukan CSS moden penuh. Kekal dengan susun atur blok dan jadual yang ringkas, dan gunakan page-break-before serta page-break-after untuk kawalan halaman.