Menambahkan tanda air teks dan gambar, atau latar belakang, ke halaman
Sekilas
Bagian berjudul “Sekilas”Anda dapat menambahkan tanda “DRAFT” atau “CONFIDENTIAL” di setiap halaman, atau menempatkan logo samar di belakang konten. Resep ini menunjukkan cara menambahkan keduanya ke halaman core NextPDF melalui permukaan dokumen publik: setAlpha() untuk transparansi, startTransform() / rotate() / stopTransform() untuk stempel diagonal, text() untuk tanda teks, dan image() untuk latar belakang raster.
Tanda air dan latar belakang berbeda dalam satu pilihan: urutan penggambaran.
- Latar belakang: gambar terlebih dahulu, lalu tulis konten halaman Anda di atasnya. Tanda berada di belakang teks.
- Tanda air hamparan: tulis konten halaman Anda terlebih dahulu, lalu gambar tandanya di atasnya. Tanda berada di lapisan atas.
NextPDF menggambar konten sesuai urutan pemanggilan Anda, sehingga urutan panggilan menentukan urutan lapisan. Tidak ada “mode latar belakang” tersendiri. Anda memilih lapisan dengan menentukan kapan konten digambar.
Prasyarat: instalasi core (composer require nextpdf/core:^3), dan, untuk latar belakang gambar, berkas raster yang dapat dibaca (PNG, JPEG, atau WebP) di disk. Seluruh alur berjalan di dalam proses, tanpa peramban headless atau panggilan jaringan.
Instalasi
Bagian berjudul “Instalasi”composer require nextpdf/core:^3Gambaran konseptual
Bagian berjudul “Gambaran konseptual”Setiap tanda yang Anda tambahkan adalah konten halaman biasa yang digambar melalui graphics state. Tiga bagian dari permukaan publik bekerja sama untuk menghasilkan tanda air:
-
Transparansi.
setAlpha(float $alpha, BlendMode $mode = BlendMode::Normal)mengatur opasitas isian untuk semua yang Anda gambar setelahnya, dari0.0(tidak terlihat) hingga1.0(buram). Tanda air biasanya paling efektif pada0.1hingga0.3, sehingga konten di bawahnya tetap dapat dibaca. Mode pembauran berasal dari enumNextPDF\Graphics\BlendMode. Sebagai contoh,BlendMode::Multiplymenggelapkan area tempat tanda bertumpang tindih dengan konten. -
Rotasi. Stempel diagonal adalah teks yang diputar di sekitar titik poros.
startTransform()menyimpan graphics state,rotate(float $angle, float $x, float $y)memutar sistem koordinat berlawanan arah jarum jam dengan poros pada($x, $y), danstopTransform()memulihkan state yang tersimpan. Membungkus tanda dalam blok transform mencegah rotasi dan alpha memengaruhi bagian halaman lainnya. -
Tanda itu sendiri.
text(float $x, float $y, string $text)menulis string pada posisi absolut dengan fon, warna, dan alpha saat ini.image(string $file, ?float $x, ?float $y, ?float $width, ?float $height)menempatkan gambar raster: dasar untuk tanda air gambar atau latar belakang satu halaman penuh.
Graphics state dipulihkan dengan bersih karena startTransform() dan stopTransform() mengapit perubahannya. Nilai setAlpha() bertahan hingga Anda mengaturnya lagi. Jika konten berikutnya harus sepenuhnya buram, atur ulang opasitas ke 1.0 setelah tanda. Pola yang lebih aman, seperti ditunjukkan di bawah ini, menggambar tanda di dalam blok transform-nya sendiri dan mengatur alpha konten halaman secara eksplisit.
Paket ini juga menyertakan objek nilai NextPDF\Graphics\Watermark dan NextPDF\Graphics\WatermarkPosition. Watermark adalah wadah konfigurasi yang tidak dapat diubah untuk teks, ukuran fon, sudut, warna, flag hamparan, dan praatur posisi, seperti WatermarkPosition::Diagonal. Objek-objek ini memodelkan parameter tanda air. Resep ini menggambar tanda dengan metode-metode penggambaran halaman di atas, sehingga keluarannya langsung masuk ke aliran konten halaman.
Permukaan API
Bagian berjudul “Permukaan API”Semua metode di bawah ini bersifat publik pada NextPDF\Core\Document dan mengembalikan static, sehingga Anda dapat merantainya.
setAlpha(float $alpha, BlendMode $mode = BlendMode::Normal): static: mengatur opasitas isian (0.0-1.0) dan mode pembauran untuk konten berikutnya.startTransform(): static: menyimpan graphics state (memancarkanq).rotate(float $angle, float $x = 0, float $y = 0): static: memutar sistem koordinat sebesar$anglederajat berlawanan arah jarum jam dengan poros pada($x, $y).stopTransform(): static: memulihkan state yang disimpan olehstartTransform()(memancarkanQ), sekaligus membatalkan rotasi dan perubahan alpha.setFont(string $family, string $style = '', float $size = 12.0): static: memilih fon untuk tanda. Keluarga Base-14helveticaselalu tersedia dan tidak memerlukan berkas fon.setTextColor(int $r, int $g = -1, int $b = -1): static: mengatur warna tanda dalam komponen merah, hijau, biru (atau satu nilai skala abu-abu).text(float $x, float $y, string $text): static: menulis tanda pada posisi absolut.image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static: menempatkan gambar raster, dasar untuk tanda air gambar atau latar belakang satu halaman penuh.getPageWidth(): float/getPageHeight(): float: membaca ukuran halaman saat ini dalam poin sehingga Anda dapat memusatkan tanda.
Tipe-tipe pendukung berada di bawah NextPDF\Graphics: enum BlendMode, objek nilai Color, dan pasangan konfigurasi Watermark / WatermarkPosition.
Contoh kode — Mulai cepat
Bagian berjudul “Contoh kode — Mulai cepat”Contoh ini menulis satu halaman, menggambar stempel diagonal “DRAFT” yang samar di atas konten, dan menyimpan berkasnya. Contoh ini mengabaikan penanganan kesalahan untuk menunjukkan bentuk pemanggilannya. Contoh produksi di bawah ini menambahkan seluruh pengaman.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();
// Page content first, so the watermark lands on top of it.$doc->setFont('helvetica', '', 12);$doc->text(20.0, 40.0, 'Quarterly report: internal review copy.');
// Watermark second: a translucent, rotated stamp through the page center.$pivotX = $doc->getPageWidth() / 2.0;$pivotY = $doc->getPageHeight() / 2.0;
$doc->startTransform();$doc->setAlpha(0.15);$doc->setTextColor(150, 150, 150);$doc->setFont('helvetica', 'B', 72);$doc->rotate(45.0, $pivotX, $pivotY);$doc->text($pivotX - 110.0, $pivotY, 'DRAFT');$doc->stopTransform();
file_put_contents(__DIR__ . '/watermarked.pdf', $doc->getPdfData());Contoh kode — Produksi
Bagian berjudul “Contoh kode — Produksi”Program mandiri ini menggambar tanda air teks diagonal di atas konten yang dihasilkan. Ketika Anda memberikan path gambar melalui variabel lingkungan NEXTPDF_WATERMARK_IMAGE, program ini menempatkan gambar tersebut sebagai latar belakang samar yang terpusat pada halaman kedua. Program ini memvalidasi path gambar sebelum digunakan, menangkap eksepsi NextPDF yang paling spesifik, dan menulis hasilnya ke path yang dikendalikan server. Ganti konten dalam memori dengan konten Anda sendiri, lalu teruskan keluarannya ke lapisan respons atau penyimpanan Anda.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;use NextPDF\Exception\ImageProcessingException;use NextPDF\Exception\NextPdfException;use NextPDF\Exception\PageLayoutException;
/** * Paint a translucent, rotated text stamp across the current page. * * The mark is bracketed in a transform block so the rotation and the alpha * change are undone together and never leak into later content. * * @param non-empty-string $mark The watermark text (for example "CONFIDENTIAL") */function paintTextWatermark(Document $doc, string $mark): void{ $pivotX = $doc->getPageWidth() / 2.0; $pivotY = $doc->getPageHeight() / 2.0;
// Estimate the mark width so the rotated text sits centered on the pivot. // Helvetica averages ~0.5 em per glyph; half the width offsets the origin. $fontSize = 64.0; $halfWidth = (\strlen($mark) * $fontSize * 0.5) / 2.0;
$doc->startTransform(); $doc->setAlpha(0.12); $doc->setTextColor(120, 120, 120); $doc->setFont('helvetica', 'B', $fontSize); $doc->rotate(45.0, $pivotX, $pivotY); $doc->text($pivotX - $halfWidth, $pivotY, $mark); $doc->stopTransform();}
/** * Place a raster image as a faint, full-page background behind later content. * * The image is drawn first and at low opacity; page content written after this * call sits over it. The path is validated by the caller before it arrives. * * @param non-empty-string $imagePath A readable raster image (PNG, JPEG, WebP) * * @throws ImageProcessingException If the file is missing, unreadable, or corrupt. * @throws PageLayoutException If the placement coordinates are rejected. */function paintImageBackground(Document $doc, string $imagePath): void{ $doc->startTransform(); $doc->setAlpha(0.08); // Cover the full page: origin at the top-left, sized to the page box. $doc->image( file: $imagePath, x: 0.0, y: 0.0, width: $doc->getPageWidth(), height: $doc->getPageHeight(), ); $doc->stopTransform();}
$doc = Document::createStandalone();$doc->setTitle('Watermark and background sample');
// Page 1: content first, then an overlay text watermark on top.$doc->addPage();$doc->setAlpha(1.0);$doc->setTextColor(0, 0, 0);$doc->setFont('helvetica', '', 12);$doc->text(20.0, 40.0, 'Quarterly report: internal review copy.');
try { paintTextWatermark($doc, 'CONFIDENTIAL');} catch (PageLayoutException $e) { // Raised if a coordinate or page state is rejected while placing the mark. throw new RuntimeException( sprintf('Watermark placement failed: %s', $e->getConstraint()), previous: $e, );}
// Page 2: an optional image background, then content over it.$imagePath = getenv('NEXTPDF_WATERMARK_IMAGE');
if ($imagePath !== false && $imagePath !== '') { // Validate the path before touching the image loader: reject NUL bytes, // require a real readable file, and resolve it to defeat path traversal. if (str_contains($imagePath, "\0")) { throw new RuntimeException('Image path must not contain NUL bytes.'); }
$resolved = realpath($imagePath);
if ($resolved === false || !is_file($resolved) || !is_readable($resolved)) { throw new RuntimeException( sprintf('Background image "%s" is not a readable file.', $imagePath), ); }
$doc->addPage();
try { paintImageBackground($doc, $resolved); } catch (ImageProcessingException $e) { // Raised when the file cannot be decoded as a supported raster format. throw new RuntimeException( sprintf( 'Background image rejected (%s, op "%s").', $e->getFormat(), $e->getOperation(), ), previous: $e, ); } catch (PageLayoutException $e) { throw new RuntimeException( sprintf('Background placement failed: %s', $e->getConstraint()), previous: $e, ); }
$doc->setAlpha(1.0); $doc->setTextColor(0, 0, 0); $doc->setFont('helvetica', '', 12); $doc->text(20.0, 40.0, 'Page two over a faint background.');}
try { $pdf = $doc->getPdfData();} catch (NextPdfException $e) { // Base of the NextPDF exception hierarchy: any output-stage failure. throw new RuntimeException( sprintf('Document output failed: %s', $e->getMessage()), previous: $e, );}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$path = $out !== false && $out !== '' ? $out : __DIR__ . '/watermarked.pdf';
if (file_put_contents($path, $pdf) === false) { throw new RuntimeException(sprintf('Could not write PDF to "%s".', $path));}
printf("Wrote %d-byte PDF to %s\n", strlen($pdf), $path);Keluaran standar (STDOUT) yang diharapkan (ukuran byte bergantung pada build dan apakah Anda memberikan gambar):
Wrote <n>-byte PDF to <path>Kasus tepi & jebakan
Bagian berjudul “Kasus tepi & jebakan”- Urutan lapisan adalah urutan panggilan. Latar belakang adalah konten yang digambar sebelum konten halaman Anda. Tanda air hamparan adalah konten yang digambar setelahnya. Tidak ada flag yang mengatur ulang urutan lapisan; cukup pindahkan panggilannya.
- Alpha bertahan hingga diatur ulang.
setAlpha()mengubah state untuk semua yang digambar setelahnya. Apit tanda dalamstartTransform()/stopTransform(), yang memulihkan alpha sebelumnya, atau panggilsetAlpha(1.0)sebelum konten buram. Contoh produksi melakukan keduanya. - Seimbangkan setiap blok transform. Setiap
startTransform()membutuhkanstopTransform()yang sepadan. Blok yang tidak seimbang membuat rotasi atau alpha tetap diterapkan pada konten berikutnya, danstopTransform()yang hilang menciptakan ketidakseimbangan graphics state yang ditolak oleh penulis saat tahap keluaran. rotate()berporos dalam koordinat pengguna. Poros($x, $y)dinyatakan dalam satuan pengguna yang diukur dari sudut kiri atas halaman, dalam kerangka yang sama dengantext(). Untuk diagonal yang melintasi pusat, gunakan pusat halaman (getPageWidth() / 2,getPageHeight() / 2).- Teks yang diputar membutuhkan offset lebar manual.
text()menempatkan titik asal string; ia tidak memusatkannya untuk Anda. Kurangi kira-kira setengah dari perkiraan lebar teks dari poros X agar tanda yang diputar melintang di pusat, seperti yang dilakukan helper. - Gambar diskalakan ke kotak yang Anda berikan.
image()meregangkan raster kewidthdanheightyang Anda berikan. Untuk latar belakang satu halaman penuh, berikan lebar dan tinggi halaman; untuk logo di sudut, berikan ukuran aslinya. Dimensi nol atau negatif memunculkanPageLayoutException. image()menolak URL dan byte NUL. Pathscheme://atau byte NUL dalam$filememunculkanPageLayoutExceptionsebelum dekode apa pun. Berikan hanya path lokal yang sudah divalidasi.- Tanda adalah konten yang terlihat. Tanda air yang digambar dengan cara ini adalah konten halaman nyata, bukan anotasi tersembunyi. Siapa pun yang memiliki berkas tersebut dapat membacanya. Ini adalah isyarat visual, bukan kontrol akses.
Performa
Bagian berjudul “Performa”Tanda air teks menggunakan sedikit operator aliran konten per halaman dan menambahkan waktu atau memori yang dapat diabaikan. Tanda air atau latar belakang gambar memerlukan satu dekode raster ditambah byte gambar yang tertanam dalam keluaran. Menggunakan ulang gambar yang sama di seluruh halaman akan menggunakan XObject yang sudah didekode melalui cache gambar, sehingga Anda membayar biaya dekode hanya sekali. Sesuaikan ukuran gambar latar belakang dengan kotak tampilannya sebelum menanamkannya. Foto 4000 px yang diskalakan ke halaman letter membawa byte yang tidak pernah dilihat pembaca. Tanda air teks satu halaman yang umum tetap jauh di bawah anggaran waktu 500 ms dan puncak 32 MB. Latar belakang gambar mengikuti ukuran raster sumber yang sudah didekode.
Catatan keamanan
Bagian berjudul “Catatan keamanan”Alur berjalan di dalam proses. Tidak ada byte dokumen yang meninggalkan host, dan tidak ada panggilan jaringan yang dilakukan. Perlakukan setiap path gambar yang berasal dari luar kode Anda sebagai masukan yang tidak tepercaya.
- Validasi path gambar sebelum digunakan. Tolak byte NUL, selesaikan path dengan
realpath(), dan pastikanis_file()danis_readable()sebelum Anda memanggilimage(), persis seperti yang dilakukan contoh produksi. Ini memblokir path traversal serta menolak direktori dan tautan rusak sejak dini. - Jangan pernah menyisipkan bidang permintaan ke dalam sebuah path. Turunkan path gambar dan path keluaran dari nilai yang dikendalikan server, bukan dari parameter permintaan. Ini mencegah Anda membaca atau menulis berkas di luar direktori yang dimaksud.
- Perlakukan gambar yang tidak tepercaya sebagai masukan berbahaya. Raster yang cacat memunculkan
ImageProcessingExceptionalih-alih merusak dokumen, dan loader membatasi dimensi gambar untuk menahan masukan bom dekompresi. Tangkap eksepsi tersebut dan tolak unggahannya. Jangan mencoba ulang secara membabi buta. - Tanda air bukan tempat menyimpan rahasia. Tanda adalah konten yang terlihat. Jangan menyandikan kredensial, token, atau pengidentifikasi internal dalam tanda air atau latar belakang yang Anda kembalikan ke klien.
Kesesuaian
Bagian berjudul “Kesesuaian”Resep ini tidak membuat klaim standar normatif tersendiri. Resep ini menyusun primitif publik alpha, transform, text, dan image. Setiap primitif memancarkan operator aliran konten PDF standar. Graphics state diisolasi dengan operator q / Q yang dipancarkan oleh startTransform() dan stopTransform(), dan transparansi dibawa melalui parameter graphics state ExtGState. Keluarannya baru secara struktural, bukan stabil per byte, sehingga halaman ini mendeklarasikan profil reproduksibilitas structural. Untuk detail tingkat operator tentang permukaan transform dan graphics state, lihat referensi modul Graphics.
Lihat juga
Bagian berjudul “Lihat juga”- Referensi modul Graphics: permukaan path, transform, warna, dan graphics state lengkap di balik metode-metode ini.
- Menanamkan gambar: memuat, mengukur, dan menempatkan gambar raster sebagai dasar untuk tanda air atau latar belakang gambar.
- Gradien dan transparansi: permukaan alpha dan mode pembauran secara mendalam, termasuk isian tembus pandang.
- Mentransformasi ruang koordinat: memutar, menskalakan, dan mentranslasi konten dengan blok transform yang seimbang.
- Penanganan kesalahan yang sadar eksepsi: hierarki eksepsi NextPDF di balik
ImageProcessingException,PageLayoutException, danNextPdfException.