選択テキストを約物カーニング→文字ツメ→変形(長体・平体)で指定ライン数に追い込む「shorten_line_selection 0.5」

何をするスクリプトか?

選択したテキスト部分を

  1. 約物のカーニング
  2. 文字ツメ
  3. 変形(長体・平体)

の順に適用して、指定ライン数以内に収めます。

同梱ファイル2Files

  • README.txt このファイルです。とにかく最初によんでください。
  • shorten_line_selection.jsx スクリプト本体です。

動作環境

このスクリプトが正常に動作する環境は以下の通りです。Windows環境でも動作する可能性がありますが、動作確認はしていません。

  • MacOS X10.7.3
  • InDesign CS5_J(7.0.4)

インストール

スクリプト本体(shorten_line_selection.jsx)を
~/Library/Preferences/Adobe InDesign/Version 7.0-J/ja_JP/Scripts/Scripts Panel/
にコピーしてください。エイリアスを入れておくだけでもかまいません。
スクリプトパレットから使用します。

使用方法

  1. 「ウインドウ」メニューから「スクリプティング」ー「スクリプト」を選択し、スクリプトパレットを出します。
  2. 変形をかけたい部分のテキストを選択します。
  3. スクリプトパレットから、スクリプト「shorten_line_selection.jsx」をダブルクリックします。
  4. テキストが変形されたのを確認してください。

設定

スクリプトをエディタまたはESTKで開くと、最初の部分に以下の設定があります。この設定値を変更すれば、簡単にカスタマイズできます。

変数名 初期値 説明
kerning_var -300 約物のカーニング量。限界値ではなく、定数値。:: カーニングしない時は0にする
my_tsume_limit 25 文字ツメの限界値(%):: 適用しない時は0にする
my_scale_limit 70 文字変形の限界値(%):: 適用しない時は0にする
my_line_limit -1 何ライン以内に収めるか。-1を指定すると、常に現状よりも-1ラインとなる。-2は無効。

既知の不具合、またはToDo、あるいは仕様

  • カーニング、文字ツメ、変形は縮小方向のみに働きます。例えば、2ライン分を選択していて、3ラインになったりしません。

免責事項

  • 本アプリケーションはInDesignにおける作業効率支援なのであって、処理結果を保証するものではありません。かならず確認をされることをおすすめします。
  • このツールを使用する上でデータの破損などのあらゆる不具合・不利益については一切の責任を負いかねますのでご了解ください。
  • このツールはすべてのMacintoshとMac OS上で動作をするという確認をとっていませんし、事実上出来ません。したがって、動作を保証するものではありません。

履 歴

2010-02-10 ver.0.1 とりあえず
2010-02-15 ver.0.2 my_line_limitに「-1」を指定すると、常に-1ラインにするようにした。
2012-03-14 ver.0.3 最初の
文字ツメを試し、解消しなかった時に変形するようにした。文字変形のルーチンはmilligrammeさんのhttp://www.milligramme.cc/wp/archives/591 を参考にさせていただきました、感謝。文字属性の変更と復帰のために1文字づつのリスト(オブジェクト)にした。こうすることで、現在のツメないし変形に加算されるようにした(そして速度は遅くなった)。文字変形の限界値を超えて復帰するとき、アラートを出すようにした。文字を選択していない時(InsertionPoint)、挿入点の段落を処理対象にした。
2012-03-14 ver.0.4 約物カーニングを最初に試すようにした。ToDo:行ツメに失敗してもカーニング値だけは戻らない。
2012-03-14 ver.0.5 行ツメに失敗してもカーニング値も含めてUndoできるようにした。副作用として、行ツメに成功した場合もUndo一発で復元できるようになった。この、UndoModes.FAST_ENTIRE_SCRIPT使っているので、気持ち程度は速くなったかも。UndoModesの使い方は、@k_kazutoshi さんに教えていただきました。感謝します。https://twitter.com/k_kazutoshi/status/179901033370234880

ソースコード

