Skip to content

Instantly share code, notes, and snippets.

@Yamonov
Last active November 14, 2025 12:21
Show Gist options
  • Select an option

  • Save Yamonov/b63d9c67402ef7af4c17ab33caccce31 to your computer and use it in GitHub Desktop.

Select an option

Save Yamonov/b63d9c67402ef7af4c17ab33caccce31 to your computer and use it in GitHub Desktop.
Photoshopで開いている画像をIllustratorドキュメントから探し、適正解像度にリサイズする

DL方法

  • 右上の「Download Zip」ボタンでzipをDL
  • 解凍してPhotoshopImageResize_Illustrator.jsxを取り出す

使用方法

  • Illustratorで該当ドキュメントを開いておく
  • Photoshopで、配置している画像を開く
  • Photoshopからこのスクリプトを実行する
    • Hint)Photoshop側のPresetsフォルダ内に入れておくと、ファイルメニュー>スクリプト内にメニュー項目として出てきます。編集>キーボードショートカットでショートカットキーを割り当てられるようになります。
    • プリセット保存位置についてはこのblogエントリを参照してください。 やもめも
SS_Google Chrome_14_001401

#changelog

2025/11/14(Ver 1.5.1):?ボタンでこのページを表示

2025/10/28:動作が重くなることがあるのを修正。UIに警告等追加。タイトルバーにバージョンを表示する。

2025/10/27:Windowsで動作するよう改修

2025/10/11:前回処理内容を記録できるよう変更

2025/10/01:ドキュメント内に不正リンクがあった場合にスキップする処理を追加

