スニペットを使って部品をコマに配置する

イベントのプログラムを掲載したチラシなどで、プログラムの裏面にスポンサー企業の広告をビッシリと並べることがありませんか。このようなチラシの場合、スポンサー企業の入れ替わりが幾度となく繰り返され、そのたびに広告を入れ替える作業でウンザリするのが常です。そんなストレスを払しょくしようと、コマに広告を配置する作業を自動化するスクリプトを作ってみました。
コマの原稿はスニペットで管理します。テキストフレームに下図のようにラベルを付け、Excel(実際にはTSVファイル)を使ってラベルとスニペットの対応を指定します。スクリプトを走らせると、Excelで指定したとおり、各テキストフレームにスニペットを配置するというアルゴリズムです。

【 ラベルとスニペットの対応の指定 】

CM1\t広告1スニペット
CM2\t広告2スニペット
CM3\t広告3スニペット
CM4\t広告4スニペット
CM5\t広告5スニペット
CM6\t広告6スニペット
CM7\t広告7スニペット
CM8\t広告8スニペット

【 ハマリどころ 】
スクリプトを書いてみてわかったのが、スニペットを配置するとき位置の指定ができないという点です。textFrameObj.place()メソッドが配置したオブジェクトを返すので、戻り値のオブジェクトの座標を指定すれば済むだろうと考えていました。ところが、placeメソッドの戻り値はstoryオブジェクトで、座標の指定ができません。何か方法がないかとインターネットを調べあさったところ、下記のページを見つけました。
Placing Snippets Inline/Anchored
かなりトリッキーでオーバーヘッドが大きい方法ですが、スニペットをライブラリに登録してテキストフレームに配置してしまおうというアイデアです。このコードを流用してスクリプトを作ったところ、確かにテキストフレーム内にスニペットを配置できます。実行には予想どおりかなり時間がかかり、1コマ配置するのに3秒です。100コマで5分。まぁ、何とかがまんできるかなぁ。
今回は、(1)スニペットをライブラリに登録、(2)テキストフレームにスニペットを配置、という処理を1コマずつ行っています。パフォーマンスアップするのであれば、ライブラリにスニペットをまとめて登録すればいいと思います。

【 動作確認 】
Windows7 x64
InDesign CS4

/*
 * 外部ファイルの指定に従って、テキストフレームにスニペットを配置する
 * 
 * Rev01 2011.01.09 初版
 *
 */

/*******************************************************************************
 * グローバル変数
 *******************************************************************************/
var docObj = app.activeDocument;
var docFullName;                  // アクティブドキュメントの完全パス
var docFolderPath;                // アクティブドキュメントのフォルダパス
var docFileName;                  // アクティブドキュメントのファイル名
var label2snippet = [];           // ラベルとスニペットファイル名のマッピング
var snippetFolder = "スニペット"; // スニペットを保存したフォルダの名前
var snippetExt = ".idms";         // スニペットファイルの拡張子
var snippetCount;                 // スニペットの数

main();

/*******************************************************************************
 * メインルーチン
 *******************************************************************************/
function main() {
  if (app.documents.length == 0) {
    myError("文書が開かれていません。\n文書を開いてから実行してください。");
  }

  // アクティブドキュメントのパスを取得する
  docFullName = docObj.fullName;
  docFullName = File.decode(docFullName);
  //$.writeln("Active Doc Full Path = " + docFullName);
  var re = new RegExp("(.*)/([^/]*)");
  if (docFullName.match(re)) {
    docFolderPath = RegExp.$1;
    docFileName = RegExp.$2;
    //$.writeln("Folder Path = " + docFolderPath);
    //$.writeln("File Name   = " + docFileName);
  } else {
    myError("ドキュメントのパスの取得に失敗しました。");
  }

  // スニペットの配置情報を書いたTSVファイルを読み込む
  readMappingFile();
  
  // TSVファイルの指定に従ってスニペットを配置する
  placeSnippet();
  
  alert(snippetCount + "個のスニペットを配置しました");
  
  exit();
}


/*******************************************************************************
 * [概要] スニペットの配置情報を書いたTSVファイルを読み込む
 *******************************************************************************/
function readMappingFile() {
  var fileName = File.openDialog("スニペットの配置情報を書いたTSVファイルを選択してください");
  if (fileName) {
    var fileObj = new File(fileName);
    var flag = fileObj.open("r"); // ファイルを読み込みモードで開く
    if (flag == true) {
      while (!fileObj.eof) {
        var oneLine = fileObj.readln();
        //$.writeln(oneLine);
        var tmpArray = oneLine.split("\t");
        //$.writeln(tmpArray[0] + " -> " + tmpArray[1]);
        label2snippet[tmpArray[0]] = tmpArray[1];
      }
    } else {
      myError("ファイル [" + fileName + "] が開けませんでした。");
    }
  } else {
    exit(); // キャンセルボタンがクリックされた場合は、プログラムを終了する
  }
}

/*******************************************************************************
 * [概要] TSVファイルの指定に従ってスニペットを配置する
 *******************************************************************************/
function placeSnippet() {
  snippetCount = 0;
  for (var i = 0; i < docObj.pages.length; i++) {
    var pageObj = docObj.pages[i];
    // 全テキストフレームのループ
    for (var j = 0; j < pageObj.textFrames.length; j++ ) {
      var textFrameObj = pageObj.textFrames[j];
      var label = textFrameObj.contents;
      if (label in label2snippet) {
        textFrameObj.contents = ""; // テキストフレーム内の文字列を消去
        // スニペットファイルのパスを作る
        var snippetFilePath = docFolderPath + "/" + snippetFolder + "/" + label2snippet[label] + snippetExt;
        //$.writeln(label + "->" + snippetFilePath);
        var fileObj = new File(snippetFilePath);        
        placeSnipInline(fileObj, textFrameObj.insertionPoints[0]);
        snippetCount++;
      }
    }
  }
}

/*******************************************************************************
 * [概要] スニペットをテキストフレーム内に配置する
 * 
 * [引数]
 *    mySnipFile = スニペットファイルのハンドル
 *    text = スニペットの挿入ポイント
 *******************************************************************************/
function placeSnipInline( mySnipFile, text ) {
  var myDoc = app.documents.add(false);     // テンポラリ・ドキュメントを作成
  app.scriptPreferences.userInteractionLevel = UserInteractionLevels.neverInteract;
  myDoc.pages[0].place(mySnipFile);         // テンポラリ・ドキュメントにスニペットを配置
  app.scriptPreferences.userInteractionLevel = UserInteractionLevels.interactWithAll;
  myLib = app.libraries.add(File("~/Desktop/templib.indl"));  // テンポラリ・ライブラリを作成
  myLib.store(myDoc.pageItems[0]);          // テンポラリ・ライブラリにスニペットを登録
  myDoc.close(SaveOptions.no);              // テンポラリ・ドキュメントをクローズ
  // 引数で指定された挿入ポイントにテンポラリ・ライブラリに登録されたスニペットを配置
  myLib.assets[0].placeAsset(text);
  myLib.close();                            // テンポラリ・ライブラリを閉じる
  File("~/Desktop/templib.indl").remove();  // テンポラリ・ライブラリのファイルを削除
}

/*******************************************************************************
 * [概要] エラーメッセージを表示して、スクリプトを抜ける
 * 
 * [引数]
 *   msg = エラーメッセージ
 *******************************************************************************/
function myError(msg) { 
  if (arguments.length > 0) { alert(msg); }
  exit();
}

【 免 責 】
上記スクリプトの使用により発生する、データの破損などのあらゆる不具合・不利益については、一切の責任を負いかねますのでご了解ください。