Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Photoshop Generatorプラグインの作り方

Photoshopとサードパーティツールとの相互運用性を高める「Generatorテクノロジー」(後編)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2013/12/18 14:00

 前回は、サードパーティ製アプリケーションとの相互運用性を高める「Generatorテクノロジー」で何ができるようになったかを、PhotoshopやEdge Reflowを使ったワークフローを交えつつ、具体的に紹介しました。本稿では、node.jsアプリケーションとして実装されているGeneratorプラグインを自作し、機能拡張する方法を詳しく解説します。

改めてGeneratorとは

 前回の冒頭では概念的にGeneratorの概要を書きましたが、端的に言うとGeneratorはiPhone/AndroidやPhotoshop以外のアプリケーションからTCP/IPを利用してPhotoshopへリモートアクセスを可能にするnode.jsサーバーです。

 従って、Generatorプラグインはnode.jsアプリケーションということになります。Photoshopと接続して状態の変化をイベントとして探知できるので、夢が広がります。本稿では、プラグイン作成の基本的な作法を紹介していきたいと思います。

 なお、本稿で使用するPhotoshop CCは、Adobe Creative Cloudの無償メンバーシップへの登録で30日間の体験版が利用可能です。

コアライブラリの準備

 まず第一にnode.jsをインストールしている必要があります。まだインストールをされていない方は、node.jsよりダウンロードしてインストールを済ませてください。

 node.jsがインストールできたら、Pluginのコアとなるadobe-photoshop/generator-coreをダウンロードもしくはcloneします。

 ダウンロードして展開したファイル一式を、今回はgenerator-core-masterというフォルダに入れます。ここまでできたら、first_pluginのフォルダをターミナル(Mac)もしくはCommand Line(Windows)で開き、下記のコマンドを叩きます。

npm install

 NPMNode Packaged Modulesの略称で、node.jsをインストールすると使えるようになります。上記コマンドを叩くと、開発に必要なモジュール一式がインストールされます。

Photoshopへのリモート接続準備

 [環境設定]-[プラグイン]で「リモート接続を有効にする」にチェックを入れ、任意のサーバー名とパスワードの項目には初期値として「password」とセットします。passwordというパスワードを変更したい場合は、generator-core/app.jsの58行目辺りで設定されているのでここを変更します。

 以上の設定が終わったら、一度Photoshopを再起動します。再起動をしないと接続可能になりませんので注意してください。

自作プラグイン用の構成ファイルを用意する

 generator-core-masterと同階層にpluginsフォルダを用意し、その中に自作のプラグインフォルダを用意します。まずは初めてのプラグインということで「first-plugin」という名前でフォルダを作成し、その中に下記2つのファイルを用意します。

  • main.js
  • package.json

 このファイルがプラグイン作成に必要な最低限のファイルです。なお、現在の自作プラグインフォルダはこのような状態になっています。

package.jsonにプラグイン情報を記述する

 前項のファイルが用意できたら、package.jsonにプラグインの基本情報を記述します。

{
    "name": "first-plugin",
    "version": "1.0.0",
    "description": "はじめてのPhotoshop Generatorプラグイン",
    "main": "main.js"
}

プラグインを実行する

 必要最低限の初期化処理を実装してひとまず実行してみます。main.jsに下記のコードを記述します。初期化の処理でgeneratorオブジェクトが渡ってきますので、変数に入れておきます。

(function() {
    "use strict";

    var PLUGIN_ID = require("./package.json").name;
    var MENU_ID = "fp";

    var _generator;

    function init(generator) {
        _generator = generator;
        console.log("はじめてのPhotoshop Generatorプラグイン");
    }

    exports.init = init;
}());

 以上の準備ができたら、generator-core-masterのフォルダへ行き、以下のコマンドを叩きます。

node app -f ../plugins

 上記のコマンドでgenerator-core-master/app.jsをpluginsフォルダを引数として実行しました。コマンドラインにconsole.logで指定した内容が表示されていれば、ひとまずPhotoshopとプラグインとの接続成功です。

注意事項

 もし、下記のようなエラーが表示されて、Photoshopとの接続が切れてしまう場合は、「Photoshopへのリモート接続準備」の項を再度読み返して、パスワードの設定をしてください。初期パスワードは「password」です。

Communiation error: 1
photoshop communcations error: {"error":"Photoshop communication error: 1"}
Photoshop connection closed
Exiting
Exiting with code 0: generator close event

自作プラグインをPhotoshopのメニューに追加する

 初期化の処理が呼ばれるようになったら、次はGeneratorのメニューからプラグインのメソッドを実行できるようにしてみましょう。

 generatorのaddMenuというメソッドを使うだけでPhotoshop上のメニューに追加できます。下記のコードをinitの中に追記します。

_generator.addMenuItem(MENU_ID, "はじめてのプラグイン", true, false);

 それぞれの引数の役割は下記のとおりです。