/*
<javascriptresource>
<name>Illustratorに合わせてリサイズ</name>
<category>YPresets</category>
</javascriptresource>
*/
var SCRIPT_VERSION = "Ver.1.5.1(2025-11-14)";
// ==== 拡大・縮小の警告しきい値 ====
var efScaleMin = 0.9;
var efScaleMax = 1.1;
var scaleMax = 2;
// ==== ターゲット解像度候補 ====
var targetPPIList = [350, 400, 600, 1200];
// ==== CustomOptions(前回設定の保存/復元) ====
var PREF_ID = "com.yamo.psAiresize_v1";
var K_VER = stringIDToTypeID("version");
var K_RADIO_INDEX = stringIDToTypeID("radioIndex");
var K_UPSCALE = stringIDToTypeID("upscaleMethod");
var K_DOWNSCALE = stringIDToTypeID("downMethod");
var K_USE_PREV = stringIDToTypeID("usePrevSettings");
var SCHEMA_VERSION = 1;
var defaultsPrefs = {
usePrev: false,
radioIndex: 0,
upscaleMethod: "deepUpscale",
downMethod: "bicubic"
};
function cloneDefaultsPrefs() {
return {
usePrev: defaultsPrefs.usePrev,
radioIndex: defaultsPrefs.radioIndex,
upscaleMethod: defaultsPrefs.upscaleMethod,
downMethod: defaultsPrefs.downMethod
};
}
function loadPrefsCO() {
var prefs = cloneDefaultsPrefs();
try {
var descriptor = app.getCustomOptions(PREF_ID);
if (descriptor.hasKey(K_USE_PREV)) prefs.usePrev = descriptor.getBoolean(K_USE_PREV);
if (descriptor.hasKey(K_RADIO_INDEX)) {
var index = descriptor.getInteger(K_RADIO_INDEX);
if (index >= 0 && index < targetPPIList.length) prefs.radioIndex = index;
}
if (descriptor.hasKey(K_UPSCALE)) prefs.upscaleMethod = descriptor.getString(K_UPSCALE);
if (descriptor.hasKey(K_DOWNSCALE)) prefs.downMethod = descriptor.getString(K_DOWNSCALE);
} catch (error) {
// 初回起動時や設定破損時は既定値を使用
}
return prefs;
}
function savePrefsCO(prefs) {
var descriptor = new ActionDescriptor();
descriptor.putInteger(K_VER, SCHEMA_VERSION);
descriptor.putBoolean(K_USE_PREV, !!prefs.usePrev);
descriptor.putInteger(K_RADIO_INDEX, Math.max(0, Math.min(targetPPIList.length - 1, prefs.radioIndex)));
descriptor.putString(K_UPSCALE, String(prefs.upscaleMethod));
descriptor.putString(K_DOWNSCALE, String(prefs.downMethod));
app.putCustomOptions(PREF_ID, descriptor, true);
}
// usePrev フラグのみを保存(他の設定値は維持)
function saveUsePrevOnly(flag) {
var currentPrefs = loadPrefsCO();
savePrefsCO({
usePrev: !!flag,
radioIndex: currentPrefs.radioIndex,
upscaleMethod: currentPrefs.upscaleMethod,
downMethod: currentPrefs.downMethod
});
}
// ==== ブラウザでURLを開くヘルパー関数 ====
function openURLInBrowser(url) {
if (!url) return;
try {
var os = $.os.toLowerCase();
if (os.indexOf("mac") >= 0) {
app.system('/usr/bin/open "' + url + '"');
} else if (os.indexOf("win") >= 0) {
app.system('cmd.exe /c start "" "' + url + '"');
} else {
alert("未対応OSです。\n" + url);
}
} catch (error) {
alert("ブラウザを開けませんでした。\n" + error);
}
}
// ==== Illustrator側でアイテムを選択する関数(BridgeTalk経由) ====
function selectInIllustrator(handle) {
function illustratorSelectByHandle(handle) {
if (app.documents.length === 0) return;
var doc = app.activeDocument;
doc.selection = null;
var targetItem = null;
// 1) placedIndex によるダイレクト検索(最速)
if (typeof handle.placedIndex === 'number' && handle.placedIndex >= 0 && handle.placedIndex < doc.placedItems.length) {
try {
targetItem = doc.placedItems[handle.placedIndex];
} catch (error) {
targetItem = null;
}
}
// 2) itemId による検索
if (!targetItem && typeof handle.itemId === 'number') {
for (var i = 0; i < doc.placedItems.length; i++) {
try {
if (doc.placedItems[i].id === handle.itemId) {
targetItem = doc.placedItems[i];
break;
}
} catch (error) {}
}
}
// 3) fsName による検索(フォールバック)
function canonFsAlready(filePath) {
if (!filePath) return filePath;
if ($.os.indexOf('Windows') >= 0) return String(filePath).split('/').join('\\').toLowerCase();
return String(filePath);
}
if (!targetItem && handle.pathFs) {
var canonicalTargetPath = canonFsAlready(handle.pathFs);
for (var j = 0; j < doc.placedItems.length; j++) {
var item = doc.placedItems[j];
var itemFilePath = null;
try {
if (item.file && item.file.fsName) itemFilePath = item.file.fsName;
} catch (error) {
itemFilePath = null;
}
if (itemFilePath && canonFsAlready(itemFilePath) === canonicalTargetPath) {
targetItem = item;
break;
}
}
}
if (!targetItem) return;
// 選択と中央表示
try {
targetItem.selected = true;
} catch (error) {}
var visibleBounds = null;
try {
visibleBounds = targetItem.visibleBounds;
} catch (error) {
visibleBounds = null;
}
if (visibleBounds) {
var centerX = (visibleBounds[0] + visibleBounds[2]) / 2;
var centerY = (visibleBounds[1] + visibleBounds[3]) / 2;
var view = doc.activeView;
view.centerPoint = [centerX, centerY];
view.zoom = 1.0;
}
try {
doc.activate();
} catch (error) {}
try {
app.redraw();
} catch (error) {}
}
var bridgeTalk = new BridgeTalk();
bridgeTalk.target = "illustrator";
bridgeTalk.body = "(" +
"function(){" +
illustratorSelectByHandle.toString() + "\n" +
"illustratorSelectByHandle(" +
"{itemId:" + (handle.itemId != null ? handle.itemId : "null") + "," +
"placedIndex:" + (handle.placedIndex != null ? handle.placedIndex : "null") + "," +
"pathFs:'" + String(handle.pathFs || '').replace(/\\/g, "\\\\").replace(/'/g, "\\'") + "'}" +
");" +
"}" +
")();";
bridgeTalk.onError = function(errorEvent) {
alert("Illustrator BridgeTalk Error: " + errorEvent.body);
};
bridgeTalk.onResult = function(resultEvent) {
try {
BridgeTalk.bringToFront("illustrator");
} catch (error) {}
};
bridgeTalk.send();
}
// ==== メインエントリ ====
function main() {
if (!app.documents.length) {
alert("開いているドキュメントがありません。");
return;
}
var activeDoc = app.activeDocument;
var imagePath = activeDoc.fullName.fsName;
// BridgeTalk: Illustratorへ配置情報を問い合わせ
var bridgeTalk = new BridgeTalk();
bridgeTalk.target = "illustrator";
bridgeTalk.body = "(" + illustratorSideForPT.toString() + ")(\"" + imagePath.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\");";
// Illustrator応答処理
bridgeTalk.onResult = function(responseEvent) {
var responseData = responseEvent.body;
if (responseData == "null" || !responseData) {
alert("Illustratorドキュメント内に一致するリンク画像が見つかりません。");
return;
}
// JSON応答のパース
var parsedObject;
var responseText = String(responseData || "").replace(/^\uFEFF/, "").replace(/^\s+|\s+$/g, "");
try {
parsedObject = JSON.parse(responseText);
} catch (error) {
try {
parsedObject = eval("(" + responseText + ")");
} catch (error) {
alert("Illustrator応答の解析に失敗しました。\nraw: " + responseData);
return;
}
}
// Photoshopドキュメント情報
var documentWidthPixels = activeDoc.width.as("px");
var documentHeightPixels = activeDoc.height.as("px");
var longSidePixels = Math.max(documentWidthPixels, documentHeightPixels);
// 配置アイテム配列の正規化
var placedItemsArray = (parsedObject && parsedObject.items && parsedObject.items.length) ? parsedObject.items : null;
if (!placedItemsArray) {
placedItemsArray = [{
longSidePt: parsedObject.longSidePt,
linkStatus: parsedObject.linkStatus,
itemId: (parsedObject && parsedObject.itemId != null) ? Number(parsedObject.itemId) : null,
placedIndex: (parsedObject && parsedObject.placedIndex != null) ? Number(parsedObject.placedIndex) : null
}];
}
// リンク状態チェック
for (var linkIndex = 0; linkIndex < placedItemsArray.length; linkIndex++) {
var linkStatus = placedItemsArray[linkIndex].linkStatus;
if (linkStatus === 1) {
alert("リンク切れの画像が含まれています。処理を中止します。");
return;
}
if (linkStatus === 2) {
alert("未更新のリンク画像が含まれています。処理を中止します。");
return;
}
}
// 最大配置サイズと最小配置サイズを検索
var maxSizeIndex = 0,
maxLongSidePt = -Infinity;
var minSizeIndex = 0,
minLongSidePt = Infinity;
for (var itemIndex = 0; itemIndex < placedItemsArray.length; itemIndex++) {
var longSidePtValue = Number(placedItemsArray[itemIndex].longSidePt) || 0;
if (longSidePtValue > maxLongSidePt) {
maxLongSidePt = longSidePtValue;
maxSizeIndex = itemIndex;
}
if (longSidePtValue < minLongSidePt) {
minLongSidePt = longSidePtValue;
minSizeIndex = itemIndex;
}
}
// 最小配置サイズのppi(最大ppi)を算出
var minLongSidePtValue = Number(placedItemsArray[minSizeIndex].longSidePt) || 0;
var minLongSideMM = minLongSidePtValue * 25.4 / 72.0;
var minSizePpi = (minLongSideMM > 0) ? (longSidePixels / (minLongSideMM / 25.4)) : 0;
// 処理対象(最大配置サイズ)の情報を取得
var targetItem = placedItemsArray[maxSizeIndex];
var longSidePt = Number(targetItem.longSidePt) || 0;
var illustratorItemId = (targetItem.itemId != null) ? Number(targetItem.itemId) : null;
var illustratorPlacedIndex = (targetItem.placedIndex != null) ? Number(targetItem.placedIndex) : null;
var longSideMM = longSidePt * 25.4 / 72.0;
var placedPPI = (longSideMM > 0) ? (longSidePixels / (longSideMM / 25.4)) : 0;
// ==== リサイズ確認ダイアログ ====
function showConfirmDialog(messageBase, placedPPI, imagePath) {
var dialog = new Window("dialog", "画像リサイズ " + (typeof SCRIPT_VERSION !== 'undefined' ? SCRIPT_VERSION : ""));
dialog.orientation = "column";
dialog.alignChildren = ["fill", "top"];
dialog.margins = 15;
var infoPanel = dialog.add("panel", undefined, "画像情報");
infoPanel.orientation = "column";
infoPanel.alignChildren = ["fill", "top"];
infoPanel.margins = [10, 12, 10, 12];
infoPanel.spacing = 2;
var separatorIndex = messageBase.indexOf("\n\n");
var headerText = (separatorIndex >= 0) ? messageBase.substring(0, separatorIndex) : messageBase;
var tailText = (separatorIndex >= 0) ? messageBase.substring(separatorIndex + 2) : "";
var infoText = infoPanel.add("statictext", undefined, headerText, {
multiline: true
});
infoText.minimumSize.width = 400;
if (tailText) {
var messageText = infoPanel.add("statictext", undefined, tailText, {
multiline: true
});
messageText.minimumSize.width = 400;
try {
var font = messageText.graphics.font;
messageText.graphics.font = ScriptUI.newFont(font.name, 'Bold', font.size);
} catch (error) {}
}
// 指定解像度
var radioGroup = dialog.add("panel", undefined, "指定解像度");
radioGroup.orientation = "row";
radioGroup.alignChildren = ["left", "center"];
radioGroup.margins = [10, 12, 10, 12];
var radioButtons = [];
for (var i = 0; i < targetPPIList.length; i++) {
var radioButton = radioGroup.add('radiobutton', undefined, String(targetPPIList[i]));
radioButtons.push(radioButton);
}
// 前回設定の読み込み
var customOptionsPrefs = loadPrefsCO();
var initialRadioIndex = 0;
var initialUpscaleMethod = "deepUpscale";
var initialDownscaleMethod = "bicubic";
if (customOptionsPrefs.usePrev) {
initialRadioIndex = (customOptionsPrefs.radioIndex >= 0 && customOptionsPrefs.radioIndex < targetPPIList.length) ? customOptionsPrefs.radioIndex : 0;
initialUpscaleMethod = customOptionsPrefs.upscaleMethod || initialUpscaleMethod;
initialDownscaleMethod = customOptionsPrefs.downMethod || initialDownscaleMethod;
}
for (var radioButtonIndex = 0; radioButtonIndex < radioButtons.length; radioButtonIndex++) {
radioButtons[radioButtonIndex].value = (radioButtonIndex === initialRadioIndex);
}
// 拡大メソッド
var upscaleMethodPanel = dialog.add("panel", undefined, "拡大メソッド");
upscaleMethodPanel.orientation = "row";
upscaleMethodPanel.alignChildren = ["left", "center"];
upscaleMethodPanel.margins = [10, 12, 10, 12];
var upscaleMethodRadioButtons = [];
var upscaleMethodLabels = [
"ディテールを保持2.0(推奨)",
"ディテールを保持(旧)",
"ニアレストネイバー"
];
var upscaleMethodValues = [
"deepUpscale",
"preserveDetails",
"nearestNeighbor"
];
for (var i = 0; i < upscaleMethodLabels.length; i++) {
var upscaleRadioButton = upscaleMethodPanel.add('radiobutton', undefined, upscaleMethodLabels[i]);
upscaleMethodRadioButtons.push(upscaleRadioButton);
upscaleRadioButton.value = (upscaleMethodValues[i] === initialUpscaleMethod);
}
// 縮小メソッド選択
var downscaleMethodPanel = dialog.add("panel", undefined, "縮小メソッド");
downscaleMethodPanel.orientation = "row";
downscaleMethodPanel.alignChildren = ["left", "center"];
downscaleMethodPanel.margins = [10, 12, 10, 12];
var downscaleMethodRadioButtons = [];
var downscaleMethodLabels = [
"バイキュービック(滑らか)",
"ニアレストネイバー"
];
var downscaleMethodValues = [
"bicubic",
"nearestNeighbor"
];
for (var i = 0; i < downscaleMethodLabels.length; i++) {
var downscaleRadioButton = downscaleMethodPanel.add('radiobutton', undefined, downscaleMethodLabels[i]);
downscaleMethodRadioButtons.push(downscaleRadioButton);
downscaleRadioButton.value = (downscaleMethodValues[i] === initialDownscaleMethod);
}
// 拡縮率のリアルタイム表示
var scaleText = dialog.add("statictext", undefined, "");
scaleText.minimumSize.width = 400;
try {
var scaleFont = scaleText.graphics.font;
scaleText.graphics.font = ScriptUI.newFont(scaleFont.name, 'Bold', scaleFont.size);
} catch (error) {}
// 警告テキスト(背景は変更せず、テキストのみ表示)
var warningText = dialog.add("statictext", undefined, " ", {
multiline: true
});
warningText.preferredSize = [400, 80];
warningText.minimumSize = [400, 80];
warningText.alignment = ["fill", "top"];
try {
warningText.graphics.font = ScriptUI.newFont(warningText.graphics.font.name, 'Bold', warningText.graphics.font.size);
} catch (error) {}
// 追加警告(白・ボールド、最下段に表示)
var extraWarningText = dialog.add("statictext", undefined, " ", {
multiline: true
});
extraWarningText.preferredSize = [400, 40];
extraWarningText.minimumSize = [400, 40];
extraWarningText.alignment = ["fill", "top"];
try {
var extraWarningFont = extraWarningText.graphics.font;
extraWarningText.graphics.font = ScriptUI.newFont(extraWarningFont.name, 'Bold', extraWarningFont.size);
} catch (error) {}
dialog.preferredSize = [460, 360];
function updateUI() {
var selectedPPI = null;
for (var i = 0; i < radioButtons.length; i++) {
if (radioButtons[i].value) {
selectedPPI = parseInt(radioButtons[i].text);
break;
}
}
var scaleRatio = (selectedPPI / placedPPI);
var scalePercent = scaleRatio * 100;
scaleText.text = "拡縮率: " + scalePercent.toFixed(2) + " %";
// しきい値ベースの注意喚起(無駄拡縮/過大拡大)+ Bitmap専用注意
var warningMessage = "";
// Bitmap(2値)モードは最優先で注意喚起
try {
if (activeDoc && activeDoc.mode == DocumentMode.BITMAP) {
warningMessage = "【注意:キャンセル推奨】\n2値画像のリサイズは、モアレが発生しやすいため推奨しません。\nリサンプルする場合は2,400ppi以下、600ppi以上を選択してください。";
}
} catch (error) {}
// 既存のしきい値警告を付加
if (scaleRatio >= efScaleMin && scaleRatio <= efScaleMax) {
warningMessage += (warningMessage ? "\n" : "") + "【注意:キャンセル推奨】\n拡大率が " + scalePercent.toFixed(2) + "% です。無駄な拡縮で、余計な画像劣化の可能性があります。";
}
if (scaleRatio > scaleMax) {
warningMessage += (warningMessage ? "\n" : "") + "【警告:キャンセルを推奨】\n 拡大率が " + (scaleMax * 100).toFixed(0) + "% を超えています。\nPhotoshop以外の手段を検討してください。";
}
// 最少配置サイズのppiに対する逸脱判定(高すぎる場合)
var extraWarningMessage = "";
try {
if (typeof minSizePpi !== 'undefined' && isFinite(minSizePpi) && placedItemsArray && placedItemsArray.length > 1) {
var minPlacementScale = selectedPPI / minSizePpi; // minSizePpi が大きいほど scale は小さくなる
if (minPlacementScale < efScaleMin) {
extraWarningMessage = "最小配置画像の実効解像度が高すぎます。画像ファイルを分けることを推奨します。";
}
}
} catch (error) {}
warningText.text = warningMessage;
var hasWarning = !!warningMessage;
try {
var warningPen = warningText.graphics.newPen(warningText.graphics.PenType.SOLID_COLOR, hasWarning ? [1, 0, 0, 1] : [0, 0, 0, 1], 1);
warningText.graphics.foregroundColor = warningPen;
warningText.window.update();
} catch (error) {}
// 追加メッセージ(白・ボールド)を最下段に
extraWarningText.text = extraWarningMessage ? ("\n" + extraWarningMessage) : " ";
try {
var extraWarningPen = extraWarningText.graphics.newPen(extraWarningText.graphics.PenType.SOLID_COLOR, [1, 1, 1, 1], 1);
extraWarningText.graphics.foregroundColor = extraWarningPen;
extraWarningText.window.update();
} catch (error) {}
dialog.layout.layout(true);
dialog.layout.resize();
}
updateUI();
for (var i = 0; i < radioButtons.length; i++) {
radioButtons[i].onClick = updateUI;
}
// 前回設定値を使用 チェックボックス(ボタン群の直上)
var usePrevGroup = dialog.add('group');
usePrevGroup.alignment = ['left', 'center'];
var usePrevCheckbox = usePrevGroup.add('checkbox', undefined, '前回設定値を使用');
usePrevCheckbox.value = customOptionsPrefs.usePrev === true;
// ラジオボタンに値を設定する共通関数
function setRadioValues(radioIndex, upscaleMethod, downscaleMethod) {
var validIndex = (radioIndex >= 0 && radioIndex < radioButtons.length) ? radioIndex : 0;
for (var i = 0; i < radioButtons.length; i++) radioButtons[i].value = (i === validIndex);
for (var j = 0; j < upscaleMethodRadioButtons.length; j++) upscaleMethodRadioButtons[j].value = (upscaleMethodValues[j] === upscaleMethod);
for (var k = 0; k < downscaleMethodRadioButtons.length; k++) downscaleMethodRadioButtons[k].value = (downscaleMethodValues[k] === downscaleMethod);
updateUI();
}
function applyFromPrefs(prefs) {
setRadioValues(prefs.radioIndex, prefs.upscaleMethod, prefs.downMethod);
}
function applyDefaultsNow() {
setRadioValues(0, 'deepUpscale', 'bicubic');
}
usePrevCheckbox.onClick = function() {
if (usePrevCheckbox.value) applyFromPrefs(customOptionsPrefs);
else applyDefaultsNow();
};
var buttonGroup = dialog.add("group");
buttonGroup.orientation = "row";
buttonGroup.alignment = "center"; // グループ自体を中央揃え
buttonGroup.alignChildren = ["center", "center"]; // ボタンも中央揃え
buttonGroup.margins = [0, 15, 0, 0];
var okButton = buttonGroup.add("button", undefined, "処理する (S / Enter)");
var cancelButton = buttonGroup.add("button", undefined, "キャンセル (Esc)");
var illustratorButton = buttonGroup.add("button", undefined, "Illustratorで選択 (I)");
var helpButton = buttonGroup.add("button", undefined, "?");
helpButton.preferredSize = [24, 24];
okButton.helpTip = "S キー または Enter で実行";
cancelButton.helpTip = "Esc でキャンセル";
illustratorButton.helpTip = "I キー で Illustrator で選択";
helpButton.helpTip = "このスクリプトの説明ページをブラウザで開きます";
okButton.properties = {
name: 'ok'
}; // Enter = OK
cancelButton.properties = {
name: 'cancel'
}; // Esc = Cancel
okButton.active = true; // 初期フォーカスをOKに
dialog.defaultElement = okButton; // Enter で OK 実行
dialog.cancelElement = cancelButton; // Esc でキャンセル
// ラジオボタンの選択値を取得する共通関数
function getSelectedRadioValues() {
var selectedRadioIndex = 0;
for (var i = 0; i < radioButtons.length; i++) {
if (radioButtons[i].value) {
selectedRadioIndex = i;
break;
}
}
var selectedUpscaleMethod = null;
for (var j = 0; j < upscaleMethodRadioButtons.length; j++) {
if (upscaleMethodRadioButtons[j].value) {
selectedUpscaleMethod = upscaleMethodValues[j];
break;
}
}
var selectedDownscaleMethod = null;
for (var k = 0; k < downscaleMethodRadioButtons.length; k++) {
if (downscaleMethodRadioButtons[k].value) {
selectedDownscaleMethod = downscaleMethodValues[k];
break;
}
}
return {
radioIndex: selectedRadioIndex,
upscaleMethod: selectedUpscaleMethod || 'deepUpscale',
downMethod: selectedDownscaleMethod || 'bicubic'
};
}
// ダイアログクローズ時の共通処理
function savePrefsAndClose(exitCode, saveAll) {
try {
if (saveAll) {
var selectedValues = getSelectedRadioValues();
savePrefsCO({
usePrev: usePrevCheckbox.value === true,
radioIndex: selectedValues.radioIndex,
upscaleMethod: selectedValues.upscaleMethod,
downMethod: selectedValues.downMethod
});
} else {
saveUsePrevOnly(usePrevCheckbox.value);
}
} catch (error) {}
try {
dialog.close(exitCode);
} catch (error) {
dialog.close();
}
}
okButton.onClick = function() {
savePrefsAndClose(1, true);
};
cancelButton.onClick = function() {
savePrefsAndClose(0, false);
};
illustratorButton.onClick = function() {
savePrefsAndClose(0, false);
var selectionHandle = illustratorSelectionHandle || {
itemId: null,
placedIndex: null,
pathFs: imagePath
};
selectInIllustrator(selectionHandle);
};
helpButton.onClick = function() {
// 設定ダイアログは閉じずに、説明ページだけを開く
openURLInBrowser("https://gist.github.com/Yamonov/b63d9c67402ef7af4c17ab33caccce31");
};
dialog.addEventListener('keydown', function(keyEvent) {
try {
var keyName = String(keyEvent.keyName || '').toUpperCase();
if (keyName === 'S') {
okButton.notify('onClick');
keyEvent.preventDefault();
} else if (keyName === 'I') {
illustratorButton.notify('onClick');
keyEvent.preventDefault();
} else if (keyName === 'ENTER' || keyName === 'RETURN') {
okButton.notify('onClick');
keyEvent.preventDefault();
}
} catch (error) {}
});
dialog.onShow = function() {
try {
dialog.layout.layout(true);
dialog.layout.resize();
dialog.center();
} catch (error) {}
};
var isOk = (dialog.show() === 1);
var result;
if (isOk) {
var selectedValues = getSelectedRadioValues();
var targetPpi = targetPPIList[selectedValues.radioIndex];
result = {
ppi: targetPpi,
method: selectedValues.upscaleMethod,
downMethod: selectedValues.downMethod,
usePrev: !!usePrevCheckbox.value,
cancelled: false
};
} else {
result = {
usePrev: !!usePrevCheckbox.value,
cancelled: true
};
}
return result;
}
// ダイアログに表示する情報の整形(ファイル名・フォルダ・色情報→配置サイズ・ppi)
// 画像情報収集(カラーモード/プロファイル/ファイル名/フォルダ)
function modeToString(documentMode) {
switch (documentMode) {
case DocumentMode.BITMAP:
return "Bitmap";
case DocumentMode.GRAYSCALE:
return "Grayscale";
case DocumentMode.INDEXEDCOLOR:
return "Indexed";
case DocumentMode.RGB:
return "RGB";
case DocumentMode.CMYK:
return "CMYK";
case DocumentMode.LAB:
return "Lab";
case DocumentMode.MULTICHANNEL:
return "Multichannel";
case DocumentMode.DUOTONE:
return "Duotone";
default:
return "Unknown";
}
}
var photoshopFileName = activeDoc.name;
var photoshopFilePath;
try {
photoshopFilePath = activeDoc.fullName.parent.fsName;
} catch (error) {
photoshopFilePath = "(未保存)";
} // 表示はフォルダのみ(ファイル名は除外)
var photoshopColorProfile;
try {
photoshopColorProfile = activeDoc.colorProfileName;
} catch (error) {
photoshopColorProfile = "(不明)";
}
var photoshopColorMode = modeToString(activeDoc.mode);
// Illustrator側選択用ハンドル(UI内 I ボタンで使用)
var illustratorSelectionHandle = {
itemId: illustratorItemId,
placedIndex: illustratorPlacedIndex,
pathFs: imagePath
};
// ダイアログに表示する基本メッセージ(配置サイズとppi)
var messageBase;
if (placedItemsArray.length > 1) {
messageBase =
"ファイル名: " + photoshopFileName + "\n" +
"フォルダ: " + photoshopFilePath + "\n" +
"カラーモード:" + photoshopColorMode + "(" + photoshopColorProfile + ")\n" +
"\n" +
"処理対象の配置サイズ(長辺): " + longSideMM.toFixed(2) + " mm\n" +
"処理対象の実効ppi: " + placedPPI.toFixed(2) + "\n" +
"\n" +
"配置点数: " + placedItemsArray.length + "\n" +
"最小画像のppi: " + (isFinite(minSizePpi) ? minSizePpi.toFixed(2) : "-") + "\n" +
"※最大サイズの画像を処理します。";
} else {
messageBase =
"ファイル名: " + photoshopFileName + "\n" +
"フォルダ: " + photoshopFilePath + "\n" +
"カラーモード:" + photoshopColorMode + "(" + photoshopColorProfile + ")\n" +
"\n" +
"処理対象の配置サイズ(長辺): " + longSideMM.toFixed(2) + " mm\n" +
"処理対象の実効ppi: " + placedPPI.toFixed(2) + "\n";
}
// 解像度選択ダイアログを表示し、ユーザーの選択を取得
var dialogResult = showConfirmDialog(messageBase, placedPPI, imagePath);
// ダイアログクローズ時にも usePrev を保存
try {
if (dialogResult && dialogResult.hasOwnProperty('usePrev')) saveUsePrevOnly(dialogResult.usePrev);
} catch (error) {}
// キャンセル・クローズ時は処理を中止
if (!dialogResult || dialogResult.cancelled) {
return;
}
var targetPPI = dialogResult.ppi;
var upscaleMethod = dialogResult.method;
var downscaleMethod = dialogResult.downMethod;
var scaleRatio = targetPPI / placedPPI;
// 長辺を拡縮率に応じてリサイズ(縦横比維持)
var newWidthPixels, newHeightPixels;
if (documentWidthPixels >= documentHeightPixels) {
newWidthPixels = longSidePixels * scaleRatio;
newHeightPixels = documentHeightPixels * scaleRatio;
} else {
newHeightPixels = longSidePixels * scaleRatio;
newWidthPixels = documentWidthPixels * scaleRatio;
}
// 縮小/拡大でメソッドを分岐
if (scaleRatio < 1) {
if (downscaleMethod === "bicubic") {
activeDoc.resizeImage(UnitValue(newWidthPixels, "px"), UnitValue(newHeightPixels, "px"), targetPPI, ResampleMethod.BICUBIC);
} else if (downscaleMethod === "nearestNeighbor") {
activeDoc.resizeImage(UnitValue(newWidthPixels, "px"), UnitValue(newHeightPixels, "px"), targetPPI, ResampleMethod.NEARESTNEIGHBOR);
} else {
// 万一未知の値の場合はBICUBIC
activeDoc.resizeImage(UnitValue(newWidthPixels, "px"), UnitValue(newHeightPixels, "px"), targetPPI, ResampleMethod.BICUBIC);
}
} else {
// 拡大時は選択された拡大メソッドで分岐
if (upscaleMethod === "deepUpscale") {
// ActionDescriptorでディテールを保持2.0(推奨)
var resizeDescriptor = new ActionDescriptor();
resizeDescriptor.putUnitDouble(charIDToTypeID('Wdth'), charIDToTypeID('#Pxl'), newWidthPixels);
resizeDescriptor.putUnitDouble(charIDToTypeID('Hght'), charIDToTypeID('#Pxl'), newHeightPixels);
resizeDescriptor.putUnitDouble(charIDToTypeID('Rslt'), charIDToTypeID('#Rsl'), targetPPI);
resizeDescriptor.putBoolean(stringIDToTypeID('scaleStyles'), true);
resizeDescriptor.putEnumerated(charIDToTypeID('Intr'), charIDToTypeID('Intp'), stringIDToTypeID('deepUpscale'));
executeAction(charIDToTypeID('ImgS'), resizeDescriptor, DialogModes.NO);
} else if (upscaleMethod === "preserveDetails") {
// ResampleMethod.PRESERVEDETAILS
activeDoc.resizeImage(UnitValue(newWidthPixels, "px"), UnitValue(newHeightPixels, "px"), targetPPI, ResampleMethod.PRESERVEDETAILS);
} else if (upscaleMethod === "nearestNeighbor") {
// ResampleMethod.NEARESTNEIGHBOR
activeDoc.resizeImage(UnitValue(newWidthPixels, "px"), UnitValue(newHeightPixels, "px"), targetPPI, ResampleMethod.NEARESTNEIGHBOR);
} else {
// 万一未知の値の場合はBICUBIC
activeDoc.resizeImage(UnitValue(newWidthPixels, "px"), UnitValue(newHeightPixels, "px"), targetPPI, ResampleMethod.BICUBIC);
}
}
// 最後に100%表示に切り替え
app.runMenuItem(stringIDToTypeID("actualPixels"));
};
// ----- BridgeTalk通信エラー(Illustrator未起動/応答なし等)
bridgeTalk.onError = function(errorEvent) {
alert("Illustratorとの通信エラー: " + errorEvent.body);
};
try {
bridgeTalk.timeout = 30;
} catch (error) {}
bridgeTalk.send();
}
// ===== Illustrator側:対象パスの配置アイテムを探索し一致した全配置の長辺(pt)等を返す =====
function illustratorSideForPT(photoshopImagePath) {
var targetPath = photoshopImagePath;
var isWindows = $.os.indexOf("Windows") >= 0;
function canonFsAlready(filePath) {
if (!filePath) return filePath;
var normalizedPath = String(filePath);
if (isWindows) {
// Remove "\\?\" prefix and normalize "\\?\UNC\server\share" -> "\\server\share"
if (normalizedPath.length >= 4 && normalizedPath.substring(0, 4) === "\\\\?\\") {
var restOfPath = normalizedPath.substring(4);
if (restOfPath.length >= 4 && restOfPath.substring(0, 4).toLowerCase() === "unc\\") {
normalizedPath = "\\\\" + restOfPath.substring(4); // prepend UNC prefix
} else {
normalizedPath = restOfPath; // long-path local form: \\?\C:\ -> C:\
}
}
// Normalize separators to backslash
normalizedPath = normalizedPath.split('/').join('\\');
// Collapse redundant backslashes (except keep leading UNC double backslash)
normalizedPath = normalizedPath.replace(/\\+/g, "\\");
// Unicode正規化 (NFC) を File() 経由で実現
try {
normalizedPath = File(normalizedPath).fsName; // 実在しないパスでもOK、NFC寄せと区切り統一がかかる
} catch (error) {}
}
return normalizedPath;
}
var canonicalTargetPath = canonFsAlready(targetPath);
var rawTargetPath = targetPath; // Windows用にまずは生の比較を試す
if (app.documents.length === 0) return null;
var doc = app.activeDocument;
var placedItems = doc.placedItems;
var matchedItems = [];
// basename抽出(ループ外で事前計算)
var targetBaseName = "";
try {
var targetPathString = String(isWindows ? rawTargetPath : canonicalTargetPath);
var lastSlash = Math.max(targetPathString.lastIndexOf('/'), targetPathString.lastIndexOf('\\'));
targetBaseName = (lastSlash >= 0) ? targetPathString.substring(lastSlash + 1) : targetPathString;
targetBaseName = targetBaseName.toLowerCase();
} catch (error) {}
// 回転・斜行に依存しない長辺(pt)を算出(bboxは最初の一致のみ取得し、以降は行列スケールだけを適用)
var baseWidthPt = null;
var baseHeightPt = null;
for (var i = 0; i < placedItems.length; i++) {
var item = placedItems[i];
var isMatched = false;
// 1) 最軽量: basename一致チェック(小文字化のみ)
try {
var itemName = String(item.name || "").toLowerCase();
if (itemName && targetBaseName && itemName === targetBaseName) {
isMatched = true;
}
} catch (error) {}
if (!isMatched) {
// 2) 軽量: fsNameによるRAW文字列比較
var itemFilePath = null;
try {
if (item.file && item.file.fsName) itemFilePath = item.file.fsName;
} catch (error) {
itemFilePath = null;
}
if (itemFilePath) {
if (isWindows) {
var rawItemPath = String(itemFilePath);
var rawTarget = String(rawTargetPath);
if (rawItemPath === rawTarget || rawItemPath.toLowerCase() === rawTarget.toLowerCase()) {
isMatched = true;
} else {
// 3) 重量: File().fsNameを含む正規化比較(最後の手段)
var canonicalItemPath = canonFsAlready(rawItemPath);
var canonicalTarget = canonicalTargetPath;
if (canonicalItemPath === canonicalTarget || canonicalItemPath.toLowerCase() === canonicalTarget.toLowerCase()) {
isMatched = true;
}
}
} else {
// mac等: canonical比較
var canonicalItemPath = canonFsAlready(itemFilePath);
if (canonicalItemPath === canonicalTargetPath) {
isMatched = true;
}
}
}
}
if (!isMatched) continue;
if (baseWidthPt === null || baseHeightPt === null) {
// 最初のヒットでのみ未変形サイズ(pt)を取得
try {
var boundingBox = item.boundingBox; // [y1,x1,y2,x2]
baseWidthPt = Math.abs(boundingBox[3] - boundingBox[1]);
baseHeightPt = Math.abs(boundingBox[0] - boundingBox[2]);
} catch (error) {
baseWidthPt = 0;
baseHeightPt = 0;
}
}
var longSidePt = 0;
try {
var matrix = item.matrix;
var scaleX = Math.sqrt(matrix.mValueA * matrix.mValueA + matrix.mValueB * matrix.mValueB);
var scaleY = Math.sqrt(matrix.mValueC * matrix.mValueC + matrix.mValueD * matrix.mValueD);
var widthPt = baseWidthPt * scaleX;
var heightPt = baseHeightPt * scaleY;
if (widthPt > 0 && heightPt > 0) longSidePt = Math.max(widthPt, heightPt);
} catch (error) {
longSidePt = 0;
}
var linkStatus = 0;
try {
if (typeof item.linkStatus !== 'undefined') linkStatus = item.linkStatus;
} catch (error) {
linkStatus = 0;
}
var placedItemId = null;
try {
placedItemId = item.id;
} catch (error) {
placedItemId = null;
}
matchedItems.push({
longSidePt: longSidePt,
linkStatus: linkStatus,
itemId: placedItemId,
placedIndex: i
});
}
if (!matchedItems.length) return "null";
// JSON.stringify が無い環境向け:オブジェクトリテラル文字列を手組みで返す
var jsonParts = [];
for (var resultIndex = 0; resultIndex < matchedItems.length; resultIndex++) {
var resultItem = matchedItems[resultIndex];
var jsonSegment = "{longSidePt:" + (resultItem.longSidePt || 0) + ", linkStatus:" + (resultItem.linkStatus || 0) + ", itemId:" + (resultItem.itemId != null ? resultItem.itemId : "null") + ", placedIndex:" + (resultItem.placedIndex != null ? resultItem.placedIndex : "null") + "}";
jsonParts.push(jsonSegment);
}
return "{items:[" + jsonParts.join(",") + "]}";
}
// ===== 実行開始 =====
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment