セル内の文字列の幅取得、空き設定

[4808]セル内の文字列の幅取得、空き設定 | 投稿者:たかぽん | 投稿日:2010/06/19(Sat) 23:35:40
いつも勉強させていただいています。
他の組版ソフト(Edianwing)で作成された書籍をInDesignCS4(windowsXP)で改訂することとなりました。
その書籍は表組みを多用しています。

そこで、質問ですが、

例えば、
セル内に箇条書きされた複数行があるとします。
その中の一番長い一行を選んで長さを取得。その長さから余白を出し、それをセルの余白に均等に指定する。

このような作業が多くある表です。

EdianWingでは数値取得、設定が比較的簡単だったのですが、現在作業中のInDesignCS4では以下のように行っています。

まず、セル内の一番長い文字列の末尾にカーソルを置く。
情報ウインドウから座標を取得。
次に文字列の先頭にカーソルを置き、座標を取得。
出た数値から計算して文字列の長さを取得。
その数値を利用して「(セルの幅-文字列の長さ)÷2」で「セルの余白」を出し均等に入力。
以上の作業です。

しかし、表組が何百ページにも続く書籍を相手にするには、あまりにも時間がかかってしまいます。

そこで、上記のような作業を「javascript」で自動化できないかと考えています。
自分の考えるScriptでのフローは以下のように思います。

1.セルの中の一番長い行を自動取得、もしくはカーソルで反転選択
2.長さを自動取得
3.上記余白を出す計算式「(セルの幅-文字列の長さ)÷2」で計算
3.出た答えを実際に文字列を含んだセル内の「セルの余白」に設定

こんな感じができたら随分楽かな、と考えました。

JavaScriptは初心者で勉強を始めたばかりです。どうか解る方、アドバイスをよろしくお願いします。
» 1
[4809]Re: セル内の文字列の幅取得、空き設定 | 投稿者:Un | 投稿日:2010/06/20(Sun) 14:34:01
スクリプトじゃないんですが、箇条書きの部分を別フレームにペーストして、
インライングラフィックとしてセルに貼り込めば、センター揃えにするだけですよね。

「フレームを内容に合わせる」でフレームサイズもピタっと...あれ?複数行だと文字の長さの方がそろわないのか...ここは目見当で。

手は疲れるけど、頭は使わない方法として。
» 2
[4810]Re: セル内の文字列の幅取得、空き設定 | 投稿者:... | 投稿日:2010/06/20(Sun) 15:48:53
>JavaScriptは初心者で勉強を始めたばかりです。どうか解る方、アドバイスをよろしくお願いします。
とおっしゃるのなら、まず自分の書いたスクリプトを出してください。その上でアドバイスができることがあるかもしれません。
いま書かれているのは「こう書いて欲しい」というスクリプトの仕様要望書です。
» 3
[4811]Re: スクリプトに関する依頼への意見 | 投稿者:五月 貴 | 投稿日:2010/06/20(Sun) 20:39:41
暗にゼロからのスクリプト開発依頼を望むのは避けましょう。
皆さんご自身のお仕事があり、その合間をぬってボランティアで特定の方だけのためにスクリプトを開発するお時間は取れませんし、必ず一度で出来上がるものではなく延々とデバッグにまで時間を消費することになります。
もしご自身で組めないのであれば、開発企業さんをお探しになりビジネスとして対価をお支払いする前提で案件を依頼すべきです。
» 4
[4812]Re: セル内の文字列の幅取得、空き設定 | 投稿者:amiza_ret | 投稿日:2010/06/21(Mon) 10:57:26
> どうか解る方、アドバイスをよろしくお願いします。

という事で、私もスクリプトは勉強中ながらアドバイスを...。
最近、似たようなスクリプトを作成しました。
自分用に作成しただけなので、かなり手抜きですし、バグ等もあるかと思いますので、あくまでも参考として下さい。