addMenu(メニューID, 表示名, 有効フラグ, チェック);
  • 第一引数:js上で扱うためのメニューID
  • 第二引数:Photoshopのメニューで表示する名前
  • 第三引数:メニューを有効にするかどうかのフラグ、falseにすると表示はされるがdisable状態になる
  • 第四引数:デフォルトチェック

 上記のメソッドを追加したら再度、下記のコードを打ちます。

node app -f ../plugins

 無事成功すると、下記のように「生成」メニューに「はじめてのプラグイン」と追加されます。

メニューのクリックを捕捉する

 ここまでの手順でメニューに項目を追加することができましたが、クリックしても何も起こりません。クリックしたことを捕捉するにはgeneratorのonPhotoshopEventというメソッドを利用して、Photoshop上のイベントを捕捉する必要があります。メニューがクリック際に発行されるイベントは、generatorMenuChangedイベントです。このイベントを下記のようにしてリスナーを登録します。

_generator.onPhotoshopEvent("generatorMenuChanged", onMenuChanged);

 第一引数でイベント名、第二引数でイベントが発生した際に実行されるメソッド名を指定しましたので、onGeneratorMenuChangedというメソッドを自前で用意します。ここではaddMenuで指定したメニューIDがfpの場合、consoleにメッセージを表示するメソッドを用意します。

function onMenuChanged(event) {
  if (event.generatorMenuChanged.name === MENU_ID) {
    console.log("はじめてのプラグインが実行されました。");
  }
}

 ここまでのコードの全体像は下記のとおりです。

(function() {
    "use strict";

    var PLUGIN_ID = require("./package.json").name;
    var MENU_ID = "fp";

    var _generator = null;

    function init(generator) {
        _generator = generator;
        _generator.addMenuItem(MENU_ID, "はじめてのプラグイン", true, false);
        _generator.onPhotoshopEvent("generatorMenuChanged", onGeneratorMenuChanged);
    }

    function onGeneratorMenuChanged(event) {
        if (event.generatorMenuChanged.name === MENU_ID) {
            console.log("はじめてのプラグインが実行されました。");
        }
    }

    exports.init = init;
}());

 コードがかけたら、先程までのPhotoshopとの接続をいったん解除し、もう一度下記のコードを実行します。

node app -f ../plugins

 再度、[ファイル]-[生成]-[はじめてのプラグイン]をクリックすると、しっかりと「はじめてのプラグインが実行されました」と表示されたかと思います。

トグルメニュー化する

 前項でメニューからクリックしてonMenuChangedのタイミングで処理を実行することができました。addMenuの第4引数でチェック状態のフラグがありましたが、Generatorのメニューはトグル状態にすることができます。方法としては下記のようにしてonMenuChangedを下記のように書きかえてフラグを切り替えます。

function onGeneratorMenuChanged(event) {
    var changedMenu = event.generatorMenuChanged;
    if (changedMenu.name === MENU_ID) {
        var menuState = _generator.getMenuState(changedMenu.name);
        _generator.toggleMenu(MENU_ID, menuState.enabled, !menuState.checked);
    }
}

 この後、画像の変更や、ツールの変更などのイベントを紹介しますが、menuStateを変数に入れておき、チェックが入っている時のみイベントハンドラの処理を実行するなど切り分けることが可能になります。

その他のイベント

 現状、公式のリファレンスなどは見当たりませんが、確認できているイベントとして下記のものがあります。

imageChanged

 レイヤーを選択した、レイヤーに変更を加えたなど、主に加工に関わる操作をした際に発行されるイベントです。

 実際に捕捉してしてみましょう。generatorMenuChangedの時と同じように、initにonPhotoshopEventでイベントリスナーを登録します。

function init(generator) {
    // ...
    _generator.onPhotoshopEvent("imageChanged", onImageChanged);
}


function onImageChanged(event) {
    console.log(event);
}

 記述して再接続し、新規レイヤーを作ってみると、下記のようなJSONがコマンドラインに表示されたかと思います。

{ version: '1.0.0',
  timeStamp: 1386556760.277,
  id: 1497,
  layers:
   [ { id: 3,
       index: 1,
       added: true,
       type: 'layer',
       name: 'レイヤー 1',
       bounds: [Object] },
     { id: 1, index: 0 } ],
  selection: [ 1 ] }

 ちょっと見づらいですが、レイヤー名やレイヤーのタイプ、領域などの情報が取得できることが分かります。このままでは見づらいのでeventオブジェクトをJSONとして整形するメソッドを用意しておきます。

function stringify(object) {
    try {
        return JSON.stringify(object, null, "    ");
    } catch (e) {
        console.error(e);
    }
    return String(object);
}

 stringifyを用意したら、onImageChangedの記述を下記のように変更します。

function onImageChanged(event) {
    console.log(stringify(event));
}
ここまで記述して再接続し、新規レイヤーを作ると下記のように、詳細のオブジェクトの中身まで綺麗に見えるようになります。

