因为组内接到了一个翻译PSD内文本图层的需求,需要将文本翻译为特定语言的文本,这些需要通过开发Photoshop 插件来实现。
因为本人之前做插件的经历也不少。所以这次依然是我来开发Adobe Photoshop Plugins。经过一定的调研,我选择了UXP API来操作PSD并搭配React来制作用户UI界面。
网络上关于Adobe Photoshop UXP Plugins方面的开发经验或说明文档等资料比较少(中文教程与文档可以说是没有),所以本文会带你开发Adobe Photoshop UXP Plugins,f分享一下开发流程。
- 介绍Adobe插件
插件基本构成
- 清单文件
- 开发工具
使用方法
- 开发
- 调试
- 打包
- 总结
- 参考
首先,我认为它就是一个网页,它跟传统的exe,app之类的不是一个东西。本质上来说,它就是一个网页,写的用的都是前端的技术栈。通俗来讲,它就是在Photoshop上运行的一个网页。
它可以做什么?
首先我们可以利用UXP API来实现对于PS整个的操作,比如修改主题色(PS本身),修改PSD文档内容,打开PSD,将PSD保存为JPG,PNG等等一切可以手动操作的内容。
这些人工操作的内容,可以完全的转化为自动化过程,让插件去自动的完成自动化处理。(将手动操作转为自动化操作)
以前
使用CEP and ExtendScript构建的插件应用,对于我们传统的web开发人员不是很友好
- ExtendScript 是ECMAScript3,写法不符合主流
- 相关的依赖项写法很难受(比如操作图层的一些脚本,是使用的ExtendScript,而不是我们使用的ES6之类的)
- 在高版本的PS中会被逐渐弃用
现在
使用UXP Plugin API and ECMAScript6 开发 Adobe Photoshop 插件。
- 可以使用现代的HTML、CSS和JavaScript构建高性能插件的UXP API。
- 它是下一代插件API,目前适用于Photoshop22以及更高版本。
清单文件 (manifest.json)
一个插件,必须都要含有一个这样的文件manifest.json,清单位于插件的根文件夹中,必须命名为 manifest.json。
顾名思义,它是扩展的组成部分的清单,在这个清单里能大约看到这个插件的一些”规则“。
罗列和简单介绍一下一些常用的配置项,说之前,先看一个大致的文件,首先感受一下先。
|{ // 清单的版本。对于 Photoshop,这应该是4或更高。 "manifestVersion": 4, // 您可以在 Adobe 开发者控制台上获得您的唯一 ID "id": "YOUR_ID_HERE", // 清单的名称 3-45个字符 "name": "Name of your plugin", // 插件的版本号 格式必须为 x.y.z "version": "1.0.0", // 插件的初始化代码的路径,可以是JavaScript或html文件 "main": "index.html", // 表示插件支持的应用程序 最小版本和最大版本 "host": { "app": "PS", "minVersion": "23.0.0" }, // 描述你的插件添加到插件菜单和插件面板的条目信息 "entrypoints": [ { "type": "command", // command 或者是 panel "id": "commandFn", // 入口点的唯一标识 "label": { "default": "Show A Dialog" // 菜单项标签 } }, { "type": "panel", // command 或者是 panel "id": "panelName",// 入口点的唯一标识 "label": { "default": "Panel Name"// 菜单项标签 }, "minimumSize": {"width": 230, "height": 200}, // 定义面板的最小尺寸 "maximumSize": {"width": 2000, "height": 2000}, // 定义面板的最大尺寸 "preferredDockedSize": {"width": 230, "height": 300}, // 停靠时面板的首选大小 "preferredFloatingSize": {"width": 230, "height": 300}, // 浮动时面板的首选大小 // 面板图标 "icons": [ {"width":23,"height":23,"path":"icons/dark.png","scale":[1,2],"theme":["darkest","dark","medium"]}, {"width":23,"height":23,"path":"icons/light.png","scale":[1,2],"theme":["lightest","light"]} ] } ], "icons": [ { "width": 23, "height": 23, "path": "icons/icon_D.png", "scale": [ 1, 2 ], "theme": [ "dark", "darkest" ], "species": [ "generic" ] }, { "width": 23, "height": 23, "path": "icons/icon_N.png", "scale": [ 1, 2 ], "theme": [ "lightest", "light" ], "species": [ "generic" ] } ] }
| |
| -- |主页面 (index.html)
- 一个入口文件,这个html在web开发中非常常见,就不去叙述了。
严格上来说,最基础的就两个文件,位于根目录。当然主页面可能引用很多JS,CSS等文件,这就是属于前端知识点的范畴了,这里就不去叙述了。
当你有了Adobe Creative Cloud、Photoshop和Visual Studio Code之类的文本编辑器,你需要安装一个方便开发UXP的开发工具,在Adobe Creative Cloud中的所有应用程序中找到UXP Developer Tools,单击安装按钮进行安装。
安装过程就不细叙述了,可以参考UXP Developer Tools 安装。
使用教程:
创建插件
- 安装完成之后,打开可以看到类似于这样的界面,点击create plugin。
- 输入插件信息
- 这是填写信息之后的,这里模板我们选择ps-react-starter,它创建了一个简单的 React-based 插件。
保存插件
- 填写信息后点击确认,选择一个文件夹将此插件保存到这个位置。
- 选择完成位置后,可以看到一个提示。
- 这个提示适用于非纯JavaScript的插件,比如react vue等需要webpack的。
- 它的提示信息意思是:注意:你将需要从你的终端运行额外的命令,以便安装各种依赖和构建插件。请查阅新创建的插件中的README文件,了解全部步骤。
保存完成
- 保存完成后,你创建的插件将在这个页面中显示。
打开插件目录
- 点击••••(又名“ action”),选择Open Folder,打开插件的根目录,所有的开发都将在这个文件夹下。
将插件加载到Photoshop中
- 点击••••(又名“ action”),选择Load,它会将你的插件加载到Photoshop中,加载成功后你可以在Photoshop中看到它。
注意:如果你创建的插件是react,那么你不能直接加载插件到Photoshop中,你需要先阅读README文件,并按文件指引进行。
加载完成后就可以在Photoshop中看到它。
接下来你只需要编辑并修改插件内容,它的主要内容如果是纯JavaScript,那么它的代码在根目录下。否则它是react vue等构建的,那么它的主要内容在src目录下。
自动刷新插件
注意:如果你开发的插件不是纯Javascript,直接编辑插件代码,可能并不会生效,你需要监听文件变动并重新打包。
你可能需要在开发者工具中,点击对应插件的action,找到watch。将它打开,它将监听你代码中html,js,css的变动并刷新插件(重新加载)。
如果是由webpack构建的,你需要同时搭配npm run watch使用。
对比同样使用webpack作为构建工具的:
Chrome 插件
- 开发者在项目运行npm run watch后,仍然需要在插件管理页面手动刷新插件。
Adobe 插件
- 开发者在开发工具处点击Watch后,在项目运行npm run watch,当你更新内容时开发工具会直接帮助你刷新插件(类似于重新加载一样的效果)
经过对比Chrome插件来看的话,开发Adobe插件的开发流程更为简单方便,更利好开发者。
认识Photoshop API
比如我们可以添加一个,点击start弹窗提示出当前打开的文档名称。
这时候我们就需要另一个API,它叫Photoshop API,它允许你通过UXP来访问Photoshop DOM,这里的DOM不是我们网页上传统的DOM,在这里它可以表示一个文档,一个图层等等。。。
我们通过import进来Photoshop,并在按钮上添加点击事件,从app中调用showAlert方法,它会打开一个警告信息弹窗。
从app中我们可以拿到当前聚焦的文档,并获取它的名称。
当然我们可以通过DOM去修改图层的名称以及内容,比如。
认识模态执行
在上一步中,我们通过遍历当前文档的图层组,来修改每个图层的name为test。但是你可以看到,我并不是直接去遍历修改,而是被一个函数包裹起来了。
这就要引出一个概念叫模态执行,它的意思说如果你想修改文档的内容,那么必须要在模态执行中去修改。(core.executeAsModal)
在模态执行过程中,用户将无法操作到Photoshop的文档区域等等,避免程序处理过程中用户依然可以操作文档的情况。
如果你不在模态执行中去修改文档内容,它将不会生效并不会给予警告信息。
这样它就可以将你所有的图层的name改为test了,但是如果你需要修改图层内容和样式,就没法使用 DOM操作来修改了,我们需要一个比较底层的东西:batchPlay。
认识batchPlay
因为目前UXP仍然在开发中,比如对图层内容进行复杂的修改,它就无法使用DOM操作去修改了,因为并没有公开在其中,所以你只能使用batchPlay去修改。
batchPlay是一种用来访问尚未通过API公开的功能,将多个动作发送到Photoshop时间队列并返回结果。
batchPlay调用的内容比较简单,它是用一组JSON对象来描述的动作,batchPlay用于播放所传入的动作描述符。
获取
在这里,我们这个例子是获取,也就是get。
|
const cmd = {
_obj: 'get',
_target: {
_ref: [{ _ref: 'textLayer', _id: layer._id }],
},
}
const layerInfo = await action.batchPlay([cmd], {})- _obj:表示将要执行的操作。比如get,set,hide等等...
_target:表示目标,
- 内部的_ ref表示操作引用,比如这里我们查找文本图层,并且ID为layer._id的图层。
设置
在这里,我们这个例子是设置值,也就是set。
|
const cmd = {
_obj: 'set',
_target: {
_ref: [{
_ref: 'textLayer',
_id: layer._id
}],
},
to: {
_obj: 'textLayer',
textKey: '修改后的文本内容 ',
},
}
const layerInfo = await action.batchPlay([cmd], {})在set中,更改图层信息的动作描述中它相比get多了一个to属性,它代表修改后的图层信息,比如这里的textKey就是图层的内容。
获取的操作不需要在模态执行中执行,但是如果你是修改了Photoshop 的状态之类的,同上诉也是必须要在模态执行中操作的,比如我们设置文本图层的文本内容。
嵌套/特殊图层处理
当然,如果你在后续碰到了遍历的图层不是文本图层之类的情况,需要进行判断图层。该如何判断?
|
const KIND = constants.LayerKind
await app.activeDocument.layers.forEach(async (layer) => {
const kindType = layer.kind
if (kindType === KIND.GROUP) {
const childLayers = layer.layers
// 嵌套图层 递归处理
} else if (kindType === KIND.TEXT) {
// 文本图层处理
console.log('文本图层 layer', layer)
layer.name = 'test'
// 获取图层信息
const cmd = {
_obj: 'get',
_target: {
_ref: [{ _ref: 'textLayer', _id: layer._id }],
},
}
const setTextlang = {
_obj: 'set',
_target: {
_ref: [{ _ref: 'textLayer', _id: layer._id }],
},
to: {
_obj: 'textLayer',
textKey: '修改后的文本内容 ',
},
}
const layerInfo = await action.batchPlay([cmd, setTextlang], {})
console.log('layerInfo', layerInfo)
//
}
//
})可以通过提供的constants和layer的kind来判断该图层的类型。
当是文本图层时候,执行获取图层信息和更改图层内容的动作。
更多
更多关于batchPlay动作描述符的资料并没有在文档中提供,你可以利用监听器。通过动作,记录动作的描述符信息。(通过你的手动操作,来获取动作的信息)
require('photoshop').action.addNotificationListener(['all'], (event, descriptor) => {console.log("Event:" + event + " Descriptor: " + JSON.stringify(descriptor))});或者是使用https://github.com/jardicc/alchemist来监听生成动作描述符。
在上面提到过,这个插件本质上来说是个网页。所以我们的调试与开发WEB端一致(有少数兼容性问题)。
在我们的开发者工具中,点开action,可以看到Debug按钮。点击它,会打开一个与Chrome DevTools一样的控制台,所有的log和插件的UI元素的Elements界面都在这里可以看到,与Chrome DevTools使用体验一致。
对于我们插件的打包非常简单,它是在开发者工具中进行打包的,而你只需要点击一次按钮,并选择打包的输出位置即可。
点开action,可以看到Package按钮,点击它将会弹出一个文件夹选择器,选择你要保存到的目标位置。
类似这样,就代表打包插件成功了。你可以将插件包分发给其他人安装。
用于 Photoshop 的 插件是以.ccx 为扩展名的文件。在底层,这是一个压缩文件。
总的来说,如果会html,css,js,三剑客。那么开发一个Photoshop插件还是非常简单的。
当然文章中上诉内容,只是将庞大的插件体系简单描述了下,缺少很多细节上的东西,还是需要在阅读之后再去看官方文档以及官方论坛。
看文档的目的是对插件的大部分功能细节能有了解,并将它们联通起来。
看论坛的目的是看大家是如何开发插件的,并看他们碰到了什么问题,是如何解决的。(个人认为极其有帮助,很多你能碰到的问题都可以在论坛中查到。即使没有,去提问也能较快的得到答复。)
如果你在这过程中遇到了什么问题,请随时评论或联系我探讨。