【寫在前面】ePub 電子書對大多數人來說只要開閱讀器能讀就好,即便要製作 ePub 電子書,現成軟體、工具、服務或線上轉換器多不勝數,極少人需要去了解技術細節。如果你對 ePub 結構感到興趣,或希望讀得不開心時有能力自己修正調整,或是想寫程式批次產生或轉換,有述需求再繼續讀下去。

去年入手電子書閱讀器,看了不少電子書。電子紙 (e-Ink) 的閱讀體驗很接近印刷紙張,比手機或平板螢幕柔和很多,長時間閱讀眼睛也較不會疲倦。但有時在網路上看到一些長篇技術文件,常會想:如果能抓回來轉成 ePub,再找時間用電子紙看多好?加上好奇心驅使,讓我有自己寫 C# 程式製作 ePub 的念頭。

一切要先從了解 ePub 格式開始

ePub 其實是一個 ZIP 檔,裡面裝了 XHTML、圖檔、css,說穿了跟 Word docx 的概念差不多。(提到 XHTML,寫網頁的老人肯定很有感,這位被 HTML5 推翻的舊時代餘毒竟轉進 ePub 撐起一片天)

用 XHTML 寫好書本內容後,要轉成 ePub 電子書還需要一些額外配件。就用實例來練習吧! 假設我想寫一本「ePub 手作練習」ePub 電子書,內容分成兩章分別存成 format.html 及 diy.html。要打包的檔案結構如下圖,根目錄下有 mimetype 跟 META-INF 及 OEBPS 子資料夾,META-INFO 下只有一個 container.xml,其餘內容都放在 OEBPS 下:(參考:EPUB 2 規格 by 周邦信 筆記本)

