Create optional content layers (OCG)
At a glance
Section titled “At a glance”Wrap content in named optional content groups (OCGs), often called layers. A PDF reader can toggle each layer in its Layers panel, and one layer starts hidden by default. This recipe follows examples/26-layers.php.
An OCG is an ISO 32000-2 optional content group dictionary with Type /OCG. NextPDF wraps the layered marks between BDC/EMC and uses the OC marked-content tag.
Install
Section titled “Install”composer require nextpdf/core:^3You do not need an optional extension. The layer API has been stable since 1.0.0 and runs on the 8.1–8.4 backport matrix.
Conceptual overview
Section titled “Conceptual overview”startLayer($name, $visible) opens an OCG. Everything you draw until the matching endLayer() belongs to that group. $name is the label the PDF reader shows in its Layers panel. ISO 32000-2 requires the OCG Name, which is the user-facing string. Passing $visible: false registers the group in the default configuration’s OFF state, so the reader hides it until the user turns it on.
Visibility is reader-cooperative. For an optional content membership dictionary (OCMD), the default visibility policy is AnyOn. The layer is visible if any referenced group is ON. A hidden layer is hidden only by reader convention. It is not removed or protected, and it is neither a redaction nor a security control. To remove content, do not draw it.
API surface
Section titled “API surface”PHPDoc auto-generates the API surface. This recipe uses these two methods:
startLayer(string $name, bool $visible = true): static— opens a named OCG;$visible: falsemakes it hidden by default.endLayer(): static— closes the most recently opened layer (balanced withstartLayer()).
Code sample — Quick start
Section titled “Code sample — Quick start”<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();
$doc->startLayer('Content', visible: true);$doc->setFont('helvetica', '', 12);$doc->cell(0, 8, 'Always-visible body content.', newLine: true);$doc->endLayer();
$doc->startLayer('Debug Grid', visible: false); // hidden until toggled$doc->setDrawColor(200, 200, 200);for ($x = 0.0; $x <= 210.0; $x += 10.0) { $doc->line($x, 0, $x, 297);}$doc->endLayer();
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/layers.pdf');Code sample — Production
Section titled “Code sample — Production”Use this complete, harness-ready example. It honors NEXTPDF_COOKBOOK_OUTPUT and introduces no entropy of its own.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Contracts\Alignment;use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Layer Examples (OCG)');$doc->addPage();
// Layer 1 — background, visible by default.$doc->startLayer('Background', visible: true);$doc->setFillColor(230, 240, 250);$doc->rect(10, 10, 190, 277, 'F');$doc->endLayer();
// Layer 2 — watermark, visible by default; can be toggled off.$doc->startLayer('Watermark', visible: true);$doc->setFont('helvetica', 'B', 54);$doc->setTextColor(200, 200, 200);$doc->startTransform();$doc->rotate(45, 105, 148);$doc->setXY(30, 135);$doc->cell(150, 20, 'DRAFT', align: Alignment::Center);$doc->stopTransform();$doc->endLayer();
// Layer 3 — main content, visible by default.$doc->startLayer('Content', visible: true);$doc->setTextColor(0);$doc->setFont('helvetica', 'B', 20);$doc->setXY(10, 15);$doc->cell(0, 14, 'Layer Examples (OCG)', newLine: true);$doc->ln(4);$doc->setFont('helvetica', '', 11);$doc->multiCell(0, 7, 'This document contains four optional content groups. ' . "Toggle them in your reader's Layers panel.");$doc->endLayer();
// Layer 4 — debug grid, hidden by default.$doc->startLayer('Debug Grid', visible: false);$doc->setDrawColor(180, 180, 180);$doc->setLineWidth(0.15);for ($x = 0.0; $x <= 210.0; $x += 10.0) { $doc->line($x, 0, $x, 297);}for ($y = 0.0; $y <= 297.0; $y += 10.0) { $doc->line(0, $y, 210, $y);}$doc->endLayer();
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/layers.pdf';$doc->save($out);
echo "Created layers.pdf\n";Edge cases & gotchas
Section titled “Edge cases & gotchas”- Balance every
startLayer()with anendLayer(). An unclosed layer leaves a danglingBDCwith noEMCand makes the document structure malformed. Pair each opening call with its closing call. - A hidden layer is not removed.
visible: falsehides content only by reader convention. The marks and any text remain in the file and are recoverable. This is not redaction. For sensitive data, do not draw it. - Layer panel support varies. Toggling requires a reader that exposes optional content. Print pipelines and minimal viewers may always show, or always hide, default-off layers.
- Nesting. Nested layers are allowed, but each inner group’s visibility remains independent. Do not assume an outer-OFF layer hides an inner-ON group unless you wire a membership policy.
Performance
Section titled “Performance”Each layer adds one OCG dictionary and a BDC/EMC pair around its marks. The overhead is negligible. Cost scales with the content inside the layers, not the number of layers, so this stays well within the 2000 ms / 64 MB budget.
Security notes
Section titled “Security notes”Optional content visibility is reader-cooperative, not access control. Hiding a layer does not encrypt, redact, or remove its content. Anyone can re-enable the layer or extract the bytes. Never use a hidden layer to conceal confidential text; omit the content entirely instead. This recipe parses no input and makes no network requests.
Conformance
Section titled “Conformance”| Statement | Spec | Clause | reference_id |
|---|---|---|---|
An OCG dictionary has Type /OCG. | ISO 32000-2 | §8.11.2 | |
The OCG Name is the required user-facing label. | ISO 32000-2 | §8.11.2 | |
Optional content is enclosed between BDC/EMC with the OC tag. | ISO 32000-2 | §8.11.3.2 | |
| OCMD policies are AllOn/AnyOn/AnyOff/AllOff (default AnyOn). | ISO 32000-2 | §8.11.4.3 |
Reproducibility profile — structural. The trailer /ID and date atoms change on every save. The harness strips those atoms and compares the qpdf-normalised structure. This recipe describes how NextPDF produces that structure. It does not assert general ISO 32000-2 conformance.
Commercial context
Section titled “Commercial context”Not applicable. Optional content groups are a Core capability with no Premium gate.