内部スクリプトと外部スクリプト

Adobe Illustrator の自動処理、「内部で JavaScript を使ってマクロ書くのに限界を感じたので、外で別のスクリプト言語を考えている。forループとかネストしたらつらいんだ(勝手な要約)」とおっしゃる方がいらしたので少し検討してみる。
結論としては、速さを期待しているなら話しにならない。中で実行したら一瞬なのに、外で実行したら十数秒とか、そんな感じ。
数値計算とかファイルIOとか、或いは言語自体の趣味とか、別の要素で判断しないと特にメリットは無いでしょう。

中で実行とか、外で実行とかその辺の言葉遣いの意味合いは追い追い実地に示します。

環境 (Windows - Illustrator 10)

Ruby (外部スクリプト)

require 'win32ole'                                                  # (A)
illustrator = WIN32OLE.connect 'Illustrator.Application'            # (B)
document = illustrator.Documents.Add                                # (C)
10.times do |i|
  10.times do |j|                                                   # (D)
    path = document.PathItems.Add
    path.Name = "#{i}-#{j}"                                         # (E)
    
    first_point = path.PathPoints.Add
    first_point.Anchor = i*30+100, j*20+300                         # (F)
    first_point.LeftDirection = first_point.Anchor
    first_point.RightDirection = first_point.Anchor                 # (G)
    
    last_point = path.PathPoints.Add                                # (H)
    last_point.Anchor = i*30+200, j*20+320
    last_point.LeftDirection = last_point.Anchor
    last_point.RightDirection = last_point.Anchor
  end # 10.times do |j|
end # 10.times do |i|
WIN32OLE.new('WScript.Shell').Popup('おしまい', 0, "nested loop", 0+64+262144)
                                                                    # (I)

取敢えず繰り返しをネストして 100本線を引く。最後の「おしまい」ポップアップまで、まあ十秒以上か、一本ずつ線を挽いて行くのが眼で追えます、そんな具合。そして例画像。
適当なファイル名で保存しておく。そして、コマンドプロンプトからファイル名で起動しても、ActiveScriptRuby などで拡張子の対応付けが済んでいるならファイルアイコンから適当に起動してもいい。

プログラムの説明をしてみる。
(A) まずは WIN32OLE、これを使うことになります。
(B) そして Illustratorアプリケーションオブジェクトを作成、Adobe Illustrator は既に起動しておいて、connectしに行った方が良いです。必ずそうしましょう。
ええと、'Illustrator.Application' というキーワードはどうやって知ったんだったかな。OLEの使い方とか解説とか見てて大体そんなもんだろうと思ってやってみたら出来たとか。「ruby -rwin32ole -e 'puts WIN32OLE_TYPE.progids'」すれば一杯出てくるからそこから中りつけるか、沢山過ぎて大変だな。或いは ASR なら付属の Simple OLE Browser (Win32OLE View)で調べたら判るんだっけ、いや、それは出てこないや。
WIN32OLEの参考としては、「るびま」の連載、「るびま」以下7回が参考になります、「るびま」はまさに Illustrator の話しです(自分が書いた)。
この連載は紙の書籍にもなってます(Illustratorのところは除く)。「Ruby on Windows -- Rubyist Magazine 出張版」Rubyist Magazine出張版 Ruby on Windows ISBN 978-4-8399-2668-7
あと、この辺「プログラミング言語 Ruby リファレンスマニュアル」とか。
(C) イラストレーターの Documentsコレクションの Addメソッドで新規ドキュメント作成。
(D) Ruby で回数の定まった繰り返しは Integer#times、ネストさせる。
(E) ドキュメントの PathItemsコレクションの Addメソッドで新規パス(まあ、線の汎型)を作成して、適当に名前を付ける。
(F) パスの始点(端点)を定義して、その Anchor配列に座標を代入。Ruby の多重代入注意。
(G) 端点座標の指定だけだとベジエ曲線のコントロールポイントがどっか言ってしまって面白い曲がり具合になりかねないので、左右のコントロールポイントの座標もアンカーの座標に合わせる。
(H) 以下、パスの終点(端点)についても同様に。
(I) そして全部終わった合図にポップアップメッセージ。ポップアップの設定詳細については述べないが、最後の引数について。

  • 0: MB_OK, MB_DEFBUTTON1、OK釦一つでそれが既定の選択
  • 64: MB_ICONINFORMATION、アイコンの種類 白い吹出しの中に青く i
  • 262144: 0x40000, MB_TOPMOST、最前面指定

あと、MSDN の WshShellオブジェクトの「Microsoft API and Reference Catalog」辺りとか。

