LaTeXとImageMagickとNode.jsを使ってスライド作成

LaTeX→DVI→PDF→（複数枚の）画像→結合した画像という順番で処理します。

LaTeXをDVIに変換するには

```
platex -guess-input-enc -interaction=nonstopmode xxx.tex
```

とします。

DVIをPDFに変換するには

```
dvipdfmx -f ptex-ipaex.map xxx.dvi
```

とします（`ptex-ipaex.map`はフォント埋め込みの設定ファイルです）。

PDFをPNG画像に変換するにはImageMagickのコマンドである`convert`を使用します。

```
convert -density 800 xxx.pdf -quality 100 -resize 800 xxx.png
```

とします（`-density`は解像度、`-quality`は品質、`-resize`はサイズです）。PDFが複数ページの場合は複数枚の画像が生成されます。複数枚の画像が生成される場合、それぞれの画像のファイル名は`xxx-1.png`、`xxx-2.png`、・・・、`xxx-9.png`、`xxx-10.png`、・・・となります。

画像を結合するには

```
montage +frame +shadow +label -tile 2x -geometry 800x600+0+0 xxx-*.png xxx.png
```

とします（`+frame +shadow +label`は`montage`に画像の結合以外の余計なことをさせないための設定、`-tile`は縦横何枚ずつ画像を並べるか、`-geometry`は画像を並べる場所（タイル）のサイズです。`-tile 2x`で横2枚ずつ並べるという設定です。`-geometry 800x600+0+0`でタイルのサイズが横`800px`、縦`600px`となります。`+0+0`は余白なしということです）。

----

このような処理をNode.jsを使って行うプログラムを書きました。

ただし、PDFから生成された画像が10枚以上の場合、ファイルの名の所為で、`montage`で結合を行うと10枚目～19枚目の画像が2枚目の画像の前に結合されてしまいます。

これでは不味いのでPDFから生成された画像が10枚以上ある場合には1枚目から9枚目までの画像のファイル名の連番部分の冒頭に`0`を挿入するようにしました。

これで画像が10枚以上100枚未満の場合には対応できていますが、100枚以上の場合には対応できていないので注意してください（スライドの枚数が100枚以上になることは稀だと思うので100枚以上の場合には対応していません）。

あと、このプログラムを実行する前には同じフォルダにある画像ファイルは全て削除するか、どこか別の場所に移動してください。そうしないと、無関係の画像が結合されてしまうかもしれません（プログラムの最初に自動で削除するようにしても良いかもしれません）。

```js
var childProcess = require('child_process');
var fs = require('fs');

if (process.argv.length < 3) {
	console.error('lack argument.');
	process.exit(1);
}

var tex = process.argv[2] + '.tex';
var dvi = process.argv[2] + '.dvi';
var pdf = process.argv[2] + '.pdf';
var pngs = process.argv[2] + '-*.png';
var png = process.argv[2] + '.png';

function pngn(n) {
	return process.argv[2] + '-' + + n + '.png';
}

function pngn2(n) {
	return process.argv[2] + '-0' + + n + '.png';
}

processPlatex(0);

function processPlatex(i) {
	var platex = childProcess.spawn('platex', ['-guess-input-enc', '-interaction=nonstopmode', tex], { stdio: 'inherit' });
	platex.on('close', function (code) {
		if (i == 0) {
			processPlatex(1)
		}
		else if (i == 1) {
			processDvipdfmx();
		}
		else {
			console.error('coding error.');
			process.exit(1);
		}
	});
	platex.on('error', function (err) {
		console.error(err);
		process.exit(1);
	});
}

function processDvipdfmx() {
	var dvipdfmx = childProcess.spawn('dvipdfmx', ['-f', 'ptex-ipaex.map', dvi], { stdio: 'inherit' });
	dvipdfmx.on('close', function (code) {
		processConvert();
	});
	dvipdfmx.on('error', function (err) {
		console.error(err);
		process.exit(1);
	});
}

function processConvert() {
	var convert = childProcess.spawn('convert', ['-density', '800', pdf, '-quality', '100', '-resize', '800', png], { stdio: 'inherit' });
	convert.on('close', function (code) {
		fs.stat(pngn(10), function (err, stats) {
			if (!err) {
				processRename(1);
			}
			else {
				processMontage();
			}
		});
	});
	convert.on('error', function (err) {
		console.error(err);
		process.exit(1);
	});
}

function processRename(i) {
	if (i == 10) {
		processMontage();
	}
	else {
		fs.rename(pngn(i), pngn2(i), function (err) {
			if (err) {
				console.error(err);
				process.exit(1);
			}
			else {
				processRename(++i);
			}
		});
	}
}

function processMontage() {
	var montage = childProcess.spawn('montage', ['+frame', '+shadow', '+label', '-tile', '2x', '-geometry', '800x600+0+0', pngs, png], { stdio: 'inherit' });
	montage.on('close', function (code) {
		console.log('finished!!');
	});
	montage.on('error', function (err) {
		console.error(err);
		process.exit(1);
	});
}
```