As front-end developers, we can use Web technologies to develop applications in a wide variety of environments and to serve relevant users. Among them, the Adobe series of tools led by Photoshop is a platform level application that we will face for a long time. Photoshop has a very powerful function in image processing, the number of users is also very considerable, and its function in the front-end development of some cases can also be used, so the author thinks that Photoshop related Web technology has a very good value.
This article guides you through the development of a Photoshop plug-in using JavaScript.
CEP: Universal extension platform
As a software company with a long history, Adobe has provided developers with a relatively mature extension development technology stack called the Common Extensibility Platform (CEP).
The CEP extension is based on Web technology that runs across the full range of applications, including Adobe Photoshop, Adobe Illustrator, Adobe InDesign, and has access to apis for these applications and external operating system environments.
The structure of CEP applications can be divided into five levels of abstraction:
- The user layer, the user gets, builds out the application outside;
- In the host application layer, our CEP extension is configured in the host application and successfully rendered in the embedded CEF (an open source simple framework embedded in the Chromium-based browser);
- UI layer, is based on HTML files to build WEB pages;
- The Javascript layer, the Javascript scripts that run on UI layer pages, have more functionality built in than the typical Web application environment — access to the Extendscript layer and some API native to the host application as well as the native Nodejs API;
- The Extendscript layer, which runs inside the host application and has the ability to access the host application’s internal API, can communicate with the JavaScript layer;
The so-called host application is the Adobe program that our CEP extension runs on, such as Adobe Photoshop, etc., and we also default the host application of CEP is Adobe Photoshop
ExtendScript
As a web-like application, all four layers are easier to understand than front-end developers, so in this section we’ll look at the ExtendScript layer:
ExtendScript scripts can be written in three different languages: JavaScript, VBScript, and AppleScript. There is no functional difference between the three languages. Since this article is intended for front-end engineers, we chose the former, and for the rest of the article we chose the JavaScript version of ExtendScript by default.
ExtendScript has the following features:
-
Unlike the.js JavaScript file in the CEP extension, the.jsx JavaScript file that operates ExtendScript
The.jsx files here are completely different from the.jsx files used in react, so if you’re introducing React in your CEP app, remember to keep them separate to avoid confusion
-
ExtendScript has built-in apis for fetching and manipulating the content of Adobe applications and files globally
-
In Adobe applications, ExtendScript scripts and JavaScript scripts in CEP run on two different engines. If we choose ExtendScript scripts in the JavaScript language interface, the engine is only compatible with ECMAScript3
With Extendscript in mind, let’s take a look at the horizontal relationship between the levels of CEP extensions:
- The JavaScript code in the CEP extension will be in
CEP JavaScript VM
With more calls than normal Web applicationsNode.js
Apis for interacting with the operating system, as well as by introducingCSInterface.js
Call ExtendScript’s functionality - In the host program, the JavaScript code as ExtendScript is in a different environment
Host JavaScript VM
Is resolved - In this web-like application, there are two scripting environments, both of which are JavaScript and can communicate with each other by passing strings, but their contexts are separate and must be distinguished
For details on the API ExtendScript can call in Photoshop, see Adobe’s official manual: Adobe Photoshop SCRIPTING.
The project build
Before we get started, let’s get the elements in place to run the CEP extension.
First let’s look at the directory structure required for the CEP extension:
-
CSXS/manifest. XML: Required, the project configuration file, the form size to configure the CEP extension application, the entry HTML file address, the entry JSX (ExtendScript) file address, version compatibility, startup options, and other information. Due to space limitations, we will not expand the details in this article. The official guide to configuration files is provided: Configure-your-extension-in-manifestxml
-
Client /index.html, client/index.js, client/style. CSS: CEP application related pages, scripts, styles, is our CEP extension and Web related files
-
Client/csinterface.js: Adobe’s official tool library, which needs to be introduced in the JavaScript layer, encapsulates and provides an API to access the ExtendScript layer and some native features. The csinterface.js file is also officially available on Github.
There are over a thousand lines in the library, a large portion of which are comments describing the functions, so you can learn how to use the library directly by reading the comments.
The JavaScript environment in the CEP extension itself has built-in classes that call ExtendScript. The introduction of csinterinterface. Js encapsulates ExtendScript classes in the environment to make it easier for developers to call ExtendScript. So introducing csinterface.js is not necessary.
-
Host /index.jsx: ExtendScript scripts that access the host application’s internal API. In CEP extensions, ExtendScript files can be loaded in two ways:
- Pass with JavaScript within the CEP extension
CSInterface.js
The encapsulated method loads actively - in
manifest.xml
Through the configuration entryjsx
ExtendScript is a script that is loaded the first time a CEP extension application is running
- Pass with JavaScript within the CEP extension
Finally, we will place the directory of the established CEP extension in the location specified by Photoshop:
MAC: ~ / Library/Application Support/Adobe/CEP/extensions
Win: {Photoshop installation path}\Required\CEP\extensions
This allows Photoshop to load the extensions we developed and appear in its menu bar under “Window” – “Extensions”.
Debug mode and debugging
Look at so many concepts, let’s try it!
After creating a new directory for CEP extensions, we tried to run the extensions in Windows – Extensions on the Photoshop menu bar and found a problem:
This is because our new CEP extension is not signed.
To bypass this authentication, we need to turn on The Debug mode of Photoshop:
- First of all, we need to get the version of Adobe CEP on our current machine is CEP, about the types of Adobe Applications and versions of short, we can see the official provided corresponding: Applications Integrated with CEP
- After getting the current version of the CEP, we can enter debug mode by using the following method
CSXS.[n]
In the[n]
Replace with your current CEP version)- If you are a Windows user, you need to:
- Open regedit
- find
HKEY_CURRENT_USER/Software/Adobe/CSXS.[n]
- And then add one called
PlayerDebugMode
The field of - Set values for the
string
The type of"1"
- If you are a macOS user, you need:
- Open the terminal and enter:
defaults write com.adobe.CSXS.[n] PlayerDebugMode 1
- You need to type in at the terminal
ps -axu $USER|grep cfprefsd
To findcfprefsd
The pid of the process, and then usekill
Command to delete it (or you can just restart your machine).
- Open the terminal and enter:
- If you are a Windows user, you need to:
With that done, you can run your new extension in Photoshop.
Also, if you want to debug your own extensions, you can add a.debug file in the directory specified:
In the.debug file, we specify which host and port the application can be debugged on:
<ExtensionList>
<! 1 - - - >
<Extension Id="com.example.helloworld">
<HostList>
<! -- -- -- > 2
<Host Name="PHXS" Port="8088"/>
<Host Name="PHSP" Port="8088"/>
</HostList>
</Extension>
</ExtensionList>
Copy the code
//inspect/# Devices and click on “Port Forwarding…” Listening to the port we set in.debug, we can see our own application:
Readers familiar with mobile debugging will be familiar with this interface. We found our application and clicked on “Inspect” to synchronize and debug the running CEP extension through Chrome’s developer tools on the specified port.
Case: Develop a “Get/remove all text layers” Photoshop plugin
The “Five levels of CEP application architecture” mentioned earlier is built from the bottom up:
-
First, in the Extendscript layer, we define the “Get all text layers” and “Delete all text layers” functions globally:
function getAllLayers() { var out = []; var doc = app.activeDocument; getLayers(doc.layers); function getLayers(layers) { for (var i = 0; i < layers.length; i++) { if (layers[i].typename == "LayerSet") { // Check whether it is a layer group out.push(layers[i].name); getLayers(layers[i].layers); } else{ out.push(layers[i].name); }}}return JSON.stringify(out); } function hideAllTextLayers() { var doc = app.activeDocument; var out = []; function getLayers(layers) { for (var i = 0; i < layers.length; i++) { if (layers[i] && layers[i].kind === LayerKind.TEXT) { out.push(layers[i]); } if (layers[i].typename == "LayerSet") { getLayers(layers[i].layers); } } } getLayers(doc.layers); for (var j = 0; j < out.length; j++) { out[j].remove(); } return "{}"; } Copy the code
app
As a global object in Extendscript, there are apis to access various features of the native host program that we can useapp.activeDocument.layers
To get or manipulate layers- Also, we make it available to the JavaScript side of CEP by returning the result as a string
Since JavaScript is only COMPATIBLE with ES3 in the Extendscript environment and communication between Extendscript and CEP JavaScript can only be done through strings, So we’re going to introduce JSON3 as a polyfill for JSON functionality in ExtendScript (note that this has nothing to do with CEP’s JavaScript)
-
In the JavaScript layer of CEP, we use the Promise in utils/cs.js to wrap the hideLayers and getLayers functions that are available on the interface — calling the global methods defined in Extendscript and handling the returned strings:
const cs = new CSInterface(); var c = cs.getSystemPath(SystemPath.EXTENSION) + "/jsx/"; cs.evalScript(`$.evalFile("${c}json3.jsx")`); const evalJSXScript = (script) = > new Promise((resolve) = > { cs.evalScript(script, (res) = > { resolve(JSON.parse(res)); }); }); export const getLayers = () = > evalJSXScript("getAllLayers()"); export const hideLayers = () = > evalJSXScript("hideAllTextLayers()"); Copy the code
-
In the CEP UI layer (which we introduced react instead of HTML to make the UI more intuitive), we basically deployed the plugin’s interface, using two buttons to trigger the “Get All text layers” and “Remove all text layers” functions, respectively. Also to make things more intuitive, we’ll display all the text layers in the Plugins panel:
import React, { useState } from "react"; import { hideLayers, getLayers } from "./utils/cs"; import "./styles/main.css"; export default() = > {const [layers, setLayers] = useState(null); const handleGetLayers = async() = > {const layers = await getLayers(); setLayers(layers); }; return ( <div style={{ width: "100vw", height: "100vh", background: "#FFF}} "> <button className="primary" onClick={handleGetLayers}>I'm gonna go get Layer</button> <button className="primary" onClick={hideLayers}>Click to delete all text layers</button> <div className="area"> {layers && layers.length ? layers.map((e, i) => ( <div key={i} className="layer"> {e} </div>)) : "none "}</div> </div> ); }; Copy the code
-
In the CEF layer, we configure the manifest. XML and the directory structure of the entire project according to the steps in the previous section “Project build”, open the debug mode, and put the directory of the entire CEP extension application into the corresponding path.
-
In the interface layer, we opened a random PSD file with the text layer in Photoshop, then opened the extension we just developed in window – Extension, and it was ready to run.
Let’s try the functionality we just developed. For example, when we click the “Click to get layer” button, we get the following result:
After clicking “Delete all text layers” on the right, will you notice that all the text layers in the open PSD file have disappeared?
I put the example of the project in Lumpychen/ CEP-test, you are interested in can try their own.
Signature and release
Now our application can run in Photoshop, but if we want our extension to run in Photoshop with our designer colleagues, we can’t have every user in debug mode, it’s too cumbersome.
The Adobe CEP extension must have a signature to work without entering debug mode. There are two types of signatures:
- A commercial signature certificate, which can be purchased from a digital signature provider
- A self-signed certificate can be created using Adobe’s ZXPSignCmd
Adobe also provides an official tutorial on how to obtain certificates and signature packages: package-distribut-install-Guide
Adobe has also integrated the ability to download, manage and update CEP extensions into Creative Cloud. If you install Creative Cloud, it will connect to Adobe Exchange, Adobe’s official extension marketplace, to get and update our installed extensions.
If you want to publish your own extensions to Adobe Exchange, Adobe also provides an official channel to publish your extensions using the Exchange Portal.
However……
Because Adobe’s business in China has been in a state of castration, and the domestic users who buy legitimate Adobe applications through Creative Cloud are relatively limited, so people rarely use the official channel management and access to Adobe products CEP extension.
However, the ecology of Photoshop extension applications in China is still in a slightly gray state, with many extension releases and rely on third-party communities (Zhihu, wechat public account, Taobao) or material websites. Of course, such ecology has also spawned a batch of Ps gods in the history of The Internet in China.
Auxiliary tool
At the end of this article, if you want to develop an Adobe CEP extension, THERE are a few tools I strongly recommend:
-
Script Listener
Script Listeners are tools available in the Adobe community that record user actions on Adobe host programs at any time, and then generate ExtendScript scripts on the desktop for users to view and select — this way to generate ExtendScript code, It saves developers a lot of cost of learning the Extendscript API.
-
JSX.js
Jsx. js is a JS library provided to the JavaScript environment of CEP applications. It can be used to introduce ExtendScript files or execute ExtendScript code instead of native methods. It addresses an important pain point — it provides an error message for ExtendScript execution (which is many times better than an evalScript error experience for native ExtendScript code execution).
-
ExtendScript Debugger
This is the only tool currently available from Adobe for debugging ExtendScript. It is a VSCode Debugger plug-in that provides error information and implements breakpoint debugging functions like other VSCode Debugger.
Further reading
- The CEP Intro”
- Adobe CEP Extension Development With Nullice
- Photoshop Scripting Documentation
- The Photoshop – CC – Javascript – Ref – 2019″