Salta ai contenuti

Aggiungere collegamenti e annotazioni di testo

Questa ricetta aggiunge tre elementi interattivi: un collegamento interno che rimanda a un’altra pagina, un collegamento esterno che apre un URL e un’annotazione di testo, detta anche nota adesiva. Segue examples/17-links.php e examples/29-annotations.php.

Nel modello ISO 32000-2, un collegamento cliccabile è un’annotazione di collegamento, cioè un collegamento ipertestuale a una destinazione o a un’azione. Una nota adesiva è un’annotazione di testo: mostra un’icona quando è chiusa e un popup quando è aperta.

Terminal window
composer require nextpdf/core:^3

Non è richiesta alcuna estensione facoltativa. L’API per collegamenti e annotazioni è stabile fin dalla versione 1.0.0 e funziona nell’intera matrice di backport 8.1–8.4.

I collegamenti interni usano uno schema a tre chiamate che supporta i riferimenti in avanti:

  1. addLink() riserva un identificatore di collegamento (un int).
  2. link($x, $y, $w, $h, $id) posiziona un rettangolo cliccabile associato a quell’ID.
  3. setLink($id, $pageIndex, $y) associa l’ID a una pagina di destinazione (a base zero) e a Y.

Per collegarsi a una pagina che non esiste ancora, chiamare il passaggio 3 dopo il passaggio 2. La destinazione usa una forma di destinazione esplicita. ISO 32000-2 §12.3.2.2 definisce [page /XYZ left top zoom], in cui un componente null mantiene il valore corrente del lettore.

Per un collegamento esterno, passare una stringa URL a link() anziché un int. NextPDF emette quindi un’azione URI il cui URI è una stringa ASCII UTF-8 obbligatoria. Per praticità, write($height, $text, $link) disegna testo in linea con un URL associato e annotation($x, $y, $w, $h, $text) posiziona una nota adesiva di sottotipo Text. ISO 32000-2 richiede SubtypeLink per un’annotazione di collegamento e definisce l’icona e il comportamento del popup dell’annotazione di testo.

La superficie dell’API viene generata automaticamente da PHPDoc. Questa ricetta si basa sui metodi seguenti:

  • addLink(): int — riserva un identificatore di collegamento interno.
  • setLink(int $linkId, int $pageIndex = -1, float $y = 0): static — associa un ID a una pagina di destinazione (a base zero) e a Y.
  • link(float $x, float $y, float $w, float $h, string|int $link): static — rettangolo cliccabile; un ID int è interno, una stringa è un URL esterno.
  • write(float $height, string $text, string $link = ''): static — testo in linea con un URL facoltativo.
  • annotation(float $x, float $y, float $w, float $h, string $text, string $subtype = 'Text'): static — annotazione con nota adesiva.
<?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');

Questo è l’esempio completo pronto per l’harness. Rispetta NEXTPDF_COOKBOOK_OUTPUT e non introduce entropia propria.

<?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 è a base zero. setLink($id, pageIndex: 2, …) punta alla terza pagina. Un errore di off-by-one qui è l’errore più comune.
  • String vs. int in link(). Un int è un ID di destinazione interna restituito da addLink(). Una stringa è un URL esterno. Passare il tipo errato produce il tipo di collegamento sbagliato senza generare alcun errore.
  • Associare ogni ID riservato. Un ID di addLink() che non viene mai associato tramite setLink() non ha alcuna destinazione. Il rettangolo è cliccabile ma inerte. Associarlo prima di save().
  • L’area cliccabile è il rettangolo, non il testo. link() richiede valori espliciti di x, y, w, h. Dimensionarlo in modo che copra il testo visibile. Il motore non misura i glifi automaticamente.
  • I collegamenti esterni non vengono convalidati. NextPDF memorizza l’URI letteralmente. Non verifica che la destinazione sia risolvibile o sicura. È il lettore a risolverla.

Ogni collegamento o annotazione aggiunge un dizionario di annotazione alla pagina. Il costo è O(1) per elemento. Anche centinaia di elementi per pagina restano ampiamente entro il budget di 2000 ms / 64 MB.

Le destinazioni dei collegamenti esterni vengono memorizzate letteralmente e risolte dal lettore, non dalla libreria. Considerare gli URL forniti dall’utente come non attendibili. Inserire lo schema in allowlist, in genere https. Rifiutare javascript: e file: prima di passarli a link(). Il testo dell’annotazione viene mostrato nell’interfaccia del lettore, pertanto è necessario limitarne la lunghezza e sanitizzare il contenuto delle note controllato dall’utente. In questa ricetta non avviene alcun parsing di input né accesso alla rete.

DichiarazioneSpecificaClausolareference_id
Un’annotazione di collegamento è un collegamento ipertestuale a una destinazione o a un’azione.ISO 32000-2§12.5.6.5
Subtype è Link per un’annotazione di collegamento.ISO 32000-2§12.5.6.5
L’URI di un’azione URI è una stringa ASCII UTF-8 obbligatoria.ISO 32000-2§12.6.4.8
Un’annotazione di testo è una nota adesiva (chiusa = icona, aperta = popup).ISO 32000-2§12.5.6.4
Destinazione esplicita [page /XYZ left top zoom]; null mantiene il valore corrente.ISO 32000-2§12.3.2.2

Profilo di riproducibilità — strutturale. Gli atomi /ID del trailer e gli atomi di data variano a ogni salvataggio. L’harness rimuove tali atomi e quindi confronta la struttura normalizzata con qpdf. Questa ricetta descrive come NextPDF produce la struttura. Non formula una dichiarazione generale di conformità a ISO 32000-2.

Non applicabile. I collegamenti e le annotazioni di testo sono funzionalità di Core senza vincoli Premium.