İçeriğe geç

Sayfalara metin ve resim filigranları ya da arka planları ekleme

Her sayfaya bir “DRAFT” veya “CONFIDENTIAL” işareti ekleyebilir ya da içeriğin arkasına soluk bir logo yerleştirebilirsiniz. Bu tarif, her iki yaklaşımı da genel belge yüzeyini kullanarak NextPDF core sayfalarına uygular: saydamlık için setAlpha(), çapraz bir damga için startTransform() / rotate() / stopTransform(), işaret için text() ve raster bir arka plan için image().

Filigran ile arka planı ayıran tek karar boyama sırasıdır.

  • Arka plan: önce çizin, ardından sayfa içeriğinizi onun üzerine yazın. İşaret metnin arkasında kalır.
  • Bindirme filigranı: önce sayfa içeriğinizi yazın, ardından işareti onun üzerine çizin. İşaret en üstte kalır.

NextPDF, içeriği çağırdığınız sırayla çizer; dolayısıyla çağrı sıranız katman sırasını belirler. Ayrı bir “arka plan modu” yoktur. Katmanı, ne zaman çizeceğinizi seçerek belirlersiniz.

Ön koşullar: bir core kurulumu (composer require nextpdf/core:^3) ve resim arka planı için diskte okunabilir bir raster dosyası (PNG, JPEG veya WebP). Tüm işlem hattı, başsız tarayıcı veya ağ çağrısı olmadan süreç içinde çalışır.

Terminal window
composer require nextpdf/core:^3

Eklediğiniz her işaret, bir grafik durumu üzerinden çizilen normal sayfa içeriğidir. Bir filigran üretmek için genel yüzeyin üç parçası birlikte çalışır:

  1. Saydamlık. setAlpha(float $alpha, BlendMode $mode = BlendMode::Normal), daha sonra çizdiğiniz her şey için dolgu opaklığını 0.0 (görünmez) ile 1.0 (opak) arasında ayarlar. Bir filigran genellikle 0.1 ile 0.3 arasında en iyi sonucu verir; böylece alttaki içerik okunabilir kalır. Karışım modu NextPDF\Graphics\BlendMode enum’ından alınır. Örneğin BlendMode::Multiply, işaretin içerikle örtüştüğü alanları koyulaştırır.

  2. Döndürme. Çapraz bir damga, bir eksen noktası etrafında döndürülmüş metindir. startTransform() grafik durumunu kaydeder, rotate(float $angle, float $x, float $y) koordinat sistemini ($x, $y) etrafında saat yönünün tersine döndürür ve stopTransform() kaydedilen durumu geri yükler. İşareti bir dönüşüm bloğuna almak, döndürmenin ve alpha’nın sayfanın geri kalanını etkilemesini önler.

  3. İşaretin kendisi. text(float $x, float $y, string $text), geçerli yazı tipi, renk ve alpha ile mutlak bir konuma bir dize yazar. image(string $file, ?float $x, ?float $y, ?float $width, ?float $height) bir raster resim yerleştirir: bir resim filigranının veya tam sayfa bir arka planın temeli.

Grafik durumu temiz biçimde geri yüklenir; çünkü startTransform() ve stopTransform() değişikliği sınırlar. setAlpha() değeri, yeniden ayarlayana kadar kalıcıdır. Sonraki içeriğin tam opak olması gerekiyorsa, işaretten sonra opaklığı 1.0 değerine sıfırlayın. Aşağıda gösterilen daha güvenli kalıp, işareti kendi dönüşüm bloğunun içinde çizer ve sayfa içeriğinin alpha’sını açıkça ayarlar.

Paket ayrıca NextPDF\Graphics\Watermark ve NextPDF\Graphics\WatermarkPosition değer nesnelerini de içerir. Watermark, metin, yazı tipi boyutu, açı, renk, bindirme bayrağı ve WatermarkPosition::Diagonal gibi konum ön ayarları için değişmez bir yapılandırma taşıyıcısıdır. Bu nesneler bir filigranın parametrelerini modeller. Bu tarif, işareti yukarıdaki yöntemlerle sayfaya çizer; böylece çıktı doğrudan sayfa içerik akışına ulaşır.

