InDesignでスクリプトから挿入した索引マーカーの位置がずれる問題

ご無沙汰しております。

長々と前置きはしないことにして

淡々とInDesignの(たぶん)バグ報告します。
InDesignの索引作成時は見出し語の位置にマーカーを入れていくのですが、これをスクリプトからやろうとすると、ある条件下で挿入位置がずれるというバグがあります。JavaScriptVBScriptで確認。

  • 見出し語と同じストーリー上に表が入っている
  • その表が見出し語より前にある
  • その表が複数行ある(列の数は関係ない模様)

CSまでは起きなかった問題です。CS4では起きるのを確認しました。CS2あたりからおかしくなってきたらしいと聞いたことがありますが、情報元のページが消えててわかんなくなったorz

というわけで実験してみました。InDesign CS5.5、言語はJavaScript、もちろんWindows版です。

見事にずれてます。実験に使ったスクリプトは以下のもの。

var doc = app.activeDocument;
var idx = (doc.indexes.length>0) ? doc.indexes[0] : doc.indexes.add();
idx.topics.add("見出し語","よみがな").pageReferences.add(app.selection[0]);

文書内の「→←」の間にカーソルを立てては実行、を繰り返すこと7回でさっきの画像の状態になりました。順不同にやっても変わらず。
索引を実際に追加してるのは3行目です。topics.add()で項目を追加し、pageReferences.add()で現在選択してる箇所(挿入点、またはテキスト)を参照先として指定しています。
ちなみに、リファレンス見るかぎりCS2〜CS5.5で動くはずです。CS6はまだ触ったことないです。

これのせいで

外部テキストに用意したリストを読み込んで一発で索引登録できるスクリプト*1とか、テキスト選択して実行すると読みがなを自動解析して索引に登録するスクリプト!とか、そういうのが作りづらくなってるんで何とかして欲しいです。
しかし、索引って使ってる人少ないのかなあ……。

*1:これはディザInDesignのお〜まちさんが作ってらっしゃいますね。表さえ入ってなければCS5.5でもばっちり便利に使えてます。

DTPの勉強会に出ます

ぼやぼやしていたら、今年も半分過ぎたどころかもう8月ですって。今年まだ何もしてない気がします……
というわけで9月ごろがんばります。東京のDTPの勉強会 特別編でしゃべることになりました。すでに参加受付開始してます。

DTPの勉強会 特別編・第2回
日時:平成23年9月17日(土)13時30分(13時より受付開始)〜18時(予定)
場所:大橋会館 201教室
[テーマ]
InDesignJavaScriptでコントロールする

なんとあのたけうちとおるさんと一緒ですよ。つまり私はオマケなのですが*1、いまだに実感がわきません。どうすればいいんだー。
詳しい内容は告知ページでご覧ください。
入門ということでシンプルな課題がいくつか出ていますが、これを解けるのが参加の前提条件というわけではありません。当日は解説をしながら作成、そしてその場でさらに改造し、もう少し複雑な処理ができるようになるまでをお話ししていきます。
あとお題がちょっと曖昧なのでそのうちフォロー入れます。

InDesignJavaScriptでコントロールする」

ってタイトルの通り、そしてお題を見てわかる通り、今回のテーマは「スクリプト使うとこんなすげーことできるぜヒャッハー!」というものではありません。むしろ地味です。時間かけてスクリプトをひねり出すよりは手作業でやったほうが早いかもしれない、くらいのことをあえてやろうとしてます。
なぜかというと、「書けるようになる」が目標だからです。
大規模で複雑なワークフローがきっちり出来上がってるのでもない限り、実際の作業で自動化したい処理って意外とシンプルなものじゃないでしょうか。手作業でも多少時間をかければ終わる。でもめんどくさいその時間が惜しい。手作業ではミスが出るかも。そんなときにサクッとスクリプトが書けたら便利、という。一からは難しくても、サンプルやネットで公開されてるスクリプトを読んで少し改造すれば作れるとか。
私は実際そこからはじめて、今は一応業務で使える程度になっています。手作業よりは早く書けるようになってる……かな?
というわけで、いきなり書けるようになるのは難しくても、そのきっかけになれたらいいなと思います。

