Ir al contenido

Agregar enlaces y anotaciones de texto

Esta receta agrega tres elementos interactivos: un enlace interno para saltar a otra página, un enlace externo que abre una URL y una anotación de texto, también llamada nota adhesiva. Se basa en examples/17-links.php y examples/29-annotations.php.

Un enlace en el que se puede hacer clic es una anotación de enlace de ISO 32000-2, un hipervínculo a un destino o a una acción. Una nota adhesiva es una anotación de texto: muestra un icono cuando está cerrada y una ventana emergente cuando está abierta.

Ventana de terminal
composer require nextpdf/core:^3

No se requiere ninguna extensión opcional. La API de enlaces y anotaciones es estable desde la versión 1.0.0 y funciona sobre la matriz de backport 8.1–8.4.

Los enlaces internos siguen un patrón de tres llamadas que admite referencias anticipadas:

  1. addLink() reserva un identificador de enlace (un int).
  2. link($x, $y, $w, $h, $id) coloca un rectángulo en el que se puede hacer clic, asociado a ese id.
  3. setLink($id, $pageIndex, $y) asocia el id a una página de destino (de base cero) y a una coordenada Y.

Llamar al paso 3 después del paso 2 permite enlazar a una página que aún no existe. El destino usa una forma de destino explícito. ISO 32000-2 §12.3.2.2 define [page /XYZ left top zoom], donde un componente nulo conserva el valor actual del lector.

Para un enlace externo, se pasa una cadena de URL a link() en lugar de un int. NextPDF emite entonces una acción URI cuyo URI es una cadena ASCII UTF-8 obligatoria. Como atajo, write($height, $text, $link) dibuja texto en línea con una URL adjunta, y annotation($x, $y, $w, $h, $text) coloca una nota adhesiva de subtipo Text. ISO 32000-2 requiere SubtypeLink para una anotación de enlace y define el comportamiento del icono y de la ventana emergente de la anotación de texto.

La superficie de la API se genera automáticamente a partir de PHPDoc. Esta receta se apoya en estos métodos:

  • addLink(): int — reserva un identificador de enlace interno.
  • setLink(int $linkId, int $pageIndex = -1, float $y = 0): static — asocia un id a una página de destino (de base cero) y a una coordenada Y.
  • link(float $x, float $y, float $w, float $h, string|int $link): static — rectángulo en el que se puede hacer clic; un id int es interno, una cadena es una URL externa.
  • write(float $height, string $text, string $link = ''): static — texto en línea con una URL opcional.
  • annotation(float $x, float $y, float $w, float $h, string $text, string $subtype = 'Text'): static — anotación de tipo nota adhesiva.
<?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');

Este es el ejemplo completo, listo para el arnés de pruebas. Respeta NEXTPDF_COOKBOOK_OUTPUT y no añade entropía por su cuenta.

<?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 es de base cero. setLink($id, pageIndex: 2, …) apunta a la tercera página. Un error de desfase por uno aquí es el fallo más común.
  • Cadena frente a int en link(). Un int es un id de destino interno de addLink(). Una cadena es una URL externa. Pasar el tipo equivocado produce el tipo de enlace equivocado sin emitir ningún error.
  • Asociar cada id reservado. Un id de addLink() que nunca se asocia con setLink() no tiene destino. El rectángulo permite hacer clic, pero es inerte. Debe asociarse antes de save().
  • El área en la que se puede hacer clic es el rectángulo, no el texto. link() toma x, y, w, h explícitos. Debe dimensionarse para cubrir el texto visible. El motor no mide los glifos automáticamente.
  • Los enlaces externos no se validan. NextPDF almacena el URI textualmente. No verifica que el destino se resuelva ni que sea seguro. El lector lo resuelve.

Cada enlace o anotación agrega un diccionario de anotación a la página. El costo es O(1) por elemento. Cientos por página se mantienen holgadamente dentro del presupuesto de 2000 ms / 64 MB.

Los destinos de los enlaces externos se almacenan textualmente y los resuelve el lector, no la biblioteca. Las URL proporcionadas por el usuario deben tratarse como no confiables. Usar una lista de permitidos para el esquema, que normalmente es https. Rechazar javascript: y file: antes de pasarlos a link(). El texto de la anotación se muestra en la interfaz del lector, así que conviene limitar su longitud y sanear el contenido de las notas cuando esté controlado por el usuario. En esta receta no se realiza ningún análisis de entrada ni acceso a la red.

DeclaraciónEspecificaciónCláusulareference_id
Una anotación de enlace es un hipervínculo a un destino o a una acción.ISO 32000-2§12.5.6.5
Subtype es Link para una anotación de enlace.ISO 32000-2§12.5.6.5
El URI de una acción URI es una cadena ASCII UTF-8 obligatoria.ISO 32000-2§12.6.4.8
Una anotación de texto es una nota adhesiva (cerrada = icono, abierta = ventana emergente).ISO 32000-2§12.5.6.4
Destino explícito [page /XYZ left top zoom]; null conserva el valor actual.ISO 32000-2§12.3.2.2

Perfil de reproducibilidad — estructural. El /ID del tráiler y los átomos de fecha varían en cada guardado. El arnés de pruebas elimina esos átomos y después compara la estructura normalizada por qpdf. Esta receta describe cómo NextPDF produce la estructura. No constituye una declaración general de conformidad con ISO 32000-2.

No aplica. Los enlaces y las anotaciones de texto son capacidades de Core sin restricción de Premium.