跳至主要内容

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
段落、表格、圖片、群組、彙總、頁首頁尾、頁碼與分頁 hintscrosstab、互動式 drill-down、任意 script/expression engine
直接 PDF renderer 與字型設定PDF 以外 renderer 的完整實作
Figure、SVG/raster graphic 與至少一條 chart adapter 路徑全套商業圖表元件
PDF metadata、頁數與 diagnosticsPDF/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 另立版本契約後才承諾相容性

實作不變量與驗證​

  1. 公開 API 不出現 renderer vendor 型別或 raw PDF drawing object。
  2. 一般 flow node 不接受絕對頁面座標;局部座標只存在 Figure/fixed-layout capability。
  3. 同一輸入、字型組合與 renderer 版本必須產生等價的頁數、文字與版面。
  4. renderer diagnostics 能指出不支援的節點、遺失字型、overflow 與被降級的圖形。
  5. PDF renderer 以結構 assertion、文字擷取、頁面 raster golden diff 及記憶體 benchmark 驗證。
  6. Range delivery 必須驗證 Accept-Ranges、Content-Range、206、條件式請求及各 storage backend 的實際 ranged read,不能只在完整 InputStream 上呼叫 skip()。
  7. 前端必須驗證第一頁可在完整檔案下載前顯示、頁面資源會回收、授權失效可處理,且列印的 subset 頁序與使用者選擇一致。

具體交付順序與完成條件見 報表產生開發計畫。

實作註記 (Implementation Notes)​

2026-09-29:REP-01 renderer spike​

本註記記錄 REP-01 之後確認的 backend 選型,屬於 renderer 內部實作;上述決策的責任邊界不變 (公開 API 不出現 renderer vendor 型別)。證據見 REP-01 報表 renderer spike。

  • PDF backend(2026-09-29 由框架維護者確認):REP-03 採用 Apache PDFBox 3.0.x(Apache-2.0) 加上 AppFuse 自有排版層;Figure 向量輸出採用 pdfbox-graphics2d 與 Batik。三份代表性報表已在 PDFBox 上重現頁數、文字、metadata 與點陣基線。只有 PDFBox 做了實測;若 REP-03 發現自製排版 範圍明顯膨脹(例如複雜文字、進階表格),先以同一組 corpus 實測 Apache FOP 再決定是否更換。
  • 排版歸屬:PDFBox 不提供後備字型、斷行、分頁與表格版面,這些由 AppFuse renderer 負責, 也是 REP-02/03 的主要工作量。
  • 字型政策:PDFBox 無法嵌入 CFF 輪廓的 OTF,可變字型只會用預設 instance;font registry 限定靜態 TrueType。
  • Figure:經 Batik 與 pdfbox-graphics2d 輸出的圖內文字是向量外框,無法抽取;alt text 與實體文字圖說為必要欄位, REP-04 需要決定圖表文字的處理方式。
  • 記憶體:頁面物件樹會保留到輸出結束,存活記憶體隨頁數約以每頁 12KB 成長;內容串流可寫入暫存檔。
  • 跨平台:macOS、Linux arm64、Linux amd64 的輸出除 trailer /ID 外位元組相同。
  • 待補:CI pipeline 接入、文字 shaping 與 bidi。

2026-09-29:REP-02 ReportDocument 契約設計​

契約經設計審閱(peer review 3 輪)通過,全文見 REP-02 ReportDocument 核心契約設計。影響架構的取捨:

  • IR 只放已綁定的資料:群組與彙總由樣板計算,IR 只記錄列角色與群組路徑;框架提供 GroupedRows, 彙總器事先註冊、記憶體與列數無關。
  • 大型明細的列來源:開啟前不持有資源,由 renderer 在每次 render 時開啟並負責關閉,文件可重複 render; 延遲來源的列在讀取時以同一套規則驗證,失敗時不產生任何產物。
  • v1 範圍再收斂:不含頁面層級彙總(本頁小計、承上頁)與首頁/奇偶頁不同的頁首頁尾。
  • 不可分割內容:圖形超頁時等比縮小並回報 WARNING;連圖說都放不下時回報 ERROR 且不產生產物。
  • 實驗性標示:公開 API 以新的 @ExperimentalApi 註解標示;此框架層機制的規則更新另行處理。

2026-09-29:REP-03 PDF renderer 契約設計​

