做爲前端開發者,咱們能夠利用Web技術在很是多的環境下開發應用,爲相關的用戶提供服務。其中,以Photoshop爲首的Adobe系列工具是咱們時長要去面對的一個平臺級應用。Photoshop在圖像處理上有着很強大的功能,用戶量也很是可觀,並且其功能在前端開發的一些狀況下也用獲得,所以筆者認爲Photoshop相關的Web技術具備很不錯的價值。javascript
本文將引導你們使用JavaScript開發一個Photoshop插件。css
Adobe做爲一家歷史悠久的軟件公司,已經給開發者提供了相對成熟的擴展開發技術棧,被稱做CEP——Common Extensibility Platform(通用擴展平臺)。html
CEP擴展基於Web技術,能夠在Adobe Photoshop、Adobe Illustrator、Adobe InDesign等全系列應用中運行,而且能夠訪問這些應用和外部操做系統環境的API。前端
CEP應用的結構能夠被分爲五個抽象層級:java
所謂宿主應用,就是咱們CEP擴展運行在的Adobe程序,例如Adobe Photoshop等,同時後文咱們也默認CEP的宿主程序是Adobe Photoshopreact
做爲一個類Web應用,上面四層相對前端開發者來講都比較好理解,這一節咱們來看下ExtendScript層:git
ExtendScript腳本能夠用三套不一樣語言去編寫,分別爲JavaScript、VBScript和AppleScript。三種語言功能上沒有任何區別。鑑於本文面向的是各位前端工程師,咱們果斷選擇前者,同時文章的後文咱們也是默認選擇JavaScript版本的ExtendScript。github
ExtendScript有如下特色:chrome
區別於 CEP 擴展中後綴爲.js
的 JavaScript 文件,操做ExtendScript 的JavaScript文件後綴名爲.jsx
json
這裏的.jsx文件和react用到的.jsx文件徹底不一樣,若是你在本身的CEP應用中引入了react,記得把它們分開以免混淆
ExtendScript在全局下內置了用來獲取和操做Adobe應用和文件內容的各類API
Adobe應用中,ExtendScript腳本和CEP中的JavaScript腳本運行於兩個不一樣的引擎,若是咱們選擇JavaScript語言接口的ExtendScript腳本,對應的引擎僅僅兼容至 ECMAScript3的標準
瞭解了Extendscript以後,咱們再來看一下CEP擴展各層級之間的橫向關係:
CEP JavaScript VM
中運行,比起通常的Web應用,增長了調用Node.js
的API與操做系統交互,以及經過引入CSInterface.js
調用ExtendScript的功能Host JavaScript VM
中被解析關於Photoshop中,ExtendScript具體能夠調用的API,咱們能夠直接看Adobe的官方手冊:ADOBE PHOTOSHOP SCRIPTING)
在動手開發前,咱們先把運行CEP擴展的各類要素準備齊全。
首先咱們來看下CEP擴展須要的目錄結構:
CSXS/manifest.xml
:必需,項目的配置文件,配置CEP擴展應用的窗體大小、入口的html
文件地址、入口jsx
(ExtendScript)文件地址、版本兼容、啓動選項等信息,因爲篇幅所限咱們不在本文裏具體展開,官方提供了配置文件的指南:Configure-your-extension-in-manifestxml
client/index.html
、client/index.js
、client/style.css
:CEP應用相關頁面、腳本、樣式,就是咱們CEP擴展和Web相關的所有文件
client/CSInterface.js
:Adobe官方提供的工具庫,須要在JavaScript層引入,封裝並提供了訪問ExtendScript層和一些原生功能的API,官方也在github上提供了文件CSInterface.js。
這個工具庫大概一千多行,其中很大一部分是描述各個函數功能的註釋,因此能夠直接經過閱讀註釋來學習這個工具庫的用法。
CEP擴展中的JavaScript環境下自己就內置了調用ExtendScript環境的類,引入的CSInterface.js
是對環境裏調用ExtendScript環境的類進行封裝使得開發者更便於調用而已,因此引入CSInterface.js
並非必要的。
host/index.jsx
:ExtendScript的腳本,訪問宿主應用的內部API的能力,在CEP擴展中,ExtendScript文件有兩種加載方式:
CSInterface.js
封裝好的方法主動進行加載manifest.xml
中經過配置入口的jsx
(ExtendScript)文件的腳本,在CEP擴展應用運行的第一時間進行加載最後,咱們要把創建好的CEP擴展的目錄放到Photoshop指定的位置:
mac:~/Library/Application Support/Adobe/CEP/extensions
win:{Photoshop安裝路徑}\Required\CEP\extensions
這樣Photoshop就能夠加載咱們開發的擴展,出如今其菜單欄中的「窗口」-「擴展」中。
看了這麼多概念,咱們動手試試吧!
新建CEP擴展的目錄以後,咱們嘗試在Photoshop菜單欄的「窗口」-「擴展」中運行擴展,就發現了一個問題:
這是由於咱們新建的CEP擴展沒有通過簽名認證。
爲了繞過這個認證,咱們須要打開Photoshop的debug模式:
CSXS.[n]
中的[n]
用你目前的CEP版本替換)
HKEY_CURRENT_USER/Software/Adobe/CSXS.[n]
PlayerDebugMode
的字段string
類型的"1"
defaults write com.adobe.CSXS.[n] PlayerDebugMode 1
ps -axu $USER|grep cfprefsd
,找到cfprefsd
這個進程的pid,而後用kill
命令刪掉它(或者你也能夠直接從新啓動你的機器)。執行完上面的操做後,你就能夠在本身的Photoshop裏運行本身新建的擴展了。
同時,若是你想調試本身的擴展,能夠在目錄指定位置中添加.debug
文件:
.debug
文件中,咱們指定開發的應用能夠在哪一個宿主應用和哪一個端口進行調試:
<ExtensionList>
<!-- 1 -->
<Extension Id="com.example.helloworld">
<HostList>
<!-- 2 -->
<Host Name="PHXS" Port="8088"/>
<Host Name="PHSP" Port="8088"/>
</HostList>
</Extension>
</ExtensionList>
複製代碼
而後,咱們訪問在chrome瀏覽器中訪問chrome://inspect/#devices
,點擊「Port forwarding...」監聽咱們在.debug
中設置的端口,咱們能夠看到的本身的應用:
熟悉移動端調試的讀者必定對這個界面不陌生,咱們找到本身的應用並點開「inspect」,就能夠在指定端口經過chrome的開發者工具來同步調試運行的CEP擴展了。
咱們從前文提到的「CEP應用結構的五個層級」自下向上來構建:
首先,在Extendscript層,咱們先在全局定義好「獲取全部文字圖層」和「刪除全部文字圖層」的功能函數:
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") {
//判斷是不是圖層組
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 "{}";
}
複製代碼
app
做爲Extendscript中的全局對象,有着獲取原生宿主程序各類功能的api,咱們能夠經過app.activeDocument.layers
來獲取或者操做圖層因爲在Extendscript環境下,JavaScript僅兼容ES3,並且ExtendScript和CEP JavaScript之間只能經過字符串進行通訊,因此咱們要在ExtendScript的環境下引入JSON3做爲JSON功能的polyfill(注意這和CEP的JavaScript無關)
在CEP的JavaScript層,咱們在utils/cs.js
中使用Promise
封裝好界面上用獲得的的hideLayers
和getLayers
函數——調用Extendscript中已經定義好在全局的方法,並處理返回的字符串:
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()");
複製代碼
在CEP的UI層(爲了更直觀,這裏咱們用引入react來代替html展現UI),咱們大體部署一下插件的界面,用兩個按鈕分別觸發「獲取全部文字圖層」和「刪除全部文字圖層」的功能。同時爲了直觀一些,咱們把獲取到的全部文字圖層在插件面板上顯示:
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}> 點擊獲取圖層 </button> <button className="primary" onClick={hideLayers}> 點擊刪除所有文字圖層 </button> <div className="area"> {layers && layers.length ? layers.map((e, i) => ( <div key={i} className="layer"> {e} </div> )) : "無"} </div> </div>
);
};
複製代碼
在CEF層,咱們根據上一小節「項目構建」的步驟,配置好manifest.xml
和整個項目的目錄結構,打開debug模式,並將整個CEP擴展應用的目錄放到相應的路徑下;
在界面層,咱們在Photoshop中隨便打開一個包含文字圖層的psd文件,而後在「窗口」-「擴展」裏面打開咱們剛開發好的擴展,就能夠成功運行了。
讓咱們試試剛剛開發的功能,例如,當咱們點擊「點擊獲取圖層」的按鈕時,獲得了以下的結果:
而後咱們點擊右側「刪除全部文字圖層」後,是否是能夠發現打開的psd文件中的文字圖層都消失了呢?
我把實例的項目放在了Lumpychen/CEP-Test,你們有興趣能夠本身嘗試。
如今咱們的應用能夠在記得Photoshop中跑起來了,可是若是想讓本身的擴展能夠在設計師同事的Photoshop裏運行,咱們不能給讓每一個用戶都開啓一下debug
模式,這太麻煩了。
在沒有進入debug
模式的狀況,Adobe CEP 擴展必須有簽名才能正常運行,簽名分爲兩種:
具體如何獲取證書、簽名打包,Adobe也提供了官方的教程:package-distribute-install-guide
同時,Adobe官方也把下載、管理和更新CEP擴展的功能集成到了Creative Cloud裏,若是你安裝了Creative Cloud,它會鏈接Adobe Exchange——Adobe官方推出的擴展市場,以獲取和更新咱們安裝的擴展。
若是你想把你本身開發的擴展發佈到Adobe Exchange上,Adobe官方也提供了Exchange Portal用來發布擴展的渠道。
然而……
因爲Adobe在中國的業務一直處於被閹割的狀態,且國內經過Creative Cloud購買正版Adobe應用的用戶也相對有限,因此你們不多采用官方的渠道管理和獲取Adobe產品的CEP擴展。
而國內的Photoshop擴展應用的生態依然處於一個略微灰色的狀態,不少擴展的發佈和都依賴第三方社區(知乎、微信公衆號、淘寶)或素材網站,固然這樣的生態也催生了我國互聯網的歷史上一批又一批的ps大神。
文章的最後,若是你想要開發一個Adobe CEP擴展,我這邊強烈推薦幾個輔助用的工具:
Script Listener是Adobe社區裏推出的輔助工具,能夠隨時記錄用戶對Adobe宿主程序的操做,而後生成ExtendScript腳本文件在桌面上供用戶查看和選用——使用這種方式生成ExtendScript代碼,可讓開發者省去不少學習Extendscript API的成本。
JSX.js是提供給CEP應用的JavaScript環境一個JS庫,能夠代替原生的方法來引入ExtendScript的文件或執行Extendscript的代碼,它解決了一個很重要的痛點——提供了執行ExtendScript的報錯信息(這比起原生調用ExtendScript代碼執行獲得一句evalScript error
體驗要強上不少倍)
這是目前Adobe官方提供的,當前版本惟一用來調試ExtendScript的工具。它是一個VSCode Debugger插件,能夠像其它的VScode Debugger同樣,提供相關報錯信息,實現斷點調試的功能。