生成Kindle可讀的mobi和PDF電子書


購買kindle之后,自然欣喜萬分,不來自於工具本身,而來自於發現自己能夠靜下心來閱讀長篇和復雜的文字了,可喜可賀。更重要的是,kindle減輕了我眼睛的莫大的壓力。但馬上就出現幾個問題:

  • 不是所有的電子書都有kindle,最常見的是掃描PDF
  • 大量的論文無法閱讀,這和上面的問題一致
  • 網絡上很多精彩的博客,新聞,都是沒法閱讀的

可能有人說,用手機看不就得了?用手機看花邊娛樂新聞當然很好,可是當看數學推導時,推送欄上面妹子發來的消息,會直接把你的思路全部打亂。沒用過kindle的人,是有些難以體會那種接近於紙張的質感的。OK,既然是程序員,我們就嘗試解決這些問題。

有關kindlegen和HTML

kindlegen是亞馬遜官方出品的一個電子書生成工具。但它明顯就沒打算讓普通用戶使用,命令行界面,幾乎沒有任何像樣的文檔。只是在實例樣例里給了幾個生成電子書的文件。我就因為沒有文檔兜了大彎,翻遍國外各大網站,才慢慢摸清kindlegen的使用細節。
可以這么理解,KG是將一組HTML和相關文件,打包成mobi文件的工具。

最簡單的例子,隨意編寫一個HTML文件,送給KG,會生成對應的mobi。基本有title,h1,h2,正文,kindle渲染就差不多了。如果需要修改樣式,可以提供CSS文件。
但是,這樣的做法,沒有圖片,沒有超鏈接,無法提供目錄,如果輸入單一的大型HTML文件,kindle的渲染性能就不足了。

因此,需要生成層級化,多文件形式的html文件夾,然而kg並不能直接識別html文件夾,還是需要一些元數據描述。

編寫元數據文件

要想解決這個問題,就需要編寫兩個文件,opf和ncx, 他們可以理解為KG的makefile, KG通過這兩個文件索引HTML,目錄和其他多媒體資源。介紹如下:

image

值得注意的是,所有的文件都應該保存在本地,尤其是jpg, html中的圖片超鏈接,需要重定向到本地的jpg文件,如果依然在服務器上,據我所知,kg是不負責渲染下載的。

資源聚合文件: opf和ncx

由於opf文件非常重要,我們下面就講解opf的格式:

<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="BookId">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
<dc:title>電子書標題</dc:title>
<dc:language>en-us</dc:language>
</metadata>
<manifest>
     <!-- table of contents [mandatory] -->
    <item id="tochtml" media-type="application/xhtml+xml" href="toc.html"/>
    <item id="item0" media-type="application/xhtml+xml" href="Artical-1277621753.html"/>
    ...
    <!--下面是圖片-->
     <item id="0.368541311142" media-type="image/jpg" href="Images/-1720404282.jpg"/>
</manifest>
<spine toc="desertfire">
  <!-- 下面描述了KG生成電子書后文本的順序 -->
    <itemref idref="toc"/>  
    <itemref idref="tochtml"/>  
    <itemref idref="item31"/>
  
</spine>
<guide>
    <reference type="toc" title="Table of Contents" href="toc.html"></reference>
    <reference type="text" title="Welcome" href="toc.html"></reference>