契約經設計審閱(3 輪)通過,全文見 REP-03 PDF renderer 基線契約設計。影響架構的取捨:

  • 相依政策:PDFBox 以 compileOnly 編譯,使用報表的應用自行加入 org.apache.pdfbox:pdfbox 3.0.x; 未使用報表的應用不載入 PDFBox。缺少時 PdfReportRenderer.create 丟出指引明確的例外。
  • 輸出兩階段:完整 PDF 先序列化到 renderer 自己的暫存檔,最後一次取消檢查通過後才複製到呼叫者的 OutputStream; 第一個位元組寫出前的任何失敗或取消都不寫出任何內容,寫出後的失敗由呼叫者丟棄部分內容。
  • 排版架構:先排版成每頁的定位 box、繪製只讀 box;排版引擎只依賴字寬量測介面,不直接依賴 PDFBox。
  • 前進保證:自動換頁後同一內容仍放不下即以 ERROR 停止,明示的 PageBreak 不受限。
  • REP-04 之前的 Figure:以替代文字框與圖說呈現並回報 WARNING,不拒絕 render。

2026-09-29:REP-03 PDF renderer 實作​

已實作並通過程式碼審閱(3 輪),細節見契約設計的「程式碼審閱裁決」與「實作註記」。影響後續工作的事實:

  • PDFBox 版本下限 3.0.8:compileOnly 的版本範圍不會傳給下游,因此由執行期檢查 [3.0.8,4.0.0); 3.0.8 是驗證 corpus、決定性與暫存檔權限的版本。
  • 記憶體有界但不是常數:-Xmx128m 下 50,000 列(2,248 頁)完成,GC 後存活量每頁約增加 11KB(PDFBox 保留頁面物件, 與 REP-01 一致);圖片快取以弱參照持有,每列一張不同圖片的長表格不隨列數成長。
  • 跨平台:三份 corpus 在 macOS 與 Linux(arm64、amd64)除 PDF /ID 外位元組相同;REP-05 的 ETag 不能直接以 PDF bytes 雜湊, 需排除或固定 /ID。
  • REP-01 spike 已移除(FU-100);corpus 與點陣 golden 改由正式 renderer 產生。

2026-09-29:REP-04 Figure 與圖表契約設計​

契約經設計審閱(3 輪)通過,全文見 REP-04 Figure 與圖表契約設計。影響架構的取捨:

  • 取代 REP-01 的 Batik 路線:Figure 的 SVG 改為 AppFuse 自行解析的受限子集,直接繪製成 PDF,不採用 Batik+pdfbox-graphics2d。REP-01 實作註記中「圖內文字是向量外框、無法抽取」的前提因此不再成立:SVG 內文字以 ReportFonts 的字型輸出為可抽取的 PDF 文字,也不需在 JVM 全域註冊 AWT 字型。點陣內容仍無法抽取,altText 與圖說維持必填。
  • SVG 子集是模型契約:元素與屬性白名單、複雜度上限與衍生數值範圍(累積矩陣、viewBox 映射比例、轉換後座標) 在 ReportDocument.Builder#build() 驗證,不合法者為 ERROR、不產生產物;子集不含任何引用外部資源的元素或屬性(FU-105)。
  • 圖表 adapter 邊界:ChartSpec(長條圖、折線圖)在模型層產生子集內的 SVG,renderer 核心不理解圖表語意; 模型層沒有字型,版面以字寬上界估算,對齊交給 renderer 的真實字寬。

2026-09-30:REP-04 Figure 與圖表實作​

已實作並通過程式碼審閱(3 輪),細節見契約設計的「程式碼審閱裁決」與「實作註記」。影響後續工作的事實:

  • 圖內文字可抽取已實證:corpus 統計報表的刻度、類別與圖例文字可由 PDF 抽取;描邊文字以 PDF 文字繪製模式輸出。
  • 安全政策(FU-105)已落實:XML 解析禁止 DOCTYPE 與外部實體並使用拒絕式 resolver,測試以本機 socket 確認不會連線; 不繪製的子樹同樣受元素與深度上限約束;文字位置以累積區間檢查,負向位移不會被字寬估算掩蓋。
  • 跨平台:三份 corpus、SVG 範例、描邊文字與點陣 Figure 在 macOS 與 Linux(arm64、amd64)除 /ID 外位元組相同。
  • v1 取捨:SVG 在 build() 與 render 各解析一次,不做快取;圖表類別標籤收進 Figure 後若相撞則略過。

2026-09-30:REP-05 ReportArtifact 與交付契約設計​

