SVGからPDFへの変換はHeadless Chromeでやろう（と思ったけどやっぱりrsvg-convertでやろう）

# 2021/09/09追記: やっぱり`rsvg-convert`でやろう

Headless Chromeはpattern fillがラスタ画像になってしまったり、コメントでご指摘を受けた通りなんだかグリッド線が太くなっていたり、なんだかんだそんなに良いものではないことが分かってきました。

そんな中、試しに`rsvg-convert`の新しいバージョンで再度試してみたら以下で紹介しているようなはみ出しバグが消えていることに気づきました。

試した`rsvg-convert`のバージョン:

```
$ rsvg-convert --version
rsvg-convert version 2.50.3
```

rsvg-convertならグリッド線が太くなることもないしpattern fillもちゃんと扱えるので、**やっぱりHeadless Chromeではなく`rsvg-convert`でやるのがベストっぽい**です。

以下の記事の内容は古いですが、ログとして一応残しておきます。

# はじめに

LaTeXがsvgに対応していないというのは深刻な問題で、古代ギリシャではそれに怒った民衆が国を滅ぼしたとも伝えられます。

にも関わらず令和の時代になってもまともなsvgサポートがLaTeXでなされないのはそれだけsvgの仕様が複雑ということでしょうか。
現代においてすら、我々はsvgからpdfへの変換を余儀なくされています。

しかし最近、svg -> pdfの変換はそう簡単なものではないらしく、inkscapeですらまともに変換できない場合があることに気付いてきました。

各種ライブラリを試してみましたが、結論から言えば、**Google ChromeのheadlessモードでのPDF出力**が最もまともな変換をなすということが分かりました。身も蓋もない。

ということでsvg -> pdfの変換を行うシェルスクリプトを作りました。先に置いておきます。

svg2pdf.bash:
https://gist.github.com/s417-lama/84bf66de1096c4587e8187092fb41684

```
$ ./svg2pdf.bash plot.svg plot.pdf
```

chromeがインストールされていれば動くと思います。
`google-chrome`というコマンドは環境依存かもしれないので適宜対応してください。

__※ 2021/07/29 追記:__ Headless Chromeでもpattern fillはあんましちゃんと出力してくれないことが分かったので一概にHeadless Chromeが最良の選択肢とも言えなくなってきました。
記事の末尾参照。

# 各種ライブラリの（簡単な）検証

ここでは実際のsvgの例を通して各種svg -> pdf変換ライブラリの出力を確認していきます。

使用するのは[Plotly.js](https://plotly.com/javascript/)で描いたグラフをsvg出力したものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/44c233a6-bad7-dd7e-e040-145c784a6555.png)

環境はUbuntu 18.04です。

## Inkscape

LaTeXでsvgを貼りたいよstackoverflow〜〜〜〜。と言うと真っ先に返ってくるのが「inkscapeでPDFに変換しろ」という答え。はたして上手く行くのか。

```
$ inkscape --version
Inkscape 0.92.3 (2405546, 2018-03-11)
```

```
$ inkscape -D -z -f plot.svg -A plot.pdf
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/26ba5708-f741-d1cd-2dc5-061a192fe55c.png)

さすがはinkscapeですね。問題なくPDFに変換され、、、おっと危ない。`foo`の点線が変ですね。
svgでは`foo`と`bar`の点線は区別されていたのに一緒になっています。これは結構困る。

## rsvg-convert

```
$ apt install librsvg2-bin
```
で入るやつ。librsvgというライブラリ。

```
$ rsvg-convert --version
rsvg-convert version 2.40.20
```

```
$ rsvg-convert -f pdf plot.svg -o plot.pdf
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/abbd1d6d-d1bb-0127-1d3e-2d3737277bc0.png)

惜しいですね。x軸からはみ出してます。

**2021/09/09追記: 少なくともバージョン2.50.3ではこのバグが消えていました。**

## Cairosvg

https://cairosvg.org

```
$ cairosvg --version
1.0.20
```

```
$ cairosvg plot.svg -o plot.pdf
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/86b064bc-201b-a9af-6173-ee1e5f8e05e6.png)

同じくはみ出してます。あとなんかよく見るとフォント変わってません？？

## Headless Chrome

先程の。

svg2pdf.bash:
https://gist.github.com/s417-lama/84bf66de1096c4587e8187092fb41684

```
$ google-chrome --version
Google Chrome 81.0.4044.129
```

```
$ ./svg2pdf.bash plot.svg plot.pdf
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215516/17b7d524-9977-ff6f-a2fd-549adcbd0169.png)

良いですね。ちゃんと変換されているようです。

# `svg2pdf.bash`の説明

svg2pdf.bash:
https://gist.github.com/s417-lama/84bf66de1096c4587e8187092fb41684

このスクリプトが何をしているのか簡単な説明です。

基本的にはchromeのheadlessモードでPDF印刷をすればいいので、

```
$ google-chrome --headless --disable-gpu --print-to-pdf=plot.pdf plot.svg
```

基本的にはこれを走らせれば良いわけですが、このままでは印刷特有の謎のheader, footerが表示される上、サイズがA4で出てきてしまうのでこの辺を調整してやる必要があります。

shell scriptと言っても中身はほぼhtmlとjsなのですが、svgのサイズをjsで抜き出して印刷設定のCSS attributeを設定しています。

# おわりに

ベクター画像の代表たるsvgですが、もはやブラウザしかまともに取り扱えない仕様になってしまっているんですかね。
でもよく考えればsvgにcssとか設定できるしブラウザが得意なのは当然なのかも。

ということで、svg -> pdfの変換にはchromeを使おう、という話でした。おしまい。

# 参考

- https://superuser.com/questions/381125/how-do-i-convert-an-svg-to-a-pdf-on-linux
- https://stackoverflow.com/questions/44970113/how-can-i-change-paper-size-in-headless-chrome-print-to-pdf/56844278#56844278
- https://stackoverflow.com/questions/44575628/alter-the-default-header-footer-when-printing-to-pdf

# （2021/07/29 追記）pattern fillへの対応状況

SVGのpattern fillをベクタ形式のままPDFに出力してくれるかどうか確認しました。pattern fillは

https://qiita.com/s417-lama/items/c039107fb2defd960493

こんな感じのやつ。

結果としては、

- Inkscape: OK
- rsvg-convert: OK
- Cairosvg: patternが消える
- Headless Chrome: ラスタ画像になっちゃう

一概にHeadless Chromeを使うべきとも言えなくなってきた。うーむ。
場合に応じて使い分けましょう！