轉換座標空間:旋轉、縮放、傾斜與鏡像
以選定的樞紐點為中心,轉換繪圖座標空間。這則 recipe(範例)涵蓋旋轉、縮放、傾斜與鏡像。每個轉換都隔離在已儲存的圖形狀態區塊中,因此不會影響後續內容。本文依循 examples/21-transforms.php 的做法。
composer require nextpdf/core:^3你不需要 Pro 或 Enterprise 套件。轉換 API 隨 Core 提供,可在 PHP 8.1 到 8.4 上執行。
概念說明
標題為「概念說明」的區段PDF 內容是繪製在 使用者空間(user space) 中。預設情況下,使用者空間的原點位於頁面左下角,一個單位等於 1/72 英吋(ISO 32000-2 §8.3.2)。轉換會透過 cm 運算子,將一個新矩陣串接到目前的轉換矩陣(CTM)上(§8.3.4)。多個轉換是以矩陣串接的方式組合,因此順序很重要。
NextPDF 對外提供一套以左上角為原點的作者座標系統。內部會在各個轉換方法中透過 toY() 投影,換算成原生、以左下角為原點的使用者空間。位置採用使用者空間單位,也就是 PDF 點(point),其中 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 trait:
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完整範例涵蓋全部四個轉換家族:旋轉、縮放、傾斜與鏡像。使用 php examples/21-transforms.php 執行它,它會寫出 examples/output/21-transforms.pdf。
邊界案例與陷阱
標題為「邊界案例與陷阱」的區段- 務必成對使用區塊。 每個
startTransform()都需要一個對應的stopTransform()。不平衡的q/Q計數會讓該頁後續的圖形狀態損毀(ISO 32000-2 §8.4.2)。NextPDF 會追蹤巢狀深度,但在 recipe 層級,約定是一對一。 - 順序不可交換。 多個轉換是以矩陣串接的方式組合,因此先
rotate()再scale(),與先scale()再rotate()並不相同。請在同一個區塊內,按照你想要的順序套用它們。 - 樞紐點預設為原點。 若省略樞紐點,轉換就會繞著頁面原點旋轉,而不是繞著該形狀。這通常不是你想要的結果,所以請明確傳入樞紐點。
- Y 軸採用作者空間。 樞紐點的
y是距離作者座標左上角的距離,NextPDF 會把它投影到原生使用者空間。將原始 PDF 座標與作者 API 混用,會產生鏡像反轉的結果。 - 狀態外洩。 在轉換區塊內設定的顏色、字型與線寬,在
stopTransform()之後仍會留存,因為在這份 API 介面中,Q只會還原 CTM。如果後續章節不應繼承這些值,請像正式版範例那樣明確重設它們。
一個轉換就是單一的 cm 運算子,再加上一對 q/Q。每個部分都只有幾個位元組,不會帶來可測量的執行時間成本,因此這則 recipe 能輕鬆維持在 1500 ms / 96 MB 的預算之內。可重現性設定檔為 結構性(structural)。輸出包含 trailer 的 /ID 陣列,以及建立時的中繼資料;這些並非每次執行都穩定,因此比對前你必須先將它們正規化。轉換串流本身則是決定性的。
安全性備註
標題為「安全性備註」的區段- 資料落地與 PII 緩解措施。 不適用。這則 recipe 繪製的是幾何基本圖形與簡短標籤。它不處理任何外部或個人資料。
- 安全遙測與日誌清理。 這則 recipe 只寫出單一固定的進度訊息行。不會記錄任何文件內容。
- 威脅模型。 不適用。沒有任何輸入剖析、沒有密碼學運算,也沒有信任邊界。轉換純粹是一段內容串流的輸出。
- FIPS 模式行為。 不適用。沒有任何密碼學運算。
符合性
標題為「符合性」的區段| 陳述 | 規範 | 條款 | 參考 ID |
|---|---|---|---|
轉換會透過 cm 運算子,把一個矩陣串接到 CTM 上。 | ISO 32000-2 | §8.3.4 | |
| 多個轉換是以矩陣串接的方式組合;順序具有重要影響。 | ISO 32000-2 | §8.3.4 | |
q 儲存、Q 還原圖形狀態,藉此限定轉換的作用範圍。 | ISO 32000-2 | §8.4.2 | |
| 預設使用者空間原點位於左下角;一個單位等於 1/72 英吋。 | ISO 32000-2 | §8.3.2 |
這則 recipe 實作了所引用的 ISO 32000-2 圖形狀態與轉換條款。它並未主張全面符合 ISO 32000-2;所引用的條款僅限這則 recipe 實際運用到的部分。