契約經設計審閱(3 輪)通過,全文見 REP-05 ReportArtifact 與交付契約設計。影響架構的取捨:

  • 兩種 render 並存:本 ADR 概念契約的 ReportArtifact render(document, options) 以暫存檔承載產物、關閉即刪除; REP-03 的串流版 render(document, out, options) 保留。artifact 不是 Entity;保存交給 FileStorage,框架不擁有 job 或產物 Entity。
  • 非同步 seam 只到值型別:狀態值型別對齊前端 ReportViewer、頁數進度回呼;executor、重試、保存期限屬應用。
  • 時間分離:PDF metadata 的文件時間不用於保存期限;保存時間取自保存時的時鐘。
  • 交付:直接串流在請求執行緒同步寫入並關閉,不依賴 async executor 或 GC;保存後以 fileId 為 ETag,已授權內容一律私有快取。
  • 授權與稽核在應用:依 ADR-031 分類;fileId 不作授權憑證;subset PDF 沿用原始報表的授權。

2026-09-30:REP-05 ReportArtifact 與交付實作​

已實作並通過程式碼審閱(2 輪),細節見契約設計的「程式碼審閱裁決」與「實作註記」。影響後續工作的事實:

  • 保存成功就交出 fileId:StoredReports.save 寫入成功後,關閉讀取串流或刪除暫存檔的失敗只記 WARN report.resource.cleanup-failed,不讓呼叫者失去已保存的檔案;寫入失敗時儲存例外為主例外,清理錯誤附為 suppressed。
  • Content-Disposition 依 Spring 實際行為:非 ASCII 檔名寫 filename* 與 Spring 產生的 RFC 2047 後備,ASCII 檔名只寫 filename;既有 FileResponseBuilder 宣稱的 ASCII 底線後備從未生效,另立 FU-104。
  • 報表 tenant 中立有規則保護:ArchUnit 禁止 report.. 相依 tenant 機制,servlet/Spring Web 只在 report.web; tenant 與 tenantless 以不同 partition 接線,框架測試驗證互不可見。消費端指南併入 REP-09。
  • 跨平台:新增交付路徑不改變 PDF 輸出;三份 corpus 等 8 份 PDF 在 macOS 與 Linux(arm64、amd64)除 /ID 外位元組相同。

2026-09-30:REP-06 Ranged read 契約設計​

契約經設計審閱(3 輪)通過,全文見 REP-06 Ranged read 契約設計。影響架構的取捨:

  • 能力宣告而非模擬:FileStorage 以 default 方法 rangeSupport()/openRange(...) 宣告能否只讀一個區段;Local、S3、 Azure、SFTP 原生實作,不支援的 backend(包括下游自訂者)回完整內容與 Accept-Ranges: none,不以 skip() 模擬。
  • 下載 builder 只服務 GET/HEAD:HEAD 由 metadata 建構回應、不讀內容;其他方法視為程式錯誤。條件式請求先於 Range;If-Range 只接受相同的強 ETag。
  • 沒有 write-once 識別就沒有驗證器:每次重新產生的 ZIP 不送 ETag,If-Range 一律不符,避免續傳拼接不同內容。
  • 跨來源:框架公開 range 回應需 expose 的 headers;參考伺服器的 CORS 調整隨 FU-103。

2026-09-30:REP-06 Ranged read 實作​

已實作並通過程式碼審閱(2 輪),細節見契約設計的「程式碼審閱裁決」與「實作註記」。影響後續工作的事實:

  • 四個 backend 原生區段讀取已實證:同一組契約測試跑 Local、SFTP、TransactionAwareFileStorage 與真實服務(S3Mock、 Azurite);S3/Azure 另以 mock client 驗證送出的 range,SFTP 以伺服器端記錄確認第一個讀取即從 offset 開始。
  • 真實服務測試抓到既有缺陷:AzureBlobFileStorage 的 blob metadata 名稱含連字號,對 Azure 的寫入一律失敗,已修正。 MinIO 官方映像已不可取得,S3 相容驗證改用 S3Mock。
  • REP-07 的前提:已保存的報表可經 FileResponseBuilder.request(...) 以 206 按需讀取。參考實作為同來源部署 (開發由 Vite 代理 API;SIT/UAT 以 context path 區分),不需 CORS;跨來源部署或直接讀取預簽 URL 時才需設定 CORS(見檔案儲存設計指南 §4.10)。

2026-09-30:REP-07 大型 PDF 按需預覽設計​