/*
shorten_line_selection.jsx
選択したテキスト部分を文字ツメ、変形(長体・平体)させて、指定ライン数以内に収めます。

2010-02-10	ver.0.1	とりあえず
2010-02-15	ver.0.2	my_line_limitに「-1」を指定すると、常に-1ラインにするようにした。http://d.hatena.ne.jp/seuzo/20100215/1266213438
2012-03-13  ver.0.3 最初の
文字ツメを試し、解消しなかった時に変形するようにした。文字変形のルーチンはmilligrammeさんのhttp://www.milligramme.cc/wp/archives/591 を参考にさせていただきました、感謝。文字属性の変更と復帰のために1文字づつのリスト(オブジェクト)にした。こうすることで、現在のツメないし変形に加算されるようにした(そして速度は遅くなった)。文字変形の限界値を超えて復帰するとき、アラートを出すようにした。文字を選択していない時(InsertionPoint)、挿入点の段落を処理対象にした。
2012-03-14  ver.0.4 約物カーニングを最初に試すようにした。ToDo:行ツメに失敗してもカーニング値だけは戻らない。
2012-03-14  ver.0.5 行ツメに失敗してもカーニング値も含めてUndoできるようにした。UndoModes.FAST_ENTIRE_SCRIPT使っているので、気持ち程度は速くなったかも。UndoModesの使い方は、@k_kazutoshi さんに教えていただきました。感謝します。https://twitter.com/k_kazutoshi/status/179901033370234880
*/

#target "InDesign"
//////////////////////////////////////////// 設定
var kerning_var = -300 //約物のカーニング量。限界値ではなく、定数値。:: カーニングしない時は0にする
var my_tsume_limit = 25;//文字ツメの限界値(%):: しない時は0にする
var my_scale_limit = 70;//文字変形の限界値(%):: しない時は0にする
var my_line_limit = -1;//何ライン以内に収めるか。-1を指定すると、常に現状よりも-1ラインとなる。-2は無効。



////////////////////////////////////////////エラー処理 
function myerror(mess) { 
  if (arguments.length > 0) { alert(mess); }
  exit();
}

////////////////////////////////////////////正規表現検索置換
/*
my_range	検索置換の範囲
my_find	検索オブジェクト ex.) {findWhat:"(わたく?し|私)"}
my_change	置換オブジェクト ex.)  {changeTo:"ぼく"}

my_changeが渡されない時は検索のみ、マッチしたオブジェクトを返す。
my_changeが渡されると置換が実行されて、返値はなし。
*/
function my_RegexFindChange(my_range, my_find, my_change) {
    //検索の初期化
    app.findGrepPreferences = NothingEnum.nothing;
    app.changeGrepPreferences = NothingEnum.nothing;
    //検索オプション
    app.findChangeGrepOptions.includeLockedLayersForFind = false;//ロックされたレイヤーをふくめるかどうか
    app.findChangeGrepOptions.includeLockedStoriesForFind = false;//ロックされたストーリーを含めるかどうか
    app.findChangeGrepOptions.includeHiddenLayers = false;//非表示レイヤーを含めるかどうか
    app.findChangeGrepOptions.includeMasterPages = false;//マスターページを含めるかどうか
    app.findChangeGrepOptions.includeFootnotes = false;//脚注を含めるかどうか
    app.findChangeGrepOptions.kanaSensitive = true;//カナを区別するかどうか
    app.findChangeGrepOptions.widthSensitive = true;//全角半角を区別するかどうか

    app.findGrepPreferences.properties = my_find;//検索の設定
    if (my_change == null) {
        return my_range.findGrep();//検索のみの場合:マッチしたオブジェクトを返す
    } else {
        app.changeGrepPreferences.properties = my_change;//置換の設定
        my_range.changeGrep();//検索と置換の実行
    }
}

