コンテンツにスキップ

リンクとテキスト注釈を追加する

このレシピでは、3 つのインタラクティブ要素を追加します。別のページへジャンプする内部リンク、URL を開く外部リンク、そして付箋とも呼ばれるテキスト注釈です。examples/17-links.phpexamples/29-annotations.php に基づいています。

クリックできるリンクは ISO 32000-2 のリンク注釈であり、宛先またはアクションへのハイパーテキストリンクです。付箋はテキスト注釈です。閉じているときはアイコンを表示し、開いているときはポップアップを表示します。

Terminal window
composer require nextpdf/core:^3

オプションの拡張機能は不要です。リンクと注釈の API は 1.0.0 以降で安定しており、8.1〜8.4 のバックポートマトリックスに対応しています。

内部リンクでは、前方参照をサポートする 3 つの呼び出しから成るパターンを使います。

  1. addLink() はリンク識別子(int)を予約します。
  2. link($x, $y, $w, $h, $id) は、その id にひも付くクリック可能な矩形を配置します。
  3. setLink($id, $pageIndex, $y) は、id を宛先ページ(0 始まり)と Y にひも付けます。

まだ存在しないページへリンクする場合は、ステップ 2 の後にステップ 3 を呼び出します。宛先には明示的宛先形式を使います。ISO 32000-2 §12.3.2.2 は [page /XYZ left top zoom] を定義しており、null のコンポーネントはリーダーの現在の値を保持します。

外部リンクでは、int の代わりに URL 文字列link() に渡します。すると NextPDF は URI アクションを出力し、その URI は必須の UTF-8 ASCII 文字列になります。ショートカットとして、write($height, $text, $link) は URL 付きのインラインテキストを描画し、annotation($x, $y, $w, $h, $text)Text サブタイプの付箋を配置します。ISO 32000-2 は、リンク注釈に対して SubtypeLink を必須とし、テキスト注釈のアイコンとポップアップの動作を定義しています。

API サーフェスは PHPDoc から自動生成されています。このレシピは次のメソッドに依存しています。

  • addLink(): int — 内部リンク識別子を予約します。
  • setLink(int $linkId, int $pageIndex = -1, float $y = 0): static — id を宛先ページ(0 始まり)と Y にひも付けます。
  • link(float $x, float $y, float $w, float $h, string|int $link): static — クリック可能な矩形を配置します。int の id は内部、文字列は外部 URL です。
  • write(float $height, string $text, string $link = ''): static — オプションの URL を付けたインラインテキスト。
  • annotation(float $x, float $y, float $w, float $h, string $text, string $subtype = 'Text'): static — 付箋注釈を配置します。
<?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');

これはハーネスに対応した完全な例です。NEXTPDF_COOKBOOK_OUTPUT に従い、独自のエントロピーは追加しません。

<?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 は 0 始まりです。 setLink($id, pageIndex: 2, …) は 3 ページ目を対象とします。ここでの 1 つずれ(off-by-one)エラーが最もよくある間違いです。
  • link() での文字列と int の違い。 int は addLink() から取得した内部宛先 id です。文字列は外部 URL です。誤った型を渡すと、エラーが出ないまま誤った種類のリンクが生成されます。
  • 予約したすべての id をひも付けてください。 addLink() の id を setLink() でひも付けないと、宛先がありません。矩形はクリックできますが、何も起きません。save() の前にひも付けてください。
  • クリック可能な領域はテキストではなく矩形です。 link() は明示的な x, y, w, h を受け取ります。表示されているテキストを覆うようにサイズを設定してください。エンジンが自動的にグリフを測定することはありません。
  • 外部リンクは検証されません。 NextPDF は URI をそのまま保存します。ターゲットが resolve(解決)可能かどうか、安全かどうかは検証しません。解決はリーダー側で行われます。

各リンクまたは注釈は、ページに 1 つの注釈辞書を追加します。コストは要素ごとに O(1) です。ページあたり数百個でも、2000 ms / 64 MB の予算内に十分収まります。

外部リンクのターゲットはそのまま保存され、ライブラリではなくリーダーによって解決されます。ユーザーから提供された URL は信頼できないものとして扱ってください。スキームを許可リスト方式で制限してください。通常は https です。javascript:file: は、link() に渡す前に拒否してください。注釈テキストはリーダーの UI に表示されるため、ユーザーが制御するノートの内容は長さを制限し、サニタイズしてください。このレシピでは、入力の解析やネットワークアクセスは発生しません。

ステートメント仕様条項リファレンス ID
リンク注釈は、宛先またはアクションへのハイパーテキストリンクISO 32000-2§12.5.6.5
リンク注釈における SubtypeLinkISO 32000-2§12.5.6.5
URI アクションで必須となる UTF-8 ASCII 文字列の URIISO 32000-2§12.6.4.8
テキスト注釈は付箋(閉じている = アイコン、開いている = ポップアップ)ISO 32000-2§12.5.6.4
明示的宛先 [page /XYZ left top zoom]、null は現在の値を保持ISO 32000-2§12.3.2.2

再現性プロファイル — 構造的。 トレーラーの /ID と日付アトムは、保存するたびに変化します。ハーネスはこれらのアトムを除去したうえで、qpdf で正規化した構造を比較します。このレシピは、NextPDF がその構造をどのように出力するかを説明します。ISO 32000-2 への包括的な適合を主張するものではありません。

該当なし。リンクとテキスト注釈は Core の機能であり、Premium による制限はありません。