Zum Inhalt springen

Links und Textanmerkungen hinzufügen

Dieses Recipe fügt drei interaktive Elemente hinzu: einen internen Link, der zu einer anderen Seite springt, einen externen Link, der eine URL öffnet, sowie eine Textanmerkung, auch Haftnotiz genannt. Es orientiert sich an examples/17-links.php und examples/29-annotations.php.

Ein anklickbarer Link ist eine ISO 32000-2-Linkanmerkung: ein Hypertext-Link zu einem Ziel oder einer Aktion. Eine Haftnotiz ist eine Textanmerkung; geschlossen zeigt sie ein Symbol, geöffnet ein Popup.

Terminal-Fenster
composer require nextpdf/core:^3

Es ist keine optionale Erweiterung erforderlich. Die Link- und Anmerkungs-API ist seit 1.0.0 stabil und läuft auf der Backport-Matrix 8.1–8.4.

Interne Links nutzen ein dreistufiges Aufrufmuster, das Vorwärtsreferenzen unterstützt:

  1. addLink() reserviert einen Link-Bezeichner (einen int).
  2. link($x, $y, $w, $h, $id) platziert ein anklickbares Rechteck, das an diese id gekoppelt ist.
  3. setLink($id, $pageIndex, $y) koppelt die id an eine Zielseite (nullbasiert) und Y.

Rufen Sie Schritt 3 nach Schritt 2 auf, um auf eine Seite zu verlinken, die noch nicht existiert. Das Ziel hat die Form eines expliziten Ziels. ISO 32000-2 §12.3.2.2 definiert [page /XYZ left top zoom], wobei eine null-Komponente den aktuellen Wert des Readers beibehält.

Übergeben Sie für einen externen Link einen URL-String an link() statt eines int. NextPDF gibt dann eine URI-Aktion aus, deren URI ein erforderlicher UTF-8-ASCII-String ist. Als Abkürzung zeichnet write($height, $text, $link) Inline-Text mit angehängter URL; annotation($x, $y, $w, $h, $text) platziert eine Haftnotiz vom Subtyp Text. ISO 32000-2 verlangt SubtypeLink für eine Linkanmerkung und definiert das Symbol der Textanmerkung sowie das Popup-Verhalten.

Die API-Oberfläche wird automatisch aus dem PHPDoc generiert. Dieses Recipe verwendet die folgenden Methoden:

  • addLink(): int — reserviert einen internen Link-Bezeichner.
  • setLink(int $linkId, int $pageIndex = -1, float $y = 0): static — koppelt eine id an eine Zielseite (nullbasiert) und Y.
  • link(float $x, float $y, float $w, float $h, string|int $link): static — anklickbares Rechteck; eine int-id ist intern, ein String steht für eine externe URL.
  • write(float $height, string $text, string $link = ''): static — Inline-Text mit optionaler URL.
  • annotation(float $x, float $y, float $w, float $h, string $text, string $subtype = 'Text'): static — Haftnotiz-Anmerkung.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$jump = $doc->addLink(); // 1. reserve an id (forward reference)
$doc->addPage();
$doc->setFont('helvetica', 'B', 12);
$x = $doc->getX();
$y = $doc->getY();
$doc->cell(60, 10, 'Go to page 2', newLine: true);
$doc->link($x, $y, 60, 10, $jump); // 2. clickable rectangle -> id
$doc->link($doc->getX(), $doc->getY(), 80, 10, 'https://nextpdf.dev'); // external
$doc->addPage();
$doc->setLink($jump, pageIndex: 1, y: 0); // 3. bind id to page 2 (index 1)
$doc->cell(0, 10, 'Destination (page 2).', newLine: true);
$doc->annotation(x: 180, y: 20, w: 10, h: 10, text: 'A reviewer note.');
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/links.pdf');

