DTPab

印刷やデザイン、アドビ製アプリやスクリプトなど、雑多な技術ブログ

スクリプトもくもく会#5を開催しました(1)

2018年最初のもくもく会でしたが、おかげさまで大盛況のうちに終わりました。ご参加のみなさま、そして会場を貸してくださったYUIDEAさま、ありがとうございました。今年もゆる〜く続けていくつもりですので、よろしくお願いします。
さて、もくもく会でいただいた質問のまとめと、一部の方からいただいた宿題の回答、そして自分の資料の補足をしたいと思います。
思ったよりもだいぶ長くなってしまった&スクリプトの検証に時間がかかってしまっているので、2回に分けて更新します。

いただいた質問

スクリプト制作に参考になりそうなサイトや書籍は?

もくもく会#5の資料にもアップしましたが、こちらに再掲しておきます。

スクリプト制作の参考になるWebサイト

CS5

cs5.xyz

現在はフリーで自動組版インストラクターをなさっている、お〜まちさんのサイトです。僕自身、InDesignスクリプト制作においてもっともお世話になっているサイトの1つです。大変有用な、InDesignスクリプトで扱うことができるドキュメントオブジェクトモデル(DOM)の日本語版(一部英語)を公開されています。ほかにもたくさんのサンプルスクリプトを公開されています。

利用のすすめ

まずはDOMですが、CS〜CS5.5までのものと、CS6〜CC2018(13.0)までのものの2種類が用意されています。環境に合わせてDOMを俯瞰的に調べることが可能です。

InDesign DOM CS-CS5.5 について

InDesign DOM CS5-CC2015 について

あとはInDesign向けのサンプルスクリプトが用意されていますので、それをダウンロードし、処理の手順や書き方の参考にされると良いと思います。

ディザInDesign

また、今年4月から始まる予定のInDesignスクリプト初学者向け講習会についてはこちらのページから案内メールの登録が可能です。

InDesignで学ぶJavaScript教室 ~入門・基礎編~ | CS5

こういった講習会等の情報や、お〜まちさんご自身の近況等はブログにアップされますので、ブログもチェックしてみてください。RSSに対応しています。

ブログ | CS5

OpenSpace

Java Script/HTML5, iPhone/Android, ハイビジョン映像, 自動化関連:[OpenSpace]

Adobeスクリプトについて、たくさんのご著書を書かれている古籏さんのサイトです。「組版時間を半減する! InDesign自動処理実例集」は、InDesignスクリプトを作る者ならもはや知らない人はいないくらいの有名な書籍ですね*1。2014年刊「ExtendScript Toolkit(ESTK)基本編」や、2013年刊「Adobe JavaScriptリファレンス」などにも僕はお世話になっています。

利用のすすめ

僕自身は、前掲の「組版時間を半減する! InDesign自動処理実例集」とあわせ、InDesign自動化作戦に置いてある数々のInDesignスクリプト実例集の写経からスクリプトの勉強に入りました。

InDesign CC自動化作戦

見ていただくと分かるように、スクリプトの実例集以外にも、プログラミングとかJavaScriptとか演算子とかといった周辺の事柄についても説明があり、こういったものがスクリプトの理解を深めてくれると思います。

中綴製作所 DTPスクリプト研究室

nakatoji.lolipop.jp

昨年知った中綴さんのウェブサイトです。ExtendScriptの仕様・動作検証など、一歩踏み込んだ内容が素晴らしくとても勉強になります。まずはひとしきりスクリプトについて触れてみたあとで、こちらのサイトの記事に目を通すと良いと思います。

MDN

developer.mozilla.org

基本的にはWEB向けのJavaScriptになりますので注意が必要ですが(ExtendScriptでは動作しないメソッドなどが多々ある*2)、演算子やプリミティブ値、スコープなど、JavaScriptを構成する基本的な部分はExtendScriptでも同じです。こういったJavaScriptの根っこの部分は、正しい知識を仕入れたほうがいいと思います。

スクリプト制作の参考になる書籍

ExtendScript Toolkit(ESTK)基本編

