使用 EPUB 製做數字圖書

開始以前

本教程講述如何建立 EPUB 格式的電子圖書。EPUB 是一種基於 XML 的、對開發者友好的格式,正逐漸成爲數字圖書的事實標準。但 EPUB 不只可用於圖書,還包括: php

  • 對文檔打包以便離線閱讀或者分發
  • 打包博客文章或者其餘 Web 內容
  • 使用常見的開放源代碼工具建立、搜索和整理

關於本教程

經常使用的縮寫詞

  • API:應用程序編程接口(application programming interface)
  • CSS:級聯樣式表(Cascading stylesheet)
  • DOM:文檔對象模型(Document Object Model)
  • DTD:文檔類型定義(Document type definition)
  • GUI:圖形用戶界面(Graphical user interface)
  • HTML:超文本標記語言(Hypertext Markup Language)
  • SAX:XML 簡易 API(Simple API For XML)
  • W3C:萬維網聯盟(World Wide Web Consortium)
  • XHTML:可擴展的 HTML(Extensible HTML)
  • XML:可擴展標記語言

本教程首先手工建立一個 EPUB 圖書,幫助您瞭解其構成和須要的文件。而後說明如何捆綁完成的數字圖書,按照規範進行驗證以及在不一樣的閱讀系統上測試。 css

而後討論如何從 DocBook XML 生成 EPUB — 最經常使用的技術文檔標準之一 — 以及如何使用 Python 實現從 DocBook 到 EPUB 的自動建立。 html

目標

經過本教程能夠學習以下內容: java

  • 瞭解 EPUB 是什麼,誰支持它,誰採用它
  • 瞭解 EPUB 包的結構,包括須要的文件及其模式
  • 如何從頭建立一個內容簡單而有效的 EPUB 文件
  • 使用開放源代碼工具從 DocBook 生成 EPUB 文件,DocBook 是一種常見的技術文檔和圖書模式
  • 使用 Python 和 DocBook 自動轉換成 EPUB

先決條件

本教程對操做系統沒有特殊要求,可是應該熟悉建立文件和目錄的機制。建議使用 XML 編輯器或者集成開發環境(IDE)。 git

對於本教程後半部分的 EPUB 建立自動化內容,須要讀者瞭解基本的 XML 處理技巧 — XSLT、 DOM 或者基於 SAX 的解析 — 並熟悉使用 XML 原生 API 構造 XML 文檔。 編程

閱讀本教程不須要熟悉 EPUB 文件格式。 瀏覽器

系統需求

嘗試本教程中的例子,須要一個 Java 解釋器(1.5 或更高版本)和 Python 解釋器(2.4 或更高版本)以及相應的 XML 庫。不過,有經驗的 XML 開發人員很容易將這些例子修改成適合任何編程語言和 XML 庫。 網絡

回頁首 app

關於 EPUB 格式

瞭解 EPUB 的背景,EPUB 最適合作什麼,以及 EPUB 和便攜式文檔格式(PDF)的區別。 jsp

什麼是 EPUB?

EPUB 是可逆的數字圖書和出版物 XML 格式,數字出版業商業和標準協會 International Digital Publishing Forum (IDPF) 制定的標準。IDPF 於 2007 年 10 月正式採用 EPUB,隨後被主流出版商迅速採用。可使用各類開放源代碼或者商業軟件在全部主流操做系統、Sony PRS 之類的 e-ink 設備或者 Apple iPhone 之類的小型設備上閱讀 EPUB 格式。

誰在使用 EPUB?只能用於圖書嗎?

雖然最先採用 EPUB 的是傳統的印刷品出版商,可是這並不妨礙它在電子圖書中的應用。利用免費的軟件工具,能夠將網頁捆綁成 EPUB,轉化成文本文件或者將原有的 DocBook XML 文檔轉化成結構良好的、有效的 EPUB(後一點將在 從 DocBook 到 EPUB 一節討論)。

EPUB 與 PDF 有什麼不一樣?

PDF 仍然是世界上應用最普遍的電子文檔格式。從圖書出版商的角度來看,PDF 的優勢包括:

  • PDF 文件容許對頁面佈局進行像素級的控制,包括複雜的打印格式,如多欄格式和奇偶頁相間的格式。
  • 有多種不一樣的 GUI 文檔工具可生成 PDF,如 Microsoft® Office Word 和 Adobe® InDesign®。
  • PDF 閱讀器很是普及,如今大多數計算機上都有安裝。
  • PDF 能夠嵌入特殊的字體,精確控制最終的輸出結果。

三合一的標準

EPUB 包括三個單獨的 IDPF 規範,雖然實際上將其統稱爲 EPUB 更保險:

  • Open eBook Publication Structure Container Format (OCF):定義了 EPUB 檔案的目錄樹結構和文件結構(ZIP)。
  • Open Publication Structure (OPS):定義了電子圖書的公共詞彙表,特別是可做爲圖書內容的格式(好比 XHTML 和 CSS)。
  • Open Packaging Format (OPF):描述了 EPUB 必須的和可選的元數據、閱讀順序和目錄。

