スポンサーサイト

Tags :
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

画像の色の分布を『xy色度図』にプロットしてみる(あと、CMYKプロファイルの事も少し)

MacOSX付属の『Grapher』を使った画像の色の分布図の作成その2。

今回は、カラーマネジメント関係の書籍等には必ず出てくる、馬蹄形の『xy色度図』上の座標に、画像の色の分布をプロットしてみます。

前のエントリではPhotoshopで画像を開き、JavaScriptを使ってサンプリングしたピクセルの『L*a*b*値』をファイルに書き出して、そのファイルをGrapherに読み込ませて表示させましたが、今回はピクセルの色を『xyY』の値で書き出すので、『L*a*b*』から『xyY』の値に変換する部分をJavaScriptに新たに書き加えました。

今回は色度を表す『xy』の二次元のグラフなので、明度を示す『Y』の値は使いません。

『L*a*b*』から『xyY』ヘの変換式は、
Color Conversion Algorithms 』(http://www.cs.rit.edu/~ncs/color/t_convert.html)や、
Color Conversion Library in ANSI C』 (http://www.tecgraf.puc-rio.br/~mgattass/color/ColorIndex.html)
あたりをを参考にしています。

PhotoshopのAdobeRGB環境で下のグラデーデョン画像を開き、JavaScriptを実行して書き出されたファイルをGrapherで読み込んで、色度図にプロットしてみます。


赤と緑の三角形の部分はそれぞれ『AdobeRGB』と『sRGB』の色域を表していますが、それらは色度図上の値をあらかじめ調べておいてグラフに数値を入力しました。また、グレーの馬蹄形の部分、360nm~830nmの各波長の単色光の色度を示す『スペクトル軌跡』も別途計算して求めた数値を読み込ませてプロットしてあります。


上のグラデーション画像を『JapanColor2001corted』のプロファイル、レンダリングインテントが『知覚的』の設定でCMYKに変換した場合の色の分布を見てみると、色域が以下の様に圧縮されているのが解ります。



次に、下の画像は始めからCMYKモードで作成した、CMYK各色100%と、その二次色のレッド、グリーン、ブルーをグラデーションにしたもので、


これを『JapanColor2001corted』で開いた状態でプロットしてみると、非常になめらかな色の分布を持つグラフがプロットされました。


同じ画像を、『某印刷所の本機(枚葉)の印刷物を測色して作成したプロファイル』を適用した状態でプロットすると、

ややいびつな輪郭でプロットされました。グリーン(上の頂点の部分)の色域がやや狭く、イエロー方向に転んでいるのが見て取れます。

ただ、これはいびつなプロファイルが悪いと言うのではなく、あくまで『プロファイル』として実際の印刷物の状態を表したものなので、特に、JapanColorをターゲットとした『印刷の標準化』が行われていない印刷会社では、おそらく色が合わない等の問題も出てくる筈なので、必ずしも『JapanColor2001corted』の方が良い結果になるとは言い切れないと思います。

今回プロットした画像を見て思ったんですが、『JapanColor2001corted』の不自然なほど整ったプロファイルは、『理想的なJapanColor』をターゲットにして最適化の処理がされた、『架空のプロファイル』に近いものの様な気がします。

カラーマネジメントの書籍等では『CMYKのカラー設定はJapanColor2001corted推奨』というのが殆どで、もちろん出鱈目なプロファイルで運用する事に比べればベストに近い選択という事で異論は無いんですが、画像をCMYK分解する時点ではトーンジャンプも無くキレイに分解されていても、後の工程でRIPにデバイスリンクプロファイルを噛ましたり、刷版や印刷で色を調整する必要が出るとすれば、『画像が劣化する要因が他の工程に移っただけ』とも考えられるので、その辺りもツッ込んで解説してあればなぁとか思った次第です。(画像の段階で劣化しないのが望ましいとは思いますが)

画像を『xyY』の数値で書き出すのに使ったJavaScriptは以下の通り。

■動作確認
MacOSX 10.5.7
Photoshop CS4
#target Photoshop

var samplingPixelsAsYxy = {
	
	'tristimulus_values': {
		/* 2 degrees */
		'ccTA_2': [109.850, 100.000, 35.585],
		'ccTC_2': [98.074,  100.000, 118.232],
		'D50_2': [96.422,  100.000, 82.521],
		'D55_2': [95.682,  100.000, 92.149],
		'D65_2': [95.047,  100.000, 108.883],
		'D75_2': [94.972,  100.000, 122.638],
		'F2_2': [99.187,  100.000, 67.395],
		'F7_2': [95.044,  100.000, 108.755],
		'F11_2': [100.966, 100.000, 64.370],

		/* 10 degrees */
		'A_10': [111.144, 100.000, 35.200],
		'C_10': [97.285,  100.000, 116.145],
		'D50_10': [96.720,  100.000, 81.427],
		'D55_10': [95.799,  100.000, 90.926],
		'D65_10': [94.811,  100.000, 107.304],
		'D75_10': [94.416,  100.000, 120.641],
		'F2_10': [103.280, 100.000, 69.026],
		'F7_10': [95.792,  100.000, 107.687],
		'F11_10': [103.866, 100.000, 65.627],
	
		/* PCS illuminant of AdobeRGB & sRGB profiles */
		'PCS_illuminant': [96.420, 100.000, 82.491],
		
		/* reference white in xyY coordinates */
		'chromaD65': [0.3127, 0.3290, 100.0]
	},


	'convertCIELabToXYZ': function(L, a, b) {
		var ref = this.tristimulus_values['PCS_illuminant']; //tristimulus of reference white
		var X, Y, Z;
		
		/* via http://www.cs.rit.edu/~ncs/color/API_JAVA/XYZSet.java
		var frac = (L + 16) / 116;
		if ( L < 7.9996 ) {
			Y = L / 903.3;
			X = a / 3893.5 + Y;
			Z = Y - b / 1557.4;
		} else {
			var tmp = frac + a / 500;
			X = tmp * tmp * tmp * ref[0];
			Y = frac * frac * frac * ref[1];
			tmp = frac - b / 200;
			Z = tmp * tmp * tmp * ref[2];
		}
		*/
		
		/* via http://www.tecgraf.puc-rio.br/~mgattass/color/CIELabtoXYZ.htm */
		var var_Y = ( L + 16 ) / 116;
		var var_X = a / 500 + var_Y;
		var var_Z = var_Y - b / 200;
		var_Y = Math.pow(var_Y, 3) > 0.008856 ? Math.pow(var_Y, 3) : ( var_Y - 16 / 116 ) / 7.787;
		var_X = Math.pow(var_X, 3) > 0.008856 ? Math.pow(var_X, 3) : ( var_X - 16 / 116 ) / 7.787;
		var_Z = Math.pow(var_Z, 3) > 0.008856 ? Math.pow(var_Z, 3) : ( var_Z - 16 / 116 ) / 7.787;
		X = var_X * ref[0];
		Y = var_Y * ref[1];
		Z = var_Z * ref[2];
		//
		return [X, Y, Z];
	},

	'main': function() {
		var t = new Date();
		var outputFile = '~/Desktop/xy_colors_64x64.txt';
		var sampling_size = 64;
		//var threshold = 10;
		var sampling_data = '';
		var ru = app.preferences.rulerUnits;
		app.preferences.rulerUnits = Units.PIXELS;
		var doc = app.activeDocument;
		doc.flatten();
		doc.resizeImage(sampling_size, sampling_size, 72, ResampleMethod.BICUBIC);
		//doc.resizeImage(sampling_size*2, sampling_size*2, 72, ResampleMethod.NEARESTNEIGHBOR);
		
		var smpl = doc.colorSamplers.add([0, 0]);
		var col = new SolidColor();
		var Yxy_x, Yxy_y;
		var XYZ = [];
		var X, Y, Z;
		for (var i=0; i<sampling_size; i++) {
			for (var j=0; j<sampling_size; j++) {
				smpl.move([j+0.5, i+0.5]);
				col = smpl.color;
				XYZ = this.convertCIELabToXYZ(col.lab.l, col.lab.a, col.lab.b);
				X = XYZ[0]; Y = XYZ[1]; Z = XYZ[2];
				Yxy_x = X / ( X + Y + Z );
				Yxy_y = Y / ( X + Y + Z );
				if (Y > 1) { sampling_data += Yxy_x + '\t' + Yxy_y + '\r'; }
			}
		}
		
		var f = new File(outputFile);  
		if (f.open("w")) {  
			f.encoding = 'BINARY';  
			f.write(sampling_data);
			f.close();
		}
		app.preferences.rulerUnits = ru;
		var now = new Date();
		alert('Finished!   time: ' + (now - t));
	}
}

if (app.documents.length > 0) {
	samplingPixelsAsYxy.main();
}

画像の色の分布を3Dグラフにプロットしてみる

MacOSXに付属しているグラフ作成アプリ『Grapher』を使って、画像の色(L*a*b*値)の分布を3Dのグラフにプロットしてみました。

この手の情報の表示は、高価なカラーマネジメントツールを買うとか、年間18万9千円の会費を払って研究会に参加すれば実現できると思いますが、手持ちのツールを使えばまあそこそこ近い事は出来るのではないかと思いやってみました。先日のエントリ、『PhotoshopでのCMYK分解カーブをJavaScriptを使ってプロットしてみる』を書いたのも、そんなふうに思ったからです。

まず、JavaScriptを使ってPhotoshopで開いている画像を64 x 64ピクセルに縮小し、その4096のポイントをサンプリングしてそれぞれのL*a*b*値をタブ区切りのテキストでファイルに書き出します。そしてそれをGrapherで読み込んで、3Dのグラフにプロットする様にしました。

Photoshopのカラー設定でAdobeRGBが設定された状態で、下のグラデーション画像のL*a*b*値を書き出してプロットすると、



AdobeRGBの色域一杯に色が均等に分布したグラフが現れました。

このグラフは横軸にa*チャンネル(-128~128)、縦軸がb*チャンネル(-128~128)、垂直のZ軸がL*チャンネル(0~100)になっています。

同じ画像がsRGBの色空間だと、

こんな感じになります。(比較の為にAdobeRGBのプロットを表示しています)

そして下の画像はAdobeRGBのカラースペースで開いてプロットしましたが、色の分布はsRGBの空間に収まっているようです。画像を保存するまでの過程でプロファイル変換されているのかも知れません。




Photoshop&JavaScriptでピクセルの色を取得してファイルに書き出す所で、CMYK分解カーブのプロットの時と同じく、ピクセル単位での操作が今ひとつ解らず、色を取得するのに1ピクセルの選択範囲を作ってその中のヒストグラムが立った数値を調べるという回りくどい事をしています。(なので処理は劇遅。カラーサンプラーを置いて取得するのも試みましたが、さらに遅かったです)

あと、Photpshopで色を扱う、『SolidColor』オブジェクトは、RGBなど1つのカラーモードの値を与えてやれば、後はカラー設定の作業用スペースに設定されたプロファイルに応じて、CMYKやL*a*b*の値が一意に決まるので簡単に他のカラーモードでの値を取り出せるみたいです。ただ、ドキュメントのカラープロファイルは無視される様なので注意が必要です。

■2009.06.06追記
ちょっとやっつけだったコードを直しました。ピクセルの色の取得にカラーサンプラを使う様に変更してあります。ほんの少しだけですが速くなりました。


■2010.03.08追記
このエントリで使用している『Grapher』のファイルをアップロードしました。以下のリンクから落とせます。
http://dl.dropbox.com/u/271700/cielab.gcx.zip


■画像のL*a*b*値を書き出すJavaScriptのソース(遅いです)
#target Photoshop

/*
function getPixelColor(x, y) {
	var hg;
	doc.selection.select([[x, y], [x + 1, y], [x + 1, y + 1], [x, y + 1]], SelectionType.REPLACE, 0, false);
	hg = doc.channels[0].histogram;
	for (var i=0; i<256; i++) if (hg[i] != 0) { colObj.rgb.red = i; break; }
	hg = doc.channels[1].histogram;
	for (var i=0; i<256; i++) if (hg[i] != 0) { colObj.rgb.green = i; break; }
	hg = doc.channels[2].histogram;
	for (var i=0; i<256; i++) if (hg[i] != 0) { colObj.rgb.blue = i; break; }
}
*/

var t = new Date();
var sampling_size = 64;
var ru = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var doc = app.activeDocument;
doc.flatten();
doc.resizeImage(sampling_size, sampling_size, 72, ResampleMethod.BICUBIC);
doc.resizeImage(sampling_size*2, sampling_size*2, 72, ResampleMethod.NEARESTNEIGHBOR);

var smpl = doc.colorSamplers.add([0, 0]);
var sampling_data = '';

for (var i=0; i<sampling_size*2; i+=2) {
	for (var j=0; j<sampling_size*2; j+=2) {
		smpl.move([j+1, i+1]);
		sampling_data += smpl.color.lab.a + '\t' + smpl.color.lab.b + '\t' + smpl.color.lab.l + '\r';
	}
}

var f = new File('~/Desktop/clelab_colors_64x64.txt');  
if (f.open("w")) {  
	f.encoding = 'BINARY';  
	f.write(sampling_data);
	f.close();
}

app.preferences.rulerUnits = ru;
alert('Finished.   time: ' + (new Date() - t));

JacvaScriptでInDesignやIllustratorのドキュメントに配置されているEPS画像の情報を取得する

じわじわと過去の物にされようとしているEPSですが、まだまだ製版の現場では出力用の画像として幅を利かせてると思います。が、InDesignやIllustratorでは配置されているEPS画像の解像度等の情報をJavaScriptで得るには画像ファイルを直接調べてみるしか無い様なので、そのための関数を書きました。

一応、IllustratorとInDesign両方で動く様になっています。InDesignの場合はEPSクラスのprototypeを拡張する書き方の方が良いかもしれませんが。

InDesignのJavaScript書く時いつも参考にしているサイト InDesign Scripting ReferenceのEPSクラスのプロパティの所に解像度を示す『actualPpi』『effectivePpi』の記述があるので、もしや?と思ったんですけど本当に値を持ってるのか確認できず…(for ~ inでオブジェクトを見たんですが解像度がらみのプロパティは表示されませんでした。ひょっとして英語版との差とかあるんでしょうか)

■使用例
以下の様に、配置された画像を引数として渡します。
/*
	epsObj は処理するEPS画像

	InDesignの場合はallGraphics等で得られるEPSオブジェクト
	Illustratorの場合はPlacedItemオブジェクトを渡す

*/

 //クリエータを得る
var creator = epsInfo.getCreator(epsObj);

//カラーモードを得る
var mode = epsInfo.getMode(epsObj);

 //エンコーディングを得る
var encoding = epsInfo.getEncoding(epsObj);

 //解像度を得る
var ppi = epsInfo.getPpi(epsObj);

//まとめて連想配列で得る
var epsInfoObj = epsInfo.getAll(epsObj);
最近のPhotoshopやIllustratorで保存されたEPSなら対応出来てると思いますが、変な訛りのあるEPSだとちゃんと動作しないかも知れません。処理出来なかった場合はundefinedが返ります。

■動作確認
MacOSX 10.5.7
InDesign CS4
Illustrator CS4
var epsInfo = {
	'parseHeader': function(imgObj) {
		if (imgObj.constructor.name == 'EPS') { 
			var f = new File(imgObj.itemLink.filePath); // InDesign
		} else if (imgObj.constructor.name == 'PlacedItem') {
			var f = new File(imgObj.file); // Illustrator
		} else {
			return [];
		}
		if (!f.open("r")) return [];
		for (var i=0; i<500; i++) {
			var line = f.readln();
			if (line.indexOf("%%Creator", 0) != -1) {
				var creator = line.slice(10);
			} else if (line.indexOf("%%HiResBoundingBox", 0) != -1) {
				var hrbbParams = line.split(" ");
			} else if (line.indexOf("%ImageData", 0) != -1) {
				var idParams = line.split(" ");
				break;
			}
		}
		f.close();
		return [creator, hrbbParams, idParams];
	},

	'getMode': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		if (!headerObj[2]) return;
		var idParams = headerObj[2];
		switch(idParams[4]) {
			case "4":
				return "CMYK";
				break;
			case "3":
				return "RGB";
				break;
			case "2":
				return "Lab";
				break;
			case "1":
				return (idParams[3] == 1) ? "1bit" : "Gray";
				break;
			default:
				return;
		}
	},

	'getEncoding': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		if (!headerObj[2]) return;
		var idParams = headerObj[2];
		switch(idParams[7]) {
			case "7":
				return "ASCII85";
				break;
			case "6":
				return "JPEG:Max";
				break;
			case "5":
				return "JPEG:High";
				break;
			case "4":
				return "JPEG:Mid";
				break;
			case "3":
				return "JPEG:Low";
				break;
			case "2":
				treturn "ASCII";
				break;
			case "1":
				return "Binary";
				break;
			default:
				return;
		}
	},

	'getPpi': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		if (!headerObj[1] || !headerObj[2]) return;
		var hrbbParams = headerObj[1];
		var idParams = headerObj[2];
		var pt_x = hrbbParams[3];
		//var pt_y = hrbbParams[4];
		var px_x = idParams[1];
		//var px_y = idParams[2];
		return (px_x / pt_x * 72).toFixed(1);
	},

	'getCreator': function(imgObj) {
		var headerObj = this.parseHeader(imgObj);
		return headerObj[0] ? headerObj[0] : undefined;
	},

	'getAll': function(imgObj) {
		return {
			'creator': this.getCreator(imgObj),
			'mode': this.getMode(imgObj),
			'encoding': this.getEncoding(imgObj),
			'ppi': this.getPpi(imgObj)
		}
	}
}

PhotoshopでのCMYK分解カーブをJavaScriptを使ってプロットしてみる

Photoshopのカラー設定のプロファイルでCMYK分解する際のアミ%をプロットするJavaScriptを書いてみました。

分解カーブは、ニュートラルグレー(RGB 0,0,0~255,255,255)を分解した際のアミ%でプロットされ、カラープロファイル名やグレーバランスでのCMYKの最大値、インキの総量を表示します。

PhotoshopでこのJavaScriptを実行すると、以下の様な画像が生成されます。

Japan Colorが設定されている場合の分解カーブ。


こちらはSWOP。シャドウ側のCMYのグレー成分ががスミ版に置き換わっています


某社の広色域インキ用プロファイル。グレーバランス出すのにマゼンタとイエローがかなり少なめの設定になってる様です。


■課題というか問題点というか…
JavaScriptで『任意の位置にピクセルを描画する』やり方が良くわからなかったので、プロットする点のサイズの選択範囲を作り、それを塗り潰していく様なやり方なので処理がメチャクチャ遅いです(ウチのMacBook Proで全部描画するのに2分以上かかります)。パスを使って描いても良かったかも知れません。

■動作確認
MacOSX 10.5.6
Photoshop CS4
#target Photoshop

function setRgb(r, g, b){
	colObj = new SolidColor()
	colObj.rgb.red = r;
	colObj.rgb.green = g;
	colObj.rgb.blue = b;
	return colObj;
}

function plotColor(c_val, plot_color){
	c_val *=  plot_ratio;
	doc.selection.select([[510-i*2, 290-c_val-1.5], [510-i*2+2, 290-c_val-1.5], [510-i*2+2, 290-c_val+1], [510-i*2, 290-c_val+1]]);
	doc.selection.fill(plot_color, ColorBlendMode.NORMAL, 100, false);
}

function makeText(props) {
	lay = doc.artLayers.add();
	lay.kind = LayerKind.TEXT;
	ti = lay.textItem
	ti.font = 'HiraMinPro-W6';
	ti.contents = props.contents;
	ti.size = props.size;
	ti.color = props.color;
	ti.position = props.position;	
}

var ru = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
var doc = app.documents.add(512, 320, 72, "CMYK Separation Curves", NewDocumentMode.CMYK);
var profile_name = doc.colorProfileName;
doc.changeMode(ChangeMode.RGB);

var colors = {
	'cyan': setRgb(0, 255, 255),
	'magenta': setRgb(255, 0, 255),
	'yellow': setRgb(255, 255, 0),
	'black': setRgb(0, 0, 0),
	'gray': setRgb(128, 128, 128),
	'white': setRgb(255, 255, 255),
	'line': setRgb(116, 116, 116)
};

var plot_ratio = 2.5;

doc.selection.selectAll();
doc.selection.fill(colors.gray, ColorBlendMode.NORMAL, 100, false);

for (var i=40; i<290; i+=25) {
	doc.selection.select([[0, i],[512, i],[512, i+1],[0, i+1]]);
	doc.selection.fill(colors.line, ColorBlendMode.NORMAL, 100, false);
}
var tgt_color = new SolidColor();

for (var i=255; i>-1; i--) {
	doc.selection.select([[510-i*2, 290], [510-i*2+2, 290], [510-i*2+2, 320], [510-i*2, 320]]);
	tgt_color.rgb.red = i;
	tgt_color.rgb.green = i;
	tgt_color.rgb.blue = i;
	doc.selection.fill(tgt_color, ColorBlendMode.NORMAL, 100, false);
	plotColor(tgt_color.cmyk.cyan, colors.cyan);
	plotColor(tgt_color.cmyk.magenta, colors.magenta);
	plotColor(tgt_color.cmyk.yellow, colors.yellow);
	plotColor(tgt_color.cmyk.black, colors.black);
}

doc.selection.deselect();
var max_cmyk = tgt_color.cmyk;
var total = max_cmyk.cyan + max_cmyk.magenta + max_cmyk.yellow + max_cmyk.black;
var ink_info = 'Max\r'
			+ 'Cyan : ' + Math.round(max_cmyk.cyan) + '%\r'
			+ 'Magenta : ' + Math.round(max_cmyk.magenta) + '%\r'
			+ 'Yellow : ' + Math.round(max_cmyk.yellow) + '%\r'
			+ 'Black : ' + Math.round(max_cmyk.black) + '%';
			
makeText({'contents': profile_name, 'size': 20, 'color': colors.white, 'position': [10, 30]});
makeText({'contents': ink_info, 'size': 12, 'color': colors.white, 'position': [10, 55]});
makeText({'contents': 'Total\r' + Math.round(total) + '%', 'size': 12, 'color': colors.white, 'position': [120, 55]});

app.preferences.rulerUnits = ru;

InDesignでTwitterのAPIにアクセスし、タイムラインを取得してドキュメントに自動レイアウトしてみる

JavaScriptを使ってInDesignでTwitter APIにアクセスし、タイムラインのステータスを取得して、followしている人たちの発言をInDesignのドキュメントに自動でレイアウトしてみます。

TwitterのAPIではfollowしている人たちの発言の他にも、自分の発言のみ、@Repliesなんかを取得出来ますが、今回は『http://twitter.com/home』で表示される『自分がfollowしている人たちの発言』を取り出して、それをアイコン付きで生成したドキュメントにレイアウトして行く様にしてみました。

■参考URL 『Twitter API 仕様書 日本語訳』
http://watcher.moe-nifty.com/memo/docs/twitterAPI.txt

Socketを使ったWebへのアクセスには、以前のエントリで書いた、Basic認証とファイルのダウンロードが出来るスクリプトをそのまま使っています。Basic認証には、『ユーザID:パスワード』の文字列をBase64エンコードして渡す必要がありますが、今回はその部分は省略してあらかじめエンコードした文字列を用意しています。