・一番長い文字のセルを自分で選択してスクリプトを実行します。
・セルの中の改行は一つのみという仕様です。
・段落スタイル「表データN」ってのが操作したいセルに全てがかかっているという仕様です。
・文字の長さを知るために、適当な場所([0, -64, 5, -33])にテキストフレームを作成しフィットさせて長さを測っています(ちょっと怖いやり方。)。その後テキストフレームをremoveしてます(結構、怖いやり方。)。
・変数名やら何やらも適当です。
・ほとんどデバッグはしてませんし、エラー処理や例外処理もしてません。

//一番長い文字のあるセルを選択し、スクリプト実行
//一番長い文字列をセンターに見せかけるために余白をセット。
//他のセルの余白もそれに合わせる
//InDesign CS4_Win


> 1.セルの中の一番長い行を自動取得、もしくはカーソルで反転選択

私は、セルを選択させました。
var selObj = app.activeDocument.selection;
var paraObj = selObj[0].paragraphs[0];
var DocObj = app.activeDocument;


> 2. 長さを自動取得

テキストフレームを作成し文字を貼り、フィットさせて文字の長さを決めました。
ホントはもっといい方法もあると思いますけど、スクリプトを作る時間もなかったので、こんな仕様としちゃいました。
けど、自分にしてはいいアイデアだと思ってます(笑)

var txtObj = DocObj.textFrames.add();
txtObj.visibleBounds = [0, -64, 5, -33];
txtObj.contents = paraObj.contents;
txtObj.paragraphs[0].appliedParagraphStyle = "表データN";
txtObj.fit(FitOptions.frameToContent);
var a0 = txtObj.visibleBounds[0];
var a1 = txtObj.visibleBounds[1];
var a2 = txtObj.visibleBounds[2];
var a3 = txtObj.visibleBounds[3];


> 3.上記余白を出す計算式「(セルの幅-文字列の長さ)÷2」で計算

var txtWidth = a3 - a1;
var LeftGutter = (sleWidth - txtWidth)/2;
txtObj.remove();


> 3.出た答えを実際に文字列を含んだセル内の「セルの余白」に設定

for (i =0; i < selObj[0].parentColumn.cells.length ; i++){
   if (selObj[0].parentColumn.cells[i].rowType == 1161982583){
       selObj[0].parentColumn.cells[i].leftInset = LeftGutter;
   }
}

あと、ちょっと改良すれば、たかぽんさんのやりたいスクリプトになると思いますよ。
※たまたま、似たスクリプトを作成していたので[返信]さてせ頂きました。
» 5
[4814]Re: セル内の文字列の幅取得、空き設定 | 投稿者:たかぽん | 投稿日:2010/06/21(Mon) 22:25:53
すみません。レスが遅れました。現在会社でしかPCに触れなくって(自宅のPCが壊れてしまっていて平日しかレスつけられませんでした。)

>とおっしゃるのなら、まず自分の書いたスクリプトを出してください。
>その上でアドバイスができることがあるかもしれません。

おっしゃる通りです。すみません。
言葉が足りなかったです。

現在詰まっているのが、「文字幅を取得」という冒頭部分でして、

どのような方法があるのか意見が聞きたかったのです。

説明が足りなくてどうもすみません。

また、たくさんの返信をありがとうございます。
 Unさん、
 amiza_ret さん

参考になる例をどうもありがとうございました。
» 6
[4816]Re: セル内の文字列の幅取得、空き設定 | 投稿者:ミシマバイカモ | 投稿日:2010/06/21(Mon) 23:48:04
横組みならば、horizontalOffsetから水平距離を計算できます。縦組みの場合はbaselineで垂直位置を計算することになるかと思いますが、文字のベースラインの位置なのでちょっとやっかいですね。

