Photoshopで開いている画像をIllustrator配置サイズにリサイズするAppleScript

:::note warn
動きます。動きますが、ロジックから書き直す予定。
:::
:::note warn
Illustratorのバージョンによって、配置画像がどう扱われるかがだいぶ変わります。
2024ではJPEGは72ppi扱いされているため、このスクリプトではそのように扱いますが、2023では動作が変わるため、コードを変更しないと正しくサイズを取得できません。
:::

:::note warn
バージョンアップしたときには、サンプルファイルを作って動作をテストすることをお勧めします。
:::

Photoshopで開いている画像をIllustratorドキュメントから探し、matrixを用いて計算した横幅サイズに指定解像度でリサイズするAppleScriptです。
jsxで書こうとして、Illustratorのbridgetalkがめちゃくちゃ面倒だったため、BT要らないAppleScriptで、ChatGPT4oにアフィン変換のコード書かせて書きました。

- Photoshop側から実行し、ダイアログ類もPs側から出す
- ファイルタイプ、マスク状態、回転状態に依らず正しいサイズを取得できること（ここに穴がありそう）
- 拡大・縮小で処理を分岐する（縮小は古き良きバイキュービック、拡大はディテールを保持2.0（deepUpscale）を使用する
- 拡大率によって処理の停止を勧告する（scaleMax200%以上は保持2.0でもしんどいので他のAIリサンプルで処理したほうがいい。また微妙な拡縮は劣化するだけで無駄）
- 重ねがけしないよう、psのスナップショットに記録する（ヒストリーなのでファイル閉じれば消える）

1枚ずつ処理することを目的としています。
リサイズ処理は破壊的処理でもあり、モアレを作ってしまうとまずいこと
リサイズ後のシャープ処理は画像に最適化されてなければならないこと
以上の理由から、links一括処理は危険だと考えているため。


