📷

[Photoshop] 画像書き出し用スクリプト

に公開

どんだけ擦られたネタなんだよっていうやつだが、自分が使う丁度いいやつがなかったので書いた。

背景

僕は写真を趣味にしているのだが、ブログやSNSに撮って出しの画像をアップするには画像サイズが大きすぎ、かつサービス側での勝手な圧縮は避けたいなという思いがある。なので下記処理のスクリプトを用意した。

  • 縦横の長辺方向を3000pxに固定する(僕のユースケースでは確実に縮小にあたるので拡大の考慮はしていない)
  • PSDの同ディレクトリに命名規則に沿ったファイル名でJPG書き出しを行う(バッチ処理もしくは単体処理)
  • バッチ処理の場合はPSDを保存せず閉じる
  • ファイル名がアンダーバーで始まるのはエクスプローラーでファイル名ソートしたときに頭出ししやすいようにという意図がある

画像リサイズ + JPG書き出しバッチ処理

// Photoshopスクリプト: 画像解像度変更とJPG書き出し(バッチ処理版)
// 開いているすべてのPSDファイルに対して実行

// スクリプトの実行
(function() {
    try {
        // 開いているドキュメントの数をチェック
        if (app.documents.length === 0) {
            alert("開いているドキュメントがありません。PSDファイルを開いてから実行してください。");
            return;
        }
        
        var processedCount = 0;
        var errorCount = 0;
        var errorMessages = [];
        
        // 開いているすべてのドキュメントを処理
        // 配列のコピーを作成して処理(処理中にドキュメントが閉じられるため)
        var docsToProcess = [];
        for (var j = 0; j < app.documents.length; j++) {
            docsToProcess.push(app.documents[j]);
        }
        
        // 画質設定用のダイアログを作成
        var dialog = new Window("dialog", "バッチ処理設定");
        dialog.orientation = "column";
        dialog.alignChildren = "fill";
        dialog.spacing = 10;
        dialog.margins = 16;
        
        // 画質設定グループ
        var qualityGroup = dialog.add("group");
        qualityGroup.add("statictext", undefined, "画質 (0-100):");
        var qualityInput = qualityGroup.add("edittext", undefined, "60");
        qualityInput.characters = 3;
        
        // 説明テキスト
        var infoText = dialog.add("statictext", undefined, "開いている " + docsToProcess.length + " 件のドキュメントを処理します。");
        var infoText2 = dialog.add("statictext", undefined, "各ファイルは処理後に保存せずに閉じられます。");
        
        // ボタングループ
        var buttonGroup = dialog.add("group");
        var cancelButton = buttonGroup.add("button", undefined, "キャンセル");
        var okButton = buttonGroup.add("button", undefined, "実行");
        
        // ボタンのイベント設定
        okButton.onClick = function() {
            dialog.close(1); // OKの場合は1を返す
        };
        
        cancelButton.onClick = function() {
            dialog.close(2); // キャンセルの場合は2を返す
        };
        
        // ダイアログを表示
        var result = dialog.show();
        
        // キャンセルされた場合は処理を中止
        if (result !== 1) {
            return;
        }
        
        // 画質値を取得して検証
        var qualityValue = parseInt(qualityInput.text);
        if (isNaN(qualityValue) || qualityValue < 0 || qualityValue > 100) {
            qualityValue = 60; // 不正な値の場合は60を使用
        }
        
        for (var i = 0; i < docsToProcess.length; i++) {
            var doc = docsToProcess[i];
            
            try {
                // ドキュメントがまだ開いているかチェック
                if (!doc || doc.closed) {
                    continue;
                }
                
                // ドキュメントをアクティブにする
                app.activeDocument = doc;
                
                // ドキュメントの現在のサイズを取得
                var currentWidth = doc.width.value;
                var currentHeight = doc.height.value;
                
                // 長手方向を3000pxに設定(アスペクト比を維持)
                var targetSize = 3000;
                var newWidth, newHeight;
                
                if (currentWidth >= currentHeight) {
                    // 横長の場合
                    newWidth = targetSize;
                    newHeight = (currentHeight / currentWidth) * targetSize;
                } else {
                    // 縦長の場合
                    newHeight = targetSize;
                    newWidth = (currentWidth / currentHeight) * targetSize;
                }
                
                // 画像解像度を変更
                doc.resizeImage(
                    UnitValue(newWidth, "px"),
                    UnitValue(newHeight, "px"),
                    doc.resolution,
                    ResampleMethod.BICUBIC
                );
                
                // ファイル名を生成(PSDファイル名_s.jpg)
                var psdFileName = doc.name.replace(/\.psd$/i, "");
                var jpgFileName = "_" + psdFileName + "_s.jpg";
                
                // ファイルパスを取得
                var filePath = doc.path;
                var jpgPath = filePath + "/" + jpgFileName;
                
                // Web用に保存を使用してJPG書き出し
                var exportOptions = new ExportOptionsSaveForWeb();
                exportOptions.format = SaveDocumentType.JPEG;
                exportOptions.quality = qualityValue; // ダイアログで設定された画質値
                exportOptions.optimized = true;
                exportOptions.progressive = false;
                exportOptions.includeProfile = false;
                
                // 書き出し実行
                doc.exportDocument(new File(jpgPath), ExportType.SAVEFORWEB, exportOptions);
                
                // 書き出し完了後、保存せずに閉じる
                try {
                    doc.close(SaveOptions.DONOTSAVECHANGES);
                    processedCount++;
                } catch (closeError) {
                    // 閉じる処理でエラーが発生した場合
                    errorCount++;
                    errorMessages.push(doc.name + " (閉じる処理でエラー): " + closeError.message);
                }
                
            } catch (docError) {
                errorCount++;
                errorMessages.push(doc.name + ": " + docError.message);
                
                // エラーが発生した場合もドキュメントを閉じることを試行
                try {
                    if (doc && !doc.closed) {
                        doc.close(SaveOptions.DONOTSAVECHANGES);
                    }
                } catch (closeError) {
                    // 閉じる処理も失敗した場合
                    errorMessages.push(doc.name + " (エラー後の閉じる処理でエラー): " + closeError.message);
                }
            }
        }
        
        // 処理結果を表示(エラーがある場合のみ)
        if (errorCount > 0) {
            var resultMessage = "バッチ処理が完了しました。\n";
            resultMessage += "設定画質: " + qualityValue + "\n";
            resultMessage += "処理済み: " + processedCount + "件\n";
            resultMessage += "エラー: " + errorCount + "件\n";
            resultMessage += "\nエラー詳細:\n" + errorMessages.join("\n");
            alert(resultMessage);
        }
        
    } catch (error) {
        alert("スクリプト実行中にエラーが発生しました: " + error.message);
    }
})();

