ExtendScriptを使って年号おじさんをたくさん生成する（文字列置き換えと別名保存）

揚げピーナッツというサークルで、[プログラム言語神経衰弱](https://booth.pm/ja/items/1117078)や[ときならべ](https://agepeanuts.booth.pm/items/1321769)といったゲームと作っています。
私はこれらのカードゲームの版下（Illustratorを使用）を作成しています。

版下作成する際に面倒なのが、カードのデザインは同じだけど文言だけが異なるというのが多いことです。
それらを自動化するのにExtendScriptを使っています。

ExtendScriptとは、adobeの作ったECMA Script(ES3)準拠の独自言語です。
IllustratorはこのExtendScriptを使用することで作業の自動化を図ることが出来ます。
今回は簡単な例を通して、ExtendScripの使い方を紹介したいと思います。

## 作例

今回はいらすとやにあるこちらの[元号を掲げる人のイラスト（令和）](https://www.irasutoya.com/2019/04/blog-post_90.html)の令和の文字を別の年号に置き換えて年号おじさんを大量生産しようと思います。

完成イメージは以下のようになります（画像は令和の場合）
その年号開始日を下に記載しています。
![2019-05-04_23h23_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83495/481f27c2-4ccd-ba3a-f97a-a7e46e320362.png)

レイヤーは `Base` と `Design` に分かれており、 `Design` にある `Date` と `Nengo` の文字を置き換えます。
`Date` は日付、 `Nengo` には年号がはいります。
![2019-05-04_23h26_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83495/f0217b72-7783-7e1e-365e-4f02e4d99258.png)

年号は近代のもののみを使います。（csv作るのが大変なので…）
年号に関するデータは事前にcsvでまとめておきます。
`fileName` は別名保存するファイル名、 `name` は年号（漢字）、 `start_at` は年号が開始した日が記載されています。

```nendai.csv
fileName,name,start_at
meiji,明治,1868/01/25
taisho,大正,1912/07/30
showa,昭和,1926/12/25
heisei,平成,1989/01/08
reiwa,令和,2019/05/01
```

フォルダは以下のように配置しています。
`main.jsx` にスクリプトを書いていきます。

![2019-05-04_23h34_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83495/81a7473a-5786-78ee-5f00-cd7149be7cf5.png)


## 開発環境

次に開発環境を整えていきます。
一般的には 「ExtendScript Toolkit」 を入れるのがいいみたいですが、私の場合は ExtendScript Toolkit が CC から見つけられずインストール出来なかったので vscodeで開発しています。
Illustrator でスクリプトを実行するのに [Adobe Script Runner](https://marketplace.visualstudio.com/items?itemName=renderTom.adobe-script-runner) という拡張機能を入れています。
スクリプトが出来たら、vscodeで `ctrl + shift + p` でコマンドパレットを開いて `> Adobe Illustrator` と入力すると開いているイラレの方に切り替わってスクリプトが実行されます。
初めは仕方なく Adobe Script Runner を使っていたのですが、自分の使い慣れてるエディタで開発出来るのはいいですね。

またスクリプトを実行する際は、スクリプトの対象となるイラレファイルを開いてある状態にします。

がっつりスクリプトを書く場合は、ES3だと古くて色々辛いです。
なので、babelを使ったりTypeScriptなどを使って環境を整えることをおすすめします。
私の場合は単純な置き換えにしか使用してないので、直接ExtendScriptを書いています。

## 開発

準備が出来たので、開発をしていきます。

### csvを読み込んでパースする

まずcsvファイルを読み込んで、javascriptで扱いやすいようにします。
今回はシンプルにcsvファイルをテキストファイルとして読み込んで、パースします。

テキストファイルは

```javascript
var file = new File("C:\\<csvまでのパス>\\nendai.csv");
file.encoding = "UTF-8";
file.open("r", "TEXT");
var text = file.read();
```

とすることで読み込めます。
あとは単純に文字列を操作してパースしていきます。
特に面白いことはないので、コードだけ貼っておきます。

```javascript
// 変換した結果を入れる
var csvDataList = [];

// ファイル読み込み
var file = new File("C:\\<csvまでのパス>\\nendai.csv");
file.encoding = "UTF-8";
file.open("r", "TEXT");
var text = file.read();

// 各行ごとの配列に変換
var textRow = text.split("\n");

// 最初の行はカラムとして分離
var columns = textRow.shift().split(",");

// 全行に対してkey valueになるように変換
for (var i = 0; i < textRow.length; i++) {
    var tmp = {};
    var rowItems = textRow[i].split(",");
    for (var j = 0; j < columns.length; j++) {
        tmp[columns[j]] = rowItems[j]
    }
    csvDataList.push(tmp);
}

/* こんな感じになっています
 * csvDataList = [
 *     { "fileName": "meiji", "name": "明治", "start_at": "1868/01/25" },
 *     { "fileName": "taisho", "name": "大正", "start_at": "1912/07/30" },
 *     ...
 * ];
 */
```

### TextFrameを取得する

次にイラレのデータの文字列を置き換える準備を行います。

`Date` と `Nengou` の文字データは `TextFrame` というデータで存在しています。
こいつを編集すると文字を変えることが出来ます。
文字を置き換えるために、対象となる `TextFrame` を取得しようと思います。

`var doc = app.activeDocument;` で現在開いているイラレファイルの　`document` を取得します。
次にレイヤーを取得します。
`Date` と `Nengou` は `Desgin` レイヤーあるので、`doc` から対象のレイヤーを取得します。
レイヤー一覧は `document.layers` に格納されています。
なので

```javascript

// 対象のレイヤーを取得
var targetLayer;
var layers = doc.layers;
for (var i = 0; i < layers.length; i++) {
    if (layers[i].name === "Design") {
        targetLayer = layers[i];
    }
}
```

でレイヤーを探し出します。
レイヤー内の `TextFrame` は `layer.TextFrames` に格納されています。
同様に

```javascript

// 対象のTextFrameを取得
var nengouTextFrame;
var dateTextFrame;
var textFrames = targetLayer.textFrames;
for (var i = 0; i < textFrames.length; i++) {
    if (textFrames[i].name === "Nengou") {
        nengouTextFrame = textFrames[i];
    }
    if (textFrames[i].name === "Date") {
        dateTextFrame = textFrames[i];
    }
}
```

で編集する `TextFrame` を探し出します。

### TextFrameの文字を書き換えるて別名保存する

`TextFrame.contents` を置き換えることで文字を置き換えられます。

```javascript:例

nengouTextFrame.contents = "平成";
dateTextFrame.contents = "1989/01/08";
```

また、別名保存は `doc.saveAs(new File("C:\\<保存先ファイルのパス>"));` で可能です。

これらを組み合わせてcsvファイルのデータに置き換える+別名保存は

```javascript
//// 置き換えて別名保存
for (var i = 0; i < csvDataList.length; i++) {
    var csvData = csvDataList[i];
    nengouTextFrame.contents = csvData.name;
    dateTextFrame.contents = csvData.start_at;
    doc.saveAs(new File("C:<保存先のパス>" + csvData.fileName + ".ai"));
}
```

のようになります。

実際に実行して確認してみると、

![2019-05-05_00h16_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83495/3510d711-7209-7ecd-c92d-633d9e1263a3.png)

のようにイラレファイルが生成されて、

![2019-05-05_00h21_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83495/4f8fc5b5-f7a4-c7a8-5525-136b76396c56.png)

と、見事全年号の差分作成出来ました。

## おわりに

このようにExtendScriptを使うと簡単に文字の置き換えが出来ます。
今回はかなりハードコーディングで作っちゃいましたが（レイヤー名やファイルのパス）、様々なAPIを使用することもっと汎用的に出来ます。
私の場合は年に数回しか使わないのでこれでいいかなと思っていますが、時間があればもう少し踏み込んでやってみたいと思います。

スクリプトはこちらに上がています。（若干内容がことなります）
https://github.com/orange634nty/nengo_ozi
また、ちょっとだけ見通しをよくした版を `kai.jsx` も一応入れておきます。

これからもExtendScriptの記事を色々書いていきたいと思います。