d3.jsを皆さまはご存知でしょうか?data-driven-documentの頭文字をとって、d3.js。JavaScriptでsvgでチャートを書くためのライブラリです。
そして、d3.jsをベースにチャートを生成しやすくしたライブラリがc3.js。
c3.jsで描いたチャートを印刷したかったのですが、d3.js・c3.js共に印刷とかファイルへの保存とかには対応していないみたいだったので、自分でなんとかしてみました。 元々は、highchartsというライブラリを使っていたのですが、有償ということもあって、別にものに変えたかったというのがありました。こちらは、印刷とかpngやpdfにして保存という機能が元々あって便利です。
方針
チャートだけ印刷したかったので、以下の手順で実現することにしました。
- ポップアップウィンドウで別ウィンドウを開く。
- そこに親ウィンドウのsvgタグの内容をマルッとコピーして突っ込む。
- 印刷を起動する。
- 印刷が終わったらポップアップウィンドウを自動で閉じる。
とりあえずは、これでやりたいことは実現できました。
ハマったこと
IE11と旧Edgeの対応具合が悪い
d3.jsでチャートを書くときに、IE11でエラーが起きていました。d3.jsのバージョン5だと、fetchやpromiseを使っているので、それが存在しないということでエラーになっていたので、polyfillを入れました。 また、svgタグの内容をinnerHTMLで取得していたのですが、IE11だとsvgタグ内の内容をinnerHTMLで取得できないという仕様のようでした😨 それもまた、Googleが作っているinnersvg-polyfillというライブラリを入れることで解決しました。
最初はjQueryのouterHTMLを使っていましたが、jQueryに依存した状態になるのも微妙なので、XMLSerializerでNodeを文字列に変換するのがよいという記事を見かけたのでそうすることにしました。
c3.jsのcssを読み込まなければならない
c3.jsはチャートの表示の整形にcssを使っているので、それをポップアップウィンドウ側でも読み込まなければなりません。
IE11がafterprintイベントを拾わない
イベントリスナを定義して、afterprintでポップアップウィンドウを閉じていたのですが、閉じてくれないケースがありました。 かなり悩んだのですが、setTimeoutで閉じるという技を見かけたので、これを採用しました。
上記の問題を解決したコード
CoffeeScriptですが、以下のような感じになりました。
# self.svgには、d3.jsのSelectionオブジェクトが入っている printWindow = window.open('', 'PrintChart', "width=#{self.svg.attr('width')}, height=#{self.svg.attr('height')}") printWindow.document.writeln('<head><link rel="stylesheet" href="/css/c3.css" media="all" /></head>') printWindow.document.writeln("<body class='c3'>#{new XMLSerializer().serializeToString(self.svg.node())}</body>") printWindow.document.close() printWindow.print() setTimeout -> printWindow.close() , 1000
動かしてみる
実際に開いた印刷画面はこちら。(Chromeにて)
とりあえず印刷できるところまではいけたのでOK!!👍
他にもボチボチc3.jsのノウハウが溜まってきたので書いていこうと思います。