「Vivliostyleのプレビューで表示されているHTMLをPDF出力前に編集できたらアレができるのに」と考えたことはありませんか?おそらくそのアイディアは筋が良くないので一旦忘れたほうがよいでしょう1。忘れたほうがよいのですが、できないことはありません。たとえばleader()
の実際の長さやtarget-counter()
の解決済みの値を知りたい場合に役に立つかもしれません。
playwright-coreのうちVivliostyle CLIが使う部分をProxy
でラップしただけのパッケージを用意しました。
まず執筆用のプロジェクトを用意します。Vivliostyle CLIが使っているplaywright-coreをこのパッケージでオーバーライドし、執筆プロジェクトにもインストールします。Vivliostyle CLIはパッチバージョンまで固定します。
$ mkdir example && cd example
$ cat <<EOF > package.json
{
"name": "example",
"type": "module",
"dependencies": {
"@vivliostyle/cli": "9.7.0",
"playwright-core": "npm:@u1f992/vivliostyle-cli-pdf-hook@0.1.0"
},
"overrides": {
"@vivliostyle/cli": {
"playwright-core": "npm:@u1f992/vivliostyle-cli-pdf-hook@0.1.0"
}
}
}
EOF
$ npm install
$ npx vivliostyle init
CSSと原稿は次の通り用意します。target-counter()
を使用したa
要素には、id="foobar"
の要素が配置されたページ番号が表示されるはずです。ですが何ページになるのかは原稿からはわかりません。何かの事情で、この値を取得したいとします。
$ tree -I node_modules
.
├── manuscript.md
├── package-lock.json
├── package.json
├── theme
│ ├── package.json
│ └── style.css
└── vivliostyle.config.js
2 directories, 6 files
{
"name": "theme",
"vivliostyle": {
"theme": {
"style": "style.css"
}
}
}
@page {
size: A4;
}
a::before {
content: target-counter(attr(href url), page);
}
<a href="#foobar" data-pseudo-id="665ca512-9bd5-49c8-8431-a96fb88cd62b"></a><!--
Vivliostyleに処理されたあとで検索できるID値が必要
id属性やhref属性は、文書全体で通るように処理の過程で置き換えられることがある
data-*属性なら、Vivliostyleの内部処理で使う命名と衝突していなければ保持されるようだ
-->
<div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div style="break-after: page"></div><div id="foobar">foobar</div>
vivliostyle.config.jsは次のように記述してください。
// @ts-check
import fs from "node:fs";
import path from "node:path";
import { hooks } from "playwright-core";
hooks.pdf.before = async ({ page }) => {
process.stdout.write("\n");
const html = await page.evaluate(() => document.documentElement.outerHTML);
fs.writeFileSync(path.join(process.cwd(), "output.html"), html, {
encoding: "utf-8",
});
const textContent = await page.evaluate(
() =>
document.querySelector(
'[data-pseudo-id="665ca512-9bd5-49c8-8431-a96fb88cd62b"]',
)?.textContent,
);
process.stdout.write(`resolved: ${textContent}\n`);
};
/** @type {import('@vivliostyle/cli').VivliostyleConfigSchema} */
const vivliostyleConfig = {
title: "example",
theme: "./theme",
entry: ["manuscript.md"],
};
export default vivliostyleConfig;
いま"playwright-core"
の名前で実際にインポートされているのは先ほどの@u1f992/vivliostyle-cli-pdf-hookです。このパッケージは、playwright-coreのすべてのメンバーに加えて、hooks
を公開します。hooks.pdf.before
に登録した関数は、同じくplaywright-coreを参照しているVivliostyle CLIがPage.pdf
を実行する前に割り込んで実行されます。ここで、PDF出力直前に表示されているVivliostyle ViewerのHTMLの保存や変形を行うことができます。
実行すると、以下の通りVivliostyleが解決した後の値を取得することができています。
$ npx vivliostyle build
INFO Start building
INFO Launching PDF build environment
INFO Building pages
▁║▁ Building PDF
resolved: 42
INFO Building PDF
INFO Processing PDF
SUCCESS Finished building example.pdf
📙 Built successfully!
同様にhooks.pdf.after
も用意しています。ここではブラウザが書き出したPDFに対する加工を行えます。
-
unified/remark(mdast)/rehype(hast)やCSSの工夫で実現するべきで、Vivliostyleの実装の詳細に依存するのは全く良いアイディアではありません。 ↩