EPUB 2 規格

2016-05-28

關於 EPUB 格式

一個 EPUB 是一個 ZIP 格式壓縮檔,它包含了一些依規定排序的檔案。

EPUB 裡所有的東西幾乎都是 XML,可以使用標準 XML 工具來建立 EPUB 檔案,不需要任何特殊或私有軟體。

EPUB 的內容 (電子書的實際文字) 幾乎永遠都是 XHTML version 1.1.

也就是說,EPUB metadata 是 XML,EPUB 內容是 XHTML。

基本結構

一個最小的 EPUB 檔案,它的基本結構如下:

mimetype
META-INF/
   container.xml
OEBPS/
  content.opf
  title.html
  content.html
  stylesheet.css
  toc.ncx
  images/
     cover.png

首先,建立一個資料夾,供這個 EPUB project 使用。

mimetype 檔案

這個很簡單:mimetype 檔是必要的,而且名稱必須是 mimetype.

這個檔案的內容永遠都是:

application/epub+zip

注意這個 mimetype 檔不能包含任何換行字元,包括 newlines 以及 carriage returns。

而且,這個 mimetype 檔必須是 ZIP 檔裡的第一個檔案。這在建立 ZIP 檔時可以使用參數指定。

META-INF/container.xml

在 EPUB 的根目錄必須要有一個 META-INF 資料夾,而且它必須包含一個名叫 container.xml 的檔案。EPUB 閱讀系統會先找到這個檔,因為它指向這本電子書的 metadata 所在位置。

這個 container 檔很小,但是它的結構要求很嚴格,把下列編碼複製到 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 資料夾。

在 EPUB 檔案裡,只有 mimetype 以及 container 檔案的位置是被嚴格控制的。依照建議 (但不是必須),將 EPUB 裡的其他檔案在存放在一個子資料夾中。 (按照慣例,通常是叫做 OEBPS 資料,它的意思是 Open eBook Publication Structure,但是你愛把它設為什麼名稱都可以。)

接下來,在 EPUB project 下建一立一個資料夾叫做 OEBPS。下面的章節說明 OEBPS 資料夾裡的檔案,這是這本電子書的實際內容:它的 metadata 以及它的頁面。

Open Packaging Format metadata file

雖然這個檔案可以是任何名字,但慣例將 OPF 檔命名為 content.opf。它指定了這本電子書裡所有內容的檔案位置,包含文字以及圖檔等其他媒體。它也指向另一個 metadata 檔案:Navigation Center eXtended (NCX) 目次。

OPF 檔是 EPUB 規格中最複雜的 metadata。建立 OEBPS/content.opf,並將以下內容複製到檔案裡:

<?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:0cc33cbd-94e2-49c1-909a-72ae16bc2658</dc:identifier>

    <dc:language>en-US</dc:language>
    <meta name="cover" content="cover-image" />
  </metadata>
  <manifest>
    <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+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="title.html" type="cover" title="Cover"/>
  </guide>
</package>

OPF schemas and namespaces

OPF 文件本身必須使用 http://www.idpf.org/2007/opf 做為 namespace,metadata 則是要在 Dublin Core Metadata Initiative (DCMI) 這裡 namespace 裡:http://purl.org/dc/elements/1.1/。

Metadata

Dublin Core 定義了一組 metadata 術語,讓你用來描述各種不同的數位材料;它本身並不是 EPUB 規格的一部分。這些術語都允許在 OPF metadata 區段中出現。當你要建立一個正式發佈用的 EPUB,儘可能在這裡提供詳細的資料,雖然現在使用下面這些就可以做為一個起始點了:

<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 規格,identifier 必須是一個唯一值,然而它是由電子書製作者來定義這個唯一值。對書籍發行者來說,這個欄位通常會是個 ISBN 碼或是國會圖書館編號。對於 EPUB 製作者來說,考慮使用一個 URL,或是一個大的、亂數產生的 unique user ID (UUID)。注意:根元素 package 的屬性 unique-identifier 的值,與 dc:identifier 元素的 ID 必須相符。

如果要使用 URL 做為 identifier,可以像這樣:

<dc:identifier opf:scheme="URL">http://www.learnjavasmartway.com/epub-test.html</dc:identifier>

