Photoshopで実際にガイドを引くスクリプトを作成しますが、これは「ExtendScript Toolkit」を使います。

ExtendScript Toolkitはスクリプト対応のアプリケーションと共にインストールされているはずです。

 

ExtendScript Toolkitを起動したら、ウィンドウの上にある「対象アプリケーション」をPhotoshopにします。

 

<関数の作成>

とりあえず関数の外枠を作成します。

function addGuide(dir, inp) {}

関数名は「addGuide」とし、引数は押されたボタンの種類を受け取る「dir」、オフセット値を受け取る「inp」としました。

 

<エラー処理の作成>

まず、関数が呼び出されたらガイドを引く前提条件をチェックしなければなりません。

具体的には「ドキュメントが開かれているか否か」「選択範囲があるか否か」の2点です。

複雑な条件の場合はそれらを単独で調べつつ、その状況ごとにエラーを発信する流れになるのですが、今回は進ちょく状況がわかればエラーの種類がわかりますので、以下のような方法をとることにします。

function addGuide(dir, inp) {
    var errCnt= 0; // 進ちょく状況チェック用カウンター
    try{
        // アクティブドキュメント取得
        var actDoc= activeDocument;
        errCnt++; // カウントアップ
        var selObj= actDoc.selection.bounds; // 選択範囲(の座標)取得
        errCnt++; // カウントアップ
    } catch (e){
        var msg; // エラーメッセージ収納用
        switch (errCnt) {
            case 0:
                msg= 'ドキュメントが開かれていません';
                break;
            case 1:
                msg= '選択範囲がありません';
                break;
            default:
                msg= '原因不明のエラーです';
                break;
        }
        alert (msg);
    }
}
addGuide(); // 関数呼び出しテスト

実際にアクティブドキュメントや選択範囲を取得し、それぞれが終わった後にカウンターをアップしています。存在しないドキュメントや選択範囲を取得しようとすれば、try文がエラーをキャッチします。そして、その時点でのカウンター値(errCnt)を調べれば、エラーの原因が特定できるという仕掛けです。

とりあえずここまでのスクリプトを、Photoshopでこれらのエラーが出る状況を作り動作をチェックします。エディタの上部にある「実行ボタン」をクリックするとスクリプトが実行されます。

 

なお、アクティブドキュメントと選択範囲の取得については以下のページを参照してください。

参照:最前面のドキュメントを取得する選択範囲の大きさを取得・変更

 

<条件分岐の作成>

今回は押されたボタンによって配置するガイドの種類や座標が変わるので、その動作を分岐しなければなりません。

分岐する条件設定はいくつか考えられますが、今回は以下のように分岐することにします。

  • 上下左右それぞれ
  • 四辺に引く場合は上下左右それぞれに反応
  • 垂直センターと水平センター
  • 両センターに引く場合は垂直・水平それぞれに反応

これらの条件をコード化すると以下のようになります。

// 左 または 四辺の場合
if ( (dir=='button_L') || (dir=='button_S') ) {}
// 右 または 四辺の場合
if ( (dir=='button_R') || (dir=='button_S') ) {}
// 上 または 四辺の場合
if ( (dir=='button_T') || (dir=='button_S') ) {}
// 下 または 四辺の場合
if ( (dir=='button_B') || (dir=='button_S') ) {}
// 垂直センター または クロスの場合
if ( (dir=='button_V') || (dir=='button_C') ) {}
// 水平センター または クロスの場合
if ( (dir=='button_H') || (dir=='button_C') ) {}

 dirは関数の引数で、押されたボタンのnameプロパティ(前回・前々回参照)を受け取る予定になっています。

 

<条件ごとの座標を考える>

動作の分岐が決定したので、その分岐ごとにどんな座標が必要かを考えます。

選択範囲の座標はエラー処理のところで取得済みですが、この値は配列になっています。

そして、この配列内にある座標は「左, 上, 右, 下」の順番になっていますので、そこからそれぞれの目的に合わせて座標を取得します。

var pos;
// 左
pos= selObj[0];
// 右
pos= selObj[2];
// 上
pos= selObj[1];
// 下
pos= selObj[3];
// 垂直センター
pos= selObj[0]+(selObj[2]-selObj[0])/2;
// 水平センター
pos= selObj[1]+(selObj[3]-selObj[1])/2;

このツールはオフセット値によって選択範囲の内側や外側にずらしてガイドが引けるように、オフセット値をユーザーが入力できるようになっています。

上下左右の値にはこのオフセット値を加えなければなりませんが、常にユーザーが正しく入力てくれるとは限りませんので、引数で受け取った値をそのまま加えるのは不安です。

よって、このオフセット値は座標値へ加える前に整える必要があります。

 

<オフセット値を整える>

extendscriptは単位付き数値の計算が可能なので、このオフセット値も単位付き入力ができるようにします。 

なお、選択範囲の座標も単位付き数値で取得することになります。

参照:単位付き数値の演算、および単位の変換

まず、予想されるユーザーの入力と対応を以下のように分類します。

  • 単位なし(数値のみ)の入力
  • 単位付きの入力
  • 不適切な入力

 