以下簡單說明各檔案用途:

  1. mimetype 檔案(無附檔名),內容必須為 application/epub+zip 不多不少共 20 個字元,而且要是 ZIP 的第一個檔案。
  2. container.xml 放在 META-INF 子目錄下,指向 content.opf,供閱讀器取得這本電子書相關資訊:
    <?xml version="1.0"?>
    <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">
      <rootfiles>
        <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/>
      </rootfiles>
    </container>    
    
    書籍內容檔案建議放在名為 OEBPS (Open eBook Publication Structure) 的目錄下,但此點非強制性,要放在 ZIP 根目錄或任意名稱的資料夾也可以。
  3. content.opf 定義了電子書資訊與內容元素。 metadata 可包含書籍作者、出版日期、分類等圖書資訊;manifest 要列舉 ePub 檔內的檔案項目,項目 id 屬性用於與 metadata 及 spine 連結;spine 則定義閱讀時相關元素的先後順序;
    <?xml version='1.0' encoding='utf-8'?>
    <package xmlns="http://www.idpf.org/2007/opf" unique-identifier="uuid_id" version="2.0">
      <metadata 
    	xmlns:calibre="http://calibre.kovidgoyal.net/2009/metadata" 
    	xmlns:dc="http://purl.org/dc/elements/1.1/" 
    	xmlns:dcterms="http://purl.org/dc/terms/"
    	xmlns:opf="http://www.idpf.org/2007/opf" 
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <dc:language>zh</dc:language>
        <dc:title>ePub 手作練習</dc:title>
        <dc:creator opf:file-as="darkthread" opf:role="aut">黑暗執行緒</dc:creator>
        <meta name="cover" content="cover"/>
    	<dc:identifier id="uuid_id" opf:scheme="uuid">b1ce20ed-9214-470c-97d0-2648fc2f7760</dc:identifier>
        <dc:contributor opf:role="bkp"></dc:contributor>
        <dc:publisher>黑暗出版社</dc:publisher>
      </metadata>
      <manifest>
        <item href="cover.jpg" id="cover" media-type="image/jpeg"/>
        <item href="titlepage.xhtml" id="titlepage" media-type="application/xhtml+xml"/>
        <item href="format.html" id="html1" media-type="application/xhtml+xml"/>
        <item href="diy.html" id="html2" media-type="application/xhtml+xml"/>
        <item href="page_styles.css" id="page_css" media-type="text/css"/>
        <item href="stylesheet.css" id="css" media-type="text/css"/>
        <item href="toc.ncx" id="ncx" media-type="application/x-dtbncx+xml"/>
      </manifest>
      <spine toc="ncx">
        <itemref idref="titlepage" />
        <itemref idref="html1"/>
        <itemref idref="html2"/>
      </spine>
      <guide>
        <reference href="titlepage.xhtml" title="Cover" type="cover"/>
      </guide>
    </package>
    
  4. titlepage.xhtml 為封面定義檔(註:svg ViewBox 的寬高要配合 conver.jpg 尺寸)
    <?xml version='1.0' encoding='utf-8'?>
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
        <head>
            <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
            <title>Cover</title>
            <style type="text/css" title="override_css">
                @page {padding: 0pt; margin:0pt}
                body { text-align: center; padding:0pt; margin: 0pt; }
            </style>
        </head>
        <body>
            <div>
            <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 
            version="1.1" width="100%" height="100%" viewBox="0 0 904 1186" preserveAspectRatio="none">
                 <image width="904" height="1186" xlink:href="cover.jpg"/>
            </svg>
            </div>
        </body>
    </html>
    
  5. toc.ncx 為一個 XML,包含了書本標題、目錄項次。navPoint 的 id 可自訂,不重複即可,src 指向讓章節所在位置,若文章較長可拆成各章一個 html 檔案,小節部分可以用 URL Fragment (例如: #sectionName) 連結。範例:
    <?xml version='1.0' encoding='utf-8'?>
    <ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1" xml:lang="zh-TW">
      <head>
        <meta content="b1ce20ed-9214-470c-97d0-2648fc2f7760" name="dtb:uid"/>
        <meta content="1" name="dtb:depth"/>
        <meta content="calibre (3.44.0)" name="dtb:generator"/>
        <meta content="0" name="dtb:totalPageCount"/>
        <meta content="0" name="dtb:maxPageNumber"/>
      </head>
      <docTitle>
        <text>ePub 手作練習</text>
      </docTitle>
      <navMap>
        <navPoint id="ch1" playOrder="1">
          <navLabel>
            <text>ePub 格式解析</text>
          </navLabel>
          <content src="format.html"/>
        </navPoint>
        <navPoint id="ch2" playOrder="2">
          <navLabel>
            <text>動手做書樂無窮</text>
          </navLabel>
          <content src="diy.html"/>
        </navPoint>
       </navMap>
    </ncx>
    
  6. page_styles.css 是全書通用的樣式
    @page {
     margin-bottom: 5pt;
     margin-top: 5pt
     }
    
  7. styles.css 可用來儲存自訂樣式
    各章節檔案再用 <link href="xxx.css" rel="stylesheet" type="text/css" /> 指向 page_styles.css 及 styles.css,這部可依網頁設計原理安排,無強制要求。
  8. 封面圖片,例如: cover.jpg。尺寸上沒有嚴格規定,參考各家電子書平台建議,大約抓寬高 1:1.6,500x800 以上,取 jpg 格式。

準備好上述項目,將整個資料夾壓成 ZIP 檔(注意:mimetype、META-INFO、OEBPS 要直接在 ZIP 內容的根目錄)再更名為 .epub,電子書就完成了!

thumbnail

thumbnail

補充,自製 ePub 過程蠻常因格式、內容錯誤、缺檔造成無法開啟,有個 EPUBCheck 工具能快速指出 ePub 錯誤所在,是 DIY 時不可或缺的好工具:

掌握手工製作 ePub 技巧,未來要修校電子書或搞搞網頁批次轉換,都知道怎麼下手囉~

Explaining the structure of ePub format and try to create one by hand.


Comments

# by John

epubcheck錯誤訊息是mimetype為第一個檔案且不要壓縮

# by Jeffrey

to John, 對,後來自己寫 .NET 程式打包刻意不壓縮且放第一個,epubcheck 才不再抱怨,不過實測了 Kobo 跟 Chrome ePub 閱讀器,似乎不在意這條規範。

# by John

如果需要上架販售,epubcheck條件全過是基本條件

# by Jeffrey

to John, 原來如此,長知識了。謝謝分享!

# by Danny Lin

抓網頁真的是博大精深的一門學問。一般要抓網頁目前還是認為用 WebScrapBook 擷取和管理效果最好,至少我會特地去把網頁抓下來,主要目的通常是: 1. 清理垃圾 2. 畫線,寫筆記 3. 保留 metadata 供需要時作為 reference 4. 全文檢索 我在想如果 WebScrapBook 可以滿足絕大部分需求,做成 ePub 只是為了改善在手機、平板、電子閱讀器上排版效果及外出閱讀體驗,或許可以寫個 WebScrapBook => ePub 轉檔程式來處理...🤔 (對電子紙不熟,不曉得目前的電子紙是否可直接看網頁?還是只支援 ePub、pdf 等特定電子書格式?) 但如果製作 ePub 還打算把網頁整個 tidy,格式統一成像一本書的樣子,那可能還是不得不針對各網站寫專門的擷取及格式清理工具了...😂

# by Jeffrey

to Danny Lin, 電子紙因特性關係,畫面更新速度不快(體感換個畫面要花一秒吧),閱讀器的 CPU 完全無法跟手機平板相比,就算能支援一般網頁,瀏覽體驗也好不到哪去。「針對各網站寫專門的擷取及格式清理工具 」<= 一語道破網頁轉 ePub 最大的痛點。如果 WebScrapBook 已有擷取及清理功能,WebScrapBook => ePub 轉檔是好點子,有空來研究一下,感謝分享。

# by Danny Lin

WebScrapBook 主要是設計為「如實擷取螢幕呈現頁面」的通用工具,是有清理功能,但多半是通用性的,如要針對特定站台做清理可以寫針對性的擷取助手,但擷取助手功能相當有限(很大一部分原因是受限於瀏覽器套件的限制),必要時可以考慮寫 bookmarklet 或用其他 monkey 之類的腳本工具。 這就是網頁擷取比較坑的地方,基本上網頁大致可以分成幾類: 1. 一般的公開靜態網頁 2. 需要身分認證的私人網頁 3. 要瀏覽器跑 JavaScript 動態加載的動態網頁 一般第三方擷取工具處理 1. 還行,處理 2. 就很麻煩(要把 cookie 等認證資訊從瀏覽器傳過去,或是自備身分認證處理機制),至於 3.,目前似乎只有瀏覽器擴充套件才有機會正常擷取……(bookmarklet 容易受限於 same origin policy 和 content security policy 而抓不全)。 但是 WebScrapBook 目前還只支援單頁擷取,把連結頁面一併擷取(且維持交互連結)的功能還難產中XD 前身的 ScrapBook X 有這功能,不過 ScrapBook X 架構太舊了,不少現代網頁的新功能無法完全正常擷取,要注意一下。

Post a comment


60 - 17 =