f:id:uske_S:20180204131050j:plain
Twitterでも紹介した、古籏さんのご著書です。ESTKの基本的な使い方はもちろん、プログラミングやJavaScriptの基本からサポートしてくれる唯一の書籍だと思います。Kindle版のほうが少し安く、また全文検索やコピペもできておすすめです。

Adobe JavaScript リファレンス

f:id:uske_S:20180204132020j:plain
こちらも同じ古籏さんのご著書です。解説書ではなくリファレンスなので、ある程度スクリプトについて慣れてきてから開いてみてください。特にあてもなく開いて読むだけでも気づきがあったりして面白いです。特定のアプリケーションに限定せずに広く浅く網羅的に書かれたリファレンスで、実際にリファレンスとして使うもよし、適当に開いて斜め読みするもよしです。

開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

f:id:uske_S:20180204132213j:plain
スクリプト向けではなくWEB向けの書籍になりますが、ECMA Script 3.0準拠のJavaScriptの基本(仕様)とその応用について書かれています。ExtendScriptに慣れて、改めて記法や細かい仕様について知りたいと思って読むととても勉強になると思います。

オブジェクトスタイルのリンクを切断したいが、設定を残したまま切断するには?

さて、次にいただいた質問です。
段落スタイルや文字スタイルで同じことをする場合、例えばParagraph.appliedParagraphStyle = app.activeDocument.paragraphStyles[0]というように、段落スタイルなし・文字スタイルなしを指定すれば必然的にそうなります。それがオブジェクトスタイルだとしっかりオーバーライドまで解除され、オブジェクトスタイルパネルから[なし]をクリックしたようなことになるようですね(初めて知りました)。これをどうにか「設定値はそのままにオーバーライドとして保持してスタイルとのリンクを切れないか」というご質問でした。
そこで、オブジェクトスタイルのパネルメニューから「スタイルとのリンクを切断」を実行することと同義であろう考え、MenuAction(InDesignに用意されているメニューコマンドをスクリプトで制御するためのオブジェクト)を呼ぶことにしました。
ただここで厄介だったのは、単純に$ID/BreakLinkToStyle(「スタイルとのリンクを切断」のMenuActionオブジェクト)としてMenuActionを呼んでも、同じ名前のパネルメニューコマンドが複数存在するためうまく動作しなかったのです。段落・文字・オブジェクトスタイルのほかに、表・セルスタイルにも同じコマンドがあるからでした。そこで、以下のようなフローを考えました。

  1. MenuAction.areaプロパティを参照
  2. それがオブジェクトスタイルのパネルメニューであれば実行

コード自体は以下ですが、この「同じコマンドの判別」について何かもっとスマートなやり方をご存知の方がいらっしゃればご教示ください(ぜひにも)。

//オブジェクトスタイルとのリンクを切りたいオブジェクトを選択してから実行
//メニューアクションのareaとidをそれぞれ配列として取得しておく
var myArea = app.menuActions.itemByName("$ID/BreakLinkToStyle").area;
var myID = app.menuActions.itemByName("$ID/BreakLinkToStyle").id;

for (var i=0; i<myArea.length; i++){
    //areaがオブジェクトスタイルであれば、そのindexと同じidのメニューアクションを実行する
    if (myArea[i] === "パネルメニュー:オブジェクトスタイル"){
        app.menuActions.itemByID(myID[i]).invoke();
        }
    }

MenuAction.areaには、このように"パネルメニュー:〜〜スタイル"という形で文字列が入っています。これを参照してどのパネルメニューのコマンドなのかを判別しています。
もくもく会の終わり際、このコードを今度は「ドキュメント全体のオブジェクトに実行したい」という要望もいただきました。申し訳ないながら時間切れになってしまったので、宿題ということにさせてもらいました(後述)。この記事の末尾にありますので、ご参照ください!(遅くなってごめんなさい)

補足:メニューアクションの実行について

InDesignのメニューコマンドをスクリプトから実行するには、MenuActionオブジェクトを参照し、invokeメソッドを利用します。MenuActionオブジェクトの親はApplicationです。