契約經設計審閱(3 輪)通過,全文見 REP-07 大型 PDF 按需預覽設計。影響架構的取捨:

  • range 經 HttpClient:PdfViewer 以自製的 PDF.js range transport 讀取受權 URL,每個區段沿用 HttpClient 的 Bearer、 refresh、401 重試與 cookie;框架不公開 token。伺服器回 200 時退回完整下載;區段回應逐項驗證,不一致即不送入 PDF.js。
  • 有界的是框架能控制的部分:canvas、縮圖、DOM 節點與保留渲染資源的頁數有上限(memoryLimits);PDF.js 內部的 page proxy、worker 已下載的原始位元組與快取不受控,如實說明。
  • 搜尋無法中途取消:以跨搜尋世代共用的單一文字抽取佇列保證最多一頁在進行;搜尋不中止共用的區段請求。
  • 內容替換的偵測依賴強 ETag:弱或缺少 ETag 時同長度替換無法偵測;框架端點以 fileId 為強 ETag。

相關文檔 (References)​

變更歷史 (Change Log)​

日期變更內容變更者
2026-08-06初版;確立 ReportDocument IR、兩層排版、PDF renderer 與 fixed-layout 邊界Framework Team + AI
2026-09-29新增 REP-01 實作註記:確認 PDFBox+AppFuse 自有排版層,記錄字型與 Figure 限制Framework Team + AI
2026-09-29新增 REP-02 實作註記:ReportDocument 契約設計通過審閱的架構取捨Framework Team + AI
2026-09-29新增 REP-03 實作註記:PDF renderer 契約設計通過審閱的架構取捨Framework Team + AI
2026-09-29新增 REP-03 實作完成註記:PDFBox 執行期版本下限、記憶體與跨平台驗證結果Framework Team + AI
2026-09-29新增 REP-04 設計註記:以自寫受限 SVG 子集取代 Batik 路線、子集為模型契約、圖表 adapter 邊界Framework Team + AI
2026-09-30新增 REP-04 實作完成註記:圖內文字可抽取、FU-105 安全政策落實、跨平台一致與 v1 取捨Framework Team + AI
2026-09-30新增 REP-05 設計註記:兩種 render 並存、artifact ownership、時間分離、同步交付與私有快取Framework Team + AI
2026-09-30新增 REP-05 實作完成註記:保存成功即交出 fileId、Content-Disposition 實際行為、tenant 中立規則與跨平台結果Framework Team + AI
2026-09-30新增 REP-06 設計註記:ranged read 能力宣告、下載 builder 只服務 GET/HEAD、ZIP 無驗證器、跨來源 headersFramework Team + AI
2026-09-30新增 REP-06 實作完成註記:四個 backend 原生區段讀取實證、Azure metadata 缺陷修正、REP-07 前提Framework Team + AI
2026-09-30新增 REP-07 設計註記:range 經 HttpClient、有界範圍、搜尋單一佇列、強 ETag 偵測替換;修訂 REP-06 註記的 CORS 前提Framework Team + AI

文檔維護者: Development Team + AI Assistant
最後審閱: 2026-08-06

2026-09-30:獨立固定表單能力與 RPT 規格​

使用者確認訂購單、簽收單、繳款單等固定版面是通用需求,並要求報表規格同時支援。 沿用本 ADR 的模型分離決策,新增獨立 pdf.FixedDocument/FixedPdfRenderer 實驗性 API; 共用字型與 PDF runtime,沒有把固定座標加入 ReportDocument 或重用 ReportArtifact。 原 v1「不提供整頁任意座標 DSL」仍描述 flow 報表範圍,獨立固定能力見 固定表單契約。 RPT 可先獨立交付範本與驗證;US 負責流程及授權,見 報表規格指南。 本紀錄不是下游實際採用或人工版面驗收通過的宣稱。

2026-10-01:中立 PDF 交付與獨立 RPT 工作流程​

固定表單與 flow 共用 PdfArtifact 的資源生命週期及 StoredPdfs 保存契約;只有 flow 產物是 ReportArtifact。 兩個 server 參考實作以 ReportJobWork.Flow/Fixed 分派產生器,再共用 ReportJob 的保存、取消、授權與交付。 此變更不合併兩種文件模型。報表開發由 /report spec|impl|verify|accept|status 承接,不要求先建立 US; 產品流程與權限仍由 US 引用 RPT。API 與工作流程目前在工作區驗證,尚未發布。

2026-10-01:報表開發支援審閱收尾​

固定表單、共用交付管線及獨立 RPT 工作流程經三輪審閱通過,28 條意見皆已處置。 中立 PdfArtifact/StoredPdf/StoredPdfs 遵循一般公開 API 相容政策,只有 Fixed* 型別為實驗性。 本輪接受 code-point 換行、尾端空白對齊及素材建構/解碼錯誤分類的既有限制;詳細契約與診斷邊界見固定表單指南。 RPT 結果只寫獨立帳本;先脫敏、保存不可覆寫的 runs 附件,再 record,真人接受不自動承接變更後來源。 圖片/底稿重用另由 FU-109 追蹤,LocalFileStorage 分區路徑隔離另由 FU-110 追蹤。

