Free Pascal 與 Lazarus 支援
HotXLS 支援 Windows Win32 與 Win64 上的 Free Pascal 3.2.2 搭配 Lazarus/LCL,包括 XLS/XLSX 活頁簿 API、公式、格式設定、直接串流、TDataToXLS 與 TGridToXLS 匯出元件,以及轉譯協助程式
原生 Linux 與 macOS 活頁簿核心
範圍限定的目錄 API、明確的原生儲存所有權、時間戳與標準的 Classic 識別碼鍵值,請見複合檔案儲存
選用的 LX_PORTABLE_CORE 設定檔在 Linux 與 macOS 上公開 TXLSWorkbook 與 TXLSXWorkbook 而不需 LCL。原生 Linux x64 與 macOS ARM64 已用 Free Pascal 3.3.1 編譯並執行過;原始碼的版本防護要求至少 3.2.2,但這個最低版本並不聲明每種原生編譯器與目標組合都已驗證
把 cthreads 與 cwstring 放在 HotXLS 單元之前,將 Lib 加入單元與 include 搜尋路徑,並對整個建置定義 LX_PORTABLE_CORE。原生主控台應用程式不需要 Interfaces 或 LCL widgetset
處理 Unicode 檔案系統路徑時,請在應用程式環境中選擇已安裝的 UTF-8 地區設定。無效或非 UTF-8 的地區設定可能讓原生 RTL 的檔名轉換失真;驗證協助程式會明確拒絕這種環境,而不是改變地區設定或猜測字碼頁
program NativeWorkbookExample;
{$mode delphiunicode}
uses
cthreads, cwstring, SysUtils, lxHandleX;
var
Workbook: TXLSXWorkbook;
begin
Workbook:= TXLSXWorkbook.Create;
try
Workbook.Sheets.Add('Data').Cells[1, 1].Value:= 5;
Workbook.Sheets[1].Cells[1, 2].Formula:= 'A1*2';
if Workbook.Recalculate<> 1 then
raise Exception.Create('Workbook calculation failed');
if Workbook.SaveAs('native.xlsx')<> 1 then
raise Exception.Create('Workbook save failed');
finally
Workbook.Free;
end;
end.
| 區域 | 原生核心合約 |
|---|---|
| 活頁簿檔案 | 透過公開的活頁簿 API 建立、開啟、編輯、計算與儲存 Classic BIFF8 XLS、XLSX、受支援的擴充 XLSB 子集與受支援的 ODS 子集,並保留既有的轉換檢查與格式界限。帶有明確宣告 CP1252 與 CP932 編碼的 Classic BIFF2 記錄也已透過匯入與 Unicode BIFF8 匯出驗證 |
| Unicode 與名稱 | 活頁簿文字保留 UTF-16 語意,檔案系統路徑在原生邊界使用 UTF-8。定義名稱身分使用釘選的 Unicode 16 標準正規化與 BMP 完整大小寫摺疊,獨立於主機地區設定地保留符號、變音符號、土耳其語區分與星象平面的原大小寫身分;限定公式保留其確切的工作表範圍。一般工作表文字排序仍使用原生 RTL 地區設定比較 |
| 基礎設施 | 原生 critical section、全寬執行緒識別碼、真實工作者執行緒、已註冊的獨占暫存檔、原子的同層檔案取代與作業系統亂數位元組,都在不做 Windows 模擬的情況下使用 |
| 壓縮與密碼學 | 隨附的 Pascal ZIP/壓縮與 AES 仍可用。Classic RC4 與 RC4 CryptoAPI XLS 檔案可用作業系統亂數位元組原生讀寫。原生加密 OOXML 讀取使用純複合檔讀取器;寫入加密 OOXML 複合檔需要 Windows,在原生 Unix 上引發明確的平台例外 |
| 公式 REGEX 函式 | 釘選的靜態 PCRE2 UTF-16 後端會在原生目標上以其 C 編譯器編譯。編譯包含公式引擎的應用程式前,先執行 sh Lib/thirdparty/build-pcre2-unix.sh;提供 Linux x64 與 macOS ARM64 靜態目標 |
| Windows 服務 | 剪貼簿存取、Windows COM 啟用、內建 ADO/WinHTTP 查詢提供者、GDI 幾何擷取、HTML 背景圖片解碼與舊版 PDF 匯出,會在其明確邊界引發 EXLSPlatformUnsupported。自訂查詢提供者與文字提供者仍可用 |
| 舊版元件 | Classic 活頁簿模型與 BIFF 讀寫器在原生核心可用。LCL 執行階段套件、資料集/網格匯出元件與視覺控制項仍是 Windows/LCL 元件。Classic 剪貼簿方法、HTML 匯出與舊版 PDF 匯出在原生 Unix 上引發明確的平台例外 |
| 位元組導向文字公式 | 需要 Windows 作用中 ANSI/DBCS 字碼頁的函式,在原生 Unix 上回傳明確的不支援公式結果(#NAME?);不會選擇隱含的地區設定或字碼頁替代品 |
開啟與儲存方法保留其一般的結果碼與診斷合約。直接的平台邊界呼叫可能引發 EXLSPlatformUnsupported;從共用應用程式程式碼呼叫僅限 Windows 的服務時,請處理這個例外
可重現的原生驗證
在原生訪客系統上,使用既有的 Free Pascal 工具鏈、Python 3 與原生 C 編譯器執行驗證協助程式。請在訪客檔案系統上選擇原始碼簽出目錄之外、全新或空白的輸出目錄
python3 Tests/Lazarus/run_native_core.py --fpc /path/to/fpc --output /guest-local/hotxls-validation
python3 Tests/Lazarus/run_native_core.py --fpc /path/to/fpc --config /path/to/fpc.cfg --output /guest-local/hotxls-validation-2
該協助程式會複製並雜湊原始輸入,在私有建置副本中為 FPC 區分大小寫的檔名搜尋正規化 Pascal 單元檔名,在本機建置 PCRE2,並執行核心、AES、壓縮、REGEX、公開活頁簿與整合見證。它保留來源清單、編譯器記錄、產出物與失敗記錄,不改動簽出、不安裝工具,也不編輯編譯器設定。 macOS ARM64 建置明確以 macOS 11 或更新版本為目標
整合見證涵蓋 Unicode 路徑、Excel 撰寫的 XLSB 測試檔、公式快取與重算、重複開啟、稀疏 ODS 備註與保護、受檢轉換的拒絕、保留呼叫端目的地的取消、不變的範圍名稱查找、真實的工作者失敗隔離,以及密碼學亂數位元組
原生 Classic XLS 合約
Classic TXLSWorkbook 使用 lxHandle , TXLSXWorkbook 使用 lxHandleX。 Classic 的 Recalculate 回傳錯誤數,零代表成功;其 Calculate 方法回傳公式結果。 Classic 儲存格的 Formula 指派需要前置 =。活頁簿開啟與儲存方法維持既有的成功結果 1
原生 Classic 儲存實作使用真實的複合檔階層與資料流身分,保留巢狀範圍、MiniFAT 資料與延伸 DIFAT 鏈。資料流承載會被具現化,寫入器會在輸出前拒絕超出其受支援帶正負號 32 位元緩衝區/磁區界限的彙總輸出;這不是多 GB 的串流儲存實作
原生複合儲存提供直接的資料流操作、列舉、中繼資料與複製操作。交易回復、區域鎖定、移動與不受支援的排除模式回傳明確的儲存錯誤。它不模擬 Windows COM、剪貼簿或 GDI 服務
Classic 原生檔案儲存會先序列化,再原子取代已註冊的同層檔案。呼叫端資料流儲存會在原位置複製前先暫存完整複合檔,保留前綴並在提交前保留取消失敗。最後的進度通知仍不可取消。直接複合儲存協助程式寫入遵循其一般直接寫入合約,而非公開的活頁簿儲存交易
一般摘要與文件摘要屬性使用有界的標準 OLE 屬性集,含 Unicode 文字與 UTC FILETIME 時間戳。Classic 活頁簿資料加密時,這些屬性資料流維持明文, RC4 CryptoAPI 標頭會明確記錄這個選擇。定義名稱共用 XLSX 門面使用的釘選標準識別碼鍵值,同時保留原始拼寫與明確的工作表範圍。編碼的 PNG/JPEG 繪圖資料仍供模型使用;原生點陣圖/中繼檔轉換需要另一個受支援的轉譯器,否則引發 EXLSPlatformUnsupported
使用相同的原生核心選項編譯 Tests/Lazarus/HotXLSNativeClassicWorkbookSmoke.lpr,然後傳入 Tests/Fixtures/classic-native/native-classic.xls、一個可丟棄的訪客本機輸出路徑,以及 Tests/Fixtures/classic-native/native-classic-encrypted.xls。這些控制涵蓋 Excel 撰寫的快取、重算、精確的 Unicode 名稱、明文與加密往返、獨立解碼的屬性時間戳、重複開啟、超過 16 MB 的 SST 資料與呼叫端輸出保留
舊版 BIFF 位元組編碼
TXLSWorkbook.SetCodePage 選擇 SaveAs(..., xlExcel5) 使用的明確位元組編碼,預設 CP1252;匯入帶有可用非零 CODEPAGE 記錄的 BIFF2–BIFF5 檔案時,也會為後續的舊版儲存選擇該字碼頁
舊版儲存格標籤、公式文字常數與陣列、快取公式字串、定義名稱、工作表名稱與參照、字型名稱、數字格式、樣式名稱、頁首、頁尾與一般註解文字,都使用該宣告字碼頁,而不是作業系統地區設定或 UTF-8
記錄長度與工作表資料流位移以編碼位元組計數;既有的 255 位元組舊版標籤與公式常值限制只在完整字元邊界截斷,因此 CP932 雙位元組字元絕不會被切開;帶一位元組長度的名稱與中繼資料會拒絕超出其可表示長度的編碼文字,而不是輸出無效長度
無法透過所選字碼頁往返的文字會在悄悄變成替代字元或最佳近似字元之前引發 EConvertError;請選擇能表示文件文字的字碼頁,或儲存為 BIFF8 以使用 Unicode 字串
跨越 CONTINUE 記錄的公式快取字串會先以位元組組合再解碼,因此記錄邊界可以切開雙位元組字元而不損毀快取值
變更所選字碼頁會從其 Unicode 模型重建具型別的舊版公式與定義名稱位元組; BIFF8 儲存繼續使用 Unicode,其在磁碟上的 CODEPAGE 值維持 1200
匯入的 BIFF5 圖表記錄為具型別數列與附加標題檢查保留其原始字碼頁,包括活頁簿字碼頁編輯或圖表複製之後;以不同字碼頁儲存時,會明確轉碼受支援的 SeriesText、純快取標籤與公式字串、頁首、頁尾與目前活頁簿的外部工作表名稱,而不是在新宣告下重新解讀保留的位元組
繼續的圖表文字、未建模的舊版外部工作表編碼與不透明的舊版位元組文字記錄會以 EConvertError 拒絕字碼頁遷移;無法表示的受支援文字也會被拒絕,而公開的活頁簿儲存會在提交前保留呼叫端目的地;這份文字合約不承諾完整的原生圖表外觀轉換
BIFF5 自訂樣式名稱緊接在一位元組編碼長度之後開始,而 BIFF5 SeriesText 帶有一個識別碼與一位元組編碼長度且無 Unicode 旗標; BIFF8 SeriesText 增加其 Unicode 旗標,轉換受支援圖表文字時會同時更新該記錄與圖表 BOF 版本
非空 BIFF5 圖表頁首與頁尾使用一位元組編碼長度、上限 255 位元組;快取的 LABEL 與 STRING 記錄使用兩位元組長度,而 BIFF8 頁首與頁尾使用兩位元組 UTF-16 長度加其 Unicode 旗標;遷移會檢查每個記錄的實際版面,並在附加前以 ERangeError 拒絕過大的舊版頁首或頁尾
HotXLS 在 Windows、Linux 與 macOS 上依其宣告字碼頁解讀舊版位元組;已安裝的 Excel 16.0 組建 20430 獨立示範了一個接收端限制:即使原生檔案的 CODEPAGE 記錄單獨改為 CP1252 或 CP932,它仍以其本機 Windows CP936 解讀舊版位元組,因此正確的宣告位元組並不保證該接收端設定下的文字相符
在不同地區設定設定之間散布 BIFF5 時,請保留文件的實際編碼宣告並測試接收應用程式; Unicode BIFF8 可避開這個特定的舊版位元組互通界限
這個位元組編碼修正保留既有的 BIFF5 一般註解記錄大小限制;不會在舊版記錄中新增註解作者、rich-text 格式或圖形幾何欄位
VBA 模組原始碼編輯使用專案宣告的位元組字碼頁,並保留來源位移之前的二進位前綴,包括內嵌的 null 位元組;這會改變已儲存的原始碼文字,不執行巨集
fHighByte = 0 的 BIFF8 壓縮 Unicode 把每個位元組直接對應到 U+0000 到 U+00FF 的 UTF-16 code unit;它不是 CP1252、UTF-8 或檔案的舊版字碼頁,即使在原本 BIFF8 的檔案中出現非標準 CODEPAGE 記錄也一樣
原生文字公式保留 UTF-16 長度與位置,包括代理配對 code unit; Unicode 的 LOWER、UPPER、SEARCH、不分大小寫的 TEXTBEFORE/TEXTAFTER 分隔符號、資料庫欄位標頭、不分大小寫準則與動態文字鍵值,使用原生編譯器的 Unicode 字元資料,而不是主機 C 地區設定的僅 ASCII 大小寫函式,一般工作表排序則保留既有的地區設定比較
原生 LET/LAMBDA 本機名稱比對與本機陣列儲存分類使用相同的 Unicode 感知大小寫,因此大小寫對應會解析到正確的擷取繫結並保留其陣列形狀;這不改變釘選的公開定義名稱身分合約
Classic 不分大小寫的 FindText 與 ReplaceText 在原生 FPC 上對字面與 Excel 萬用字元搜尋保留 Unicode 字元比對; MatchCase 仍區分大小寫,字面取代維持全部取代行為,萬用字元取代保留其既有的最左跨度行為,公式儲存格仍被排除
使用原生核心選項編譯 Tests/Lazarus/HotXLSNativeLegacyEncodingSmoke.lpr,並傳入一個可丟棄的輸出目錄,後面接 Tests/Fixtures/legacy-encoding/native-biff5-cp936-chart.xls;見證會獨立檢查宣告的 CP1252、CP932 與 CP936 位元組、每個工作表位移、字碼頁變更、雙位元組延續邊界、壓縮 BIFF8 匯入、公式計算、原生撰寫的圖表文字,以及失敗儲存的目的地保留
完整安裝程式
完整安裝程式會偵測 Lazarus,並支援在沒有 RAD Studio 的情況下安裝。在 IDE 整合頁面選取 Lazarus / Free Pascal,即可安裝執行階段套件、相容性單元與建置指令碼;當偵測到的 IDE 只有 Lazarus 時,這個選項會預設勾選
在安裝後編譯頁面,為 Win32 或 Win64 選取 Free Pascal 執行階段套件。當某個目標的編譯器、RTL、LCL 與 LazUtils 單元都偵測得到,該目標就可用;目標不可用時仍可安裝套件原始碼,之後再自行編譯
這個套件僅含執行階段,在 Lazarus 中會以專案相依的方式加入。安裝程式不會用 Free Pascal 編譯 VCL 範例
建置與測試
在 Lazarus 中開啟 Lib/FPC/HotXLSLaz.lpk 並編譯執行階段套件,或從 HotXLS 目錄執行這些命令
build-FPC-Lib.cmd Win64
Tests\Lazarus\Run-HotXLSLazarusSmoke.cmd Win64
build-FPC-Lib.cmd Win32
Tests\Lazarus\Run-HotXLSLazarusSmoke.cmd Win32
設定 LAZARUS_DIR 以選取安裝;必要時,FPC_EXE 與 LAZBUILD_EXE 可分別指定明確的編譯器與套件建置可執行檔
所選安裝必須包含目標架構的 FPC 與已編譯的 LCL/LazUtils 單元;輸出與套件建置設定會保存在個別的架構目錄中
應用程式設定
將 Lib 與 Lib/FPC 加入單元搜尋路徑,並將 Lib 加入 include 搜尋路徑,或將執行階段套件加入為 Lazarus 專案的相依項
在應用程式的 uses 清單中,將 Interfaces 放在 HotXLS 單元之前,主控台應用程式也不例外;這會初始化 LCL widgetset 以及 RTL/LCL 邊界上的 UTF-8 轉換
program WorkbookExample;
{$mode delphiunicode}
uses
Interfaces, SysUtils, lxHandleX;
var
Workbook: TXLSXWorkbook;
begin
Workbook:= TXLSXWorkbook.Create;
try
Workbook.AddSheet('Data');
Workbook.Sheets[1].Cells[1, 1].Value:= 'Hello';
if Workbook.SaveAs('example.xlsx')<> 1 then
raise Exception.Create('Workbook save failed');
finally
Workbook.Free;
end;
end.
HotXLS 原始碼單元在內部選用 Delphi Unicode 語意,因此字串、字元與公式文字保留 UTF-16 行為;應用程式程式碼可以使用自己偏好的 Pascal 模式
相容性細節
- 執行階段套件需要 Windows 與 LCL;
TDataToXLS匯出 LCL 資料集,TGridToXLS匯出TDBGrid,不論有無明確欄位,而 Linux、macOS、VCL 示範表單、VCL 活頁簿檢視器與 DevExpress Grid 配接器則不在這個套件的範圍內 - ZIP 與壓縮串流使用內建的 Pascal 後端,不需要另外的 zlib DLL
- AES 在兩種架構下都使用 Pascal 後端,包括受密碼保護的 XLSX 檔案
- PNG 保留 Alpha 色板,EMF 使用 Windows 中繼檔錄製與播放,多頁 TIFF 則使用 Windows GDI+ 執行階段
- 直接文字讀取接受 UTF-8 與帶 BOM 的 UTF-16/UTF-32,可處理串流輸入,並將串流擁有權保留給呼叫端
- 在修改個別欄位之前,先以
TXlsCsvImportOptions.Default初始化TXlsCsvImportOptions - FPC 報表運算式和匯入中用到的模式採用 Unicode
URegExpr語法,而非 Delphi 的 PCRE 後端。空輸入的處理方式與TRegEx相同:接受空字串的模式(例如^$或.*)會符合空字串,而[0-9]+不會;取代空字串時,只有當模式接受空字串才會產出取代文字;不支援的模式與空模式會擲出ERegularExpressionError,報表運算式會將其呈現為REPORT_EXPRESSION_INVALID_REGEX