InDesignのリンク画像をPhotoshopで開いて保存するスクリプト

# 結論

**BridgeTalk**を使えばできるよ！

# BridgeTalkまとめ
基本的に以下で紹介されている内容が詳しいです。
特に　「[中綴製作所](http://nakatoji.lolipop.jp/index.php/extendscript/bridgetalk/bt-list)」　様は他のExtendScriptについても詳しく説明されていて、お世話になっております。

[他のアプリケーションにスクリプトを実行させる](http://nakatoji.lolipop.jp/index.php/extendscript/bridgetalk/bt-list/12-2014-05-09-14-20-41)
[BridgeTalk考](http://detaramedia.blog95.fc2.com/blog-entry-79.html)
[BridgeTalkまとめ…というか覚え書き](http://d.hatena.ne.jp/kamiseto/20090721/1246622746)

ともあれ、この記事では私が使った技術と、詰まったところだけメモ的に残します。

## Photoshopに処理させたいFunctionを呼ぶときはunevalを使う
```ts:open-psd.ts
(function () {
	function openPsd() {
		alert( "alert" );
	}

	const bridgeTalk = new BridgeTalk();
	bridgeTalk.target = 'photoshop';
	bridgeTalk.body = uneval( openPsd + "()" );
	bridgeTalk.send();
})()
```

### 引数を渡したい場合は次のようにする。

```ts:open-psd2.ts
(function () {
	function openPsd( filePath: string ) {
		alert( filePath );
	}

	const bridgeTalk = new BridgeTalk();
	bridgeTalk.target = 'photoshop';
	bridgeTalk.body = uneval( openPsd ) + "(" + uneval( filePath ) + ")";
	bridgeTalk.send();
})()
```
### unevalで呼び出すFunction内にコメントを書くと、エラーになる
```ts:open-psd-error.ts
(function () {
	function openPsd( filePath: string ) {
		// ここにコメントを書くとエラーになる
		alert( filePath );
	}

	// ここにはコメントを書いても問題ない
	const bridgeTalk = new BridgeTalk();
	bridgeTalk.target = 'photoshop';
	bridgeTalk.body = uneval( openPsd ) + "(" + uneval( filePath ) + ")";
	bridgeTalk.send();
})()
```

## Photoshopの処理終了を受け取る

```ts:onResult.ts
(function () {
	function openPsd() {
		alert( "Photoshop" );
	}

	const bridgeTalk = new BridgeTalk();
	bridgeTalk.target = 'photoshop';
	bridgeTalk.body = uneval( openPsd + "()" );
	bridgeTalk.onResult = function () {
		alert( "処理が終了したようです。" );
	};
	bridgeTalk.send();
})()
```

# 今回の要件
* InDesignで開いたドキュメントに含まれる全てのリンクをPhotoshopで開く。
    * Photoshopで開けないデータがリンク先に存在する可能性は考慮しない。<br>（テストで使用していたのはepsファイルとpsdファイル）
* Photoshopで開いたリンクは、全てJPEGで保存する。

# コード
typescriptで書いています。
実装した時は複数ファイルに分けてましたけど、多分以下のコードを1ファイルにまとめても動くのではないかと。（未検証）

あと、もっと綺麗に書けるなぁというところもありますが、~~面倒だったので~~作成時点の当時の実力を忘れないために、そのまま載せます。

## 開き終わったクラス数をカウントするためのクラス

複数リンクを全てPhotoshopで開くが、BridgeTalkは非同期。
そのため、開き終わったリンク先の数をカウントするために使う。

```ts
class Counter {
	private count: number = 0;
	constructor( private maxCount: number ) {
	}
	increment() {
		if ( this.count + 1 < this.maxCount ) {
			this.count = this.count + 1;
		}
	}
	isMax() {
		return this.count === this.maxCount - 1;
	}
}
```

## 外部から呼ばれる窓口
InDesign側で実行される処理。
リンク画像のパスを取得して、そのパスを`bridgeOpenPsd`に渡す。
そのあと`bridgeSavePsd`を呼ぶ。

```ts
export function saveLink(
	outputPath: string
) {
	const doc = app.activeDocument; // 必要に応じてドキュメントを開いたり、アクティブに切り替えたりする
	const graphics = doc.allGraphics;
	const len = graphics.length;
	const counter = new Counter( len );
	for ( let i = 0; i < len; i++ ) {
		bridgeOpenPsd( graphics[ i ].itemLink.filePath, counter );
	}
	bridgeSavePsd( counter, outputPath );
}
```

## Photoshopで指定されたファイルを開くBridgeTalkを起動する
処理が終わったらカウンターをインクリメント。

```ts
function bridgeOpenPsd(
	filePath: string,
	counter: Counter
) {
	const btObj = new BridgeTalk();
	btObj.target = 'photoshop';
	btObj.body = uneval( openPsd ) + "(" + uneval( filePath ) + ")";
	btObj.onResult = function () {
		counter.increment();
	};
	btObj.send();
}
```

## Photoshopで指定されたファイルを開く（Photoshop用スクリプト）
bridgeOpenPsdから呼ばれる。
指定されたファイルを開くだけ。
丁寧に作るなら、ファイルが存在するかどうかとか、拡張子とか判定する。（ここを呼ぶ前にInDesgin側で調べておいても良い）
この中にコメントを書くとエラーになる。

```ts
function openPsd(
	path: string
): void {
	app.open( new File( path ) );
}
```

## Photoshopでファイルを保存するBrideTalkを起動する
タイムアウトの書き方は適当。
Promise使ってやりたいところだけど、処理している間に他の処理を挟むわけではないので妥協。
処理後のalertは処理確認のためなので、本番時は不要。

```ts
function bridgeSavePsd(
	counter: Counter,
	outputPath: string,
	timeout: number = 30
) {
	const dObj = new Date();
	while ( counter.isMax() === false ) {
		if ( (new Date()).getTime() > dObj.getTime() + 1000 * timeout ) {
			throw new Error( "タイムアウトしました。" );
		}
	}

	const btObj = new BridgeTalk();
	btObj.target = 'photoshop';
	btObj.body = uneval( savePsd ) + "(" + uneval( outputPath ) + ")";
	btObj.onResult = function () {
		alert( "complete" );
	};
	btObj.send();
}
```

## Photoshopで開いているファイルを保存する（Photoshop用スクリプト）
bridgeSavePsdから呼ばれる。
JPEG保存時のオプションは適当。
JPEGじゃない形式に書き出したければここで色々と指定する。
保存するJPEGのファイル名は、元ファイル名の拡張子をJPEGに変更したものとなるように修正。
この中にコメントを書くとエラーになる。

```ts
function savePsd( outputPath: string ): void {
	const jpegOptions = new JPEGSaveOptions();
	jpegOptions.embedColorProfile = false;
	jpegOptions.formatOptions = FormatOptions.STANDARDBASELINE;
	jpegOptions.matte = MatteType.NONE;
	jpegOptions.quality = 12;

	const len = app.documents.length;
	for ( let i = 0; i < len; i++ ) {
		const doc = app.documents[ i ];
		app.activeDocument = doc;

		const name = doc.name;
		const dotIndex = name.lastIndexOf( "." );
		const filename = name.substring( 0, dotIndex );
		const saveFile = createFile( outputPath + filename + ".jpg" );
		doc.saveAs( saveFile, jpegOptions, true );
	}
}

```

#　さいごに
この手の記事は、数がとても少ないと感じます。
引っかかるのは10年くらい前のものが多いです。
それだけ使われていない、ニッチな技術なのだと再確認してしまう今日この頃です。