驗證包括 141 項選定 JUnit 與 12 項證據工具測試,以及相關建置、Javadoc、文件站及 adapters 檢查; 不代表全量回歸、固定表單真人版面接受、實紙掃讀或下游實際採用完成。 本次收尾只解除成果提交與方法論發布的人審閘門,不表示已提交、發布或選定版本;發布順序見報表開發計畫。

2026-10-05:流式報表四邊框線、跨列與首頁表頭審閱收尾​

本次框架擴充經兩輪人審通過,8項意見均已處置,第2輪無新增意見。 完整 API 與繪製契約見流式報表版面擴充。接受的取捨與理由:

  • 相鄰格邊獨立繪製:不提供 CSS border-collapse,保留各格獨立樣式,不引入共享邊的衝突決策。 作者可指定相同樣式或只保留其中一邊,以取得一致外觀。
  • 跨列限同筆不可拆紀錄:Cell.rowSpan 接受1~256,限正 maxRecordsPerPage 的同筆不可拆 DETAIL 列, 以最多256列加邊界列的預看維持有界排版與同頁幾何。跨紀錄、可拆列、表頭及跨頁跨度拒絕, 不延伸為任意 CSS rowspan。
  • 首次表頭不能形成孤立簽核頁:首頁表頭不能與首筆最小內容同頁即 ERROR, 不另產生空簽核頁,讓首次表頭與其明細保持同頁。

逐列線略過跨列格內部,合併外緣及左右欄仍保留;整筆底色先於所有文字與框線繪製, 末列增高依索引回寫。已發布建構器與非跨列診斷行為保留,未發布的 Cell 五參數過渡建構器不納入 API。

驗證包括框架1632例(9 skipped)、nbsos報表與架構64例,以及 revision10 的四案例六頁 HTML/PDF 比對(最大位置差0.4676mm)。10,000筆跨列及首次表頭案例在128MB heap下產生667頁。 本輪只驗證本機 composite build,不宣稱跨平台、正式資料 adapter、revision11 或真人 PDF 接受。 擴充仍未發布,alpha.24不含新 API;人審收尾解除成果提交閘門,沒有提交、切版或發布。 下游 HTML/PDF 版面及產品操作授權仍由應用的 RPT/US 驗收承接。

2026-10-06:分節最後內容頁摘要覆寫審閱收尾​

本擴充經兩輪人審通過,3條意見均已處置,第2輪無新增意見。完整公開契約見流式報表版面擴充。接受的取捨與理由:

  • 獨立覆寫值模型:node.PageSummaryOverride 各側 null 表示繼承、空清單表示清空,避免改變 PageRegion 既有空內容語意。各側依分節覆寫 → 文件 lastPage → everyPage 解析。
  • 無內容分節直接拒絕:沒有已放置內容時回報 summary-empty,不猜落頁;有表頭的空表格仍屬內容,節尾換頁不改最後內容頁。
  • 共用文件摘要配置:必須已有 PageSummary,沿用其樣式、線及間距;不隱式建立摘要或增加分節樣式。巢狀覆寫與同頁多節結束衝突均失敗。
  • 基線與既有 bytes 相容的取捨:僅覆寫頁逐行共用 baseline,一般頁保留原算法;不同字級的右側頁次可在覆寫頁下移,跨頁一致時左右使用相同字級/行高。
  • 環境限定的 bytes 基準:固定已發布 alpha.24、字型與 metadata;常駐 golden 先核對 PDFBox、JDK、平台及 Deflater 探針,不符時明確略過。略過不代表相容性通過,探針亦不證明跨平台 zlib 完全相同;另一環境須以已發布基準與候選同環境重產,不用候選覆蓋 golden。

Section 保留已發布四參數建構器,record component 的反射、toString、equals/hashCode 與模式解構影響仍須依公開契約處理。摘要回填沿用失敗零交付與資源清理契約;lazy 來源只讀一次,狀態有界。

驗證包括全量1655項(9 skipped、零失敗)、Javadoc/文件建置、128MB下10000列/10節/670頁、四頁實際PDF座標,以及本機8份 alpha.24 完整 bytes。版本/壓縮前提不符探針各8項按預期略過。第1輪另有 nbsos 18項及17份已接受PDF bytes比較通過;第2輪僅診斷、測試與文件改動,沿用該下游證據。

收尾解除提交閘門,尚未提交、切版或發布,alpha.24/alpha.25均不含此API。RPT-005 的HTML/PDF、正式資料與產品驗收由下游承接。