Tekst- en afbeeldingswatermerken of achtergronden toevoegen aan pagina's
In het kort
Sectie met titel “In het kort”Je kunt een „DRAFT”- of „CONFIDENTIAL”-markering over elke pagina plaatsen, of een vaag logo achter de inhoud zetten. Dit recipe voegt beide toe aan NextPDF core-pagina’s via het publieke documentoppervlak: setAlpha() voor transparantie, startTransform() / rotate() / stopTransform() voor een diagonale stempel, text() voor de markering en image() voor een rasterachtergrond.
Een watermerk en een achtergrond verschillen in één keuze: de tekenvolgorde.
- Achtergrond: teken eerst, schrijf daarna je pagina-inhoud eroverheen. De markering komt achter de tekst te staan.
- Watermerk als overlay: schrijf eerst je pagina-inhoud, teken daarna de markering eroverheen. De markering komt bovenop te staan.
NextPDF tekent inhoud in de volgorde waarin je die aanroept, dus je aanroepvolgorde bepaalt de laagvolgorde. Er bestaat geen aparte „achtergrondmodus”. Je kiest de laag door te kiezen wanneer je tekent.
Vereisten: een core-installatie (composer require nextpdf/core:^3), en, voor een afbeeldingsachtergrond, een leesbaar rasterbestand (PNG, JPEG of WebP) op schijf. De volledige pijplijn draait in het proces, zonder headless browser of netwerkaanroep.
Installeren
Sectie met titel “Installeren”composer require nextpdf/core:^3Conceptueel overzicht
Sectie met titel “Conceptueel overzicht”Elke markering die u toevoegt, is gewone pagina-inhoud die via een grafische staat wordt getekend. Drie onderdelen van het publieke oppervlak werken samen om een watermerk te produceren:
-
Transparantie.
setAlpha(float $alpha, BlendMode $mode = BlendMode::Normal)stelt de vuldekking in voor alles wat je daarna tekent, van0.0(onzichtbaar) tot1.0(ondoorzichtig). Een watermerk werkt meestal het best bij0.1tot0.3, zodat de onderliggende inhoud leesbaar blijft. De overvloeimodus komt uit deNextPDF\Graphics\BlendMode-enum. Zo verdonkertBlendMode::Multiplybijvoorbeeld de gebieden waar de markering de inhoud overlapt. -
Rotatie. Een diagonale stempel is tekst die rond een draaipunt is gedraaid.
startTransform()slaat de grafische staat op,rotate(float $angle, float $x, float $y)draait het coördinatenstelsel linksom rond($x, $y), enstopTransform()herstelt de opgeslagen staat. Door de markering in een transformatieblok te plaatsen voorkom je dat de rotatie en alpha de rest van de pagina beïnvloeden. -
De markering zelf.
text(float $x, float $y, string $text)schrijft een tekenreeks op een absolute positie in het huidige lettertype, de huidige kleur en de huidige alpha.image(string $file, ?float $x, ?float $y, ?float $width, ?float $height)plaatst een rasterafbeelding: de basis voor een afbeeldingswatermerk of een paginavullende achtergrond.
De grafische staat wordt netjes hersteld doordat startTransform() en stopTransform() de wijziging omsluiten. De waarde van setAlpha() blijft van kracht totdat je die opnieuw instelt. Moet latere inhoud volledig ondoorzichtig zijn, stel de dekking dan na de markering terug op 1.0. Het veiligere patroon hieronder tekent de markering binnen een eigen transformatieblok en stelt de alpha van de pagina-inhoud expliciet in.
Het pakket bevat ook de waardeobjecten NextPDF\Graphics\Watermark en NextPDF\Graphics\WatermarkPosition. Watermark is een onveranderlijke configuratiehouder voor tekst, lettergrootte, hoek, kleur, overlayvlag en positievoorinstellingen, zoals WatermarkPosition::Diagonal. Deze objecten modelleren de parameters van een watermerk. Dit recipe tekent de markering met de methoden hierboven die rechtstreeks naar de pagina schrijven, zodat de uitvoer direct in de paginacontent-stream terechtkomt.
API-oppervlak
Sectie met titel “API-oppervlak”Alle onderstaande methoden zijn publiek op NextPDF\Core\Document en retourneren static, zodat je ze kunt koppelen.
setAlpha(float $alpha, BlendMode $mode = BlendMode::Normal): static: stel de vuldekking (0.0-1.0) en de overvloeimodus in voor volgende inhoud.startTransform(): static: sla de grafische staat op (zendtquit).rotate(float $angle, float $x = 0, float $y = 0): static: draai het coördinatenstelsel$anglegraden linksom rond draaipunt($x, $y).stopTransform(): static: herstel de staat die doorstartTransform()is opgeslagen (zendtQuit), waarmee de rotatie en de alpha-wijziging samen ongedaan worden gemaakt.setFont(string $family, string $style = '', float $size = 12.0): static: selecteer het lettertype voor de markering. De Base-14-familiehelveticais altijd beschikbaar en heeft geen fontbestand nodig.setTextColor(int $r, int $g = -1, int $b = -1): static: stel de kleur van de markering in via rood, groen, blauw (of een enkele grijswaarde).text(float $x, float $y, string $text): static: schrijf de markering op een absolute positie.image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static: plaats een rasterafbeelding, de basis voor een afbeeldingswatermerk of een paginavullende achtergrond.getPageWidth(): float/getPageHeight(): float: lees de huidige paginagrootte in punten uit, zodat je de markering kunt centreren.
Ondersteunende typen bevinden zich onder NextPDF\Graphics: de BlendMode-enum, het Color-waardeobject en het configuratiepaar Watermark / WatermarkPosition.
Codevoorbeeld — snelstart
Sectie met titel “Codevoorbeeld — snelstart”Dit voorbeeld schrijft één pagina, tekent een vage diagonale „DRAFT”-stempel over de inhoud en slaat het bestand op. Het laat foutafhandeling weg om de vorm van de aanroepen te laten zien. Het productievoorbeeld hieronder voegt de volledige beveiligingen toe.
<?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());Codevoorbeeld — productie
Sectie met titel “Codevoorbeeld — productie”Dit zelfstandige programma tekent een diagonaal tekstwatermerk over gegenereerde inhoud. Wanneer je een afbeeldingspad opgeeft via de omgevingsvariabele NEXTPDF_WATERMARK_IMAGE, plaatst het die afbeelding als vage, gecentreerde achtergrond op een tweede pagina. Het valideert het afbeeldingspad vóór gebruik, vangt de meest specifieke NextPDF-uitzonderingen op en schrijft het resultaat naar een door de server beheerd pad. Vervang de in-memory inhoud door je eigen inhoud en koppel de uitvoer vervolgens aan je response- of opslaglaag.
<?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);Verwachte standaarduitvoer (STDOUT) (de bytegrootte hangt af van de build en van de vraag of je een afbeelding hebt opgegeven):
Wrote <n>-byte PDF to <path>Randgevallen en valkuilen
Sectie met titel “Randgevallen en valkuilen”- Laagvolgorde is aanroepvolgorde. Een achtergrond is inhoud die vóór je pagina-inhoud wordt getekend. Een watermerk als overlay is inhoud die erna wordt getekend. Geen enkele vlag herschikt de lagen; verplaats in plaats daarvan de aanroep.
- Alpha blijft van kracht tot het opnieuw wordt ingesteld.
setAlpha()wijzigt de staat voor alles wat erna wordt getekend. Plaats de markering tussenstartTransform()/stopTransform(), die de eerdere alpha herstelt, of roepsetAlpha(1.0)aan vóór ondoorzichtige inhoud. Het productievoorbeeld doet beide. - Houd elk transformatieblok in evenwicht. Elke
startTransform()heeft een bijbehorendestopTransform()nodig. Een blok dat niet in evenwicht is, laat de rotatie of alpha op volgende inhoud doorwerken, en een ontbrekendestopTransform()veroorzaakt een onevenwicht in de grafische staat dat de writer bij uitvoer afwijst. rotate()draait om gebruikerscoördinaten. Het draaipunt($x, $y)is in gebruikerseenheden, gemeten vanaf de linkerbovenhoek van de pagina, in hetzelfde frame alstext(). Gebruik voor een diagonaal door het midden het paginamidden (getPageWidth() / 2,getPageHeight() / 2).- Gedraaide tekst vereist een handmatige breedteverschuiving.
text()plaatst de oorsprong van de tekenreeks; het centreert niet voor je. Trek ongeveer de helft van de geschatte tekstbreedte af van de draaipunt-X, zodat de gedraaide markering rond het midden komt te staan, zoals de helper doet. - Afbeeldingen schalen naar het vak dat je opgeeft.
image()rekt het raster uit naar dewidthenheightdie je opgeeft. Geef voor een paginavullende achtergrond de paginabreedte en -hoogte op; geef voor een hoeklogo de natuurlijke grootte op. Een nul- of negatieve afmeting veroorzaaktPageLayoutException. image()weigert URL’s en NUL-bytes. Eenscheme://-pad of een NUL-byte in$fileveroorzaaktPageLayoutExceptionvóór enige decodering. Geef alleen een lokaal, gevalideerd pad door.- De markering is zichtbare inhoud. Een op deze manier getekend watermerk is echte pagina-inhoud, geen verborgen annotatie. Iedereen met het bestand kan het lezen. Het is een visuele aanwijzing, geen toegangscontrole.
Prestaties
Sectie met titel “Prestaties”Een tekstwatermerk gebruikt een handvol content-stream-operatoren per pagina en voegt verwaarloosbaar weinig tijd of geheugen toe. Een afbeeldingswatermerk of -achtergrond kost één rasterdecodering plus de ingebedde afbeeldingsbytes in de uitvoer. Gebruik je dezelfde afbeelding op meerdere pagina’s opnieuw, dan wordt het gedecodeerde XObject via de afbeeldingscache hergebruikt, zodat je de decoderingskosten één keer betaalt. Pas de grootte van achtergrondafbeeldingen vóór het inbedden aan hun weergavevak aan. Een foto van 4000 px die naar een letter-pagina wordt geschaald, slaat bytes op die de lezer nooit ziet. Een typisch tekstwatermerk van één pagina blijft ruim binnen een budget van 500 ms wandkloktijd en 32 MB piekgeheugen. Een afbeeldingsachtergrond volgt de gedecodeerde grootte van het bronraster.
Beveiligingsnotities
Sectie met titel “Beveiligingsnotities”De pijplijn draait in het proces. Er verlaten geen documentbytes de host en er wordt geen netwerkaanroep gedaan. Behandel elk afbeeldingspad dat buiten je code ontstaat als niet-vertrouwde invoer.
- Valideer het afbeeldingspad vóór gebruik. Weiger NUL-bytes, los het pad op met
realpath(), en bevestigis_file()enis_readable()voordat jeimage()aanroept, precies zoals het productievoorbeeld doet. Dit blokkeert path traversal en wijst mappen en hangende koppelingen vroegtijdig af. - Interpoleer nooit een verzoekveld in een pad. Leid het afbeeldingspad en het uitvoerpad af van door de server beheerde waarden, niet van een verzoekparameter. Zo voorkom je dat je bestanden buiten de bedoelde map leest of schrijft.
- Behandel niet-vertrouwde afbeeldingen als vijandige invoer. Een misvormd raster veroorzaakt
ImageProcessingExceptionin plaats van het document te beschadigen, en de loader begrenst de afbeeldingsafmetingen om invoer met decompressiebommen te weerstaan. Vang de uitzondering op en weiger de upload. Probeer het niet blindelings opnieuw. - Een watermerk is geen geheimenopslag. De markering is zichtbare inhoud. Codeer geen referenties, tokens of interne identifiers in een watermerk of achtergrond die je aan een client retourneert.
Conformiteit
Sectie met titel “Conformiteit”Dit recipe doet zelf geen normatieve aanspraak op standaarden. Het combineert de publieke primitieven voor alpha, transformatie, tekst en afbeeldingen. Elke primitief zendt standaard PDF-content-stream-operatoren uit. De grafische staat wordt geïsoleerd met de q / Q-operatoren die startTransform() en stopTransform() uitzenden, en transparantie wordt overgedragen via een ExtGState-grafische-staatparameter. De uitvoer is structureel nieuw in plaats van byte-stabiel, dus deze pagina declareert een structural reproduceerbaarheidsprofiel. Zie voor details op operatorniveau over het transformatie- en grafische-staatoppervlak de referentie van de Graphics-module.
Zie ook
Sectie met titel “Zie ook”- Referentie van de Graphics-module: het volledige oppervlak voor paden, transformatie, kleur en grafische staat waarop deze methoden zijn gebaseerd.
- Afbeeldingen inbedden: laad, schaal en plaats rasterafbeeldingen, de basis voor een afbeeldingswatermerk of -achtergrond.
- Verlopen en transparantie: het alpha- en overvloeimodusoppervlak in detail, inclusief doorschijnende vullingen.
- Het coördinatenstelsel transformeren: draai, schaal en verplaats inhoud met gebalanceerde transformatieblokken.
- Uitzonderingsbewuste foutafhandeling: de NextPDF-uitzonderingshiërarchie achter
ImageProcessingException,PageLayoutExceptionenNextPdfException.