epub 文件转pdf
前言
目前从其它地方下载到了epub资料文件,想打印出来,但打印需要pdf,这个记录一下简单的转换过程。
epub文件格式
epub是开放式电子书格式,简单理解就是用网页技术包装的zip压缩包,具体格式如下
example.epub
├── mimetype # 固定内容 application/epub+zip,必须存在
├── META-INF/
│ └── container.xml # 入口文件,指向主 OPF 文件的位置
└── OEBPS/ # 通常叫 OEBPS,存放所有内容
├── content.opf # 核心清单文件(元数据 + 文件列表 + 阅读顺序)
├── toc.ncx / toc.xhtml # 目录文件(EPUB 2 用 ncx,EPUB 3 用 xhtml)
├── chapter1.xhtml # 正文内容(XHTML/HTML5)
├── chapter2.xhtml
├── styles.css # 样式表
└── images/ # 图片等资源
这里简单的方法就是读取 opf文件,解析 <spine> 和 <manifest> ,得到按照顺序排列的XHTML文件路径
然后读取xhtml,从中截图需要的内容,最终输入到pdf中
VersOne.Epub
自己读取opf文件,需要处理各种关系,这里可以通过类库 VersOne.Epub来自动处理zip解压、OPF、NCX解析等任务。
using var epubBook = EpubReader.OpenBook(epubFilePath); // epubBook.Title 标题 // epubBook.Author 作者 // epubBook.Description 简介
下一步就是解析整个epub文件的读取顺序, 通过 GetNavigation方法 获取整个的顺序,这里要注意 它的NestItems 要进行递归处理。
foreach (var navigationItem in epubBook.GetNavigation()) { PrintNavigationItem(navigationItem, 0,document,pdfWriter); } public static void PrintNavItem(EpubNavigationItemRef itemRef, int identLevel) { Log.Information(itemRef.Title); foreach (EpubNavigationItemRef nestedNavigationItem in itemRef.NestedItems) { PrintNavItem(nestedNavigationItem, identLevel + 1); } }
顺序找到之后就要要对每个顺序的XHTML文档进行处理。
EpubNavigationItemRef 类有HtmlContentFileRef 属性,通过这个属性来读取对应的XHTML 文件,这样整个顺序和文件都可以了。
if (navigationItem.HtmlContentFileRef != null) { bool isToc = navigationItem.Title == "目录"; string text = navigationItem.HtmlContentFileRef.ReadContent(); if (isToc) { PrintTocContent(text,document); } else { PrintContent(text,document,pdfWriter); } }
通过 ReadContext 方法,把对应的XHMTL文件整个读取出来,后续就是解析XHTML文件,从中读取 对应的章节和章节内容。
XHTML 文件读取和解析
XHTML文件通过研究,有两个类型,一个是TOC 目录,一个是纯粹内容,其中TOC目录的大体如下:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" epub:prefix="z3998: http://www.daisy.org/z3998/2012/vocab/structure/#" lang="zh" xml:lang="zh"> <head> <title>目录</title> <link href="stylesheet.css" rel="stylesheet" type="text/css"/> </head> <body><h1>目录</h1> <nav epub:type="toc" id="inline-toc"> <p class="no-indent">点击章节标题可跳转到对应正文位置。</p> <h3>第一卷:默认</h3> <ol> <li><a href="chapter_00001.xhtml">第1章 银子去哪儿了</a></li> <li><a href="chapter_00571.xhtml">第570章 乱世出枭雄(完)</a></li> </ol> </nav> </body> </html>
具体内容的XHTML 如下:
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" epub:prefix="z3998: http://www.daisy.org/z3998/2012/vocab/structure/#" lang="zh" xml:lang="zh"> <head> <title>第2章 忽悠抢夺,自入陷阱</title> <link href="stylesheet.css" rel="stylesheet" type="text/css"/> </head> <body><h1>第2章 忽悠抢夺,自入陷阱</h1> <p>赵弘祖从抽屉拿出一大块卤肉,又拿出两壶酒,还有一碟花生,抬手示意他落座,语气亲热中带着试探,</p> <p>这小子是真贼,早猜到这是大舅哥骆养性的买卖。</p> <p>望着消失在后院门口的林威,赵弘祖嘴角露出一丝邪魅,内心暗笑,有点意思,瞌睡来了个枕头,关老头,这可不是我拉你徒弟下水啊,是他自找的。</p> </body> </html>
HtmlAgilityPack解析
这里就用HtmlAgilityPack 通过 xpath来进行进行解析
目录的解析是 在body节点下通过xpath ".//ol/li/a" 获取所有的目录
内容的解析是 在body节点下通过xpath ”.//p" 获取所有的内容
pdf构建
pdf这里通过iTextSharp来构建,目录页面对应pdf暂不需要,这里直接就通过 把 内容解析中的 p标签对应为 iTextsharp的 Paragraph对象即可。
这里再稍微加一个处理,内容解析中还有 h1 标签,这里作为每一章的标题,对应iTextSharp的 Chapter对象,最后就是构建的代码:
public static void PrintContent(string text, Document document, PdfWriter pdfWriter) { HtmlDocument htmlDocument = new(); htmlDocument.LoadHtml(text); HtmlNode bodyNode = htmlDocument.DocumentNode.SelectSingleNode("//body"); if (bodyNode != null) { Chapter chapter = null; //解析文章的标题,用 Chapter对象存储 var h1Node = bodyNode.SelectSingleNode("./h1"); if (h1Node != null) { chapter = new Chapter(new Paragraph(h1Node.InnerText, titleFont), 1); chapter.NumberDepth = 0; } var pNodes = bodyNode.SelectNodes(".//p"); if (pNodes != null) { foreach (var pNode in pNodes) {
//解析文章的具体内容,用 Paragraph 对象存储 Paragraph p=new Paragraph(pNode.InnerText, commonFont); p.FirstLineIndent = 20f; chapter.Add(p); } } document.Add(chapter); } document.NewPage(); }
pdf字体
一般中文字体是 宋体,这里推荐一个好看的中文字体(霞鹜文楷),效果如下图

结尾
最终的转换只是有内容,没有图片和样式的支持,算是一个简化版本,而且pdf的目录也不支持,后续可以参考此文章进行细化。

浙公网安备 33010602011771号