我有寫了一個 Word 套表服務,最早是用 C# 呼叫 Word 執行置換及轉 PDF,後來改走 OpenXML SDK,罝換速度快了五倍以上,唯獨轉 PDF 這段還只能仰賴 Word 完成。從 ASP.NET 呼叫 Word Application 會受限執行身分權限過低,Word 程序的生命周期亦較難掌控,最後我決定寫成 Windows Service,以特定登入帳號啟動固定數量的 Word 程序,以 Web API 方式接收並平均消化套表需求。做法可行且運行了一陣子,但有以下缺點:

1.    套表服務為獨立 Windows Service,當網頁執行套表出錯,追查範圍變大,追查路徑較複雜
2.    必須在伺服器安裝 Word,且為配合背景執行有額外設定步驟
3.    Word 為前景程式,以 Windows Service 長期執行時偶爾會出鎚,需人工介入排除

因此,我一直惦記著想找到不需 Word 也能將 DOCX 轉 PDF 的方法,攻下「非 Word 不可」的最後一里路,把整個套表轉 PDF 作業都搬進 ASP.NET 簡化系統架構。

DOCX 轉 PDF 有不少商業元件選項,但一致特色是大多價格不斐(數百至數千美金不等),且是否能精確轉換亦需驗證。所以我決定先從免費開源解決方案看起,LibreOffice 是最佳選擇,它延續 OpenOffice 的使命,提供跨平台的
免費開放文書軟體,社群很活躍持續有更新,對 MS Office 格式支援也夠完整。

LibreOffice 有安裝程式,但實測 Copy 檔案也能部署,使用命令列 soffice  --headless --convert-to pdf filename.docx 即可將 .docx 轉 .pdf,另外驗證可在 ASP.NET 直接呼叫執行沒問題。(Github 有相關元件可以省工) 餘下的問題只剩相容性 – LibreOffice 讀取 DOCX 轉 PDF 是否能保有 Word 編譯格式,決定其可接受度。

為此我做了一次批次測試,請同事提供五百多個線上實際使用的套表範本,以 ASP.NET 程式呼叫 LibreOffice 批次轉成 PDF。除了一個檔案有問題需要手工前置轉檔(先存 doc 用 LibreOffice 開啟另存 docx,很無厘頭但有效),其餘檔案都成功轉成 PDF,每次耗時在 3 秒內,速度很 OK。

而最關鍵的問題 – 轉出的PDF排版有跑掉嗎?有壞消息也有好消息。

壞消息是大約有六至七成出現程度不一的排版差異,有些只影響美觀,有些影響閱讀必須調整。
好消息是所有排版差異都有解法,使用者必須改變習慣或重新修校,但沒有遇到無解項目。

整理遇到的問題典型如下:

  1. 簽章圖檔只有部分顯示
    原因:圖檔被貼在表格儲存格裡。
    解法:剪下改貼至文件本體,文繞圖改為「文字在後」即可
  2. 表格框線消失、變粗
    原因:多為 Word 重複設定框線造成(上欄設了下框線,下欄位又設上框線),在 Word 合併只顯示一條,但 LibreOffice 兩條都顯示會變成粗黑線。
    解法:重新調整框線可修正
  3. 表格行高不足,文字下半截消失 
    原因:在 Word 指定不足以放入字型大小的固定列高,Word 會自動撐大但 LibreOffice 不會
    解法:修改為合理列高
  4. 段落下半部被遮蔽
    原因:段落設定了過小的固定行高,Word 會強迫撐大但 LibreOffice 不會
    解法:調為單行間距
  5. 簽章圖檔變形(雙倍寬度且只顯示左半邊)
    原因不明,將圖檔另存GIF重新插入後恢復正常
  6. 頁首圖檔錯位
    原因是插入的圖檔未經修剪,幾與紙張同寬,在 Word 裡部分已超出頁面範圍。而 LibreOffice 不允許圖檔超出範圍,導致圖案左移,裁剪圖檔至適當尺寸可解決。
  7. 頁首、頁碼跑位
    原因:使用未使用 Word 頁首功能,是自己算位置放標題、頁碼,LibreOffice 排版結果與 Word 有出入位置就錯了
    解法:回歸使用頁首、頁尾正統做法
  8. 表格浮至上方並覆蓋文字
    原因為 Word 排版抓太緊,LibreOffice 與 Word 顯示位置有誤差,需加入緩衝區
  9. docx 無法使用 LibreOffice 開啟
    五百多份文件僅有一例,原因不明。
    Workaround 為 Word 先改存 doc,交給 LibreOffice 開啟再另存回 docx,過水後問題排除。

【結論】

使用 LibreOffice 取代 Word 轉 PDF 作業,直接在 ASP.NET 執行是可行的。但改用第三方轉換軟體排版結果難免有所出入(實測差異性比原本想像來得大),需使用者配合在上傳範本前自行調整 Word 文件確認輸出 PDF 正常。最簡單的做法是請使用者安裝 LibreOffice,上傳前自行檢查試轉 PDF 目視確認,另外還需提供 FAQ 引導遇到輸出不一致時如何調整。與現行使用者可無腦使用 Word 任意修改文件都不用擔心出錯不便,將影響使用者接受度。至於二者輸出結果差異有些屬於對文件規格實作的見解不同,無關對錯,嘗試其他文書轉換元件應也會面臨相似問題,只差在嚴重程度有別。若要求零誤差,使用 Word 才能保證萬無一失。


Comments

# by Lik

想問問黑大有沒有asp.net 配合excel以及用excel 轉pdf的經驗?

# by Jeffrey

