InDesignのスウォッチ色設定切り替えスクリプトをウィンドウにしてみる

こないだのスクリプトをちょっと改造してみました。
実行のたびにダイアログを生成するのではなく、一度実行したら(閉じない限り)InDesignを終了させるまでずっと表示されたままになるウィンドウを作ります。その他、ちらほら修正など。
例によって動作確認したのはWindows XP SP3 InDesign CS4(6.0.4)のみです。

#target "InDesign"
#targetengine "changeSwatchColor"

(function(){

  if ( app.documents.length == 0 ) { alert("ドキュメントが開かれていません"); return; }

  // スウォッチの設定セットをつくる
  var presetColorArr = [
    { name  : "見出し1用",
      cmyk  : [  0,100,100,  0],
      black : [  0,  0,  0,100],
      set3  : [ 20, 20, 20,100] },

    { name  : "見出し2用",
      cmyk  : [  0,  0,100,  0],
      black : [  0,  0,  0, 50],
      set3  : [ 10, 10, 10, 40] }

    // 長いので省略
  ];

  // ダイアログ(ウィンドウだけど)の内容を作成
  var dlg = new Window("window", "スウォッチ色変更");
  dlg.add("statictext", undefined, "どの設定に変更しますか?");
  dlg.add("button", undefined, "カラー").onClick = function(){ changeColor("cmyk"); }
  dlg.add("button", undefined, "モノクロ").onClick = function(){ changeColor("black"); }
  dlg.add("button", undefined, "設定3").onClick = function(){ changeColor("set3"); }

  // ダイアログを表示
  dlg.show();

  // 各スウォッチの色設定をモードに応じて変更する処理
  // ボタンを押すと、この関数が実行される
  var changeColor = function(mode){
    var actDoc = app.activeDocument; // ボタンを押した時点のアクティブドキュメントを処理するのでココ
    for ( var i = 0, len = presetColorArr.length; i < len; i++ ) {
      var tmpSet = presetColorArr[i];
      try {
        var targetColor = actDoc.swatches.item(tmpSet.name);
        targetColor.colorValue = tmpSet[mode];
      } catch(e) {
        alert("スウォッチがみつかりません: " + tmpSet.name);
      }
    }
  }

})();

私が使うときには複数のファイルに同じ設定をどんどん適用していくことになるので、いちいちダイアログを呼び出すのは面倒だなーって思ってこの形になりました*1
あと、せっかく覚えたのでウィンドウを作ってみたかった。これだったら、最初の起動時にスウォッチの設定セットを書いた外部ファイルを読み込むようにするのもありかもしれない。

ウィンドウの使用にはtargetengineの指定が必要です。最初の文はそれ。このスクリプトをESTKで実行するにはちょっと注意が必要ですね。
参考:Double-talk Publisher お手元系スクリプト考

ところで、applicationオブジェクトからたどって作るダイアログは使い終わったら破棄(destroy)すべしと言われてるけど、ScriptUIで作る場合はどうなんだろう?
そのへんよくわからないで作っています。(だめじゃん……)

*1:開いてるすべてのドキュメントに一発で適用してもいいけど、1ファイルずつ確かめながら実行したいんだ……

InDesignで複数のスウォッチの色を一括で切り替えるJavaScript

たくさんあるスウォッチの色を頻繁に切り替えたいとき、手作業や別ドキュメントからの読み込みをするのがものすごく面倒になった*1ので作りました。
ScriptUIを触ってみたかったのでCS3/CS4のみ対応です(たぶん)。動作確認はWindows XP SP3 InDesign CS4(6.0.4)でしか行っていません。

