close

前陣子買了一本電子書,沒想到 epub 檔只不過是紙本的圖檔打包而已。充斥白邊在紙上或許有某種美感,但是在閱讀器上實在有點惱人,所以我就用 PIL 做了適當的剪裁,然後再用 ebooklib 重新打包 epub。因為對 ebooklib 不那麼熟,花了一點點時間才搞定,程式碼放在這裡給其他有類似需求的人參考。

這裡要說明一下,一般來說圖檔打包成 cbz 已經足夠了,但我這本是文字書,還是希望有個完善的目錄。雖然 Calibre 可以直接將 cbz 轉成 epub,但是還是有些不盡理想的地方,首先是圖片自動縮放的問題,再來是目錄的問題。當然還有一個因素是我忍不住愛折騰。

本程式會為每一張圖檔生成對應的 xhtml,例如 005.jpg 對應 005_p.xhtml,然後把 xhtml 加到 spine 裡面。

主要使用的技巧是包一層 svg,用 viewBox 屬性自動縮放影像大小,這點必須仰賴閱讀器支援 svg。必須提醒一下, ebooklib 會擅自把 viewBox 屬性名稱改成全小寫的 viewbox,以致於在某些閱讀器上失效 ,所以生成 epub 檔之後還是得解開然後用全域搜尋工具把大小寫改回來,算是比較鳥的地方。

由於只是隨用即丟程式碼,細節就不太講究,有需要的人自己動手改吧。

import uuid
from pathlib import Path
from ebooklib import epub
from string import Template

srcDir = Path('path/to/images')
author = 'Book Author'
bookName = 'Book Name'
lang = 'zh-TW'

# assume that all images have the same size.
# otherwise, better get them using tools like PIL.
imageWidth, imageHeight = 840, 1280

toc = ( # epub.Link(href, title, uid)
    epub.Link('007_p.xhtml', u'序言', 'ch-intro'),
    epub.Link('012_p.xhtml', u'第一章', 'ch-1'),
    epub.Link('321_p.xhtml', u'參考書目', 'ch-ref'),
    )


pageTempl = Template("""<body><div>
  <svg xmlns="http://www.w3.org/2000/svg"
      xmlns:xlink="http://www.w3.org/1999/xlink"
      width="100%" height="100%"
      viewBox="0 0 $IMG_WIDTH $IMG_HEIGHT">
    <image width="$IMG_WIDTH" height="$IMG_HEIGHT"
        xlink:href="image/$IMG_NAME" />
  </svg>
</div></body>""")

style = """html, body, svg {
  margin: 0;
  padding: 0;
  font-size: 0;
}"""


def addImageItems(book):
    imageList = []
    for imagePath in srcDir.glob('*.*'):
        with open(imagePath, 'rb') as f:
            print("adding...", imagePath)
            item = epub.EpubItem(uid=imagePath.stem,
                    file_name='image/' + imagePath.name,
                    content=f.read())
            book.add_item(item)
            imageList.append(imagePath)
    return imageList

def createPageItems(book, imageList):
    pageList = []
    for imagePath in imageList:
        pageId = imagePath.stem + '_p'
        page = epub.EpubHtml(uid=pageId, file_name=pageId+'.xhtml')
        page.set_language(lang)
        page.set_content(pageTempl.substitute(
                    IMG_NAME=imagePath.name,
                    IMG_WIDTH=imageWidth,
                    IMG_HEIGHT=imageHeight));
        page.add_link(href='style/main.css', rel='stylesheet', type='text/css')
        book.add_item(page)
        pageList.append(pageId)
    book.spine = pageList

book = epub.EpubBook()
book.set_identifier(str(uuid.uuid4()))
book.set_title(bookName)
book.set_language(lang)
book.add_author(author)

imageList = addImageItems(book)
createPageItems(book, imageList)

css = epub.EpubItem(uid="main_css",
            file_name="style/main.css",
            media_type="text/css",
            content=style)
book.add_item(css)

book.toc = toc
book.add_item(epub.EpubNav())
book.add_item(epub.EpubNcx())

outName = "[{}]{}.epub".format(author, bookName)
epub.write_epub(outName, book)
arrow
arrow
    全站熱搜

    novus 發表在 痞客邦 留言(0) 人氣()