2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vivliostyle CLIのPDF出力時に、裏で表示されているVivliostyle ViewerのHTMLを編集する/出力されるPDFを加工する

Posted at

「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
theme/package.json
{
  "name": "theme",
  "vivliostyle": {
    "theme": {
      "style": "style.css"
    }
  }
}
theme/style.css
@page {
  size: A4;
}

a::before {
  content: target-counter(attr(href url), page);
}
manuscript.md
<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は次のように記述してください。

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!

image.png

同様にhooks.pdf.afterも用意しています。ここではブラウザが書き出したPDFに対する加工を行えます。

  1. unified/remark(mdast)/rehype(hast)やCSSの工夫で実現するべきで、Vivliostyleの実装の詳細に依存するのは全く良いアイディアではありません。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?