ADR-029: Headless 報表引擎與中介文件模型
ADR 編號: 029 狀態: 已接受 (Accepted) 決策日期: 2026-08-06 決策者: Framework Team + AI Assistant
摘要
AppFuse 將提供面向開發者與 AI 的 headless 報表能力:應用先建立與渲染器無關的
ReportDocument 中介模型,再由 ReportRenderer 產生 PDF。報表採文件流式排版,統計圖表
在 Figure 區塊內擁有局部座標系;固定座標的證書、申請書與既有表單則屬於獨立的 PDF
套版能力,不共用報表 authoring model。
背景 (Context)
問題陳述
框架需要產生資料量與頁數都可能很大的報表,並讓前端按需預覽、選擇頁面後呼叫瀏覽器 列印。報表的設計者實際上是開發者與 AI,而不是使用圖形化設計器的 power user,因此核心 契約必須適合程式產生、版本控制、測試與重構。
既有 Word 文件模組以 DOCX 範本與 Apache POI handle 為中心,適合公文與套版文件,但它不是渲染器中立的文件模型。HTML/CSS 則是瀏覽器渲染語言;其分頁、頁首頁尾與列印結果受瀏覽器引擎影響,不適合作為框架報表的 核心語言。PDF 能穩定保存最終版面,但若直接拿 PDF 操作指令當 authoring API,資料群組、 表格跨頁、重複表頭與 keep-together 等報表語意會散落在座標計算中。
限制條件
- 公開 API 不得洩漏 PDFBox、JasperReports、SVG library 或其他渲染後端型別。
- 主要輸出是可保存、下載與列印的 PDF;瀏覽器原生列印對話框仍由使用者控制。
- 大型 PDF 不應要求前端先下載完整 Blob 才能顯示第一頁。
- 圖表需要向量圖、文字、座標軸與圖例,但任意座標不能污染整份報表的流式排版。
- 框架不得為報表工作佇列或產物宣告業務
@Entity;持久化與權限仍由應用擁有。 - 固定表單與資料驅動報表的版面語意不同,不能只因輸出都是 PDF 就合併模型。
名詞邊界
| 名詞 | 定義 |
|---|---|
| Report | 由資料集合、群組、彙總與條件內容驅動,依文件流自動分頁的輸出 |
| Fixed-layout PDF template | 以既有頁面為底,在固定座標填字、勾選、蓋章或放圖的文件 |
ReportDocument | 完成資料綁定後、尚未分頁成 PDF 的渲染器中立文件樹 |
Figure | 報表流中的單一區塊;區塊內可使用局部座標描述向量圖形 |
ReportArtifact | 渲染後產物的描述與串流存取契約,不代表資料庫 Entity |
考量的方案 (Options Considered)
方案 A: HTML/CSS 為核心,再轉成 PDF
優點:
- 開發者熟悉,文字與一般版面工具多。
- 可直接使用瀏覽器或 HTML-to-PDF 引擎。
缺點:
- paged media 支援與輸出一致性依賴特定引擎。
- 報表語意會退化成 DOM/CSS 細節,難以替換後端或做結構測試。
- 後端仍需固定並維護一套瀏覽器或 HTML renderer。
評分: 2/5
方案 B: 直接以 PDF drawing API 撰寫報表
優點:
- 最接近輸出格式,可精確控制每一個繪圖指令。
- 不需要額外的中介模型。
缺點:
- 應用必須自行處理測量、換行、表格跨頁、孤行寡行與重複區塊。
- authoring code 與 PDF library 綁死,難以測試及替換實作。
- AI 產生的低階座標程式脆弱,無法清楚表達報表意圖。
評分: 2/5
方案 C: JasperReports/JRXML 作為框架報表模型
優點:
- 已有成熟的 group、band、subreport、chart 與 PDF 輸出能力。
- 可 headless 執行,且有完整生態系。
缺點:
- 框架 API 與 JRXML/Jasper 的 band、expression 和 pagination model 綁定。
- 圖形化設計器不是本次 developer/AI-first 使用情境的必要能力。
- 應用需承擔另一套模板語言、編譯流程與相依版本。
評分: 3/5;可作為未來 renderer/adapter 的候選,不作 canonical model。
方案 D: DOCX 作為中介格式,再轉 PDF
優點:
- 可重用既有範本與 Word 文件能力。
- 使用者可用 Word 檢視或修改中間文件。
缺點:
- 轉檔結果依賴 Office/LibreOffice 或第三方 converter,PDF 不是直接產物。
- DOCX 的文件與套版語意不能完整代表報表分組、圖表及可預測分頁。
- 大型報表增加一次完整文件建立與轉換成本。
評分: 2/5;保留為文件匯出流程,不作報表核心。
方案 E: AppFuse 擁有 ReportDocument IR,由可替換 renderer 產生 PDF
優點:
- authoring API 直接表達章節、段落、表格、群組、彙總、圖形與分頁意圖。
- 模型可由 Java、AI 或日後其他 DSL 產生,且能做結構測試。
- PDF library、chart library 與 HTML rich-text parser 都留在 adapter 邊界。
- 能把流式排版與局部座標繪圖放在不同層次處理。
缺點:
- 必須自行定義模型、pagination contract 與 renderer conformance suite。
- v1 需克制功能範圍,不能一次複製成熟商用報表引擎的全部能力。
評分: 5/5
決策 (Decision)
選擇方案: E
1. canonical authoring model
框架擁有 backend-neutral 的 ReportDocument。第一個 authoring surface 是 type-safe Java
builder;應用可用 ReportTemplate<T> 把 domain/query model 轉成文件樹。IR 不等同 PDF
object model,也不以 HTML、JRXML 或 DOCX 作為內部事實來源。
概念契約如下;名稱可在 API design review 中微調,但責任邊界不可反轉:
public interface ReportTemplate<T> {
ReportDocument create(T data, ReportContext context);
}
public interface ReportRenderer {
boolean supports(ReportFormat format);
ReportArtifact render(ReportDocument document, RenderOptions options);
}
ReportDocument 的結構與樣式以 immutable value/object tree 為基線。公開節點至少能表達
document metadata、section、paragraph、table、image、figure、page break、header/footer、
group、aggregate 與 keep-together/page-break hint。大型重複資料可由有明確 ownership、關閉與
單次消費語意的 row source 提供,不要求先展開成完整記憶體樹。renderer 可以忽略無法實現的
soft hint,但必須透過 capability 或 diagnostic 明確回報,不得默默產生語意錯誤的輸出。
2. 兩層排版模型
報表最外層採 flow layout,由 renderer 決定測量、換行與分頁。任意座標只存在於有界的
Figure 內;Figure 本身仍像圖片或表格一樣參與文件流。如此可以產生折線圖、長條圖、
散佈圖與自訂統計圖形,又不讓 page/table layout 退化為整頁座標程式。
圖表語意放在 optional chart adapter,例如 ChartSpec -> Figure。核心只認得 Figure 的
尺寸、替代文字與向量/點陣內容;優先使用 SVG 作為 renderer 間的局部向量交換格式,必要時
允許 raster fallback。輸入 SVG 必須套用安全 policy,禁止外部資源與可執行內容。
3. PDF 是主要輸出,不是 authoring model
首個 ReportRenderer 直接產生 PDF,負責字型嵌入、文字 shaping、表格跨頁、重複表頭、
頁首頁尾、頁碼與圖形輸出。PDF backend 為內部實作細節;若採用 PDFBox、OpenPDF、
JasperReports 或其他 library,其型別不得出現在公開簽章。
HTML 只可作為受限 rich-text 輸入 adapter,轉成既有 IR 節點;不接受任意 HTML/CSS 控制
整份報表。DOCX 仍由既有 document 能力負責,報表引擎不以 DOCX-to-PDF 實作 PDF renderer。
4. 固定格式 PDF 套版是獨立能力
證書、申請書、政府表單與既有 PDF 底稿採 fixed-layout authoring model,其核心操作是
「選頁/定位/覆寫/填表/合併」。它可與報表共用底層 PDF runtime、字型、影像、安全與
binary/PDF artifact 基礎設施,但不得重用 ReportDocument、ReportArtifact 或 flow nodes
來假裝固定座標版面。
5. 預覽與列印是交付通道,不進入 IR
大型 PDF 以支援 HTTP byte-range 的受權 URL 交給 PDF.js,前端只渲染可視頁並以有界快取 回收離開視窗的 canvas/page resources。選頁列印由後端從已產生的原始 PDF 建立 subset PDF, 前端載入 subset 後呼叫瀏覽器列印介面。
瀏覽器原生列印對話框不能由 Web API 安全地預填使用者的 page range,也不保證 silent print; 框架只保證開啟正確的完整或 subset 文件並觸發 print。silent/kiosk printing 屬於另外的桌面、 瀏覽器管理政策或原生整合能力。
6. ownership 與持久化
純 renderer 是同步、無狀態 library。長時間產生、排程、重試、進度、授權與 retention 是 application orchestration。框架可提供 SPI、狀態 value types 與參考 adapter,但不宣告 report job/report artifact Entity,遵循框架 jar 不擁有 Entity。
v1 範圍
| v1 必須提供 | 延後或獨立處理 |
|---|---|
| Java builder、immutable IR、樣式與量測單位 | JSON/YAML 報表 DSL 與 GUI designer |
| 段落、表格、圖片、群組、彙總、頁首頁尾、頁碼與分頁 hints | crosstab、互動式 drill-down、任意 script/expression engine |
| 直接 PDF renderer 與字型設定 | PDF 以外 renderer 的完整實作 |
Figure、SVG/raster graphic 與至少一條 chart adapter 路徑 | 全套商業圖表元件 |
| PDF metadata、頁數與 diagnostics | PDF/A、數位簽章、加密與無障礙標記完整支援 |
| Range-friendly delivery、選頁 subset、瀏覽器列印整合契約 | silent/kiosk printing |
Fixed-layout PDF template 不在 report v1 內;它應另立 capability/ADR 與交付計畫。
權衡分析 (Trade-offs)
我們獲得什麼 (Gains)
- 報表意圖與 PDF library 分離,開發者與 AI 可產生可讀、可測的高階模型。
- 同一份 IR 可在不改應用 authoring code 的前提下更換 renderer 或增加輸出格式。
- 圖表擁有必要的座標能力,同時保住一般報表的 flow/pagination 語意。
- 大型報表的產生、儲存、按需預覽與選頁列印形成清楚但可分階段交付的管線。
我們放棄什麼 (Losses)
- 不直接取得 JasperReports 的完整功能面與 designer 生態。
- 不承諾任意 HTML/CSS 都能像瀏覽器一樣渲染。
- v1 不提供整頁任意座標 DSL,也不把固定表單混入報表 API。
風險與緩解措施 (Risks & Mitigations)
| 風險 | 嚴重性 | 機率 | 緩解措施 |
|---|---|---|---|
| 自建 pagination scope 膨脹 | 高 | 高 | 鎖定 v1 節點與排版規則;以代表性報表 corpus 驗收 |
| 中文字型、fallback 與 shaping 結果不一致 | 高 | 中 | 明確 font registry、嵌入政策與跨平台 golden tests |
| 大型表格造成記憶體壓力 | 高 | 中 | renderer 採逐頁輸出;資料來源與節點引入受控 streaming/batching contract |
| SVG 帶入外部資源或 script | 高 | 中 | sanitize/allowlist、禁止 network/file resolver、限制尺寸與複雜度 |
| subset PDF 破壞 outline、link 或 metadata | 中 | 中 | 定義保留規則並以 PDF 結構與視覺測試驗證 |
| IR 過早凍結 | 中 | 中 | v1 保持 Java API;序列化 schema 另立版本契約後才承諾相容性 |
實作不變量與驗證
- 公開 API 不出現 renderer vendor 型別或 raw PDF drawing object。
- 一般 flow node 不接受絕對頁面座標;局部座標只存在 Figure/fixed-layout capability。
- 同一輸入、字型組合與 renderer 版本必須產生等價的頁數、文字與版面。
- renderer diagnostics 能指出不支援的節點、遺失字型、overflow 與被降級的圖形。
- PDF renderer 以結構 assertion、文字擷取、頁面 raster golden diff 及記憶體 benchmark 驗證。
- Range delivery 必須驗證
Accept-Ranges、Content-Range、206、條件式請求及各 storage backend 的實際 ranged read,不能只在完整InputStream上呼叫skip()。 - 前端必須驗證第一頁可在完整檔案下載前顯示、頁面資源會回收、授權失效可處理,且列印的 subset 頁序與使用者選擇一致。
具體交付順序與完成條件見 報表產生開發計畫。
相關文檔 (References)
變更歷史 (Change Log)
| 日期 | 變更內容 | 變更者 |
|---|---|---|
| 2026-08-06 | 初版;確立 ReportDocument IR、兩層排版、PDF renderer 與 fixed-layout 邊界 | Framework Team + AI |
文檔維護者: Development Team + AI Assistant
最後審閱: 2026-08-06