DTPab

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

1if-1for(3):特定の文字スタイルの次の文字

f:id:uske_S:20180721002323p:plain

職場で「ある文字スタイルが当たっている文字の、次の文字をスクリプトで処理したいが可能か?」と聞かれたので、試しに書いてみました。
そしたら結果的に1if-1forで収まったのでここに書いておきます。
この画像は、スクリプトで色が青くなる文字スタイル(A)を検索し、その次の文字に赤くなる文字スタイル(B)を当ててみた結果です。

f:id:uske_S:20180809212840g:plain

InDesignの検索だと、例えば「(?<=A)B」として検索するときに、検索条件に文字スタイルを設定するとAもBもその文字スタイルが当たっていないと検索ヒットしません。でもこれがスクリプトならできるよね、ということです。

コードはこちらです。例によって半人前のスクリプトですのであしからず。
たまには最大限にコメント入れてみました。

var doc = app.activeDocument;
app.findGrepPreferences = null; //正規表現検索条件のリセット
//正規表現検索条件の同時定義はpropertiesプロパティにオブジェクトを渡す
app.findGrepPreferences.properties = {
    findWhat: ".+",
    appliedCharacterStyle: doc.characterStyles.itemByName("A")
};
//ドキュメントを対象に検索->検索結果を配列として取得
var findList = doc.findGrep();

for (var i=0; i<findList.length; i++) {
    //検索マッチの最後のCharacterオブジェクト
    var lastChar = findList[i].characters[findList[i].characters.length-1];
    //ストーリー上での、検索マッチの次の文字のindex
    var nextIndex = lastChar.index + 1;
    //その文字のCharacterオブジェクト
    var nextChar = lastChar.parentStory.characters[nextIndex];
    //nextCharが利用可能か?(ストーリーの最後で次の文字が存在しない場合などを回避)
    if (nextChar.isValid) {
        //利用可能であれば文字スタイルを割り当て
        nextChar.appliedCharacterStyle = doc.characterStyles.itemByName("B");
    }
}

Aという文字スタイルとBという文字スタイルを用意したドキュメントで実行してください。

今回で3回目の不定期連載ですが、意外と書けるんですよね、if文とfor文を1回使うだけで。
ただこのスクリプトには難点があって、めちゃくちゃ処理が遅いです。なぜならとてもまだるっこしい処理をしているからです*1

まぁパフォーマンスについては別の機会に考えるとして、「次の文字」を取得するという方法ではnextItemメソッドというものがあります。これはほとんどのコレクションオブジェクトが持っているメソッドで、文字通り、コレクションのあるメンバーを基準に(その基準となるメンバーをnextItemメソッドの引数として渡します)次のメンバーを取得するメソッドです。
先程のfor文をnextItemメソッドを使ってリライトするとこんな感じでしょうか。

for (var i=0; i<findList.length; i++) {
    //検索マッチの最後のCharacterオブジェクト
    var lastChar = findList[i].characters[findList[i].length-1];
    //次のCharacterオブジェクト
    var nextChar = findList[i].characters.nextItem(lastChar);
    //nextCharが利用可能か?(ストーリーの最後で次の文字が存在しない場合などを回避)
    if (nextChar.isValid) {
        //利用可能であれば文字スタイルを割り当て
        nextChar.appliedCharacterStyle = doc.characterStyles.itemByName("B");
    }
}

ほかにもいろいろ方法がありますので、オブジェクトモデルと睨めっこしながら、思いついたものを試してみてください〜^^/

*1:余談ですが、僕が初めて実装した「検索結果の次の文字を取得する」という処理のアイディアが、この何ともまだるっこしい方法でしたw