购买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,目录和其他多媒体资源。介绍如下:

值得注意的是,所有的文件都应该保存在本地,尤其是jpg, html中的图片超链接,需要重定向到本地的jpg文件,如果依然在服务器上,据我所知,kg是不负责渲染下载的。

资源聚合文件: opf和ncx

由于opf文件非常重要,我们下面就讲解opf的格式:

  1. <package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="BookId">
  2. <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
  3. <dc:title>电子书标题</dc:title>
  4. <dc:language>en-us</dc:language>
  5. </metadata>
  6. <manifest>
  7. <!-- table of contents [mandatory] -->
  8. <item id="tochtml" media-type="application/xhtml+xml" href="toc.html"/>
  9. <item id="item0" media-type="application/xhtml+xml" href="Artical-1277621753.html"/>
  10. ...
  11. <!--下面是图片-->
  12. <item id="0.368541311142" media-type="image/jpg" href="Images/-1720404282.jpg"/>
  13. </manifest>
  14. <spine toc="desertfire">
  15. <!-- 下面描述了KG生成电子书后文本的顺序 -->
  16. <itemref idref="toc"/>
  17. <itemref idref="tochtml"/>
  18. <itemref idref="item31"/>
  19.  
  20. </spine>
  21. <guide>
  22. <reference type="toc" title="Table of Contents" href="toc.html"></reference>
  23. <reference type="text" title="Welcome" href="toc.html"></reference>
  24. </guide>
  25. </package>
  26. ```

需要注意的有以下几点:

  • 所有资源都需要一个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的模板文件,基本就长下面这个样子:

  1. <package xmlns="http://www.idpf.org/2007/opf" version="2.0" unique-identifier="BookId">
  2. <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
  3. <dc:title>{{ title }}</dc:title>
  4. <dc:language>en-us</dc:language>
  5. </metadata>
  6. <manifest>
  7. <!-- table of contents [mandatory] -->
  8. <item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx"/>
  9. <item id="tochtml" media-type="application/xhtml+xml" href="toc.html"/>
  10. {% for item in navigation %}
  11. <item id="{{ item.id }}" media-type="application/xhtml+xml" href="{{ item.href }}"/>
  12. {% endfor %}
  13. {% for item in media %}
  14. <item id="{{ item.id }}" media-type="image/{{ item.format}}" href="{{ item.href}}"/>
  15. {% endfor %}
  16.  
  17. </manifest>
  18. <spine toc="{{ title }}">
  19. <!-- the spine defines the linear reading order of the book -->
  20. <itemref idref="toc"/>
  21. <itemref idref="tochtml"/>
  22. {% for item in navigation %}
  23. <itemref idref="{{ item.id }}"/>
  24. {% endfor %}
  25. </spine>
  26. <guide>
  27. <reference type="toc" title="Table of Contents" href="toc.html"></reference>
  28. <reference type="text" title="Welcome" href="toc.html"></reference>
  29. </guide>
  30. </package>

我在此处就不费事讲解jinja2的语法了。这样,就能解决阅读网页新闻和HTML资源的问题了。

生成扫描版MOBI

下一个问题,是如何阅读扫描版的PDF,如电子书和论文。有以下几类初始想法:

权衡之后,我们选用第二种方案。PDF分为两类,一种是一页一栏,如电子书,另一种是一页两栏,如论文。
那么,为了保证质量,有以下的步骤:

将PDF转换为图片

如果使用python,则有一些类库可以使用,如imagemagick和一系列相关类库。
但这些类库安装比较麻烦,因此笔者使用了软件生成,此处强烈推荐一款软件:
AP PDF to IMAGE 国产软件?的骄傲!不需要其他任何类库,体积小,性能稳定,生成图片尺寸可调,可批量处理,非常清晰!
百度可搜索各类绿色版下载,我都想给作者支付宝捐钱了。

图片处理

如果你是PS大神,当然可以使用宏和批量命令完成这些,此处我们用的还是python,使用著名的PIL类库,下面贴出代码:

  1. # coding=utf-8
  2. import os
  3.  
  4. import Image as img
  5. import jinja2 as jj
  6.  
  7. import extends
  8. import libs.kindlestrip as kp
  9.  
  10. # 要PDF转JPG时,如果用python的方案,则需要安装一堆库
  11. # 用现成的工具,则难以与Python集成,而且速度很慢,目前还是采用现成的工具吧
  12.  
  13. # 当生成论文时,第一页的上半部分,单独抽出,剩下的分为四页导出。设置如下
  14. horizon = 2
  15. vertic = 2
  16. firstpage = True
  17. # 生成普通横版PDF时,则为如下设置:
  18. # horizon = 1
  19. # vertic = 2
  20. # firstpage=False
  21.  
  22. topblood = 0.05;
  23. sideblood = 0.06;
  24. booktitle = u"Paper";
  25. author = "zhaoyiming"
  26. outputfolder = "pdf2mobi/";
  27. imgTypes = ['.png', '.jpg', '.bmp']
  28. kindlegen = r"Tools/kindlegen.exe" # kindlegen position
  29. shouldsplit = True;
  30. imagefolders = outputfolder + 'raw';
  31. splitfolder = outputfolder + 'split'
  32.  
  33. docs = [];
  34. pageindex = 0;
  35.  
  36. if shouldsplit == True:
  37. for root, dirs, files in os.walk(imagefolders):
  38. index = 0;
  39. for currentFile in files:
  40. crtFile = root + '\\' + currentFile
  41. format = crtFile[crtFile.rindex('.'):].lower();
  42.  
  43. if format not in imgTypes:
  44. continue;
  45. crtIm = img.open(crtFile)
  46. crtW, crtH = crtIm.size
  47. hStep = crtW * (1 - 2 * sideblood) // horizon
  48. vStep = crtH * (1 - 2 * topblood) // vertic
  49. hstart = crtW * sideblood
  50. vstart = crtH * topblood;
  51. if (firstpage == True and pageindex == 0):
  52. crtOutFileName = 'pdf2mobi/split/' + str(index) + format,
  53. box = (hstart, vstart, crtW, crtH // 3)
  54. box = list((int(x) for x in box));
  55. cropped = crtIm.crop(box)
  56. cropped.save(crtOutFileName[0])
  57. myimg = {};
  58. myimg["href"] = "split/" + str(index) + format;
  59. myimg["id"] = index;
  60. myimg["format"] = format;
  61. myimg["width"] = box[2] - box[0];
  62. myimg["height"] = box[3] - box[1];
  63. docs.append(myimg)
  64. index += 1;
  65.  
  66. for j in range(horizon):
  67. for i in range(vertic):
  68. crtOutFileName = 'pdf2mobi/split/' + str(index) + format,
  69. box = (hstart + j * hStep, vstart + i * vStep, hstart + (j + 1) * hStep, vstart + (i + 1) * vStep)
  70. box = (int(x) for x in box);
  71. cropped = crtIm.crop(box)
  72. cropped.save(crtOutFileName[0])
  73. myimg = {};
  74. myimg["href"] = "split/" + str(index) + format;
  75. myimg["id"] = index;
  76. myimg["format"] = format;
  77. myimg["width"] = hStep;
  78. myimg["height"] = vStep;
  79. docs.append(myimg)
  80. index += 1;
  81. pageindex += 1;
  82. else:
  83. for root, dirs, files in os.walk(imagefolders):
  84. index = 0;
  85. for currentFile in files:
  86. crtFile = root + '\\' + currentFile
  87. format = crtFile[crtFile.rindex('.'):].lower();
  88. if format not in imgTypes:
  89. continue;
  90. myimg = {};
  91. myimg["href"] = "split/" + str(index) + format;
  92. myimg["id"] = index;
  93. myimg["format"] = format;
  94. myimg["width"] = "";
  95. myimg["height"] = "";
  96. docs.append(myimg)
  97. index += 1;
  98. images = [];
  99. env = jj.Environment(loader=jj.FileSystemLoader([r"templates/"]))
  100. articaltemplate = env.get_template('jpgs.html')
  101.  
  102. opftemplate = env.get_template('opf.html')
  103. ncxtemplate = env.get_template('ncx.html')
  104.  
  105. extends.SaveFile(outputfolder + "toc.html", articaltemplate.render(navigation=docs, title=booktitle, author=author));
  106. extends.SaveFile(outputfolder + booktitle + ".opf",
  107. opftemplate.render(navigation=docs, title=booktitle, author=author, media=images));
  108. extends.SaveFile(outputfolder + "toc.ncx", ncxtemplate.render(navigation=docs, title=booktitle, author=author));
  109.  
  110. currentPath = os.getcwd() + "\\" + outputfolder.replace("/", "\\") + booktitle + ".opf";
  111. mobipath = outputfolder.replace("/", "\\") + booktitle + ".mobi";
  112. kindlepath = os.getcwd() + "\\" + kindlegen.replace("/", "\\");
  113. cmd = kindlepath + " " + currentPath;
  114. cmd = cmd.encode();
  115. print cmd;
  116. os.system(cmd);
  117. kp.Convert(mobipath, mobipath)

(我觉得我应该把代码上传到github上,恩,一会再说)

这样,就能生成可读的漂亮的PDF转mobi了。

最终效果

这些代码花了我一个下午的时间,不过与爬虫配合,生成各位大神的博客,效果真是非常赞!

妈妈再也不用担心我的眼睛了!终于可以随时随地,没有广告地批量看大神们的博客了!

有任何问题,欢迎随时讨论。

生成Kindle可读的mobi和PDF电子书的更多相关文章

  1. 关于计算机学习的书(doc,mobi,epub,pdf四种格式)

    关于计算机学习的书(doc,mobi,epub,pdf四种格式) <html> <body> <div> 21天学通C+ +2016/6/22 18:47文條 30 ...

  2. 爬虫:把廖雪峰的教程转换成 PDF 电子书

    写爬虫似乎没有比用 Python 更合适了,Python 社区提供的爬虫工具多得让你眼花缭乱,各种拿来就可以直接用的 library 分分钟就可以写出一个爬虫出来,今天就琢磨着写一个爬虫,将廖雪峰的 ...

  3. python制作pdf电子书

    python制作pdf电子书 准备 制作电子书使用的是python的pdfkit这个库,pdfkit是 wkhtmltopdf 的Python封装包,因此在安装这个之前要安装wkhtmltopdf 安 ...

  4. Python 爬虫:把廖雪峰教程转换成 PDF 电子书

    写爬虫似乎没有比用 Python 更合适了,Python 社区提供的爬虫工具多得让你眼花缭乱,各种拿来就可以直接用的 library 分分钟就可以写出一个爬虫出来,今天尝试写一个爬虫,将廖雪峰老师的 ...

  5. Python爬虫实战:将网页转换为pdf电子书

    写爬虫似乎没有比用 Python 更合适了,Python 社区提供的爬虫工具多得让你眼花缭乱,各种拿来就可以直接用的 library 分分钟就可以写出一个爬虫出来,今天就琢磨着写一个爬虫,将廖雪峰的 ...

  6. 【转】PDF电子书分享

    http://www.voidcn.com/blog/u013830841/article/p-4343018.html http://www.voidcn.com/blog/u013830841/a ...

  7. 《c#入门经典第五版》简介及pdf电子书网盘下载地址(收藏)

    <C#入门经典(第5版)>全面讲解C# 2010和.net架构编程知识,为您编写卓越C# 2010程序奠定坚实基础.C#入门经典系列是屡获殊荣的C#名著和超级畅销书.最新版的<C#入 ...

  8. 100本Python精品书籍(附pdf电子书下载)

    51本Python精品书籍(附下载)链接: https://pan.baidu.com/s/19ydAKCFxM0plkepXMlqQLg 提取码: nnpe 400集python视频教程下载:链接: ...

  9. 轻量级JAVA+EE企业应用实战(第4版)pdf电子书和源码的免费下载链接

    轻量级JAVA+EE企业应用实战(第4版)pdf电子书和源码的免费下载链接: pdf链接:https://pan.baidu.com/s/1dYIWtsv2haL4v7vx3w-8WQ 无提取密码源码 ...

随机推荐

  1. excel使用总结

    单元格拆分:选中列-->数据-->分列 常用函数: clean 清除文本中不能打印的字符 countif,countifs,在指定区域中按指定条件对单元格进行计数(单条件计数,多条件计数) ...

  2. 使用HttpClient来异步发送POST请求并解析GZIP回应

    .NET 4.5(C#): 使用HttpClient来异步发送POST请求并解析GZIP回应 在新的C# 5.0和.NET 4.5环境下,微软为C#加入了async/await,同时还加入新的Syst ...

  3. Web服务网站故障分析常用的命令

    1.查看TCP连接状态netstat -nat |awk ‘{print $6}’|sort|uniq -c|sort -rn netstat -n | awk ‘/^tcp/ {++S[$NF]}; ...

  4. normalize.css入门和下载

    CSS Reset 是革命党,CSS Reset 里最激进那一派提倡不管你小子有用没用,通通给我脱了那身衣服,凭什么你 body 出生就穿一圈 margin,凭什么你姓 h 的比别人吃得胖,凭什么你 ...

  5. Java ArrayList和Vector、LinkedList与ArrayList、数组(Array)和列表集合(ArrayList)的区别

    ArrayList和Vector的区别ArrayList与Vector主要从二方面来说.  一.同步性:   Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步 ...

  6. Mysql 学习笔记

    创建表: create table testtable( id_ bigint not null AUTO_INCREMENT, name varchar(75) null, vmid varchar ...

  7. nodejs学习之events的使用

    实用events做个小例子: var mysql = require("mysql"); var Event = require("events").Event ...

  8. ubuntu 安装与开始学习

    下载地址 http://cn.ubuntu.com/download/ 经验: 1.遇到安装问题,首先尝试解读错误,再使用  ./configure --help  不行再上Stack overflo ...

  9. CSS基础篇之选择符2

    属性选择符: 选择符 版本 描述 E[att] CSS2 选择具有att属性的E元素. E[att="val"] CSS2 选择具有att属性且属性值等于val的E元素. E[at ...

  10. 基于Entity Framework 6的框架Nido Framework

    随着 Entity Framework 最新主版本 EF6 的推出,Microsoft 对象关系映射 (ORM) 工具达到了新的专业高度,与久负盛名的 .NET ORM 工具相比已不再是门外汉. EF ...