</guide>
</package>
```

image

需要注意的有以下幾點:

  • 所有資源都需要一個id,命名任意,但不能重復
  • media-type描述了資源的類型,記住兩類基本就夠用了,"application/xhtml+xml"代表HTML文件,"image/jpg"或 "image/png"代表圖片。
  • 其他都可以省略,只是會影響電子書完整性。
    由於這兩個文件內部其實都是html,所以修改編輯都很容易。

最終,KG的命令行目標,不是目錄HTML,而是OPF文件!將所有的文件放入一個文件夾后,啟動KG命令行,最后KG會在該目錄下生成你心儀已久的mobi!

編輯HTML和OPF文件

知道其原理后,主要的任務是填充HTML和OPF文件,幾頁內容還好,如果內容繁多,不論是手工( ⊙ o ⊙ ),還是編程字符串拼接,都會變得異常低效。
此時,就需要模板引擎出手了,python推薦使用Jinja2, 資料眾多,功能強大,性能尚可。生成opf的模板文件,基本就長下面這個樣子:

<package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="BookId">
<metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
<dc:title>{{ title }}</dc:title>
<dc:language>en-us</dc:language>
</metadata>
<manifest>
      <!-- table of contents [mandatory] -->
    <item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx"/>
    <item id="tochtml" media-type="application/xhtml+xml" href="toc.html"/>
{% for item in navigation %}
    <item id="{{ item.id }}" media-type="application/xhtml+xml" href="{{ item.href }}"/>
{% endfor %}
 {% for item in media %}
   <item id="{{ item.id }}" media-type="image/{{ item.format}}" href="{{ item.href}}"/>
{% endfor %}
 
</manifest>
<spine toc="{{ title }}">
  <!-- the spine defines the linear reading order of the book -->
    <itemref idref="toc"/>  
    <itemref idref="tochtml"/>  
  {% for item in navigation %}
    <itemref idref="{{ item.id }}"/>
  {% endfor %}
</spine>
<guide>
    <reference type="toc" title="Table of Contents" href="toc.html"></reference>
    <reference type="text" title="Welcome" href="toc.html"></reference>
</guide>
</package>

我在此處就不費事講解jinja2的語法了。這樣,就能解決閱讀網頁新聞和HTML資源的問題了。

生成掃描版MOBI

下一個問題,是如何閱讀掃描版的PDF,如電子書和論文。有以下幾類初始想法:

image

權衡之后,我們選用第二種方案。PDF分為兩類,一種是一頁一欄,如電子書,另一種是一頁兩欄,如論文。
那么,為了保證質量,有以下的步驟:

將PDF轉換為圖片

如果使用python,則有一些類庫可以使用,如imagemagick和一系列相關類庫。
但這些類庫安裝比較麻煩,因此筆者使用了軟件生成,此處強烈推薦一款軟件:
AP PDF to IMAGE 國產軟件?的驕傲!不需要其他任何類庫,體積小,性能穩定,生成圖片尺寸可調,可批量處理,非常清晰!
百度可搜索各類綠色版下載,我都想給作者支付寶捐錢了。

圖片處理

如果你是PS大神,當然可以使用宏和批量命令完成這些,此處我們用的還是python,使用著名的PIL類庫,下面貼出代碼:

# coding=utf-8
import os

import Image as img
import jinja2 as jj

import extends
import libs.kindlestrip as kp



# 要PDF轉JPG時,如果用python的方案,則需要安裝一堆庫
# 用現成的工具,則難以與Python集成,而且速度很慢,目前還是采用現成的工具吧

# 當生成論文時,第一頁的上半部分,單獨抽出,剩下的分為四頁導出。設置如下
horizon = 2
vertic = 2
firstpage = True
# 生成普通橫版PDF時,則為如下設置:
# horizon = 1
# vertic  = 2
# firstpage=False

topblood = 0.05;
sideblood = 0.06;
booktitle = u"Paper";
author = "zhaoyiming"
outputfolder = "pdf2mobi/";
imgTypes = ['.png', '.jpg', '.bmp']
kindlegen = r"Tools/kindlegen.exe"  # kindlegen position
shouldsplit = True;
imagefolders = outputfolder + 'raw';
splitfolder = outputfolder + 'split'

docs = [];
pageindex = 0;

if shouldsplit == True:
    for root, dirs, files in os.walk(imagefolders):
        index = 0;
        for currentFile in files:
            crtFile = root + '\\' + currentFile
            format = crtFile[crtFile.rindex('.'):].lower();

            if format not in imgTypes:
                continue;
            crtIm = img.open(crtFile)
            crtW, crtH = crtIm.size
            hStep = crtW * (1 - 2 * sideblood) // horizon
            vStep = crtH * (1 - 2 * topblood) // vertic
            hstart = crtW * sideblood
            vstart = crtH * topblood;
            if (firstpage == True and pageindex == 0):
                crtOutFileName = 'pdf2mobi/split/' + str(index) + format,
                box = (hstart, vstart, crtW, crtH // 3)
                box = list((int(x) for x in box));
                cropped = crtIm.crop(box)
                cropped.save(crtOutFileName[0])
                myimg = {};
                myimg["href"] = "split/" + str(index) + format;
                myimg["id"] = index;
                myimg["format"] = format;
                myimg["width"] = box[2] - box[0];
                myimg["height"] = box[3] - box[1];
                docs.append(myimg)
                index += 1;

            for j in range(horizon):
                for i in range(vertic):
                    crtOutFileName = 'pdf2mobi/split/' + str(index) + format,
                    box = (hstart + j * hStep, vstart + i * vStep, hstart + (j + 1) * hStep, vstart + (i + 1) * vStep)
                    box = (int(x) for x in box);
                    cropped = crtIm.crop(box)
                    cropped.save(crtOutFileName[0])
                    myimg = {};
                    myimg["href"] = "split/" + str(index) + format;
                    myimg["id"] = index;
                    myimg["format"] = format;
                    myimg["width"] = hStep;
                    myimg["height"] = vStep;
                    docs.append(myimg)
                    index += 1;
            pageindex += 1;
else:
    for root, dirs, files in os.walk(imagefolders):
        index = 0;
        for currentFile in files:
            crtFile = root + '\\' + currentFile
            format = crtFile[crtFile.rindex('.'):].lower();
            if format not in imgTypes:
                continue;
            myimg = {};
            myimg["href"] = "split/" + str(index) + format;
            myimg["id"] = index;
            myimg["format"] = format;
            myimg["width"] = "1347";
            myimg["height"] = "1023";
            docs.append(myimg)
            index += 1;
images = [];
env = jj.Environment(loader=jj.FileSystemLoader([r"templates/"]))
articaltemplate = env.get_template('jpgs.html')

opftemplate = env.get_template('opf.html')
ncxtemplate = env.get_template('ncx.html')

extends.SaveFile(outputfolder + "toc.html", articaltemplate.render(navigation=docs, title=booktitle, author=author));
extends.SaveFile(outputfolder + booktitle + ".opf",
                 opftemplate.render(navigation=docs, title=booktitle, author=author, media=images));
extends.SaveFile(outputfolder + "toc.ncx", ncxtemplate.render(navigation=docs, title=booktitle, author=author));

currentPath = os.getcwd() + "\\" + outputfolder.replace("/", "\\") + booktitle + ".opf";
mobipath = outputfolder.replace("/", "\\") + booktitle + ".mobi";
kindlepath = os.getcwd() + "\\" + kindlegen.replace("/", "\\");
cmd = kindlepath + " " + currentPath;
cmd = cmd.encode();
print cmd;
os.system(cmd);
kp.Convert(mobipath, mobipath)

(我覺得我應該把代碼上傳到github上,恩,一會再說)

這樣,就能生成可讀的漂亮的PDF轉mobi了。

最終效果

這些代碼花了我一個下午的時間,不過與爬蟲配合,生成各位大神的博客,效果真是非常贊!

image

image

image

媽媽再也不用擔心我的眼睛了!終於可以隨時隨地,沒有廣告地批量看大神們的博客了!

有任何問題,歡迎隨時討論。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM