【WEB Flex】 はんこ API ではんこ画像( png ) を取得

現在もともとのAPIが使え無くなっていますが、フリーフォントではんこ画像作成 というサービスを
稼働しています。あまりスマートなものではありませんがそれなりのものが作成できます。

オリジナルの API は、gif 画像ですが、Flex に取り込んで、回転加工付きで png をダウンロード
しようというアプリケーションですが、目的より、目的に到達する方法が、flex でアプリケーション
を作成する場合のサンプルになると思います

ブラウザでダウンロード
↓実行ページ



メインコード
業務アプリケーションでは無いので、通常あまり使う機会の無いコントロール
もありますが、そのような意味もあって、レイアウトは absolute を使って
指定位置にコントロールを置けるようにしています。
( API 側で画像の幅の最大値が 500 なのでそれにあわせてます )

いろいろ注意事項があるのですが、ソースコードがそれなりに長いので
要点の一覧だけここで挙げておいて、解説は別のページで行います。
1) flex 内部からの url エンコード
2) HSlider クラスのつまみをドラッグした時に表示されるテキストのスタイル
3) 整数を 16進数文字列に変換
4) Image クラスの complete イベントと プロパティセットのタイミング
5) 繰り返し描画をしない場合の beginBitmapFill のバグ
6) Image インスタンスの graphics プロパティを使った描画範囲( 画面全体になる )
7) Image インスタンスの回転と Matrix を使った回転描画の違い
8) Matrix を使った並行移動
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
	xmlns:mx="http://www.adobe.com/2006/mxml"
	initialize="initData();"
	layout="absolute"
>

<mx:Style source="Style.css" />

<mx:Script>
<![CDATA[

	import mx.controls.*;
	import mx.events.*; 
	import mx.formatters.*;
	import mx.graphics.codec.*;
	import flash.external.*;
	import flash.geom.*;

	private var post:URLLoader;
	private var hid:String;
	private var param:String;

	// *********************************************************
	// ログ表示
	// *********************************************************
	public function firebug(data:Object):void {

		var fmt:DateFormatter = new DateFormatter();

		fmt.formatString = "YYYY/MM/DD HH:NN:SS";
		var logdt:String = fmt.format( new Date );

		ExternalInterface.call(
			"console.log", logdt,
			data+""
		);

	}

	// *********************************************************
	// アプリケーションの初期化
	// *********************************************************
	public function initData():void {

		firebug( "initData" );

		getHanko();

		post = new URLLoader();

		// IO エラー
		post.addEventListener(IOErrorEvent.IO_ERROR, systemError);

		// 処理終了
		post.addEventListener(Event.COMPLETE, completeUpload);

	}

	// *********************************************************
	// はんこ画像取得(1) img
	// *********************************************************
	public function getHanko():void {

		firebug( "getHanko" );

		param = "?"
		param += "name=" + encodeURIComponent( hname.text );
		param += "&size=" + hsize.text;
		if ( option1.selected ) {
			param += "&type=" + "maru";
		}
		else {
			param += "&type=" + "shikaku";
		}

		var hcolor:int = cp.selectedColor;
		var hcolorString:String = hcolor.toString(16);
		hcolorString = ("000000" + hcolorString).substring( hcolorString.length );
		param += "&color=" + encodeURIComponent("#" + hcolorString );

		param += "&time=" + (new Date()).getTime();

		img.source = "getHanko.php" + param;

	}

	// *********************************************************
	// はんこ画像取得(2) img => img2
	// *********************************************************
	public function completeGet():void {

		firebug( "completeGet" );

		firebug( img.width + "," + img.height );

		img2.width = Bitmap(img.content).bitmapData.width;
		img2.height = Bitmap(img.content).bitmapData.height;

		firebug( img2.width + "," + img2.height );

		var matrix:Matrix = new Matrix(); 
		var hwidth:int = parseInt(hsize.text);

		switch( hSlider.value ) {
			case 0:
				matrix.rotate(0);
				matrix.translate(0, 0) 
				break;
			case 45:
				matrix.rotate(Math.PI/4);
				matrix.translate(hwidth/2, hwidth/2+hwidth/1.414) 
				break;
			case 90:
				matrix.rotate(Math.PI/2);
				matrix.translate(hwidth, 0) 
				break;
			case 135:
				matrix.rotate(Math.PI/2+Math.PI/4);
				matrix.translate(hwidth/2, hwidth/2+hwidth/1.414) 
				break;
			case 180:
				matrix.rotate(Math.PI);
				matrix.translate(hwidth, hwidth) 
				break;
			case 225:
				matrix.rotate(Math.PI+Math.PI/4);
				matrix.translate(hwidth/2, hwidth/2+hwidth/1.414) 
				break;
			case 270:
				matrix.rotate(Math.PI+Math.PI/2);
				matrix.translate(0, hwidth) 
				break;
			case 315:
				matrix.rotate(Math.PI+Math.PI/2+Math.PI/4);
				matrix.translate(hwidth/2, hwidth/2+hwidth/1.414) 
				break;
		}

		img2.graphics.clear();
		img2.graphics.beginBitmapFill( Bitmap(img.content).bitmapData, matrix, true );
		img2.graphics.drawRect(0, 0, img2.width, img2.height);
		img2.graphics.endFill();

	}

	// *********************************************************
	// はんこ画像をアップロード
	// *********************************************************
	public function getPng():void {

		firebug( "getPng" );

		var bmp:BitmapData = new BitmapData(img2.width, img2.height);
		bmp.draw(img2);

		var enc:PNGEncoder = new PNGEncoder();
		var png:ByteArray = enc.encode(bmp);

		var req:URLRequest = new URLRequest("./savePng.php");

		req.data = png;
		req.method = URLRequestMethod.POST;

		post.load(req);

		bmp.dispose();

	}

	// *************************************************
	// アップロード完了
	// *************************************************
	private function completeUpload(event:Event):void {

		firebug( "completeUpload" );

		var result:Array = (post.data).split(/\s/);

//		Alert.show(result[0] + "を保存しました。5分間有効です");

		firebug(result[0]);

		hid = result[0];

		OpenUrl(hid);

	}
			
	// *************************************************
	// IO エラー
	// *************************************************
	private function systemError(event:IOErrorEvent):void{
		Alert.show("IOError:" + event.text);
	}

	// *************************************************
	// 指定 URL でブラウザを開く
	// *************************************************
	public function OpenUrl(url:String):void {

		firebug( img.width + "," + img.height );
		firebug( img2.width + "," + img2.height );

		if ( url == 'image/' ) {
			Alert.show( "画像は保存されていません" );
		}
		else {
			var req:URLRequest = new URLRequest(url);
			navigateToURL(req, "_blank");
		}
	}

]]>
</mx:Script>

