2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Node.js の svgexport を使って SVG から提案書に使える綺麗な PNG 画像を作成する

Last updated at Posted at 2018-04-04

追記

  • svgexport が依存している PhantomJS の開発が終了したようです。
    本記事の内容は参考程度にしていただき、ImageMagick や別のモジュールでの実装をお願いします。
  • svgexport の処理に Promise は不要だったので修正しました。
  • Font Awesome の配布元が更新されていたので変更しました。

SVG はレスポンシブデザインのサイトを制作するうえで綺麗にアイコンを表示できるので重宝しています。
今では企業やブランドのロゴも SVG で提供されていることも増えてきました。

そんな便利な SVG ですが Google スライドなどで提案書やドキュメントを作る場合、そのままでは簡単に使用できません。
Illustrator などでひとつずつ PNG に変換されている方もいらっしゃるのではないでしょうか。

今回は svgexport という Node.js のモジュールを使用して、できるだけ簡単に SVG を希望のサイズで PNG に一括変換できる方法を検証してみました。

  • 検証環境
    • macOS High Sierra 10.13.6
      • Node.js: v8.11.3
      • npm: 5.6.0
    • Windows 7 Home Premium
      • Node.js: v8.11.3
      • npm: 5.6.0

svgexport を試す

svgexport はコマンドラインでも使用できます。
詳しくは svgexport の README.md を参照していただければ解ると思いますが、 npx コマンドで簡単に試してみます。

# download-cli を使用して Node.js のロゴをダウンロード
npx download-cli https://upload.wikimedia.org/wikipedia/commons/d/d9/Node.js_logo.svg > Node.js_logo.svg

# SVG の viewBox に対して倍率を指定してエクスポート
npx svgexport Node.js_logo.svg Node.js_logo_1.5x.png 1.5x

# 比率はそのままで指定した横幅( px )に合わせて拡大、縮小してエクスポート
npx svgexport Node.js_logo.svg Node.js_logo_320.png 320:

上記の他にも細かくオプションの指定はできますが、複数の SVG を一括で変換する場合は JSON ファイルやオブジェクトを使用します。
以降は Node.js の勉強も兼ねて任意のディレクトリの SVG のリスト取得と変換まで試してみます。

npm install

使用する npm のモジュールをインストールします。

  • svgexport
    SVG を PNG や JPG に変換します。
  • yargs
    コマンドラインの引数をパースします。
# package.json の作成
npm init -y

# モジュールのインストール
npm i svgexport yargs

package.json に以下の項目が追加されていれば問題ありません。

package.json
{
  "dependencies": {
    "svgexport": "0.3.2",
    "yargs": "11.0.0"
  }
}

svgexport の処理を追加する

index.js を新規に作成し以下のコードを記述します。

index.js
const fs = require('fs'); // Node.js のコアモジュール File System を読み込み
const path = require('path'); // Node.js のコアモジュール Path を読み込み
const svgexport = require('svgexport'); // svgexport の読み込み
const { argv } = require('yargs'); // yargs を使用してコマンドの引数をパース
const opt = {
  input: argv._[0] || '', // ディレクトリの指定がない場合は現在のディレクトリを指定
  extension: argv.jpg ? '.jpg' : '.png', // `--jpg` で出力を jpg に変更可
  styles: argv.styles || false, // 背景色など SVG のスタイルを指定
  width: parseInt(argv.width) || parseInt(argv.w) || false, // 画像の横幅
  json: Boolean(argv.json) // 変換対象のリストを JSON として書き出すか
};
const svgs = []; // input, output のパスを格納する配列

/**
 * svgexport で参照する input, output のパスを配列に追加する関数
 * @param {string} input - process.cwd() からファイルまでの相対パス
 */
function storeSvgPath(input) {
  const file = input.replace(/(.*)\.svg$/, `$1${opt.extension}`);
  const styles = opt.styles ? ` ${opt.styles}` : '';
  const width = opt.width ? ` ${opt.width}:` : '';
  const output = `svgexport/${file}${width}${styles}`;
  svgs.push({ input, output });
}