今回のコンセプトについては、勉強会主催者のあかつきさんが詳しい記事を書かれていますので、そちらもどうぞ。

実を言えば

私がふだん書いてるスクリプトってわりと偏ってまして、ほとんどがテキスト周りの処理です。これは業務がそっち側に偏っているからなんですが、問題は今回の課題がオブジェクトの位置とかから始まっていることで……自分で課題を解くために、しょっぱなからぐぐったりサンプルコード見たりしております。
こんなのがしゃべっていいのかと若干不安になりつつ、がんばりますのでお手柔らかに。

*1:前にWindowsネタでしゃべったときもオマケでしたね

ライブラリのアイテムを自動で配置する(InDesign CS3〜)

とってもお久しぶりです。最近アウトプット減ってて*1インプットも滞っています。
スクリプトもまとまったものはあまり書いてないんですが、久しぶりに汎用っぽいのができたのでおすそわけ。すでに書いてる人がいたらすいません……

ライブラリの各アイテムを自動でまとめて配置する

たとえばコンピュータ関連の解説書とかで「Ctrl+Pを押します」なんて書いたりして、それぞれのキーを絵で表現したりするようなとき。

フォントを作っちゃうのも手なんですが、そこまでする数じゃないような場合はライブラリを使うと便利。

ただ、ひとつずつD&Dして配置するのはめんどくさいです。私なら3つくらいやったとこでイラッと来ます。
というわけでこんなのを書きました。テストはWinXP SP3、InDesign CS4でのみ行っています。

↓↓↓

