圖形:path(路徑)、shading(漸層著色)與 transform(座標轉換)的基本元件
重點速覽
標題為「重點速覽」的區段Graphics 模組會將繪圖意圖轉換成 PDF 圖形運算子。它涵蓋 path(路徑)、線條樣式、色彩空間、座標轉換、shading(漸層著色)、pattern(圖樣)、半色調與影像載入。
composer require nextpdf/core:^3概念總覽
標題為「概念總覽」的區段Graphics 是向量與點陣的繪圖層。它產生的運算子序列會由 ContentStream 與 Writer 模組序列化成 PDF。content stream(內容串流)把頁面內容編碼成一連串有序的圖形運算子 —— ISO 32000-2 §8。本模組負責輸出這些運算子,並不寫出檔案。
DrawingEngine 是主要操作介面。它是一個支援鏈式呼叫且具狀態的 builder(建構器)。每個 setter 都回傳 self,累積一筆圖形狀態變更或一個 path-painting 運算子,並附加到內部緩衝區,你可以用 getStream() 讀取它。這個引擎直接對映 PDF 圖形狀態 —— 線寬、線條樣式、描邊與填色、alpha 與混色模式、斜接限制、soft mask(柔性遮罩)、裁切、疊印、平坦度、平滑度、算繪意圖、黑色生成與底色移除,都有明確記載的運算子對應。與色彩相關的 setter 接受一個 Color 值物件,或一個明確的 ColorSpace,所以裝置色彩空間與 CIE 色彩空間共用同一種呼叫形式。
這個引擎還搭配三組相關元件。第一組是影像輸入。ImageLoader 會將檔案或記憶體中的位元組區塊解碼成 ImageLoadResult。ImageRegistry 透過 MemoryReport 對已解碼影像去重並追蹤,讓大型文件維持在記憶體預算內。第二組是向量匯入。SvgParser 與 EpsParser 會將外部向量格式翻譯成同一份運算子串流,並對外提供 getBoundingBox() 供版面配置使用。第三組負責裝置色彩保真度:shading(漸層著色,ShadingManager,包含 Type2/Type3 與 mesh 系列)、pattern(圖樣,PatternFill)、半色調(Type1/Type5/Type6/ Type10/Type16)、轉換函式,以及以 ICC 為基礎的色彩空間。
TransformEngine 是專責座標轉換的精簡配套元件。它用 startTransform() 與 stopTransform() 包住一段轉換,這兩者會輸出 q 與 Q 這組 save/restore 配對。它提供具名的仿射輔助方法 —— scale、translate、rotate、skew、mirrorH、mirrorV —— 每個都接受一個選用的樞軸點。轉換矩陣會把內部座標空間對映到目標座標空間。這與 ISO 32000-2 套用在 shading 定義域上的模型相同 —— 見 §8.7.4。
色彩管理遵循 ADR-012:ICCBased 與以 CIE 為基礎的色彩空間會輸出明確的 cs/CS 內容串流運算子,而不是依賴裝置色彩 fallback。ICC profile 會被包裝成 ICCBased 串流,並依 ISO 32000-2 §8.6.5.5 帶有正確的成分數量。
API 介面
標題為「API 介面」的區段| 類別 | 主要方法 | 角色 |
|---|---|---|
DrawingEngine | getStream(), reset(), setLineWidth(), setLineStyle(), setDrawColor(), setFillColor(), setAlpha(), setSoftMask(), clip(), setOverprint(), setRenderingIntent(), line(), rect(), circle(), ellipse(), polygon(), linearGradient() | 有狀態的 path 與圖形狀態運算子建構器 |
TransformEngine | startTransform(), stopTransform(), scale(), translate(), rotate(), skew(), mirrorH(), mirrorV(), getStream() | 仿射座標轉換工具 |
ImageLoader | load(string $filePath), loadFromString(string $data, string $mimeType) | 將影像解碼成 ImageLoadResult |
ImageRegistry | load(), loadFromString(), getMetadata(), memoryUsage(), reset() | 具去重與記憶體報告功能的影像快取 |
SvgParser | parse(), parseFile() | 將 SVG 翻譯成運算子串流 |
EpsParser | parse(), parseFile(), getBoundingBox() | 將 EPS 翻譯成運算子串流 |
ShadingManager | shading 註冊與字典輸出 | 軸向、放射狀與 mesh shading |
Halftone(抽象類別) | halftoneType(), toDict(), hasStream(), getStream() | Type 1/5/6/10/16 半色調網屏 |
執行 composer docs:generate-api-php -- --module=Graphics 即可取得完整的 PHPDoc 表格。
程式碼範例 —— 快速上手
標題為「程式碼範例 —— 快速上手」的區段來源:examples/06-colors-and-drawing.php。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\Color;use NextPDF\Graphics\DrawingEngine;use NextPDF\Graphics\LineStyle;
$engine = new DrawingEngine();
$engine ->setLineWidth(1.5) ->setDrawColor(Color::rgb(0, 51, 102)) ->setFillColor(Color::rgb(230, 240, 250)) ->rect(20.0, 20.0, 160.0, 80.0) ->line(20.0, 110.0, 180.0, 110.0, new LineStyle(dash: [3.0, 2.0]));
$contentStreamBytes = $engine->getStream();程式碼範例 —— 正式環境
標題為「程式碼範例 —— 正式環境」的區段這段程式碼會把附帶記憶體報告的影像 registry 與一段 transform 包夾組合在一起。它的結構對應 examples/07-images.php 與 examples/21-transforms.php 所用的結構。
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\DrawingEngine;use NextPDF\Graphics\ImageRegistry;use NextPDF\Graphics\TransformEngine;
$registry = new ImageRegistry();$image = $registry->load('/srv/assets/logo.png');
$report = $registry->memoryUsage();if ($report->bytes > 32 * 1024 * 1024) { // Decoded image cache exceeded the budget — reset before the next page. $registry->reset();}
$transform = new TransformEngine();$transform ->startTransform() ->translate(40.0, 700.0) ->scale(0.5, 0.5) ->stopTransform();
$engine = new DrawingEngine();$engine->reset();$page = $transform->getStream() . $engine->getStream();邊界情況與陷阱
標題為「邊界情況與陷阱」的區段DrawingEngine是有狀態的。在各自獨立的頁面之間要呼叫reset(),否則前一個圖形狀態會洩漏到下一份串流。TransformEngine需要一組成對的startTransform()/stopTransform()。如果包夾不對稱,會留下一個懸空的q,並在下游的 Writer 裡破壞 save/restore 堆疊。setSoftMask()、setOverprint()、setBlackGeneration()與setUnderColorRemoval()會寫出延伸圖形狀態標記。在拒絕該功能的 profile 下,這些方法不會生效。在依賴視覺結果之前,先檢查 profile 守門條件。ImageRegistry會依內容去重。兩個位元組內容相同的路徑會共用同一個物件。不要假設每一次load()呼叫都會對應一張 PDF 影像。EpsParser::getBoundingBox()回傳的是剖析出的邊界框,而不是頁面框。如果 EPS 溢出目標矩形,請自行套用裁切。- 黑點補償屬於建議性質,且以標記為基礎。它本身並不會轉換像素。
驗證與錯誤處理
標題為「驗證與錯誤處理」的區段有兩項產生端變更屬於破壞性變更。兩者都會把過去靜默發生的損毀,改為在呼叫處明確失敗。
輸入驗證現在會擲出例外(遷移注意事項)。 繪圖輸入在抵達運算子串流之前會先經過驗證,並以 InvalidArgumentException 拒絕格式錯誤的值。呼叫端過去若傳入 NaN、Infinity 或超出範圍的值,會靜默產生損毀的運算子;同樣的輸入現在會擲出例外。會受驗證的限制條件如下:
- 色彩 alpha 必須是有限值,且落在
[0, 1]範圍內。 - CTM 運算元、模板尺寸、漸層頂點座標與 mesh 補丁座標都必須是有限值 —— 不可有
NaN或Infinity。 - 漸層補丁的邊緣旗標必須是
{0, 1, 2, 3}其中之一。 - Type 2/3/4 函式參數與半色調參數都會進行邊界檢查。
- 著色劑名稱會被轉義。
- OCG(選用內容)圖層名稱不得為空。
在升級前,請稽核那些從上游資料計算座標或 alpha 的呼叫處:過去能通過的值,現在會成為硬性錯誤。
ICCBased 的 /N 預設為 fail-closed。 純 PDF 輸出會拒絕 /N 成分數量落在 {1, 3, 4} 之外的 ICCBased 色彩空間,並把宣告的 /N 與內嵌 profile 以及 /Alternate 空間相互核對。這遵循 ISO 32000-2 §8.6.5.5 對 ICCBased 串流的規則,該串流帶有 /N 以及一個 /Alternate 空間。N 通道 ICC profile(例如 N = 6 的六色 profile)只有在 PDF/A 或 PDF/X profile 啟用、並透過 IccConformancePolicy::ProfileGated 選擇加入時才會保留。這是針對成分數量的結構性守門,並非宣稱已通過 PDF/A 或 PDF/X 認證。
運算子的輸出量與繪圖呼叫次數成線性關係 —— 對緩衝區做 O(n) 次附加,不會重排。影像解碼成本主要取決於 codec 與像素數量,而不是 registry。registry 以內容雜湊去重,是大型文件最重要的優化槓桿:重複使用的素材只需付出一次解碼與一個 PDF 物件的成本。本模組參考工作負載的 performance_budget 為 1500 ms 牆鐘時間與 64 MB 尖峰記憶體。可用 ImageRegistry::memoryUsage() 觀察已解碼影像的記憶體佔用,並用 reset() 在各個頁面群組之間釋放它。
安全性注意事項
標題為「安全性注意事項」的區段SvgParser 與 EpsParser 會處理不受信任的向量輸入。請把兩者都視為會面對惡意資料的剖析器。呼叫 parse() 之前,請先強制套用輸入大小上限。當來源是使用者提供時,請在受限的 worker 裡執行擷取。EPS 是一種 PostScript 方言。剖析器只翻譯一個受限的子集,並不會執行通用直譯器,但你仍應限制輸入大小與剖析時間。影像載入器會解碼第三方 codec。請讓執行環境的影像擴充套件保持在最新版本,並對解碼後的尺寸設定上限。關於信任邊界與 worker 隔離指引,請參閱 /modules/core/security/ 一節中的引擎威脅模型。
符合性
標題為「符合性」的區段本模組輸出的 PDF 圖形運算子結構與 ISO 32000-2 §8 一致,ICCBased 色彩空間字典依 §8.6.5.5,shading 字典的 Domain、Function、Matrix 與 BBox 則依 §8.7.4。這些都是實作層面的事實:運算子與字典的形狀由 src/Graphics/ 產生,並由 tests/Unit/Graphics/ 加上 tests/Golden/PdfWriter/PdfWriterShadingGoldenBaselineSmokeTest 與 PdfWriterExtGStateGoldenSmokeTest 基準線加以驗證。它們並不代表端對端的 PDF 2.0 或 PDF/X 符合性聲明。全文件層級的符合性由 /modules/core/conformance/ 一節所述的 oracle 與 golden 測試套件另行驗證。ICC OutputIntent 的 profile 行為是由 ADR-011 與 ADR-012 決定,而非僅由本模組決定。