此外,對於檔案中的特定類型的內容,EPUB 還重用了其餘一些標準,如 XHTML 1.0 和 Digital Accessible Information SYstem (DAISY)。

從軟件開發人員的角度來看,PDF 還遠遠不夠理想:

  • 這不是一種簡單易學的標準,所以編寫本身的 PDF 生成代碼很是困難。
  • 雖然 PDF 如今是一種 International Organization for Standardization(ISO)標準(ISO 32000-1:2008),但過去一直受一家公司的控制:Adobe Systems。
  • 儘管多數編程語言都提供了 PDF 庫,但不少是商業產品或者嵌入到 GUI 應用程序中,外部進程不容易控制。並不是全部的免費庫都獲得積極的維護。
  • PDF 原生文本能夠經過程序提取出來並進行搜索,但不多能夠對 PDF 進行標記以便簡單可靠地轉化成 Web 友好的格式。
  • PDF 文檔不容易流動,就是說很難適應小屏幕或者對佈局進行明顯的改變。

爲什麼說 EPUB 對開發人員是友好的

EPUB 解決了 PDF 和開發人員友好性有關的全部瑕疵。一個 EPUB 就是一個簡單 ZIP 格式文件(使用 .epub 擴展名),其中包括按照預先定義的方式排列的文件。如何製做 ZIP 文檔有一些技巧,稍後將在 將 EPUB 文件捆綁爲 ZIP 文檔 一節介紹。除此之外,EPUB 很是簡單:

  • EPUB 中的全部內容基本上都是 XML。EPUB 文件可以使用標準 XML 工具建立,不須要任何專門或者私有的軟件。
  • EPUB 內容(eBook 的具體內容)基本上都是 XHTML 1.1(另外一種格式是 DTBook,爲視力受限者編碼書籍的一種標準。關於 DTBook 的更多信息請參閱 參考資料,本教程中不涉及這部分)。
  • 大多數 EPUB XML 模式都來自現成的、可免費得到的、已發佈的規範。

最關鍵的在於 EPUB 元數據是 XMLEPUB 內容是 XHTML。若是您的文檔構建系統產生的結果用於 Web 和/或基於 XML,那麼也可用於生成 EPUB。

回頁首

建立第一個 EPUB

最小的 EPUB 包至少要包含幾個文件。規範對於 EPUB 包中這些文件的格式、內容和位置要求可能很嚴格。這一節討論使用 EPUB 標準必須瞭解的基礎知識。

解剖 EPUB 包

小型 EPUB 文件的基本結構遵循 清單 1 所示的樣式。準備好分發以前,整個目錄結構被壓縮到一個 ZIP 格式文件中,幾點特殊要求將在 用 ZIP 打包 EPUB 文件 一節討論。

清單 1. 簡單 EPUB 檔案的目錄和文件結構
mimetype
META-INF/
   container.xml
OEBPS/
  content.opf
  title.html
  content.html
  stylesheet.css
  toc.ncx
  images/
     cover.png

提示:可 下載 符合該結構的一個電子圖書,但建議按照本教程的說明本身建立一個。

編寫 EPUB 圖書以前首先建立 EPUB 項目的目錄。打開文本編輯器或者 Eclipse 之類的 IDE。建議採用支持 XML 的編輯器 — 具體而言就是可以根據 參考資料 給出的 Relax NG 模式進行驗證。

mimetype 文件

這個文件很是簡單,必須命名爲 mimetype,文件內容以下:

application/epub+zip

要注意,mimetype 文件不能包含新行或者回車。

此外,mimetype 文件必須做爲 ZIP 檔案中的第一個文件,並且自身不能壓縮。用 ZIP 打包 EPUB 文件 一節將介紹如何使用通常的 ZIP 參數將其包含進來。如今建立該文件並保存,並確保它在 EPUB 項目的根目錄中。

META-INF/container.xml

EPUB 根目錄下必須包含 META-INF 目錄,並且其中要有一個文件 container.xml。EPUB 閱讀系統首先查看該文件,它指向數字圖書元數據的位置。

建立目錄 META-INF。在其中建立一個新文件 container.xml。container 文件很是小,可是對結構要求很嚴格。將 清單 2 中的代碼粘貼到 META-INF/container.xml 中。

清單 2. container.xml 文件
<?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>

full-path(粗體)的值僅僅是該文件的一部分,不一樣的文件可能相差甚大。目錄路徑必須相對於 EPUB 文件根目錄自己,而不是 META-INF 目錄。

關於 META-INF

META-INF 目錄還能夠包含其餘幾個文件。這些文件使 EPUB 支持數字簽名、加密和數字版權管理(DRM)。本教程不討論這些主題。更多信息請參閱 OCF 規範。

mimetype 和 container 是 EPUB 檔案中僅有的兩個須要嚴格限制位置的文件。建議(儘管不是必須的)將其餘文件保存到 EPUB 的子目錄下(按照慣例,一般被稱爲 OEBPS,即 Open eBook Publication Structure,但不是必須的)。

接下來在 EPUB 項目中建立目錄 OEBPS。本教程下一節將介紹 OEBPS 中的文件 — 數字圖書的核心:元數據和頁面。