```AppleScript:
(*===================================
諸注意
Illustratorリンクの更新状態は見ていません。
リンク状態が正常であることをまず確認してから実行してください。
Photoshopの単位をpxに変更します（元の状態に戻しません）。
===================================*)

(*===================================
ターゲット解像度と、リサイズ限界値の変数設定
===================================*)
set newPPI to 350 -- 解像度設定
--Keyboard Maestroから実行する場合、解像度をVariableに入れておく。
--Keyboard Maestroを使わない場合は下の1行をコメントアウトする。
tell application "Keyboard Maestro Engine" to set newPPI to getvariable "targetPPI"

set scaleMax to 250 -- この倍率を超えたら処理の停止勧告

--この倍率以内なら処理の停止勧告（無駄な処理で劣化させないため）
set efScaleMax to 110
set efScaleMin to 90

(*===================================
Photoshopの処理
画像ファイルのパス名とピクセル幅を取得
Illustratorはjpeg/png/クリッピングパス付きPSDを72ppiと見なすため、
ファイル種別も判別する
===================================*)

tell application id "com.adobe.photoshop"
	
	-- ドキュメントが開かれていなければ中止する
	if (count of documents) is 0 then
		display alert "⚠️開いているドキュメントがありません。"
		return
	end if
	
	--スナップショットに"ResizeImagebyAS"があるなら中止する
	if (do javascript "(function(){var myH=app.activeDocument.historyStates;for(i=0;i<myH.length;i++){if(myH[i].snapshot && myH[i].name=='ResizeImagebyAS'){return 1}}return 0;}())") = "1" then
		display alert "⚠️実行済みです" & return & "再度実行する際は、" & return & "ヒストリーパネルのスナップショット" & return & "\"ResizeImagebyAS\"" & return & "を削除してください"
		return
	end if
	
	set psDoc to current document
	set ruler units of settings to pixel units
	set pxW to width of psDoc
	set pxH to height of psDoc
	set psPPI to resolution of psDoc
	set psPath to file path of psDoc as string
	
	-- ファイルの種類を判定
	if psPath ends with ".jpg" or psPath ends with ".jpeg" then
		set fileType to "JPEG"
	else if psPath ends with ".png" then
		set fileType to "PNG"
	else if psPath ends with ".psd" then
		set fileType to "PSD"
	else
		set fileType to "OTHER"
	end if
	
	-- クリッピングパスが設定されているかを判定
	if fileType is "PSD" then
		set pathItems to path items of psDoc
		set clippingFound to false
		repeat with pItem in pathItems
			if kind of pItem is clipping then
				set clippingFound to true
				exit repeat
			end if
		end repeat
	else
		set clippingFound to false
	end if
end tell


(*===================================
Illustratorでパス名が同じものを拾い
matrixからサイズとscaleを取得
===================================*)

tell application id "com.adobe.illustrator"
	-- ドキュメントが開かれていなければ中止する
	if (count of documents) is 0 then
		my PSMsg("⚠️Illustratorドキュメントが開かれていません。")
		return
	end if
	
	set ilDoc to current document
	set placedItems to every placed item of ilDoc
	set matchItems to {}
	
	-- 同じファイルパスの配置アイテムを探す
	repeat with i in placedItems
		if file path of i as string = psPath then
			set end of matchItems to i
		end if
	end repeat
	
	if (count of matchItems) is 0 then
		my PSMsg("⚠️一致する配置アイテムが見つかりませんでした。")
		return
	end if
	
	-- 最も大きく配置されているアイテムを探す
	set largestItem to item 1 of matchItems
	set maxScale to 0
	repeat with pItem in matchItems
		set matrixVals to matrix of pItem
		set aVal to mvalue_a of matrixVals
		set bVal to mvalue_b of matrixVals
		set cVal to mvalue_c of matrixVals
		set dVal to mvalue_d of matrixVals
		
		-- スケールを計算
		set scaleX to ((aVal * aVal) + (cVal * cVal)) ^ 0.5
		set scaleY to ((bVal * bVal) + (dVal * dVal)) ^ 0.5
		set curScale to scaleX * scaleY -- 現在のスケールを計算
		
		if curScale > maxScale then
			set maxScale to curScale -- 最大スケールを更新
			set largestItem to pItem -- 最大スケールのアイテムを更新
		end if
	end repeat
	
	-- 配置サイズを計算
	set matrixVals to matrix of largestItem
	set aVal to mvalue_a of matrixVals
	set cVal to mvalue_c of matrixVals
	
	-- ファイルタイプに応じた処理
	if fileType is "JPEG" or fileType is "PNG" or (fileType is "PSD" and clippingFound) then
		set actualPpi to 72
	else
		set actualPpi to psPPI
	end if
	
	set mmW to (pxW / actualPpi) * 25.4
	set scaleX to ((aVal * aVal) + (cVal * cVal)) ^ 0.5
	
	-- 配置サイズを計算
	set placedW to mmW * scaleX
end tell

(*===================================
Photoshopリサイズ処理
===================================*)

tell application id "com.adobe.photoshop"
	activate
	set pxW_before to width of psDoc
	set mmW_before to (pxW_before / psPPI) * 25.4
	set pxW_after to (placedW / 25.4) * newPPI
	set resizeScale to pxW_after / pxW_before
	set scaleAlert to ""
	
	-- 拡大率がscaleMaxを越える場合の確認
	if (resizeScale * 100) > scaleMax then
		set defaultBtn to "キャンセル"
		set scaleAlert to "⚠️拡大率が" & (scaleMax as text) & "%を越えています。キャンセルしますか？"
	else
		if efScaleMin < resizeScale * 100 and resizeScale * 100 < efScaleMax then
			set defaultBtn to "キャンセル"
			set scaleAlert to (("⚠️拡大率が" & efScaleMin as text) & "％～" & efScaleMax as text) & "％程度です。" & return & "⚠️不要な劣化を避けるため、処理しないことをお勧めします。"
		else
			set defaultBtn to "実行"
		end if
	end if
	
	set dialogMessage to (((((round (pxW_before * 10) / 10) as text) & " px（配置サイズ：" & (placedW + 0.05) div 0.1 / 10) as text) & "mm）" & return & ¬
		"▼" & (round (resizeScale * 1000) / 10) as text) & "%" & return & ¬
		((round (pxW_after * 10) / 10) as text) & " px（" & newPPI & "ppiで" & (placedW + 0.05) div 0.1 / 10 & "mm）" & return & scaleAlert
	
	display dialog dialogMessage buttons {"キャンセル", "実行"} default button defaultBtn
	if button returned of result is "キャンセル" then
		return
	end if
	
	-- JavaScriptでリサイズ処理
	if resizeScale < 1 then
		-- 縮小処理
		do javascript "var doc = app.activeDocument;
			doc.resizeImage(UnitValue(" & pxW_after & ", 'px'), null, " & newPPI & ", ResampleMethod.BICUBIC);
			"
	else
		-- 拡大処理
		do javascript "var doc = app.activeDocument;
			var desc = new ActionDescriptor();
			desc.putUnitDouble(app.charIDToTypeID('Wdth'), app.charIDToTypeID('#Pxl'), " & pxW_after & ");
			desc.putBoolean(app.stringIDToTypeID('scaleStyles'), true);
			desc.putBoolean(app.stringIDToTypeID('constrainProportions'), true);
			desc.putEnumerated(app.charIDToTypeID('Intr'), app.stringIDToTypeID('interpolationType'), app.stringIDToTypeID('deepUpscale'));
			desc.putInteger(app.stringIDToTypeID('noise'), 10);
			desc.putUnitDouble(app.charIDToTypeID('Rslt'), app.charIDToTypeID('#Rsl'), " & newPPI & ");executeAction(app.charIDToTypeID('ImgS'), desc, DialogModes.NO);
			"
	end if
	
	-- ピクセル等倍表示
	tell application "System Events" to keystroke "0" using {command down, option down}
	
	--スナップショットに"ResizeImagebyAS"を作り、実行したことを記録する。
	do javascript "var idMk=charIDToTypeID('Mk  ');var desc7=new ActionDescriptor();var idnull=charIDToTypeID('null');var ref1=new ActionReference();var idSnpS=charIDToTypeID('SnpS');ref1.putClass(idSnpS);	desc7.putReference(idnull,ref1);var idFrom=charIDToTypeID('From');var ref2=new ActionReference();var idHstS=charIDToTypeID('HstS');var idCrnH=charIDToTypeID('CrnH');ref2.putProperty(idHstS,idCrnH);desc7.putReference(idFrom,ref2);var idNm=charIDToTypeID('Nm  ');desc7.putString(idNm,'''ResizeImagebyAS''');var idUsng=charIDToTypeID('Usng');var idHstS=charIDToTypeID('HstS');var idFllD=charIDToTypeID('FllD');desc7.putEnumerated(idUsng,idHstS,idFllD);executeAction(idMk,desc7,DialogModes.NO);"
	
	
end tell

(*===================================
関数
===================================*)

on PSMsg(DispMesg) -- InDesign側でなく、実行側のPsでアラートを出すための関数
	tell application id "com.adobe.photoshop" to display alert DispMesg
end PSMsg

```