VBScript (内部外部両方)

Dim illustrator, document, path, firstPoint, lastPoint      ' (A)
Set illustrator = CreateObject("Illustrator.Application")   ' (B)
Set document = illustrator.documents.add                    ' (C)
For i = 0 To 9
  For j = 0 To 9                                            ' (D)
    Set path = document.pathItems.add                       ' (E)
    path.name = i & "-" & j

    Set firstPoint = path.pathPoints.add()
    firstPoint.anchor = Array(i*30+100, j*20+300)           ' (F)
    firstPoint.leftDirection = firstPoint.anchor
    firstPoint.rightDirection = firstPoint.anchor
    
    Set lastPoint = path.pathPoints.add()
    lastPoint.anchor = Array(i*30+200, j*20+320)
    lastPoint.leftDirection = lastPoint.anchor
    lastPoint.rightDirection = lastPoint.anchor
  Next 'For j = 0 To 9
Next 'For i = 0 To 9
Dim wsh
Set wsh = CreateObject("WScript.Shell")
wsh.Popup "おしまい", 0, "nested loop", 0+64+262144         ' (G)

適当なファイル名で保存しておく。
やることはさっきの Rubyスクリプトと一緒。この VBScript はこのまま同じファイルで Illustrator の外部からと内部でと実行可能。
外部からの実行というのは、コマンドプロンプトからファイル名で起動したり、ファイルアイコンからの起動という類のこととしておく。Rubyスクリプトと同様にまあ十秒以上という感じ、線を挽いてくのが眼で追えます。
一方、Adobe Illustrator自体から、「ファイルメニュー - スクリプト - 参照」ダイアローグで同じファイルを探して開いても良い。この記事で内部から実行すると称してる実行方法です。まあ、一瞬、一拍おくかおかないかでおしまいポップアップ。ポップアップが出たままだと描画が確認できないかもしれないけど、なんならポップアップ出さない様にコメントアウトしてみれば、やっぱり一瞬。

プログラムの説明をしてみる。
(A) 変数の宣言をしておく、前述内部からの実行の場合はなくてもなんとかなる。
(B) VBScript では CreateObject() でアプリケーションオブジェクトを作成する、その為に特に何かライブラリ等を用意する必要はないです。また、Adobe Illustrator は既に起動しておいた方が良い、必ずそうしましょう。
MSDNの「Microsoft API and Reference Catalog
(C) そして新規ドキュメント作成。
(D) 繰り返しの構文は For です。やっぱ .times のがいいよう。
(E) 実際に線を挽くやり方は Rubyの場合と大体同じ。
(F) anchor 配列への座標の代入の際には、Array() を使って配列を作成してそれを代入。
(G) そしておしまいポップアップ、最初にやっておかなかったので変数宣言から。

JavaScript (内部スクリプト)

doc = documents.add();                                  // (A)
for (i = 0; 10 > i; i++) {
  for (j = 0; 10 > j; j++) {                            // (B)
    p = doc.pathItems.add();                            // (C)
    p.name = i+'-'+j

    first_point = p.pathPoints.add();
    first_point.anchor = [i*30+100, j*20+300];          // (D)
    first_point.leftDirection = first_point.anchor;
    first_point.rightDirection = first_point.anchor;
    
    last_point = p.pathPoints.add();
    last_point.anchor = [i*30+200, j*20+320];
    last_point.leftDirection = last_point.anchor;
    last_point.rightDirection = last_point.anchor;
  } // for (j = 0; 10 > j; j++)
} // for (i = 0; 10 > i; i++)
alert('おしまい');                                      // (E)

適当なファイル名で保存しておく。
やることは今までのスクリプトと一緒、内部から実行する、Adobe Illustrator自体から、「ファイルメニュー - スクリプト - 参照」ダイアローグで保存したファイルを探して「開く」
まあ、一瞬、一拍おくかおかないかでおしまいポップアップ。ポップアップが出たままだと描画が確認できないかもしれないけど、なんならポップアップ出さない様にコメントアウトしてみれば、やっぱり一瞬。

プログラムの説明をしてみる。
(A) 新規ドキュメント作成。Illustrator の中だし、documentsコレクションは既にある。
(B) 繰り返しの構文は for です。VBScript の For より大変な気がする、やっぱ times のがいいよう
(C) 実際に線を挽くやり方は今までの場合と大体同じ。
(D) anchor 配列への座標の代入の際には、[] を使って配列を作成してそれを代入。
(E) そしておしまいポップアップ(アラート)、IllustratorJavaScript付属の alert() メソッドを使います、アイコンとか微調整はなし。(CS以降ではなんとかなるのかな、知らないです)
逆に、(new ActiveXObject('WScript.Shell')).popup('おしまい') とかはなし、Illustrator内から、OS側(と言うか WSH)の ActiveXObject() は触われません。(CS以降ではなんとかなるのかな、知らないです)