打開 Packaging Format 元數據文件

儘管該文件名沒有特殊要求,但一般被稱爲 content.opf。它指定了圖書中全部 內容的位置,如文本和圖像等其餘媒體。它還給出了另外一個元數據文件,內容的 Navigation Center eXtended (NCX) 表。

該 OPF 文件是 EPUB 規範中最複雜的元數據。建立 OEBPS/content.opf 並粘貼 清單 3 所示的內容。

清單 3. 包含示例元數據的 OPF content 文件
<?xml version='1.0' encoding='utf-8'?>
<package xmlns="http://www.idpf.org/2007/opf" 
            xmlns:dc="http://purl.org/dc/elements/1.1/" 
            unique-identifier="bookid" version="2.0">
  <metadata>
    <dc:title>Hello World: My First EPUB</dc:title>
    <dc:creator>My Name</dc:creator>
    <dc:identifier id="bookid">urn:uuid:12345</dc:identifier>
    <meta name="cover" content="cover-image" />
  </metadata>
  <manifest>
    <item id="ncx" href="toc.ncx" media-type="text/xml"/>
    <item id="cover" href="title.html" media-type="application/xhtml+xml"/>
    <item id="content" href="content.html" media-type="application/xhtml+xml"/>
    <item id="cover-image" href="images/cover.png" media-type="image/png"/>
    <item id="css" href="stylesheet.css" media-type="text/css"/>
  </manifest>
  <spine toc="ncx">
    <itemref idref="cover" linear="no"/>
    <itemref idref="content"/>
  </spine>
  <guide>
    <reference href="cover.html" type="cover" title="Cover"/>
  </guide>
</package>

OPF 模式與名稱空間

OPF 文檔自己必須使用名稱空間 http://www.idpf.org/2007/opf,元數據則使用 Dublin Core Metadata Initiative (DCMI) 名稱空間http://purl.org/dc/elements/1.1/

最好如今將 OPF 和 DCMI 模式添加到 XML 編輯器中。EPUB 用到的全部模式均可以 下載

元數據

Dublin Core 定義了一組經常使用的元數據,可用於描述各類不一樣的數字資料,它不是 EPUB 規範的一部分。全部這些術語均可以出如今 OPF 元數據部分。編寫要分發的 EPUB 時,這裏能夠放不少內容,目前來講 清單 4 的內容就足夠了。

清單 4. OPF 元數據摘要
...
<metadata>
  <dc:title>Hello World: My First EPUB</dc:title>
  <dc:creator>My Name</dc:creator>
  <dc:identifier id="bookid">urn:uuid:12345</dc:identifier>
  <meta name="cover" content="cover-image" />
</metadata>
...

有兩個術語是必須的,即 title 和 identifier。按照 EPUB 規範,標識符必須 是唯一的,可是這個唯一的值要靠數字圖書的建立者來定義。對於圖書出版商來講,這個字段通常包含 ISBN 或者 Library of Congress 編號。對於其餘 EPUB 建立者,能夠考慮使用 URL 或者很大的隨機生成的唯一用戶 ID(UUID)。要注意,屬性 unique-identifier 的值必須和 dc:identifier 元素的 ID 屬性匹配。

其餘和內容相關的能夠考慮添加的元數據包括:

  • 語言(如 dc:language)。
  • 出版日期(如 dc:date)。
  • 出版商(如 dc:publisher)。(能夠是公司或我的的名稱)。
  • 版權信息(如 dc:rights)。(若是採用 Creative Commons 許可證,能夠將許可證的 URL 放在這裏)。

關於 DCMI 的更多信息請參閱 參考資料

EPUB 規範沒有要求包含 name 屬性值爲 cover 的 meta 元素,但爲了增長封面和圖像的可移植性,建議這樣作。一些 EPUB 呈現程序喜歡使用圖像文件做爲封面,另外一些則願意使用包含內聯封面圖像的 XHTML 文件。該例子顯示了這兩種狀況。meta 元素的 content 屬性的值應該是圖書封面圖像在 manifest 中的 ID 號,manifest 是 OPF 文件的一部分。

Manifest

OPF manifest 列出了 EPUB 內容(不包括元數據)中的全部資源。就是說,一般是組成電子圖書文本的一組 XHTML 文件再加上一些相關的媒體如圖像。EPUB 鼓勵使用 CSS 設定圖書內容的樣式,所以 manifest 中也包含 CSS。進入數字圖書的全部文件都必須在 manifest 中列出。

清單 5 顯示了 manifest 的一部分。

清單 5. OPF manifest 的一部分
...
<manifest>
  <item id="ncx" href="toc.ncx" media-type="text/xml"/>
  <item id="cover" href="title.html" media-type="application/xhtml+xml"/>
  <item id="content" href="content.html" media-type="application/xhtml+xml"/>
  <item id="cover-image" href="images/cover.png" media-type="image/png"/>
  <item id="css" href="stylesheet.css" media-type="text/css"/>
</manifest>
...

高級 OPF manifest

