🖨️

deck2pdfの刷新をしたら自作スライドのエクスポート体験がすごく向上した

2024/12/25に公開

数年前に deck2pdf というPythonプロジェクトを公開していました。
その後はしばらくdecktapeを使っていたのですが、uvがuvxによる実行をサポートしだしたのをきっかけに結構な刷新をしました。

なんと、およそ9年ぶりのバージョン更新です。 [1]

更新リリース自体は今年の中盤ぐらいだったのですが、アドカレを理由に振り返ってみることにしました。

先代 deck2pdf

https://github.com/attakei/deck2pdf-python-v0.5

そもそもdeck2pdfとは

名前の通り、プレゼンテーションをPDFにエクスポートするツールです。
PowerPointやKeynoteのようなタイプのアプリには標準的にPDF出力する機能を持っていることが普通であるため、必然的に「HTMLプレゼンテーションをきれいにPDF化する」ものと言えるでしょう。

もともとの動機自体は今も同じで、自分のプレゼンテーション(HTML製)をSpeakerDeck等にバックアップとしてアップロードしておくPDFを生成することがゴールです。
v0.5の時点で、html5slideとGoogle IO 2012のみに対応して更新が停滞してました。

内部実装の簡易解説

基本的な動作は、このような感じです。これは今代でも大きく変わっていません。

  1. ブラウザを起動する。
  2. 指定したURLにアクセスする。
  3. スライド1枚毎をキャプチャしていく。
  4. キャプチャしたファイルを纏めて、PDFとして保存する。

なお、ブラウザ部分の動作にはGhost.pyを採用していました。時代を感じますね。 [2]

また、キャプチャ自体もシンプルなPNG出力のみとなっています。つまりPDF化した後にファイル内検索しても何も引っかかりません。
こちらは、時代というよりも「テキストを残したままきれいなキャプチャをする」ような実装をする技術力が当時なかったせいでしょう。

なぜ更新を止めたのか?

今振り返っても、明確な要因が思い出せずにいます。
ただ、十中八九で後述のdecktapeを発見したことが理由と考えて良さそうです。

decktape

さて、しばらく経ってからとある事情で探してみたら見つかったのがこのdecktapeです。[3]

https://github.com/astefanutti/decktape

decktapeの位置づけ

decktapeはNode.js製で実装された「HTMLプレゼンテーションをPDFにエクスポートするCLI」で、「deck2pdfのNode.js版」と言ってよいでしょう。 [4]
使い方もあまり変わらなく、npm install -gなどで実行可能にして下記のコマンドを実行するだけです。

$ decktape https://slides.attakei.net/pyconjp-2022/ output.pdf

かなり多くのHTMLプレゼンテーションをターゲットにしており、指定したURLにアクセスした後の状態をもとに自動判別してくれます。

Puppetterベースの動作

package.jsonなどを見るとわかるのですが、decktapeはブラウザ部分にPuppeteerを採用しています。
PhantomJSなどがWebKitをエンジンに採用しているのに対してPuppeteerはChromium系エンジンであるため、普段ユーザーが使っているブラウザとほとんど同じ挙動をさせたうえでエクスポートできるようになっていました。

このあたりは、更新を止めていたタイミングでのdeck2pdf-pythonでは実現できていない箇所です。

なぜ使うのをやめたのか?

いくつか要因があります。結局のところ「deck2pdf-pythonをイイ感じに改修するモチベーションとアイデアが出た」には帰結するのですが。

ひとつは、日本語表示周りのコントロールに若干の苦戦をしていたことです。
実装側と利用側のどちらに直接的な原因があったのかがハッキリさせられていないのですが、Webフォントまわりの挙動で何故か変な時があり、想定した出力になりにくかった記憶があります。

また、Playwrightの存在も大きいです。他の開発等で触る機会がたびたびあったため、Playwright慣れしていたというものがあります。
これは改修に大きく寄与したポイントで、後ほどもう少し書きます。

最終的に「PyPI上でdeck2pdfの名前空間を持ってるし、手持ちの技術で大きく改善できるんだから自作ツールとしてコントロールしよう」というモチベーションが働き、アップデートにつながっています。

今代 deck2pdf

https://github.com/attakei/deck2pdf-python

さて、v0.5からv0.6で大幅な刷新をしつつ更に細かい調整を進めた結果、現在のdeck2pdfはv0.8となっています。
ここからは、主にどうアップデート実装をしたかについて書いてみます。

Playwrightの採用

基本的な動作自体は先代と全く同じです、「URLにアクセスして画面キャプチャを取ってPDFにする」という概要レベルでは本当に何も変わっていません。
一方で、この表題にもあるようにブラウザ部分にPlaywrightを採用したことで、自身の技術力の向上と相まって大きな違いが生まれました。

1つは、エンジンがChromium系になったことです。これはdecktapeと同じですが、結果的に出力内容への要改善ポイントが大きく解消されました。

また、Playwrightには表示しているページを指定サイズでPDFに出力するメソッドを備えています。これにより、キャプチャが画像ではなくなり、「連続した画像」から「複数ページのドキュメント」といえる形式のPDFエクスポートが可能になっています。

起動時にブラウザを自動インストールする

Playwrightを使ったことがある人は分かると思いますが、Playwrightはライブラリをただインストールするだけでは使えないようになっています。
Playwrightの立ち位置はあくまで「ブラウザを操作するライブラリ」で、必要なブラウザはplawright installを用いて自分でインストールする必要があります。

deck2pdf-pythonでは、CLIでの実行時にブラウザを捜索して未インストールなら自動でインストールするようにしています。
初回挙動やCI/CDでは結構な負担になりますが、実行の手間が結構減っています。
特にこの実装によって、pipx runuvxでの直接実行が非常に簡単になりました。

uvを使っている人なら、これだけでライブラリとブラウザをインストールしてPDFを生成できるようになります。(自分はそうしています)

$ uvx deck2pdf https://slides.attakei.net/pyconjp-2022/ output.pdf

「エクスポート体験」の向上

この実装を進めた結果、いろいろな意味で「エクスポート体験」が向上しています。
大まかにはこのあたりが精神衛生上イイ感じになったと言えます。

  • 自環境にuvがあるから、大体どの環境でも1コマンドでのエクスポートが出来るようになった。
  • 自分で実装した箇所について、仮に「なんか変だぞ?」となっても自分の裁量で適宜対処できるようになった。
脚注
  1. v0.5のリリースコミットが、2016年1月6日。v.06が2024年10月9日。 ↩︎

  2. Phantom.jsを使うオプションもありました。おそらく「なるべくPure Pythonを選択できると良いが、やはりレンダリングのクオリティは重視したい」人向けだったのでしょう。 ↩︎

  3. 記憶と状況をすり合わせる限りでは、おそらくsphinx-revealjsを本格的に使い始めた頃です。 ↩︎

  4. 本家のJava版deck2pdfから見てという意味で。 ↩︎

GitHubで編集を提案

Discussion