脚本解析photoshop文本属性

尊重原创,转载请在文首注明出处:http://blog.csdn.net/cai612781/article/details/78072531

在做unity项目中,用到了psd2ngui插件来把psd直接导出成prefab,psd2ngui的原理就是解析psd中图层的命名来生成组件。用的还是最早的版本,插件中导出的文本只有一个文本内容,其余属性例如颜色、字号、描边、投影都得另外设置,很麻烦,新版不知有没这功能,一直没有继续研究。于是当时就有了这么个想法,写个ps脚本,解析文本的属性并命名,再扩展插件来实现导出ui后不用额外修改文本属性。这也是很早很早之前做的,现在来总结下。

语言 

ps支持几种脚本语言,Mac平台有AppleScript,Windows平台有VBScript,以及两个平台都支持的JavaScript。我用的就是Js语言,后缀是jsx。

工具 

可以使用任何文本编辑来写代码,这里推荐Adobe的IDE,叫Adobe ExtendScript Tookit,支持断点调试,还可以通过Data Browser窗口查看对象的属性和方法。


脚本解析photoshop文本属性_第1张图片


语法

ps script一种方式是通过文本对象模型DOM(Document Object Model)来访问和修改psd中的对象的属性,直观而方便,但不能实现所有的ps操作。 

ps dom类结构图:

脚本解析photoshop文本属性_第2张图片

另一种方式是通过动作代理(ActionManager)的方式,可以实现比DOM更多的操作,但貌似不支持AppleScript。


动作代理主要类有ActionDescriptor,ActionReference,ActionList。

ActionDescriptor相当于一个存储属性和值的字典,

ActionList是一个存储相同属性的值的数组,

ActionReference存储一个action的引用,


举个栗子:

function emboss(inAngle, inHeight, inAmount)
{
	var keyAngleID = charIDToTypeID("Angl");
	var keyHeightID = charIDToTypeID("Hght");
	var keyAmountID = charIDToTypeID("Amnt");
	var eventEmbossID = charIDToTypeID("Embs");
	
	var filterDescriptor = new ActionDescriptor();
	filterDescriptor.putInteger(keyAngleID, inAngle);
	filterDescriptor.putInteger(keyHeightID, inHeight);
	filterDescriptor.putInteger(keyAmountID, inAmount);
	
	executeAction(eventEmbossID, filterDescriptor);
}

emboss(120, 10, 100);
运行该脚本,会给选中图层添加一个浮雕滤镜



插件

ActionManager看起来比DOM复杂繁琐,好在Adobe有个监听Action的插件,下载地址:

Windows: ScriptingListenerPlugInForWindow 

Mac:ScriptingListenerPlugInForMac 

下载解压后将ScriptListener.8li拷贝到Adobe Photoshop/Plug-ins/目录下即可。

重开ps,操作将会被记录在桌面的ScriptingListenerJS.log和ScriptingListenerVB.log中

切记不用时把该插件移除,需要时再复制到Plug-ins目录下,以免撑爆磁盘以及影响ps操作性能。或者使用下载的压缩包里带有两个开关插件的脚本。

脚本解析photoshop文本属性_第3张图片

上图记录了我打开一张图片,选中它,改变颜色三个步骤的脚步。


代码实现

##遍历所有文本图层

ps script中跟图层相关的对象有LayerSet(组),ArtLayer(图层),Layer(包括组和图层)。

function getTextLayers(layers)
{
	for(var i = 0, len = layers.length; i < len; i++)
	{
		if(layers[i].typename == "LayerSet")
		{
			getTextLayers(layers[i].layers)
		}
		else
		{
			if(layers[i].kind == LayerKind.TEXT)
			{
				//得到文本图层
			}
		}
	}
}


getTextLayers(app.activeDocument.layers);


##文本字号和颜色

var text = layer.textItem;//layer即上文得到的layers[i]
var color = text.color.rgb["hexValue"];
var size = text.size.value;



下面代码是怎么从layer获得ActionDescriptor, 需要先将layer设置为目标图层:

app.activeDocument.activeLayer = layer;

function char2Type(charId)
{
	return app.charIDToTypeID(charId);
}

function getActiveLayerDescriptor()
{
	var ref = new ActionReference();
	ref.putEnumerated(char2Type("Lyr "), char2Type("Ordn"), char2Type("Trgt"));
	return executeActionGet(ref);
}

var layerDesc = getActiveLayerDescriptor();


##描边/投影颜色

function getOutline(layerDesc)//layerDesc参数即上文通过layer获得的ActionDescriptor类型对象
{
	var isEffectVisible = layerDesc.getVal("layerFXVisible");//判断图层是否有可见效果
	if(!isEffectVisible)
	{
		return "";
	}
	var lfxDesc = layerDesc.getVal("layerEffects");//获得图层效果属性
	var dsDesc = lfxDesc ? lfxDesc.getVal("frameFX") : null;//获得图层描边属性
	//var dsDesc = lfxDesc ? lfxDesc.getVal("dropShadow") : null;//获得图层投影属性

	if(dsDesc == null)
	{
		return "";
	}
	var enable = dsDesc.getVal("enabled");//判断描边/投影是否启用
	if(!enable)
	{
		return "";
	}
	var rgbTxt = descToColorList(dsDesc, "color");//获得图层描边/投影颜色
	return changeToHex(rgbTxt);//转换成16进制
}