更高級的 manifest 文件可能包含多個 XHTML 文件以及圖像和 CSS。可 下載 一個完整的包含各類常見類型的 EPUB 例子。

第一項 toc.ncx(參見 下一節)是必須的。全部的項都有相應的 media-type 值,XHTML 內容的媒體類型爲 application/xhtml+xml。媒體類型必須正確,不能 是 text/html或者其餘類型。

EPUB 支持四種核心 圖像文件類型:Joint Photographic Experts Group (JPEG)、Portable Network Graphics (PNG)、Graphics Interchange Format (GIF) 和 Scalable Vector Graphics (SVG)。若是可以提供對核心類型的後退轉換(fall-back),也可包含不支持的文件類型。關於後退轉換內容的更多信息請參閱 OPF 規範。

href 屬性的值應該是一個相對於該 OPF 文件 的統一資源標識符(URI)。(很容易和 container.xml 中對 OPF 文件的引用混淆,其中的引用是相對於 EPUB 的總體引用)。這裏的 OPF 文件位於和內容相同的 OEBPS 目錄中,所以不須要路徑信息。

Spine

manifest 告訴 EPUB 閱讀器哪些文件屬於檔案,spine 則指定這些文件出現的順序或 — 按照 EPUB 的說法 — 數字圖書的線性閱讀順序。能夠將 OPF spine 看做是書中 「頁面」 的順序。按照文檔順序從上到下依次讀取 spine。清單 6 顯示了 OPF 文件的一個片斷。

清單 6. OPF spine 的一部分
...
<spine toc="ncx">
  <itemref idref="cover" linear="no"/>
  <itemref idref="content"/>
</spine>
...

每一個 itemref 元素都須要有一個 idref 屬性,而且和 manifest 中的某個 ID 匹配。toc 屬性也是必需的。它引用 manifest 中表示內容 NCX 表文件名的 ID。

spine 中的 linear 屬性代表該項是做爲線性閱讀順序中的一項,仍是和前後次序無關。建議將封面定義爲 linear=no。符合 EPUB 規範的閱讀系統將首先打開 spine 中沒有 設置爲 linear=no 中的第一項。

Guide

OPF 內容文件的最後一部分是 guide。這一節是可選的,但最好保留。清單 7 顯示了 guide 文件的部份內容。

清單 7. OPF guide 的一部分
...
<guide>
  <reference href="cover.html" type="cover" title="Cover"/>
</guide>
...

guide 能夠爲 EPUB 閱讀系統提供語義信息。manifest 定義了 EPUB 中的物理資源,spine 提供了這些資源的順序信息,guide 負責解釋這些部分的含義。下面是能夠出如今 OPF guide 中的部分值:

  • cover: 圖書封面
  • title-page: 包含做者和出版商信息的頁面
  • toc:目錄

完整的列表請參閱 OPF 2.0 規範(參見 參考資料)。

內容的 NCX 表

NCX 和 OPF 元數據的交叉

因爲 NCX 源自其餘標準,使用 NCX 編碼的信息和 OPF 內容之間存在重複。若是經過程序生成 EPUB,這算不上什麼問題,由於一樣的代碼可輸出到兩個文件中。兩個位置的信息要一致,不一樣的 EPUB 讀者可能使用不一樣位置的值。

儘管 OCF 文件是做爲 EPUB 自己的一部分定義的,但最後一個主要的元數據文件參照了不一樣的數字圖書標準。DAISY 是一個專門爲不能使用傳統書籍的讀者設計數據格式的組織,一般是由於視力受損或者不便於使用印刷的書籍。EPUB 借用了 DAISY 的 NCX DTD。NCX 定義了數字圖書的目錄表。複雜的圖書中,目錄表一般採用層次結構,包括嵌套的內容、章和節。

使用 XML 編輯器建立 OEBPS/toc.ncx 並粘貼 清單 8 所示的代碼。

清單 8. 簡單的 NCX 文件
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE ncx PUBLIC "-//NISO//DTD ncx 2005-1//EN" 
                 "http://www.daisy.org/z3986/2005/ncx-2005-1.dtd">
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
  <head>
    <meta name="dtb:uid" content="urn:uuid:12345"/>
    <meta name="dtb:depth" content="1"/>
    <meta name="dtb:totalPageCount" content="0"/>
    <meta name="dtb:maxPageNumber" content="0"/>
  </head>
  <docTitle>
    <text>Hello World: My First EPUB</text>
  </docTitle>
  <navMap>
    <navPoint id="navpoint-1" playOrder="1">
      <navLabel>
        <text>Book cover</text>
      </navLabel>
      <content src="title.html"/>
    </navPoint>
    <navPoint id="navpoint-2" playOrder="2">
      <navLabel>
        <text>Contents</text>
      </navLabel>
      <content src="content.html"/>
    </navPoint>
  </navMap>
</ncx>

NCX 元數據

DTD 要求 NCX <head> 標記中包含四個 meta 元素:

  • uid: 數字圖書的唯一 ID。該元素應該和 OPF 文件中的 dc:identifier 對應。
  • depth:反映目錄表中層次的深度。該例只有一層,所以是 1
  • totalPageCount 和 maxPageNumber:僅用於紙質圖書,保留 0 便可。

