DEV Community

Renewable Systems
Renewable Systems

Posted on

Stamping the downloader's email on every PDF page at download time in PHP

When a contract leaks, the first question is: whose copy was it? In Warqa, a contract management platform for companies in Saudi Arabia, every PDF and image gets the downloader's email stamped on it at download time. The original file stays clean in storage. This post shows how we built it with FPDI and TCPDF, and what we learned.

The rules we set

  1. Never change the stored original. Stamp a temporary copy for each download.
  2. The stamp names a person: the email of the logged-in user, or of the external recipient for a shared link.
  3. A failed stamp must never block work. If stamping throws, report the error and send the file.
  4. Every stamped copy gets its own tracking ID, written into the file and into the activity log.

Pick the method by file type

$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));

if ($extension === 'pdf') {
    try { return $this->processPdf($file, $watermarkText, $title); }
    catch (Exception $e) { report($e); }
} elseif (in_array($extension, ['jpg', 'jpeg', 'png'])) {
    try { return $this->processImage($file, $watermarkText); }
    catch (Exception $e) { report($e); }
}

return $file; // other types go out unchanged
Enter fullscreen mode Exit fullscreen mode

PDF and images cover almost all signed contracts and scanned documents. Word files are drafts, and a watermark in an editable file can simply be deleted.

PDF: import each page, then draw on top

FPDI imports pages of an existing PDF as templates. TCPDF then draws text over them:

$pdf = new \setasign\Fpdi\Tcpdf\Fpdi();
$pageCount = $pdf->setSourceFile($file);

for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) {
    $templateId = $pdf->importPage($pageNo);
    $size = $pdf->getTemplateSize($templateId);

    $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]);
    $pdf->useTemplate($templateId);

    $this->drawWatermark($pdf, $size['width'], $size['height'], $watermarkText);
}
Enter fullscreen mode Exit fullscreen mode

Keep the page size of each source page. Contracts often mix A4 portrait pages with landscape annex tables.

The stamp itself is small diagonal text, repeated in a grid across the page. The text is gray at about 10% opacity, so the document stays readable when printed. A single large stamp in the middle is easy to crop out. A grid is not.

The PDF that FPDI cannot open

The free FPDI parser does not read every compression method in newer PDFs. When it fails, we rewrite the file with Ghostscript to an older PDF version, then try again. If that fails too, rule 3 applies: report the error and send the file unstamped.

Images: GD is enough

For JPEG and PNG we use PHP's GD extension: load the image, draw the same diagonal text grid, save to a new temp file. There is no need for Imagick for this job.

A tracking ID per copy

Each stamped copy gets a new ULID. We write it into the PDF metadata keywords as TID:<id> and save it in the activity log next to the email, IP address and user agent. If a copy turns up somewhere, its ID points to one download event, even when the visible stamp was cut off.

The download flow

File::put($tmpPath, $attachment->getContent());

$result = app(LeakProtectionService::class)->protect(
    file: $tmpPath,
    watermarkText: $share->recipient_email,
    title: pathinfo($attachment->name, PATHINFO_FILENAME),
);

File::delete($tmpPath);
$share->recordDownload();

return response()->download($result->path)->deleteFileAfterSend();
Enter fullscreen mode Exit fullscreen mode

Temp files are deleted after the response is sent. Nothing stamped stays on disk.

Where the email comes from on shared links

External recipients do not have accounts. A shared link opens only after the recipient enters a one-time code sent to their email. That verified email is the text we stamp. Without the code step, the stamp would only show the email of the person who created the link.

What a watermark does not do

A watermark does not stop a leak. It deters one and helps trace one. It works together with other controls: access permissions per folder, links that expire, and a log of who viewed or downloaded each file.

ملخص بالعربية: يشرح هذا المقال كيف يضيف نظام إدارة العقود والمستندات علامة مائية تحمل البريد الإلكتروني للشخص الذي حمل الملف. وتضاف العلامة إلى ملفات PDF والصور لحظة التحميل، ويبقى الملف الأصلي دون تغيير. كما يتناول المقال رقم تتبع خاصًا بكل نسخة، يساعد على معرفة مصدر أي نسخة متسربة.


Written by the engineering team at Renewable Systems in Riyadh, the makers of Warqa.

Top comments (0)