座標空間を変換する: 回転、拡大縮小、傾斜、ミラー
指定したピボットを中心に描画座標空間を変換します。このレシピでは、回転、拡大縮小、傾斜、ミラーを扱います。各変換は保存済みのグラフィックス状態ブロック内に隔離されるため、後続のコンテンツには影響しません。examples/21-transforms.php に沿っています。
インストール
「インストール」という見出しのセクションcomposer require nextpdf/core:^3Pro パッケージや Enterprise パッケージは必要ありません。変換 API は Core に含まれており、PHP 8.1 から 8.4 で動作します。
概念の概要
「概念の概要」という見出しのセクションPDF コンテンツは ユーザー空間 で描画されます。デフォルトでは、ユーザー空間の原点はページの左下にあり、1 単位は 1/72 インチに相当します (ISO 32000-2 §8.3.2)。変換では、cm 演算子を通じて、現在の変換行列 (CTM) に新しい行列を乗算します (§8.3.4)。変換は行列の連結によって合成されるため、順序が重要です。
NextPDF は左上を基準とする作成者座標系を提供します。内部的には、変換メソッド内の toY() 射影を通じて、これをネイティブの左下ユーザー空間へ変換します。位置にはユーザー空間単位を使用します。これは PDF ポイントであり、1 pt は 1/72 インチに相当します。変換をローカルに保つには、startTransform() と stopTransform() の間で囲みます。これらのメソッドは、q (保存) と Q (復元) のグラフィックス状態演算子を出力します (§8.4.2)。その間に描画されるものはすべて、その変換を継承します。stopTransform() 以降のものはすべて、以前の CTM に戻ります。各 rotate()/scale()/skewX()/mirrorH() 呼び出しは明示的なピボットを取るため、変換はページ原点ではなく、期待する位置にアンカーされます。
API サーフェス
「API サーフェス」という見出しのセクションAPI サーフェスは PHPDoc から生成されます。中心となるエントリポイントは \NextPDF\Core\Concerns\HasTransforms トレイトから提供されます:
Document::startTransform(): static—qを出力し、状態ブロックを開始しますDocument::stopTransform(): static—Qを出力し、ブロックを閉じますDocument::rotate(float $angle, float $x = 0, float $y = 0): staticDocument::scale(float $sx, float $sy, float $x = 0, float $y = 0): staticDocument::skewX(float $angle, float $x = 0, float $y = 0): static/skewY(...)Document::mirrorH(float $x = 0): static/mirrorV(float $y = 0): staticDocument::translateCtm(float $dx, float $dy): static
コードサンプル — クイックスタート
「コードサンプル — クイックスタート」という見出しのセクション<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Coordinate Transforms');$doc->addPage();
$cx = 60.0;$cy = 60.0;
// Rotate 30° around (cx, cy). The transform is scoped to this block.$doc->startTransform();$doc->rotate(30, $cx, $cy);$doc->setFont('helvetica', '', 14);$doc->text($cx, $cy, 'Rotated 30 degrees');$doc->stopTransform();
// Back to the untransformed CTM — this text is upright.$doc->setFont('helvetica', '', 10);$doc->text($cx, $cy + 20, 'Not rotated');
$doc->save(__DIR__ . '/transforms.pdf');
echo "Created: transforms.pdf\n";コードサンプル — 本番環境
「コードサンプル — 本番環境」という見出しのセクションこれは自己完結型で、ハーネスから実行できるプログラムです。examples/21-transforms.php の拡大縮小セクションを反映しています。各変換は、明示的なピボットを指定した保存済みのグラフィックス状態ブロック内に隔離されています。色と線の状態は最後にリセットされるため、後続のページへ状態が漏れません。
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Coordinate Transforms');$doc->addPage();
$doc->setFont('helvetica', 'B', 13);$doc->cell(0, 8, 'Scaling a reference square at 0.5x, 1.0x, 1.5x, 2.0x', newLine: true);$doc->ln(6);
$scaleBaseY = $doc->getY();$scaleFactors = [0.5, 1.0, 1.5, 2.0];
$doc->setDrawColor(30, 58, 138);$doc->setLineWidth(0.4);
foreach ($scaleFactors as $idx => $factor) { $cx = 25.0 + $idx * 45; $cy = $scaleBaseY + 5;
$doc->startTransform(); $doc->scale($factor, $factor, $cx, $cy); // scale about (cx, cy)
$doc->setFillColor(220, 230, 241); $doc->rect($cx, $cy, 15, 15, 'DF'); $doc->line($cx, $cy, $cx + 15, $cy + 15);
$doc->stopTransform(); // CTM restored here
// Drawn AFTER the block — at the original scale, untransformed. $doc->setFont('helvetica', '', 8); $doc->setTextColor(0); $doc->text($cx, $scaleBaseY + 38, sprintf('%.1fx', $factor));}
// Explicit state reset so nothing carries into the next section.$doc->setTextColor(0);$doc->setFillColor(255);$doc->setDrawColor(0);
// The harness sets NEXTPDF_COOKBOOK_OUTPUT and runs this script twice under// the structural profile (the transform stream itself is deterministic).$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false && $out !== '' ? $out : __DIR__ . '/transforms.pdf');
echo "Wrote transforms.pdf\n";想定される STDOUT:
Wrote transforms.pdf完全な例では、4 種類の変換ファミリすべて (回転、拡大縮小、傾斜、ミラー) を扱います。php examples/21-transforms.php で実行すると、examples/output/21-transforms.pdf が書き出されます。
エッジケースと注意点
「エッジケースと注意点」という見出しのセクション- ブロックは必ずペアにします。 すべての
startTransform()には、対応するstopTransform()が必要です。q/Qの数が釣り合っていないと、ページの残りの部分でグラフィックス状態が破損します (ISO 32000-2 §8.4.2)。NextPDF は深さを追跡しますが、このレシピでの契約は 1 対 1 です。 - 順序は可換ではありません。 変換は行列の連結によって合成されるため、
rotate()の後にscale()を呼び出すのは、scale()の後にrotate()を呼び出すのと同じではありません。意図した順序で、1 つのブロック内に適用してください。 - ピボットのデフォルトは原点です。 ピボットを省略すると、変換は図形ではなくページ原点を中心に回転します。通常は期待する動作ではないため、ピボットを明示的に渡してください。
- Y 軸は作成者空間です。 ピボット
yは作成者空間の左上からの距離であり、NextPDF はこれをネイティブのユーザー空間に射影します。生の PDF 座標を作成者 API と混在させると、ミラーされた結果になります。 - 状態の漏れ。 変換ブロック内で設定された色、フォント、線幅は
stopTransform()の後も残ります。この API サーフェスではQが CTM のみを復元するためです。後続のセクションがこれらの値を継承してはならない場合は、本番環境のサンプルのように、明示的にリセットしてください。
パフォーマンス
「パフォーマンス」という見出しのセクション変換は、単一の cm 演算子と q/Q のペアで構成されます。各部分は数バイトで、測定可能な実行時コストは追加されないため、このレシピは 1500 ms / 96 MB のバジェット内に十分収まります。再現性プロファイルは 構造的 です。出力には、実行ごとに安定しないトレーラーの /ID 配列と作成メタデータが含まれるため、比較前にこれらを正規化する必要があります。変換ストリーム自体は決定的です。
セキュリティに関する注意
「セキュリティに関する注意」という見出しのセクション- データレジデンシーと PII の緩和策。 該当しません。このレシピは、幾何プリミティブと短いラベルを描画します。外部データや個人データは一切処理しません。
- 安全なテレメトリとログのスクラビング。 このレシピは、固定された進捗行を 1 行だけ出力します。ドキュメントのコンテンツはログに記録されません。
- 脅威モデル。 該当しません。入力の解析も、暗号処理も、信頼境界もありません。変換は、純粋なコンテンツストリームの出力です。
- FIPS モードの動作。 該当しません。暗号処理は一切ありません。
| 記述 | 仕様 | 箇条 | reference_id |
|---|---|---|---|
cm 演算子を介した、行列の CTM への連結 | ISO 32000-2 | §8.3.4 | |
| 行列連結による変換の合成と順序の重要性 | ISO 32000-2 | §8.3.4 | |
q によるグラフィックス状態の保存と Q による復元、および変換スコープの限定 | ISO 32000-2 | §8.4.2 | |
| 左下を原点とし、1 単位を 1/72 インチとするデフォルトのユーザー空間 | ISO 32000-2 | §8.3.2 |
このレシピは、引用された ISO 32000-2 のグラフィックス状態および変換に関する箇条を実装しています。これは ISO 32000-2 への全面的な適合を主張するものではありません。引用された箇条は、このレシピが実際に使用するものに限られます。