Skip to content

Security & Files

This page covers three slices of the spec that share the “attach extra content to the document” theme — encryption (who can read), embedded files (extra payloads), and 3D annotations (interactive viewports).

PdfEncryptor::publicKeyAes256() seals the PDF for one or more X.509 recipients. Only the holder of a matching private key can open it. The example generates a throwaway keypair so it runs anywhere; in production you would import a recipient’s certificate.

use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Core\Security\PdfEncryptor;
use Phpdftk\Pdf\Writer\PdfWriter;
// Public-key encryption locks the PDF to one or more X.509 recipients. Each
// recipient unlocks the document with their own private key — no shared password.
// Here we generate a throwaway RSA keypair and self-signed cert so the example
// runs anywhere. In production, you would import a recipient's certificate.
$config = [
'digest_alg' => 'sha256',
'private_key_bits' => 2048,
'private_key_type' => OPENSSL_KEYTYPE_RSA,
];
$privateKey = openssl_pkey_new($config);
$csr = openssl_csr_new(['commonName' => 'phpdftk-recipient'], $privateKey, $config);
$cert = openssl_csr_sign($csr, null, $privateKey, 365, $config);
openssl_x509_export($cert, $certPem);
openssl_pkey_export($privateKey, $keyPem);
// Save the keypair alongside the PDF so docs can demonstrate decryption.
$samplesDir = dirname(example_output_path('core/security/public-key.pdf'));
file_put_contents($samplesDir . '/recipient.cert.pem', $certPem);
file_put_contents($samplesDir . '/recipient.key.pem', $keyPem);
$writer = new PdfWriter();
$page = $writer->addPage();
$body = $writer->addFont(new Type1Font(StandardFont::Helvetica))->getResourceName();
$bold = $writer->addFont(new Type1Font(StandardFont::HelveticaBold))->getResourceName();
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($bold, 20)->moveTextPosition(72, 720)
->showText('Public-Key Encrypted PDF')->endText();
$cs->beginText()->setFont($body, 12)->moveTextPosition(72, 690)
->showText('This document is sealed for a single X.509 recipient.')
->endText();
$cs->beginText()->setFont($body, 12)->moveTextPosition(72, 670)
->showText('Only the holder of the matching private key can open it.')
->endText();
$cs->beginText()->setFont($body, 12)->moveTextPosition(72, 650)
->showText('Recipient certificate and key are written next to this file.')
->endText();
$fileId = md5('phpdftk-public-key-showcase', true);
$encryptor = PdfEncryptor::publicKeyAes256(
recipients: [['cert' => $certPem]],
fileId: $fileId,
);
$writer->setEncryption($encryptor);
$writer->save('public-key.pdf');

The generated recipient.cert.pem and recipient.key.pem are written next to the PDF so you can decrypt it locally.

FileAttachmentAnnotation exposes embedded files at fixed positions on the page. Names → EmbeddedFiles is the document-wide equivalent — files show up in any viewer’s Attachments panel without consuming page space.

use Phpdftk\Pdf\Core\Document\NameTree;
use Phpdftk\Pdf\Core\Document\NamesDictionary;
use Phpdftk\Pdf\Core\FileSpec\EmbeddedFile;
use Phpdftk\Pdf\Core\FileSpec\FileSpec;
use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Core\PdfArray;
use Phpdftk\Pdf\Core\PdfReference;
use Phpdftk\Pdf\Core\PdfString;
use Phpdftk\Pdf\Writer\PdfWriter;
// FileAttachment annotations expose embedded files at fixed page locations.
// /Names → /EmbeddedFiles makes the same files available document-wide —
// they show up in any viewer's "Attachments" panel without taking page space.
$writer = new PdfWriter();
$page = $writer->addPage();
$body = $writer->addFont(new Type1Font(StandardFont::Helvetica))->getResourceName();
$bold = $writer->addFont(new Type1Font(StandardFont::HelveticaBold))->getResourceName();
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($bold, 22)->moveTextPosition(72, 740)
->showText('Embedded Files (Attachments Panel)')->endText();
$cs->beginText()->setFont($body, 12)->moveTextPosition(72, 706)
->showText('Open the Attachments panel in any viewer to download the files below.')
->endText();
$attachments = [
['hello.txt', 'Plain-text greeting', "Hello from inside the PDF.\n"],
['report.csv', 'CSV report data', "month,visits\nJan,1240\nFeb,1430\nMar,1700\n"],
['config.json', 'Sample configuration', json_encode(['version' => '1.0', 'mode' => 'demo'], JSON_PRETTY_PRINT)],
];
// Build the names tree: an alternating array of name strings and FileSpec references.
$namesArray = [];
$y = 660;
foreach ($attachments as [$filename, $description, $payload]) {
$embedded = new EmbeddedFile($payload);
$writer->register($embedded);
$fs = new FileSpec($filename);
$fs->desc = new PdfString($description);
$fs->attachEmbeddedFile(new PdfReference($embedded->objectNumber));
$writer->register($fs);
$namesArray[] = new PdfString($filename);
$namesArray[] = new PdfReference($fs->objectNumber);
// Render a line on the page so the viewer also sees the file inventory.
$cs->beginText()->setFont($bold, 12)->moveTextPosition(72, $y)
->showText($filename)->endText();
$cs->beginText()->setFont($body, 12)->moveTextPosition(220, $y)
->showText($description)->endText();
$y -= 24;
}
// Wire the embedded-files name tree into the catalog's /Names dictionary.
$nameTree = new NameTree();
$nameTree->names = new PdfArray($namesArray);
$writer->register($nameTree);
$names = new NamesDictionary();
$names->embeddedFiles = new PdfReference($nameTree->objectNumber);
$writer->register($names);
$writer->getCatalog()->names = new PdfReference($names->objectNumber);
$writer->save('embedded-file.pdf');