MenuActionの指定には2つのやり方がありますが、その指定の際、そしてコマンドの実行の際に気をつけたいポイントがあります。まずMenuActionの指定方法です。

コマンド名による指定

ひとつは、ロケールされたコマンド名、もしくは先述のような$ID/で始まる特定の文字列を、MenuAction.itemByNameメソッドの引数として渡す方法です。
InDesignのコマンドには、前述のように「場所が違うが同じ名前」というコマンドがいくつもあります。これがひとつしかないもの(例えば「書き出し ...」はファイルメニューにしかありません)であればMenuActionがひとつに特定できますが、今回のように複数のパネルに同じコマンド名がいくつもある場合は注意が必要です。
今回のやり方がベストかどうかはわかりませんが、今回はMenuAction.areaを調べ、MenuAction.idを特定して実行するという方法を採りました。

IDによる指定

もうひとつの方法は、メニューアクションのIDを指定してMenuAction.itemByIDメソッドの引数として渡すものです。
IDを利用したメニューアクションの指定にも気をつけなければならないことがあります。それはMenuAction.idは流動的だということです。
スクリプトからMenuAction.idを利用して叩く場合、実行するドキュメントごとに、極端な場合は同じドキュメントでも実行するタイミングでidが変化します。なぜなら、メニューアクションの数自体が容易に増減するからです。メニューアクションを一覧で取得したりするとわかりますが、例えば段落スタイル名ひとつ、オブジェクトスタイル名ひとつがメニューアクションとして定義されています。言い方を変えると、スタイルが増えたり減ったりするだけでメニューアクションのIDも変わるのです。
したがって、IDは常に変化しているということを念頭にスクリプトを書く必要があります。「このメニューアクションのidは0000か」とスクリプトで取得できたIDの数値をメモっておいて、それをすべてのドキュメント共通で使おうとしてもダメなのです。

メニューアクションの実行時は、基本的に「現在のドキュメントの状態」で実行されます。前掲のスクリプトですが、何も選択していない状態で実行するとエラーになります。これはオブジェクトスタイルのリンクを切断するメニューコマンドが、オブジェクトを選択した状態でないと動作しないコマンドだからです。したがって、スクリプトで利用する場合は「現在、ドキュメントがどのような状態なのか」を特定する必要があります。

一応、MenuActionを実行する際の雛形と、コマンド名を一覧で掲載してくれているサイトを載せておきます。

//メニューアクション名から定義する場合(引数は文字列型)
var myAction = app.menuActions.itemByName("hoge");
//IDから定義する場合(引数は数値型)
var myAction = app.menuActions.itemByID(000);
//実行する
myAction.invoke();

MenuActionのコマンド名はこちらにまとまっています。ページ内検索で探したいコマンド名を検索すればすぐ見つかるでしょう。

menuActionTitleToString

表の左側でも右側でも、どちらもitemByNameメソッドの引数として渡せばOKです。ただ、ここに載っているのはドキュメント間でほぼ普遍であろうというものです。ここになくても使えるMenuActionは多々あります。

BridgeTalkを使った非同期処理中に、ユーザーの操作を禁止するには?

この質問にはしたたか企画さんが答えてくださいました(感謝!)。僕自身、BridgeTalkに1年くらい触れていなかったこともあってその時明確な答えを出せませんでした。そういうわけで、少し時間をかけてまとめて検証したいので次回以降の更新にさせていただきます。すみません^^;;

スクリプトから直接クリップボードにテキストを送る方法は?

直接クリップボードに送るには、OSによって処理を変える必要があります。

Macの場合

Macの場合はdoScriptメソッドでAppleScriptを実行します。1行でいけますので暗記してもいいくらいです。

app.doScript ('set the clipboard to "' + str + '"', ScriptLanguage.APPLESCRIPT_LANGUAGE);

strに、クリップボードに送りたい文字列をセットしておけばOKです。

Windowsの場合

僕自身はまったくわからず、以前、Twitterでお〜まちさんから教えていただきました。処理としてはMacと同じくdoScriptメソッドを使いますが、中身はVBScriptになります。

ツイートには表記間違いがあったので(お〜まちさんご自身も後から訂正ツイートもされていましたが)、一応修正の上再掲しておきます。

var scpt = 'CreateObject("WScript.Shell ").Exec("clip").StdIn.Write "' + str +'"';
app.doScript(scpt, ScriptLanguage.VISUAL_BASIC);

ただ恐縮ながら僕自身は検証していませんので(手元にWindowsがない^^;)、試してみて何かお気づきのことがあれば教えてください。また、お〜まちさんがツイートでも言及されていますが、文字コードS-JISになってしまうことと、改行が使えないようです。これはなかなかにやっかいですね。
なので、質問者の方が質問の際に仰っていた「新たにテキストフレームを作り、スクリプトからコピーを実行し、そのテキストフレームを削除する」という一見回りくどい方法の方が現実的かもしれません^^;
とはいえ、新たにテキストフレームを生成してそれを削除する、というのは「ドキュメントに手を加える」という点でちょっと不安が残るのも事実です(それになんだかスマートじゃないですよね)。ほかの方法もいくつか検討してみましたが、これだ、という決定打はありませんでした。
ひとつには、検索置換パネルを表示すると自動的に検索文字列が選択された状態になることを利用して、「検索条件にコピーしたい文字列を設定、検索置換パネルを開く、コピー」という流れでも実装は可能です。ただし、検索置換パネルが開かれているかどうかを調べることができない(編集メニューの「検索と置換...」コマンドを実行すると、検索置換パネルの表示/非表示がトグルされるだけ)とか、テキスト検索か正規表現検索か任意でアクティベートできない?(検索置換パネルを閉じたときと同じタブが開く)とか、いろいろ不確定要素があるので利用には難がありそうです。
いつかもっといい方法が見つかれば改めて記事にしたいと思います。

宿題の回答

前掲のオブジェクトスタイルのリンクを切断を、ドキュメント全体で実行したいというものでした。ひとまずこんな感じでどうでしょうか。

//MenuActionの特定
var myArea = app.menuActions.itemByName("$ID/BreakLinkToStyle").area;
var myID = app.menuActions.itemByName("$ID/BreakLinkToStyle").id;
for (var i=0; i<myArea.length; i++){
    if (myArea[i] === "パネルメニュー:オブジェクトスタイル"){
        var myAction = app.menuActions.itemByID(myID[i]);
        break;
        }
    }

if (app.documents.length === 0) myExit("ドキュメントを開いてから実行してください");
else var myDoc = app.documents[0];

var myTgt = myDoc.allPageItems;
for (var i=0, len=myTgt.length; i<len; i++){
    myTgt[i].select();
    myAction.invoke();
    }

alert("すべてのオブジェクトスタイルのリンクを切断しました");

function myExit(mes){
    alert(mes);
    exit();
    }

AllPageItemに対して実行することで、マスターページを含むすべてのオブジェクトを対象にしています。一応、ドキュメントを開いていない場合のエラー処理も入れておきました。

スライドの補足

長くなってしまったので次回に回します。すみません。

さいごに

BridgeTalkに関するものとスライドの補足については次回以降にさせてもらいました。改めて記事に起こしたいと思います。
何はともあれ、今回のもくもく会には初心者の方が6人もエントリーしてくださりとても嬉しかったです。この会が少しでも「スクリプトに触れてみたい、作ってみたい」という意欲のお手伝いができれば、主催者の一人としてこんなに嬉しいことはありません。引き続き、DTPerのスクリプトもくもく会をよろしくお願いします。

*1:版元のシータスさんは、もくもく会で会場としてお借りしているYUIDEAさんの合併前の社名です!

*2:ExtendScriptECMA Script 3.0準拠を元に拡張された言語なので、JavaScriptの規格としてはかなり古くなってきました。特にここ最近のプログラミング言語としてのJavaScriptの進化のスピードは早く、最新のものは3.0までの知識だと別言語に感じられるくらいです。(Twitterでご指摘いただいた部分を訂正しました)