////////////////////////////////////////////target_objに含まれる約物を検索して、kerningを実行
function kerning_yakumono(target_obj, kerning_var) {
    ////////////////正規表現文字列の作成と検索(1:起こし括弧類)
    var regex_str = "「『([{‘“〈《【〔";//起こし括弧類
    var hit_obj = [];
    var hit_obj = my_RegexFindChange(target_obj, {findWhat:"[^「『([{‘“〈《【〔 \s\n\r][" + regex_str + "]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var; //カーニング処理(1)
    }
    
    ////////////////正規表現文字列の作成と検索(2:受け括弧類と句読点類)
    regex_str = "」』)]}’”〉》】〕、,。.";//受け括弧類と句読点類
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[" + regex_str + "][^」』)]}’”〉》】〕、,。.\s\n\r]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var; //カーニング処理(2)
    }
    
    ////////////////正規表現文字列の作成と検索(3:中点類)
    regex_str = "・:;";//中点類
    kerning_var2 = kerning_var / 2;
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[" + regex_str + "][^\n\r]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var2; //カーニング処理(3)
    }
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[^\n\r][" + regex_str + "]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var2; //カーニング処理(3)
    }
    hit_obj = my_RegexFindChange(target_obj, {findWhat:"[" + regex_str + "][" + regex_str + "]"});
    for (i = 0; i < hit_obj.length; i++) {
        hit_obj[i].insertionPoints.item(1).kerningValue = kerning_var; //カーニング処理(3)
    }
}



////////////////////////////////////////////my_objの文字数分の文字属性prop_listを取得してオブジェクトで返す
//ex.) prop_list = ["horizontalScale", "tsume"]
function get_char_prop(my_obj, prop_list) {
    var my_data = new Object();
    for (var i =0; i < prop_list.length; i++){
        my_data[prop_list[i]] = my_obj.characters.everyItem()[prop_list[i]];
    }
    return my_data;
}

////////////////////////////////////////////my_objに文字数分の文字属性prop_objをセット(復帰)する。何も返さない
function set_char_prop(my_obj, prop_obj) {
    for ( var p in prop_obj) {
        if (prop_obj.hasOwnProperty (p)) {
            //alert(p);//
            //alert(prop_obj[p]);
            for (var i = 0; i < my_obj.characters.length; i++) {//セットするときは1文字づつ
                my_obj.characters[i][p] = prop_obj[p][i];
            }
        }
    }
}

////////////////////////////////////////////配列aryの各々の要素にnを加算。要素が数値であることを期待
function add_each_number(ary, n) {
    for (var i = 0; i < ary.length; i++) {
        ary[i] += n;
    }
    return ary
}