A ThreeDAnnotation reserves a viewport on the page and binds a U3D or PRC model stream, a default view, and rendering parameters (background, lighting, render mode). The example below wires the full object graph using placeholder model bytes — substitute a real .u3d file in production.

use Phpdftk\Pdf\Core\Annotation\ThreeDAnnotation;
use Phpdftk\Pdf\Core\Font\StandardFont;
use Phpdftk\Pdf\Core\Font\Type1Font;
use Phpdftk\Pdf\Core\Graphics\ColorSpace\DeviceRGB;
use Phpdftk\Pdf\Core\PdfArray;
use Phpdftk\Pdf\Core\PdfName;
use Phpdftk\Pdf\Core\PdfNumber;
use Phpdftk\Pdf\Core\ThreeD\ThreeDBackground;
use Phpdftk\Pdf\Core\ThreeD\ThreeDLightingScheme;
use Phpdftk\Pdf\Core\ThreeD\ThreeDRenderMode;
use Phpdftk\Pdf\Core\ThreeD\ThreeDStream;
use Phpdftk\Pdf\Core\ThreeD\ThreeDView;
use Phpdftk\Pdf\Writer\PdfWriter;
// 3D annotations carry a U3D or PRC payload along with viewport, background, lighting
// and render-mode configuration. This example wires the full object graph using
// placeholder model bytes — substitute a real .u3d file in production.
$writer = new PdfWriter();
$page = $writer->addPage();
$body = $writer->addFont(new Type1Font(StandardFont::Helvetica))->getResourceName();
$bold = $writer->addFont(new Type1Font(StandardFont::HelveticaBold))->getResourceName();
$cs = $writer->addContentStream($page);
$cs->beginText()->setFont($bold, 22)->moveTextPosition(72, 740)
->showText('3D Annotation')->endText();
$cs->beginText()->setFont($body, 11)->moveTextPosition(72, 706)
->showText('A 3D viewport is reserved below. Viewers with 3D support load the')
->endText();
$cs->beginText()->setFont($body, 11)->moveTextPosition(72, 690)
->showText('embedded U3D stream into the box and let users rotate/pan/zoom.')
->endText();
// 1) U3D model stream. Real applications supply real geometry; the structure
// is the same.
$u3d = new ThreeDStream('U3D', "U3D PLACEHOLDER MODEL DATA — substitute a real file in production.");
$u3d->colorSpace = new DeviceRGB();
$u3dRef = $writer->register($u3d);
// 2) Default view: camera/coords/background/render/lighting.
$bg = new ThreeDBackground();
$bg->cs = new DeviceRGB();
$bg->c = new PdfArray([new PdfNumber(0.96), new PdfNumber(0.97), new PdfNumber(0.99)]);
$bgRef = $writer->register($bg);
$rm = new ThreeDRenderMode('Solid');
$rm->op = 1.0;
$rmRef = $writer->register($rm);
$ls = new ThreeDLightingScheme('Day');
$lsRef = $writer->register($ls);
$view = new ThreeDView('DefaultView');
$view->co = 120.0;
$view->ms = new PdfName('M');
$view->bg = $bgRef;
$view->rm = $rmRef;
$view->ls = $lsRef;
$viewRef = $writer->register($view);
$u3d->va = new PdfArray([$viewRef]);
$u3d->dv = $viewRef;
// 3) Annotation: the rectangle on the page where the 3D viewport renders.
$annot = new ThreeDAnnotation(new PdfArray([
new PdfNumber(72), new PdfNumber(200),
new PdfNumber(540), new PdfNumber(660),
]));
$annot->dd = $u3dRef;
$annot->di = true;
$annot->db = new PdfArray([
new PdfNumber(0), new PdfNumber(0), new PdfNumber(1), new PdfNumber(1),
]);
$page->corePage()->annots[] = $writer->register($annot);
$writer->save('3d-annotation.pdf');