単体処理

// Photoshopスクリプト: 画像解像度変更とJPG書き出し
// 現在アクティブなPSDファイルに対して実行

// スクリプトの実行
(function() {
    try {
        // 現在アクティブなドキュメントを取得
        var activeDoc = app.activeDocument;
        
        if (!activeDoc) {
            alert("アクティブなドキュメントが見つかりません。PSDファイルを開いてから実行してください。");
            return;
        }
        
        // ドキュメントの現在のサイズを取得
        var currentWidth = activeDoc.width.value;
        var currentHeight = activeDoc.height.value;
        
        // 長手方向を3000pxに設定(アスペクト比を維持)
        var targetSize = 3000;
        var newWidth, newHeight;
        
        if (currentWidth >= currentHeight) {
            // 横長の場合
            newWidth = targetSize;
            newHeight = (currentHeight / currentWidth) * targetSize;
        } else {
            // 縦長の場合
            newHeight = targetSize;
            newWidth = (currentWidth / currentHeight) * targetSize;
        }
        
        // 画像解像度を変更
        activeDoc.resizeImage(
            UnitValue(newWidth, "px"),
            UnitValue(newHeight, "px"),
            activeDoc.resolution,
            ResampleMethod.BICUBIC
        );
        
        // ファイル名を生成(PSDファイル名_s.jpg)
        var psdFileName = activeDoc.name.replace(/\.psd$/i, "");
        var jpgFileName = "_" + psdFileName + "_s.jpg";
        
        // ファイルパスを取得
        var filePath = activeDoc.path;
        var jpgPath = filePath + "/" + jpgFileName;
        
        // Web用に保存を使用してJPG書き出し
        var exportOptions = new ExportOptionsSaveForWeb();
        exportOptions.format = SaveDocumentType.JPEG;
        exportOptions.quality = 60; // 画質60
        exportOptions.optimized = true;
        exportOptions.progressive = false;
        exportOptions.includeProfile = false;
        
        // 書き出し実行
        activeDoc.exportDocument(new File(jpgPath), ExportType.SAVEFORWEB, exportOptions);
        
    } catch (error) {
        alert("エラーが発生しました: " + error.message);
    }
})();

サンプル画像

こんな感じの画像がエクスポートされる。

温泉
メガネ
ゼリー
ryugon

まとめ

レガシーなJS書きたくないですね。

Discussion