Tech Sketch Bucket of Technical Chips by TIS Inc.

D3.jsで作成したグラフ(SVG)を画像として保存する

Pocket

先日公開した D3.jsをつかってData-Drivenにリッチなグラフやチャートを作成しよう の記事ではD3.jsをつかってグラフを作成しました。本記事ではD3.jsで作成したSVG形式のグラフをPNG形式の画像として保存する方法をご紹介します。

D3.jsにはCSVやJSONなど 様々なデータ形式を扱う為の便利なメソッド が用意されており、アプリケーションへの組み込みが容易です。しかしアプリケーションへの組み込みとなると、「X月X日の状態のグラフを保存しておきたい」、「グラフを資料に添付したい」といった要望も出てくるのではないでしょうか。

単純に画面のスクリーンショットを取得する以外では Phantom.js を利用してキャプチャを取得する方法や Apache Batik を利用してSVGを変換する方法などが考えられましたが、クライアントサイドだけで完結しない上、実装が少々手間です。

本記事ではcanvgというJavaScriptのライブラリを利用し、SVGを元に作成したCanvasを画像として保存する方法をご紹介します。
この方法はクライアントサイドだけで完結しますし、実装も容易ですので導入の敷居も低いかと思います。

以下でサンプルを交えながら、具体的な方法と注意点などを紹介していきます。

canvgとは

canvg はその名の通り?(canvas+svgだと思っています...) SVGを元にCanvasを描画するJavaScriptライブラリです。

利用するには canvgのサイト からファイルをダウンロードするか以下を記述します。

ライセンスは MIT License ですので商用利用可能です。

利用方法は簡単で、canvgメソッドの第1引数に描画する対象のcanvas要素を、第2引数にはSVGのテキスト、またはSVGファイルへのパスを指定するだけです。
第3引数はオプションです。offsetやscaleなどの指定が可能です。詳細はサイトにてご確認ください。

なぜCanvasにする必要があるのか

SVGは、それ自体を画像に変換したりダウンロードすることは出来ません。Canvasは画像に変換出来るのですが、SVG同様ダウンロードすることは出来ません。画像は、a要素やJavaScriptのライブラリを使うことでダウンロードすることが出来ます。

SVG Canvas 画像
画像への変換 -
ダウンロード

canvgはSVGを元にCanvasを描画してくれるので、CanvasをtoDataURLで画像(Base64エンコードしたバイナリデータ)に変換し、a要素の参照先にdata URI schemeで表した画像データへのリンクを設定、download属性にファイル名を指定すれば画像のダウンロードが可能です。

changeFlow.png

SVGで作られたグラフをCanvasに変換し、PNGとして保存する

今回は単純なグラフで試せれば良かったので、 dimple.js を利用してサンプルを作成してみました。
dimple.jsはd3.jsベースのJavaScriptライブラリで、d3.js単体で利用するよりも簡単に基本的なグラフやチャートを作成できるようになっています。
他にも NVD3.jsdc.jsRickshawxCharts などD3.js関連で優れたライブラリがありますので、用途や好みに合わせて検討してみると良いでしょう。

サンプル

dimple.js_graph.jpg

サンプルでは 内閣府 が提供している、内閣府 2009年度国民経済計算(2000年基準・93SNA)- 4. 主要系列表 - (3) 経済活動別国内総生産 - 実質(固定基準年方式) (Excel)をCSV化してデータとして利用しました。

こちらのサンプルは "Create Download Link" ボタンを押すと画像データへのリンクが作成されるようになっています。
ダウンロード用のリンクをクリックすると、自動的に画像ファイルがダウンロードされます。
※IE、Safariではa要素のdownload属性をサポートしていません。対応ブラウザは こちら からご確認ください。

まずはHTMLとCSSから説明していきます。

[1] SVGのグラフ描画用div要素

ページを表示すると <div id="graph"></div> の子要素にSVGでグラフが作成されます。

[2] Canvasのグラフ描画用div要素

ボタンを押すと、<div id="canvasArea"> の子要素のCanvasにSVGと同様のグラフが作成されます。
CSS<div id="canvasArea"> に対して display: none; を指定している為、表示はされません。

[3] ダウンロード用リンク作成

ボタンを押すと以下のJavaScriptが実行されます。

[3-1] SVGをテキスト形式で取得

canvgに '<svg>...</svg>' の形式でデータを渡すために、SVGの親要素である <div id="graph"></div> からinnerHTMLで文字列を取得します。

[3-2] canvgでSVGを元にCanvasに描画

取得したSVGテキストを元に、canvgでCanvasに変換し、Canvas要素にグラフを作成します。