////////////////////////////////////////////カーニングと文字ツメと変形
//最初に約物のカーニングを試す。約物のカーニングで収拾できなければ文字ツメ
//次に文字ツメをmy_tsume_limit_numになるまで試してみる。文字ツメで収拾できなければ文字変形へ
//my_target_obj(テキストオブジェクト)を、最大my_scale_limit_num(%)まで変形して、my_line_limit_num(行)以内に収める
var my_func = function set_tsume_scale(args) {
    var my_target_obj = args[0];
    var my_kerning_var = args[1];
    var my_tsume_limit_num = args[2];
    var my_line_limit_num = args[3];
    var my_scale_limit_num =args[4];
    
    if (my_target_obj.lines.length <= my_line_limit_num) {return 1;}//選択行数が指定行数以下ならなにもしない
    var HorV = my_target_obj.parentStory.storyPreferences.storyOrientation == StoryHorizontalOrVertical.HORIZONTAL ? 'horizontalScale':'verticalScale';//my_target_objは縦組みか横組みか。HorVはStrings
    var tmp_tsume_obj = get_char_prop(my_target_obj, ["tsume"]);//文字ツメのオブジェクト
    var tmp_HorV_obj = get_char_prop(my_target_obj, [HorV]);//変形率のオブジェクト。 ディープコピーするのめんどいので、2回つくることにした。
    
    
    //約物カーニング
    if (my_kerning_var !== 0) {
        kerning_yakumono(my_target_obj, my_kerning_var);
        my_target_obj.recompose();//文字列の再描画
        if (my_target_obj.lines.length === my_line_limit_num) {return 1;}//収拾したらreturn
    }
    
    //ツメ
    var tmp_tsume_list = tmp_tsume_obj["tsume"];
    //var tmp_tsume_max =Math.max.apply(null, tmp_tsume_list);//現在のツメ量の最大値
    //myerror(tmp_tsume_list);
    while (my_target_obj.lines.length > my_line_limit_num){
        if (my_tsume_limit_num === 0) {break}//文字ツメはやらない
        if (Math.max.apply(null, tmp_tsume_list) > my_tsume_limit_num) {break}//現在のツメ量の最大値がリミットを超えたら、ツメで調整をあきらめてbreak
        tmp_tsume_list = add_each_number(tmp_tsume_list, 0.01);//各文字のツメを1%づつ加算
        set_char_prop(my_target_obj, tmp_tsume_obj);//ツメを実行
        my_target_obj.recompose();//文字列の再描画
        if (my_target_obj.lines.length === my_line_limit_num) {return 1;}//収拾したらreturn
    }

    //変形しない設定になっている。行ツメは成功していない
    if ((my_scale_limit_num ===0) && (my_target_obj.lines.length > my_line_limit_num)){
        return ("ユーザー設定により、行の変形をおこないませんでした。\n設定:my_scale_limit =0");
    }

    //変形
    var tmp_scale_list = tmp_HorV_obj[HorV];
    while (my_target_obj.lines.length > my_line_limit_num){
        tmp_scale_list = add_each_number(tmp_scale_list, -1);//各文字の変形率を1%づつ減算
        set_char_prop(my_target_obj, tmp_HorV_obj);//各文字の変形を実行
        my_target_obj.recompose();//文字列の再描画
         if (my_target_obj.lines.length === my_line_limit_num) {return 1;}//収拾したらreturn
         if (my_target_obj[HorV] < my_scale_limit_num) {//変形限界を超えてしまったら
             return ("変形限界の設定値" + my_scale_limit_num + "%を超えても" + my_line_limit_num +"行に収まりませんでした。");
         }
     }
}


////////////////////////////////////////////実行処理
function main() {
    if (app.documents.length == 0) {myerror("ドキュメントが開かれていません")}
    var my_document = app.documents[0];
    if (my_document.selection.length !== 1) {myerror("テキストを一カ所だけ選択してください")}
    var my_selection = my_document.selection[0];
    var my_class =my_selection.reflect.name;
    if (my_class === "InsertionPoint") {
        my_selection = my_selection.paragraphs[0];
    } else {
        if (my_selection.paragraphs.length > 1) {myerror("段落をまたいで処理できません")}
        my_class = "Text, TextColumn, Story, Paragraph, Line, Word, Character, TextStyleRange".match(my_class);
        if (my_class === null) {myerror("テキストを選択してください")}
    }
    
    
    //my_line_limitに-1が指定されていた
    if (my_line_limit === -1) {
        my_line_limit = my_selection.lines.length - 1;
        if(my_line_limit < 1) {myerror("設定my_line_limitが不正です")}//1行以上は詰められないので終了
    }
    
    //文字ツメ量は1/100で指定
    if (my_tsume_limit <= 0) {
        my_tsume_limit = 0;
    } else if (my_tsume_limit > 100) {
        my_tsume_limit = 1;
    } else {
        my_tsume_limit = my_tsume_limit / 100;
    }
    
    //処理
    var mess = app.doScript(my_func, ScriptLanguage.JAVASCRIPT, [my_selection, kerning_var, my_tsume_limit, my_line_limit, my_scale_limit], UndoModes.FAST_ENTIRE_SCRIPT, 'MyUndo');
    
    //文字列のメッセージが返ってきたら、失敗したので復元しろって合図
    if (typeof mess === "string") {
        my_document.undo();
        myerror(mess);
    }
}

main();