JavaScript (外部スクリプト不調)

JavaScript で外部からの実行は不調です、線は挽けるんですが端点の座標が指定できません、どうするのが良いのでしょう。

var illustrator = new ActiveXObject('Illustrator.Application');   // (A)
var document = illustrator.documents.add();                       // (B)
for (i = 0; 10 > i; i++) {
  for (j = 0; 10 > j; j++) {                                      // (C)
    var p = document.pathItems.add();                             // (D)
    p.name = i+'-'+j;
//    p.setEntirePath([[(i*30+100), (j*20+300)],  [i*30+200, j*20+320]])
                                                                  // the argument is not valid 80070057
//    p.setEntirePath( Array( Array( (i*30+100), (j*20+300) ),  Array( i*30+200, j*20+320 ) ) )
                                                                  // the argument is not valid 80070057

    var firstPoint = p.pathPoints.add();
    firstPoint.anchor[0] = (i*30+100);
//    firstPoint.anchor['0'] = (i*30+100);
    firstPoint.anchor[1] = (j*20+300);                            // (E)

//    firstPoint.anchor = Array((i*30+100), (j*20+300));          // an unknown error occurred 80044205
//    firstPoint.anchor = new Array((i*30+100), (j*20+300));      // an unknown error occurred 80044205
//    firstPoint.anchor = (new Array((i*30+100), (j*20+300)));    // an unknown error occurred 80044205
//    firstPoint.anchor = [(i*30+100), (j*20+300)];               // an unknown error occurred 80044205
//    firstPoint.anchor = {0:(i*30+100), 1:(j*20+300), length:2}; // an unknown error occurred 80044205
    var a = new Array((i*30+100), (j*20+300));
//    firstPoint.anchor = a;                                      // an unknown error occurred 80044205
    a = [(i*30+100), (j*20+300)];
//    firstPoint.anchor = a;                                      // an unknown error occurred 80044205
    a = {0:(i*30+100), 1:(j*20+300), length:2};
//    firstPoint.anchor = a;                                      // an unknown error occurred 80044205

    firstPoint.leftDirection = firstPoint.anchor;
    firstPoint.rightDirection = firstPoint.anchor;

    lastPoint = p.pathPoints.add();
    lastPoint.anchor[0] = (i*30+200);
    lastPoint.anchor[1] = (j*20+320);
    lastPoint.leftDirection = lastPoint.anchor;
    lastPoint.rightDirection = lastPoint.anchor;
  } // for (j = 0; 10 > j; j++)
} // for (i = 0; 10 > i; i++)
(new ActiveXObject('WScript.Shell')).popup('おしまい', 0, "nested loop", 0+64+262144);

適当なファイル名で保存してコマンドプロンプトからファイル名で起動したり、ファイルアイコンからの起動で、まあ十数秒以上。
パスの端点の座標が設定できず、新規ドキュメントのアートボード左下隅に短い線が百本重なって出来ます。

プログラムの説明をしてみる。
(A) ActiveXObject() でアプリケーションオブジェクトを作成する、その為に特に何かライブラリ等を用意する必要はないです。また、Adobe Illustrator は既に起動しておいた方が良い、必ずそうしましょう。
MSDNの「Microsoft API and Reference Catalog
(B) そして新規ドキュメント作成。
(C) 繰り返しの構文は for
(D) 実際に線を挽くやり方は今までの場合と大体同じ筈なんだが。
(E) anchor配列の各要素に値を代入しても座標に反映しない。
(各種エラー) コメントアウトしてますが、setEntirePath() の使用を含めやみくもに配列の代入を試みました、駄目だった。どうするのが良いのでしょう。

感想

というわけで、幾つかやってみました。JavaScript で外部から実行したとき、配列の代入が出来ないのはつらい、どうするのが良いのでしょう。
それは兎も角、外部からの実行と内部での実行で、速度の面で全然話しになりません、内部からの実行が良いです一瞬です。
あとは、Popupなんかが例になるけど Illustrator以外の処理、それから Illustrator 付属の JavaScript ではファイルIO が辛かった様な気がするのでその方面とか、そして言語自体の好みとか、そういうのがないと外部からの実行は有利にならないでしょう。
要は好き好きという事で。(まあ、ファイルIOの為に外部からスクリプトを使うとかなら少しは意味があるかも)