mdbook-pdf: 用于生成 PDF 文件的 mdBook 后端

简介

mdBook 允许您从 Markdown 文件创建书籍。它与 Gitbook 非常相似,但使用 Rust 实现。但是,与 Gitbook 支持使用 calibre 生成 PDF 不同,长期以来,mdBook 不支持原生生成 PDF 文件功能,并且支持此功能也不在他们的计划内。现有的插件(后端),例如利用 Tectonicmdbook-latex 以及pandoc 解决方案将生成一个与现有 mdBook 生成的 HTML 版本不统一的 PDF 页面。考虑到这些,我因而创建了一个名为 mdbook-pdf 的 mdBook 后端,基于headless chromeChrome 开发工具协议 Page.printToPDF生成PDF文件。

mdbook-pdf 依赖于 Google Chrome / Microsoft Edge / Chromium。生成的PDF页面与您手动在浏览器中通过打开print.html或执行google-chrome-stable --headless --print-to-pdf=output.pdf file:///path/to/print.html的方法十分类似,但 mdbook-pdf 支持自定义PDF纸张方向、页面缩放比例、纸张宽度和高度、页面边距、生成的PDF页面范围、是否显示页眉和页脚以及自定义其格式等,以及实现了自动化生成PDF。它支持 Google Chrome / Microsoft Edge / Chromium 可以工作的所有平台。您可以在这里对应的 Artifacts 中查看生成的 PDF 文件的示例(生成 PDF 文件的平台包括 x86_64 的 Windows、macOS 以及Linux)。

安装和使用

由于它是 mdBook 的插件(后端),首先您应该确保 mdbook 可用。

如果您的计算机的架构为x86_64,或者ARM64版本的Linux,请检查成功的build GitHub Actions workflows,单击最新的一次运行记录,然后您可以从 Artifacts中获取二进制文件(包括 WindowsLinuxmacOS)。

否则,确保 rust 编译环境可用,执行cargo install mdbook-pdf编译安装即可。

如果需要最新版的编译二进制文件,请确保 Rust 编译环境可用(cargo build),然后执行git clone https://github.com/HollowMan6/mdbook-pdf.git,在然后在克隆下来的文件夹中运行cargo build --release,在target/release/中获取可执行文件,并将其放入PATH。

为了使得程序能够正常运行,请确保计算机上安装了 Google Chrome / Chromium / Microsoft Edge,(安装在默认的位置,在当前的PATH中,或配置了二进制文件位置),因为现在自动下载 Chromium 功能还不可用.

  • 在Windows 10及以上该程序无需安装任何额外软件即可正常生成 PDF,因为 Microsoft Edge 是 Windows 系统自带的浏览器。当然如果考虑到对没有自带安装 Edge 的老版本Windows的支持,在电脑上安装一个 Google Chrome 即可。
  • 在 macOS 中需要下载并安装 Google Chrome 或者 Microsoft Edge 或者 Chromium。
  • 在 Linux 中安装Google Chrome / Chromium / Microsoft Edge 浏览器中的任意一个即可,推荐安装 Chromium,该软件包在您的发行版中一般名称为 chromiumchromium-browser(注意,在 Ubuntu 18.04 之后需要通过 snap 安装 chromium-browser)。

请确保您的book.toml中存在以下内容:

[output.html]

[output.pdf]

而且,[output.html.print]也没有被禁用(默认情况下应该是启用的,所以如果您的book.toml中没有出现以下行,请不要担心)。

[output.html.print]
enable = true

一个最简单的book.toml文件示例如下:

[book]
title = "An Example"

[output.html]

[output.pdf]

最后,您可以使用 mdbook build 命令生成书籍并获取PDF文件,您的PDF文件将被存放在book/pdf/output.put

PDF生成过程示例

配置

支持自定义PDF纸张方向、页面缩放比例、纸张宽度和高度、页面边距、生成的PDF页面范围、是否显示页眉和页脚以及自定义其格式等。

查看 book.toml 以了解 [output.pdf] 可用配置的详细信息。

具体参数详解

  • trying-times

接受输入一个整型数,默认为1。其指定假如发生PDF生成失败的情况重试的次数。

  • browser-binary-path

接受输入一个字符串,默认为空'',程序自动判断路径。其指定浏览器可执行文件路径。

本程序支持最新的基于Chromium的浏览器,不支持Safari和Firefox。如果你需要指定,请指定完整的路径,比如说/usr/bin/foo。如果指定了错误的可执行文件,则很可能会出现超时错误或者直接报错。

  • landscape

接受输入一个布尔值,默认为false。其指定PDF纸张方向,true为横向,false为纵向。

  • display-header-footer

接受输入一个布尔值,默认为false。其指定是否显示页眉和页脚,true为显示,false为不显示。

  • print-background

接受输入一个布尔值,默认为false。其指定是否在PDF中显示背景图片,true为显示,false为不显示。

  • scale

接受输入一个数字,默认为1。其指定缩放因子,例如指定值为1.25,则将页面缩放125%。

  • paper-width