其他考慮加入的 metadata:

  • dc:language 語言
  • dc:date 發行日期
  • dc:publisher 發行者 (這可以是你的公司或個人姓名)
  • dc:rights 版權資訊 (如果以 Creative Commons license 發佈,將版權 URL 放在這)

加上一個 meta 元素,name 屬性值為 "cover",這不直接是 EPUB 規格的一部分,但這是個建議的方式,可以讓封面頁、圖檔更有可攜性。 某些 EPUB 呈現程序偏好使用一個圖檔做封面,其他的則是使用一個含有 inline 封面圖片的 XHTML 檔。meta 元素的 content 屬性值必須是 manifest 裡的封面圖檔的 ID,manifest 是 OPF 檔案中在 metadata 的下一個部分。

Manifest

OPF manifest 列出 EPUB 中的所有資源 (不包含 metadata)。這通常意味著一個電子書文字的 XHTML 檔案清單,再加上圖片等相關媒體檔。EPUB 鼓勵使用 CSS 來做書本內容的樣式,所以 CSS 檔也應該包含在 manifest 裡面。每一個電子書會用到的檔案都必須列在 manifest 裡面。

...
<manifest>
  <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+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>
...

你必須包含第一個 item:toc.ncx (在下一節中討論)。注意所有的 items 都有一個適當的 media-type,XHTML 的 media-type 是 application/xhtml+xml。這個確切的值是必須的,而且不能是 text/html 或是其他的 type。

EPUB 支援四種圖檔格式:JPEG, PNG, GIF, SVG。

href 屬性值必須是相對於 OPF 檔的 Uniform Resource Identifier (URI)。(這很容易跟 container.xml 中參照到 OPF 檔的路徑搞混,它是相對於整個 EPUB 的路徑) 以目前的範例情況,OPF 檔跟內容一樣是放在 OEBPS 資料夾裡,所以不需要路徑資訊。

Spine

雖然 manifest 已經告訴 EPUB 閱讀器,哪些檔案是這個 EPUB 的一部分,spine 則是指出它們出現的順序,或者,以 EPUB 的術語來說,這本電子書的 linear reading order。也可以把 OPF spine 想成定義電子書「頁面」的順序。spine 會依文件順序由上往下讀取。

...
<spine toc="ncx">
  <itemref idref="cover" linear="no"/>
  <itemref idref="content"/>
</spine>
...

每一個 itemref 元素都必須有一個 idref 屬性,它必須對應到 manifest 裡的一個 ID。toc 屬性也是必須的,它指向 manifest 裡的一個 ID,而該元素必須指出 NCX 目次的檔名。

spin 裡的 linear 屬性用來指出該項目是 linear reading order 的一部分,或是 extraneous front- or end-matter。建議將封面定義為 linear=no。遵循 EPUB 標準的閱讀系統在開啟電子書的時候,會開啟 spin 中 linear="no" 以外的第一個 item。

Guide

OPF 內容檔的最後一個部分是 guide。這個區段不是必要的,但建議放。

...
<guide>
  <reference href="cover.html" type="cover" title="Cover"/>
</guide>
...

這個 guide 是提供語義資訊給 EPUB 閱讀系統的一種方式。manifest 定義 EPUB 裡的 physical resources,span 提供它們的順序資訊,guide 則是解釋這些區段的含義。以下是 OPF guide 裡所允許的值的部分清單:

  • cover:書的封面
  • title-page:含有作者、出版者資訊的頁面
  • toc:目次

NCX 目次

NCX 定義電子書的目次。對複雜的書籍來說,它通常是階層式的,包含巢狀的部分、章、節。

建立 OEBPS/toc.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:0cc33cbd-94e2-49c1-909a-72ae16bc2658"/>

    <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 metadata

DTD 要求 NCX 的 head 元素裡要有四個 meta 元素:

  • uid: 這本電子書的唯一 ID。這個元素必須與 OPF 檔裡的 dc:identifier 符合。
  • depth: 目次層級的深度。這個範例中只有一層,所以這個值是 1。
  • totalPageCount 以及 maxPageNumber:只適用於紙本書,可以直接給 0。

docTitle/text 是作品標題,符合 OPF 裡的 dc:title。