一方、UnitValueオブジェクト(単位付き数値)の計算は、どのような結果になるかも把握しておかなければなりません。

とりあえず単位付きの文字列をそのまま加えてみます。

var pos= new UnitValue('7mm'); // 仮座標(UnitValueオブジェクト)
var inp= '50mm'; // 仮入力
var res= pos+inp; // 演算
$.writeln (res); // 7 mm50mm

結果は文字列の連結になってしまいました。

なお、単位なしの文字列の場合も同じ結果になります。

var pos= new UnitValue('7mm'); // 仮座標(UnitValueオブジェクト)
var inp= '50'; // 仮入力
var res= pos+inp; // 演算
$.writeln (res); // 7 mm50

単位付き文字列はUnitValueオブジェクトに変換できますので、これはすぐに解決できます。

var pos= new UnitValue('7mm'); // 仮座標(UnitValueオブジェクト)
var inp= '50mm'; // 仮入力
inp= new UnitValue(inp); // UnitValueオブジェクトに変換
var res= pos+inp; // 演算
$.writeln (res); // 57 mm

しかし、単位なしの文字列はそのままUnitValueオブジェクトに変換できません。

var pos= new UnitValue('7mm'); // 仮座標(UnitValueオブジェクト)
var inp= '50'; // 仮入力
inp= new UnitValue(inp); // UnitValueオブジェクトに変換
var res= pos+inp; // 演算
$.writeln (res); // NaN

単位なしの文字列は数値化することによって解決します。

var pos= new UnitValue('7mm'); // 仮座標(UnitValueオブジェクト)
var inp= '50'; // 仮入力
inp= parseFloat(inp);
var res= pos+inp; // 演算
$.writeln (res); // 57 mm

 

これらの結果から、まずは入力が数値のみか否かを正規表現で振り分けることにします。

var inp= '50';
var regex = new RegExp(/^[-+]?[0-9]+(\.[0-9]+)?$/); //数字、符号、小数点のみ
if (regex.test(inp)) {
    $.writeln ('num only');
} else {
    $.writeln ('not num only');
}

 

次にUnitValueオブジェクトへ変換不可能な不正な入力をはじかなければなりませんが、UnitValueへの変換はどのような場合であってもエラーを返しません。

しかし、変換が適正に行われたかどうかは、変換結果のtypeプロパティを見ればわかります。

適正に変換されていれば、typeプロパティは"mm"や"pt"といった単位を返します。もし、変換が適正でなかった場合は"?"となります。

var inp= '10xyz';
var testStr= new UnitValue(input); //UnitValueオブジェクト作成
if (testStr.type=='?') {
    $.writeln ('illegal input');
}

しかし、残念なことにUnitValueは「mm」のみといった入力で半端に反応してしまい、「NaN mm」と変換してしまいます。これではtypeプロパティが「?」にならないので判別することができません。

この問題にも対応するため、indexOf関数で「NaN」を検出する処理を加え、オフセット値の部分をまとめると以下のようになります(あらかじめ不要な空白を削除する文も加えてあります)。

var inp= 'mm'; // 入力値
var offsetNum;
offsetNum= inp.replace(/\s+/g, ''); // 空白の削除
var regex = new RegExp(/^[-+]?[0-9]+(\.[0-9]+)?$/); //数字、符号、小数点のみ
if (regex.test(offsetNum)) {
    offsetNum= parseFloat(offsetNum); //文字列を数値化
    $.writeln ('number '+offsetNum);
} else {
    offsetNum= new UnitValue(offsetNum); //UnitValueオブジェクト作成
    var testStr= offsetNum.toString();
    if (offsetNum.type=='?' || testStr.indexOf( 'NaN' )>-1) {
        $.writeln ('illegal input');
    } else {
        $.writeln ('UnitValue '+offsetNum);
    }
}

入力値の部分をいろいろ変えてテストしてみてください。

 

<座標にオフセット値を加える>

座標へオフセット値を加えるには、もうひとつ考慮しなければならないことがあります。

先の実験でUnitValueオブジェクトに数値をそのまま足したり、UnitValueオブジェクト同士の演算が正しく行われることは確認済みです。

あらためて実験しませんが、異なる単位のUnitValueオブジェクトの演算も正しく行われます。

しかし、pixelとmmのような関係は、解像度によって解釈が異なる相対的なものです。よって、そのまま演算することはできません。

var pos= new UnitValue('10px'); // 仮座標(UnitValueオブジェクト)
var inp= new UnitValue('3mm'); // 仮入力(UnitValueオブジェクト)
var res= pos+inp; // 演算
$.writeln (res); // NaN

 

UnitValueオブジェクトにはこういった演算に対応するため、変換比率を設定するbaseUnitプロパティを持っています。

ここに「1ピクセルあたりどのくらいか?」という値を設定します(=ppi解像度)。

var pos= new UnitValue('10px'); // 仮座標(UnitValueオブジェクト)
var inp= new UnitValue('3mm'); // 仮入力(UnitValueオブジェクト)
inp.baseUnit= UnitValue (1/350, 'in'); // 350ppi
inp.convert('px'); // pixelに変換
var res= pos+inp; // 演算
$.writeln (res); // 51.3385826771654 px

