kick the base

Houdiniと、CG技術と、日々のこと。

Photoshop/JSX: フォルダ内の画像を開き、調整レイヤーを追加して保存する

先日、友人が困ってました。

「スキャンした書籍のデータに調整レイヤーを載せて、PSDで保存してるんだけど、ページ数が膨大で作業が大変」

その日はちょうどぼくに時間の余裕があったので、その作業を自動化するJSXを作ってプレゼントしました。

本記事ではそのJSXのポイントを実際のコードを見ながら解説したいと思います。十数行の非常に短いコードなので、プログラムを普段書かないデザイナさんもチャレンジしてはいかがでしょうか。

最初に最終形をお見せします。(本来JSのコードはもっとモダンなパターンを用いて書いたほうがベターなのですが、自身のコーディング規約によってこのような書き方にしています。それについては本記事の最後に触れます)

(function () {
    var folder = Folder.selectDialog('画像があるフォルダを選択してください');
    if (!folder)
        return;
    var files = folder.getFiles(/jpe?g/);
    for (var i = 0; i < files.length; i++) {
        var file = files[i];
        app.open(file);
        app.doAction('add_adjustment_layer', 'MyAction');
        var exportPath = file.path + '/' + file.name.replace(/jpe?g/, '');
        app.activeDocument.saveAs(new File(exportPath));
    }
})();

実際にコードを書いた順に解説を加えていきましょう。

画像があるフォルダの選択

var folder = Folder.selectDialog('画像があるフォルダを選択してください');
if (!folder)
    return;
  1. Folder.selectDialog('画像があるフォルダを選択してください');で取得したFolderオブジェクトを変数folderに代入します。
  2. 上記フォルダ選択ダイアログでキャンセルを押された場合などはfoldernullになるためif文のブロックでreturn;が実行されて以下の処理を抜けます。

2の処理はデザインパターンでいうところのボーキングパターンというやつです。

「デザインパターン?なにそれ難しそう!」と思うかもしれませんが、難しいことは全然なくて、先人の残してくれたコツのようなものです。

特にこのボーキングパターンは最も簡単な部類のもので、都合がわるい時は何もせず抜けるというただそれだけのパターンです。使い勝手が良いのでオススメです。

フォルダからファイルを取得する

var folder = Folder.selectDialog('画像があるフォルダを選択してください');
if (!folder)
    return;
var files = folder.getFiles(/jpe?g/); //この段階はif文のチェック後なのでfolderがnullになることはない

Folder.getFilesメソッドを使ってファイルオブジェクトを取得します。このときポイントとなるのが引数の処理で、/jpe?g/とすることでjpegjpg両方にマッチさせることができます。

Folder.getFilesメソッドは引数に正規表現をとります

取得したファイルひとつひとつに対して操作を行う

var folder = Folder.selectDialog('画像があるフォルダを選択してください');
if (!folder)
    return;
var files = folder.getFiles(/jpe?g/);
for (var i = 0; i < files.length; i++) {
    var file = files[i];
    app.open(file);
    app.doAction('add_adjustment_layer', 'MyAction');
    var exportPath = file.path + '/' + file.name.replace(/jpe?g/, '');
    app.activeDocument.saveAs(new File(exportPath));
}
  1. 変数filesは配列としてjpeg/jpgファイルオブジェクトへの参照を持っているので、files[i]でひとつづつ順に取り出し、変数fileに代入します
  2. app.openメソッドでファイルを開きます
  3. app.doAction('add_adjustment_layer', 'MyAction');
    ここで先ほど開いたファイルに調整レイヤーを入れているのですが、重要なので後述します
  4. ファイル自身のパスを調べ、拡張子を取り除いたものを変数exportPathに代入します
  5. app.activeDocument.saveAs(new File(exportPath));でPSDを画像ファイルと同階層に保存します

3の行程ではプログラムPhotoshopでの操作の合わせ技をしています。

下準備としてPhotoshopでアクションを作っておき、それを呼び出しているのがこのコードとなるのです。

本例ではアクションはMyActionフォルダ内のadd_adjustment_layerという名前となっています

最後に即時関数でくるんでおしまい

(function () {
    var folder = Folder.selectDialog('画像があるフォルダを選択してください');
    if (!folder)
        return;
    var files = folder.getFiles(/jpe?g/);
    for (var i = 0; i < files.length; i++) {
        var file = files[i];
        app.open(file);
        app.doAction('add_adjustment_layer', 'MyAction');
        var exportPath = file.path + '/' + file.name.replace(/jpe?g/, '');
        app.activeDocument.saveAs(new File(exportPath));
    }
})();

※ これで最初に示した最終形と同じになりました!

(function(){ ... })();で処理をくるむことでグローバルスコープへの名前空間汚染をなくしておきましょう。

スコープに関する説明をすると長くなってしまうので、あとで記事を書くかもしれません

今回のJSXのまとめ

PhotoshopやIllustratorの自動化においてはプログラムとソフトのオペレーション、両方の知識が求められるので大変ですが、使えるようになると時間の節約になるので皆さんトライしてはいかがでしょうか。

JSXとモダンJSのコーディング規約

近年のJavaScriptコーディングにおいては、単独varパターンや厳格モードの使用など、大規模開発時により安全なコードになるようプロジェクトごとに規約が設定されていることが多いかと思います。

もちろん、そういったより現代的なJSのアプローチは正しいのですが、ことJSXに関しては下記のような背景により中々難しい現状があります。

  • コードを普段書かないデザイナがメンテナンスを行う場合がある
  • ネット上に情報が少なく、古いものだとすべてグローバルスコープに変数を定義しているようなものも見受けられる
  • JSXは依存関係も少なく小さいツールであることが多いので厳格なコーディング規約の恩恵を受けにくい

こういった現状を鑑みて、JSXのコーディングではぼくはあまり高度な規約を持ち込まないようにしています。もちろんこの判断もプロジェクトによるかと思いますが、ひとつの例として参考としてみてください。

追記

多くの方に見ていただいたので、追記をしておきます。本記事には続きがあり、正規表現の理解とともにコードに手を加えるという記事を執筆予定です。

書きました。

正規表現って何?という方は1から、正規表現はわかるよって方は2からご覧いただければと思います。