次のスクリプトは段落が折り返されて複数行になっている場合も考えて、行末に文字幅ゼロの「先頭文字スタイルの終了文字」を追加してその horizontalOffsetから行の幅を計算していますが、1行で終わっているならば
var xt = myLine.characters[-1].insertionPoints[-1].horizontalOffset;
として計算しても大丈夫です。

//カーソルのある行の幅を表示するスクリプト
var myLine = app.selection[0].lines[0]; //選択範囲の最初の行(あるいは文字キャレットの立っている行)
var x0 = myLine.characters[0].horizontalOffset; //行の先頭の文字の水平位置
myLine.characters[-1].insertionPoints[-1].contents = 1396855379; //行の最後に文字幅ゼロの「先頭スタイルの終了文字」を追加
var xt = myLine.characters[-1].horizontalOffset;
myLine.characters[-1].remove();// 追加した「先頭スタイルの終了文字」を削除
alert(xt - x0);//行の幅

(追記)行末が改行文字の場合はこのスクリプトだとまずいですね。行末が改行文字の場合は、文字幅ゼロの文字の挿入はせずに行末文字の水平位置(var xt = myLine.characters[-1].horizontalOffset;)で計算する必要があります。
myLine.characters[-1].remove();で改行文字が削除されちゃいますから。
» 7
[4817]Re: セル内の文字列の幅取得、空き設定 | 投稿者:いき | 投稿日:2010/06/22(Tue) 12:15:10
よこからすみません。
バイカモさん、いつもお世話になっております。

やってみたのですが、一行の方は問題ないですね。
複数行の方は問題がありそうです。
「先頭文字スタイルの終了文字」にしても「結合なし」にしても文字幅ゼロのはずなのになぜか次行の行頭に挿入されます。

app.selection[0].lines[1].characters[0].remove();

したがって上のようにしてやらないと、1行目の行末にある必要な文字が削除されてしまいます。
それと、
alert(xt - x0);
で出てきた値が実際の文字幅よりちょうど1文字分短いようです。
スクリプトの内容に問題があるようには思えないのになぜこのうな動作をするのか不思議です......。
» 8
[4818]Re: セル内の文字列の幅取得、空き設定 | 投稿者:梅花藻 | 投稿日:2010/06/22(Tue) 12:38:18
あちゃちゃ!
CS3以降のものでも、このあたりはCS2と同じだろうとCS2でしか弄ってませんでした。(うまく動いていたと思ったのですが......、少なくとも挿入した先頭文字の終了文字は(行末が改行文字でない限り)次行の行頭にはいかないはず。)
波罫.jsxで使っている部分を抜き出して変えてみたのですが......
これから、確認しなくてはいけないのですが、仕事中はちょっと時間をとれないのでしばしお待ち下さい。
» 9
[4820]Re: セル内の文字列の幅取得、空き設定 | 投稿者:いき | 投稿日:2010/06/22(Tue) 13:27:50
ごめんなさい、一度レスしましたが、内容に問題があったので削除しました。
特殊文字としてあえて「ここまでインデント文字」を使ってみては、という意見だったのですが、条件によっては問題があることがわかりました。
» 10
[4821]Re: セル内の文字列の幅取得、空き設定 | 投稿者:いき | 投稿日:2010/06/22(Tue) 14:22:14
何度もすみません。
No.4817への自己レスと共に気付いたことを書いておきます。

CS2(Win版)、CS3(Win/Mac版)、CS4(Mac版)、CS5(Win/Mac版)で確認しました。

CS2までは先頭文字スタイルの終了文字がきちんと行末に挿入されます。
しかしCS3以降は末尾の文字を次行の行頭に送り出し、その後ろに先頭文字スタイルの終了文字が挿入されます。

これに対して先のレスの
app.selection[0].lines[1].characters[0].remove();
を実行すると、先頭文字スタイルの終了文字の直前にある必要な文字が削除されてしまいますね。この点、訂正しておきます。

