PDF文档转换成mobi格式forkindle并解决排版问题.docx
《PDF文档转换成mobi格式forkindle并解决排版问题.docx》由会员分享,可在线阅读,更多相关《PDF文档转换成mobi格式forkindle并解决排版问题.docx(21页珍藏版)》请在冰豆网上搜索。
![PDF文档转换成mobi格式forkindle并解决排版问题.docx](https://file1.bdocx.com/fileroot1/2023-2/4/ef28a7b3-165a-4d7f-b446-2dbbeec76579/ef28a7b3-165a-4d7f-b446-2dbbeec765791.gif)
PDF文档转换成mobi格式forkindle并解决排版问题
PDF文档转换成mobi格式(forkindle),并解决排版问题
∙0.前言
∙1.下载和安装calibre
∙2.PDF导入calibre,并转换为azw3格式
∙3.编辑电子书,获取HTML内容和图片
∙4.程序处理HTML文档
∙5.将HTML文档导入calibre,并转换成azw3格式
∙6.编辑azw3文档
∙7.将azw3文档转换成mobi格式
∙8.附录
0.前言
正式介绍之前,先回答下面几个问题:
1.为什么要将PDF转换成mobi?
想要将PDF转换成mobi格式,初衷在于想在kindle上面看一些从网上获取到的PDF文档。
直接将PDF导入kindle本来也可以,但是效果不是很好——要么竖着看,但是字体很小;要么横着看,字体会大一些,但是总感觉比较别扭,而且PDF的一页需要在kindle上翻3页。
kindle支持azw3、mobi等格式,但是不支持直接将azw3格式的文档直接导入到kindle,所以需要将PDF文档转换成mobi格式
2.为什么不直接用在线转换工具?
其实网上有很多工具支持将PDF转换成mobi格式,但是效果都很差:
1.章节标题和正文内容没有区别;
2.正文内容格式混乱,在kindle上看是以PDF的一行进行的分段,行首也没有空格
3.
3.将PDF转换成mobi格式,我大概怎么做?
将PDF转换成mobi格式,我主要是借助于calibre工具:
4.转换效果如何?
转换之后效果如下图,其中对章节标题和段落划分进行了处理:
1.下载和安装calibre
calibre下载地址:
https:
//calibre-
根据自己的系统下载安装即可
2.PDF导入calibre,并转换为azw3格式
打开calibre,点击菜单栏的“添加数据”,选择PDF格式文件,点击“Open”
在calibre的主窗口中选中刚导入的图书,点击菜单栏的“转换书籍”,在弹出的转换窗口,将输出格式选择为“AZW3”,然后点击“确定”。
转换过程可能会持续一小段时间,看calibre主窗口右下角的任务执行结束即转换完成
【注】这里有一个坑,图书转换的设置,目录针对章节有一个默认的最大值,需要根据图书的实际情况进行调整。
我一般将两个值分别设置为1000,50
3.编辑电子书,获取HTML内容和图片
在calibre主窗口选中目标电子书,点击菜单栏的“编辑书籍”打开编辑书籍窗口
在编辑书籍窗口,选中文本下的“part0000.html”,点击右键,选择“导出part0000.html”
同样的方式,全选图片下的所有图片,然后导出
4.程序处理HTML文档
由上一步可以看到,直接由PDF转换得到的HTML文档,PDF里面的每一行转换之后在HTML文档中都独立成了一段,而且段首也没有空格,章节标题也没有突出展示。
针对这个问题,于是自己写了一段Java代码,主要是根据语义对文档内容进行分段,同时将标题突出,然后将每一张的起始位置独立成页。
具体的代码见文末,里面也有比较详细的注释,有一些特殊情况的处理可以根据自己的要求适当修改代码。
5.将HTML文档导入calibre,并转换成azw3格式
首先,将calibre中已有的同名电子书删掉,操作方式是:
在calibre主窗口,选中要删除的电子书,右键,选择“移除书籍”>“移除选定书籍”,之后在弹出窗口做二次确认
然后,将处理之后的HTML文档导入到calibre,操作方式同第2章。
最后,由于直接导入的HTML文档是ZIP格式,不能直接进行编辑,所以需要再次将文档转换成azw3格式,操作方式同第3章。
可以看到,导入HTML文档之后,图书的封面丢失了,可以在转换的时候,直接修改封面图(可以用第3章导出保存的图片),如下图:
6.编辑azw3文档
这一步是非必须的,但是如果电子书中有插图,那么就需要在这一步进行补充。
另外也可以在文件预览中检查各个HTML文件,如有必要,也可以进行简单编辑。
进入编辑操作同第3章所介绍,这里主要介绍一下补充图片。
在编辑书籍窗口,点击菜单栏的“新增文件”按钮。
然后在弹出框选择“导入文件”,选择需要导入的图片。
注意,文件的路径最终要是“images/xx”格式。
最后点击“确定”。
如果有多张图片,则重复上面的操作。
目前calibre不支持批量导入。
编辑完成之后,点击编辑书籍窗口菜单栏的“保存”按钮进行保存。
7.将azw3文档转换成mobi格式
在calibre主窗口,选中电子书,点击“转换书籍”,在弹出窗口中,输入格式选择“AZW3”
,输出格式选择“MOBI”。
点击“确定”。
然后等转换任务结束。
转换结束之后,在calibre主窗口,选中电子书,在右侧边栏,点击“点击打开”即可获取到mobi格式的电子书。
然后将其共享至kindle就可以了。
8.附录
推荐的PDF电子书网址:
(大家有其他好的电子书链接也欢迎评论区共享。
)
HTML格式文档处理代码:
packagecom.amwalle.walle.util;
importorg.springframework.util.StringUtils;
importjava.io.*;
importjava.util.*;
importjava.util.regex.Matcher;
importjava.util.regex.Pattern;
publicclassPDFConverter{
publicstaticvoidmain(String[]args)throwsIOException{
Scannerscanner=newScanner(System.in);
System.out.println('请输入要转换的文件路径:
');
StringfilePath=scanner.nextLine();
System.out.println(filePath);
convert(filePath);
}
publicenumHeaderLevel{
Part('H1','.*第.*部分.*'),
Chapter('H2','(.*第.*章.*)|(.*[1-9].*)');
privateStringheaderLevel;
privateStringexpression;
HeaderLevel(StringheaderLevel,Stringexpression){
this.headerLevel=headerLevel;
this.expression=expression;
}
publicstaticStringgetHeaderLevel(Stringinput){
if(StringUtils.isEmpty(input)||(!
input.contains('')&&!
input.contains(''))){
returnnull;
}
Stringdummy=preHandleHeader(input);
for(HeaderLevellevel:
HeaderLevel.values()){
Patternpattern=Ppile(level.expression);
Matchermatcher=pattern.matcher(dummy);
if(matcher.find()){
returnlevel.headerLevel;
}
}
returnnull;
}
privatestaticStringpreHandleHeader(Stringinput){
Stringdummy=input;
if(input.contains('')&&input.contains('
')){
dummy=dummy.substring(dummy.indexOf('')+4,dummy.indexOf('
'));
}elseif(input.contains('')&&input.contains('
')){
dummy=dummy.substring(dummy.indexOf('')+20,dummy.indexOf('
'));
}
dummy=dummy.replaceAll('','');
dummy=dummy.replaceAll('','');
dummy=dummy.replaceAll('','');
dummy=dummy.replaceAll('','');
dummy=removeSpace(dummy);
returndummy;
}
publicStringgetHeaderLevel(){
returnheaderLevel;
}
publicvoidsetHeaderLevel(StringheaderLevel){
this.headerLevel=headerLevel;
}
publicStringgetExpression(){
returnexpression;
}
publicvoidsetExpression(Stringexpression){
this.expression=expression;
}
}
publicstaticvoidconvert(StringfilePath)throwsIOException{
Filefile=newFile(filePath);
if(!
file.isFile()||!
file.canRead()||!
file.getName().endsWith('.html')){
System.out.println('请输入正确的html文件路径,并保证文件可读!
');
}
Stringtitle=file.getName();
title=title.replace('.html','');
FileoutFile=newFile(filePath.replace('.html','1.html'));
if(outFile.exists()){
outFile.delete();
outFile.createNewFile();
}
try(Scannerscanner=newScanner(file,'UTF-8');BufferedWriteroutWriter=newBufferedWriter(newFileWriter(outFile))){
Setcatalog=newHashSet<>();
StringBufferoutput=newStringBuffer();
booleanisIndentationNeeded=false;
intcount=0;
while(scanner.hasNextLine()){
count++;
//有些文件太大,需要在中间存一下文件
if(count>=10000){
outWriter.append(output);
count=0;
output.delete(0,output.length());
}
Stringline=scanner.nextLine();
//目录前面的内容(封面、版权等)
if(catalog.isEmpty()&&!
line.startsWith('output.append(line).append('\n');
continue;
}
//处理目录:
目录数据存set用于后续增加标题格式
if(line.endsWith('
')){
if(catalog.isEmpty()){
output.delete(output.length()-1,output.length());
Stringheader=output.toString();
StringcatalogTitle=header.substring(header.lastIndexOf('\n')+1);
//格式化目录标题,增加目录前的分页
if(catalogTitle.contains('目录')||catalogTitle.contains('Contents')){
catalogTitle='
'+catalogTitle+'
'+'\n';
output.delete(header.lastIndexOf('\n')+1,output.length());
output.append(addPagination(title));
output.append(catalogTitle);
}else{
output.append('\n');
output.append(addPagination(title));
}
}
line=line.replaceAll('part0000\\.html','');
output.append(line).append('\n');
Stringtemp=line.substring(line.lastIndexOf('\'>')+2,line.indexOf('
'));
temp=removeSpace(temp);
catalog.add(temp);
continue;
}
if(StringUtils.isEmpty(line)){
output.append('\n');
continue;
}
//处理章节标题
if(isLineATitle(line,catalog)){
isIndentationNeeded=true;
StringtitleLevel=HeaderLevel.getHeaderLevel(line);
if(HeaderLevel.Part.headerLevel.equals(titleLevel)){
output.append(addPagination(title));
output.append('
').append(line).append('
').append('\n');
continue;
}
if(HeaderLevel.Chapter.headerLevel.equals(titleLevel)){
output.append(addPagination(title));
output.append('
').append(line).append('
').append('\n');
continue;
}
output.append('
').append(line).append('
').append('\n');
continue;
}
//首行缩进
if(isIndentationNeeded){
line=line.replace('','2em;\'>');
line=line.replace('
','');
output.append(line).append('\n');
isIndentationNeeded=false;
continue;
}
//文本内容分段
if(isParagraphEnd(line)){
isIndentationNeeded=true;
line=line.replace('','');
line=line.replace('
','');
output.append(line).append('\n');
continue;
}
//普通行处理:
去掉换行
if(line.startsWith('')&&line.endsWith('
')){
line=line.replace('','');
line=line.replace('
','');
output.append(line);
continue;
}
output.append(line).append('\n');
}
outWriter.append(output);
System.out.println('********************************************');
System.out.println('Done!
生成的新文件路径:
');
System.out.println(outFile.getPath());
}catch(FileNotFoundExceptione){
System.out.println('Fileconvertfailed!
');
e.printStackTrace();
}
}
privatestaticStringremoveSpace(Stringinput){
Stringresult=input.trim().replaceAll('\\.','').replaceAll('·','');
result=result.replaceAll('\\s*','').replaceAll('\\u00a0','').replaceAll((char)12288+'','');
returnresult;
}
privatestaticStringaddPagination(Stringtitle){
return'\n'+
'