to Lik, LibreOffice 也可 Excel 轉 PDF,讀取及寫入 Excel 我慣用 ClosedXML http://blog.darkthread.net/post-2012-12-28-closedxml.aspx

# by Lik

坦白講,正在讀你的“令人驚豔的Excel程式庫 - ClosedXML” http://blog.darkthread.net/post-2012-12-28-closedxml.aspx 希望能用上在asp.net mvc上。

# by Ted

黑大您好,近期在使用word元件將xml轉成doc時,是沒有問題的,但是再轉成odt後,使用open office來開啟目標卻出現了"非預期的錯誤"而導致文件無法開啟的問題,想請問除了使用word元件之外還有其他比較好的方法可以將xml格式轉成odt格式嗎?

# by Jeffrey

to Ted, 我不是很懂用「Word元件將XML轉DOC」的做法,XML是指某種包含文件內容與樣式的特殊規格文件嗎?

# by 11

LibreOffice匯出所有設定 LibreOffice的所有設定,都放在LibreOffice資料夾裡。 C:\Users←使用者。\a←安裝作業系統的時候,輸入的名稱。\AppData←檔案總管〜『隱藏的項目』要開啟,才會顯示AppData資料夾。\Roaming\LibreOffice 備份LibreOffice資料夾,重新安裝的時候,再把LibreOffice資料夾,放到原來的地方。 註: 1、如果安裝的是預覽版,因為預覽版的名稱是LibreOfficeDev,所以會顯示LibreOfficeDev資料夾。 2、正式版跟預覽版可以一起安裝,如果正式版跟預覽版都安裝的話,就會顯示LibreOffice資料夾跟LibreOfficeDev資料夾。 3、要清除所有設定,就把LibreOffice資料夾刪掉,在開啟程式,就會在建立新的LibreOffice資料夾。 LibreOffice匯出自己做的單一工具列 Toolbar=工具列 共同路徑 C:\Users←使用者。\a←安裝作業系統的時候,輸入的名稱。\AppData←檔案總管〜『隱藏的項目』要開啟,才會顯示AppData資料夾。\Roaming\LibreOffice\4\user\config\soffice.cfg\modules\下面請接個別軟體的分支路徑。 分支路徑 \modules\StartModule\toolbar\自己做的『開始畫面』工具列,都放在這裡。 \modules\swriter\toolbar\自己做的『writer文件』工具列,都放在這裡。 \modules\scalc\toolbar\自己做的『calc試算表』工具列,都放在這裡。 \modules\simpress\toolbar\自己做的『impress簡報』工具列,都放在這裡。 \modules\sdraw\toolbar\自己做的『draw繪圖』工具列,都放在這裡。 \modules\smath\toolbar\自己做的『math公式』工具列,都放在這裡。 \modules\dbapp\toolbar\自己做的『base資料庫』工具列,都放在這裡。 備份檔案,重新安裝的時候,再把檔案,放到原來的地方。 註: 1、因為自己做的工具列,預設的檔名,都是用編號,所以要打開檔案,才能知道工具列的名稱。 2、前面的檔名『custom_toolbar_』不可以改,改了會出現錯誤,後面的檔名可以改。 例如:custom_toolbar_c01611ed.xml→custom_toolbar_我我我.xml。 3、做好的工具列,可以複製到別的地方用。 例如:在『writer文件』做好的工具列,可以複製到『calc試算表』的地方用。 LibreOffice自製標點符號工具列 第一步 啟動『錄製巨集功能』 工具\選項\進階\啟用巨集錄製功能(打勾),這樣在『工具\巨集』裡,才會出現『記錄巨集』的選項。 第二步 錄製巨集 工具\巨集\記錄巨集→錄製動作(點『Ω』輸入符號→選擇符號→插入)→停止記錄→巨集儲存在『Module1』裡的名稱是Main→修改Main名稱→儲存。 第三步 加入新工具列 工具\自訂\工具列→『新增』→『輸入名稱』(例如:標點符號)→確定,新的工具列,就會在左上方出現。 第四步 將巨集加入新工具列 工具\自訂\工具列\類別\巨集\我的巨集\Standard\Module1\Main→點『Main』→加入→修改→重新命名(可以用標點符號命名)→確定→確定。

# by 鄭竣丰

謝謝分享

# by 毛豆

解決問題的路上,有參考大大本文,在此回饋我的經驗 https://www.facebook.com/dplayerd/posts/4240768175946506

# by Jeffrey

to 毛豆,感謝分享。(但我後來放棄 LibreOffice 這條路了,主要是因為我應用在套表,輸出結果與 Word 的微小出入常讓使用者跳腳 orz)

# by 小黑迷

請問黑大目前Excel轉PDF的成熟方案,讀您的文章感覺還沒有完美解法?

# by Jeffrey

to 小黑迷,轉 PDF 我目前是寫成 Windows Service 呼叫 Word 處理,運轉多年下來還算行。若要 Excel 轉 PDF,應該也會比照辦理。

# by 小黑迷

這比較尷尬,asp.net core會部署到windows跟linux服務器,這樣就只能限定在 windows 了... 謝謝

# by Jimmy

Excel轉PDF我之前有用過GemBox.Spreadsheet,算簡單易用,不過免費版有限制150行內與5個sheets內

# by 小黑迷

To Jimmy 感謝!

# by Anonymous

正在踩這個坑! ClosedXML 設定前五列列印置頂失敗 目前來啃 LibreOffice 的 SDK 話說,用 LibreOffice 轉 PDF 只要 50 毫秒耶 很扯得快

Post a comment