(function(){

  if ( app.documents.length == 0 ) { alert("ドキュメントが開かれていません"); return; }

  // スウォッチの設定セットをつくる
  var colorSetArr = [
    { name  : "見出し1用",
      cmyk  : [  0,100,100,  0],
      black : [  0,  0,  0,100],
      set3  : [ 20, 20, 20,100] },

    { name  : "見出し2用",
      cmyk  : [  0,  0,100,  0],
      black : [  0,  0,  0, 50],
      set3  : [ 10, 10, 10, 40] },

    { name  : "見出し3用",
      cmyk  : [100,  0,100,  0],
      black : [  0,  0,  0, 60],
      set3  : [ 10, 10, 10, 50] },

    { name  : "見出し4用",
      cmyk  : [100, 50,  0,  0],
      black : [  0,  0,  0, 70],
      set3  : [ 10, 10, 10, 60] },

    { name  : "見出し5用",
      cmyk  : [ 50,100,  0,  0],
      black : [  0,  0,  0, 60],
      set3  : [ 10, 10, 10, 50] }
  ];

  // モード識別準備
  var mode = "";

  // ダイアログの内容を作成
  var dlg = new Window("dialog", "モード選択");
  dlg.add("statictext", undefined, "どの設定に変換しますか?");
  dlg.btn1 = dlg.add("button", undefined, "カラー");
  dlg.btn2 = dlg.add("button", undefined, "モノクロ");
  dlg.btn3 = dlg.add("button", undefined, "設定3");
  dlg.btn1.onClick = function(){ mode = "cmyk"; dlg.close(); }
  dlg.btn2.onClick = function(){ mode = "black"; dlg.close(); }
  dlg.btn3.onClick = function(){ mode = "set3"; dlg.close(); }

  // ダイアログを表示
  dlg.show();

  // キャンセルされてたらここで処理終了
  if ( mode == "" ) { return; }

  var actDoc = app.activeDocument;

  // 各スウォッチの色設定をモードに応じて変更する処理
  for ( var i = 0, len = colorSetArr.length; i < len; i++ ) {
    var tmpSet = colorSetArr[i];
    try {
      var targetColor = actDoc.swatches.item(tmpSet.name);
      targetColor.colorValue = tmpSet[mode];
    } catch(e) {
      alert("スウォッチがみつかりません: " + tmpSet.name); // エラー処理(手抜き)
    }
  }
  
})();


↓↓↓

スウォッチは名前で識別しています。
処理するスウォッチの数やモードの数は増やすことができます。外部に設定ファイルを作ってそれを読み込んで……とかも考えたのですが、それはそれで面倒なので直打ちで。
設定を変更したいときに修正する箇所をもっと減らす方法はないかなあ。

2010.3.24 追記:スクリプトの38行目、「staticText」→「statictext」に修正しました。ご指摘下さったmilligrammeさん、ありがとうございました。

そういえば

今年最初のエントリでした。あけましておめでとうございます?
前回あんなことを言っておいてこの体たらくですが、一応、オンラインブックマーク等でコメントは積極的にしているのです……でも100のブクマより1のブログエントリ、という気もしています。

