D3.js + jsdomでSVGファイルを作成 & pngに変換

SVGファイルを作るに当たって、[過去にQiitaに投稿したもの][過去記事]を参照したら動作しませんでした。
どうやら[jsdom]が色々変わったらしいので、新しいバージョンで動くように書き換えました。

## 基本形

#### versions

```text
d3@4.11.0
jsdom@11.5.1
```

#### JavaScript

```JavaScript:makeSVG.js
const d3 = require('d3');
const jsdom = require('jsdom');
const { JSDOM } = jsdom;

const document = new JSDOM().window.document;
const size = {
  width: 900,
  height: 1200
};
const svg = d3.select(document.body)
              .append('svg')
              .attr("xmlns",'http://www.w3.org/2000/svg')
              .attr('width', size.width)
              .attr('height', size.height)
              .style('background-color', "#363b44");

console.log(document.body.innerHTML)
```

#### Command Line

```Shell
$ node makeSVG.js > output.svg
```

#### SVGファイル

```xml:output.svg
<svg xmls="http://www.w3.org/2000/svg" width="900" height="1200" style="background-color: #363b44;"></svg>
```

これが基本形です。makeSVG.jsが`console.log()`で出力した内容を、`>`（リダイレクト）でファイルoutput.svgに出力しています。
jsdomの書き方が過去のものとは少し変わりました。

## その他変更点など

過去の記事では`d3.queue`と`d3.json`で国土地理院のベクトルタイルを読み込みましたが、今回ローカルのファイルを読み込むに当たって`d3.json`が使えなかったので、代わりに`fs.readFile`を使用します。

```JavaScript
const fs = require('fs');

// 上記の基本形は省略

const q = d3.queue()
            .defer(fs.readFile, './data/j1results.json')
            .defer(fs.readFile, './data/j2results.json')
            .defer(fs.readFile, './data/j3results.json');

q.awaitAll((err, files) => {
  if (err) throw err;
  const data = files.map(str => JSON.parse(str));
  
  /** D3 Scripts
    * ex. svg.selectAll('rect')
    *        .data(d3.merge(data))
    *        .enter()
    *        .append('rect')
    *        ...
    */
  
  console.log(document.body.innerHTML);
});
```

fsを使うなら、ファイルの出力方法は`console.log()`ではなく、`fs.writeFile()`でもいいかもしれません。お好きな方をお選びください。

```JavaScript
fs.writeFile('output.svg', document.body.innerHTML, (err) => {
  if (err) throw err;
  console.log('save successful!');
});
```

D3.jsで描写したSVGはcssで要素のスタイルを設定する場合が多いのですが、SVGファイルはcssが読み込めません。`selection.attr`で地道にスタイリングしましょう。

以下は作成したSVGファイルの例です。

[2017年の明治安田生命Jリーグ全試合の観客数(外部リンク)](https://cieloazul310.github.io/img/j-attd-2017.svg)

## SVGファイルをpngに変換(imagemagick編)

SVGファイルをpngに変換する方法はいくつかあると思いますが、まず最初にimagemagickを使う方法を試してみます。

![j-attd-2017.png](https://qiita-image-store.s3.amazonaws.com/0/154454/17a7e7e5-5ff7-34b5-be33-72af9fb24c70.png)

#### Command Line

```Shell
$ convert -density 120 -background "#363b44" \
  -font "/Library/Fonts/KozGoPr6N-Regular.otf" input.svg output.png
```

- `-density`は倍率と考えてよい（実際の定義は知らない）
- `-background`を設定しないと背景透過になってしまいます
- `-font`でフォントを指定しないと日本語は文字化けしてしまいます

フォントが変わる部分はさておき、SVGの透過度も再現してくれないのでかなり微妙な出来となってしまいました。

参考: [convert コマンドでラベルに日本語を含むsvgをPDFに変換 ](//qiita.com/hkoshiba/items/d5fe07f5d16316756f49)

## SVGファイルをpngに変換(Headless Chrome編)

imagemagickの他に何かいい方法ないかなと調べてみたらHeadless Chromeという機能がChromeに備わっているということを知りました。  
これを使ってhugoで立てたローカルホスト内のSVGファイルをキャプチャしてみます。

参考: [ヘッドレス Chrome ことはじめ] Google Developers

![j-attd-2017-screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/154454/d2961d82-f430-9749-e5a8-99b71bad29ca.png)

#### Command Line

```Shell
$ alias chrome='/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome'
$ chrome --headless --disable-gpu --screenshot --window-size=900,1200 http://localhost:1313/input.svg
```

元のSVGファイルとほぼ同じ状態のpngが生成できました。（当たり前といえば当たり前なのですが）  
画像のサイズは、指定したウィンドウサイズの2倍（この場合だと1800x2400）ですが、imagemagickで生成したものよりも小さい容量になりました。

- imagemagick: 1125x1500 **524KB**  
- Headless Chrome: 1800x2400 **496KB**

手軽に綺麗なキャプチャができるので、今後Headless Chromeを使うことが多くなりそうです。

<!-- References -->
[jsdom]: //github.com/tmpvar/jsdom
<!-- d3.js + jsdomで国土地理院のベクトルタイルからSVGファイルを生成 2017年07月06日に更新 -->
[過去記事]: //qiita.com/cieloazul310/items/a8e776bbe8a70262df99

<!-- ヘッドレス Chrome ことはじめ -->
[ヘッドレス Chrome ことはじめ]: https://developers.google.com/web/updates/2017/04/headless-chrome?hl=ja