MacOSXの場合はターミナルで、
php -r 'echo base64_encode("ユーザID:パスワード")."\n";'
とすればBase64エンコードされた文字列を生成出来ます。

スクリプトの実行結果はこんな感じになります(今回は単ページ)。ドキュメントは一から生成するので雛形は必要ありません。配置するアイコン等の画像は全てローカル(デフォルトではデスクトップ)にダウンロードして保存されます。

旬のDTP Boosterさんfollowさせてもらってます。

Webブラウザで見るのと似た様なレイアウトってのも芸が無い気がしますけど。テキストも最低限オーバーフローしないための処理しかしてないので組版なんて呼べる代物ではありませんが…

一応ドキュメントサイズの変更にはある程度追従出来る様な書き方にはなってますが、本当はレイアウトを自動生成する部分をもうちょっとキレイに、と言うか、CSS的な文脈で書ければ良いなと思ったりもしたんですけど今回は全然煮詰められませんでした(相変わらず思い付くままに書き殴ったようなコードです)。何かテンプレートエンジン的な物が用意出来れば良いんでしょうかね。

■動作確認
MacOSX 10.5.6
InDesign CS4
#target InDesign

//HTTPレスポンスを得る
function getHttpResponse(requests) {
	var parseUrl = function(url) {
		var urlObj = {};
		//[url, scheme, slash, host, port, path, query, fragment] via O'REILLY JavaScript: The Good Parts
		var url_re = /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/;
		var m = url_re.exec(url);
		urlObj.host = m[3];
		urlObj.port = m[4] || '80';
		urlObj.path = m[5];
		urlObj.query = (m[6]) ? '?' +  m[6] : '';
		urlObj.frag = (m[7]) ? '#' + m[7] : '';
		return urlObj;
	}
	var urlObj = parseUrl(requests.url);
	var encoding = requests.encoding || 'UTF-8';
	var method = requests.method || 'GET';
	var auth = (requests.basic_auth != undefined) ? 'Authorization: Basic ' + requests.basic_auth + '\r\n' : '';
	var conn = new Socket;
	conn.timeout = 10;
	if (conn.open (urlObj.host + ':' + urlObj.port, encoding)) {
  	conn.write (method + ' /' + urlObj.path + urlObj.query + urlObj.frag + ' HTTP/1.0\r\n'
		+ 'Host: ' + urlObj.host + '\r\n'
		+ 'User-Agent: ' + 'InDesign/6.0' + '(Macintosh; U; Intel Mac OS X 10_5_6; ja-jp)' + '\r\n'
		+ auth
		+ '\r\n');
		var reply = conn.read(999999);
		conn.close();
		return reply; //ヘッダ込みで返す
	} else {
		return conn.error;
	}
}