{
    "version": "1.0.0",
    "timeStamp": 1386557554.314,
    "id": 1510,
    "layers": [
        {
            "id": 2,
            "index": 1,
            "added": true,
            "type": "layer",
            "name": "レイヤー 1",
            "bounds": {
                "top": 0,
                "left": 0,
                "bottom": 0,
                "right": 0
            }
        },
        {
            "id": 1,
            "index": 0
        }
    ],
    "selection": [
        1
    ]
} 

toolChanged

 主にツールパレットでツールを変更した際に発行されるイベントです。"moveTool"、"paintbrushTool"などのツール名が取得できます。

 下記のように記述します。

function init(generator) {
    // ...
    _generator.onPhotoshopEvent("imageChanged", onImageChanged);
}

function onToolChanged(event) {
    console.log("ツールが変更されました。");
    console.log(stringify(event));
}

currentDocumentChanged

 選択しているPSDを切り替えた際に発行されるイベントです。変更するとPSDのidがeventとして渡ってきます。これを元に現在のドキュメントidを変数で管理すると良さそうです。

 下記のように記述します。

function init(generator) {
    // ...
    _generator.onPhotoshopEvent("currentDocumentChanged", onCurrentDocumentChanged);
}

function onCurrentDocumentChanged(event) {
    console.log("現在のドキュメントが変更されました。");
    console.log(stringify(event));
}

JSXを読み込んで実行する

 GeneratorからPhotoshop用JavaScript、JSXを利用できます。Generatorには2つのJSX評価用メソッドが用意されています。1つはevaluateJSXString(string)で文字列でJSXコードを渡すことで処理を実行してくれるメソッドです。文字列で書いていくのは少々しんどいので、外部jsxファイルを読み込んで実行するためにevaluateJSXString(path)というメソッドが用意されています。

 まずは自作のプラグインフォルダ内にjsxファイルを用意します。main.jsと同階層です。試しにレイヤーの選択を切り替えるごとにレイヤー名をアラートで表示してみましょう。imageChangedのリスナーが呼ばれたらjsxを読み込むようにしてみます。selectedLayer.jsxというファイル名で下記のコードを記述します。

alert(app.activeDocument.activeLayer);

 このjsxファイルをonImageChangedで読み込むようにします。下記のように記述します。ファイルパスはgenerator-core/lib/generator.jsから見た相対パスを指定します。実際にプラグインとして稼働させるにはファイルパスの指定の仕方が変わりますので、本稿の最後のセクションを御覧ください。

function onImageChanged(event) {
    console.log("イメージが変更されました。");

    _generator.evaluateJSXFile("../../plugins/first-plugin/selectedLayer.jsx").then(
        function(result) {
            console.log(result);
        },
        function(error) {
            console.error(error);
        }
    );
}

 ここまで用意してからPhotoshopへ再接続し、レイヤーを選択してみると、コマンドラインにレイヤー名が表示されると思います。

プラグインとして稼働させる

 このままでは任意のフォルダで開発している状態なので、実行コマンドを打たないと稼働しません。実際にプラグインとして稼働させるには、アプリケーションフォルダ内の下記の場所にプラグインを配置する必要があります。

  • /Applications/Adobe Photoshop CC/Plug-ins/Generator/

 また、jsxのファイルパスはこの配置によって変わってしまうので、下記の箇所を書き換えます。

変更前
_generator.evaluateJSXFile("../../plugins/first-plugin/selectedLayer.jsx")
変更後
_generator.evaluateJSXFile(this._photoshop._applicationPath + "/Plug-ins/Generator/first-plugin/selectedLayer.jsx")

 これでPhotoshopを起動すると、今度は起動しただけで下図のように[ファイル]-[生成]メニューに「はじめてのプラグイン」が追加されていることが確認できます。これでプラグインとして正式に稼働し始めました。

 いかがでしょうか。だいぶやりたいことが膨らんできたと思います。プラグインからjsxにパラメータを渡すこともできるので、JSX上でレイヤー情報を取得してCSSを生成したり、fsモジュールを使ってリアルタイムでファイル生成をしたりして、Photoshopとhtmlコードの自由な連携を独自に生み出すことができそうですね。これを機にnode.jsとjsxを使って楽しんでみてはいかがでしょうか。

 本稿のサンプルは下記URLで公開しています。

参考

 Generatorのリファレンスは現状見当たらないので、generator.jsのソースが参考になります。

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 又村 洋史(マタムラ ヒロフミ)

    株式会社イノーヴ WEBクリエイター。キャンペーンサイト・コーポレートサイト・ECサイトを中心に手がける。 土日はWEBの学校クスールで非常勤講師としてJavaScriptなどを教えています。日々学ぶことばかり。個人ブログ「Deconcepter」ではWEBに関わる人が押さえておきたい情報の...

All contents copyright © 2006-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5