參考原文:EPUB彈出窗口式腳注
網上搜到一些國學典籍的EPUB版,雖有古人的注解,但正文和注解混排在一起,當我只想迅速讀正文的時候比較礙眼。於是研究了一下 EPUB3 中有關腳注(footnote)的規格定義,寫了一個 Python 腳本把所有混在正文中的腳注全部改寫成了彈出窗口樣式,在 iBooks 里測試通過,略記一筆。
什么是EPUB彈出窗口式腳注
彈出式腳注是 EPUB3 推出的,簡單的說就是正文中加一個鏈接錨點,對應一個腳注模塊,點擊鏈接的時候,腳注內容會直接以彈出窗口的形式顯示出來。這樣就省去了頁面跳轉這個步驟,更加方便閱讀。
一圖勝千言,下圖是腳本處理后的《三國志》(這個還是混排版的)在 iPad 版 iBooks 下的效果。(原本是像日文電子書那樣的豎排EPUB,我把 CSS 里和豎排相關的定義注釋掉了)
如何實現EPUB彈出窗口式腳注
要實現這種效果,有三個注意點。
1.正文中的鏈接錨點。
<p>
太祖武皇帝,沛國譙人也,姓曹,諱操,字孟德,漢相國參之後。
<a epub:type="noteref" href="#fn1">
<sup>1</sup>
</a>
桓帝世,曹騰為中常侍大長秋,封費亭侯。
......
</p>
2.腳注aside模塊
<aside epub:type="footnote" id="fn1">
〔曹瞞傳曰:太祖一名吉利,小字阿瞞。王沈魏書曰:其先出於黃帝。當高陽世,陸終之子曰安,是為曹姓。周武王克殷,存先世之後,封曹俠於邾。春秋之世,與於盟會,逮至戰國,為楚所滅。子孫分流,或家於沛。漢高祖之起,曹參以功封平陽侯,世襲爵士,絶而復紹,至今適嗣國於容城。〕
</aside>
在 iBooks 下,如果 epub:type
屬性的值為 footnote
,這個 aside
會默認隱藏。只有對應的鏈接被點擊時,其內容才會在彈出窗口中顯示。
3.epub 命名空間(namespace)。
上面兩處都有一個共同的屬性名,epub:type
。一般 EPUB 文檔都沒有定義 epub 這個命名空間,所以滿足以上兩點之后直接打開會提示 epub 命名空間沒有定義。EPUB 定義 namespace 有兩種方式,一種是在 CSS 里定義,一種是在內容頁的HTML標簽里定義。我測試過,iBooks 無法識別 CSS 里定義的 namespace,所以我采用了另外一種方式。
<html xml:lang="zh-CN" xmlns="http://www.w3.org/1999/xhtml" xmlns:xml="http://www.w3.org/XML/1998/namespace" xmlns:epub="http://www.idpf.org/2007/ops">
用Python腳本處理EPUB的HTML文檔
了解了這些基本概念之后,再來看要處理的對象。原EPUB文檔中注解夾雜在正文中,以span
標簽標記,所以 Python 腳本的基本流程就比較清楚了,這里使用 BeautifulSoup 來解析並更改 HTML 文檔樹。
- 循環讀入所有 EPUB 內容文檔並解析
- 給
html
標簽加上 epub 命名空間定義 - 獲取
p
標簽下的 所有span
標簽 - 遍歷獲取的
span
標簽,取出文本,並以此創建aside
模塊 - 清除
span
標簽的內容,更改為鏈接錨點
原 EPUB 中的 HTML 文檔節選 :
<p>太祖武皇帝,沛國譙人也,姓曹,諱操,字孟德,漢相國參之後。
<span class="zhushi">
〔曹瞞傳曰:太祖一名吉利,小字阿瞞。王沈魏書曰:其先出於黃帝。當高陽世,陸終之子曰安,是為曹姓。周武王克殷,存先世之後,封曹俠於邾。春秋之世,與於盟會,逮至戰國,為楚所滅。子孫分流,或家於沛。漢高祖之起,曹參以功封平陽侯,世襲爵士,絶而復紹,至今適嗣國於容城。〕
</span>
桓帝世,曹騰為中常侍大長秋,封費亭侯。
......
</p>
在 OS X 版 iBooks 中的顯示效果,正文注釋混排。
詳細處理方式見下面的 Python 代碼 :
# -*- coding: utf-8 -*- from bs4 import BeautifulSoup import os epub_content_path = 'E:\MachineLearning\sanguozhi_bak.epub\OEBPS\Text' #zizhitongjian_path='E:\MachineLearning\zizhitongjian.epub\OEBPS\Text' #for f in os.listdir(zizhitongjian_path): for f in os.listdir(epub_content_path): #html = os.path.join(zizhitongjian_path,f) html = os.path.join(epub_content_path,f) print html doc = open(html,'rb') soup = BeautifulSoup(doc) #如果沒有HTML標簽里沒有定義epub namespace,則加上 if not 'xmlns:epub' in soup.html.attrs: soup.html['xmlns:epub'] = "http://www.idpf.org/2007/ops" #沒有使用soup.find_all('span')是為了略過<span>中內嵌<span>的情況 #???假如有嵌套的怎么辦呢? #因為原文里面是每一句話后面都有一個注釋(zhushi) notes = soup.select('p > span') #如果沒有找到span標簽,進入下一個循環,也就是進入下一個html文件 if not notes: continue #反序循環notes 列表 #len(notes)統計有多少個zhushi(note) for n in range(len(notes)-1, -1, -1): #n是列表index,number是實際注釋序號 note = notes[n] number = n+1 footnote = soup.new_tag('aside') footnote['epub:type'] = 'footnote' footnote['id'] = 'fn%d' % number footnote.string = note.get_text() #-----------------------change by cici------------------------------------------------- #-----------------------add a new tag:腳注前綴標識---------------------------------- ##需要在腳注前面加上這句話,可以跳轉到原文里面去<a href="#fns1">[1]</a> ##<a href="#fns1">[1]</a> #change by cici footnotesup = soup.new_tag('a') footnotesup['href'] = '#fns%d' % number footnotesup.string = '['+str(number)+']' #--------------------------------------------------------------------------------------- #---------------------------在注釋的前邊假如數字[1]-------------------------------- #下面這句是在footnote的string前面加入一個tag footnotesup footnote.string.insert_before(footnotesup); #--------------------------------------------------------------------------------------- #為了保證aside模塊是按數字順序逐一插入到段落之后,所以反序讀取notes列表 note.parent.insert_after(footnote) #--------------------------------------------------------------------------------------- #note.parent.insert_after(footnotesup) #--------------------------------------------------------------------------------------- note.clear() note.name = "a" del note['class'] note['epub:type'] = 'noteref' note['href'] = '#fn%d' % number #自己修改對上標假如id note['id'] = 'fns%d' % number sup = soup.new_tag('sup') sup.string = str(number) note.append(sup) #print soup.prettify() doc.close() doc = open(html,'wb') doc.write(str(soup)) doc.close()
注釋錨點的美化
為了讓鏈接錨點看起來美觀一點,我順手在CSS里給sup
添加了幾個定義。其中 text-indent
是為了重置原CSS代碼中 p
標簽中的定義,其他的就沒什么好說的了。
sup { font-family: Arial; font-size: 0.5em; color:#FFF; background-color: #333; display: inline-block; border-radius:0.25em; /* reset text-indent */ text-indent: 0; padding:0 0.5em; box-shadow: 0px 1px 1px #333; text-shadow: 0 -1px 0 #333; }
其實在 epub 的 CSS 里定義顏色是一件不太好的事,以 iBooks 為例,主題分純白、棕褐、夜間三種模式,如果 hardcode 顏色,主題變更時顏色不隨之變化就會很難看。不過 iBooks 也似乎沒有提供一個辦法來解決這種矛盾,所以作罷。
iBooks 對 EPUB3 標准的支持
iBooks 對 EPUB3 的支持也並不完全,除了上文提到的CSS命名空間之外,aside
的 CSS 樣式 iBooks 也不支持,此外還有很多槽點。好像這也是蘋果的一貫風格——把現有的處於上升趨勢的技術拿來為我所用,然后搞一個私有的變種出來,至於標准,就隨便隨便啦。
蘋果現在對 iBooks 似乎也不是很上心了,可能在電子出版方面遇到的阻力很大,沒有幫主的現實扭曲立場,在可見的未來也不太可能復制當年在音樂出版上的成功,於是 iBooks 的臭蟲一堆也沒人修復,新功能也不見有什么添加,似乎已經很久沒有更新了。
可惜現在網上流傳的四書五經、二十四史之類的 EPUB 制作良莠不齊,HTML定義也不盡相同,所以沒法弄一個通用的腳本出來,只能見招拆招。
注意事項:
- 在制作這個之前要求電腦上已經安裝了Python2.6(或2.7),沒有測試Python3.4版本,並且已經安裝了BeautifulSoup庫。
- 因為EPUB屬於一種壓縮文件,需要先將EPUB文件加上".zip"后綴,然后用Winrar或7zip將其解壓成***.epub文件夾,再進行代碼里面的操作,不然的話,Python程序不能打開EPUB壓縮文件。最后用Python程序處理完成之后,同樣的過程用7zip將幾個文件夾打包壓縮成zip壓縮包(EPUB閱讀器不識別rar壓縮的格式),然后將后綴“.zip”去掉就可以了。
對於原作者提供的代碼和文件做了一定的修改,現在講修改的部分貼出來。
1.第43-55行,在腳注的文本前面加上了<a href="#fns1">[1]</a>,實際顯示為[1]。點擊可以跳轉到腳注對應的原文。
#-----------------------change by cici------------------------------------------------- #-----------------------add a new tag:腳注前綴標識---------------------------------- ##需要在腳注前面加上這句話,可以跳轉到原文里面去<a href="#fns1">[1]</a> ##<a href="#fns1">[1]</a> #change by cici footnotesup = soup.new_tag('a') footnotesup['href'] = '#fns%d' % number footnotesup.string = '['+str(number)+']' #--------------------------------------------------------------------------------------- #---------------------------在注釋的前邊假如數字[1]-------------------------------- #下面這句是在footnote的string前面加入一個tag footnotesup footnote.string.insert_before(footnotesup);
2.第70-71行,加上了上標的id,腳注可以通過這個id跳轉到原文中。
#自己修改對上標假如id note['id'] = 'fns%d' % number
參考文章:
后記:效果圖
最后修改完成圖書《三國志-陳壽》(共享密碼:17ib),下圖是在Windows+Calibre上現實的效果圖。
后續增加:
發現多看的手機和Kindle版本的多看是可以顯示腳注的,可能和EPUB3.0的實現方式不同,就跟iBOOKS是類似的,不過這樣的確是增加了一個選擇的機會。
在博客園非官方月刊這篇文章中作者制作的電子書的確是可以顯示腳注的,而且顯示的效果還是不錯的,多看是目前在國內發現的性能和體驗最好的電子書閱讀器APP。
多看也在自己的官方論壇貼出了關於如何制作帶有腳注EPUB電子書的方法:多看電子書規范擴展開放計划
顯示效果如下圖(在Android手機平台):