使用 EPUB 制作數字圖書 基于 XML 的開放式 eBook 格式
/////////////////////////////////////////////////////////////////////////////////////////////Page1
簡介: 是否需要分發文檔、創建電子圖書或者把喜歡的博客文章存檔?EPUB 是一種開放式的數字圖書規范,以常用的技術如 XML、CSS 和 XHTML 為基礎,EPUB 文件可在便攜式的 e-ink 設備、移動電話和桌面計算機上閱讀。本教程詳細闡述了 EPUB 格式,首先用 Java™ 技術示范了 EPUB 驗證,然后詳細說明如何使用 DocBook 和 Python 自動創建 EPUB。
開始之前
本教程講述如何創建 EPUB 格式的電子圖書。EPUB 是一種基于 XML 的、對開發者友好的格式,正逐漸成為數字圖書的事實標準。但 EPUB 不僅可用于圖書,還包括:
- 對文檔打包以便離線閱讀或者分發
- 打包博客文章或者其他 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 圖書,幫助您了解其構成和需要的文件。然后說明如何捆綁完成的數字圖書,按照規范進行驗證以及在不同的閱讀系統上測試。
然后討論如何從 DocBook XML 生成 EPUB — 最常用的技術文檔標準之一 — 以及如何使用 Python 實現從 DocBook 到 EPUB 的自動創建。
通過本教程可以學習如下內容:
- 了解 EPUB 是什么,誰支持它,誰采用它
- 了解 EPUB 包的結構,包括需要的文件及其模式
- 如何從頭創建一個內容簡單而有效的 EPUB 文件
- 使用開放源代碼工具從 DocBook 生成 EPUB 文件,DocBook 是一種常見的技術文檔和圖書模式
- 使用 Python 和 DocBook 自動轉換成 EPUB
本教程對操作系統沒有特殊要求,但是應該熟悉創建文件和目錄的機制。建議使用 XML 編輯器或者集成開發環境(IDE)。
對于本教程后半部分的 EPUB 創建自動化內容,需要讀者了解基本的 XML 處理技巧 — XSLT、 DOM 或者基于 SAX 的解析 — 并熟悉使用 XML 原生 API 構造 XML 文檔。
閱讀本教程不需要熟悉 EPUB 文件格式。
嘗試本教程中的例子,需要一個 Java 解釋器(1.5 或更高版本)和 Python 解釋器(2.4 或更高版本)以及相應的 XML 庫。不過,有經驗的 XML 開發人員很容易將這些例子修改為適合任何編程語言和 XML 庫。
/////////////////////////////////////////////////////////////////////////////////////////////Page2
關于 EPUB 格式
了解 EPUB 的背景,EPUB 最適合做什么,以及 EPUB 和便攜式文檔格式(PDF)的區別。
EPUB 是可逆的數字圖書和出版物 XML 格式,數字出版業商業和標準協會 International Digital Publishing Forum (IDPF) 制定的標準。IDPF 于 2007 年 10 月正式采用 EPUB,隨后被主流出版商迅速采用。可以使用各種開放源代碼或者商業軟件在所有主流操作系統、Sony PRS 之類的 e-ink 設備或者 Apple iPhone 之類的小型設備上閱讀 EPUB 格式。
雖然最早采用 EPUB 的是傳統的印刷品出版商,但是這并不妨礙它在電子圖書中的應用。利用免費的軟件工具,可以將網頁捆綁成 EPUB,轉化成文本文件或者將原有的 DocBook XML 文檔轉化成結構良好的、有效的 EPUB(后一點將在 從 DocBook 到 EPUB 一節討論)。
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 解決了 PDF 和開發人員友好性有關的所有瑕疵。一個 EPUB 就是一個簡單 ZIP 格式文件(使用 .epub 擴展名),其中包括按照預先定義的方式排列的文件。如何制作 ZIP 文檔有一些技巧,稍后將在 將 EPUB 文件捆綁為 ZIP 文檔 一節介紹。除此以外,EPUB 非常簡單:
- EPUB 中的所有內容基本上都是 XML。EPUB 文件可使用標準 XML 工具創建,不需要任何專門或者私有的軟件。
- EPUB 內容(eBook 的具體內容)基本上都是 XHTML 1.1(另一種格式是 DTBook,為視力受限者編碼書籍的一種標準。關于 DTBook 的更多信息請參閱參考資料,本教程中不涉及這部分)。
- 大多數 EPUB XML 模式都來自現成的、可免費獲得的、已發布的規范。
最關鍵的在于 EPUB 元數據是 XML,EPUB 內容是 XHTML。如果您的文檔構建系統產生的結果用于 Web 和/或基于 XML,那么也可用于生成 EPUB。
/////////////////////////////////////////////////////////////////////////////////////////////Page3
創建第一個 EPUB
最小的 EPUB 包至少要包含幾個文件。規范對于 EPUB 包中這些文件的格式、內容和位置要求可能很嚴格。這一節討論使用 EPUB 標準必須 了解的基礎知識。
小型 EPUB 文件的基本結構遵循 清單 1 所示的樣式。準備好分發之前,整個目錄結構被壓縮到一個 ZIP 格式文件中,幾點特殊要求將在 用 ZIP 打包 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,文件內容如下:
application/epub+zip |
要注意,mimetype 文件不能包含新行或者回車。
此外,mimetype 文件必須作為 ZIP 檔案中的第一個文件,而且自身不能壓縮。用 ZIP 打包 EPUB 文件 一節將介紹如何使用一般的 ZIP 參數將其包含進來。現在創建該文件并保存,并確保它在 EPUB 項目的根目錄中。
EPUB 根目錄下必須包含 META-INF 目錄,而且其中要有一個文件 container.xml。EPUB 閱讀系統首先查看該文件,它指向數字圖書元數據的位置。
創建目錄 META-INF。在其中創建一個新文件 container.xml。container 文件非常小,但是對結構要求很嚴格。將 清單 2 中的代碼粘貼到 META-INF/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 目錄。
mimetype 和 container 是 EPUB 檔案中僅有的兩個需要嚴格限制位置的文件。建議(盡管不是必須的)將其他文件保存到 EPUB 的子目錄下(按照慣例,通常被稱為OEBPS,即 Open eBook Publication Structure,但不是必須的)。
接下來在 EPUB 項目中創建目錄 OEBPS。本教程下一節將介紹 OEBPS 中的文件 — 數字圖書的核心:元數據和頁面。
盡管該文件名沒有特殊要求,但通常被稱為 content.opf。它指定了圖書中所有 內容的位置,如文本和圖像等其他媒體。它還給出了另一個元數據文件,內容的 Navigation Center eXtended (NCX) 表。
該 OPF 文件是 EPUB 規范中最復雜的元數據。創建 OEBPS/content.opf 并粘貼 清單 3 所示的內容。
<?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 文檔本身必須使用名稱空間 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 的內容就足夠了。
... <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 文件的一部分。
OPF manifest 列出了 EPUB 內容(不包括元數據)中的所有資源。就是說,通常是組成電子圖書文本的一組 XHTML 文件再加上一些相關的媒體如圖像。EPUB 鼓勵使用 CSS 設定圖書內容的樣式,因此 manifest 中也包含 CSS。進入數字圖書的所有文件都必須在 manifest 中列出。
清單 5 顯示了 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> ... |
第一項 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 目錄中,因此不需要路徑信息。
manifest 告訴 EPUB 閱讀器哪些文件屬于檔案,spine 則指定這些文件出現的順序或 — 按照 EPUB 的說法 — 數字圖書的線性閱讀順序。可以將 OPF spine 看作是書中 “頁面” 的順序。按照文檔順序從上到下依次讀取 spine。清單 6 顯示了 OPF 文件的一個片段。
... <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
中的第一項。
OPF 內容文件的最后一部分是 guide。這一節是可選的,但最好保留。清單 7 顯示了 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 和 OPF 元數據的交叉
由于 NCX 源自其他標準,使用 NCX 編碼的信息和 OPF 內容之間存在重復。如果通過程序生成 EPUB,這算不上什么問題,因為同樣的代碼可輸出到兩個文件中。兩個位置的信息要一致,不同的 EPUB 讀者可能使用不同位置的值。
盡管 OCF 文件是作為 EPUB 本身的一部分定義的,但最后一個主要的元數據文件參照了不同的數字圖書標準。DAISY 是一個專門為不能使用傳統書籍的讀者設計數據格式的組織,通常是因為視力受損或者不便于使用印刷的書籍。EPUB 借用了 DAISY 的 NCX DTD。NCX 定義了數字圖書的目錄表。復雜的圖書中,目錄表通常采用層次結構,包括嵌套的內容、章和節。
使用 XML 編輯器創建 OEBPS/toc.ncx 并粘貼 清單 8 所示的代碼。
<?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> |
DTD 要求 NCX <head>
標記中包含四個 meta
元素:
uid
: 數字圖書的惟一 ID。該元素應該和 OPF 文件中的dc:identifier
對應。depth
:反映目錄表中層次的深度。該例只有一層,因此是 1。totalPageCount
和maxPageNumber
:僅用于紙質圖書,保留 0 即可。
docTitle/text
的內容是圖書的標題,和 OPF 中的 dc:title
匹配。
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 文件的例子。
清單 9 包含了一個有效的 EPUB 內容頁。將其作為標題頁(title.html),用一個類似的頁面作為主要內容頁(content.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 規范將圖書裝訂起來,并看看如何進行驗證。
/////////////////////////////////////////////////////////////////////////////////////////////Page4
打包和檢查 EPUB
現在,應當可以對 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 標準并不很難,但其 XML 文件必須符合特定的模式。如果使用模式感知的 XML 編輯器生成元數據和 XHTML,就能事半功倍。對 EpubCheck 包進行最后檢查(參見 參考資料)。
Adobe 負責維護 EpubCheck 包,它是采用 Berkeley Software Distribution (BSD) 許可證的開源項目。它是一個可以作為獨立工具、Web 應用程序運行的 Java 程序,或者可以將它集成到在 Java Runtime Environment (JRE) 1.5 或更高版本下運行的應用程序中。
在命令行中運行非常簡單。清單 12 給出了一個例子。
$ java -jar /path/to/epubcheck.jar my-book.epub |
如果沒有創建輔助文件或者元數據文件出錯,可能會看到 清單 13 所示的錯誤消息。
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 閱讀器可供選擇。圖 1 顯示了 Adobe Digital Editions (ADE) 的屏幕截圖,這是最常用的 EPUB 閱讀器。
字體顏色和圖像都顯示出來了,不錯。ADE 未能用 sans-serif 字體正確地顯示標題,不過這可能是 CSS 的問題。這時候最好換一個閱讀器試試。圖 2 是用我自己編寫的、開放源代碼的、基于 Web 的 EPUB 閱讀器 Bookworm 顯示的同一本書。
這里的問題在于 ADE 不支持這種特殊聲明。如果數字圖書的格式非常重要,那么就必須了解不同閱讀軟件的特點。
前面我們費了很大力氣手工創建了一個簡單的 EPUB,現在看看如何將一種常見的 XML 文檔 DocBook 轉換成 EPUB。
/////////////////////////////////////////////////////////////////////////////////////////////Page5
從 DocBook 到 EPUB
DocBook 是需要維護大型技術文檔的開發人員常用的選擇。與傳統字處理程序生成的文件不同,可以使用基于文本的版本控制系統管理 DocBook 輸出。由于 DocBook 是 XML,很容易將其轉換成不同輸出格式。2008 年夏天出現了正式的 DocBook XSL 項目,將 EPUB 作為一種輸出格式。
從一個簡單 DocBook 文檔開始,如 清單 14 所示。該文檔的類型為 book
,包括前言、兩個章節以及標題頁面中內聯顯示的圖像。圖像和 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 所示。
$ 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 |
然后添加 mimetype 文件并建立 epub+zip 檔案。清單 16 顯示了這三個命令和通過 EpubCheck 驗證程序的結果。
$ 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
利用 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 調用 XSLT 的最有效辦法是事先解析 XSLT,然后創建反復使用的轉換器。這樣很方便,因為我的 DocBook-to-EPUB 需要轉換多個 DocBook 文件。如 清單 17所示。
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 的路徑。
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')) |
DocBook XSL 不會創建 mimetype 文件,不過 清單 19 中所示的代碼可以完成這項任務。
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() |
現在只需要將文件打包成有效的 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() |
好了!切記要進行驗證。
/////////////////////////////////////////////////////////////////////////////////////////////Page6
結束語
上一節 中的 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 是為具有圖書長度的文檔設計的,因此非常適合越來越多的在線或者漸進式編程圖書。
來自:http://www.cnblogs.com/linlf03/archive/2011/12/15/2285953.html
posted on 2011-12-28 10:57 狼愛上貍 閱讀(779) 評論(0) 編輯 收藏 所屬分類: EPUB