Quantcast
Channel: chalcedony_htnの日記
Viewing all 46 articles
Browse latest View live

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

$
0
0

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

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

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


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

$
0
0

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


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


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


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




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

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

$
0
0

たくさんあるスウォッチの色を頻繁に切り替えたいとき、手作業や別ドキュメントからの読み込みをするのがものすごく面倒になった*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回くらい繰り返したあたりで(早

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

$
0
0

こないだのスクリプトをちょっと改造してみました。
実行のたびにダイアログを生成するのではなく、一度実行したら(閉じない限り)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ファイルずつ確かめながら実行したいんだ……

DTP Booster 013に行ってきた

$
0
0

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だと思いこんでいて実際その通りだったのですが、実は対象アプリケーションについては明記されていなかったらしいです

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

$
0
0

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


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

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

まずは、お題を読む

「開いているドキュメントをすべてPDF書き出しする」スクリプトを作って下さい。

DTP Booster 13のお題 - たけうちとおるのスクリプトノート

シンプルだ。そして実用的。
たぶんスクリプト書く人ならとっくに自分で作っていそうな気がするし、作らない人でもWebで公開されている数々のありがたいスクリプトを利用しているのではないかと思う。私は一つずつドキュメントを確かめながら書き出していくから使ってなかったけど。
っていうかそもそもたけうち氏のブログに置いてなかったっけ、と思って調べたら案の定ありました。ダイアログの出る親切設計。でもお題なので、今だけ見なかったことにしよう。

この時点で、この処理を実現するには「PDFを書き出す処理を」「開いているドキュメントすべてに対して繰り返し行う」という形のスクリプトになるなあとぼんやり考える。
繰り返し処理は基本なので簡単。開いているドキュメントをすべて取得する方法もわかる。あとはPDF書き出し処理か……どうやって書くんだっけかな。一度調べようとしたことがあるはずだけど、もう完全に忘れ去りましたね。

お題をさらに読む

書き出すPDFの設定その他について追記がありました。

書き出されるPDFはドキュメントと同じファイル名で(拡張子が.pdfになる)同一階層に保存されるとします。

とりあえず、処理するドキュメントのファイル名(と、たぶんパス)を取得する必要があるっぽい。

PDF書き出しプリセットは「PDFx/1-a」です。

たぶん書き出しするメソッドにプリセットを指定する引数があるんだな……などと、普通はリファレンス見てから知ることだけど今回はヒントのおかげで知る。でも具体的な指定方法はやっぱり調べないとね。

PDFを書き出したドキュメントは保存せずに閉じます。

ふむ。これはとりあえず書き出しが終わった後のことだから後で考えよう。

20行以内で出来ると思います。
多少のエラー処理はしなくて結構です。

行数指定きたー。でもあんまり気にしないでおこう。ただの目安と思っておく。
多少のエラー処理っていうのは、たぶんドキュメントがひとつも開かれていない場合とかそんな感じのことだろう。お言葉に甘えてそのへんはざっくり省略することに。

わかるところから書き始めてみる

なんとなく方向性がみえたところでおもむろに書き始めます。
まずは骨組みとしてドキュメント取得と繰り返し処理の部分を用意。後で必要になる処理をコメントで書き込んでおこう。

var docs = app.documents; // 現在開いているすべてのドキュメントを得る// ドキュメントの数だけループfor( var i = 0, docLen = docs.length; i < docLen; i++ ) {var tmpDoc = docs[i]; // 今処理してるドキュメント// ★ドキュメントのPDF書き出しを実行// ★ドキュメントを閉じる(保存しない)}// ここまでループ

forループのときに繰り返す回数を変数に入れておくのは癖のようなもの。処理中のオブジェクトを変数に入れておくのも同じ。プロパティへのアクセスは結構時間がかかるものなんだとWebな人たちから聞いたので。この程度だと大して変わらない気がするけど。

あとは、★のついたところを埋めていくだけだな!

PDF書き出し処理はどう書くの

というところがわからないので、ここでリファレンスの出番です。ESTK付属の「オブジェクトモデルビューア」を起動。表示するオブジェクトモデルで「InDesign CS4(6.0)」を選ぶのを忘れずに。
「書き出し」ってくらいだからたぶんexportとか検索したら出てくるんじゃないの、と試しに検索してみたところ、

検索結果が多すぎます。検索内容を絞ってください。

(ノ`Д´)ノ彡┻━┻

……気を取り直して続ける。
「ドキュメントを」PDFファイルに書き出すって動作をすることになるので、たぶんDocumentクラス(クラスでいいのかな*1)のあたりにそういうメソッドが用意されてるんじゃないかと予想を立てる。このへんはただの勘のようなもので、なぜそう思ったか説明できない……ちなみに、この予想が外れたりそもそも見当がつかなかったりした場合は潔くぐぐります。

ともかく、まずはDocumentクラスを調べてみる。オブジェクトモデルのブラウザでDocumentを選び、下に出てくるメソッド群からexportっぽいものを探す。ありました。「exportFile」メソッド。

Document.exportFile (format, to, showingOptions, using, withGrids, versionComments, forceSave)


Exports the object(s) to a file.

オブジェクト(この場合はドキュメント)をファイルに書き出す。そのままだ。
あとは引数の正体を調べる。

引数引数の型意味
format-書き出すファイルの形式。
toFile書き出すファイル。
showingOptionsBooleanオプション。書き出しのときダイアログを出すかどうか。デフォルトはfalse。
usingPDFExportPresetオプション。書き出し方式。ここがPDFの書き出しプリセットっぽい。
withGridsBooleanオプション。グリッドを書き出すかどうか。デフォルトはfalse。
versionCommentsStringオプション。このバージョンのためのコメント?
forceSaveBooleanオプション。強制的に上書き保存するかどうか? デフォルトはfalse。

最後のほう適当ですがあんまり関係なさそうなので……というか、普段こんなきっちり考えない。使いそうな引数だけ拾い読みですよ。英語だし。

ということで、このメソッドを実行するときにPDF形式を選べるようだ。「ファイル名」と「プリセット」も設定できそう。

PDF形式で書き出す指定

引数formatのところでPDF形式で書き出すよう指定するらしい。リファレンスには「Can accept: ExportFormat enumerator or String.」と書いてある……文字列で指定できるようだけど、きっと適当に「PDF」とか書いてもダメなんだろうな。
とりあえず「ExportFormat」をオブジェクトビューアで検索して、同名のクラスに「PDF_TYPE」ってプロパティがあるのを発見。たぶん、これを書けばいいんだと思う。やってみてダメだったらぐぐろう。

Fileオブジェクト?

出たな妖怪! いや妖怪じゃないけど、個人的にいまだに理解しきれない領域、それがFileとFolder。
書き出しメソッドの引数toには、書き出すファイルのFileオブジェクト(Fileクラスのインスタンス? っていうの?)を用意しなきゃならないらしい。
Fileオブジェクトを生成する方法は、

var fileObj = new File("ファイル名まで含んだパスの文字列");

でいいはず。この場合に必要な文字列は、「ドキュメントのファイルパスから拡張子を取り除いた文字列」+「.pdf」。
というわけで、まずは処理中のドキュメントのフルパスを取得しなければ。

var tmpDocFileObj = tmpDoc.fullName; // tmpDocは処理中のドキュメント

fullNameプロパティの値はFileオブジェクトらしいのでそのままでは使えない。さらにこのファイルのフルパスを取得。

var fileStr = tmpDocFileObj.fullName; // tmpDocFileObjはさっき取得したFileオブジェクト

さっきと同じfullNameって名前のプロパティだからややこしいけど、これで処理中のドキュメントのファイルパスが文字列で取得できたことになる。
でもって、これだと変数fileStrには拡張子「.indd」までついているはず。なのでそれを取っ払って、拡張子「.pdf」をくっつけてやらないといけない。
後ろ5文字削ってから足してもいいけど、ここは正規表現置換といこうじゃないか。*2

// 行末にある「.(なんでもおk)」を「.pdf」に置換
fileStr = fileStr.replace(/\.[^.]*$/,".pdf");

これでやっと、書き出すPDFファイルのFileオブジェクトを作ることができる。

var fileObj = new File(fileStr); // fileStrは(以下略

せっかくだからここまでの過程を一行にまとめてみよう。

// tmpDocは処理中のドキュメントvar fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));

このfileObjを、書き出しメソッドの引数として使えばいいはずだ!

さっきかららしいとかはずとかばっかり。手探りでやってるから仕方ない。
でも正直、今書いている過程でちょっと理解が進みました。とりあえず今回の妖怪は退治できた……かな?

PDF書き出しプリセット

最後の大物、書き出しプリセットの指定。例によって「PDFExportPreset」をオブジェクトモデルビューアで検索してみる。どうでもいいけどこの検索窓、テキストのペーストがうまくいかないですね。いちいち手打ち……
Applicationオブジェクトのプロパティに「pdfExportPresets」を発見。複数形ってことはたくさんあるプリセットが全部ここにまとまって入ってるんだろう。その中から目的のプリセット「PDFx/1-a」を取り出して使う。
オブジェクトのコレクション(っていうの?)から目的のオブジェクトだけを取得する方法はいくつかあるけど、今回は名前をキーにして取得することになる。

var preset = app.pdfExportPresets.item("PDFx/1-a");

このへんの書き方はどのクラスでもたいてい同じ。itemメソッドの引数はインデックスの数字でも名前の文字列でもOK。他にitemByNameってメソッドもあって、そちらは名前の文字列だけが使えるんだけど……itemメソッドで充分な気がするのに、なんで用意されてるんだろう? まあいいか。

材料が揃ったので

PDF書き出し処理のところを書いてはめ込んでみよう。
あ、忘れてたけどshowingOptions(書き出すときダイアログを出すかどうか)の値はfalseにしておくことにします。それ以外の必須でない引数は省略。

var docs = app.documents; // 現在開いているすべてのドキュメントを得る// ドキュメントの数だけループfor( var i = 0, docLen = docs.length; i < docLen; i++ ) {var tmpDoc = docs[i]; // 今処理してるドキュメント// 今回追加した部分ここから ------------------------------// 書き出すファイルのFileオブジェクトを作成var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
  // プリセットのオブジェクトを取得var preset = app.pdfExportPresets.item("PDFx/1-a");

  // ドキュメントのPDF書き出しを実行!
  tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);

  // 今回追加した部分ここまで ------------------------------// ★ドキュメントを閉じる(保存しない)}// ここまでループ

ドキュメントを閉じるのは後回しで、とりあえずこれが動くかどうかやってみよう(`・ω・´)
ESTKに貼り付けて、適当なinddドキュメントを4つくらい作って開いて実行!

イベント 'exportFile'のパラメータ 'using'の値が無効です。予想される値は PDFExportPreset ですが、値 nothing を受け取りました。

( ゚д゚)...

なにか間違えたようです。

修正しよう

エラーメッセージを見るに、プリセットのあたりで間違えているらしい。スペルミスはないと思うんだけど。
実際に処理が止まってしまった(エラーで赤く染まった)行は、書き出し実行のところ。ということは、プリセットがきちんと取得できてなかった(ので、変数presetの値がnothingだった)ということだろう。その原因は……あっさり判明。

( ´д`)...プリセットの名前、違うじゃん。

お題で指定されたのは確かに「PDF/X-1a」だけど、私の環境ではInDesignにこの名前のPDF書き出しプリセットはなかった。そりゃあ動かないわ。お題に従うならこのコードであってるけど、とりあえず動かすには修正しなくちゃ*3
ということで、InDesignのPDF書き出しプリセットメニューを見て、(デフォルトで用意されてる)PDF/X-1aで書き出すプリセットの名前を調べる。「[PDF/X-1a:2001 (日本)]」、これだな(Win版InDesign CS4の場合)。

var docs = app.documents; // 現在開いているすべてのドキュメントを得る// ドキュメントの数だけループfor( var i = 0, docLen = docs.length; i < docLen; i++ ) {var tmpDoc = docs[i]; // 今処理してるドキュメント// 書き出すファイルのFileオブジェクトを作成var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
  // プリセットのオブジェクトを取得var preset = app.pdfExportPresets.item("[PDF/X-1a:2001 (日本)]"); // ← ココ// ドキュメントのPDF書き出しを実行!
  tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);

  // ★ドキュメントを閉じる(保存しない)}// ここまでループ

今度こそ、と実行ボタンぽちっ。

キタ.*・゜゚・*:.。..。.:*・゚ヽ(゚∀゚)ノ ゚・*:.。. .。.:*・゜゚・*ー!!!!!

ドキュメントと同じフォルダに、PDFがきっちり書き出されました。
これでほぼスクリプトは出来上がったようなものではないだろうか。一気に完成させてしまおう。

あとはドキュメントを閉じるだけ……のはず

これはもう何度も書いたことある処理なので悩むところはないと思う。でも一応、リファレンスを確認しておこうかな。Documentクラスのcloseメソッドだよね。

Document.close (saving, savingIn, versionComments, forceSave)


Close the Document

引数引数の型意味
savingSaveOptionsオプション。閉じる前に保存するかどうか。デフォルトはSaveOptions.ASK。
savingInFileオプション。保存するファイル。
versionCommentsStringオプション。バージョンコメント?
forceSaveBooleanオプション。強制的に上書き保存するかどうか?

ん、SaveOptionsってなんだ。trueかfalseで指定するわけじゃないのか。とりあえずオブジェクトモデ(ryで検索する。

SaveOptions.ASK変更を保存するかどうかのプロンプトを表示する。
SaveOptions.NO変更を保存しない。
SaveOptions.YES変更を保存する。

今まで作ってきたスクリプトでは、保存するかどうかはデフォルトのままにしていました。それがSaveOptions.ASKで、今回は保存しないのでNOを指定すればいいようです。

var docs = app.documents; // 現在開いているすべてのドキュメントを得る// ドキュメントの数だけループfor( var i = 0, docLen = docs.length; i < docLen; i++ ) {var tmpDoc = docs[i]; // 今処理してるドキュメント// 書き出すファイルのFileオブジェクトを作成var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
  // プリセットのオブジェクトを取得var preset = app.pdfExportPresets.item("[PDF/X-1a:2001 (日本)]");

  // ドキュメントのPDF書き出しを実行!
  tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);

  // 今回追加した部分ここから ------------------------------// 保存せずにドキュメントを閉じる
  tmpDoc.close(SaveOptions.NO);

  // 今回追加した部分ここまで ------------------------------}// ここまでループ

残っていた★の行も埋まって、これで完成だ! ということでもう一度ESTKに貼り付けて、ドキュメントを4つ開いて実行してみる。

順調に書き出し→ドキュメント閉じる、という処理が進んでいく……と思ったら、3つ目のドキュメントの処理中になぜかストップ?

オブジェクトが無効です

(;゚Д゚)...アレ?

なんで?

エラーが出たのは書き出すファイルのFileオブジェクトを作成している行。でも、2つ目のドキュメントまでは一切引っかからずきちんと進んでる。なんでだ。なんでだ! エラーならエラーでいいけどもう少し教えてくれよー!

まず疑うべきは、追加したばかりの「ドキュメントを閉じる処理」のところだろう。ここを追加する前は4つのドキュメントをすべて書き出せたんだから。ただ、2つ目までは閉じることに成功して次のループに入ってる。この行の書き方を間違えたわけじゃなさそうな気がする。試しにドキュメントを閉じる処理をコメントアウトしてみたら、ちゃんと4つのPDFが書き出せました。
閉じる処理をすることで何かが起きてるらしいけど……と、ふと思い立ってエラーで処理が止まったままのESTKで「データブラウザ」を開いてみたところ、

なんでdocs.lengthが2になってるの……

つまり、こういうこと。自分の理解のためにクドい説明をしますよ。
最初にドキュメントを4つ開いたとき、app.documents(このスクリプトではdocsで参照してる)のlengthは4で、0〜3のインデックスがつけられています。

indexドキュメント
0ひとつめ.indd
1ふたつめ.indd
2みっつめ.indd
3よっつめ.indd

でもって、1つ目のドキュメントまで処理し終わった段階で、「ひとつめ.indd」は閉じられる。そうすると、app.documentsのindexは新たに振りなおされるのだ。

indexドキュメント
0ふたつめ.indd
1みっつめ.indd
2よっつめ.indd

ここでiが1増えて、i == 1の状態で処理が行われる。つまりここで処理されるのは「みっつめ.indd」。閉じる処理まで終わったときには次の状態になります。

indexドキュメント
0ふたつめ.indd
1よっつめ.indd

ここでiが1増えるとどうなるか? i == 2だけど、docs[2](つまりapp.documents[2])は存在しない。だから「オブジェクトが無効です」というエラーが発生する!

こ、これは恥ずかしい。わかってみれば簡単なんだけどこんなことに気づかなかったとは。最初に得意げにループを書いた時点でこういう問題が起きることが決まっていただなんて!
ていうかこれ読んでる人はきっとわかってて、いつ気づくかなー(・∀・)ニヤニヤとか思ってたんだろうなー! あああ……orz

修正しよう・2

まあ、原因がわかってしまえばあとは簡単。0番目から処理するせいでドキュメントを閉じたときにインデックスが前にずれてしまうわけだから、最後から逆に処理していけばいいのだ。

var docs = app.documents; // 現在開いているすべてのドキュメントを得る// ドキュメントの数だけループfor( var i = docs.length - 1; i > -1; i-- ) {// ★var tmpDoc = docs[i]; // 今処理してるドキュメント// 書き出すファイルのFileオブジェクトを作成var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
  // プリセットのオブジェクトを取得var preset = app.pdfExportPresets.item("[PDF/X-1a:2001 (日本)]");

  // ドキュメントのPDF書き出しを実行!
  tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);

  // 保存せずにドキュメントを閉じる
  tmpDoc.close(SaveOptions.NO);

}// ここまでループ

修正したのは★の行。iの初期値を「ドキュメントの数-1」にする。これが最後のドキュメントのインデックスで、そこから1ずつ減らしていくことで0番目のドキュメントまで処理できる。
今度こそ何も問題はないはずだ! 例によって4つドキュメントを開いて実行!

Execution finished.

ヤッタ.*・゜゚・*:.。..。.:*・゚ヽ(;∀;)ノ ゚・*:.。. .。.:*・゜゚・*ー!!!!!
最後まできちんとPDFを書き出して、ドキュメントもすべて閉じました。ドキュメントを一部修正してから実行した場合でも、PDFにはその修正が反映され、元のドキュメントは保存せず閉じられています。ドキュメントの数を増やしても減らしても問題なし。やっと完成! です! やったー!

完成!

試しにコメントを取っ払ってみたところ、8行になりました。
var docs = app.documents;
for( var i = docs.length - 1; i > -1; i-- ) {var tmpDoc = docs[i];
  var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
  var preset = app.pdfExportPresets.item("[PDF/X-1a:2001 (日本)]");
  tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);
  tmpDoc.close(SaveOptions.NO);
}

20行よりはだいぶ少なくて済みましたね。ただ、やっぱりドキュメントを1つも開いてないときのチェックくらいは入れてもいい気がしてきます。ついでに、無名関数でくるんでしまおう。

(function(){var docs = app.documents;
  if( docs.length == 0 ) {alert("ドキュメントが開かれていません。"); return; }for( var i = docs.length - 1; i > -1; i-- ) {var tmpDoc = docs[i];
    var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
    var preset = app.pdfExportPresets.item("[PDF/X-1a:2001 (日本)]");
    tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);
    tmpDoc.close(SaveOptions.NO);
  }})();

ドキュメントの数が0だったときはエラーメッセージを出して、returnで処理を終了してしまうようにしています。returnが使えるのは全体を関数にしてしまったおかげ。
行数は増えましたが、その分ちょっとだけ親切になりました。普通に使うにはこれで充分かな。

蛇足:ループの書き方を変えてみる

ドキュメント数が減るのに対応するためにforループを逆転してみたけど、減っていくとわかっているならwhileでも書けると思ったので、せっかくだから書いてみる。

(function(){var docs = app.documents;
  if( docs.length == 0 ) {alert("ドキュメントが開かれていません。"); return; }while( docs.length != 0 ) {// ★1var tmpDoc = docs[0]; // ★2var fileObj = new File(tmpDoc.fullName.fullName.replace(/\.[^.]*$/,".pdf"));
    var preset = app.pdfExportPresets.item("[PDF/X-1a:2001 (日本)]");
    tmpDoc.exportFile(ExportFormat.PDF_TYPE, fileObj, false, preset);
    tmpDoc.close(SaveOptions.NO);
  }})();

変更したのは★のついた2行。ドキュメントの中で最初のものを処理の対象にして(★2)、処理が終わったら閉じる。それをドキュメントの数が0になるまで繰り返す(★1)、という感じ。毎回0番目に来てるドキュメントを対象にするので、ドキュメントが閉じてインデックスがずれても問題ありません。
でもよく考えてみると、この形のループは「うっかりドキュメントが閉じられなかった場合」に無限ループに陥る危険がある気がします(docs.lengthがいつまでたっても0にならないから)。最初に処理したいドキュメントの数は決まるのだから、forで回すほうが安全かもしれないです。

さいごにひとこと

こんなに苦労するはずではなかったんですけどね! 考えが甘かったです。最後まで読んでくださった方、いるかどうかわかりませんがありがとうございました。ツッコミ大歓迎です。
いろんなエラーが出て、原因を突き止める過程まで書き留めつつ修正していったおかげで完成まで諦めずに済みましたし、なんとなく修正してなんとなくできあがるよりも理解が深まった気がします。時間はかかったけど。あと、だいぶ恥ずかしいけど。
今後また機会があったら実況風に書き留めてみたいなと思います。

……でもこれ、たぶんPlanetDTP@jpに全文載っちゃうんじゃないかしら。載せてくれるのはありがたいことだけど、長くてクドくてすいません!(今更……

*1:JavaScriptから入ったクチだからか、クラスとかインスタンスとかいうのがよくわかっていません

*2:たぶん指定文字数削って足すほうが速いんだろうけど

*3:修正の方向としては「PDF/X-1a」という名前のプリセットを自分で作るっていうのもありなんだけど

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

$
0
0

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

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イベントハンドラが全部消えます、手抜きです

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

$
0
0

とってもお久しぶりです。最近アウトプット減ってて*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:無名関数とかやめたり、思いつく限りいろいろ削ってみたけど大差なしだった


DTPの勉強会に出ます

$
0
0

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

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

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

InDesignJavaScriptでコントロールする」

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

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

実を言えば

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

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

まいくてすと

$
0
0

あーあー本日は雨天なり

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

$
0
0

ご無沙汰しております。

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

淡々と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でもばっちり便利に使えてます。

livedoor ClipがなくなったのでPinboardはじめました

$
0
0

はいこんばんは。
ちょっと前からですが、メインのソーシャルブックマークサービスPinboardに乗り換えましたはてなのほうは継続利用中です。
これだけで完了するような話ですが、せっかくだからいろいろ書いておきます。

さようならlivedoor Clip

愛用していたソーシャルブックマークサービスlivedoor Clip」がお亡くなりになってしまったので、泣く泣く新しいサービスへ移行しなければならなくなりました。かなしい……
どうやらクリップでなじみの面々の多くははてなブックマークに移行したようなのですが……今までのブックマークを移行して長ったらしいコメントが途中でちょん切れるのは絶対に避けたかったため、他のサービスを探すことに。

ソーシャルな部分はいまとなってはTwitterで十分なところがあるので、機能面を重視して選びました。
具体的に言うとコメント文字数と速度、それからある程度メジャーで今後のサービス継続が期待できるもの。
ブックマーク支援ツール(Firefoxアドオンの「Tombloo」を使用)が対応しているかどうかも大事なところです。あ、あと日本語対応か。

こんにちはPinboard

いろいろ比較した結果、何人かがおすすめしてくれたPinboardに決定。なんとコメント文字数無制限*1なのです。桃源郷はここにあったんやー!
他の機能についてはツアーを参照。
面白いと思ったのは「to read」設定ですね。いわゆる「あとで読む」。このフラグが付けられたブックマークは「未読一覧」みたいな形で一覧表示できて、読み終えたしるしに「mark as read」リンクをクリックするまで非公開になるというものです。
たしかに、読んでみるまではブクマしたことを知られたくないってのはありますし。通常の非公開設定と分けてあるところが、使うシーンをきっちり考えてるなーって思います。まあ個人的には非公開とかまず使わないんですけど。

Pinboardは有料のサービスなのですが、月額とかではなくて1回支払えばOKということでさくっと登録。金額が登録の時期によって(つまりユーザー数が増えて維持費が上がるに従って)だんだん高くなっていくらしくて、それもちょっと面白いと思いました*2

ちなみに

LDC時代のブックマークはエクスポートしてPinboardのほうにぶちこんであります。エクスポートしたXMLをPinboard用に変換するのにえらい苦労したのですが、そのときのメモはうっかり意図的に消したので省略。
なお、ミスって日付情報がすべて吹っ飛んだため、移行した分のエントリはいつのものかさっぱりわからない状態です。細けぇ事はいいんだよ!

*1:システム上の上限はあるでしょうけどね

*2:金額据え置きで無理した結果破綻するよりよっぽどいいと思う

INDD 2013 Tokyo(spring)に行ってきた(第3セッション感想)

$
0
0

はいこんばんは。

先週の土曜日はInDesignの祭典、INDD 2013 Tokyo (spring)に行ってきました。セミナーのレポートとか最近サボり気味なんですが、今回は自分の業務にダイレクトにつながる内容だったので、せっかくだから俺は赤い扉を選感想など書いてみようと思います。他の2セッションについても書こうと思ったのですが力尽きましたごめんなさい……
感想なので、内容のまとめではありません。内容については当日のTwitterの様子がまとめられたTogetterがあるのでそちらを。
http://togetter.com/li/464817
うわっ……私のpost、多すぎ……?(例の顔で

第3セッション:多ページ作成でのInDesignテクニック

テクニカルドキュメント、いわゆる「トリセツ」を日々制作されている西村さんが、あかねさんを相棒に多ページのドキュメントを大量に処理する場合のテクニックを解説してくれるセッション。
立場や仕事量などもろもろ違いますが、私も主な業務は同じくテクニカルドキュメントの制作です。この種の作業の特徴としては、スライドで挙げられていた

  • ページ数が多い
  • ファイル数も多い
  • レイアウトはシンプル
  • テキストがメイン

に加え、

  • ほかのページへ誘導する記述が大量にある(X章のXを参照、XXページを参照、など)
  • ページ増減が頻繁にある(途中のページに記述がどっさり追加され、以降の改ページ位置などがすべて変わる)
  • 改訂が多く、データが長年引き継がれ続ける
  • 改訂サイクルが早い(ことがある)

などが挙げられると思います。
下版した次の日から改訂作業開始とかざらにあったり。逆に5年も前のデータを掘り起こしたり。最近は時代の流れか紙のトリセツも減りまして、最終的な出力がPDFでWeb掲載だったりすると、数百ページあるのに作業期間は1〜2日とかね。ありますね。
数百ページのドキュメントにちりばめられた「詳しくはXXページを参照してください」って記述を手作業で検索して参照元と引き合わせて違ってたら手で数値打ち直して……とか、ページ増減で内容とずれてしまった柱テキストを目視確認してちまちま修正して……とか、「テキスト修正だからそんなに時間かからないでしょ? PDFだし、できたらすぐちょうだい」、と、か……考えただけでSAN値*1ががりがり削れていきます。

そんな作業をInDesignで(正気を保ちつつ)効率的に行うためには、どんな機能をどう使ったらいいのか? というのが今回のセッション。
その内容ですが……いやー、濃かったですね! ものっすごいボリューム。

  • テキスト変数
  • 箇条書き
  • 相互参照
  • アンカー付きオブジェクト
  • ブック
  • 目次
  • 索引
  • (条件テキスト)

まさに普段業務で使っている機能ばかりで嬉しくなってしまいました。
逆に、初めて「この機能ってこういう時に使うのか」と知った人もいるんじゃないでしょうか? Adobeのヘルプではそれぞれ別の箇所に記述されていて、こんなふうに逆引き的に関連付けてまとまってはいないんですよね。機能の存在と概要を知らないと調べようがない。そもそも自動番号が箇条書きってどういうことなの……とかね。
セッションでは、取り上げたそれぞれの機能について、何がどう効率化できるのか、どう設定したら使えるのか、使うときの注意点は何か、を丁寧に説明していました。
この「注意点」のところが実はセッションの目玉だったんじゃないかと。ものすごく実践的で、「あるある!!」と何度も何度もうなずきながら聞いてたり、もちろん初めて知るものもたくさんあったり。単なる機能紹介や公式のヘルプでは絶対出てこないですよね、箇条書きの自動番号で51番目以降は丸数字が使えないとかw
設定方法についても、普段使ってる部分以外はあいまいにしか理解していなかったのできっちり復習できました。索引の参照形式とか、アンカー付きオブジェクトの位置オプションとか。後者はスライドで視覚的にまとめられていてわかりやすかったです。今まで回りくどいやり方をしていなかったか、チェックしてみるいい機会になりました。


実を言えば、これらの機能は日々使っているものの、他の環境ではどんなふうに使われているんだろう……といつも思っていたんですよね。よそで制作したデータを見ることが一切ないので。Webで検索してみても、実践的な内容はなかなか出てこないですし。
もっと効率的なやり方があるのではないか? むしろ根本的に機能を勘違いしていたりしないか? と不安を抱いていたのですが、今回ピンポイントでテクニカルドキュメントの例を見ることができ、おおむね方向性は間違っていないとわかって心底ほっとしました。そのテンションの結果があのpost数だよ*2


改めて今回紹介されていた機能を俯瞰してみると、何をおいてもまずは「ドキュメントを(データ上で)構造化すること」がはじめの一歩になるんだなあと思います。変数、相互参照、箇条書き、目次などなど、「テキストに適切なスタイルが設定されている」からこそ使えるんですよね。
逆にこれらの機能をフルに使いたいなら、それを前提にしたスタイルの設計をする、というのも必要になってくると思います。「見出しだから文字を大きくする段落スタイルを付けよう」ではなくて、「見出しとして使いたい段落だから見出しという段落スタイルを付けて、見た目でも見出しとわかるように文字を大きくしよう」という感じ。
データを構造化しておくと、さらに進んだ自動処理(たとえばスクリプトを使うとか)をしやすくなったり、Eなんとか形式にするときにもたぶん比較的スムーズだったりと、メリットは多いと思います。


あとひとつ、大事だと思ったのは、最後のまとめにあった「自動だからと安心しないでチェックは必ずしよう」です。
各機能の注意点を見返してみるとわかりますが、自動更新されたりされなかったり、テキスト量によってオブジェクトが重なってしまったり、自動挿入されるテキストが手動で修正できてしまったりと、落とし穴も結構あります。相互参照まわりとかちょいちょい怪しいし*3
自分の仕事では、修正作業に時間がかからない分、チェックは念入りに……というつもりでやっています。
終了まぎわに西村さんがぽそっと言ってた、「あくまで入力支援ですからね」という言葉を肝に銘じておきたいですね。

というわけで

InDesignかわいいよInDesign
横見出し機能搭載はよ
貴重な話を聞かせていただいてありがとうございました! 姐さん、よければ次は条件テキストも教えてください!

*1:正気度

*2:参照元と参照先を間違えてるところがあって赤面

*3:相互参照、便利でかわいいやつだけどダメなところもたくさん知ってます。こちらのBlogに詳しいです→ InDesignerの悪あがきブログ内検索で「相互参照」を検索するといろいろ出てきます。

なんとなく、使っているツール(フリーウェア)を晒してみる

$
0
0

はいこんばんは。

4GBパッチというものの存在を知ってちょっと調べていたのですが、その過程でこの記事を見かけまして。
4GBパッチは効果がある!  & 個人的使用ツール
せっかくなので自分の愛用しているソフトウェアをいくつか晒してみたいと思います。
当然すべてWindows用です。

テキストエディタ

サクラエディタUnicode

メモ書きから置換処理、Grep検索、ちょっとしたスクリプト書きまでこれ一つで済んでしまいます。マクロもあるよ。
設定項目が膨大にあるけど、その分かゆいところをゴリゴリカスタマイズできて好きです。デフォルトでも十分使えますけどね。
昔は内部がShift-JISだったのですが、いまはUnicode版もあります。

ファイルリネームツール

お〜瑠璃ね〜む

http://beefway.sakura.ne.jp/dl-allrename.html
仕事しはじめて最初に使ったのがこれで、そのまま使い続けてます。正直ほかのやつ使ったことないので、どこがいいとかアピール難しい……
一通りの機能が揃っていながら、あんまり悩まずに直感的に使える素直さが魅力ですかね。リネーム前のプレビューが見やすいので、致命的なミスも回避できます。
開発はちゃんと継続されていて、たまにバージョンアップされてます。

CopyExt(拡張コピー)

http://www.htosh.com/software/freesoft/copyext.html
エクスプローラ拡張。ファイルやフォルダの右クリックメニュー(コンテキストメニュー)から、

  • リネームしつつコピー
  • 特定のフォルダにコピー
  • タイムスタンプを比較してコピー
  • まとめて選択して、フィルタを通ったものだけをコピー

などなど、いろんな方法でファイルコピーを行えます。設定をプロファイルで保存しておけば、複雑なコピーも一発で実行できたり。
たとえば私は、「1つ以上のファイルを選択した状態で、マウス右ボタンを押しながらドラッグ→表示されるコンテキストメニューで[ここに拡張コピー]をクリックすると“backupXX_(元ファイル名)”という名前でそれぞれ複製される」という設定で使っています。XXは連番で、すでに同じ名前のファイルがある場合だけ番号が増えていきます。所要時間1.5秒。
これでぽいぽいバックアップ取っていけば万が一の時も安心。エクスプローラ拡張なので、ファイルの保存ダイアログ上でも有効。別名保存時に新しい日付のファイル名をつけるのでなく、古いファイルのバックアップを取ってから上書き、という感じで使ってます。
ファイルが壊れる恐怖と日々戦うDTPerにとっては強い味方であります。けどMacAutomatorのほうが便利じゃね?

圧縮・解凍ツール

Cube ICE

いろんなの使ってきましたが、今はこれを使用中。MacのFinderデフォルトで圧縮されたzipファイルも文字化けせず解凍できます。
使用感はごく普通。標準的なアーカイブ形式にはだいたい対応してると思います。
圧縮時・解凍時に不要ファイルを任意でフィルタリングできるのが精神衛生上よいです。Thumbs.db爆発しろ。
インストール時に謎ツールバーをインストールするかどうか聞かれますので注意。

7-zip

普段使うことはありませんが、後述する比較ツールWinMergeアーカイブ比較機能を使うために入れています。

Explzh for Windows

ライセンスの関係で仕事用PCには入れてないですが、こちらもMacのデフォルトで圧縮したzipファイルを解凍できます。個人用PCではこちらを主に使っていますね。

ランチャー

CLaunch

パネル型のランチャー。有名どころだと思います。
タブ分けできるのである程度整理ができます。しかしだんだん登録アイテム数が増えてきてパネル自体が巨大に……
デスクトップのダブルクリックで呼び出すようにしているので、普段Adobeのアプリケーションとか使う時はあえて最大化せず、端っこ数ピクセルのスキマを作って(ダブルクリックできるようにして)います。

ファイラー(エクスプローラ拡張)

QTTabBar

Windowsエクスプローラをタブ型にするツール。たぶん似たようなのは山ほどありますが、最近は新しいのを開拓してないです。
いわゆるタブ型アプリケーションに求める機能はだいたい入っていると思います。動作のカスタマイズも細かくできます。
タブの横幅が可変で長いフォルダ名でも省略せず表示してくれること、タブをたくさん開いても多段表示できるので一覧性が損なわれないこと、あたりが個人的に好みですね。

データ比較ツール

WinMerge

定番中の定番。ファイルやフォルダを比較、テキストデータならその場でマージや編集もできます。
プラグインを入れればWordやExcel、PDFも比較可能。圧縮ファイルも解凍なしでそのまま中身を比較できます(要7-zipプラグイン)。
TortoiseSVNと連携させてリビジョン間の差分確認にも使っています。
InDesignのデータは直接比較できないけど、IDML書き出して拡張子をzipに変えたらWinMergeで修正箇所の割り出しができると最近気づきました。たまに便利。

ファイルバックアップ

BunBackup

定番。自動バックアップ、ミラーリング、フィルタ指定、上書き条件指定、世代管理などとりあえず欲しい機能が揃っています。これもカスタマイズ次第でかなり自分好みの設定を作れる。
作業データのバックアップ方法は今後変わりそうだけど、ちょっとしたデータのバックアップには使い続けるつもり。

Subversionクライアント

TortoiseSVN

最近使い始めたばかりのニューフェイス。作業データのバージョン管理を画策していまして、絶賛修行中なのです。
Windows用のGUIクライアントとしては定番どころか鉄板っぽい。エクスプローラを拡張するのはわかりやすいし便利なんだけど、ちょっと動作が重くなる気がしないでもない……
「とーたすえすぶいえぬ」、かるせど覚えた。

その他

WinCDEmu

CD/DVDのイメージファイル(isoとか)を開くと、空いているドライブレターが割り当てられ、通常のCD/DVDドライブと同じ感覚で扱うことができるようになる仮想ドライブツール。ドライブレターに空きがある限りいくつでも開けます。
なにも考えずイメージファイルをダブルクリックするだけで使えて、ドライブの解放も同じくダブルクリックでできます。仮想ドライブを右クリックして[取り出し]でもOK。このシンプルさが普段使いに便利。
例によって最初に出会ったのがこれだっただけで、他にもたくさんあるはず。

M電卓

http://www.vector.co.jp/magazine/softnews/080311/n0803113.html
電卓です。個人的には起動の速さと邪魔にならない小さいウィンドウが魅力かなあ。高機能らしいんだけど、関数電卓とか使わないし……
あと便利なのが16進数での演算。10進数で計算した結果がリアルタイムで16進数表示できたり、その逆もできたり。途中で切り替えもできます。16進数と10進数を混ぜて計算したいとき、たとえば文字コード連番でここから20文字分、とかによく使います。
ちなみに単位換算機能もあるのですが、ポイントがアメリカンポイントなので使ってないというオチ付き。

pinz

http://www.vector.co.jp/soft/win95/util/se343088.html
任意のウィンドウを最前面固定にしてくれるシンプルなツール。常駐型ですが邪魔にはならないです。
タスクバーから固定用アイコンをドラッグ→目的のウィンドウにドロップするだけで使えます。解除も同じく解除用アイコンをドロップだけでOK。
テキストエディタなりファイラーなり、それぞれに最前面固定機能は備えていると思いますが、私くらいになるといちいち個別に設定するのも(設定方法を覚えるのも)めんどくさいですからね。愛用してます。

などなど

思いつくままにいろいろ書いてみました。ほぼ毎日使うものだけに絞った*1のであんまり数なかったですね。
似た機能を持つフリーソフトがたくさんあって選択肢が多いのがWindows界のいいとこなので、「そういう使い方ならこっちのが便利だよ」というのがあったらぜひ教えてくださいー。

*1:諸事情で書かなかったやつもある

選択した文字列を索引項目に登録するJavaScript(読み仮名自動入力、CS3〜)

$
0
0

はいこんばんは。

InDesignで索引項目を追加するときはソートのために読み仮名を入力しなくてはならないのですが、手打ちするのがあまりにもめんどくさいので自動的に取得するスクリプトを作りました。
読み仮名の取得にはみんな大好きYahoo!のテキスト解析WebAPIを利用しています。

これを


こうします


あくまで自分用に作ったものなので、

  • 参照形式はデフォルトの「現在のページ」のみ
  • 項目のレベルは設定できない(すべてレベル1になる)

という仕様になっています。
あと、APIのアプリケーションIDは消してありますので、もし使用する場合は自分のアプリケーションID(ランダムな英数字になってます)を取得して、「◆◆◆ココにアプリケーションIDを書く◆◆◆」のところに入れてください(1箇所だけです)。YahooのIDを作れば誰でも取得できます。
ちょっと試してみたいという人にはだいぶ不親切ですがご了承ください><


このスクリプトを書くにあたって、2つの記事を大いに参考にさせていただいています。


動作確認は、WinXP + CS4、WinXP + CS5.5、Win7 + CS5.5、MacOSX 10.7.5 + CS3、MacOSX 10.5.8 + CS3、MacOSX 10.5.8 + CS4で行いました。Macでの動作確認にご協力いただいたお二方、ありがとうございました! 遅れてすいません!


■使用前の確認事項

  • なにが起きても泣かないようにデータのバックアップを取りながら使用してください(最重要)。
  • インターネットへアクセスできる環境が必要です。
  • Yahoo!の「日本語形態素解析API」を使用しています。サービスが終了した場合、スクリプトも使用できなくなります。
  • 選択中の文字列をインターネット経由でYahoo!のサービスへ送信します。機密情報などが漏洩した場合でも責任は持ちません。
  • アクセスログなどを見れば、どんな文字列を送信したか誰でもわかります。
  • 記号類は読み仮名から削除されます。


■使用中の注意事項

  • APIが期待通りの読み仮名を返してくるとは限らないので、必ず1件ずつ確認しながら登録してください。
  • 登録直後は索引項目のページ数が表示されません。索引パネルのメニューから[プレビューを更新]を実行すると直ります。
  • 索引登録時、ドキュメントの最初のページ付近にダミーのテキストフレームが作られます。通常は自動的に削除されますが、スクリプトが途中で止まった場合などに残る可能性があります。ひととおり登録したら、フレームが残っていないか確認してください。あったら消してね。
  • なにが起きても泣かないようにデータのバックアップを取りながら使用してください(念押し)。
// 索引登録支援ツール(InDesign CS3〜)// Yahoo!のテキスト解析APIを利用して索引の読み仮名入力を自動化します。// http://developer.yahoo.co.jp/webapi/jlp///// 以下の記事をパク^H^H大いに参考にさせていただいています。// kmutoさん// via http://d.kmuto.jp/20120912.html// CLさん// via http://d.hatena.ne.jp/C_L/20081012/indesign_socket_http//// v0.9 2013/04/03// v1.0 2013/06/03  デフォルトスタイルの初期化処理追加、自分で使用開始// v1.1 2013/06/17  初期化処理をやめてダミー文字のサイズだけ指定する形に変更// // NYSL http://www.kmonos.net/nysl/// ==============================================================================


main();


// メインの処理function main(){//Yahoo!APIのアプリケーションIDvar myAppID = "◆◆◆ココにアプリケーションIDを書く◆◆◆";

  // 選択状態チェック(テキストオブジェクトを選択してる状態のみ動作)if(app.selection.length == 0 || !app.selection[0].constructor.name.match(/^(Text|Word|Character|Paragraph|Line|TextColumn|TextStyleRange)$/)){alert("索引登録可能なテキストを選択してください。");
    returnfalse;
  }var actDoc = app.activeDocument;
  var idx = (actDoc.indexes.length > 0) ? actDoc.indexes[0] : actDoc.indexes.add();

  var targ = app.selection[0]; // 選択中のテキストvar title = targ.contents;   // 索引項目になる文言// APIリクエストvar yapiObj = new YAPIReading();
  yapiObj.appid = myAppID;
  var reading = yapiObj.getReading(title); // 読みがなになる文言// 表示ダイアログ準備// ダイアログで読みがなを修正可能var dlg = createDialog();
  dlg.tf.text = title;   // 索引項目欄に入力
  dlg.rf.text = reading; // 読みがな欄に入力// 読みがなが空文字(APIがエラー返してきてる)だったらメッセージを上書きするif(reading == ""){ dlg.info.text = "読みがなの自動取得に失敗しました。直接入力してください。"; }// ダイアログ表示から登録実行// 登録ボタンを押すと、その時点の読みがな欄のテキストを読みがなとして登録if(dlg.show() == 1){
    title = dlg.tf.text;
    reading = dlg.rf.text;

    try{
      embedIndex(idx, targ, title, reading); // 登録実行}catch(e) {alert("登録に失敗しました。\n索引項目または読みがなに使えない文字がないか確認してください。");
      arguments.callee();
    }returntrue;
  }returnfalse; // 登録しなかったらfalse返すことにしておく(なんとなく)}// ***************************************************************************//// 以下、関数・オブジェクト定義など//// ***************************************************************************// 索引を追加する関数 ********************************************************// kmutoさんのアイディア(マーカーのコピペ)を拝借// via http://d.kmuto.jp/20120912.html// ***************************************************************************function embedIndex(index, target, title, reading) {// ダミーのテキストフレームを作って★マークとか入れておくvar dummyFrame = index.parent.pages[0].textFrames.add();
  dummyFrame.geometricBounds = [0, 0, 50, 50]; // サイズは適当
  dummyFrame.insertionPoints[0].pointSize = 1; // 文字あふれ対策にサイズを小さくしておく
  dummyFrame.contents = "★";

  // 項目追加// ダミーの★マークのところに索引マーカーを入れる
  index.topics.add(title, reading).pageReferences.add(dummyFrame.characters[0]);

  // マーカー文字をカット&ペーストして正しい位置に移動
  dummyFrame.characters[0].select();
  app.cut();
  target.insertionPoints[0].select();
  app.pasteWithoutFormatting(); // フォーマットなしでペースト// ダミーのフレームを始末
  dummyFrame.remove();
}// ダイアログオブジェクトを作って返す関数 ************************************// あとで部品にアクセスしやすいようにショートカット作ってある// dlg.tf   : 項目入力欄// dlg.rf   : 読みがな入力欄// dlg.info : 情報欄// ***************************************************************************function createDialog(){var dlg = new Window("dialog", "索引登録");
  dlg.orientation = "row";
  dlg.alignChildren = "top";

  var inputG = dlg.add("group");
  inputG.orientation = "column";
  inputG.alignChildren = "left";
  var infoLabel = inputG.add("statictext", undefined, "読みがなを修正してください。キャンセルすると登録を中止します。");

  var inputTitle = inputG.add("group");
  inputTitle.orientation = "row";
  var titleLabel = inputTitle.add("statictext", undefined, "索引項目:");
  var titleField = inputTitle.add ("statictext", undefined, undefined);
  titleLabel.characters = 9;
  titleField.characters = 35;

  var inputReading = inputG.add("group");
  inputReading.orientation = "row";
  var readingLabel = inputReading.add("statictext", undefined, "読みがな:");
  var readingField = inputReading.add ("edittext", undefined, undefined);
  readingLabel.characters = 9;
  readingField.characters = 35;

  var buttonG = dlg.add("group");
  buttonG.orientation = "column";
  buttonG.add("button", undefined, "登録", {name: "ok"});
  buttonG.add("button", undefined, "キャンセル", {name: "cancel"});

  // ショートカット定義
  dlg.tf = titleField;
  dlg.rf = readingField;
  dlg.info = infoLabel;

  return dlg;
}// 読みがな取得用クラス定義 **************************************************// アプリケーションIDはインスタンス側で設定する//// var hoge = new YAPIReading();      // インスタンス作成// hoge.appid = "★★アプリケーションID★★"; // 自分のアプリケーションIDを指定する// var title = "僕の妹は漢字が読める";     // 読みたい文言// var reading = hoge.getReading(title);     // 解析結果を取得//// ってする// 記号類はfilterで除去。これも変更可能// 読みがな部分の抽出はテキストそのまま正規表現でぶっこぬき。XML解析なにそれおいしいの// ***************************************************************************function YAPIReading(){this.appid    = "";
  this.filter   = "1|2|3|4|5|6|7|8|9|10|11|12";
  this.response = "reading";
  this.results  = "ma";

  this.rex = /<reading>(.*?)<\/reading>/g; // <reading>要素を見つける正規表現(gオプション付き)this.getReading = function(sentence){var reading = "";

    var requestURI = 'http://jlp.yahooapis.jp/MAService/V1/parse?'
                   + 'appid=' + this.appid
                   + '&ma_filter=' + this.filter
                   + '&response='  + this.response
                   + '&results='   + this.results
                   + '&sentence='  + encodeURI(sentence);

    var lwp = new Lwp();
    var result = lwp.get(requestURI);

    // gオプション付きRegExpオブジェクトのexecループ// <reading>要素を見つけるたびに中身のテキストを足していくvar m;
    while (m = this.rex.exec(result)) {
      reading += m[1];
    }return reading; // リクエストに失敗した場合はreadingタグがないので空文字になってるはず}returnthis;
}// HTTPアクセス(GET)用クラス定義 *******************************************// CLさんのモジュールを微改造// via http://d.hatena.ne.jp/C_L/20081012/indesign_socket_http// prototypeをやめてUser-Agentをそれっぽくしただけ// ***************************************************************************function Lwp() {this.userAgent = "InDesign/" + app.version + " (InDesign " + app.version + "; " + $.os + "; ja)";
  this.uri = function(uri) {var rex = newRegExp('http://([^:/]+)(?::(\d+))?(.+)');
    // via http://pc11.2ch.net/test/read.cgi/php/1015692614/57var urlObj =[];
    if ( uri.match(rex) ) {
      urlObj.host = RegExp.$1;
      urlObj.port = RegExp.$2 ?RegExp.$2 :80;
      urlObj.path = RegExp.$3;
    }return urlObj;
  }this.get = function (uri) {var conn = new Socket;
    var urlObj = this.uri(uri);
    if ( conn.open(urlObj.host + ':' + urlObj.port, 'UTF-8') ) {
      conn.write ("GET " + urlObj.path + " HTTP/1.0\n" + "Host: " + urlObj.host + "\n" + "User-Agent: " + this.userAgent + "\n\n");
        var reply = conn.read(999999);
        conn.close();
      return reply.substring(reply.indexOf("\n\n") + 2);
    }}returnthis;
};

JavaScriptの条件演算子(三項演算子)

$
0
0

PCの中身を整理したので、お蔵出しシリーズと称して、昔自分のために書いた文章を供養していこうと思います。たぶんあんまり続かない。
今回は2008年2月に書いたらしいものに加筆修正しました。

基本(条件演算子の記法)

if(条件式) {条件式が真(true)の場合の処理
}else{条件式が偽(false)の場合の処理
}

っていう文と同じ処理をする式を、

(条件式) ? 条件式が真の場合に評価する式 : 条件式が偽の場合に評価する式

と書くことができる。


?:を条件演算子という。JavaScriptでは唯一、項(被演算子)が三つの演算子なので、三項演算子とも呼ばれる。


条件式のカッコ()は可読性のために入れただけで、必須ではない。同じくスペースも必須ではない。

  • それぞれの項はすべて式である
  • 全体が一つの式になっている
  • 式なので全体をカッコ()でくくってもよい

という点に注目。といってもこれは演算子が作る式ならすべてに当てはまるのだけど。

条件式 ?
条件式が真の場合に評価する式 :
条件式が偽の場合に評価する式

とかなんとか、途中で改行してもOK。

式の評価結果

全体で一つの式になっているので、全体で一つの値を返す。
返す値は最後に評価された式の値になる。


たとえば、変数aに(xの値に応じて)bまたはcを代入したい場合、if文を使うとこんな風になる。

x = 10;
if(x == 10) {
  a = b;
}else{
  a = c;
}// 結果:aにbが代入される

これを省略して

x = 10;
(x == 10) ? a = b : a = c;
// 結果:aにbが代入される

と書けて、さらに省略して

x = 10;
a = (x == 10) ? b : c;
// 結果:aにbが代入される

と書ける。
演算子の優先順位にもとづき、代入演算子=より条件演算子?:のほうが先に評価されるので、「(x == 10) ? b : c」という式の値がaに代入される。


最後の書き方は個人的によく使ってる。変数に代入するという目的が目に見えやすい気がするので。


あとは、たとえば「引数が条件に合うかどうかを真偽値で返す関数」が

function hoge(huga) {return (huga == piyo) ? true : false; }

とか書けたりする。
if文が増えないからすっきり。でもこっちはやり過ぎるとたぶんあとで読めない。

応用1:入れ子にもできる

全体が式なので、入れ子にもできる。たとえば

if(x < 10) {
  y = a;
}elseif(x < 20) {
  y = b;
}elseif(x < 30) {
  y = c;
}else{
  y = d;
}

は、

y = (x < 10) ? a : (x < 20) ? b : (x < 30) ? c : d;

と書ける。改行もできるので

y = (x < 10) ? a :
    (x < 20) ? b :
    (x < 30) ? c :
    d;

とか書いてもいい。
素直にifかswitchで書いたほうが、条件分岐ってことがわかりやすくていいと思う。人の書いたコードで見つけたときに面食らわない程度におぼえておく。

応用2:配列/ハッシュの添え字にもできる

くどいようだが式なので、配列やハッシュの添え字にもできる。

var arr = newArray("あ","い","う","え");
var x = 10;

alert(arr[(x==10)?1:3]); // 結果:「い」

使う必要ないと思う。役に立つのはブックマークレットを作るときくらいかな。

JavaScriptのループ文まとめ

$
0
0

お蔵出しシリーズ2。2008年2月に書いたらしいものに加筆したら原型がなくなりました。

ループ文とは

通常は上から下に処理が進んでいくプログラムを、一定条件に当てはまるあいだだけ繰り返し処理する制御文。

for文

初期値、条件、増減式を設定して、条件が真であるあいだ、一定の処理を繰り返す命令。
正確に言うと条件が偽になったら処理をしないでループを抜ける、ってことになる。


ループ用の変数に初期値(数値)を代入して、それを増減してループ回数を制御する。なので、主にループ回数が開始前に設定できる場合に使う。

for( 式1 ; 式2 ; 式3 ) {繰り返したい処理
}
式1
初期値を設定する式。ループ開始前に一度だけ評価される。
式2
条件式。毎回のループの最初に評価され、もしも偽(false)だったら{}内の処理を行わずにループを抜ける。
式3
ループ用の変数の値を増減する式。後処理。毎回のループの最後に評価される。式2が偽になった場合はその時点でループを抜けてしまうので、この式は実行されない。


ちなみに、式1と式3には複数の式を書くことができる。その場合は式をカンマ , で区切ること。*1
例えば「配列arrの要素を順番にすべて書き出す」みたいな処理をしたい場合、

var arr = [10, 20, 30, 40];
for( var i = 0 ; i < arr.length ; i++ ) {alert( arr[i] );
}// 結果:「10」「20」「30」「40」

って書いてもいいんだけど、これだと式2にあたる「 i < arr.length 」の評価時にいちいち配列の要素数を調べることになる。

var arr = [10, 20, 30, 40];
for( var i = 0 , arr_len = arr.length ; i < arr_len ; i++ ) {alert( arr[i] );
}// 結果:「10」「20」「30」「40」

こうすれば、要素数を調べる処理はループ開始前の一回だけで済む。ループ回数が最初に固定できる場合は、採用すると処理が早くなるかも。

while 文

条件を設定し、それが真である間、一定の処理を繰り返す命令。

while( 条件式 ) {繰り返したい処理
}

初期値の設定や後処理がないので、ループ内で条件を変更するか、break(後述)とかでループを抜ける必要がある。でないと無限ループになる。

while(true) {
  breakがなければ無限ループされる処理
}


forのところで書いた「配列arrの要素を順番にすべて書き出す」処理をwhile文でやる場合、

var arr = [10, 20, 30, 40];
var i = 0, arr_len = arr.length;
while( i < arr_len ) {alert( arr[i] );
  i++;
}// 結果:「10」「20」「30」「40」

って書くと、for文とまったく同じことができる。


もうちょっと抽象化すると、

for( 式1 ; 式2 ; 式3 ) {繰り返したい処理
}

式1;
while( 式2 ) {繰り返したい処理
  式3;
}

が同じということ。
でもwhileでこれをやるくらいなら素直にfor使えばいいと思う。


whileの場合、たとえばarrを破壊してしまってもいいなら以下のようなことができる。

var arr = [10, 20, 30, 40];
while( arr.length ) {alert( arr.shift() );
}// 結果:「10」「20」「30」「40」

Array.shift()は配列から最初の要素を取り除き、その要素を返す。
ループのたびに配列arrから要素がひとつずつ減っていって、要素がなくなったら次のループ開始時には条件式「 arr.length 」が0(真偽値に変換するとfalse)になるので、ループを抜ける。
複数行にわたる文字列を取得して、一行ずつ最後まで処理したい場合なんかに使える。

do-while 文

while文と似てるけど、これは一度は必ず一定の処理を行い、その後、条件が真のあいだだけ同じ処理を繰り返す場合に使う命令。

do{一度は必ず行うけど、もしかしたら繰り返したいかもしれない処理
}while( 条件式 )

for文やwhile文の場合、条件式が最初からfalseだったらその時点でループを抜けてしまうので、{}内の処理は一度も行われない。
do-while文では、条件式は{}内の処理が行われた後に評価される。なので条件式の結果がどうであっても、とりあえず一度は必ず処理が行われる。

つっても、今までこれ使う機会は一度もなかったんだが……

break文、continue文

ループの途中で処理の流れを中断したい場合に使う命令文。
breakの場合は処理を中断し、直ちにループを抜け、次の処理に移る。

for( var i = 0 ; i < 10 ; i++ ) {if( i==5 ) {break; // forループを脱出alert( "ここは処理されない" );
  }alert( "i = " + i );
}alert( "ループ外 i = " + i );
// 結果:「i = 0」「i = 1」「i = 2」「i = 3」「i = 4」「ループ外 i = 5」

breakでループを抜けた場合、ループ終了時の処理(「i++」の部分)は行われていない。

continueの場合は、直ちにその回のループ終了処理を行う

for( var i=0 ; i<10 ; i++ ) {if( i==5 ) {continue; // ループ1回分をここで終了するalert( "ここは処理されない" );
  }alert ( "i = " + i );
}alert( "ループ外 i = " + i );
// 結果:「i = 0」「i = 1」「i = 2」「i = 3」「i = 4」「i = 6」「i = 7」「i = 8」「i = 9」「ループ外 i = 10」

while文の場合は、条件の変更前にcontinueを置いてしまうと無限ループになるので注意。

for-in文

for-in文はfor文から派生した構文。
配列の各要素……というか、オブジェクトの各プロパティにアクセスして、同じ処理を繰り返す場合に使う。

var arr = [10, 20, 30, 40];
for( var i in arr ) {alert( arr[i] );
}// 結果:「10」「20」「30」「40」すべての要素を列挙

ちなみに i にはオブジェクトのプロパティ名(文字列)が入る。

var arr = [10, 20, 30, 40];
for( var i in arr ) {alert( i + " : " + typeof i );
}// 結果:「0 : string」「1 : string」「2 : string」「3 : string」

数値に見えるけど文字列。


例は配列で書いたけど、実はfor-inは配列に使わないほうがいい。
以下は理由。わからなければ「使っちゃいけないのねフーン」でいいと思う(よくないけど)。


for-in構文は「オブジェクトのプロパティ」にアクセスするので、例えば他のところでArrayオブジェクトが拡張された場合、そのプロパティまで列挙してしまう。

Array.prototype.hoge = function(){alert("ほげー"); }; // Arrayオブジェクトを拡張var arr = [10, 20, 30, 40];

for( var i in arr ) {alert( arr[i] );
}// 結果:「10」「20」「30」「40」「function(){ alert("ほげー"); }」最後のはhogeの中身が見えてる

オブジェクト(インスタンス)自身が持っているプロパティだけを列挙したいときは、都度hasOwnProperty()で判定すればOK。

Array.prototype.hoge = function(){alert("ほげー"); }; // Arrayオブジェクトを拡張var arr = [10, 20, 30, 40];

for( var i in arr ) {if(arr.hasOwnProperty(i)){alert( arr[i] );
  }}// 結果:「10」「20」「30」「40」hogeはarrが持っているプロパティじゃないのではじかれる
var arr = [10, 20, 30, 40];
arr.hoge = function(){alert("ほげー"); }; // arrに対してhogeメソッドを追加for( var i in arr ) {if(arr.hasOwnProperty(i)){alert( arr[i] );
  }}// 結果:「10」「20」「30」「40」「function(){ alert("ほげー"); }」hogeも列挙される

for-inのループはプロパティを定義した順番で行われる(正確には、プロパティに値を代入した順番)。
また、プロパティの値が定義されていないものは無視される。

var obj = newObject;
obj.age;               // 宣言だけはしたけど未定義(undefined)
obj.name = "権兵衛";
obj.age  = "20";       // ここで値を代入
obj.addr = "ちば";
obj.hobby;             // 宣言だけはしたけど未定義(undefined)for( var i in obj ) {alert( i + " : " + obj[i]);
}// 結果:「name : 権兵衛」「age : 20」「addr : ちば」hobbyは出てこない


疲れたのでこの辺で切り上げ。

*1:正確に言うと、式1〜式3のそれぞれが式であればいいので、複数の式をカンマ演算子でつないで一つの式にしているというだけ。式2も同じことができるけど、評価後の値によって分岐するので順番が大事になる。ループ毎に処理したいものは素直に式3に書いたほうがいい。

JavaScriptのArray.sort覚え書き

$
0
0

お蔵出しシリーズ3。いつだか忘れたけど昔書いて、2013年5月にリライトしたらしいものをほぼそのまま。どうでもいいけどこのシリーズ、タイトルを統一しようとする気持ちがまったく見受けられませんね。

基本

array.sort(比較関数);

配列arrayの要素を、比較関数(省略可)に基づいて並べ替える。並べ替えは破壊的に行われる、つまり配列array自体が並べ替えられて、元の順番は保存されない。

比較関数を省略した場合

配列arrayの各要素を文字列として比較し、辞書順に並べ替える。

たとえば以下のように処理される。

var array = [10,9,5,15,60];
array.sort();

alert(array); // [10,15,5,60,9] ←数値順ではないalert(typeof array[0]); // number ←要素としてはnumberのまま

数値順に並べ替えたい場合、あるいはもっと複雑な比較を行って並べ替えたい場合は、比較関数の指定が必要。

比較関数

比較関数には2つの要素が引数として渡され、戻り値に応じてその要素の順序が入れ替えられる。仮に比較関数の第一引数をa、第二引数をbとしたとき、

戻り値 < 0aが前に来るように並べ替えられる
戻り値 == 0保証なし(順序を入れ替えないことになっている)
戻り値 > 0bが前に来るように並べ替えられる

という処理が行われる。

function compare(a, b){if(aがbより小) {return -1; // 負の数であればなんでもいい}if(aがbより大) {return 1;  // 正の数であればなんでもいい}return 0;
}

array.sort(compare);

compareを別に定義せず、sortメソッド実行時に指定してもよい。無名関数でもOK。

array.sort(function(a, b){if(aがbより小) {return -1; }// 負if(aがbより大) {return 1;  }// 正return 0;
});

要するに、「こういう基準で要素を並べ替えてね」というのをsortメソッドに教えるためのもの。

例:数値の配列を数値として小さい順に並べ替えたいとき
var array = [10,9,5,15,60];

array.sort(function(a, b){return a - b; });

alert(array); // [5,9,10,15,60] ←数値順

aとbどちらも数値であれば減算できる。aがbより小さければ式「a - b」の結果は負の数になるので、そのまま戻り値にしている。

例:オブジェクトの配列を、各オブジェクトのプロパティ「value」の値が大きい順に並べ替えたいとき
array.sort(function(a, b){if(a.value > b.value) {return -1; }// a.valueが大きければ負if(a.value < b.value) {return 1;  }// a.valueが小さければ正return 0;
});

これだけ覚えろ


aをbよりに置きたいときは戻り値をにする

parseIntとparseFloatの覚え書き

$
0
0

前フリが思いつかない件について。
この記事の主旨は一言で言うと「parseIntには第2引数で基数を指定しよう」です。

基本

parseIntとparseFloatは、どちらも引数として渡された文字列をパースして数値に変換するグローバル関数。
parseIntは整数、parseFloatは小数と整数の両方(浮動小数点数)を扱える。

parseInt("100")    // 100
parseInt("3.14")   // 3

parseFloat("100")  // 100
parseFloat("3.14") // 3.14

パース方法とNaN

parseIntとparseFloatは、どちらも引数の文字列を先頭から1文字ずつ確認し、数値と解釈できない文字が見つかった時点で、その直前までの部分を数値として返す。
一文字も数値にできなかった場合はNaN(数値ではないことを表す数値型の値)を返す。

わかりにくいので例を。

parseInt("100pt")     // 100
parseInt("-3.14pt")   // -3
parseInt("314e-2")    // 314
parseInt("hoge123")   // NaN

parseFloat("100pt")   // 100
parseFloat("-3.14pt") // -3.14
parseFloat("314e-2")  // 3.14
parseFloat("hoge123") // NaN

parseFloatが数値と解釈できるのは、正負符号(+/-)、0〜9の数字、小数点、指数(e)。
parseIntはそこから小数点を除いたもの……ではなくて、実はもうちょっと複雑。

parseIntの第2引数(基数)

parseIntの引数として、パースしたい文字列のほかに、「何進数として解釈するか」という基数を渡すことができる。
基数は文字列でなくて数値で渡すことに注意。

parseInt("15", 10) // 15
parseInt("15", 8)  // 13
parseInt("15", 16) // 21

同じ"15"という文字列を、10進数、8進数、16進数でパースした値が返ってくる。

基数の指定によって、数として解釈できる文字は変わってくる。ちょっと複雑と言ったのはこのこと。

parseInt("19e", 10) // 19("e"はパース不可)
parseInt("19e", 8)  // 1 ("9e"はパース不可)
parseInt("19e", 16) // 414

基数を省略した場合、通常は10進数でパースされる。のだけど、省略するとけっこう危険だったりする。

基数を省略した場合

もしくは、基数に0を指定した場合、自動的に以下のように解釈されることが多い。

  • "0x"または"0X"で始まる文字列が渡された場合、基数は16(16進数)と解釈される。
  • "0"で始まる文字列が渡された場合、基数は8(8進数)と解釈される。
  • その他の文字で始まる文字列が渡された場合、基数は10(10進数)と解釈される。
parseInt("0x15") // 21(16進数として解釈)
parseInt("015")  // 13(8進数として解釈)※実装による
parseInt("15")   // 15(10進数として解釈)


16進数はともかく、この8進数がなかなか厄介で……

parseInt("015")  // 13(8進数)※実装による
parseInt("019")  // 1 (8進数、"9"はパース不可)※実装による
parseInt("08")   // 8 (8進数だとNaNになるので10進数で解釈)※実装による


あるでしょう、ノンブルとかで先頭をゼロで埋めてるやつ。
それをうっかりparseInt(textframe.contents)なんてやってしまうと、ページによって変な数値が返ってくる可能性があるということ。

余談

先頭の文字列が"0x"なら16進数、というのは、JavaScript(というか、ECMAScript)の仕様として規定されている。しかし実は先頭が"0"の場合は、「8進数にしてもいいよ(どっちかいうと10進数でやってね)」ということになっていた。
実装では8進数を採用したものが多いけど、確実ではないらしい。つまり実行する環境によって結果がまちまちということ。さっきから※実装によるって書いてるのはそういうことです。

いちおう現在の最新の仕様では、"0x"と"0X"を16進数とするのを除けばデフォルトは10進数、と定められている。けど実装のほうは今のところほとんど追いついてないらしい。AdobeのESTKでも8進数になるしね。

というわけで、この記事の主旨


parseIntには第2引数で基数を指定しよう


大事なことなので2回いいました。ちなみにparseFloatは常に10進数で、基数指定はできません。

PhotoshopドキュメントをJavaScriptで別名保存する

$
0
0

たまにはアプリケーションに特化したことを書こうのコーナーです(今考えた)。

いわゆる「別名保存」はDocment.saveAs()メソッドを使うんだけど、オプション類をいちいち調べるのが嫌になったのでまとめます。
なお、以下はPhotoshop CS6 Javascript Scripting Reference(リンク先PDF)を信用して書いたものです。ESTK付属のオブジェクトモデルビューアよりずっと使いやすいのでおすすめ。

Document.saveAs() 書式

Document.saveAs(saveIn[, options][, asCopy][, extensionType]);
引数説明
saveIn File 必須。保存先になるFileオブジェクト。ここで拡張子つけても無視してpsd形式にされる。他の形式にしたい場合はオプションで指定する必要がある。
options varies
(いろいろ)
保存オプション。ファイル形式、および保存時のオプションを指定する。ファイル形式ごとにオブジェクト(クラス)が異なる。詳しくはあとで。
asCopy boolean 複製として保存するかどうか。オプション。
extensionTypeExtension 拡張子の書式。オプション。

Extension.LOWERCASE 小文字
Extension.NONE 拡張子なし
Extension.UPPERCASE 大文字

保存先Fileオブジェクト

var fileobj = new File("ファイルパス");

とかして作ればいいんじゃないですかね(投げやり)。

保存オプション

オプションは保存形式ごとに別々のクラスが用意されている。ふつうにGUIで保存するときもそうだけど、形式によってオプションがまるで違うからだと思う。
例えばjpg形式なら、

var saveOptions = new JPEGSaveOptions;
saveOptions.embedColorProfile = true;
saveOptions.formatOptions = FormatOptions.STANDARDBASELINE;
saveOptions.quality = 12;
// 他、各プロパティを設定

app.activeDocument.saveAs(fileobj, saveOptions, false, Extension.LOWERCASE)

って感じでオブジェクトを作ってから、saveAs()実行時に引数として渡す。

とりあえず順番にいきましょう。これ使うの?ってのもあるけど。

BMP

BMPSaveOptions

プロパティ説明
alphaChannels boolean アルファチャンネルを保持するかどうか。たぶん32bitのときだけ。
depth BMPDepthType ビット数。

BMPDepthType.ONE 1bit
BMPDepthType.FOUR 4bit
BMPDepthType.EIGHT 8bit
BMPDepthType.SIXTEEN 16bit
BMPDepthType.TWENTYFOUR 24bit
BMPDepthType.THIRTYTWO 32bit

詳細モードだともっとあるけど略
flipRowOrder boolean 行の順序の反転。osTypeがWindowsのときだけ動作。
osType OperatingSystemファイル形式。

OperatingSystem.OS2 OS/2
OperatingSystem.WINDOWSWindows
rleCompressionboolean RLE圧縮するかどうか。osTypeがWindowsでdepthが4bitか8bitのときだけ動作。
typename string オブジェクトのクラス名。readonly。"BMPSaveOptions"
DCS 1.0

DCS1_SaveOptions

プロパティ説明
dCS DCSType プレビュー用ファイルの形式。

DCSType.COLORCOMPOSITE カラープレビュー
DCSType.GRAYSCALECOMPOSITE グレースケールプレビュー
DCSType.NOCOMPOSITE プレビューなし
embedColorProfileboolean カラープロファイルを埋め込むかどうか。
encoding SaveEncodingエンコーディング形式。

SaveEncoding.ASCII ASCII
SaveEncoding.BINARY バイナリ
SaveEncoding.JPEGLOW JPEG(低画質)
SaveEncoding.JPEGMEDIUM JPEG(標準画質)
SaveEncoding.JPEGHIGH JPEG(高画質)
SaveEncoding.JPEGMAXIMUM JPEG(最高画質)
halftoneScreen boolean ハーフトーンスクリーンを保持するかどうか。
interpolation boolean プレビューに画像補間方式(バイキュービック)を適用するかどうか。
previewPreviewEPSプレビュー形式。

Preview.EIGHTBITTIFF 8bitTIFF
Preview.MACOSEIGHTBIT Mac 8bitTIFF
Preview.MACOSJPEG MacJPEG
Preview.MACOSMONOCHROME Mac 1bitTIFF(モノクロ)
Preview.MONOCHROMETIFF 1bitTIFF(モノクロ)
Preview.NONE なし
transferFunction boolean トランスファー関数を保持するかどうか。
typename string オブジェクトのクラス名。readonly。"DCS1_SaveOptions"
vectorData boolean ベクターデータを保持するかどうか。
DCS 2.0

DCS 1.0のプロパティに加えて、以下のプロパティがある。typenameは当然"DCS2_SaveOptions"になる。
DCS2_SaveOptions

プロパティ説明
multiFileDCSbooleanカラーチャンネルを複数ファイルとして出力するかどうか。falseで単一ファイルになる。
spotColors boolean特色チャンネルを保持するかどうか。
EPS

ほとんどDCSとかぶってるけど改めて全部書いておく。
EPSSaveOptions

プロパティ説明
embedColorProfileboolean カラープロファイルを埋め込むかどうか。
encoding SaveEncodingエンコーディング形式。

SaveEncoding.ASCII ASCII
SaveEncoding.BINARY バイナリ
SaveEncoding.JPEGLOW JPEG(低画質)
SaveEncoding.JPEGMEDIUM JPEG(標準画質)
SaveEncoding.JPEGHIGH JPEG(高画質)
SaveEncoding.JPEGMAXIMUM JPEG(最高画質)
halftoneScreen boolean ハーフトーンスクリーンを保持するかどうか。
interpolation boolean プレビューに画像補間方式(バイキュービック)を適用するかどうか。
previewPreviewEPSプレビュー形式。

Preview.EIGHTBITTIFF 8bitTIFF
Preview.MACOSEIGHTBIT Mac 8bitTIFF
Preview.MACOSJPEG MacJPEG
Preview.MACOSMONOCHROME Mac 1bitTIFF(モノクロ)
Preview.MONOCHROMETIFF 1bitTIFF(モノクロ)
Preview.NONE なし
psColorManagementboolean PostScriptカラーマネジメントを有効にするかどうか。
transferFunction boolean トランスファー関数を保持するかどうか。
transparentWhitesboolean 白色部分を透明として扱うかどうか。モノクロ二階調時(document.modeがDocumentMode.BITMAPのとき)のみ有効。
typename string オブジェクトのクラス名。readonly。"EPSSaveOptions"
vectorData boolean ベクターデータを保持するかどうか。
GIF

GIFSaveOptions

プロパティ説明
colors number 表示色数。
dither Dither ディザ方式。

Dither.DIFFUSION 誤差拡散法
Dither.NOISE ノイズ
Dither.NONE なし
Dither.PATTERN パターン
ditherAmount number ディザの適用量(1〜100)。誤差拡散法の適用時のみ有効。
forced ForcedColors使用しているかどうかに関わらず強制的にパレットに含む色。
ForcedColors.BLACKWHITE 白黒
ForcedColors.NONE なし
ForcedColors.PRIMARIES レッド、グリーン、ブルー、シアン、マゼンタ、イエロー、黒、白
ForcedColors.WEB Webセーフカラー
interlaced boolean インターレースを有効にするかどうか。
matte MatteType マット。透明部分(不透明度が100%でない箇所)の背景色を塗りつぶす色の設定。transparencyプロパティがtrueの場合は完全な透明部分(不透明度0%の箇所)には適用されない。

MatteType.BACKGROUND 背景色
MatteType.BLACK 黒
MatteType.FOREGROUND 描画色
MatteType.NETSCAPEネットスケープグレー
MatteType.NONE なし
MatteType.SEMIGRAY 50%グレー
MatteType.WHITE 白
palette PaletteType パレットの設定。

PaletteType.EXACT 使用中の色をすべて割り付ける
PaletteType.LOCALADAPTIVE ローカル(割り付け)
PaletteType.LOCALPERCEPTUAL ローカル(知覚)
PaletteType.LOCALSELECTIVE ローカル(特定)
PaletteType.MACOSPALETTE Mac
PaletteType.MASTERADAPTIVE マスター(割り付け)
PaletteType.MASTERPERCEPTUAL マスター(知覚)
PaletteType.MASTERSELECTIVE マスター(特定)
PaletteType.PREVIOUSPALETTE 前回と同じ
PaletteType.UNIFORM ?
PaletteType.WEBPALETTE Web
PaletteType.WINDOWSPALETTE Windows
preserveExactColorsboolean 同一色の保持。ディザの設定が誤差拡散法の場合、カラーテーブルに含まれる色を保護するかどうか。
transparency boolean 透明箇所を維持するかどうか。
typename string オブジェクトのクラス名。readonly。"GIFSaveOptions"
JPEG

JPEGSaveOptions

プロパティ説明
embedColorProfileboolean カラープロファイルを埋め込むかどうか。
formatOptions FormatOptionsJPEGフォーマット。

FormatOptions.OPTIMIZEDBASELINE ベースライン(最適化)
FormatOptions.PROGRESSIVEプログレッシブ
FormatOptions.STANDARDBASELINE ベースライン(標準)
matte MatteType マット。透明部分(不透明度が100%でない箇所)の背景色を塗りつぶす色の設定。

MatteType.BACKGROUND 背景色
MatteType.BLACK 黒
MatteType.FOREGROUND 描画色
MatteType.NETSCAPEネットスケープグレー
MatteType.NONE なし
MatteType.SEMIGRAY 50%グレー
MatteType.WHITE 白
quality number 画質。1〜12の数値。
scans number プログレッシブ形式のスキャン数(完全に表示されるまでの段階の数)。3〜5の数値。
typename string オブジェクトのクラス名。readonly。"JPEGSaveOptions"
PDF

PDFSaveOptions
多すぎ。プリセット作って適用するのがいいと思う。

プロパティ説明
alphaChannels boolean アルファチャンネルを保持するかどうか。
annotations boolean 注釈を保持するかどうか。
colorConversion boolean カラー変換するかどうか。
convertToEightBit boolean 16bit画像を8bit画像に変換するかどうか。
description string 保存オプションの説明文。
destinationProfile string 出力先カラープロファイル。文字列で指定。
downgradeColorProfile boolean (非推奨)
downSample PDFResample ダウンサンプル方式。

PDFResample.NONE なし
PDFResample.PDFAVERAGE ダウンサンプル(バイリニア法)
PDFResample.PDFBICUBIC ダウンサンプル(バイキュービック法)
PDFResample.PDFSUBSAMPLE サブサンプル(ニアレストネイバー法)
downSampleSize number 画像ダウンサンプル後の解像度。ppiで指定。
downSampleSizeLimit number 画像ダウンサンプルを行う対象になる解像度(この値を超えたら適用)。ppiで指定。
embedColorProfile boolean カラープロファイルを埋め込むかどうか。
embedFonts boolean (非推奨)
embedThumbnail boolean サムネイルを埋め込むかどうか。
encoding PDFEncoding 画像の圧縮形式。

PDFEncoding.JPEGJPEG(画質を数値で指定可能)
PDFEncoding.JPEG2000HIGH JPEG2000最高
PDFEncoding.JPEG2000LOSSLESS JPEG2000ロスレス
PDFEncoding.JPEG2000LOW JPEG2000最低
PDFEncoding.JPEG2000MED JPEG2000
PDFEncoding.JPEG2000MEDHIGH JPEG2000
PDFEncoding.JPEG2000MEDLOW JPEG2000
PDFEncoding.JPEGHIGH JPEG最高
PDFEncoding.JPEGLOW JPEG最低
PDFEncoding.JPEGMED JPEG
PDFEncoding.JPEGMEDHIGH JPEG
PDFEncoding.JPEGMEDLOW JPEG
PDFEncoding.NONE なし
PDFEncoding.PDFZIP ZIP8bit
PDFEncoding.PDFZIP4BIT ZIP4bit
interpolation boolean (非推奨)
jpegQuality number 画質。1〜12の数値。encodingプロパティがPDFEncoding.JPEGの時のみ有効。
layers boolean レイヤーを保持するかどうか。
optimizeForWeb boolean Web表示用に最適化するかどうか。
outputCondition string PDF/Xの出力条件。
outputConditionID string PDF/Xの出力条件ID。
PDFCompatibility PDFCompatibilityPDFの互換バージョン。

PDFCompatibility.PDF13 1.3
PDFCompatibility.PDF14 1.4
PDFCompatibility.PDF15 1.5
PDFCompatibility.PDF16 1.6
PDFCompatibility.PDF17 1.7
PDFStandard PDFStandard PDF/X規格。

PDFStandard.NONE なし
PDFStandard.PDFX1A2001 PDF/X-1a:2001
PDFStandard.PDFX1A2003 PDF/X-1a:2003
PDFStandard.PDFX32002 PDF/X-3:2002
PDFStandard.PDFX32003 PDF/X-3:2003
PDFStandard.PDFX42008 PDF/X-4:2008
preserveEditing boolean 編集機能を保持するかどうか。
presetFile string PDF書き出しプリセット(名前)。指定すると他のプロパティよりプリセットの設定が優先される。
profileInclusionPolicyboolean プロファイルを埋め込むかどうか。
registryName string PDF/Xのレジストリ名。
spotColors boolean 特色を保持するかどうか。
tileSize nunber 画像の圧縮形式がJPEG2000の場合のタイル数。encodingプロパティがPDFEncoding.JPEG2000の時のみ有効。
transparency boolean (非推奨)
typename string オブジェクトのクラス名。readonly。"PDFSaveOptions"
useOutlines boolean (非推奨)
vectorData boolean (非推奨)
view boolean 保存後にPDFを表示するかどうか。
PSD

PhotoshopSaveOptions

プロパティ説明
alphaChannels booleanアルファチャンネルを保持するかどうか。
annotations boolean注釈を保持するかどうか。
embedColorProfilebooleanカラープロファイルを埋め込むかどうか。
layers booleanレイヤーを保持するかどうか。
spotColors boolean特色を保持するかどうか。
typename string オブジェクトのクラス名。readonly。"PhotoshopSaveOptions"
PICTファイル

PICTFileSaveOptions

プロパティ説明
alphaChannels boolean アルファチャンネルを保持するかどうか。
compression PICTCompression 圧縮形式。

PICTCompression.JPEGHIGHPICT
PICTCompression.JPEGLOWPICT
PICTCompression.JPEGMAXIMUMPICT
PICTCompression.JPEGMEDIUMPICT
PICTCompression.NONE なし
embedColorProfileboolean カラープロファイルを埋め込むかどうか。
resolution PICTBitsPerPixelsbit数。

PICTBitsPerPixels.EIGHT
PICTBitsPerPixels.FOUR
PICTBitsPerPixels.SIXTEEN
PICTBitsPerPixels.THIRTYTWO
PICTBitsPerPixels.TWO
typename string オブジェクトのクラス名。readonly。"PICTFileSaveOptions"
PICTリソース

Photoshopでは読み込み専用(書き出せない)。
PICTResourceSaveOptions

プロパティ説明
alphaChannels boolean アルファチャンネルを保持するかどうか。
compression PICTCompression 圧縮形式。

PICTCompression.JPEGHIGHPICT
PICTCompression.JPEGLOWPICT
PICTCompression.JPEGMAXIMUMPICT
PICTCompression.JPEGMEDIUMPICT
PICTCompression.NONE なし
embedColorProfileboolean カラープロファイルを埋め込むかどうか。
name string PICTリソースの名前。
resolution PICTBitsPerPixelsbit数。

PICTBitsPerPixels.EIGHT
PICTBitsPerPixels.FOUR
PICTBitsPerPixels.SIXTEEN
PICTBitsPerPixels.THIRTYTWO
PICTBitsPerPixels.TWO
resourceID number PICTリソースのID。
typename string オブジェクトのクラス名。readonly。"PICTResourceSaveOptions"
PXR

PixarSaveOptions

プロパティ説明
alphaChannelsbooleanアルファチャンネルを保持するかどうか。
typename string オブジェクトのクラス名。readonly。"PixarSaveOptions"
PNG

PNGSaveOptions

プロパティ説明
compressionnumber 圧縮設定。0〜9の数値? デフォルトは0
interlaced booleanインターレースを有効にするかどうか。
typename string オブジェクトのクラス名。readonly。"PNGSaveOptions"
RAW

RawSaveOptions

プロパティ説明
alphaChannelsbooleanアルファチャンネルを保持するかどうか。
spotColors boolean特色を保持するかどうか。
typename string オブジェクトのクラス名。readonly。"RawSaveOptions"
SGIRGB

標準ではインストールされない形式らしい(知らない)。
SGIRGBSaveOptions

プロパティ説明
alphaChannelsbooleanアルファチャンネルを保持するかどうか。
spotColors boolean特色を保持するかどうか。
typename string オブジェクトのクラス名。readonly。"SGIRGBSaveOptions"
TGA(Targa)

TargaSaveOptions

プロパティ説明
alphaChannels boolean アルファチャンネルを保持するかどうか。
resolution TargaBitsPerPixels色数。

TargaBitsPerPixels.SIXTEEN 16bit
TargaBitsPerPixels.TWENTYFOUR 24bit
TargaBitsPerPixels.THIRTYTWO 32bit
rleCompressionboolean RLE圧縮するかどうか。
typename string オブジェクトのクラス名。readonly。"TargaSaveOptions"
TIFF

TiffSaveOptions

プロパティ説明
alphaChannels boolean アルファチャンネルを保持するかどうか。
annotations boolean 注釈を保持するかどうか。
byteOrder ByteOrder バイト順序。

ByteOrder.IBMIBM PC
ByteOrder.MACOS Macintosh
embedColorProfile boolean カラープロファイルを埋め込むかどうか。
imageCompression TIFFEncoding 圧縮形式。

TIFFEncoding.JPEGJPEG
TIFFEncoding.NONE なし
TIFFEncoding.TIFFLZW LZW
TIFFEncoding.TIFFZIP ZIP
interleaveChannelsboolean ピクセルの順序。trueでインターリーブになる。
jpegQuality number 画質。1〜12の数値。encodingプロパティがPDFEncoding.JPEGの時のみ有効。
layerCompression LayerCompressionレイヤーの圧縮形式。layersプロパティがtrueの時のみ有効。

LayerCompression.RLE RLE圧縮
LayerCompression.ZIP ZIP圧縮
layers boolean レイヤーを保持するかどうか。
saveImagePyramid boolean 画像ピラミッドを保存するかどうか。
spotColors boolean 特色を保持するかどうか。
transparency boolean 透明部分を保持するかどうか。
typename string オブジェクトのクラス名。readonly。"TiffSaveOptions"


以上!! つかれたー!!!!

Viewing all 46 articles
Browse latest View live