接受输入一个数字,默认为8.5。其指定页面宽度的英尺数,如果需要使用A4纸请将此值设为8

  • paper-height

接受输入一个数字,默认为11。其指定页面高度的英尺数,如果需要使用A4纸请将此值设为10

  • margin-top

接受输入一个数字,默认为1。其指定页面上边距的厘米数。

  • margin-bottom

接受输入一个数字,默认为1。其指定页面下边距的厘米数。

  • margin-left

接受输入一个数字,默认为1。其指定页面左边距的厘米数。

  • margin-right

接受输入一个数字,默认为1。其指定页面右边距的厘米数。

  • page-range

接受输入一个字符串,默认为空'',即不截取PDF页面。其指定生成PDF文件页面截取范围,支持指定常见打印机格式页面范围,如'1-5, 8, 11-13'则是将第1到5页以及第8页和第11到13页截取出来生成。

  • ignore-invalid-page-ranges

接受输入一个布尔值,默认为false。其指定,如果上面指定的PDF文件页面截取范围格式正确,但是实际无法按照语义执行,是否忽略。true为忽略,生成全部PDF页面,false为进行报错,如在指定3-2这种情况下将会报错,PDF生成失败。

  • header-template

接受输入一个字符串。其指定PDF文件页眉的HTML模板。其值应该是一个有效的HTML标记,并使用以下类从而将对应值插入其中:

  • date: 格式化后的PDF生成日期
  • title: 书的标题
  • url: PDF文件存放路径
  • pageNumber: 当前页号
  • totalPages: 总共页数

例如,'<span class=title></span>' 将生成一个包含标题的页眉。

  • footer-template

接受输入一个字符串。其指定PDF文件页脚的HTML模板。其值的格式同header-template。

  • prefer-css-page-size

接受输入一个布尔值,默认为false。其指定是否使用 CSS 定义的页面大小。true为使用,false时页面将通过缩放来适应纸张大小。

致谢

本项目依赖于 headless_chrome。因其一直未发布新版,且默认超时时间对PDF生成不友好,所以使用了我的Fork 版本发布了mdbook-pdf-headless_chrome,将相关超时时间扩大为300秒,作为本项目的子模块,从而使得项目能够发布在Crates.io

(注:因为注意到书名中很容易出现特殊字符,比如说:,这样的话会导致生成相关PDF文件失败,所以文件名没有使用<书名>.pdf这种的形式,而是统一用output.pdf)

一些笔记和想法

mdBook 支持添加可选的额外后端。当调用mdbook build命令时,如果book文件夹中的book.toml除了生成html网页的默认[output.html]之外还有[output.pdf]项,会调用mdbook-pdf,将相关书籍信息和参数配置通过JSON的形式传递给程序的标准输入。相关的 mdBook 文档可以在 这里 找到。
mdbook-pdf背后的机制
无头浏览器意味着所有操作都将在后台进行,无需图形界面。

在决定使用Chrome 开发工具协议 Page.printToPDF之前,我也尝试过[wkhtmltopdf](https://wkhtmltopdf. org/),它基于 QT4 Webkit。但是,wkhtmltopdf 似乎不支持 CSS 打印 @media 规则,这会使一些额外的页面组件在 PDF 中可见并被打印出来。

我也尝试过基于W3C WebDriver 协议打印页面直接在python中对WebDriver进行HTTP调用,以及在 Python 中使用 selenium 调用 Chrome 开发工具协议 Page.printToPDF 。所有这些方法对于大页面生成PDF的需求并不健壮,会报出以下错误,原因与原始上游 headless_chrome 超时错误的原因相同:默认超时为 10 秒,对大页面PDF生成不友好。

timeout: Timed out receiving message from renderer: 10.000
selenium.common.exceptions.WebDriverException: Message: unknown error: session deleted because of page crash
from unknown error: cannot determine loading status
from tab crashed

目前我测试过 Firefox 不支持使用headless_chrome生成PDF,Safari 浏览器甚至不支持W3C WebDriver 协议打印页面,更不用说 Chrome DevTools 协议了。

我注意到,对于某些书,通过使用此后端,当单击书内链接的某些链接时,将打开一个指向该书原始生成的 HTML 存储路径的 html 页面,就像这里的问题提到的。我想对于书中的那些“内部”链接,应该在 mdbook 侧处理,当为 print.html像这里所述的来处理,这样所有“内部”链接的链接都会跳转到生成的print.html自身当中,因为所有内容都应该已经在print.html上,不应该有任何超链接跳转到这本书的其他html文件。通过这种方式,生成的 PDF 也会在内部跳转,而不是打开一个无法连接成功的的浏览器。目前我已经向上游提出了一个PR,希望能够被尽快合并。

另外由于我发现书名中很可能出现:等特殊字符,会导致相关PDF文件生成失败,所以文件名不使用<书名>.pdf的形式而是使用output.pdf

希望你喜欢它!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值