NCX navMap

navMap 是 NCX 檔案中最重要的部分,因為就是它定義這本書的目次。navMap 包含一個或多個 navPoint 元素,每個 navPoint 必須包含以下元素:

  • 一個 playOrder 屬性,它反應在文件中的閱讀順序。它依循 OPF spine 裡的 itemref 元素清單的相同順序。
  • 一個 navLabel/text 元素,它描述本書中這個區段的標題。這通常是一個章節標題或編號,例如「第一章」,或者本範例中的「封面頁」。
  • 一個 content 元素,它的 src 屬性指向包含這段內容的 physical resource。這應該是在 OPF manifest 中宣告的一個檔案。 (也可以指向 XHTML 內容中的某個 anchor,例如:content.html#footnote1)
  • 如果還有下層目次,那麼它下面可以再有一個或多個 navPoint 元素。

這個範例書本的結構很簡單:它只有兩個頁面,而且它們不是巢狀的。這表示你有兩個 navPoint 元素,它們的 playOrder 值是 1 跟 2。在 NCX 裡,你可以為這些區段命名,讓使用者可以跳到這本電子書中的不同位置。

加入最後的內容

現在你知道了 EPUB 中所有必須的 metadata,可以開始加入這本書的真正內容。你可以從這裡下載範例檔,或是自己建立檔案,只要檔案名稱符合 metadata。

接下來建立這些檔案以及資料夾:

XHTML and CSS in an EPUB book

下面是一個有效的 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 內容要遵循一些規則,對於一般的網頁開發者來說可能不太熟悉:

  • 必須是有效的 XHTML 1.1:XHTML 1.0 Strict 與 XHTML 1.1 之間唯一的顯著不同之處,是 name 屬性被移除了。 (使用 IDs 來指向內容裡的 anchors)
  • img 元素只能指向電子書裡包含的圖檔:不能指向網路上的圖檔
  • 避免使用 script 區塊:EPUB 閱讀並未被要求必須支援 JavaScript 程式碼。

EPUB 支援 CSS 的方式有一些小地方不一樣,但是都不會影響一般性的使用 (細節請參閱 OPS 規格)。下面示範一個簡單的 CSS 檔:

body {
  font-family: sans-serif;     
}
h1,h2,h3,h4 {
  font-family: serif;     
  color: red;
}

If you create technical documentation, this is probably not relevant, but developers who build EPUBs in multiple languages or for specialized domains will appreciate the ability to specify exact font data.

一個有趣的一點是,EPUB 特別支援 CSS2 的 @font-face 規則,允許內嵌字體。如果你是要建立技術文件,這可能無關緊要,但是對於要建立多語言或特殊領域 EPUB 的開發者來說,他們會感激能夠指定確切的字型資料。

現在,你已經準備好要用來建立你的第一個 EPUB 電子書的所有東西。下一節,我們將依據 OCF 規格打包這本電子書,並學會如何驗證它的有效性。

Package and check your EPUB

Bundling your EPUB file as a ZIP archive

EPUB 規格裡的 OEBPS Container Format 說了一些關於 EPUB 和 ZIP 的事,不過最重要的是:

  • ZIP archive 裡的第一個檔案必須是 mimetype 檔案,而且這個 mimetype 檔必須是沒有被壓縮的。這讓一些非 ZIP 工具能從 EPUB 包的位置 30 開始取得 mimetype。
  • 這個 ZIP archive 不能被加密。EPUB 支援加密,但不是在 ZIP 檔案這個層級。

在 UNIX®-like 作業系統下使用 ZIP version 2.3,如下使用兩個命命建立 EPUB ZIP 檔:(這些命令假設目前工作目錄是你的 EPUB project)

$ zip -0Xq my-book.epub mimetype
$ zip -Xr9Dq my-book.epub *

第一個命令建立新的 ZIP archive 並且以無壓縮的方式加入 mimetype 檔案。第二個命令加入其餘的項目。-X 及 -D 選項將 zip 檔案中無關緊要的資訊減到最小;-r 選項將以 recursive 方式加入META-INF 以及 OEBPS 資料夾下的所有內容。

EPUB validation

$ java -jar /path/to/epubcheck.jar my-book.epub