[3-3] Canvas上の画像をdata URI schemeで表した画像データへのリンク作成

Canvas要素に作成されたグラフデータをtoDataURL()でdata URI schemeに変換し、a要素の参照先に設定します。

FileSaver.jsを使ってワンクリックでダウンロードする

ここまで、ボタンを押してダウンロード用のリンクを作成する方法を説明してきましたが、画像をダウンロードする為のオペレーションが2段階になっていて少々手間です。ボタンを押したら画像をダウンロードして欲しいですよね。FileSaver.jsを使い、改善してみます。

FileSaver.jsとは

FileSaver.js は、クライアントサイドでデータの生成、保存を行うJavaScriptライブラリです。 デモ を見てもらうと分かりますが、画像だけでなくテキストやxhtmlなども保存出来ます。

コードの修正

まずは、ライブラリの読込を追加します。
FileSaver.jsと CanvasからBlob形式に変換する為に canvas-toBlob.js を利用します。ソースコードはそれぞれ、Githubから取得しました。

ボタンを押した際に実行されるJavaScriptを以下のように修正し、画像を保存できるようにしています。コードは、[3-3]部分をFileSaver.jsとcanvas-toBlob.jsの機能を使って保存できるように置き換えただけです。元のコードと比べてみても簡単に利用できることが分かるかと思います。

ワンクリックで画像をダウンロードするサンプル

サンプルで出力される画像ファイル

ダウンロードした画像ファイルを確認してみます。

svg_to_png.png

いかがでしょうか?実際に表示されているSVGのグラフとほとんど差異無く表示されていると思います。
実際は少々違いがありますが、その点は以下に記述する注意点で説明します。

注意点

[1] 見た目がぼやける

SVGのグラフと作成した画像では少々違いがあると上述しましたが、よーく見ると線がぼやけているのが分かると思います。軸線が分かりやすいので、拡大して確認してみてください。
SVGではshape-rendering="crispEdges"でアンチエイリアスを無効化することができるのですが、Canvasにはアンチエイリアスを無効化する仕組みがまだ実装されていないため、奇数ピクセルの幅を持った線などはぼやけた見た目になってしまいます。

[2] CSSで設定した内容がcanvgでは読み込まれない

CSSで設定した内容をcanvgは読み込んでくれません。
D3.jsでSVGを作成する際には、自身で属性を設定する必要があります。

[3] fillを省略した場合黒くなる

fillの指定が無い場合、SVGからCanvasへ変換する際にfillが黒として扱われてしまいます。明示的にfill="none"か任意の色を指定することでこの問題は回避できます。

以下に[2][3]の例を示します。
D3.jsの Gallery にある Multi-Series Line Chart というグラフから、canvgでCanvasに変換し画像を取得してみました。

このサンプルではCSSでfillやstrokeなどを設定しています。[2]で説明した通りcanvgではCSSが読み込まれませんので、fillやその他の設定が反映されません。その結果、[3]のようにfillが省略されたと見なされ黒くなってしまっています。他にもCSSで設定していた内容は反映されていないはずです。

not_set_fill.png

[2]でも説明しましたが、以下のように自身で必要な属性を設定することで正しく表示させることが可能です。サンプルでは、d3.tsvメソッドの最後に記述すれば良いでしょう。

※上記は例です。実際には.axis lineや.x.axix path、.lineに対しても適切に属性を設定する必要があります。

[4] JPEG形式で保存する場合、透過色が黒くなる

toDataURLメソッドとtoBlobメソッドには引数として、パラメータなしのMIMEタイプを指定することが出来ます。
image/jpegを指定するとJPEG形式として保存することが出来ますが、JPEGが不透明度を表現するアルファチャンネルに対応していない為、透過色が黒くなってしまいます。

まとめ

D3.jsで作成したSVGのグラフを画像として保存する方法を説明してきました。
必要なコードの量も少なく難しい内容もほとんど無い為、すでにグラフをアプリケーションに組み込んでいる場合でも簡単に導入することが出来るのではないでしょうか。

今回はSVGを変換して画像として保存する方法を説明しましたが、 Chart.js のようにCanvasでグラフを作成する場合でも保存可能です。
...SVGを取得してcanvgで変換する処理を削るだけですね!

このブログは、D3.jsの導入を検討している部門の方からグラフを保存する方法は無いかと問い合わせを受け、調査した内容をまとめたものになります。 D3.jsについて書いた記事への反応も多く、実際に導入を検討している事例も増えつつあるようです。
注目を集めどんどん進化している領域ですので、今後も継続してウォッチしていこうと思います。

エンジニア採用中!私たちと一緒に働いてみませんか?