Aşağıdaki tüm yöntemler NextPDF\Core\Document üzerinde geneldir ve static döndürür; böylece bunları zincirleyebilirsiniz.

  • setAlpha(float $alpha, BlendMode $mode = BlendMode::Normal): static: sonraki içerik için dolgu opaklığını (0.0-1.0) ve karışım modunu ayarlar.
  • startTransform(): static: grafik durumunu kaydeder (q yayar).
  • rotate(float $angle, float $x = 0, float $y = 0): static: koordinat sistemini $angle derece, ($x, $y) ekseni etrafında saat yönünün tersine döndürür.
  • stopTransform(): static: startTransform() tarafından kaydedilen durumu geri yükler (Q yayar) ve döndürme ile alpha değişikliğini birlikte geri alır.
  • setFont(string $family, string $style = '', float $size = 12.0): static: işaret için yazı tipini seçer. Base-14 ailesi helvetica her zaman kullanılabilir ve bir yazı tipi dosyasına gerek duymaz.
  • setTextColor(int $r, int $g = -1, int $b = -1): static: işaret rengini kırmızı, yeşil, mavi olarak (veya tek bir gri tonlamalı değer olarak) ayarlar.
  • text(float $x, float $y, string $text): static: işareti mutlak bir konuma yazar.
  • image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static: bir raster resim yerleştirir; bir resim filigranının veya tam sayfa bir arka planın temeli.
  • getPageWidth(): float / getPageHeight(): float: işareti ortalayabilmeniz için geçerli sayfa boyutunu nokta cinsinden okur.

Yardımcı türler NextPDF\Graphics altında yer alır: BlendMode enum’ı, Color değer nesnesi ve Watermark / WatermarkPosition yapılandırma çifti.

Bu örnek bir sayfa oluşturur, içeriğin üzerine soluk ve çapraz bir “DRAFT” damgası çizer ve dosyayı kaydeder. Hata işlemeyi, çağrı biçimini göstermek için dışarıda bırakır. Aşağıdaki üretim örneği gerekli korumaların tamamını ekler.

<?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());

Kendi içinde eksiksiz olan bu program, oluşturulan içeriğin üzerine çapraz bir metin filigranı çizer. NEXTPDF_WATERMARK_IMAGE ortam değişkeni aracılığıyla bir resim yolu sağladığınızda, o resmi ikinci bir sayfada soluk ve ortalanmış bir arka plan olarak yerleştirir. Resim yolunu kullanmadan önce doğrular, en özel NextPDF istisnalarını yakalar ve sonucu sunucu denetimli bir yola yazar. Bellekte oluşturulan içeriği kendi içeriğinizle değiştirin, ardından çıktıyı yanıtınıza veya depolama katmanınıza bağlayın.

<?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);

Beklenen standart çıktı (STDOUT) şöyledir (bayt boyutu derlemeye ve bir resim sağlayıp sağlamadığınıza bağlıdır):

Wrote <n>-byte PDF to <path>
  • Katman sırası çağrı sırasıdır. Bir arka plan, sayfa içeriğinizden önce çizilen içeriktir. Bir bindirme filigranı, ondan sonra çizilen içeriktir. Hiçbir bayrak katmanları yeniden sıralamaz; bunun yerine çağrının yerini değiştirin.
  • Alpha sıfırlanana kadar kalıcıdır. setAlpha() daha sonra çizilen her şey için durumu değiştirir. İşareti ya önceki alpha’yı geri yükleyen startTransform() / stopTransform() bloğuyla sınırlayın ya da opak içerikten önce setAlpha(1.0) çağırın. Üretim örneği her ikisini de yapar.
  • Her dönüşüm bloğunu dengeleyin. Her startTransform() eşleşen bir stopTransform() gerektirir. Dengesiz bir blok, döndürmeyi veya alpha’yı sonraki içeriğe uygulanmış halde bırakır ve eksik bir stopTransform(), yazıcının çıktıda reddedeceği bir grafik durumu dengesizliği yaratır.
  • rotate() kullanıcı koordinatlarında döndürür. Eksen ($x, $y), text() ile aynı çerçevede, sayfanın sol üstünden ölçülen kullanıcı birimleri cinsindendir. Merkezden geçen bir çapraz için sayfa merkezini kullanın (getPageWidth() / 2, getPageHeight() / 2).
  • Döndürülmüş metin için genişlik kaydırmasını elle yapmanız gerekir. text() dizenin başlangıç noktasını yerleştirir; sizin yerinize ortalamaz. Döndürülmüş işaretin merkeze oturması için, yardımcı işlevin yaptığı gibi, tahmini metin genişliğinin yaklaşık yarısını eksen X’ten çıkarın.
  • Resimler verdiğiniz kutuya ölçeklenir. image(), rasteri verdiğiniz width ve height değerlerine uzatır. Tam sayfa bir arka plan için sayfa genişliği ve yüksekliğini iletin; köşe logosu için doğal boyutunu iletin. Sıfır veya negatif bir boyut PageLayoutException oluşturur.
  • image() URL’leri ve NUL baytlarını reddeder. Bir scheme:// yolu veya $file içindeki bir NUL baytı, herhangi bir çözümlemeden önce PageLayoutException oluşturur. Yalnızca yerel, doğrulanmış bir yol iletin.
  • İşaret görünür içeriktir. Bu şekilde çizilen bir filigran, gizli bir ek açıklama değil, gerçek sayfa içeriğidir. Dosyaya sahip olan herkes bunu okuyabilir. Bu bir erişim denetimi değil, görsel bir işarettir.