docTitle/text 的內容是圖書的標題,和 OPF 中的 dc:title 匹配。

NCX navMap

NCX 和 OPF spine 有什麼不一樣?

二者很容易混淆,由於兩個文件都描述了文檔的順序和內容。要說明二者的區別,最簡單的辦法就是拿印刷書來打比方:OPF spine 描述了書中的各個章節是如何實際鏈接起來的,比方說翻過第一章的最後一頁就看到第二章的第一頁。NCX 在圖書的一開始描述了目錄。目錄確定會包含書中主要的章節,可是還可能包含沒有單獨分頁的小節。

一條法則是 NCX 包含的 navPoint 元素一般比 OPF spine 中的 itemref 元素多。實際上,spine 中的全部項都會出如今 NCX 中,但 NCX 可能更詳細。

navMap 是 NCX 文件中最重要的部分,定義了圖書的目錄。navMap 包含一個或多個navPoint 元素。每一個 navPoint 都要包含下列元素:

  • playOrder 屬性,說明文檔的閱讀順序。和 OPF spine 中 itemref 元素的順序相同。
  • navLabel/text 元素,給出該章節的標題。一般是章的標題或者數字,如 「第一章」,或者 — 像這個例子同樣 — 「封面」。
  • content 元素,它的 src 屬性指向包含這些內容的物理資源。就是 OPF manifest 中聲明的文件(也可以使用片斷標識符引用 XHTML 內容中的錨元素 — 好比content.html#footnote1)。
  • 還能夠有一個或多個 navPoint 元素。NCX 使用嵌套的導航點表示層次結構的文檔。

該文檔的結構很是簡單:只有兩頁,不存在嵌套關係。就是說有兩個 navPoint 元素,它們的 playOrder 值按升序排列,從 1 開始。在 NCX 中能夠命名這些章節,以便讀者跳到電子圖書不一樣的部分。

添加最後的內容

如今知道了 EPUB 須要的全部元數據,能夠加入真正的圖書內容了。可使用 下載 的內容,也能夠本身寫,只要文件名和元數據匹配便可。

而後建立下列文件和文件夾:

  • title.html:圖書的標題頁。建立該文件並在其中包含引用封面圖片的 img 元素,src 的屬性值爲 images/cover.png。
  • images:在 OEBPS 下建立該文件夾,而後複製給定的示例圖片(或者建立本身的圖片)並命名爲 cover.png
  • content.html:圖書的實際文字內容。
  • stylesheet.css:將該文件放在和 XHTML 文件相同的 OEBPS 目錄中。該文件能夠包含任意 CSS 聲明,好比設置字體或者文字顏色。清單 10 給出了一個 CSS 文件的例子。

EPUB 圖書中的 XHTML 和 CSS

清單 9 包含了一個有效的 EPUB 內容頁。將其做爲標題頁(title.html),用一個相似的頁面做爲主要內容頁(content.html)。

清單 9. 示例 title 頁面(title.html)
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Hello World: My First EPUB</title>
    <link type="text/css" rel="stylesheet" href="stylesheet.css" />
  </head>
  <body>
    <h1>Hello World: My First EPUB</h1>
    <div><img src="images/cover.png" alt="Title page"/></div>
  </body>
</html>

EPUB 的 XHTML 須要符合幾條要求,和通常的 Web 開發不一樣:

  • 內容必須是有效的 XHTML 1.1:XHTML 1.0 Strict 和 XHTML 1.1 的主要區別是去掉了 name 屬性(使用 ID 引用錨元素)。
  • img 元素只能引用電子圖書的本地圖片:該元素不能引用 Web 上的圖片。
  • 避免使用 script:EPUB 閱讀器不必定支持 JavaScript 代碼。

EPUB 支持 CSS 的方式有一些細微的差異,可是不會影響樣式表的通常用法(詳情參閱 OPS 規範)。清單 10 中的簡單 CSS 文件能夠設置基本的字體,並把標題設爲紅色。

清單 10. 電子圖書的示例樣式表(stylesheet.css)
body {
  font-family: sans-serif;     
}
h1,h2,h3,h4 {
  font-family: serif;     
  color: red;
}

有趣的是,EPUB 很是支持 CSS 2 @font-face 規則,容許內嵌字體。若是建立技術文檔,這點可能可有可無,可是若是用多種語言或針對特定領域編寫 EPUB,可以指定具體的字體數據就頗有必要了。

如今已經準備好了建立 EPUB 圖書所需的全部內容。下一節將按照 OCF 規範將圖書裝訂起來,並看看如何進行驗證。

回頁首

打包和檢查 EPUB

如今,應當能夠對 EPUB 包進行打包。這個包能夠是您本身建立的一本新書,也可以使用從本文 下載 部分得到的原始文件。

用 ZIP 打包 EPUB 文件

EPUB 規範的 OEBPS Container Format 討論了 EPUB 和 ZIP,最重要的幾點是:

  • 檔案中的第一個文件必須是 mimetype 文件(參見本教程 Mimetype 一節)。mimetype 文件不能被壓縮。這樣非 ZIP 工具就能從 EPUB 包的第 30 個字節開始讀取原始字節,從而發現 mimetype。
  • ZIP 檔案不能加密。EPUB 支持加密,但不是在 ZIP 文件這一層上。

在類 UNIX® 操做系統上,使用 ZIP 2.3 可經過兩個命令來建立 EPUB ZIP 文件,如 清單 11 所示(這些命令假設當前工做目錄爲 EPUB 項目。)

清單 11. 將 EPUB 打包成有效的 epub+zip 文件
$ zip -0Xq  my-book.epub mimetype
$ zip -Xr9Dq my-book.epub *

第一個命令建立了一個新的 ZIP 檔案,並添加了沒有進行壓縮的 mimetype 文件。第二個命令添加其餘內容。選項 -X 和 -D 最大限度地減小 .zip 文件中可有可無的信息;-r 遞歸地包含 META-INF 和 OEBPS 目錄的內容。

EPUB 驗證

雖然 EPUB 標準並不很難,但其 XML 文件必須符合特定的模式。若是使用模式感知的 XML 編輯器生成元數據和 XHTML,就能事半功倍。對 EpubCheck 包進行最後檢查(參見 參考資料)。

Adobe 負責維護 EpubCheck 包,它是採用 Berkeley Software Distribution (BSD) 許可證的開源項目。它是一個能夠做爲獨立工具、Web 應用程序運行的 Java 程序,或者能夠將它集成到在 Java Runtime Environment (JRE) 1.5 或更高版本下運行的應用程序中。

在命令行中運行很是簡單。清單 12 給出了一個例子。

清單 12. 運行 EpubCheck 工具程序
$ java -jar /path/to/epubcheck.jar my-book.epub

若是沒有建立輔助文件或者元數據文件出錯,可能會看到 清單 13 所示的錯誤消息。

清單 13. EpubCheck 錯誤
my-book.epub: image file OEBPS/images/cover.png is missing
my-book.epub: resource OEBPS/stylesheet.css is missing
my-book.epub/OEBPS/title.html(7): 'OEBPS/images/cover.png': 
     referenced resource missing in the package

Check finished with warnings or errors!

這時候可能須要設置 CLASSPATH 使它指向 EpubCheck 的安裝位置,由於確實須要導入幾個外部庫。若是獲得這樣的消息則須要設置 CLASSPATH:

org.xml.sax.SAXParseException: no implementation available for schema language 
   with namespace URI "http://www.ascc.net/xml/schematron"

若是驗證成功,就會看到 「No errors or warnings detected(沒有檢測到錯誤或警告)」。祝賀您完成了第一個 EPUB!

查看 EPUB

測試不只僅是驗證,還要保證書的外觀看起來不錯。樣式表能正確工做嗎?章節的邏輯順序是否正確?書中是否包含了全部須要的內容?

有多重 EPUB 閱讀器可供選擇。圖 1 顯示了 Adobe Digital Editions (ADE) 的屏幕截圖,這是最經常使用的 EPUB 閱讀器。

圖 1. ADE 中顯示的 EPUB
ADE 中顯示的 EPUB

字體顏色和圖像都顯示出來了,不錯。ADE 未能用 sans-serif 字體正確地顯示標題,不過這多是 CSS 的問題。這時候最好換一個閱讀器試試。圖 2 是用我本身編寫的、開放源代碼的、基於 Web 的 EPUB 閱讀器 Bookworm 顯示的同一本書。

圖 2. 在 Bookworm 中顯示 EPUB
在 Bookworm 中顯示 EPUB

這裏的問題在於 ADE 不支持這種特殊聲明。若是數字圖書的格式很是重要,那麼就必須瞭解不一樣閱讀軟件的特色。

前面咱們費了很大力氣手工建立了一個簡單的 EPUB,如今看看如何將一種常見的 XML 文檔 DocBook 轉換成 EPUB。

回頁首

從 DocBook 到 EPUB

DocBook 是須要維護大型技術文檔的開發人員經常使用的選擇。與傳統字處理程序生成的文件不一樣,可使用基於文本的版本控制系統管理 DocBook 輸出。因爲 DocBook 是 XML,很容易將其轉換成不一樣輸出格式。2008 年夏天出現了正式的 DocBook XSL 項目,將 EPUB 做爲一種輸出格式。

使用 XSLT 運行 DocBook-to-EPUB 管道

從一個簡單 DocBook 文檔開始,如 清單 14 所示。 該文檔的類型爲 book,包括前言、兩個章節以及標題頁面中內聯顯示的圖像。圖像和 DocBook 源文件的目錄相同。能夠本身建立該文件和標題頁,也可 下載 本文提供的例子。

清單 14. 簡單的 DocBook 圖書
<?xml version="1.0" encoding="utf-8"?`>  
<book>
  <bookinfo>
    <title>My EPUB book</title>
    <author><firstname>Liza</firstname>
            <surname>Daly</surname></author>
    <volumenum>1234</volumenum>
  </bookinfo>
  <preface id="preface">  
    <title>Title page</title>
    <figure id="cover-image">
      <title>Our EPUB cover image icon</title>
      <graphic fileref="cover.png"/>
    </figure>
  </preface>
  <chapter id="chapter1"> 
    <title>This is a pretty simple DocBook example</title>
    <para>
      Not much to see here. 
    </para>
  </chapter>
  <chapter id="end-notes"> 
    <title>End notes</title>
    <para>
      This space intentionally left blank.
    </para>
  </chapter>
</book>

而後從 參考資料 下載最新版本的 DocBook XSL 樣式表,並安裝 xsltproc 或 Saxon 之類的 XSLT 處理程序。本文使用 xsltproc,大多數類 UNIX 系統上都能找到。轉換 DocBook 文件,只須要用 DocBook XSL 中包含的 EPUB 模塊運行該文件便可,如 清單 15 所示。

清單 15. 將 DocBook 轉化成 EPUB
$ xsltproc /path/to/docbook-xsl-1.74.0/epub/docbook.xsl docbook.xml
Writing OEBPS/bk01-toc.html for book
Writing OEBPS/pr01.html for preface(preface)
Writing OEBPS/ch01.html for chapter(chapter1)
Writing OEBPS/ch02.html for chapter(end-notes)
Writing OEBPS/index.html for book
Writing OEBPS/toc.ncx
Writing OEBPS/content.opf
Writing META-INF/container.xml

定製 DocBook XSL

DocBook-to-EPUB 轉換管道仍然比較新,可能須要定製 XSLT 以獲得須要的結果。

而後添加 mimetype 文件並創建 epub+zip 檔案。清單 16 顯示了這三個命令和經過 EpubCheck 驗證程序的結果。

清單 16. 從 DocBook 建立 EPUB 檔案
$ echo "application/epub+zip" > mimetype
$ zip -0Xq  my-book.epub mimetype
$ zip -Xr9D my-book.epub *
$ java -jar epubcheck.jar my-book.epub 
No errors or warnings detected

太簡單了!圖 3 顯示了 ADE 中的結果。

圖 3. ADE 顯示了從 DocBook 轉化獲得的 EPUB
ADE 顯示了從 DocBook 轉化獲得的 EPUB

利用 Python 和 lxml 實現 DocBook-to-EPUB 轉換自動化

DocBook XSL 大大下降了生成 EPUB 的複雜性,可是在 XSLT 以外還有幾個步驟要執行。最後一節給出的 Python 示例程序可以生成有效的 EPUB 包。本教程顯示了不一樣的方法,可 下載 完整的 docbook2epub.py 程序。

可以使用不一樣的 Python XSLT 庫,我喜歡 lxml。它不但提供了 XSLT 1.0 必要的功能,並且解析效率高,徹底支持 XPath 1.0,提供了專門處理 HTML 的擴展。若是喜歡不一樣的庫或者使用 Python 之外的編程語言,修改這些例子也很簡單。

用 lxml 調用 DocBook XSL

使用 lxml 調用 XSLT 的最有效辦法是事先解析 XSLT,而後建立反覆使用的轉換器。這樣很方便,由於個人 DocBook-to-EPUB 須要轉換多個 DocBook 文件。如 清單 17 所示。

清單 17. 使用 lxml 運行 DocBook XSL
import os.path
from lxml import etree

def convert_docbook(docbook_file):
    docbook_xsl = os.path.abspath('docbook-xsl/epub/docbook.xsl')
    # Give the XSLT processor the ability to create new directories
    xslt_ac = etree.XSLTAccessControl(read_file=True, 
                                      write_file=True, 
                                      create_dir=True, 
                                      read_network=True, 
                                      write_network=False)
    transform = etree.XSLT(etree.parse(docbook_xsl), access_control=xslt_ac)
    transform(etree.parse(docbook_file))

DocBook XSL 中的 EPUB 模塊建立輸出文件自己,所以轉換過程什麼也不返回。相反,DocBook 在當前工做目錄中建立了兩個文件夾(META-INF 和 OEBPS),包含轉換的結果。

將圖片和其餘資源複製到檔案中

DocBook XSL 不會對文檔中使用的任何圖片執行操做,僅僅建立元數據文件和要呈現的 XHTML。因爲 EPUB 規範要求 content.opf manifest 列出全部資源,能夠預料到 manifest 將尋找原始 DocBook 文件引用的任何圖片。清單 18 顯示了這種技術,其中假定 path 變量包含 DocBook XSLT 生成的、當前所處理的 EPUB 的路徑。

清單 18. 解析 OPF 內容文件以尋找缺失的資源
import os.path, shutil
from lxml import etree

def find_resources(path='/path/to/our/epub/directory'):
    opf = etree.parse(os.path.join(path, 'OEBPS', 'content.opf'))

    # All the opf:item elements are resources
    for item in opf.xpath('//opf:item', 
                          namespaces= { 'opf': 'http://www.idpf.org/2007/opf' }):

        # If the resource was not already created by DocBook XSL itself, 
        # copy it into the OEBPS folder
        href = item.attrib['href']
        referenced_file = os.path.join(path, 'OEBPS', href):
        if not os.path.exists(referenced_file):
            shutil.copy(href, os.path.join(path, 'OEBPS'))

自動建立 mimetype

DocBook XSL 不會建立 mimetype 文件,不過 清單 19 中所示的代碼能夠完成這項任務。

清單 19. 建立 mimetype 文件
def create_mimetype(path='/path/to/our/epub/directory'):
    f = '%s/%s' % (path, 'mimetype')
    f = open(f, 'w')
    # Be careful not to add a newline here
    f.write('application/epub+zip')
    f.close()

用 Python 建立 EPUB 包

如今只須要將文件打包成有效的 EPUB ZIP 包。須要分兩步:將未經壓縮的 mimetype 文件做爲第一個文件加進去,而後添加其餘目錄。如 清單 20 所示。

清單 20. 使用 Python zipfile 模塊建立 EPUB 包
import zipfile, os

def create_archive(path='/path/to/our/epub/directory'):
    '''Create the ZIP archive.  The mimetype must be the first file in the archive 
    and it must not be compressed.'''

    epub_name = '%s.epub' % os.path.basename(path)

    # The EPUB must contain the META-INF and mimetype files at the root, so 
    # we'll create the archive in the working directory first and move it later
    os.chdir(path)    

    # Open a new zipfile for writing
    epub = zipfile.ZipFile(epub_name, 'w')

    # Add the mimetype file first and set it to be uncompressed
    epub.write(MIMETYPE, compress_type=zipfile.ZIP_STORED)
    
    # For the remaining paths in the EPUB, add all of their files
    # using normal ZIP compression
    for p in os.listdir('.'):
        for f in os.listdir(p):
            epub.write(os.path.join(p, f)), compress_type=zipfile.ZIP_DEFLATED)
    epub.close()

好了!切記要進行驗證。

回頁首

結束語

上一節 中的 Python 腳本僅僅是充分實現 EPUB 轉換自動化的第一步。爲了簡化起見,沒有涉及一些常見的狀況,好比任意嵌套的路徑、樣式表或者內嵌字體。Ruby 愛好者能夠看看 DocBook XSL 分發包中所含的 dbtoepub,方法與此相似。

由於 EPUB 仍是一種比較年輕的格式,不少有效的轉換方法還等待人們去創造。所幸的是,多數結構化標記,如 reStructuredText 或 Markdown 都已經存在生成 HTML 或者 XHTML 的渠道了;稍加修改來生成 EPUB 應該很是容易,尤爲是有了本文所示的 DocBook-to-EPUB Python 或 Ruby 腳本這些例子之後。

由於 EPUB 基本上就是 ZIP 和 XHTML,與其使用 .zip 文件,沒有理由不使用 EPUB 來分發文檔。擁有 EPUB 閱讀器的讀者可從傳統元數據和自動目錄表收益,沒有閱讀器的讀者也可將其做爲通常 ZIP 文件並在瀏覽器中查看 XHTML 內容。考慮將 EPUB 生成的代碼添加到各種文檔系統中,如 Javadoc 或 Perldoc。EPUB 是爲具備圖書長度的文檔設計的,所以很是適合愈來愈多的在線或者漸進式編程圖書。

回頁首

下載

描述 名字 大小
本教程中用於構建 EPUB 的資源 epub-raw-files.zip 8KB
EPUB 工具的 DocBook1 docbook-to-epub.zip 7KB

注意:

  1. 該 .zip 文件包含教程中的 DocBook XML 示例文件和完整的 docbook2epub.py 腳本。lxml 和 DocBook XSL 必須單獨下載,相關連接參見 參考資料

參考資料

學習

得到產品和技術

  • EpubCheck:Adobe EpubCheck 是建立 EPUB 的很是棒的工具。下載後可做爲獨立應用程序、Web 應用程序或庫(須要 Java 1.5 或更高版本)運行。
  • DocBook XSL:下載將 DocBook 轉化爲 EPUB 的樣式表的最新版本。這個 DocBook XSL 包還包括生成完整 EPUB 檔案的 Ruby 腳本,與本教程所述 Python 腳本相似。
  • lxml:若是沒有安裝的話請安裝 lxml,它是功能最齊全的 Python XML 庫。關於 lxml 的更多信息請參閱做者的另外一篇文章 使用用 Python 編寫的 lxml 實現高性能 XML 解析(Liza Daly,developerWorks,2008 年 10 月)。
  • Adobe Digital Editions 和 Bookworm:爲了測試 EPUB,最接近規範的電子圖書閱讀器是 ADE,這是一個跨平臺的桌面應用程序;還有做者編寫的基於 Web 的電子圖書閱讀器 Bookwork,它使用了 EPUB 呈現所用的瀏覽器。
  • IBM 試用軟件:使用這些試用軟件開發您的下一個項目,可直接從 developerWorks 下載,包括 DB2®、Lotus®, Rational®、Tivoli® 和 WebSphere® 提供的應用程序開發工具和中間件產品。

討論

相關文章
相關標籤/搜索