Dies ist das vollständige, Harness-fertige Beispiel. Es berücksichtigt NEXTPDF_COOKBOOK_OUTPUT und fügt keine zusätzliche Entropie hinzu.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Links and Annotations');
// Reserve the internal-link id before its destination page exists.
$linkToPage3 = $doc->addLink();
// Page 1 — source of the internal and external links.
$doc->addPage();
$doc->setFont('helvetica', 'B', 20);
$doc->cell(0, 14, 'Links and Annotations', newLine: true);
$doc->ln(6);
$doc->setFont('helvetica', 'B', 12);
$doc->setTextColor(0, 51, 153);
$linkX = $doc->getX();
$linkY = $doc->getY();
$doc->cell(60, 10, 'Go to Page 3', newLine: true);
$doc->link($linkX, $linkY, 60, 10, $linkToPage3); // internal: int id
$doc->setTextColor(0);
$doc->ln(6);
$doc->setFont('helvetica', 'B', 12);
$doc->setTextColor(0, 102, 204);
$doc->write(10, 'Visit https://nextpdf.dev', link: 'https://nextpdf.dev');
$doc->setTextColor(0);
$doc->ln(6);
$urlX = $doc->getX();
$urlY = $doc->getY();
$doc->cell(80, 10, 'NextPDF on GitHub', newLine: true);
$doc->link($urlX, $urlY, 80, 10, 'https://github.com/nextpdf-labs/nextpdf');
// Page 2 — intermediate.
$doc->addPage();
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'Internal links can jump across pages; this page is '
. 'skipped by the link on page 1.');
// Page 3 — destination + a sticky note.
$doc->addPage();
$doc->setLink($linkToPage3, pageIndex: 2, y: 0); // bind id to page 3 (index 2)
$doc->setFont('helvetica', 'B', 18);
$doc->cell(0, 14, 'Page 3 — Link Target', newLine: true);
$doc->ln(4);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'You arrived via the internal link on page 1.');
$doc->annotation(
x: 185, y: 40, w: 10, h: 10,
text: 'Sticky note: appears as an icon; click to read this text.',
);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/links.pdf';
$doc->save($out);
echo "Created links.pdf\n";
  • pageIndex ist nullbasiert. setLink($id, pageIndex: 2, …) zielt auf die dritte Seite. Ein Off-by-one-Fehler ist hier der häufigste Fehler.
  • String vs. int bei link(). Ein int ist eine interne Ziel-id von addLink(). Ein String steht für eine externe URL. Der falsche Typ erzeugt ohne Fehler die falsche Art von Link.
  • Binden Sie jede reservierte id. Eine addLink()-id, die Sie nie mit setLink() binden, hat kein Ziel. Das Rechteck ist anklickbar, bleibt aber wirkungslos. Binden Sie sie vor save().
  • Der anklickbare Bereich ist das Rechteck, nicht der Text. link() nimmt explizite x, y, w, h entgegen. Bemessen Sie es so, dass es den sichtbaren Text abdeckt. Die Engine misst die Glyphen nicht für Sie aus.
  • Externe Links werden nicht validiert. NextPDF speichert die URI wortgetreu. Es prüft nicht, ob das Ziel auflösbar oder sicher ist. Der Reader löst sie auf.

Jeder Link und jede Anmerkung fügt der Seite ein Anmerkungs-Dictionary hinzu. Die Kosten betragen O(1) pro Element. Hunderte pro Seite bleiben problemlos innerhalb des Budgets von 2000 ms / 64 MB.

Externe Link-Ziele werden wortgetreu gespeichert und vom Reader aufgelöst, nicht von der Bibliothek. Behandeln Sie von Nutzern bereitgestellte URLs als nicht vertrauenswürdig. Beschränken Sie das Schema auf eine Allowlist, üblicherweise https. Weisen Sie javascript: und file: zurück, bevor Sie sie an link() übergeben. Anmerkungstext wird in der Reader-Oberfläche angezeigt; begrenzen Sie daher die Länge und bereinigen Sie nutzergesteuerte Notizinhalte. In diesem Recipe findet kein Parsen von Eingaben und kein Netzwerkzugriff statt.

AussageSpezifikationAbschnittreference_id
Eine Linkanmerkung ist ein Hypertext-Link zu einem Ziel oder einer Aktion.ISO 32000-2§12.5.6.5
Subtype ist Link für eine Linkanmerkung.ISO 32000-2§12.5.6.5
Die URI einer URI-Aktion ist ein erforderlicher UTF-8-ASCII-String.ISO 32000-2§12.6.4.8
Eine Textanmerkung ist eine Haftnotiz (geschlossen = Symbol, geöffnet = Popup).ISO 32000-2§12.5.6.4
Explizites Ziel [page /XYZ left top zoom]; null behält den aktuellen Wert bei.ISO 32000-2§12.3.2.2

Reproduzierbarkeitsprofil — strukturell. Das Trailer-/ID und die Datums-Atome variieren bei jedem Speichern. Der Harness entfernt diese Atome und vergleicht anschließend die qpdf-normalisierte Struktur. Dieses Recipe beschreibt, wie NextPDF die Struktur erzeugt. Es erhebt keine pauschale Aussage zur ISO 32000-2-Konformität.

Nicht zutreffend. Links und Textanmerkungen sind Core-Fähigkeiten ohne Premium-Gate.