「#(半角英数のアイテム名)」という文字列が選択範囲内に見つかったら、指定したライブラリ内の同名アイテムで置換します。何も選択していない場合はドキュメント全体を処理します。
配置したオブジェクトはインラインオブジェクトになります。インラインオブジェクトの各種テキストスタイル属性は置換元の最後の文字(「#name」なら「e」)と同じになります。

// 選択部分が対象
var target = app.selection;
// 何か選択されていたらその中だけ。選択してなければドキュメント全体が対象
target = (target.length > 0) ? target[0] : app.activeDocument;

// ライブラリから読み込み。ファイル名を指定。InDesign上で開いてないとだめです
var lib = app.libraries.itemByName("keylib.indl"); // ★

// 検索条件(「#半角英数」の最長一致)
app.findGrepPreferences = NothingEnum.nothing;
app.findGrepPreferences.findWhat = "#[0-9A-z]+";

// 検索実行
var f = target.findGrep();

// 検索で引っかかった部分を後ろから順番に処理
// 置換によって文字数が変わる処理なので前からやると泣きを見ます
for(var i = f.length-1; i > -1; i--){
  var name = f[i].contents.substr(1); // 検索された文字列をアイテム名として扱う。substrは「#」を取り除く処理
  var asset = lib.assets.itemByName(name); // 名前からライブラリのアイテムを特定

  try {
    asset.placeAsset(f[i].insertionPoints[-1]); // 配置(検索された文字列の最後にくっつける)
    f[i].characters.itemByRange(0, -1).remove(); // 検索された文字列を削除(最後にくっつけたアイテムは残す)
  } catch(e){
    // エラーが起きたらスルー
  }
}

注意点は画像に書いたとおり。正規表現検索の使えるCS3以上が対象、アイテム名に半角英数以外は(記号も)使えません。
使うときは★をつけた行でライブラリ名(ファイル名)を指定してください。

場合によるとは思うけど

実際使うときは、元の「#アイテム名」のテキストに文字スタイルをつけておいて、その文字スタイル限定で検索するようにしたほうが安全じゃないかと思います。
検索条件のとこに

app.findGrepPreferences.appliedCharacterStyle = "ほげほげ文字スタイル名";

とか追加したり。処理後のインラインオブジェクトに文字スタイルが残るので、あとでまとめて検索もしやすいです。

以下、蛇足

せっかくなので勉強がてらちょっぴり修正してみます。

名前を発見するたびにいちいちライブラリを見に行くのは効率悪い気がしたので、最初にアイテムをリストアップしてアイテム名をキーとするオブジェクトに格納してしまうことにしました。
ついでに、いちいち選択解除するのがめんどくさいので、テキストカーソルを立ててるだけの状態のときはドキュメント全体を処理するようにします。あとコメント消したり無名関数でくるんでしまったりその他もろもろまとめ書きして……

(function(){
  var libname = "keylib.indl";

  var target = (app.selection.length > 0) ? app.selection[0] : app.activeDocument;
  target = (target.constructor.name == "InsertionPoint") ? app.activeDocument : target;

  var assets = {};
  var lib = app.libraries.itemByName(libname);
  for(var i = 0, alen = lib.assets.length; i< alen; i++){
    var tmp_a = lib.assets[i];
    assets[tmp_a.name] = tmp_a;
  }

  app.findGrepPreferences = NothingEnum.nothing;
  app.findGrepPreferences.findWhat = "#[0-9A-z]+";

  var f = target.findGrep();
  for(var i = f.length-1; i > -1; i--){
    var tmp_f = f[i];
    try {
      assets[tmp_f.contents.substr(1)].placeAsset(tmp_f.insertionPoints[-1]);
      tmp_f.characters.itemByRange(0, -1).remove();
    } catch(e){}
  }
})();

こんな感じ。

で、よーしと思って大量に配置するサンプルを用意して処理速度を計ってみたら……ほとんど変わんなかったorz
ネックはどこにあるのかな*2。ドキュメントを描画なしで開いて処理したら速そうな予感はあるけど、心折れたので試してません。

さらに蛇足(未解決)

最初に書いたとき、置換処理は次のように書いてました。

// ~~~~~~~~~~
  var asset = lib.assets.itemByName(f[i].contents.substr(1)); // fはfindGrep()の戻り値
  try {
    asset.placeAsset(f[i].texts[0]);
  } catch(e){}
// ~~~~~~~~~~

検索で引っかかったテキスト部分をplaceAssetで上書きしてしまえばいいと思ったのです。
実際それでもうまくいっていたんですが、「#name#name」など検索対象が連続しているときに不具合が出ました(前のほうの#name、つまりあとに処理されるほうが置換されない)。
いろいろ調べた結果、二回目の(つまり前のほうの)f[i].contentsに直後のインラインオブジェクトまで含まれてしまうからということがわかりました。それで「『name(オブジェクト)』なんて名前のアイテムはない」と処理がスキップされたようです。
f[i].toSpecifier()すると範囲は5文字ぶんのはずなのに、f[i].lengthをとると6が出て、f[i].contentsを表示してみると最後にオブジェクトが入っているという謎現象。私の理解がへんなのかなあ?
……とりあえず回避するため、textを扱うのは危険ということで文字単位で追加、文字単位で削除という形にしたのでした。
うーん?

*1:ついったー? あれはアウトプットとは言えん、少なくとも私の場合は……

*2:無名関数とかやめたり、思いつく限りいろいろ削ってみたけど大差なしだった

InDesignで配置実行前にデフォルトの文字スタイルを[なし]にする(CS3〜)

お久しぶりですこんばんは。主にやる気の問題でご無沙汰しておりました。

InDesignのタグ付きテキスト配置にありがちなこと

タグ付きテキストを[配置]で流し込むとき、タグで指定していないはずのスタイルが適用されてしまうことがあります。デフォルト状態(アプリケーション上で何も選択していない状態)のときにパネルで選んでおいたスタイルが、タグでスタイルを明示していない部分のテキストに適用されるためです。
うまく使えば便利なときもあるかもしれませんが、うっかりやらかしたときはがっくり来ますorz
……ありがちとか言ったけど、私だけだったらどうしよう。まあいい。

うっかり防止隊をつくろう

というわけで、やらかしそうになったときにInDesignが防止してくれるスクリプトを作りました。
具体的には、[配置]を(メニューからでも、ショートカットからでも)実行するときに、デフォルトの文字スタイルを[なし]に設定してくれるというものです*1 *2
メニューやイベントを使っているため、InDesign CS3以降でしか動きません。いつものことですが、動作確認はWindows XP(SP3)、InDesign CS4でのみ行っています。

// 配置前にデフォルト文字スタイルを[なし]にする

#targetengine "noDefaultCharaStyle"

(function(){

  // [配置...]メニューのbeforeInvokeイベントが起きたときに実行する処理を設定
  app.menuActions.item("$ID/Place...").addEventListener("beforeInvoke", function(event){

    // ドキュメントはイベント発生時に都度確認する
    var actDoc = app.activeDocument;

    // すでにデフォルト文字スタイルが[なし]のときは何もせず終了
    if( actDoc.textDefaults.appliedCharacterStyle.index == 0 ){ return; }

    // デフォルト文字スタイルを[なし]に変更
    actDoc.textDefaults.appliedCharacterStyle = actDoc.characterStyles[0];

  }); // イベントハンドラ設定ここまで

})();

だいたいコメントの通りですが、[配置]が選択されて実行される直前(beforeInvoke)に毎回デフォルトの文字スタイルを確認し、[なし]になっていなかったら変更する処理を行うよう設定します。
このスクリプトを実行しただけでは何も起きません(起きたように見えません)が、InDesign側ではうっかり防止隊が結成されています。Scriptsフォルダの下のStartup Scriptsフォルダ(なかったら自分で作る必要あり)に入れておいて、InDesignの起動時に毎回実行されるようにしておくと便利かもしれません。
そうそう、このスクリプトExtendScript Toolkitから実行すると動きませんので、使うときはファイルに保存してスクリプトパネル上から実行する必要があります*3

うっかり防止隊はInDesignが終了した時に解散になります。InDesignを終了させずにこの設定を解除したいときは、

// 配置実行前の処理を消去する

#targetengine "noDefaultCharaStyle"

(function(){
    var listeners = app.menuActions.item("$ID/Place...").eventListeners;
    for( var i = listeners.length -1; i > -1; i-- ){
        if( listeners[i].eventType == "beforeInvoke" ){ listeners[i].remove(); }
    }
})();

これを実行すればOKです*4

うっかり防止隊をちょっと親切にしてみる

最初のスクリプトだと、文字スタイルで[なし]以外を選んだまま配置したいときには一度設定を解除しなければなりません。それはそれで、逆うっかりをやらかす可能性があります。
なので少し改良して、配置するときに文字スタイルを[なし]にするかどうか訪ねるダイアログが出せるようにしてみました。

// 配置前にデフォルト文字スタイルを[なし]にする

#targetengine "noDefaultCharaStyle"

(function(){

// ★変更前に毎回確認のダイアログを出すかどうか設定する
// (true:確認する false:確認しない)
// ----------------------------------

  var askBefore = true;

// ----------------------------------


  // [配置...]メニューのbeforeInvokeイベントが起きたときに実行する処理を設定
  app.menuActions.item("$ID/Place...").addEventListener("beforeInvoke", function(event){

    // ドキュメントはイベント発生時に都度確認する
    var actDoc = app.activeDocument;

    // すでにデフォルト文字スタイルが[なし]のときは何もせず終了
    if( actDoc.textDefaults.appliedCharacterStyle.index == 0 ){ return; }

    // 毎回確認する設定(askBeforeがtrue)ならダイアログを出す
    if( askBefore ){
      var dlg = new Window("dialog", "うっかりしてませんか!");
      dlg.add("statictext", undefined, "デフォルト文字スタイルを[なし]にしますか?");
      dlg.bGroup = dlg.add("group");
      dlg.bGroup.orientation = "row";
      dlg.bGroup.add("button", undefined, "はい", {name:"OK"});
      dlg.bGroup.add("button", undefined, "いいえ", {name:"Cancel"});
      if( dlg.show() != 1 ){ return; } // 「はい」ボタン以外なら何もせず終了
    }

    // デフォルト文字スタイルを[なし]に変更
    actDoc.textDefaults.appliedCharacterStyle = actDoc.characterStyles[0];

  }); // イベントハンドラ設定ここまで

})();

★のところの変数askBeforeをtrueにしておくと、配置ダイアログが出る前に文字スタイルを変更するかどうかのダイアログが出るようになります。こんなの↓

falseにすれば確認なしで変更します。
もちろん、確認ダイアログが出るのは[なし]以外の文字スタイルが選択されているときだけです。うっかり防止を名乗るならこうでなくてはね!
なお、これを解除したいときにもさっきの削除用スクリプトが使えます。

これさえあれば

タグ付きテキストの配置で延々待たされたあげくに「見つからない字形の保護」とか言われて orz ってなることもなくなるはずだっ!

*1:配置するのがテキストかどうかは判定しない(できない)ので、画像を配置したいときにも処理が行われます

*2:段落スタイルをタグで明示しないことはあんまりないと思うので、今回は無視しています

*3:tergetengineの指定が必要なだけなので、正確に言えば初回だけファイルから起動すればInDesignの再起動まではESTK上からでも動きますが……ていうか名前がてきとうすぎる

*4:ただしこれだと[配置]のbeforeInvokeイベントハンドラが全部消えます、手抜きです

DTP Booster 013の予習:スクリプト作成過程を実況してみた

注:この記事の内容は、アップの数日前(DTP Booster 013受講前)に書かれたものです。アップするかどうか受講後に悩みましたが、結局貧乏性(モッタイナイ)に負けて載せてしまうことにしました。


参加予定のDTP Booster 013(Omotesando/100602)スクリプトがテーマ。
事前に講師のうちのお一人であるたけうちとおる氏がブログでお題を出されていたので、せっかくだから先に挑戦してみることにしました。
いい機会なので、以前からやってみたいと思っていた「スクリプト作成過程の公開」をしてみます。なんのために? もちろん自分のために。アウトプットは人のためならず。
セミナー前に書いておいて、セミナー後に無編集でアップする予定です。調べたり考えたり失敗したり、とにかく全部書きとめていくので凄まじく長いです。結果だけ見たいという場合はここから飛べます。

JavaScriptで、InDesign CS4で動くことを目指して作成します。動作確認はWindows XPInDesign 6.0.5のみで行います。

続きを読む

DTP Booster 013に行ってきた

Adobe CS5の発売にあわせて表参道で行われているイベント群、その中で3日連続で開催されたDTP Boosterの最終日のセミナーを受講してきました。
テーマは「スクリプト(入門)」。
私もなんとかスクリプトを自作するところまでは来ているので、自分のためというよりは「スクリプトの有用性をどう伝えるか」っていうところを知りたくて参加しました。……こう書くと偉そうだな! 自分のためでもありましたよもちろん!
セミナーの詳しい内容については、リアルタイムで配信されていたUstreamの録画映像がありますのでこちらで。いい時代ですなあ*1

IllustratorJavaScriptでつくるおしゃれアートワーク(秋葉 秀樹氏)

タイトルはIllustratorですが、まずはCS5からInDesignにも対応するようになったAdobe Configuratorの紹介から入っていました。以前東京DTPの勉強会第0回で樋口 泰行氏が紹介されていたものです。2.0にバージョンアップして、メニュー名などが日本語になったそうです。
Adobe Configuratorの何がいいって、とにかく「作りやすい」ことだと思います。いつも使っている機能をポイポイとドラッグ&ドロップしてボタンの形で配置すれば、それだけで便利なパネルが作成できてしまう。「アプリケーションをデフォルトのまま使うのではなく、自分用にカスタマイズする」という行為のとっかかりとしては最適じゃないでしょうか。更にスクリプトFlashの知識があればもっと便利な物が作れるわけだし。
問題は今のところ最新のCS5でもPhotoshopInDesignのみ対応ということですね。むしろこれIllustrator向きの機能じゃないかと思ったりするのですが、予定は未定だそうです。

そして予告されていたIllustratorでのランダムちりばめ系スクリプトの作成について。実際にコードを書きながら、実行しながらの解説でした。
ランダムな数を返す関数を自分で作ってそれを利用したりとか、イチから覚えようとしたらそこそこ難しいところじゃないかと思うんですが、そこをさらりと流して難しさを感じさせず、「何が出来るか、何が起こるか」を見せてくれていたと思います。
セッションの最後で実演された、たくさんの種類のシンボルをちりばめて「おもちゃ箱をひっくりかえしたような」オブジェクト群を作るところでは、(私も含めて)会場のあちこちから歓声が上がりました。あの技はそのままデザインにも使えそうです、というか明日さっそく使う気満々です。ちょうど使えそうな仕事があるんだこれが。ラッキー(・∀・)

とにかく進行のスムーズさが印象的でした。あれだけよどみなく説明しながらコード書けるとか凄い。詳しく説明しないことでわかりやすくなるってこともあるんだなあと非常に勉強になりました。真似は出来そうにないけど!

DTP作業を楽にするスクリプト入門(たけうちとおる氏)

まず怒濤のスクリプト紹介から始まりました。InDesignIllustratorを対象に、テキスト処理系、画像配置系、パス描画系、面付け系と多岐にわたるスクリプト群を次々に実演。
いろんなことできるよなあ……と感心しつつ、私が気になったのはInDesignスクリプトパネルにフォルダに分けて収められた大量のミニスクリプトでした。たとえば(実演にも含まれていましたが)表組の線幅を決められた値に変更するためだけのものとか。確かに実際自分で使うときは、GUIでいちいち数値を設定して実行ボタンを押すよりも、数値などの設定を決め打ちにした小さなスクリプトを必要に応じて選んで実行するほうが手間が少なかったりします*2。単機能のものなら作るのも楽だし。

その後は、楽しみにしていたスクリプト作成の実演。事前に出ていたお題*3「PDF書き出しスクリプト」を試行錯誤しながら作るという内容です。
要件を整理し、わかるところから書き始め、わからないところはオブジェクトモデルビューアを使用したりネットで検索したりしながら書いていく。ある程度まとまったところまで書いたら実行してみて、エラーが出たらその内容を見て修正、さらに書き進める、という、まさに試行錯誤の過程をそのまま見せてくださいました。
実は、講義を聴く前に私もこのお題に挑戦していました。エラーばかり出て思った以上に苦労したのですが……今日の講義を聴いてみてびっくり。私がわからなくて調べた箇所、書く順番、やらかしたエラー、それらがほとんど同じだったのです。ちょっと嬉しかったですね。
自動処理系の本に書いてあるスクリプトやネットで検索して見つかるスクリプトは、たいていの場合すでに整理されてそのままコピーすれば使えるようになっています。そのおかげで便利に使えるわけですが、真似して書いてみよう! と思う学習者からすると、模範解答があまりに完成されている場合「これ、どっからどう書いたらいいんだろう。そもそもどこから読めば……」と思ってしまったりすることがあります。その意味で今回のセッションはとても参考になる、そしてとても勇気づけられるものでした。失敗しながらでいいんだ!

ついでに、せっかくだから

次の記事で、たけうちさんの出された「お題」に対する私の回答を発表しようと思います。それもただ書くだけではもったいないと思い、作成過程で何を考え何をやらかしたかすべて書き留めておいたものを、そのまま。いわば実況形式です。
後出しジャンケンのようですが、実際書いた時のまま無編集で載せますのでOKということにしてください。
……しかし、書いてたときは意図も想像もしてなかったことだけど、セッションの内容とダダ被りなんだよなあ。あんまり意味がないかもー(´・ω・`)

*1:Ustでの公開と参加費負担についてはなかなか難しい問題なのでしょうが……

*2:ただ、Illustratorの場合はスクリプトパネルが用意されてないので……

*3:私は勝手にInDesignだと思いこんでいて実際その通りだったのですが、実は対象アプリケーションについては明記されていなかったらしいです