試過純手工打造 ePub 電子書,寫過程式半自動轉換。前陣子試了另一種作法 - 用現成工具軟體 Pandoc 將圖文 HTML 轉成 ePub 電子書。

Pandoc 是個功能強大的文件轉換器(使用方法可參考保哥的文章:介紹好用工具:Pandoc ( 萬用的文件轉換器 )),它支援多種文件格式間的轉換,包含 HTML 轉 ePub,而且我驚喜地發現,Pandoc 能自動識別 h1, h2, h3... 等標題製作目錄,遇到 <img src='本地圖檔' > 還能自動打包圖檔改連結,過去我靠寫程式做完的工作它都包辦了,用來把 HTML 轉成電子書超方便。

我這次要轉換的對象是 Pieter P 寫的網頁電子書 - A Beginner's Guide to the ESP8266,原本就寫成電子書形式,有章節概念,且開源放在 Github,故我只需 Fork 便能取得原始檔加工轉 ePub:

成品如下:

說說這次轉換發現的眉角:

  1. 我試出來用 Pandoc HTML 轉 ePub 參數範本如下:
    pando -s -f html -t epub3 --epub-cover-image=cover.png -c ..\CSS\css-for-epub.css -o path-to-epub-file.epub metadata.html chap1.html chap2.html ...
    
  2. 最直覺的做法是每章自成一個 HTML 檔,作者原來也是這麼做。但起初 Pandoc 將所有 HTML 併成一個 ch001.xhtml (解壓 ePub 觀察到的),研究發現是因為作者把章標題放在 <h2>,<h1> 放書名,而 Pandoc 的分檔是以 <h1> 區隔;故我寫了一段 PowerShell 將 h1 移除,h2 改 h1。h3 改 h2,h4 改 h3,修改後 ePub 也修正為一章一個 .html。但有個已知問題,Pandoc 產生的目錄連結在 Google 電子書閱讀器套件或 Calibre 閱讀時功能正常,點各層章節都能跳到對映位置,但在我的 Kobo Forma 閱讀器上只能跳到章 (.html 層),無法跳到節 (.html#section-id)。
  3. Pandoc 打包檔案時會移去 HTML 原本的 header,導致原有 CSS 樣式失效,解決方式指定改成電子書專用版本的 CSS,用 -c path-to-style-for-epub.css 參數套用。
  4. 原本每個 HTML 都內含 <meta name="author" content="Pieter P" >,會被 Pandoc 解析成這本書有 19 位同名同姓的作者(總共 19 個 HTML 檔),一樣,招喚 PowerShell 出面將 meta 移除避免困擾。
  5. 作者、書名、出版商... 等資訊可用 YAML、JSON 方式配合 --metadata-file 參數提供,但我試不出來。替代方案是多放一個 metadata.html,在其中用 <meta name="author" content="Pieter P" > 格式指定書籍相關資訊。這些資訊會顯示於 title_page.xhtml,放在電子書第一頁。
  6. Pandoc 解析圖檔位置時,..\images\picture.png 的 ..\ 會相對於執行程式當下路徑,而非 HTML 所在路徑,故建議在 HTML 所在資料夾執行 pandoc。
  7. 網頁版有上一章下一章連結,ePub 用不到,最早我用 display: none 隱藏,Calibre OK 對 Kobo 閱讀器無效,索性換置成空白才好。
  8. 我試著修改 CSS 讓 TABLE 跟 PRE/CODE 的字型變小,避免字型過大文字被截,但一樣,用 Calibre 看 OK,Kobo 閱讀器看到的還是大字,這部分我還沒研究出解法。

我用來打包的 PowerShell 長這樣:

$ErrorActionPreference="STOP"
del Chap*.html
$sb = New-Object System.Text.StringBuilder
$sb.Append('-s -f html -t epub3 --epub-cover-image=cover.png -c ..\CSS\epub-main.css -o ..\esp8266-beginner-guide.epub metadata.html') | Out-Null
Get-ChildItem -Path "..\Full HTML" -Filter *.html | ForEach-Object {
    $content = Get-Content -Raw $_.FullName -Encoding utf8
    $content = [System.Text.RegularExpressions.Regex]::Replace($content, "<meta name=`"author`" content=`".+?`">", "")
    $content = [System.Text.RegularExpressions.Regex]::Replace($content, "(?ms)<h1[^>]*?>.+?</h1>", "")
    $content = [System.Text.RegularExpressions.Regex]::Replace($content, "(?ms)<h2[^>]*?>(.+?)</h2>", "<h1>`$1</h1>")
    $content = [System.Text.RegularExpressions.Regex]::Replace($content, "(?ms)<h3[^>]*?>(.+?)</h3>", "<h2>`$1</h2>")
    $content = [System.Text.RegularExpressions.Regex]::Replace($content, "(?ms)<h4[^>]*?>(.+?)</h4>", "<h3>`$1</h3>")
    $content = [System.Text.RegularExpressions.Regex]::Replace($content, "<div class=`"(back|next|backArr|nextArr)`".+?</div>", "")
    $content | Out-File $_.Name -Encoding utf8
    $sb.Append(" `"$($_.Name)`" ") | Out-Null
}
$cmd = 'pandoc ' + $sb.ToString()
$cmd
Invoke-Expression $cmd

如果你想自己玩看看,我放了可執行版本在 Github,歡迎 Clone 回去玩。(請確認已裝好 Pandoc,執行 /ESP8266/A Beginner's Guide to the ESP8266 - article/EPub HTML/make-epub.ps1)

Tips of converting HTML with images to ePub book with Pandoc.


Comments

Be the first to post a comment

Post a comment