Fireworks拡張機能「Font Mixer」制作メモ

先日公開した、Fireworks拡張機能「Font Mixer」を制作するにあたっての自分用のメモですが、何かのヒントなれば、と公開します。

拡張機能といっても、ライブラリだったりスタイルだったりしますが、今回はコマンドおよびコマンドパネルについてです。

機能拡張の概要

Fireworksにはコマンドとコマンドパネルがあります。その違いはというと、コマンドは「Commands」フォルダへコマンドファイル(jsfファイル)を格納することで、「コマンド」メニューからスクリプトを実行するものです。

コマンドパネルは、「Command Panels」にswfファイルを格納することで、「ウィンドウ」メニューからパネルを表示させ、そのパネルからスクリプトを実行できます。

ちなみに、スクリプトとは直接関係ありませんが、各フォルダへサブフォルダを作ることで、各メニューを階層化することもできます。

このスクリプトの中身は、Fireworks用に拡張されたJavascriptです(拡張子が.jsfなので、以下jsfと呼びます)。例えば、アラートを表示させるなら、次のように書きます。

alert( 'アラート' );

コマンドパネル(swf)からからFireworksのjsfを使う場合は、ActionScriptの「MMExecute(String)」で、JSFを直接実行してやります。アラートを出すなら、次のように書きます。

MMExecute( alert( 'アラート' ) );

Font Mixerはコマンドパネルですが、swfとjsfがセットになっています。swfファイル単体で完結することはできるんですが、開発当初にコマンドとしてjsfファイルを作り始めたため、そのままセットで完成させました。

今回の動作としては、パネルのボタンが押された時点のチェックボックスの状態と、「その他」で入力されている文字をユニコード化し、ActionScriptからjsfへ渡してコマンドを実行しています。

Font Mixerでは、swfからjsfを実行するために、ファイルをあらかじめ読み込んでおき、実行する方法を採っています。

MMExecute( fw.runScript( 'FILE_PATH' ) )

FILE_PATHには「Commands」フォルダからのパスを記述します。

選択肢の組み合わせが多くなったためパネルを作成しましたが、二択ならconfirm()、単純に文字列を入力させるだけであればprompt()を使えば、ActionScriptに頼らなくともユーザーからの入力を取得することは可能です。

実際のスクリプトについて

「〜オブジェクト」といったカタカナ表記はFireworks上のモノ、「〜Object」といった英語表記の場合は、スクリプト上のモノを表します。

操作するオブジェクトの情報を取得する

fw.getDocumentDOM();

は、オブジェクトを追加したり、キャンバスの幅を変更したりと、ドキュメントそのものをスクリプトで操作する上でお世話になります。

fw.selection;

で、現在選択しているオブジェクトの情報を配列で取得できます。複数選択している場合は添字の小さいものが前面にあるオブジェクトになります。配列の中身はObject型のデータです。

オブジェクトの構造

四角内はプロパティ名です

取得したobjectからFont Mixerのテキスト処理で使う、TextRuns objectを取得します。

TextRuns objectの中には、TextAttrs objectとSingleTextRun objectが格納されています。TextAttrs objectは、テキストオブジェクト自体が持つサイズや行揃えなどのテキストプロパティです。

ただし、複数のテキストプロパティが存在する場合、1文字目のテキストプロパティがTextAttrs objectに格納されるようなので、テキストオブジェクト自体というより、「1文字目の情報」といった方がいいかもしれません。

SingleTextRun objectは配列になっており、テキストプロパティごとの情報が格納されています。画像の「\アウイエ!/」なら、フォントが異なるごとにSingleTextRuns objectがあるので、長さは3になります。

SingleTextRun objectにはTextAttrs objectとStringが格納されており、個別のテキストプロパティがTextAttrs object、それに対応する文字列がStringになります。実際にはもっとプロパティがあるのですが、画像にある「\アウイエ!/」を簡略化した構造で示すと下記のようになります。

textRuns: {  
	initialAttrs: {  
		face:"HiraKakuProN-W6"  
	},  
	textRuns:[  
		{  
			changedAttrs:{  
				face:"HiraKakuProN-W6"  
			},  
				characters:"uFF39"  
		},  
		{  
			changedAttrs:{  
				face:"HiraMinProN-W3"  
			},  
			characters:"uFF71uFF73"  
		},  
		{  
			changedAttrs:{  
				face:"HiraKakuProN-W6"  
			},  
			characters:"uFF72uFF74uFF01uFF0F"  
		}  
	]  
}

オブジェクトのプロパティを変更する。

テキストオブジェクト自体の変更は、initialAttrsプロパティを変更します。

個別に書き換える場合は、charactersをテキストプロパティごとに文字を切り分け、changedAttrsとセットでSingleTextRuns objectに設定します。また、変更するプロパティ値だけ設定すればよく、設定していないプロパティ値はinitialAttrsの値が適用されます。

例えば「あいうえお」という文字列で「う」だけフォントを変更する場合、「あい」「う」「えお」とcharactersを分割し、それぞれのchangedAttrsを設定してやります。

Font Mixerでは、プロパティを直接操作せず別途オブジェクトを生成して、SingleTextRuns objectに適用しています。

後処理をする。

ミックス後、フォント情報を得るために使ったテキストオブジェクトが不要になるため、これを削除します。

2つテキストオブジェクトを選択しているので一旦選択解除し、不要なオブジェクトを選択し直して削除する作戦にしました。

コマンド自体が、前面にあるテキストオブジェクト情報を利用するようにしているため、不要なオブジェクトが前面にあるという前提で処理をしています。

新しい配列を作成し削除するオブエジェクトを一旦退避した後、

fw.getDocumentDOM().selectNone();

で選択を解除します。

fw.selectionに退避したオブエジェクトを入れることで、削除するテキストオブジェクトだけを選択できます。

この状態で

fw.getDocumentDOM().deleteSelection( bFillDeletedArea );

を実行するとテキストオブジェクトが削除されます。bFillDeletedAreaは、ビットマップモードのみ有効な値で、削除した部分を塗りつぶし色で塗りつぶす(true)か、透明にする(false)かを選択できるオプションです。

デバッグとかテストとか

デバッグやテストですが、それらしいツールが見当たらなかったので、alert()を出しまくって検証しました。

最後に。

「混植できるコマンドとかないよなぁ」とふと思ったのが12月頭。それから冬期休暇中にちょっと作ってみようかなと思い立ち、実質2週間ぐらいかかって完成しました。

始めのうちはドキュメントもほとんど読まず、テキストオブジェクト自体のinitialAttrsを必死で設定していたので、1文字ずつ変更できないのかと諦めかけてました。しかし、何かのきっかけで、デフォルトで入っている単語の先頭を大文字変えるコマンドを眺めてたら、「ああ、こうすればいいのか」ということがわかり、何とか完成に至った次第です。

ドキュメントとかサンプル見るの大事ですね!以上。

tags