通过 HTML 与 CSS3 制作有逼格的 PDF 书籍
前言
说到印刷物排版,我觉得大多数人都会想到用 Word 来完成任务,但是对于我来说一点都不友好,第一就是它不能绑定快捷键来控制光标,第二就是它不能跨平台,所以我在高一时就开始使用标记语言进行笔记的书写,那时候我用的是 Markdown,现在用的是 Textile。
通过转换器,我们可以将这些标记语言转化成标准的 HTML 文档,通常我都是用 Chrome 打开生成的 HTML 文件然后进行打印。虽然目前的 CSS 规范已经为打印提出了一系列的属性标准,但是看起来没有哪个浏览器很好地支持。所以想输出通过 HTML 输出 PDF 书籍还是一个难题。直到我发现了另一个输出 PDF 的好方法:Prince,它对于非商业用途的使用是免费的。
通过 Prince,我得到了这样的文档:
既然 CSS 为打印提供了一系列的属性,那我们就先来看看这些属性。
规范
你所知道的绝大部分 CSS 属性都可以用在打印输出上。对于打印,我们有 CSS Paged Media Module Level 3 和 CSS Generated Content for Paged Media Module 两个规范,我们下面来看看这两个规范主要有什么。
@page
你可以通过 @page
来指定打印纸张的大小等属性。比如我想用 A6 的纸张来印刷我上面的那本书,我可以在 CSS 中这么写:
@page { size: 105mm 148mm; margin-top: 11mm; margin-bottom: 11mm; margin-inside: 18mm; margin-outside: 10mm;}
size 指定纸张大小,第一个值表示宽,第二个值表示高。margin-top 指定纸张顶部的页边距。margin-bottom 指定纸张底部的页边距。margin-inside 指定纸张装订一侧的页边距。 对于奇数页来说,装订侧表示左侧。 对于偶数页来说,装订侧表示右侧。margin-outside 指定装订侧对边的页边距。
对于 size 属性来说,你还可以这么写:
@page { size: A4 landscape;}
第一个值是纸张名称,第二个值表示横向。
页边盒结构
在打印纸边缘定义有 16 个盒,这些盒可以用来容纳页眉等内容。
@page :first
第一页是封面,不需要页边距?没问题,这个属性可以单独指定第一页的属性。
@page :first { margin: 0;}
强制换页
一节内容结束了,我们不想让下一节的标题跟上,这时候我们可以通过一套属性来完成:
div.newpage { page-break-after: always;}
这样我们就可以通过在 HTML 中加入一个 <div class="newpage"></div>
来强制换页。同时,我们还可以防止在标题后换页:
h1 { page-break-after: avoid;}
另外,我们也不希望表格和图片被分开印在两页中:
table,img { page-break-inside: avoid;}
内容生成
我们可以通过 content
属性来生成页眉等:
@page { @top-right { content: "我是页眉"; }}
页码生成
规范中定义了一个页码计数器,我们可以通过它生成页码:
@page { @bottom-left { content: counter(page); }}
字符串设置
我们不希望页眉永远都是那一串文字,我们想让页面显示出当前页码对应的节标题,我已我们可以这么设置:
h1 { string-set: title content(); /*设置title变量的值为当前元素的内容*/}@page { @top-right { content: string(title); /*并在页眉出显示出来*/ }}
分别设置左右页码样式
我们不希望左右两页的布局都一样,所以 @page
规范中定义有 :left
和 :right
状态选择器。
所以最后的设置是这样的:
@page { size: 105mm 148mm; margin-top: 11mm; margin-bottom: 11mm; margin-inside: 18mm; margin-outside: 10mm;}@page :first { margin: 0;}@page :left { @top-left { margin: 1mm 0 1mm 0; content: string(subtitle); font-size: 9pt; font-family: "AR PL UKai CN"; } @bottom-left { margin: 1mm 0 1mm 0; border-top: 0.25pt solid #666; content: counter(page); font-size: 9pt; font-family: "Droid Sans Fallback"; }}@page :right { @bottom-right { margin: 1mm 0 1mm 0; border-top: 0.25pt solid #666; content: counter(page); font-size: 9pt; } @top-right { content: string(title); margin: 2mm 0 2mm 0; font-size: 9pt; font-family: "AR PL UKai CN"; }}
目录生成
想自动生成目录吗?好像没有多简便的方法。所以我们来手动编写目录:
<div id="catalog"> <h1>目录</h1> <ul> <li><a href="#A">A</a></li> <li><a href="#B">B</a></li> <li><a href="#C">C</a></li> <li><a href="#D">D</a></li> <li><a href="#E">E</a></li> <li><a href="#F">F</a></li> <li><a href="#G">G</a></li> <li><a href="#H">H</a></li> </ul></div>
这样还不够,我们希望做出图片中那样的效果。
我们先把列表前面的圈圈去掉:
#catalog ul { list-style: none; margin: 0; padding: 0;}
然后把超链接的样式清除:
#catalog ul li a { text-decoration: none;}
最后生成页码数字并插入 .
:
#catalog ul a::after { content: leader(".") target-counter(attr(href), page);}
到此为止,一本比较像样的书就做好了。
生成 PDF
我们已经将 HTML 文档做好了,接下来我们使用 Prince 生成 PDF,执行如下命令:
prince book.html book.pdf
完成。