これで相対的な値の演算は可能になりましたが、すべての組み合わせを評価するのは手間がかかりますので、どちらの値もpixelに揃えて演算するようにします。

また、実際には複数の個所でこの処理を行うことになるので関数内関数として利用します。

var toPixel= function toPixel (uVal){
    var reso= activeDocument.resolution; // ドキュメントの解像度
    if (uVal.type !== 'px') {
        uVal.baseUnit= UnitValue (1/reso, 'in');
        uVal.convert('px'); // pixelに変換
    }
    return uVal;
}

 

各方向のオフセット値を加える演算部分だけを抜き出すと、以下のようになります。

// offsetNum= 単位付きのオフセット値はUnitValueオブジェクト作成時にpixelに変換済
var pos;
// 左
pos= toPixel(selObj[0]) - offsetNum;
// 右
pos= toPixel(selObj[2]) + offsetNum;
// 上
pos= toPixel(selObj[1]) - offsetNum;
// 下
pos= toPixel(selObj[3]) + offsetNum;

 

<ガイドを引く構文の確認>

ガイドを引く構文は以下のページを参照してください。

参照:ガイドの操作

 

<パーツを集積して仕上げる>

これまでの各パーツを組み合わせると以下のようになります。

function addGuide(dir, inp) {
    // pixel変換関数
    var toPixel= function toPixel (uVal){
        var reso= activeDocument.resolution; // ドキュメントの解像度
        if (uVal.type !== 'px') {
            uVal.baseUnit= UnitValue (1/reso, 'in');
            uVal.convert ('px'); // pixelに変換
        }
        return uVal;
    }
    var errCnt= 0; // 進ちょく状況チェック用カウンター
    try{
        var actDoc= activeDocument; // アクティブドキュメント取得
        errCnt++; // カウントアップ
        var selObj= actDoc.selection.bounds; // 選択範囲(の座標)取得
        errCnt++; // カウントアップ
        // オフセット値の整理
        var offsetNum;
        offsetNum= inp.replace(/\s+/g, ''); // 空白の削除
        var regex = new RegExp(/^[-+]?[0-9]+(\.[0-9]+)?$/); //数字、符号、小数点のみ
        if (regex.test(offsetNum)) {
            offsetNum= parseFloat(offsetNum); //文字列を数値化
        } else {
            offsetNum= new UnitValue(offsetNum); //UnitValueオブジェクト作成
            var testStr= offsetNum.toString();
            if (offsetNum.type=='?' || testStr.indexOf( 'NaN' )>-1) {
                throw 'err'; // 強制的にエラーを発生
            }
            offsetNum= toPixel(offsetNum); // pixelに変換
        }
        errCnt++; // カウントアップ
        // ガイド実行
        var pos; // 座標取得用
        // 左 または 四辺の場合
        if ( (dir=='button_L') || (dir=='button_S') ) {
            pos= toPixel(selObj[0]) - offsetNum;
            actDoc.guides.add(Direction.VERTICAL, pos);
        }
        // 右 または 四辺の場合
        if ( (dir=='button_R') || (dir=='button_S') ) {
            pos= toPixel(selObj[2]) + offsetNum;
            actDoc.guides.add(Direction.VERTICAL, pos);
        }
        // 上 または 四辺の場合
        if ( (dir=='button_T') || (dir=='button_S') ) {
            pos= toPixel(selObj[1]) - offsetNum;
            actDoc.guides.add(Direction.HORIZONTAL, pos);
        }
        // 下 または 四辺の場合
        if ( (dir=='button_B') || (dir=='button_S') ) {
            pos= toPixel(selObj[3]) + offsetNum;
            actDoc.guides.add(Direction.HORIZONTAL, pos);
        }
        // 垂直センター または クロスの場合
        if ( (dir=='button_V') || (dir=='button_C') ) {
            pos= selObj[0]+(selObj[2]-selObj[0])/2;
            actDoc.guides.add(Direction.VERTICAL, pos);
        }
        // 水平センター または クロスの場合
        if ( (dir=='button_H') || (dir=='button_C') ) {
            pos= selObj[1]+(selObj[3]-selObj[1])/2;
            actDoc.guides.add(Direction.HORIZONTAL, pos);
        }
    } catch (e){
        var msg; // エラーメッセージ収納用
        switch (errCnt) {
            case 0:
                msg= 'ドキュメントが開かれていません';
                break;
            case 1:
                msg= '選択範囲がありません';
                break;
            case 2:
                msg= 'オフセット値の入力が不適切です';
                break;
            default:
                msg= '原因不明のエラーです';
                break;
        }
        alert (msg);
    }
}
addGuide('button_L', '3mm'); // 関数呼び出しテスト

「不適切な入力」はthrow文で強制的にエラーを発生させてキャッチさせています(27行目)。

 

これで実行スクリプトは完成です。末尾にある関数呼び出しテストの行を削除して保存します。

 

 

Joomla templates by a4joomla