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:

  1. The user layer, the user gets, builds out the application outside;
  2. 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);
  3. UI layer, is based on HTML files to build WEB pages;
  4. 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;
  5. 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 inCEP JavaScript VMWith more calls than normal Web applicationsNode.jsApis for interacting with the operating system, as well as by introducingCSInterface.jsCall ExtendScript’s functionality
  • In the host program, the JavaScript code as ExtendScript is in a different environmentHost JavaScript VMIs 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 extensionCSInterface.jsThe encapsulated method loads actively
    • inmanifest.xmlThrough the configuration entryjsxExtendScript is a script that is loaded the first time a CEP extension application is running

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 methodCSXS.[n]In the[n]Replace with your current CEP version)
    • If you are a Windows user, you need to:
      • Open regedit
      • findHKEY_CURRENT_USER/Software/Adobe/CSXS.[n]
      • And then add one calledPlayerDebugModeThe field of
      • Set values for thestringThe 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 terminalps -axu $USER|grep cfprefsdTo findcfprefsdThe pid of the process, and then usekillCommand to delete it (or you can just restart your machine).

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:

  1. 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
    • appAs a global object in Extendscript, there are apis to access various features of the native host program that we can useapp.activeDocument.layersTo 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)

  2. 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
  3. 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
  4. 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.

  5. 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″