Deno で操作する PDF

Deno で PDF を操作する方法を調べたので、まとめる。

参考

Deno で PDF を操作する

PDF を結合

1
2
3
import { mergePdfs } from "https://deno.land/x/pdfrex@v0.0.2/mod.ts";

mergePdfs(["page1.pdf", "page2.pdf", "page3.pdf"]);

merged.pdf という名称で指定された PDF が結合したファイルが生成される。

PDF を分割

1
2
3
import { splitPdf } from "https://deno.land/x/pdfrex@v0.0.2/mod.ts";

splitPdf("merged.pdf");

./merged/ ディレクトリに1pずつ分割された PDF が生成される。

pdfrex は回転などもできる紹介があるが、モジュールでは提供されていない。
pdf-lib に依存しているが、このモジュールは3年近く更新されていない様子。

PDF を作る

PDFを作れそうなモジュールはいくつかある。

試してみいたが、Deno で動かすのは難しそうだった。
中を見ると phantom.js か、puppeteer が使われているようだった。
phantom.js はもうメンテされていないので、puppeteer を使うのが良さそう。

以下のような実装でPDFを作ることができる。

マークダウン => PDF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts";
import { parse as markedParse } from "npm:marked";

const sourceMarkDown = Deno.readTextFileSync("source.md");

const html = markedParse(sourceMarkDown);

console.log(html);

const browser = await puppeteer.launch({});
const page = await browser.newPage();
await page.setContent(`<html><body>` + html + "</body></html>");

await page.pdf({
path: "page.pdf",
format: "A4",
});

page.close();
browser.close();

Deno.exit();

マークダウン => PDF(スタイリング/ヘッダー/フッター)

jsonから設定を読み込む形で、複数のファイルを分割で作成できるようにした。

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
import puppeteer from "https://deno.land/x/puppeteer@16.2.0/mod.ts";
import { parse as markedParse } from "npm:marked";
import { parse as jsoncParse } from "jsr:@std/jsonc";

const setupText = Deno.readTextFileSync("setup.json");
const setup = jsoncParse(setupText);

const mds = [];

for (const document of setup.documents) {
const mdText = Deno.readTextFileSync(document.file);
mds.push(mdText);
}

const html = markedParse(mds.join("\n\n"));

const browser = await puppeteer.launch({});
const page = await browser.newPage();
await page.setContent(`<html><body>` + html + "</body></html>");

await page.pdf({
path: "output.pdf",
format: "A4",
margin: { top: "100px", bottom: "60px" },
displayHeaderFooter: true,
headerTemplate:
`<div style="display: inline-block; width: 75%; margin: 0 2cm">
<img style="height: 50px;" />
<div style="float: right; font-size: 17pt; text-align: right;">Header</div>
</div>`,
footerTemplate: `
<div style="margin: 0 2cm; width: 75%; font-size: 17px; text-align: center;">
Footer<span class="pageNumber"></span>/<span class="totalPages ">
</div>
`,
});

page.close();
browser.close();

Deno.exit();

次のような、jsonをセットアップ用のjsonを作成する。

1
2
3
4
5
6
7
8
9
10
11
{
"documents": [
{
"file": "./source.md"
},
{
"file": "./source2.md"
}
]
}

読み込み対象のマークダウンには、cssを書けるので、スタイリングも可能。
以下がマークダウンのサンプル。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<style >
* {
color: #600;
}

h1 {
page-break-before:always;
}
</style>

# h1

## h2

### h3

#### h4

##### h5

###### h6

`text`

全部赤くして、h1 が入るとページを分けるようにしている。

作成されたファイルは次のようになる。


Deno でPDFを取り扱う方法を調べました。

PDF周りは仕様に大きな動きが無いことから、あまりメンテされていないモジュールでも第一線で使われているように見えます。
npmから直接 puppeteer を使うことは少し難しそうなので、deno.land/x から使うのが良さそうです。

では。