*1:3回くらい繰り返したあたりで(早

せっかくだから2009年を振り返ってみる

ほんとは年内にあと一回くらい更新しようと思っていたけど、ぐずぐずしてたら大晦日になってしまいました。


今年はアウトプットの年にしようと突然思い立ち、このブログをはじめたりTwitterでつぶやいたりソーシャルブックマークでコメントを書いたり(これは前からやってたけど)してみました。
そうやってWeb上で活動をはじめたのをきっかけに、DTP Boosterなどの勉強会に参加したりオフ会に行ったり飲みに行ったりと、Webで知り合った人たちと会う機会が多くなりました。実はこれは引きこもり気質の私には予想外の展開で、今でもわりと信じられません。
たぶん一年前の私が今の私を見たら「あんた誰だ」って言うに違いない……


こうして振り返ってみると、アウトプットしようと思って活動をはじめた結果、むしろインプットが非常に多くなった年でした。
書くためにいろいろ調べたり、勉強会に出たり、飲みに行ったりして非常にたくさんの知識を得た……のは確かなのですが、実はそれだけではなくて。
この一年でインプットされた一番大事なものは、「意欲」だと感じています。
私のモチベーションに火をつけてくださった方々、直接お会いできた方もこちらが一方的に知っているだけの方もいますが、本当に感謝いたします。


ちゃんと勉強してみると知らないことばかりで恥ずかしくなり、もっと早くこのやる気が出ていればーとか今まで何してたんだーとか何年無駄にしたんだーなどと思うばかりですが、考えても仕方ないので今はこのまま突っ走ります。




……ということで、来年はもうちょっとまじめにこのブログでのアウトプットを増やそうかな……。まだはてダ市民にさえなれてないよ!

InDesignのセクションマーカーを一括登録するJavaScript(CS〜)

セクションマーカーを入力していく手間をちょっとだけ省きます。すでにセクションは作成されていることが前提です。
自分の作業で使うために作ったので汎用性は微妙ですが、あまりにブログを書いてないのでたまには更新しようと思い立った次第。

実行するとこんなダイアログが出るので、カンマ区切りで入力します。

数が足りないと怒られます。
動作確認はWindows版CS4でしかしていませんが、たぶんCS以降なら動くんじゃないかと……

(function(){

  var defaultArrStr = "1,2,3,4,5,6,7,8,9";

  var secs = app.activeDocument.sections;
  var secsLength = secs.length;

  var dlg = app.dialogs.add({name:"セクションマーカー一括設定"});
  var col = dlg.dialogColumns.add();
  
  col.dialogRows.add().staticTexts.add({staticLabel:"セクションマーカーを入力してください。"});
  col.dialogRows.add().staticTexts.add({staticLabel:"半角カンマ区切りで " + secsLength + " 個以上必要です。"});
  var textbox = col.dialogRows.add().textEditboxes.add({editContents:defaultArrStr,minWidth:200});

  var getArr = function(){
    if( dlg.show() ) {
      var arr = textbox.editContents.split(",");
      if ( arr.length < secsLength ) {
        alert("マーカーの数が足りません!");
        getArr();
      }
      else {
        return arr;
      }
    }
  }

  var markerArr = getArr();

  if ( !markerArr ) { return; }

  for ( var i = 0, len = secs.length; i < len; i++ ) {
    secs[i].marker = markerArr[i];
    alert("セクション「" + secs[i].marker + "」 : " + secs[i].pageNumberStart + "ページから");
  }

  dlg.destroy();

})();

コメント入れるのサボってます。

最初のdefaultArrStrの値を変えると、ダイアログにあらかじめ入力されてるテキストが変わります。
このあたりもうちょっとまじめに作りこんだら使い勝手が良くなる気がします……特定のスタイル(見出しなど)のついたテキストを自動で放り込むとか。そもそもそのスタイルついてたらセクションを自動で作成するとか。

正規表現の先読みと後読みはどっちがどっちだかわかりにくいんだよ!(追記あり)

といつも思うので、自分用にメモすることで覚えようという魂胆です。
以下はInDesign CS4の正規表現について記述します(たしかCS3から使えたような気がする)。

結論から

名前 英語で 位置 演算子
後読み lookbehind マッチパターンよりの部分 (?<=) 肯定
(?<!) 否定
先読み lookahead マッチパターンよりの部分 (?=) 肯定
(?!) 否定

肯定の場合はパターンの前が「=」、否定の場合は「!」。
英語を見ると「後読み」「先読み」の訳語も納得できそうな気はするのですが、やっぱりわかりにくいです。背後と前方……とか言うとまた混ざるし!
「つぎの電車」と「こんどの電車」はどっちが早く発車するかみたいな感じに似ていますな。
別に演算子さえ覚えてしまえば名前は意識しなくてもよさそうなのですが、せっかくInDesignには正規表現の記述支援機能がついてるので、どうにか慣れておきたいところです。

先読みと後読みは便利

テキストデータがあって、あるパターンをもった文字列を探したいけど、その中でも「後ろに/前に決まったパターンの文字列がある場合だけ」マッチさせたい、みたいなときに活躍します。
例えば「2009年9月7日19時00分〜9月17日8時30分」とかいうテキストがあって、「○時」の数字だけ取り出したい場合は「直前に『日』があって、直後に『時』がある1桁以上の数字」を検索できればOK。
これを正規表現で書くと

(?<=日)\d+(?=時)

こうなります。

(?<=日) 後読み部分
\d+ マッチ部分
(?=時) 先読み部分

ですね。

先読みと後読みは便利その2

先の例、検索で場所を探したいだけなら、実は後読みだの先読みだの考える必要もなかったりします。

日\d+時

だけでも検索には引っかかる。
ただし、そのときのマッチ文字列は「日19時」と「日8時」。一方先読み/後読みを使った場合、マッチ文字列は「19」「8」になります。先読み/後読みの部分は除外されてます。
この差は大きいです。例えば見つけた数字の修正をしたい場合でも、前者だと数字のところまでカーソルを動かさなければなりませんが、後者ならそのまま数字を打ち込めばいいわけです。
もしくは、数字の部分だけに文字スタイルを適用したい場合も、後者ならば置換文字列に「$0」(「見つかったテキスト」)を入力して、置換形式のところで文字スタイルを指定して置換すればいいだけ。いちいち数字部分を選択しなおしてスタイル適用、が必要ありません。

あと、例のアレ

CS4の「正規表現スタイル」。これを使うときはスタイルを設定したい文字列だけを厳密に取り出す必要があるので、先読み/後読みの使いこなしが前提になりますね。
正規表現スタイルは非常に気に入っている機能*1なので熱く語りたいところですが、まあ、いつかそのうち。

これだけ書けば

いい加減私のスカスカな脳ミソでも覚えただろう(`・ω・´)

『後読みが前、先読みが後ろ!』 ……やっぱりわかりにくい!! ><

追記(2009/09/08)

id:seuzoさんからコメントをいただいて、もうちょっとだけわかった気がしたので追記しておきます。

「後読み」のことを「戻り読み」ともいいます。

(?=re)先読み指定があると、
まだ読んでいない部分を先読みしてマッチしたら全体のマッチを成功させます。
(?<=re)後読み指定があると、
マッチ部分の後ろまで戻って、マッチしたら全体のマッチを成功させます。

       |
   \  __  /
   _ (m) _ピコーン
      |ミ|
    /  `´  \
     ('A`)
     ノヽノヽ
       くく

こうかな?

マッチ部分を基準にして、データ処理の流れの「先を読む」か「後ろに戻る」か。
やっぱり英語のほうがわかりやすい気がするけど*2、これでなんとか覚えられそうです。日本語ムズカシネー

*1:実を言えばCS4導入の一番の動機がコレ

*2:日本語の場合「先手」「後手」という言葉もあるし、って自分で混乱させているな

(修正版)Illustrator 10で、正規表現にマッチする文字が連続してるときだけトラッキングをかけるJavaScript

まさかの2日連続投稿ですが、ただの続きです。

昨日のスクリプトですが、せっかく正規表現を使っているのになぜか一文字ずつ検索していました。あほか。
一応動くとはいえ文字が多くなると激重だったので、勉強もかねて書き直したものが下です。パフォーマンスの改善に加えて、せっかくなので相対値だけじゃなくて絶対値指定? もできるようにしました。

// 正規表現にマッチする文字が並んでるときだけ指定量トラッキング
// 下の例だと、「正規表現にマッチする」を処理した場合は
// 「マ」「ッ」に-20のトラッキングがかかります。

// 09.08.06修正
// ・一文字ずつ現在値から増減するか、全文字を指定した値に変更するか、選べるようにしました
// ・動作が劇的に速くなりました

(function(){

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

  var amount = -20
  var charClass = "[ァ-ン]"; // エスケープとかは自分でやってね
  var relative = true; // true: それぞれ現在値から増減 false:すべて指定した値に変更

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

// 以下本体 =================================================

  if ( selection.length == 0 ) { alert("何か選択して!"); return; }

  var sel = selection;

  charClass += "+"; // 量指定子追加
  var rex = new RegExp(charClass,"g");

  for ( var i = 0, len = sel.length; i < len; i++ ) {
    var tmp = sel[i];

    if ( tmp.typename != "TextArtItem" ) {
      alert("テキストじゃないので飛ばします");
      continue;
    }

    tmp = tmp.textRange(); // textRangeオブジェクトに変更

    var contents = tmp.contents;

    var m = [];
    while ( m = rex.exec(contents) ) {
      var index = m.index;

      if ( index == contents.length - 1 ) { break; } // 最後の文字ならループ抜ける

      var range = tmp.textRange(index, rex.lastIndex -2); // 見つかった文字列の、最後から2番目の文字まで

      // 相対指定かどうかで処理を分ける。
      // relativeがtrueのときは現在の値を増減するので一文字ずつ処理する
      if ( relative ) {
        var chars = range.characters;
        for ( var j = 0, c_len = chars.length; j < c_len; j++ ) {
          chars[j].tracking += amount;
        }
      }
      else {
        range.tracking = amount; // 絶対量ならいっぺんにいける
      }

    } // whileループここまで

    rex.lastIndex = 0; // 検索開始位置リセット

  } // 選択オブジェクトループここまで

  alert("おしまい!");

})();

要するに、量指定子を使ってトラッキングをかける範囲をダイレクトに取得するようにしたってことです。おかげで次の文字がどうとか判定する必要もなくなり、RegExpオブジェクトを一つにしぼれました。うーん、昨日の自分が何を考えていたのかわからない。
相対値でなく全部同じ値にする場合はtextRangeオブジェクトのtrackingプロパティを使えば一文字ずつ処理する必要がなくなるので、より速くなります(よほど大量にヒットしなければ大差ないですが)。
ただ、一つの状況に最適化すると応用が利かなくなるというわけで、characterオブジェクトにあってtextRangeオブジェクトにないプロパティを操作したい場合にはいろいろ書き換えが必要になってしまいますね。たいしたことないけど。

Illustrator 10で、正規表現にマッチする文字が連続してるときだけトラッキングをかけるJavaScript

タイトル長い。

★追記:激重だったので修正版作りました!

どうにも入りきらないテキストをむりくりスペースに収めたいとき、かなの間だけ詰めたりすることがよくあります。それを自動でやろうっていうスクリプトです。俺得です。
Illustrator 10はダイアログを出したりできない(ぽい)ので、文字やトラッキング量の指定はスクリプトを書き換えて行います。10じゃなければ、「イラストレーターで正規表現とテキストの変形 - なにする?DTP+WEB」でもっとずっと高性能なものが配布されています。いいなあこれ使いたいなあ……。

// 正規表現にマッチする文字が並んでるときだけ指定量トラッキング
// 選択したテキストオブジェクト(TextArtItem)について処理します。複数選択対応。
// 下の例だと、「正規表現にマッチする」を処理した場合は
// 「マ」「ッ」に-20のトラッキングがかかります。
// すでにトラッキングが設定されている場合は現状から増減します。

(function(){

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

  var amount = -20
  var charClass = "[ァ-ン]"; // エスケープとかは自分でやってね

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

// 以下本体 =================================================

  if ( selection.length == 0 ) { alert("何か選択してください"); return; }

  var sel = selection;
  var rex = new RegExp(charClass,"g");
  var rex2 = new RegExp(charClass);

  for ( var i = 0, len = sel.length; i < len; i++ ) {
    var tmp = sel[i];

    if ( tmp.typename != "TextArtItem" ) {
      alert("テキストじゃないので飛ばします");
      continue;
    }

    tmp = tmp.textRange(); // textRangeオブジェクトに変更

    var contents = tmp.contents;
    var characters = tmp.characters;

    var m = [];
    while ( m = rex.exec(contents) ) {
      var index = m.index;

      if ( index == characters.length - 1 ) { break; } // 最後の文字ならループ抜ける

      var tc = tmp.characters[index];
      var nc = tmp.characters[index + 1];

      if ( !nc.contents.match(rex2) ) { continue; } // 次が違う文字クラスだったらパス

      tc.tracking += amount; // ★

    } // whileループここまで

    rex.lastIndex = 0; // 検索開始位置リセット

  } // 選択オブジェクトループここまで

  alert("おしまい!");

})();

例によってエラーとかまじめに気にしてないです。

★のついてる行を書き換えれば、文字単位の属性にはいろいろ使いまわせる(んじゃないかな)と思います。
ちなみにトラッキングでなくカーニングを使いたい場合(あるんかな)には注意が必要で、

tc.kerning += (amount / 1000); // ★

と書き換えないととんでもない量になります。文字パレットと単位が違うんですね……。
この単位の違い、リファレンスを見る限りCS2から解消されています。