<mx:Button
	x="20" y="30"
	label="表示"
	click="getHanko()"
/>

<mx:Button
	x="400" y="30"
	label="png画像表示"
	click="getPng()"
/>

<mx:ColorPicker
	x="87" y="30"
	id="cp"
	showTextField="true"
	selectedColor="13369344"
	color="black"
/>

<mx:HSlider
	x="120" y="30"
	id="hSlider"
	minimum="0"
	maximum="315"
	value="0"
	snapInterval="45"
	labels="['正','', '右', '', '逆', '', '左', '']"
/>

<mx:RadioButton
	x="300" y="30"
	groupName="type" id="option1" label="丸"
	selected="true"
	fillColors="[0xFFFFFF,0xFFFFFF]"
/>
<mx:RadioButton
	x="340" y="30"
	groupName="type" id="option2" label="四角"
	fillColors="[0xFFFFFF,0xFFFFFF]"
/>

<mx:TextInput 
	x="20" y="80"
	id="hname"
	maxChars="2"
	text="山田"
	color="black"
/>

<mx:TextInput 
	x="100" y="80"
	id="hsize"
	maxChars="3"
	text="150"
	color="black"
/>

<mx:Image
	visible="false"
	id="img"
	x="40" y="120"
	complete="completeGet()"
/>

<mx:Image
	id="img2"
	x="40" y="120"
/>

</mx:Application>


PHP コード
メインは flex なので短いコードですが、これが無いと png 画像化できない
という重要なコードです。内容は、flex を配置しているサーバから API 側
のサーバーの橋渡しを行っています。

flex のセキュリティの制限で、表示だけならば別ドメインでも問題無いので
すが、データ化しようとするとエラーになります。ですから、自分のサーバー
経由で画像データを取得できるようにしています
getHanko.php
<?
header( "Content-Type: image/gif" );
header( "Expires: Wed, 31 May 2000 14:59:58 GMT" );

$url = "http://hanko-api.web-career.com/seal/?";
$name = urlencode($_GET['name']);
$url .= "name=$name";
$url .= "&size={$_GET['size']}";
$url .= "&type={$_GET['type']}";
$color = urlencode($_GET['color']);
$url .= "&color=$color";

print file_get_contents($url);
?>


savePng.php は、PNG 画像を flex から送ったデータで保存するおきまりのコードですが、
ファイル名をセッションID をそのまま使用してカレントに作成しています。

そして、アクセスがあるたびに、5分以上経過している画像ファイルを削除
するようにしてあります。

保存後、flex 側で ブラウザを使って表示するので、ファイル名を返しています
savePng.php
<?
session_start();
$id = session_id();


$fp = fopen( "php://input", "rb" );
$wfp = fopen( "$id.png", "wb" );

while( $ret = fread( $fp, 4096 ) ) {

	fwrite( $wfp, $ret );

}

fclose($wfp);
fclose($fp);

$DirHandle = @opendir("./");
if ( $DirHandle ) {
	$Target = readdir( $DirHandle );
	while( $Target !== false ) {

		if ( $Target != "." ) {
			$ext = strrchr( $Target, "." );
			$ext = strtolower($ext);
			if ( $ext == ".png" ) {
				$astamp = stat($Target);
				$laststamp = $astamp[9];
				if ( $laststamp < time() - 300 ) {
						@unlink($Target);
				}
			}
		}
		$Target = readdir( $DirHandle );

	}
	
	closedir( $DirHandle );
}


print $id . ".png\n";
?>