//Webからファイルをダウンロードする
function downloadFile(url, localFile) {
	var rep = getHttpResponse({
		method: 'GET',
		url: url,
		encoding: 'BINARY',
	});
	if (rep.match(/HTTP.*\d{3}/).toString().indexOf('200') != -1) {
		//レスポンスのヘッダを除去 
		var body = rep.slice(rep.indexOf('\r\n\r\n') + 4);
		//ファイルに書き出し
		var f = new File(localFile);
		if (f.open("w")) {
			f.encoding = 'BINARY';
			f.write(body);
		}
		f.close();
		return f;
	} else {
		return false;	
	}
}


//RGBカラーを作成
function setRgb(cv) {
	return app.activeDocument.colors.add({
		model: ColorModel.process,
		space: ColorSpace.rgb,
		colorValue: cv
	});
}

TextFrame.prototype = { 
	getHeight: function() { return this.geometricBounds[2] - this.geometricBounds[0]; },
	getWidth: function() { return this.geometricBounds[3] - this.geometricBounds[1]; },
}


//初期設定
var id = 'your_twitter_account';
var b64_userpass = 'ZHVtbXk6ZHVtbXk='; //'username:password'をbase64エンコードした文字列(値はダミー)
var downloadPath = '~/Desktop/'; //ダウンロードしたファイルの保存場所
var cnt = 15; //1ページにレイアウトするPOSTの数
var doc = app.documents.add();
doc.documentPreferences.pageWidth = '110mm';
doc.documentPreferences.pageHeight = '150mm';
var fontName1 = app.fonts.item('小塚ゴシック Pro');
var tu = 'pt';
var default_txtSize = 7; //標準の文字サイズ
var default_txtLead = 9; //標準の行送り	
var container_margin = {
	top: 10,
	left: 7,
	right: 7,
	bottom: 3
};
var my_colors = {
	'white': setRgb([255, 255, 255]),
	'background': setRgb([148, 228, 232]),
	'profile': setRgb([218, 255, 130]),
	'link': setRgb([0, 0, 255])
};

var postRects = [];
var textRects = [];
var iconRects = [];
var iconImgs = [];

var p = doc.pages[0];
var p_b = p.bounds;

var logo = {
	url: 'http://assets0.twitter.com/images/twitter_logo_header.png',
	localFile: downloadPath + 'twitter_logo_header.png'
};

//APIでプロファイルを取得
var profile_info = getHttpResponse({
	url: 'http://twitter.com/users/show/' + id + '.json'
});