##渐变颜色

function getGradientFill(layerDesc)
{
	var isEffectVisible = layerDesc.getVal("layerFXVisible");
	if(!isEffectVisible)
	{
		return "";
	}
	var lfxDesc = layerDesc.getVal("layerEffects");
	var dsDesc = lfxDesc ? lfxDesc.getVal("gradientFill") : null;
	if(dsDesc == null)
	{
		return "";
	}
	var enable = dsDesc.getVal("enabled");
	if(!enable)
	{
		return "";
	}
	
	var graDesc = dsDesc.getVal("gradient");
	
	var colorList = graDesc.getVal("colors", false);//ps中可以有多个渐变颜色,unity中一般两个
	var result = "";
	for(s in colorList)
	{
		var rgbTxt = descToColorList(colorList[s], "color");
		result += changeToHex(rgbTxt) + ",";
	}
	result = result.substr(0, result.length - 1);
	return result;
}

##调用方法

///desc.getVal( keyList );方法的实现,获得desc的属性keyList对应的值
ActionDescriptor.prototype.getVal = function(keyList, firstListItemOnly)
{
	if (typeof(keyList) == 'string')
	{
		keyList = keyList.split('.');
	}
		
	if (typeof(firstListItemOnly) == "undefined")
	{
		firstListItemOnly = true;
	}
	
	if (keyList.length == 0)
	{
		return this;
	}
	
	keyStr = keyList.shift();
	keyID = makeID(keyStr);
	
	if (this.hasKey(keyID))
	{
		switch (this.getType(keyID))
		{
			case DescValueType.OBJECTTYPE://属性类型为Object
			{
				return this.getObjectValue(keyID).getVal(keyList, firstListItemOnly);
			}
			case DescValueType.LISTTYPE://属性类型为列表
			{
				var list = this.getList(keyID); 
				return list.getVal(keyList, firstListItemOnly);
			}	
			default: 
			{
				return this.getFlatType(keyID);
			}
		}
	}	
	else
	{
		return null;
	}
}


///将charID或stringID转换成TypeID
charID、string相当于底层定义的属性枚举
function makeID(keyStr)
{
	if (keyStr[0] == "'")
	{
		return app.charIDToTypeID(keyStr);
	}
	else
	{
		return app.stringIDToTypeID(keyStr);
	}
}

///根据不同类型获得最终的值
function getFlatType(desc, ID)
{
	switch (desc.getType(ID))
	{
		case DescValueType.BOOLEANTYPE:		return desc.getBoolean(ID);
		case DescValueType.STRINGTYPE:		return desc.getString(ID);
		case DescValueType.INTEGERTYPE:		return desc.getInteger(ID);
		case DescValueType.DOUBLETYPE:		return desc.getDouble(ID);
		case DescValueType.UNITDOUBLE:		return getPSUnitValue(desc, ID);
		case DescValueType.ENUMERATEDTYPE: 	return typeIDToStringID(desc.getEnumerationValue(ID));
		case DescValueType.REFERENCETYPE: 	return getReference(desc.getReference(ID));
		default: 				return desc.getType(ID).toString();
	}
}


///获得颜色rgb值
function descToColorList(colorDesc, colorPath)
{
	var i, rgb = ["'Rd  '", "'Grn '","'Bl  '"];
	var rgbTxt = [];
	
	colorDesc = colorDesc.getVal(colorPath);
	if (!colorDesc)
	{
		return null;
	}

	for (i in rgb)
	{
		rgbTxt.push( roundColor(colorDesc.getVal(rgb[i])));
	}
	return rgbTxt;
}

///转换成16进制颜色值
function changeToHex(rgbTxt)
{
	var value = "";
	for(var i = 0, len = rgbTxt.length; i < len; i++)
	{
		var string = rgbTxt[i].toString(16);
		if(string.length < 2)
		{
			string = "0" + string;
		}
		value += string;
	}
	return value;
}


运行

将代码保存为.jsx后缀文件,放在ps安装目录\Presets\Scripts\下

运行路径:ps中文件-脚本-.jsx

运行结果图

运行前:

脚本解析photoshop文本属性_第4张图片

运行后:

脚本解析photoshop文本属性_第5张图片


以上就是ps脚本解析psd中文本图层的描边、投影、渐变等属性的主要代码,文本还有许多其它属性,不过unity项目中基本不用。

完整的脚本在github:https://github.com/503913873/PhotoshopTextParse

该脚本写得比较早,脚本中一些方法写得比较繁琐,例如遍历图层是用ActionManager方式,没有文中采用DOM方式来得简洁易懂,

github里第二个脚本就是文中的方式。

貌似ps不同版本的代码也有些差异,该脚本在cs5、cs6中都运行过,基本都可以用,小概率会出现解析的文本字号带小数。


更详细的语法可以在Adobe官网上查看:http://www.adobe.com/devnet/photoshop/scripting.html

文笔不好,又是第一次写,写了好多天,ps script研究得也不深,基本是需要什么功能才去搜索,欢迎大家一起讨论学习。






你可能感兴趣的:(PhotoShop)