因为组内接到了一个翻译PSD内文本图层的需求,需要将文本翻译为特定语言的文本,这些需要通过开发Photoshop 插件来实现。

因为本人之前做插件的经历也不少。所以这次依然是我来开发Adobe Photoshop Plugins。经过一定的调研,我选择了UXP API来操作PSD并搭配React来制作用户UI界面。

网络上关于Adobe Photoshop UXP Plugins方面的开发经验或说明文档等资料比较少(中文教程与文档可以说是没有),所以本文会带你开发Adobe Photoshop UXP Plugins,f分享一下开发流程。

  1. 介绍Adobe插件
  2. 插件基本构成

    1. 清单文件
  3. 开发工具
  4. 使用方法

    1. 开发
    2. 调试
    3. 打包
  5. 总结
  6. 参考

首先,我认为它就是一个网页,它跟传统的exe,app之类的不是一个东西。本质上来说,它就是一个网页,写的用的都是前端的技术栈。通俗来讲,它就是在Photoshop上运行的一个网页。

它可以做什么?

首先我们可以利用UXP API来实现对于PS整个的操作,比如修改主题色(PS本身),修改PSD文档内容,打开PSD,将PSD保存为JPG,PNG等等一切可以手动操作的内容。

这些人工操作的内容,可以完全的转化为自动化过程,让插件去自动的完成自动化处理。(将手动操作转为自动化操作)

以前

使用CEP and ExtendScript构建的插件应用,对于我们传统的web开发人员不是很友好

  1. ExtendScript 是ECMAScript3,写法不符合主流
  2. 相关的依赖项写法很难受(比如操作图层的一些脚本,是使用的ExtendScript,而不是我们使用的ES6之类的)
  3. 在高版本的PS中会被逐渐弃用

现在

使用UXP Plugin API and ECMAScript6 开发 Adobe Photoshop 插件。

  1. 可以使用现代的HTML、CSS和JavaScript构建高性能插件的UXP API。
  2. 它是下一代插件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 安装。

使用教程:

  1. 创建插件

    1. 安装完成之后,打开可以看到类似于这样的界面,点击create plugin。
    2. 输入插件信息
    3. 这是填写信息之后的,这里模板我们选择ps-react-starter,它创建了一个简单的 React-based 插件。
    4. 保存插件

      1. 填写信息后点击确认,选择一个文件夹将此插件保存到这个位置。
      2. 选择完成位置后,可以看到一个提示。
      3. 这个提示适用于非纯JavaScript的插件,比如react vue等需要webpack的。
      4. 它的提示信息意思是:注意:你将需要从你的终端运行额外的命令,以便安装各种依赖和构建插件。请查阅新创建的插件中的README文件,了解全部步骤。
    5. 保存完成

      1. 保存完成后,你创建的插件将在这个页面中显示。
    6. 打开插件目录

      1. 点击••••(又名“ action”),选择Open Folder,打开插件的根目录,所有的开发都将在这个文件夹下。
      2. 将插件加载到Photoshop中

        1. 点击••••(又名“ action”),选择Load,它会将你的插件加载到Photoshop中,加载成功后你可以在Photoshop中看到它。

      注意:如果你创建的插件是react,那么你不能直接加载插件到Photoshop中,你需要先阅读README文件,并按文件指引进行。

      加载完成后就可以在Photoshop中看到它。

接下来你只需要编辑并修改插件内容,它的主要内容如果是纯JavaScript,那么它的代码在根目录下。否则它是react vue等构建的,那么它的主要内容在src目录下。

自动刷新插件

注意:如果你开发的插件不是纯Javascript,直接编辑插件代码,可能并不会生效,你需要监听文件变动并重新打包。

你可能需要在开发者工具中,点击对应插件的action,找到watch。将它打开,它将监听你代码中html,js,css的变动并刷新插件(重新加载)。

如果是由webpack构建的,你需要同时搭配npm run watch使用。

对比同样使用webpack作为构建工具的:

  1. Chrome 插件

    1. 开发者在项目运行npm run watch后,仍然需要在插件管理页面手动刷新插件。
  2. Adobe 插件

    1. 开发者在开发工具处点击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], {})
  1. _obj:表示将要执行的操作。比如get,set,hide等等...
  2. _target:表示目标,

    1. 内部的_ 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插件还是非常简单的。

当然文章中上诉内容,只是将庞大的插件体系简单描述了下,缺少很多细节上的东西,还是需要在阅读之后再去看官方文档以及官方论坛。

看文档的目的是对插件的大部分功能细节能有了解,并将它们联通起来。

看论坛的目的是看大家是如何开发插件的,并看他们碰到了什么问题,是如何解决的。(个人认为极其有帮助,很多你能碰到的问题都可以在论坛中查到。即使没有,去提问也能较快的得到答复。)

如果你在这过程中遇到了什么问题,请随时评论或联系我探讨。

https://www.adobe.io/photoshop/uxp/2022/

https://forums.creativeclouddeveloper.com/c/photoshop/63

最后修改:2023 年 03 月 24 日
感谢阅读,欢迎交流。