var profile_json = eval('(' + profile_info.match(/\{.*\}?/) + ')');
var profile_icon = {
	url: profile_json.profile_image_url,
	localFile: downloadPath + profile_json.profile_image_url.replace(/^.*\//, '')
}

//twitter_logo_header.png, profile_iconをダウンロード
var logo_file = downloadFile(logo.url, logo.localFile);
var profile_icon_file = downloadFile(profile_icon.url, profile_icon.localFile);

//APIでtimelineを取得
var timeline = getHttpResponse({
	url: 'http://twitter.com//statuses/friends_timeline.json?page=1',
	basic_auth: b64_userpass
});
timeline.replace('Connection', '');
tlObj = eval('(' + timeline.match(/\[\{.*\}\]?/) + ')');

//背景の矩形
var bgRect = p.textFrames.add({
	geometricBounds: [p_b[0] - 3, p_b[1] - 3, p_b[2] + 3, p_b[3] + 3],
	fillColor: my_colors.background
});
//ヘッダ部分
var logoRect = p.textFrames.add({
	geometricBounds: [p_b[0] + 2, p_b[1] + container_margin.left, p_b[0] + container_margin.top - 1, p_b[1] + 40],
	fillColor: my_colors.background,
	strokeWeight: 0
});
logoRect.place(logo_file);
logoRect.fit(FitOptions.proportionally);
var profile_rect = p.textFrames.add({
	geometricBounds: [p_b[0] + 2, (p_b[3] - p_b[1]) / 2, p_b[0] + container_margin.top - 1, p_b[3] - container_margin.right],
	fillColor: my_colors.profile,
	strokeWeight: 0
});
var pr_gb = profile_rect.geometricBounds;
var profile_icon_rect = p.textFrames.add({
	geometricBounds: [pr_gb[0], pr_gb[1], pr_gb[0] + profile_rect.getHeight(), pr_gb[1] + profile_rect.getHeight()],
	fillColor: my_colors.background,
	strokeWeight: 0
});
profile_icon_rect.place(profile_icon_file);
profile_icon_rect.fit(FitOptions.proportionally);
var title_rect = p.textFrames.add({
	geometricBounds: [pr_gb[0] + 2, profile_icon_rect.geometricBounds[3], pr_gb[2], pr_gb[3]],
	strokeWeight: 0,
	contents: id + '\'s timeline'
});
title_rect.paragraphs[0].properties = {
	pointSize: 9 + tu,
	appliedFont: fontName1,
	fontStyle: 'R',
	justification: 1667591796
};
//全てのPOSTを納める矩形
var container = p.textFrames.add({
	geometricBounds: [p_b[0] + container_margin.top, p_b[1] + container_margin.left, p_b[2] - container_margin.bottom, p_b[3] - container_margin.right],
	fillColor: my_colors.white
});
var container_gb = container.geometricBounds;
var container_h = container_gb[2] - container_gb[0];
var post_h = container_h / cnt; //各POSTの矩形の高さ


//各POSTをレイアウトして行く
for (var i=0; i<cnt; i++) {
	txtSize = default_txtSize;
	txtLead = default_txtLead;
	//JSONオブジェクトから情報を取り出す
	var tl_text = tlObj[i].text.replace(/</g, '<').replace(/>/g, '>').replace(/\r\n/g, ' ').replace(/\r/g, ' ').replace(/\n/g, ' ');
	var tl_name = tlObj[i].user.name;
	//各POSTの矩形
	var pr = postRects[i] = p.textFrames.add({
		geometricBounds: [container_gb[0] + post_h * i, container_gb[1] + 2, container_gb[0] + post_h * i + post_h, container_gb[3] - 2],
		fillColor: my_colors.white
	});
	var pr_gb = pr.geometricBounds;
	//textを納めるフレーム
	var tr = textRects[i] = p.textFrames.add();
	tr.properties = {
		geometricBounds: [pr_gb[0] + 1, pr_gb[1] + post_h - 1, pr_gb[0] + post_h - 1, pr_gb[3]],
		fillColor: my_colors.white,
		contents: tl_name + ' ' + tl_text,
	};
	tr.paragraphs[0].properties = {
		pointSize: txtSize + tu,
		leading: txtLead + tu,
		appliedFont: fontName1,
		fontStyle: 'R',
		kerningMethod: 'メトリクス'
	};
	//name部分のスタイル
	for (var j=0; j<tl_name.length; j++) {
		tr.paragraphs[0].characters[j].properties = {
			fillColor: my_colors.link,
			fontStyle: 'H',
			pointSize: '7.5' + tu
		};
	}
	//テキストがオーバーフローしている場合の処理
	tr_para = tr.paragraphs[0];
	while (tr.overflows && tr_para.horizontalScale > 90) {
		tr_para.horizontalScale--;
	}
	if (tr.overflows) {
		tr_para.horizontalScale = 100;
		tr_para.properties = {
			pointSize:'6' + tu,
			leading: '7' + tu
		};
		while (tr.overflows) {
			tr_para.horizontalScale--;
		}
	}
	//アイコンの情報を格納()
	iconImgs[i] = {
		url: encodeURI(tlObj[i].user.profile_image_url),
		localFile: downloadPath + tlObj[i].user.profile_image_url.replace(/^.*\//, '')
	};
	if (!new File(iconImgs[i].localFile).exists) {
		downloadFile(iconImgs[i].url, iconImgs[i].localFile); //アイコンの画像ををダウンロード
	}
	//アイコン用の矩形
	iconRects[i] = p.textFrames.add({
		geometricBounds: [pr_gb[0] + 1, pr_gb[1], pr_gb[0] + post_h - 1, pr_gb[1] + post_h - 2],
	});
	iconRects[i].place(new File(iconImgs[i].localFile)); //アイコンの画像を配置
	iconRects[i].fit(FitOptions.proportionally);
}
Profile
choco
Author : choco

印刷・製版の現場を経て、広告制作会社でPhotoshopを使ったビジュアル制作を担当。

→現在は車載機器開発ベンダにて、組み込み3Dデータ作成やUIデザインなどを行っています。

Categories
Favorites


Search
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。