電子書的規範
由於公司有開發電子書的需求,遂希望有一個便於製作電子書的平台,功能不外乎有匯入文章、編輯文章、編輯目錄與書籍資訊⋯⋯等,最後『一鍵匯出電子書』。功能本身不難,但要先理解電子書的架構,確保符合電子書規範;以前未接觸過這領域,所以簡單紀錄一下學習內容。
電子書的架構
一本電子書 epub 主要由三個規範組成,分別是 OCF
、OPF
與 OPS
:
OCF (Open Container Format)
以 zip 封裝所有檔案,完成一個 epub 容器。
OPF (Open Packaging Format)
以 xml 描述 epub 的檔案結構,連結各種資源。
OPS (Open Publication Structure)
以 xhtml 建構電子書內容,確保內容一致性。
總結來說,一個 epub 先遵守最底層的 OPS 規範來製作內容,接著透過 OPF 規範組織內容,最後以 OCF 規範完成封裝,形成一個 epub 檔。
也由於 epub 遵循 OCF 規範以 zip 封裝,因此也可以反封裝 epub,得到下面的檔案結構:
- META-INF
- container.xml
- OEBPS
- content.opf
- toc.ncx
- xxx.xhtml
- mimetype
提醒
mimetype 與 container.xml 為 OCF 所規範,檔名與檔案位置皆不可改。
content.opf 為 OPF 所規範,檔名與檔案位置未嚴格限制,但多數電子書開發工具習慣以此命名,並與 OPS 所規範的檔案合併在 OEBPS (Open eBook Publication Structure) 目錄。
mimetype
mimetype 對應 OCF 規範,主要用來宣告電子書的檔案類型,內容僅有一行:
application/epub+zip
注意
mimetype 檔名不可更改,且內容不能包含空格與其他空白行。
container.xml
container.xml 同樣對應於 OCF 規範,是電子書閱讀軟體優先讀取的檔案,主要紀錄 opf 檔的位置,引導電子書閱讀軟體讀取 opf 檔,進而獲取 OPS 內容( opf 檔主要描述這本電子書包含哪些受 OPS 規範的內容)。檔案內容如下:
<?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>
基本上,唯一會改的可能只有 <rootfile>
的 full-path
這個屬性,要注意的是其路徑是相對於整個電子書的專案頂層,而不是相對於 META-INF 目錄。
content.opf
content.opf 顧名思義,對應的是 OPF 規範,主要紀錄電子書內容資源的位置,指引電子書閱讀軟體找到屬於 OPS 所規範的檔案。內容大致如下:
<?xml version='1.0' encoding='utf-8'?>
<package version="2.0"
xmlns="http://www.idpf.org/2007/opf"
xmlns:dc="http://purl.org/dc/elements/1.1/"
unique-identifier="book_id">
<metadata>
<dc:title>...</dc:title>
<dc:identifier id="book_id">...</dc:identifier>
</metadata>
<manifest>
<item id="ncx" href="toc.ncx"
media-type="application/x-dtbncx+xml"/>
<item id="ch1" href="ch1.xhtml"
media-type="application/xhtml+xml"/>
...
</manifest>
<spine toc="ncx">
<itemref idref="ch1"/>
...
</spine>
</package>
<package> schemas & namespace
epub 是由國際數位出版論壇 (International Digital Publishing Forum, IDPF) 所提出的電子書格式,因此須引用 http://www.idpf.org/2007/opf 為命名空間;此外在描述 <metadata>
時也引用了 Dublin Core 的元素集,因此也須加入 http://purl.org/dc/elements/1.1/。
<metadata>
Dublin Core 定義一組常用項目來描述多種數位內容(不單為了電子書),epub 引入了這些項目,因此可在 <metadata>
中定義:
title
及 identifier
為必要項目,依照規範 identifier
必須是唯一值,出版商一般會使用含 ISBN 在內的一組字串;其他書籍創造者則使用一組隨機產生的 UUID 填寫。
提示
<dc:identifier>
的 id
須對應 <package>
的 unique-identifier
。
<manifest>
電子書的『內容』一般是編輯在 xhtml 裡面,<manifest>
負責指出所有在電子書專案中與這些內容相關的檔案。要注意的是:除了 xhtml 本身之外,該檔案所引入的 css、image⋯⋯等樣式等、媒體檔,也要列入裡面。
提示
每個檔案都必須賦予一個 id
,並標記該檔案的位置 (href) 與媒體類型 (media-type),幫助閱讀軟體能夠找到並打開該檔案。
除了與內容相關的檔案之外,<manifest>
還會列出一個與 metadata 相關的檔案:toc.ncx。
<spine>
條列完這本電子書有哪些內容資源後,接下來要想辦法組織、編排,便在 <spine>
處理,告訴閱讀軟體這些內容資源的讀取順序,也就是我們讀者的閱讀順序。
提示
<spine>
的 toc
必須與 <manifest>
中指向 toc.ncx 的 <item>
的 id
一致。
每個 <itemref>
都要有一個 idref
,同樣要與 <manifest>
中所對應的 <item>
的 id
一致。
toc.ncx
僅管前述的 content.opf 已經定義了大多數 metadata,但 epub 還同時借用了 DAISY 的 NCX (Navigation Center eXtended) DTD 來定義書籍的目錄表格,以應付複雜的書籍目錄 (例如分層章節這種巢狀目錄結構)。大致內容如下:
<?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="..."/>
<meta name="dtb:depth" content="1"/>
<meta name="dtb:totalPageCount" content="0"/>
<meta name="dtb:maxPageNumber" content="0"/>
</head>
<docTitle>
<text>...</text>
</docTitle>
<navMap>
<navPoint id="navpoint-1" playOrder="1">
<navLabel><text>Chapter 1</text></navLabel>
<content src="ch1.xhtml"/>
</navPoint>
...
</navMap>
</ncx>
<meta>
toc.ncx 須在 <head>
加入 4 個 <meta>
元素:
uid
為電子書的唯一ID,應與 content.opf 中的dc:identifer
一致。depth
為目錄層級。totalPageCount
與maxPageNumber
主要應用於紙本書籍,因此在電子書這邊可設為 0 就好。
<docTitle>
電子書的書籍名稱,應與 content.opf 中的 dc:title
一致。
<navMap>
主要描述電子書內容的順序,是 ncx 檔中最重要的一個部分。它包含了多個 <navPoint>
元素,每個 navPoint 相當於目錄節點,其 <navLabel>
底下的 <text>
內容即節點標題,而 <content>
則對應該節點所連結的檔案資源。
說明
ncx 的 <navMap>
與 opf 的 <spine>
皆描述了電子書內容的順序,然而實際上有很大的差異,較好的解讀方式是用紙本書類比:
opf
<spine>
的描述相當於紙本書的『裝訂』,將書的各章節『實際』裝訂在一起。例如將第一章與第二章綁定,那麼在閱讀到第一章的最後一頁時,翻下一頁即會接續閱讀第二章的第一頁。ncx
<navMap>
的描述則是書籍的目錄,目錄所顯示的編排順序未必等同於實際的裝訂順序。今天也許你點了目錄的第一篇文章,實際上卻跳到了書籍的最後一篇;明明目錄顯示後面還有文章,卻無法透過翻下一頁跳轉,只能點回目錄重新選下一篇文章跳轉。
xxx.html
在電子書內容的編輯方面,主要以 xhtml 為主,其開發方式與 html 類似,大致內容如下:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>BOOK TITLE</title>
<link type="text/css" rel="stylesheet" href="stylesheet.css" />
</head>
<body>
<!-- 將書籍內容以 html 元素呈現 -->
</body>
</html>
不過 epub 雖沒有對 xhtml 有特定規範,但與一般網頁開發相比還是有些要注意的地方,例如:
使用圖片時盡可能參考本地端圖檔,避免從外部連結引入,否則當閱讀裝置連不到網路時,電子書的圖片便無法呈現。
epub 3.0 雖已可執行 javascript,但 epub 並未要求每個裝置都必須能夠支援執行 js,因此非必要時仍盡量避免使用 script 區域。
epub 並非完全支援所有 css 的效果等。
封裝電子書
將所有內容都開發好後,透過 zip 指令封裝,就可以完成一本 epub 電子書了。
$ zip -0 -X xxx.epub mimetype
$ zip -9 -r xxx.epub */
注意
封裝時,mimetype 必須是 zip 內的第一個檔案,且本身不可被壓縮。
流式版型 (Float Layout)
前面紀錄了電子書的規範跟架構,接下來紀錄下電子書中幾個常用的版型,最常見的就是——流式版型。
隨著視窗流動
流式版型的特色,就是讓電子書像網頁一般,在不同螢幕大小的裝置讓文字重新編排,以最適於該裝置的形式提供閱讀。因此,同樣一篇文章,在電腦螢幕可能只有四十行,每行三十個字;到了手機螢幕卻變成有八十行,每行則只有十五個字,以適應手機螢幕寬度較窄但高度較高的閱讀方式。
甚至,有些裝置的閱讀器還會提供調整字體大小的功能,即使是不同的字體大小,流式版型也會重新調整文字排列,不會讓文章超出版面。
那麼…
要怎麼設定讓電子書成為流式版型呢?
什麼都不用做 (喂)
預設就是流式版型
電子書的格式之所以被提出,就是為了讓書本內容在不同電子裝置上也能被舒適地閱讀;OPS 規範中明確定義書的內容必須以 xhtml 建構,以確保內容在各裝置的一致性。因此,我們並不需要做什麼特別的設定,要的僅僅是對一些例外情況做 CSS 調整:
比如說⋯⋯ 中文小說的『竪排右書』排版
向左走向右走
儘管在網頁的世界中,大部分中文網頁都已經是『橫排左書』的排版方式,我們也都已經習慣『由左至右、由上而下』的閱讀順序;但在中文書籍中(特別是小說類書籍)仍有多數內容使用『竪排右書』、也就是『由上而下、由右至左』的閱讀順序排版。
為了讓內容在電子書上面以竪排右書的方式呈現,我們需要對 CSS 進行一些設定。epub 3 支援 CSS 3,令許多原本不可能的事情一下成真——例如改變文字的排列方向,調整 CSS 設定的writing-model
:
@namespace "http://www.w3.org/1999/xhtml";
@namespace epub "http://www.idpf.org/2007/ops";
html {
-ms-writing-mode: tb-rl;
-epub-writing-mode: tb-rl;
-webkit-writing-mode: tb-rl;
writing-mode: tb-rl;
}
提示
t、b、r、l 即 top、bottom、right、left。
提醒
在設定 writing-model
時,必須特別注意『連號前後的順序』,tb-rl 與 rl-tb 代表的是完全不同的意思:
tb-rl 為『由上至下,再由右至左』,是中文小說的常見排版。
rl-tb 為『由右至左,再由上至下』,會變成從右邊開始書寫,完成一個『橫排』之後再寫下一個橫排的奇妙版型。
眼睛往哪看
文字排列的方向不同,連帶影響的是閱讀方向也不同,當然書籍頁面的方向也會不同。一般來說,橫排左書的書籍頁面習慣由左至右,上一頁在左邊,下一頁在右邊,翻頁時是由右往左翻頁;竪排右書的書籍 (或是其他如圖畫書、漫畫等) 則相反,頁面習慣由右至左,下一頁在上一頁的左邊,翻頁時則是由左至右翻頁。
如何設置電子書的頁面方向?
只要在 OEBPS/content.opf 檔案中,編輯 <spine> 的 page-progression-direction 屬性為 ltr(由左至右,預設)或 rtl(由右至左)即可。
...
<spine toc="ncx" page-progression-direction="rtl">
<itemref idref="cover" linear="no"/>
<itemref idref="ch1"/>
...
</spine>
...
其他的小設定
在一本書籍當中,封面是重要的一環,許多人會將封面設置在 <metadata>
中,一些閱讀器也會從這邊抓取封面,以此作為在閱讀器書籍清單中顯示的封面。
封面頁除了在閱讀器的書籍清單顯示之外,也會作為書籍內容的一部分顯示在書籍當中。但是,讀者在進入書籍內容前,早已在書籍清單中看過封面,實在不需要在翻開書後再看一次封面,因此可以在 <spine>
中作為封面頁的 <itemref>
設置 liner
屬性為 no,意思為此項目不會出現在讀者的閱讀順序中,讀者進入這本書時將會忽略此項目,直接進入下一個內容。
...
<metadata>
...
<meta name="cover" content="img" />
</metadata>
<manifest>
...
</manifest>
<spine toc=”ncx” page-progression-direction="rtl">
<itemref idref=”cover” linear=”no”/>
...
</spine>
...
提示
<meta>
的 content
須與 <manifest>
中作為封面圖的 <item>
的 id
一致。
固定版型 (Fixed Layout)
流式版型雖然有利於在不同裝置間閱讀,但對於版型的可控性很低,較適合以文字為主的內容;對於一些需高度依賴版型的內容(例如以圖畫為主的圖文書),則需要採用另一種固定版型 (Fixed Layout) 才行。
定住不許動
有些書籍內容(例如圖文書)不像流式版型的內容,適合跟著螢幕的大小跟比例而調動;圖跟文必須緊密相連,才能保留原本要傳達的意義。為了因應這些內容,epub 提供了固定版型的設定,讓創作者能夠更好地發揮。
內容設定
固定版型的內容同樣遵循 OPS 規範,由 xhtml 建構;但與流式版型不同的是,除了 xhtml 之外,固定版型也支援 svg、bitmap images 等組成內容,因此創作者也可將圖文輸出成圖片來呈現。
不論以哪種檔案類型來編輯內容,首先要定義 ICB (initial containing block) dimensions,可看作是定義版型的大小 / 比例,以此來固定版型。
若內容是以 xhtml 建構,須在 <head>
中定義 viewport
的 meta
:
<head>
...
<meta name="viewport" content="width=1200, height=600"/>
...
</head>
而若是 svg,則定義其 viewBox
:
<svg xmlns="http://www.w3.org/2000/svg"
version="1.1" width="100%" height="100%"
viewBox="0 0 844 1200">
...
</svg>
提示
viewBox
的四個參數分別是 min-x、min-y、width、height。
最後若是 bitmap images,則會以圖片原始寬高來直接設定。
綁定內容順序
完成 OPS 所規範的內容之後,接著是 OPF 的部分,固定版型的 opf 檔與流式版型在設定方面有些許不同:
由於接下來在 <metadata>
中會設定 rendition
屬性,因此在 <package>
中加入 prefix
屬性,並將其對應到 http://www.idpf.org/vocab/rendition/ (當然原先在流式版型中的設定還是要有)。
<package ...
prefix="rendition: http://www.idpf.org/vocab/rendition/#">
<metadata>
...
<meta property="rendition:layout">pre-paginated</meta>
<meta property="rendition:orientation">auto</meta>
<meta property="rendition:spread">auto</meta>
</metadata>
...
<spine toc="ncx" page-progression-direction="rtl">
...
<itemref idref="s4" properties="rendition:page-spread-left"/>
...
</spine>
</package>
接著在 <metadata>
中設定 rendition
,共有 layout
、orientaion
、spread
三個面向:
rendition:layout
主要設定書籍內容的分頁版型,有兩種:
reflowable
書籍內容沒有被事先分頁,當螢幕足夠一次呈現兩頁時,系統會自動不斷地將下一頁遞補呈現。
pre-paginated
書籍的部份內容 (spine item) 有被事先分頁,通常適用於一張圖版面過大,不得被拆開成兩頁呈現時,確保這兩頁必須在同一面呈現。
rendition:orientation
設定閱讀裝置要以哪種方向開啟書籍內容:
auto
原始設定,依閱讀裝置當前的方向來開啟書籍內容。
landscape
強制閱讀裝置以水平模式開啟書籍內容。
portrait
強制閱讀裝置以垂直模式開啟書籍內容。
rendition:spread
設定當閱讀裝置為何種情況時,能夠雙頁顯示:
none
設定裝置在不論何種情況下都只能單頁呈現,不會雙頁顯示。
landscape
閱讀裝置只在水平模式下才會雙頁顯示。
portrait
閱讀裝置只在垂直模式下才會雙頁顯示。
both
不論閱讀裝置在何種情況下都能雙頁顯示。
auto
由閱讀系統自行判斷裝置是否適合雙頁顯示。
編輯目錄
編輯完 opf 之後,這本書基本上就已經成形了,接著提供目錄便大功告成。相較於以文字為主的流式版型,固定版型多是以圖為主,層級也都是一頁一頁的單層結構,不像流式版型得動用到巢狀結構的目錄。
打開 ncx ,一般流式版型是透過支援巢狀結構的 <navMap>
來定義目錄:
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
...
<navMap>
<navPoint id="navpoint-1" playOrder="1">
<navLabel><text>Chapter 1</text></navLabel>
<content src="ch1.xhtml"/>
</navPoint>
...
</navMap>
</ncx>
固定版型則是透過 <pageList>
來定義:
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">
...
<pageList>
<pageTarget type="normal" value="1">
<navLabel><text>1</text></navLabel>
<content src="s1.svg"/>
</pageTarget>
...
</pageList>
</ncx>
提示
在 pageTarget
中,value
指的是該頁的頁數,type
則有三種類型:
- front 通常指目錄頁、前言使用的羅馬數字。
- normal 是一般常用的阿拉伯數字。
- special 指其他特殊數字。
至此,一本固定式版型的電子書完成,最後透過 OCF 規範將檔案以 zip 形式封裝成 epub 後就結束了。
2019 更新
先前開發的 ncx 已是 epub 2 時代的產物,epub 3 改用 Navigation Document 來取代,詳細可參考官方文件。