Photoshop脚本 环境搭建与运行方式
文件格式
ExtendScript 脚本使用 .jsx 扩展名(JavaScript eXtendScript)。
| 项目 | 说明 |
|---|---|
| 后缀 | .jsx |
| 引擎 | Adobe ExtendScript(ES3) |
| 文件编码 | UTF-8(含 BOM 兼容性更佳) |
| 支持版本 | 所有支持脚本的 Photoshop 版本 |
开发工具
ExtendScript Toolkit(ESTK)
Adobe 官方提供的脚本 IDE,随 Creative Suite 安装(CS6 及更早版本包含)。功能:
- 语法高亮、代码补全、断点调试
- 直接运行和调试脚本
- 对象模型查看器(Object Model Viewer)
- JavaScript 控制台
如果找不到 ESTK,它通常安装在 Applications/Utilities/Adobe ExtendScript Toolkit/(macOS)或 C:\Program Files\Adobe\Adobe ExtendScript Toolkit\(Windows)。
VS Code
使用任何文本编辑器都可以编写 .jsx 文件。VS Code 安装 ExtendScript 扩展后可获得语法高亮和代码片段支持。由于 ExtendScript 只是普通的文本文件,记事本或 Sublime Text 也能胜任。
ES3 语法限制
ExtendScript 基于 ECMAScript 3,与现代 JavaScript 有显著差异:
// ✅ 正确——使用 var
var doc = app.activeDocument;
var layerName = "图层";
// ❌ 不支持 let(存在 bug)
// let count = 5;
// ❌ 错误——不支持箭头函数
// layers.forEach(l => alert(l.name));
// ✅ 正确——使用普通函数
function showLayerNames() {
var i;
for (i = 0; i < app.activeDocument.artLayers.length; i++) {
alert(app.activeDocument.artLayers[i].name);
}
}
// ❌ 错误——不支持模板字符串
// alert(`文档: ${doc.name}`);
// ✅ 正确——使用字符串拼接
alert("文档: " + doc.name); ES3 环境限制
ES3 标准不包含 JSON 全局对象,因此 JSON.stringify() 和 JSON.parse() 不可用:
// ❌ JSON 序列化不可用
// var json = JSON.stringify({name: "test"}); // ReferenceError
// ✅ 手动构造键值对字符串
function formatPairs(obj) {
var parts = [];
for (var key in obj) {
parts.push(key + ": " + String(obj[key]));
}
return "{" + parts.join(", ") + "}";
} 替代方案:在脚本内自行实现轻量序列化函数。
运行脚本的方式
方法一:菜单浏览运行
文件 > 脚本 > 浏览,选择 .jsx 文件。Photoshop 立即执行脚本内容。
方法二:复制到脚本目录
将 .jsx 文件放入 Photoshop 的 Presets/Scripts 文件夹,重启 Photoshop 后出现在 文件 > 脚本 的子菜单中。
Windows 路径示例:
C:\Program Files\Adobe\Adobe Photoshop CC\Presets\Scripts\ 方法三:ESTK 运行
在 ExtendScript Toolkit 中打开 .jsx,点击运行按钮或按 F5,可以选择在 Photoshop 中执行。ESTK 提供 JavaScript 控制台,可配合 $.writeln() 输出调试信息。
方法四:跨应用脚本
通过 ESTK 连接可以控制任意运行的 Adobe 应用,甚至可以在一个脚本中同时操作 Photoshop 和 Illustrator。
调试方式
alert()
ExtendScript 中最简单的调试手段。弹出 Photoshop 对话框显示信息:
var doc = app.activeDocument;
alert("当前文档名称: " + doc.name);
alert("图层数量: " + doc.artLayers.length); 注意:doc.name 返回字符串,doc.path 返回 File 对象。当路径包含中文等非 ASCII 字符时,ExtendScript 会将它们进行 URL 编码:
var doc = app.activeDocument;
alert(doc.name); // "example.psd"(纯 ASCII,正常)
alert(doc.path); // "C:/Users/Admin/%E4%B8%AD%E6%96%87"(中文被编码)
// 使用 decodeURI() 还原为可读的中文
alert(decodeURI(doc.name)); // "中文名.psd"
alert(decodeURI(doc.path)); // "C:/Users/Admin/中文"
alert(decodeURI(doc.path.fsName)); // "C:\Users\Admin\中文" decodeURI() 是处理路径中非 ASCII 字符的必备工具,生产环境中广泛使用(File.name、File.path、File.fsName、File.toString() 均受此影响)。
Enter 键不放可快速跳过连续的 alert() 对话框。 写入日志文件
alert() 弹窗会阻塞脚本执行,不适用于循环中大量输出。如需记录运行日志,将输出写入文本文件更实用:
var logFile = new File("C:/Users/Administrator/Desktop/script-log.txt");
logFile.encoding = "UTF8";
logFile.open("a"); // "a" 追加模式
logFile.writeln("处理文件: " + decodeURI(app.activeDocument.name));
logFile.close(); 全局 $ 对象
$ 是 ExtendScript 的全局工具对象,无需声明即可直接使用。常用属性和方法:
| 成员 | 用途 |
|---|---|
$.evalFile(file) | 运行时加载并执行另一个 .jsx 文件 |
$.fileName | 当前脚本自身的文件路径 |
$.os | 操作系统名称(如 "Windows"、"Macintosh") |
$.writeln(text) | 写入 ESTK 控制台(仅在 ESTK 中可见) |
$.writeln("当前脚本: " + $.fileName); // ESTK 控制台输出
alert("操作系统: " + $.os); // 弹窗显示系统信息 抑制对话框
自动化运行时可禁用所有弹窗:
app.displayDialogs = DialogModes.NO; // 不显示任何对话框(包括 alert)
// app.displayDialogs = DialogModes.ERROR; // 仅显示错误
// app.displayDialogs = DialogModes.ALL; // 显示所有对话框(默认) 错误处理
生产脚本中需要处理各种异常情况——文档不存在、文件损坏、操作被取消等。ExtendScript 支持标准的 try-catch:
try {
var doc = app.activeDocument;
if (!doc) throw new Error("没有打开的文档");
var layer = doc.artLayers.add();
layer.name = "新图层";
} catch (err) {
alert("错误: " + err);
} 常见模式:
// 模式 1:检查对象是否存在再操作
var file = new File("C:/Users/Administrator/Desktop/target.psd");
if (file.exists) {
try {
var doc = app.open(file);
// 处理文档...
doc.close(SaveOptions.SAVECHANGES);
} catch (err) {
alert("打开文件失败: " + err);
}
} else {
alert("文件不存在");
}
// 模式 2:静默处理可选操作(如删除可能不存在的图层)
try {
doc.artLayers.getByName("临时图层").remove();
} catch (err) {
// 图层不存在,无需处理
}
// 模式 3:确保文档关闭(即使出错)
var doc = app.open(file);
try {
// 执行可能出错的操作
doc.resizeImage(1920, null, 72, ResampleMethod.BICUBIC);
} catch (err) {
alert("处理出错: " + err);
} finally {
doc.close(SaveOptions.DONOTSAVECHANGES);
} ExtendScript 还提供 Error 对象用于主动抛出异常:
if (!app.documents.length) {
throw new Error("没有打开任何文档");
} Error 对象的 message 属性可包含中文错误信息,通过 alert() 展示给用户。
第一个脚本
创建新文档并保存为 PSD:
app.displayDialogs = DialogModes.NO;
var doc = app.documents.add(400, 300, 72, "测试文档", NewDocumentMode.RGB);
var layer = doc.artLayers.add();
layer.name = "我的图层";
var saveFile = new File("C:/Users/Administrator/Desktop/测试文档.psd");
doc.saveAs(saveFile, new PhotoshopSaveOptions(), true, Extension.LOWERCASE);
alert("保存完成: " + decodeURI(saveFile.fsName)); 模块化脚本组织
随着脚本规模增长,将代码拆分到多个文件中是良好的实践。ExtendScript 提供两种代码复用方式:
#include 指令(编译时包含)
#include 是一个预处理指令,在脚本编译时将被包含文件的全部内容插入到当前文件位置:
// main.jsx —— 入口文件
#include "config.jsxinc"
#include "lib/helpers.jsxinc"
#include "lib/export.jsxinc"
// 编译后,上面三个文件的内容会直接插入此处,
// 其中定义的所有函数和变量都可以直接使用
processAllFiles(); 包含的文件通常使用 .jsxinc 后缀(非强制,仅为标识代码片段而非独立脚本):
// utils.jsxinc —— 不包含独立执行逻辑,仅供 #include 使用
function resizeToWidth(doc, targetWidth) {
doc.resizeImage(targetWidth, null, 72, ResampleMethod.BICUBIC);
}
function saveAsJPEG(doc, filePath, quality) {
var opts = new JPEGSaveOptions();
opts.quality = quality || 10;
doc.saveAs(new File(filePath), opts, true, Extension.LOWERCASE);
} // main.jsx
#include "utils.jsxinc"
// 直接使用 utils.jsxinc 中定义的函数
var doc = app.activeDocument;
resizeToWidth(doc, 1920);
saveAsJPEG(doc, "C:/Users/Administrator/Desktop/output.jpg", 12); 注意事项:
- 路径相对于当前脚本文件所在目录
- 文件内容在编译期插入,执行期与主脚本无区别
- 同一个文件可以使用多个
#include分层组织
$.evalFile(运行时加载)
$.evalFile() 在脚本执行过程中动态加载并执行另一个 .jsx 文件:
// 运行时加载配置脚本
$.evalFile(new File("C:/Users/Administrator/Desktop/config.jsx"));
// 此时 config.jsx 中定义的变量和函数生效 选择参考
| 场景 | 推荐方式 |
|---|---|
| 静态库、工具函数 | #include |
| 用户自定义配置 | $.evalFile |
| 插件式功能模块 | $.evalFile |
| 项目依赖的基础模块 | #include |