SVG 轉換成 PDF 的問題

最近在下使用 SVG 繪製向量圖檔並需要轉換成 PDF 時,發現轉換後的 PDF 出現了預期之外的品質瑕疵。
在下嘗試尋找一些方法來正確將 SVG 轉換成 PDF。

製作圖案

<svg version="1.1" viewBox="0,0 100,100" xmlns="https://www.w3.org/2000/svg">
	<defs>
		<mask id="mask">
			<rect width="100" height="100" fill="#FFFFFF"/>
			<circle r="20" cx="50" cy="50" fill="#000000"/>
		</mask>
	</defs>
	<rect x="10" y="10" width="80" height="80" fill="#FF0000" mask="url(#mask)"/>
</svg>

在製作 SVG檔案 時,偶爾需要製作 挖空效果 。例如:一個紅色方形,並在中央 挖空一個圓形。
由於在下習慣直接用純文字來繪製 SVG檔案,最直覺、也最容易理解的做法就是使用 遮罩 (Mask)
這種做法不僅結構清晰,將來想要調整圓形的大小或位置時,也極為方便:

使用 Inkscape 指令轉換

Inkscape 雖然主要是圖形介面軟體,但也提供了強大的指令操作模式,非常適合用來做自動化批次轉換。
在終端機中,在下輸入以下指令將 SVG 轉為 PDF:

inkscape "input.svg" --actions="export-type:pdf; export-filename:output.pdf; export-do;"
  • input.svg 來源檔名。
  • --actions 執行的操作清單。指令間用 ;(分號)隔開,參數則用 :(冒號)指定。
  • export-type:pdf 指定輸出格式為 PDF。
  • export-filename:output.pdf 指定輸出檔名。
  • export-do 正式開始執行上述動作。

轉換結果

雖然輸出 PDF ,但放大後發現中央被挖空的圓形,邊緣鋸齒嚴重、解析度模糊。
理論上 PDF 支援向量圖形能顯示順滑的曲線,但卻失效了。

使用 Inkscape 圖像介面轉換

在下懷疑是指令模式的限制,於是使用 Inkscape 圖形介面模式來 另存新檔為 PDF 。

情況完全相同。這證實了不是指令寫錯,而是 渲染 有別的原因。

使用 列印成PDF 轉換

現代 網頁瀏覽器 對 SVG檔案 的支援度極高。
在下嘗試用 網頁瀏覽器 打開該 SVG檔案 ,並使用內建的 列印為PDF 功能。

PDF 看似正確,圓形邊緣很順滑。

然而將 PDF 放大到一定倍數後,模糊鋸齒再度現形,依然沒有保持真正的向量格式。

使用 Inkscape 圖像介面圖案相減方法

在下懷疑 遮罩 在轉換時會出錯,在下改用圖案 相減 的方式,直接將圓形從方形中剪掉。

在 Inkscape 中用同時選兩個圖案。
選單 路徑 (Path) > 減去 (Difference) ,或使用快捷鍵 Ctrl -

相減後,圖案 會被強制轉換成的 路徑 (Path)
(圖案一旦變成路徑,選取的圖案便無法原還。)

相減時有機會導致顏色重置,需要手動重新填色。

再將 SVG 轉換成 PDF,圓形邊緣終於變得絕對順滑、保持向量。

使用 Inkscape 指令操作圖案相減方法

如果有很多 SVG檔案,便不太可能逐個處理,因此在下使用 Inkscape 指令來完成圖案相減並導出。

使用 Inkscape 指令除了節省圖像渲染時的資源,還可以自動化批量處理及使用純文字方式執行,在 headless系統 同樣適合。

inkscape "input.svg" --actions="select-all; path-difference; export-type:svg; export-filename:output.svg; export-do;"
  • select-all 選取 SVG檔案 中的 所有圖案
  • path-difference 執行 路徑相減 操作。

不過,使用指令將 圖案相減 時, SVG檔案 內必須剛好 只有2個圖案
如果 超過2個圖案,指令會 拒絕執行 或 產生錯誤 的結果。
如果 SVG檔案 結構複雜,必須先將需要相減的 2個圖案 抽取成獨立的 SVG檔案,或是為 這2個圖案 指定 唯一id 來精準選取。

如果使用 id 來選擇圖案,請改用:

inkscape "input.svg" --actions="select-by-id:shape-0,shape-1; path-difference; export-type:svg; export-filename:output.svg; export-do;"

補充資料

SVG 最佳化

使用 Inkscape 製作 或儲存 SVG檔案 時, Inkscape 會自動加入很多 專有的標籤與中介資料
雖然不影響畫面顯示,但會讓程式碼變得臃腫、可讀性變差。

在下使用 Scour 這個開源工具來讓 SVG檔案 最佳化,在 終端機 輪入:

sudo apt install scour

按此下載並安裝 Scour

安裝後,輸入:

scour -i "input.svg" -o "output.svg"
  • input.svg 是 SVG檔案 來源位置
  • output.svg 是 SVG檔案 輸出位置

最佳化的 SVG 會刪除所有不必要的 冗餘標籤 ,並精簡路徑中的空白,讓內容變得簡潔。

輸出的 SVG檔案 會省卻不必要的標籤,路徑圖案的內容亦刪減不必要的空格(可能影響可讀性)。

自訂列印為 PDF 的尺寸

工作環境的電腦未必有安裝 Inkscape ,使用 網頁瀏覽器 將 SVG 轉換成 PDF 是方便的方法。
但 網頁瀏覽器 通常會只讓使用者選擇 標準紙張的列印尺寸 ,無法以 SVG檔案 原本設計的尺寸 轉換,
因此需要修改 SVG檔案 來達到效果。

<style>
@media print {
	@page {
		size: 100px 100px;
	}
}
</style>

SVG檔案 中加入這段 CSS樣式 , 就能解決 列印為PDF 問題,
在 預覽列印 時,便會自動使用 SVG檔案 的列印尺寸,不需要選擇列印尺寸。

同樣可以做到 SVG 轉換成 PDF 。

總結

在下翻閱了一些技術文件後,發現 PDF 格式對於 SVG 的 遮罩技術的相容性極差

當 Inkscape 或 網頁瀏覽器的 PDF虛擬印表機 在轉換時,為了確保圖案與 SVG檔案 一致, PDF 無法完美解析 遮罩 的邏輯,
就會採取妥協操作,將受到 遮罩 影響的區域 點陣圖化 (Rasterization)

因此如果 SVG檔案 需要轉換成 PDF ,在設計時,盡量避免使用 遮罩 與 裁剪路徑 (Clip Path)
需要改用 路徑布林運算 (Path Boolean Operations) ,能確保 轉換成 PDF 的圖案是向量圖。

參考資料