そこで、挿入する特殊文字なのですが、1397124194(強制改行)にしてやれば問題ないことに気付きました。
あわせてご報告しておきます。
» 11
[4830]Re: セル内の文字列の幅取得、空き設定 | 投稿者:梅花藻 | 投稿日:2010/06/24(Thu) 14:59:46
すいません。スクリプトをまるまる書いてしまいました。
1行の文字列幅を得る場合、行末文字が「改行文字」のような場合と、その行で終っている場合、次の行に折り返して続いている場合の3つの場合で求め方が変ってくるので、ちょっとやっかいですね。
1行で1段落の場合には、amiza_retさんがやられているようにするのもありだと思います。

行末が文字幅ゼロの文字(お~まちさんのオブジェクトモデル図のページからそれっぽいのを探して並べています)の全ての場合を試している訳ではありませんのであしからず。

文字列の幅を調べる為に挿入する文字は、いきさんが調べてくださった「強制改行」を使わせていただきました。ありがとうございます。

CS2からCS5で動くと思います。(一応、ちょこっとではありますが動作するかは確認しています)

(function (){
   if (app.selection.length == 1){
       var tblObj = app.selection[0];
       switch (app.selection[0].constructor.name){
           case "Table":
           case "Cell":
               var cellObj = tblObj.cells;
               break;
           default:
               alert("表かセルを選択してください");
               return;
       }
       var IDversion = app.version;
       if (IDversion[0] == "4"){ //InDesign CS2
           var CS2flag = true;
       } else if (IDversion[0] >= "5"){ //InDesign CS3~
           var CS2flag = false;
       }
       var sp = [//文字幅ゼロの文字
           {1396927554:"改段 "},
           {1397777484:"任意の改行"},
           {1396855379:"先頭文字スタイルの終了文字"},
           {1397059650:"改丁"},
           {1397124194:"強制改行"},
           {1397125698:"改フレーム"},
           {1397319796:"ここからインデント"},    
           {1397715010:"改丁(紙改め)"},
           {1397778242:"改ページ"},
           {1397780074:"結合なし"}
       ];
       for (i=0; i<cellObj.length; i++){
           if (cellObj[i].characters.length == 0) continue;//文字がないセルは処理しない
           if (cellObj[i].writingDirection != 1752134266) continue;//セル内縦組は処理しない
           with (cellObj[i]){
               var wd = 0;
               for (j=0; j<lines.length; j++){
                   var x0 = lines[j].characters[0].horizontalOffset;
                   var xt = lines[j].characters[-1].horizontalOffset;
                   if (sp[lines[j].characters[-1].contents] == undefined){//行末文字が幅ゼロの文字ではない場合
                       var pt = lines[j].characters[-1].index;
                       try {
                           characters[pt+1].contents;//次の行が存在する
                           lines[j].characters[-1].insertionPoints[-1].contents =  1397124194;//強制改行を挿入
                           xt = lines[j].characters[-1].horizontalOffset;
                           characters[pt+1].remove();                            
                       } catch(e) {
                           xt = lines[j].characters[-1].insertionPoints[-1].horizontalOffset;
                       }
                   }
                   if (wd < xt-x0) wd = xt-x0;
               }
           }
           if (wd > 0){
               cellWidth = cellObj[i].width;
               if (cellObj[i].columnSpan > 1 && CS2flag){//CS2でセルが結合セルのとき
                   var cellWidth = 0;
                   var s = cellObj[i].name.split(":");
                   var obj = cellObj[i].parent;
                   for (j=0; j<cellObj[i].columnSpan ;j++){
                       var s1 = eval(s[1])+j;
                       cellWidth += obj.cells.item(s[0]+":"+s1).width;
                   }
               }
               var aki = (cellWidth - wd) / 2;
               cellObj[i].leftInset = aki;
               cellObj[i].rightInset = aki;
           }
       }
   }
})();
この記事の書き込み元へのリンク (コメントや質問などはこちらへどうぞ)