/**
 * 再帰処理関数
 * @param {string} input - SVG が格納されたディレクトリ、または SVG までのパス
 */
function loop(input) {
  const entry = path.resolve(__dirname, input);
  try {
    fs.statSync(entry).isFile()
      ? /.*\.svg$/.test(input) && storeSvgPath(input)
      : fs.readdirSync(entry).forEach(cv => {
          loop(path.join(input, cv));
        });
  } catch (err) {
    console.log(err.toString());
    process.exit(1);
  }
}

// 再帰関数の実行
loop(opt.input);

// JSON の書き出し
opt.json && fs.writeFileSync('datafile.json', JSON.stringify(svgs, '', '\t'));

// svgexport を使った画像の書き出し
svgexport.render(svgs, err => {
  err ? console.log(err.toString()) : console.log('Success');
});

SVG の viewBox のサイズ以外でエクスポートしたい場合、比率を変えることはないと思うので任意の横幅( px 指定)で拡大縮小してエクスポートできるようにしています。

index.js を作成後は以下の node コマンドを実行することで svgexport/ ディレクトリに画像を書き出すことができます。
<input> の指定は SVG ファイル、ディレクトリのどちらかになります。

# node command
node index <input> <options>

# options
#   --jpg       出力する画像の拡張子を .jpg に変更するフラグ
#   --json      datafile.json を書き出すフラグ
#   --width, -w 出力する画像の幅を px 指定
#   --styles    SVG のスタイルを変更

# Example
node index input-directory --json --width 320 --styles "path{fill:#fff;}"
node index input.svg --jpg --width 320 --styles "svg{background:#fff;}"

# 対象の SVG のリストを JSON に書き出し、横幅 320px の白背景の JPG でエクスポート
node index Node.js_logo.svg --jpg --json --width 320 --styles "svg{background:#fff;}"

毎回コマンドの入力するのが手間な場合は npm run-script に登録しても良いかと思います。

package.json
"dependencies": {
  "svgexport": "0.3.2",
  "yargs": "12.0.1"
},
"scripts": {
  "export": "node index --json --width"
}
# 上記の package.json の記述の場合は最後に --width がきているので指定する順番に注意
npm run export <image-width> <input>

Font Awesome の SVG を一括で PNG にする

Font Awesome は Web で使用できるアイコンの中でも特に有名ではないでしょうか。
無料版でも豊富なアイコンが用意されているため提案書にもこのまま使えるようにしたいと思います。

  1. Font Awesome のページから無料版の ZIP ファイルをダウンロードします。

  2. バージョン 5.2.0 の場合 fontawesome-free-5.2.0-web/svgs に SVG が格納されてるので、 svgspackage.json と同じ階層に移動します。

  3. 以下のコマンドを実行して svgs 配下の SVG を PNG 画像に一括で変換します。

    node index svgs --width 320
    

PC のスペックや画像のサイズにもよるかと思いますが、989個の SVG を 320px 幅で PNG に変換した場合、かかった時間は約30秒( 30494.594ms )でした。
また、Windows の場合は100個を超えるファイルを一括で処理しようとするとエラーになってしまいました。
環境によっては大丈夫かもしれませんがエラーになってしまう場合はファイルを小分けにして試してみてください。

黒以外の色のアイコンが必要な場合は --styles オプションを使用して <path>fill 属性の値を変更してください。

node index svgs --width 320 --styles "path{fill:#f00;}"

最後に

今回作成したソースは GitHub にプッシュしています。

async / await を使ってモダンな処理をしたかったのですが、ちゃんと理解できておらず Promise で記述しています。
色々な記事を参考にしてなんとか再帰処理を実装できましたが、もっといい書き方などコメントいただければ嬉しいです。

※svgexport の処理に Promise は不要だったので修正しました。

なお、検証中に ImageMagick という画像処理ツールを教えていただきました。
Node.js でやるよりも ImageMagick を使った方が断然早そうでした...

参考記事一覧

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?