Bir metin filigranı, sayfa başına birkaç içerik akışı operatörü kullanır ve ihmal edilebilir düzeyde zaman ya da bellek yükü ekler. Bir resim filigranı veya arka planı, bir raster çözümleme artı çıktıdaki gömülü resim baytları kadar maliyetlidir. Aynı resmi sayfalar arasında yeniden kullanmak, çözümlenmiş XObject’i resim önbelleği aracılığıyla yeniden kullanır; böylece çözümleme maliyetini bir kez ödersiniz. Arka plan resimlerini gömmeden önce görüntülenecekleri kutulara göre boyutlandırın. Bir letter sayfasına ölçeklenen 4000 px’lik bir fotoğraf, okuyucunun hiç görmediği baytları depolar. Tipik bir tek sayfa metin filigranı, 500 ms süre ve 32 MB tepe bütçesinin oldukça içinde kalır. Bir resim arka planı, kaynak rasterin çözümlenmiş boyutunu takip eder.

İşlem hattı aynı süreç içinde çalışır. Hiçbir belge baytı ana makineden ayrılmaz ve hiçbir ağ çağrısı yapılmaz. Kodunuzun dışından gelen herhangi bir resim yolunu güvenilmeyen girdi olarak değerlendirin.

  • Resim yolunu kullanmadan önce doğrulayın. NUL baytlarını reddedin, yolu realpath() ile çözümleyin ve is_file() ile is_readable() doğrulamalarını, tam olarak üretim örneğinin yaptığı gibi, image() çağırmadan önce yapın. Bu, yol geçişini engeller ve dizinlerle asılı bağlantıları erken aşamada reddeder.
  • Bir istek alanını asla doğrudan bir yola eklemeyin. Resim yolunu ve çıktı yolunu bir istek parametresinden değil, sunucu denetimli değerlerden türetin. Bu, amaçlanan dizinin dışındaki dosyaları okumanızı veya yazmanızı önler.
  • Güvenilmeyen resimleri düşmanca girdi olarak değerlendirin. Bozuk biçimli bir raster, belgeyi bozmak yerine ImageProcessingException oluşturur ve yükleyici, açma-bombası girdilerine direnmek için resim boyutlarını sınırlar. İstisnayı yakalayın ve yüklemeyi reddedin. Körü körüne yeniden denemeyin.
  • Filigran bir sır deposu değildir. İşaret görünür içeriktir. Bir istemciye döndürdüğünüz filigran veya arka plana kimlik bilgilerini, belirteçleri ya da dahili tanımlayıcıları kodlamayın.

Bu tarif, kendi başına hiçbir normatif standart iddiasında bulunmaz. Genel alpha, dönüşüm, metin ve resim ilkelerini bir araya getirir. Her ilke standart PDF içerik akışı operatörleri yayar. Grafik durumu, q / Q operatörleriyle yalıtılır; bu operatörleri startTransform() ve stopTransform() yayar ve saydamlık bir ExtGState grafik durumu parametresi üzerinden taşınır. Çıktı bayt-kararlı olmaktan çok yapısal olarak yenidir; bu nedenle bu sayfa bir structural yeniden üretilebilirlik profili bildirir. Dönüşüm ve grafik durumu yüzeyi hakkında operatör düzeyinde ayrıntı için bkz. Graphics modülü başvurusu.