Node.js(CoffeeScript)でSVGファイルをPNGに変換する必要があって,Inkscapeを子プロセスで走らせて変換するように実装してみました.
また,はじめはsvg2pngなどのライブラリを使ってやってみましたが,単純なSVGファイルだと変換速度が遅かったのでInkscapeでの実装に切り替えました.その2つの実装の速度比較もしてみました.
環境
- Mac mini (Mid 2011) CPU2.5GHz, Memory16GB
- Node.js v5.11.0
- Inkscape 0.48.5
- npmライブラリ
- coffee-script 1.10.0
- svg2png 3.0.1
- pn 1.0.0
- q 1.4.1
サンプルSVGファイル
- 単純なSVG: color_circles.svg(3色のカラーサークル)
- 複雑なSVG: cat.svg(こちらをビットマップトレースしたもの)
svg2pngを使った場合の変換
svg2png_convert.coffee
fs = require "pn/fs"
svg2png = require "svg2png"
debug = require("debug")("svgtest")
Q = require "q"
filename = process.argv[2]
cycle = process.argv[3]
tasks = [1..cycle]
# pvg2pngを使って変換するファンクション
convert = (index) ->
debug "start to convert", index
fs.readFile filename
.then svg2png
.then (buffer) ->
fs.writeFile "#{filename.replace(/\..+$/, '')}.png", buffer
.then ->
debug "convert finished", index
# cycleの数だけ順次繰り返す
tasks.reduce (prev, cur, index) ->
prev.then ->
Q.fcall convert, index
, Q()
.catch (e) ->
console.error e
.done ->
console.log "finish"
$ npm install svg2png
$ npm install pn
$ npm install q
$ DEBUG=svgtest coffee svg2png_convert.coffee color_circles.svg 1
svgtest start to convert +0ms 0
svgtest convert finished +1s 0
finish
Inkscapeを使った場合の変換
inkscape_convert.coffee
inkscapeHelper = require "./inkscape_helper"
debug = require("debug")("svgtest")
Q = require "q"
filename = process.argv[2]
cycle = process.argv[3]
tasks = [1..cycle]
# Inkscapeを使って変換するファンクション
convert = (index) ->
debug "start to convert", index
Q.nfcall inkscapeHelper.convert,
filename, "#{filename.replace(/\..+$/, '')}.png"
.then ->
debug "convert finished", index
# cycleの数だけ順次繰り返す
Q.nfcall inkscapeHelper.open
.then ->
debug "opened"
tasks.reduce (prev, cur, index) ->
prev.then ->
Q.fcall convert, index
, Q()
.then ->
inkscapeHelper.close()
.catch (e) ->
console.error e
.done ->
console.log "finish"
inkscape_helper.coffee
spawn = require("child_process").spawn
Q = require "q"
# Inkscape実行ファイルへのパスは環境によって読み替えてください
INKSCAPE_PATH = "/Applications/Inkscape.app/Contents/Resources/bin/inkscape"
inkscape = null
completion = null
# 子プロセスの起動
exports.open = (cb) ->
inkscape = spawn INKSCAPE_PATH, ["--shell"]
completion = cb
inkscape.stdout.on "data", (data) ->
# console.log data.toString('utf-8')
if (data.toString().search /(\r\n?)?>$/) isnt -1
# Inkscapeのshellモードで入力プロンプトが表示された
if completion?
completion()
completion = null
inkscape.stderr.on "data", (data) ->
console.error "INKSCAPE ERROR", data.toString('utf-8')
inkscape.on "close", (code) ->
console.log "INKSCAPE EXITED", code
# 変換処理
exports.convert = (svgPath, pngPath, cb) ->
completion = cb
inkscape.stdin.write "#{svgPath} \
--export-png=#{pngPath} --export-background-opacity=0 \r\n"
# 子プロセスの終了
exports.close = ->
inkscape.stdin.write "quit\r\n"
$ npm install q
$ DEBUG=svgtest coffee inkscape_convert.coffee color_circles.svg 1
INKSCAPE ERROR W: AppleCollationOrder setting not found, using AppleLocale.
INKSCAPE ERROR Setting LANGSTR from AppleLocale: en
INKSCAPE ERROR Overriding empty LANG from /usr/share/locale/locale.alias
INKSCAPE ERROR Setting Language: en_US.UTF-8
INKSCAPE ERROR
(process:54607): Gtk-WARNING **: Locale not supported by C library.
Using the fallback 'C' locale.
svgtest opened +0ms
svgtest start to convert +3ms 0
svgtest convert finished +155ms 0
finish
INKSCAPE EXITED 0
- Inkscapeを
--shell
でシェルモード起動し,標準入出力で会話しています - シェルモードでの変換命令はサイズ変更なども可能ですが,ここでは単にSVGのドキュメントサイズでSVGからPNGへの変換のみを行っています.その他使えるオプションはInkscape コマンドライン マニュアルを参照してください
- 変換完了はInkscapeから
>
のプロンプトが標準出力に出たことで判断しています - 起動時にInkscapeからワーニング出てますが無視してます・・・
速度比較
svg2pngとInkscapeで変換をした時の速度比較をしてみます.(サンプルSVGファイルの2つについてそれぞれ50回変換)
$ time DEBUG=svgtest coffee svg2png_convert.coffee color_circles.svg 50
$ time DEBUG=svgtest coffee svg2png_convert.coffee cat.svg 50
$ time DEBUG=svgtest coffee inkscape_convert.coffee color_circles.svg 50
$ time DEBUG=svgtest coffee inkscape_convert.coffee cat.svg 50
結果(timeコマンドのrealの値)
実装 | color_circles.svg | cat.svg |
---|---|---|
svg2png | 13.186s | 76.624s |
Inkscape | 7.487s | 78.413s |
複雑なSVGだとそんなに違いはでませんでしたが,単純なSVGだとInkscapeの方が早い時間で変換できました.svg2pngの内部ではPhantomJS(ヘッドレスブラウザ)を使っているようなので,その関係で一定以下にはならないんでしょうか?(確証はありません)
今回私が必要だったのは,単純なSVGファイルを大量にPNG化する処理だったので,Inkscapeを使った変換に切り替えることで処理速度を向上させることができました.