若要在未安裝 Office 的情況下從 Delphi 或 C++Builder 將 Excel 工作表匯出為 PDF,請在 HotXLS 工作表或活頁簿上呼叫 SaveAsPDF。HotXLS 是原生的 VCL Excel 元件,其工作表轉 PDF 匯出功能會將工作表已使用的範圍(UsedRange)轉譯為獨立的 PDF 1.4 檔案,將 Excel 欄寬和儲存格顯示文字對應到 A4 頁面,並進行自動多頁分頁。整個轉譯過程不需要安裝 Excel、不需要印表機驅動程式,也不需要外部 DLL
HotXLS 將 PDF 視為一種資料呈現格式,是其 CSV、TSV 和 HTML 匯出路徑的同類功能,而非像素級精確的轉譯引擎。該匯出功能會重現表格內容和版面配置:您在網格中看到的數值、欄比例以及儲存格周圍的格線。這種定位非常重要,因為它在預先建立了一個合理的預期,而本文的其餘部分將具體介紹該功能的界限所在
如何在沒有 Office 的情況下在 Delphi 中將 Excel 轉換為 PDF?
最簡短的路徑是單次呼叫。TXLSWorksheet 公開了 SaveAsPDF(FileName) 和 SaveAsPDF(Stream),且 TXLSWorkbook 公開了相同的兩個多載(overloads),它們會將要求轉發給作用中的工作表。每個方法都傳回一個 Integer(成功為 1,失敗為 -1),因此您可以根據結果進行分支,而無需將呼叫包裝在您自己的例外處理中
uses
lxHandle;
var
Book: TXLSWorkbook;
begin
Book := TXLSWorkbook.Create;
try
Book.Open('sales-june.xlsx');
// 將作用中工作表已使用的範圍直接轉譯為 report.pdf。
if Book.SaveAsPDF('report.pdf') = 1 then
Writeln('PDF written');
finally
Book.Free;
end;
end;
當您在工作表上呼叫 SaveAsPDF時,HotXLS 會匯出該工作表的 UsedRange(即每個已填入資料之儲存格的邊界方框)。活頁簿的多載是一個便捷方式,它會委派給作用中的工作表,因此多工作表報表要麼對其需要的工作表進行迴圈,要麼在呼叫前選取作用中的工作表。這裡沒有將每個索引標籤串接在一起的「整個活頁簿一次完成」模式:每次呼叫僅轉譯一個工作表或一個範圍
HotXLS 如何將已使用的範圍轉換為 PDF 頁面?
HotXLS 逐個儲存格走訪已使用的範圍,並將其編排為分頁網格。欄的幾何形狀直接來自 Excel:每個欄寬都從 Excel 寬度單位轉換為印刷點(points),並設有最小下限,以防欄寬縮小成細條。當所有欄의 總寬度大於可用頁面時,HotXLS 會等比例縮小它們以符合頁面;當它們已經符合時,則保持其自然寬度,而不是將它們拉伸填滿工作表
接著,列會沿著頁面向下排版,直到下一列會超出底部邊界,此時 HotXLS 會開啟新頁面並繼續排版,因此包含上千列的工作表會自動進行分頁,而無需您手動記錄分頁位置。每個儲存格都會列印其 FormattedText,即套用數字格式後產生的顯示字串,因此日期會顯示為工作表上呈現的日期,且貨幣儲存格會保留其千分位分隔符號和符號。超出其欄寬的文字會被裁剪為儲存格寬度,而不是溢出到鄰近的儲存格中
控制方向、邊界和字型大小
對於超出預設版面配置的任何需求,請直接驅動匯出器。TXLSPDFExport 是單行協助工具背後的類別,它發布了三個屬性:Orientation(xlsPdfPortrait 或 xlsPdfLandscape,預設為直向)、Margin(印刷點,預設為 40)和 FontSize(印刷點,預設為 9)。在呼叫 SaveAsPDF 之前設定它們,整個文件就會套用這些設定
uses
lxHandle, lxPDF;
var
Book: TXLSWorkbook;
Exporter: TXLSPDFExport;
begin
Book := TXLSWorkbook.Create;
try
Book.Open('wide-ledger.xlsx');
Exporter := TXLSPDFExport.Create;
try
Exporter.Orientation := xlsPdfLandscape; // 在頁面上容納寬工作表
Exporter.Margin := 28; // 更緊湊的頁面邊界,以點為單位
Exporter.FontSize := 8; // 更緊密的列
Exporter.SaveAsPDF(Book.ActiveSheet, 'ledger.pdf');
finally
Exporter.Free;
end;
finally
Book.Free;
end;
end;
TXLSPDFExport 還可以透過 SaveAsPDF(Range: IXLSRange; ...) 接收範圍而非整個工作表,這正是您從同時包含草稿計算的工作表中匯出摘要區塊的管道。該範圍多載與工作表協助工具在內部依賴的多載相同,因此不論您傳遞的是完整的已使用範圍還是手動選取的範圍,其版面配置、縮放和分頁行為都完全一致
var
Book: TXLSWorkbook;
Exporter: TXLSPDFExport;
Summary: IXLSRange;
begin
Book := TXLSWorkbook.Create;
try
Book.Open('quarter.xlsx');
// 僅匯出摘要區塊,不匯出其旁邊的草稿欄。
Summary := Book.Sheets[1].Range['A1', 'F20'];
Exporter := TXLSPDFExport.Create;
try
Exporter.SaveAsPDF(Summary, 'summary.pdf');
finally
Exporter.Free;
end;
finally
Book.Free;
end;
end;
為什麼 HotXLS 不嵌入字型:Helvetica 和 WinAnsi 的折衷
HotXLS 使用 PDF 內建的 Helvetica 和 WinAnsiEncoding 寫入文字,這是一個刻意採用的零相依性選擇,但也包含一個實際的限制。因為 Helvetica 是每個符合規範的 PDF 閱讀器都已附帶的 14 種標準字型之一,所以匯出器完全不需要嵌入任何字型檔案:檔案保持很小且可以在任何地方開啟,無需授權或隨附任何額外組件。該選擇的代價是頁面所能顯示的字元集範圍
WinAnsiEncoding 涵蓋了 Windows-1252 字元集,因此拉丁文字、常用標點符號和一般的西歐重音符號都能正確轉譯,且 HotXLS 將少數排版字元、雙引號、半型和全型破折號以及歐元符號對應至其 CP1252 位置。該範圍之外的字元(中日韓漢字、西里爾字母、希臘字母和阿拉伯文字)在頁面上會退化為問號。如果您的工作表包含非拉丁文字,此匯出器將不是合適的工具,您需要採用支援完整樣式的 PDF 轉譯器
工作表轉 PDF 匯出的限制
HotXLS 轉譯內容和結構,而非工作表的完整視覺樣式。儲存格填滿、每邊的邊框樣式、每格的字型與色彩、圖表和嵌入的影像都不在匯出器的繪製範圍內;您獲得的是帶有統一格線的文字網格。這與 HTML 和 RTF 匯出器所遵循的約定相同,且對於數字承載主要意義而非主題風格的報表或資料交接來說,這是最合適的管道
當目標是產生符合樣式、品牌一致的文件時,通常的模式是先在試算表範本中組合數字,這在範本驅動的報表產生工作流程中進行了深入探討。且開且這一切都不涉及 COM 或執行中的 Excel 執行個體,因此該管線符合 HotXLS 全文採用的同一個無需 Office 的自動化模型:純 Pascal 程式碼、可部署在無介面伺服器上,無需授權或維護任何 Interop 組件
獨立的 PDF,位元組級精確
HotXLS 手動建置 PDF 檔案結構,並根據精確的位元組位移量(offsets)計算其交叉參照表,這就是為什麼輸出檔案能在每個閱讀器中開啟的原因。在序列化每個物件時,HotXLS 會在附加其位元組之前記錄該物件的起始位移量,然後將目前位置前進其剛寫入的長度,使 xref 表精確定位在每個物件上,並由 startxref 標記該表的起始點。閱讀器只要發現這些位移量偏差一個位元組,就會拒绝整個檔案,這使得自行計算的位移量成為匯出器中承載的合約
該合約帶來了一個值得瞭解的嚴格後果。Pages 節點會先被寫入為物件 1,但在輸出每個頁面之前是無法得知其子頁面清單的,因此 HotXLS 將其保留為預留位置,並在執行位移量掃描之前(絕非之後)修補入最終的子頁面和數量。一旦測量了用於 xref 的物件位元組,它們就會被凍結:此後變更單個位元組都會使每個後續位移量發生偏移並損毀表格。這是一個小規範,但它換來了符合標準的有效 PDF 1.4,且其下不需要任何函式庫的支援
此處介紹的 SaveAsPDF 方法和 TXLSPDFExport 類別隨附於 HotXLS Delphi Excel 元件 中,同時隨附的還有 CSV、HTML 和 RTF 匯出器,以及適用於 Delphi 和 C++Builder 的完整工作表 API