1 个不稳定版本
0.1.0 | 2020年10月23日 |
---|
#26 in #produce
1MB
7.5K SLoC
从 markdown 创建 pdf 或 epub 书籍。
基本示例
use bookbinder::{BookSrc, BookSrcBuilder, create_epub, create_pdf, EpubOptions, LatexOptions};
let src = BookSrcBuilder::new("A Book")
.author("A.N. Author")
.add_mainmatter("# Greetings\n\n Hello world...")
.process();
let epub_options = EpubOptions::default();
let epub = create_epub(src.clone(), epub_options)
.expect("Error producing epub");
let pdf_options = LatexOptions::default();
let pdf = create_pdf(src, pdf_options)
.expect("Error producing pdf");
为什么使用这个
从 markdown 创建书籍有许多选择;在 Rust 世界中,有 mdbook 和 crowbook;还有 Haskell 中的惊人的 Pandoc。
但是这些通常会将 Markdown 构造转换为书籍。当它起作用时,这很好,但书籍是复杂的东西。所以,以一个简单的例子来说,一个顶级 markdown 标题 () 可能表示一个部分、一个章节或一个部分,这取决于书籍的结构。然后它可能需要带有标签 (
Chapter 1: Header
) 或单独作为数字 (I
或 One
或 1
) 进行渲染,或单独作为标题 (Header
)...
如果它看起来更像是 或
,你将遇到另一组问题:在合适的书中,这些应该以不同的方式显示--例如,前言中的页码可能应该用罗马数字表示,而任何标签都应该指附录,如
Appendix A
,而不是 Chapter 23
。
然后你将遇到其他困难--大多数书籍都将想要一个扉页和一个扉页背页(版权页),并且它们经常包含一些东西,如题词或献词。所有这些都需要特殊的格式化,但你不能无限期地扩展 markdown 来包含它们,否则你不知道自己已经重新创造了 TEI 而没有严格的规范。
这个软件包依赖于这样一个观点:在一个被分成语义角色的文本中,Markdown 是一个理想的选择--你可以说“强调这个文本在题词内”,一切都会好起来,但你不能在 Markdown 本身中说“这个文本是题词”。由于书籍的可能部分不是无限多的,而且像 epub 结构语义词汇表 这样的东西已经定义了一系列的可能选择,因此相对容易设置一个容器,它可以在更复杂的语义系统中渲染 Markdown。
由于其中一些元素,如标题页或版权页,主要是元数据的结果,所以我们进一步扩展功能,使其可以从已包含的元数据自动生成——如果您愿意的话。
换句话说,您可以这样做
let introduction = "This is an introduction...";
let dedication = "This is dedicated to someone";
let foreword_with_custom_heading = "# A peculiar light\n\nForeword goes here...";
let mainmatter = "# Early Life\n\n## I am born\n\nThe day of my birth was a dark cold day...";
let src = BookSrcBuilder::new("A Book") // Start with a title
.author("A.N. Author")
.publisher("Publisher Name")
.add_introduction(introduction, None, "Introduction Author")
.set_dedication(dedication)
.add_foreword(foreword_with_custom_heading, None, vec!["First Foreword Author", "Second Foreword Author"])
.add_mainmatter(mainmatter);
并获得一本带有标题页和版权信息、语义化细节展示以及非常好的美感的书。
上述示例将为您提供半标题、标题页、版权页,其中说明了这是A.N. 作者今年版权并由出版社名称出版的;然后您会得到一系列前言页面,包括带有标题 Introduction
的介绍和带有说明它是序言的标签但标题为 A Peculiar Light
的序言,以及一个未标记的献词,通过其格式(通常是minipage中的斜体文本,但您可以指定其他格式)表明它是一篇献词。(当然,如果您想避免生成版权页等,或提供自己的,这也很容易做到!)
接下来的主体部分会将 作为部分标题处理,将
作为章节标题处理,但如果它们都是顶级标题,则都会被处理为章节(或者如果一个是二级标题,另一个是三级标题,并且没有顶级标题,它们将被处理为章节和部分。)
等等。这很酷。
更好的是,更改这些基本语义结构表示方式很容易——如果您只想忽略那些有标题的章节,并将它们称为 One
、Two
和 Three
,您只需设置一个选项即可!如果您想使您的PDF书籍全部使用A4纸,并使用Comic Sans字体,但标题使用Papyrus字体——许多事情都是可能的,但只有一些是明智的。
let options = LatexOptions::default()
.use_words_for_chapter_labels() // this says 'Number chapters using words!'
.only_number_chapters() // And actually, while you're about it, don't do anything *but* number chapters
.set_serif_typeface("Comic Sans") // and set the main text in Comic Sans
.set_heading_typeface("Papyrus") // and headers in Papyrus
.set_papersize(PaperSize::A4Paper); // and an A4 book is a good size
只需通过将适用于特定输出格式的选项与通过 BookSrcBuilder
创建的源组合,就可以生成一本书。
BookSrcBuilder
允许您添加各种元数据(这本书由某个人翻译,另一个人做笔记并提供介绍,但作者是另一个人)和书籍元素(这里有介绍,这里有正文,这里有翻译者的注释)。
同时,有一系列选项可以更改此源的处理方式。不同格式的选项不同,因为我们能对不同格式做的事情也不同——在epub没有页码时更改页码没有意义,但在没有封面图的PDF文件上设置封面图像也没有多少意义!
目标
这个库旨在提供一个易于使用的创建格式优美的书籍的方法,这些书籍可以处理相当细粒度的复杂性,但不应该将这种复杂性强加给不需要它的用户。
非常愿意听到有关简化用户体验或缺失功能的反馈!
我们的Markdown
说我们使用简单的Markdown有点夸张——实际上,书籍需要一些CommonMark没有的功能。因此,源字符串或文件使用受Pandoc启发的Markdown方言进行解析
- 智能地处理直引号,将连字符序列替换为适当的破折号,并使用省略号代替
...
- 包括脚注
- 包括下标和上标
有关详细信息,请参阅 extended_pulldown。顺便说一句,这样做的一个优点是构建管道非常容易 任意输入格式 -> pandoc -> markdown -> bookbinder
,这样可以从诸如Word文档之类的文件构建书籍。
技术细节
我们使用自定义解决方案来打包epub,但pdf文件是通过调用XeLaTeX
通过latexmk
生成的。因此,您需要安装LaTeX才能生成pdf!此外,如果您想在pdf中包含图像,您还需要安装pdftocairo
。
从架构上讲,这个crate是一个非常薄的包装器,覆盖了
bookbinder_ast
,它定义了一个抽象的书源,以及bookbinder_epub
和bookbinder_latex
,它们定义了如何将该源渲染成特定的输出格式以及渲染的各种选项。
因此,要详细了解某物的工作方式,您最好查看特定的crate!这种分离的设计旨在允许将来添加不同的后端 - 添加创建html书籍的方法将很有趣,并且可能有一种替代方法来生成pdf也可能很有价值,因为LaTeX很漂亮但速度慢,并且安装它是一件大事。最有可能的候选者是neatroff
或SILE
的嵌入版本。
反序列化
这个crate包含两个函数create_pdf_from_json
和create_epub_from_json
,它们支持从输入json反序列化和渲染,而不是通过手动构建对象来控制流程。
那里使用的json代表了一种简化的格式,它对流程的控制稍微少一点,主要是为了使反序列化更容易、更易于理解。
有关详细信息,请参阅deserialization
模块。
依赖关系
~28MB
~351K SLoC