生成pdf

初衷是想要做一个能够方便生成pdf的工具,这样可以把自己做的笔记什么的做成电子书或是简单的生成简历等,大概想法就是将markdown文件装换成为html,然后能将html生成pdf。

将markdown转换为html

这一步的例子还是很多的,例如hexo,ghost等做的其实就是这件工作,我主要参考的是build-static-site-generator-nodejs这篇文章。
目录结构:

1
2
3
4
5
6
7
public/ 存放生成后的静态文件
scripts/ 存放执行脚本
src/
layouts/ 存放html模板
pages/ 存放md文件
style/ 存放样式文件
site.config.js 配置相关变量

在script下新建一个build.js文件:

首先清空生成目录

1
fse.emptyDirSync(distPath)

接着在/pages文件夹下去匹配对应的md,html,ejs文件

1
2
3
4
globP('**/*.@(md|ejs|html)', { cwd: `${srcPath}/pages` })
.then((files) => {
// 进行操作
})

对文件中的文件进行操作,如果不存在生成文件夹就创建一个文件夹:

1
2
3
4
5
files.forEach((file) => {
const fileData = path.parse(file)
const destPath = path.join(distPath, fileData.dir)
fse.mkdirs(destPath)
})

获取文件里的数据

1
fse.readFile(`${srcPath}/pages/${file}`, 'utf-8')

用marked对markdown文字进行处理转换成html

1
2
3
4
5
6
7
8
9
10
11
const pageData = frontMatter(data)
const templateConfig = Object.assign({}, config, { page: pageData.attributes })
let pageContent
switch (fileData.ext) {
case '.md':
pageContent = marked(pageData.body)
break
default:
pageContent = pageData.body
}
return {pageContent,templateConfig}

因为要将html转换成pdf所以将对应的css内嵌的html中,当然也可以引用url的,或使用绝对路径。并且将markdown生成的html放到模板中拼接

1
2
var style = fs.readFileSync(`${srcPath}/style/style.css`, 'utf8');
return ejsRenderFile(`${srcPath}/layouts/default.ejs`, Object.assign({}, data.templateConfig, { body: data.pageContent,style:style }))

最后生成文件

1
fse.writeFile(`${destPath}/${fileData.name}.html`, layoutContent)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const fse = require('fs-extra')
const fs = require('fs');
const path = require('path')
const { promisify } = require('util')
const ejsRenderFile = promisify(require('ejs').renderFile)
const globP = promisify(require('glob'))
const config = require('../site.config')
const marked = require('marked');
const frontMatter = require('front-matter');

const srcPath = './src'
const distPath = './public'

// clear destination folder
fse.emptyDirSync(distPath)

// read page templates
globP('**/*.@(md|ejs|html)', { cwd: `${srcPath}/pages` })
.then((files) => {
files.forEach((file) => {
const fileData = path.parse(file)
const destPath = path.join(distPath, fileData.dir)
fse.mkdirs(destPath)
.then(() => {
return fse.readFile(`${srcPath}/pages/${file}`, 'utf-8')
})
.then((data) => {
const pageData = frontMatter(data)
const templateConfig = Object.assign({}, config, { page: pageData.attributes })
let pageContent
switch (fileData.ext) {
case '.md':
pageContent = marked(pageData.body)
break
case '.ejs':
pageContent = ejs.render(pageData.body, templateConfig)
break
default:
pageContent = pageData.body
}
return {pageContent,templateConfig}
})
.then((data) => {
var style = fs.readFileSync(`${srcPath}/style/style.css`, 'utf8');
return ejsRenderFile(`${srcPath}/layouts/default.ejs`, Object.assign({}, data.templateConfig, { body: data.pageContent,style:style }))
})
.then((layoutContent) => {
fse.writeFile(`${destPath}/${fileData.name}.html`, layoutContent)
})
.catch((err) => { console.error(err) })
})
})
.catch((err) => { console.error(err) })
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="UTF-8">
<title><%= page.title ? `${page.title} | ` : '' %><%= site.title %></title>
<meta name="description" content="<%= page.description || site.description %>"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#157878">
<style type="text/css"> <%- style %></style>
</head>
<body>
<section class="main-content">

<%- body %>

</section>
</body>
</html>

生成pdf

获取生成了的html并使用html-pdf插件生成pdf,
新建buildPdf.js 在script文件夹下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var fs = require('fs');
var pdf = require('html-pdf');

const srcPath = './src'
const distPath = './public'

var html = fs.readFileSync(`${distPath}/resume.html`, 'utf8');
var options = {
format: 'Letter',
border: {
top: "0.5in", // default is 0, units: mm, cm, in, px
right: "0.5in",
bottom: "0.5in",
left: "0.5in"
},
};

pdf.create(html, options).toFile(`${distPath}/index.pdf`, function(err, res) {
if (err) return console.log(err);
console.log(res); // { filename: '/app/businesscard.pdf' }
});

完整的代码可以参考这里github