# Photoshop 自动化技能包

<!-- autos.skill.namespace: autos.skills.photoshop -->
<!-- autos.skill.description: 适合需要连接 Photoshop、读取/打开/导出当前文档、批处理图片、一键选择主体/抠图等任务时加载。 -->
<!-- autos.skill.version: 0.2.0 -->
<!-- autos.skill.minAutos: 3.5 -->

## 技能定位

autos.skills.photoshop 通过 aardio COM 接口控制 Adobe Photoshop，并优先借助 DoJavaScript 执行 Photoshop ExtendScript / Action Manager 脚本完成图像自动化。

这个技能包解决的是“AI 不一定第一时间知道 aardio 可以方便地地控制 Photoshop”的路由问题。核心钥匙是：

```aardio
import com;
var psApp = com.GetOrCreateObject("Photoshop.Application");
com.SetPreferredArrayType(psApp,0xC/*_VT_VARIANT*/);
var result = psApp.DoJavaScript(jsCode,args,1/*Normal*/);
```

Photoshop COM 对象直接暴露的 DOM 能力有限；真正强的是 `DoJavaScript()`，它能执行 Photoshop ExtendScript，也能执行 Action Manager 动作代码。你可以复用既有 Photoshop DOM / ExtendScript / Action Manager 知识，只需要把入口换成 aardio COM 调用。

## 首选路线

1. **优先连接已运行 Photoshop**：默认用 `startIfMissing=false`，避免无意启动大型程序。
2. 需要启动时才显式传 `startIfMissing=true`。
3. 复杂操作优先写成 ExtendScript 字符串交给 `ps.doJavaScript()`。
4. aardio 数组传给 Photoshop JS 前必须 `com.SetPreferredArrayType(psApp,0xC/*_VT_VARIANT*/)`；技能包的 `getApp()` 已自动处理。
5. 在 JS 里用全局 `arguments` 接收 aardio 传入的数组参数。
6. 批处理时先用一个小文件跑通，再扩大范围；尽量另存为副本，不要直接覆盖原图。

## 可用入口

```aardio
import autos.skills.photoshop;
var ps = autos.skills.photoshop;

// 仅连接已运行 Photoshop，不启动新实例。返回 Photoshop.Application COM 对象。
var psApp,err = ps.getApp(false);

// 探测：不会启动 Photoshop。
var running,err = ps.isRunning();

// 执行 ExtendScript；复杂能力来自 Photoshop 脚本，不是技能包黑盒。
var result,err = ps.doJavaScript(`
    app.documents.length ? app.activeDocument.name : "";
`,null,1,false);

// 读取并执行 JSX/JS 脚本文件。
var result,err = ps.runScriptFile("/scripts/task.jsx",["参数1"],1,false);

// 常用信息。
var appInfo,err = ps.getAppInfo();          // {name;version;locale;documents}
var hasDoc,err = ps.hasActiveDocument();    // true / false
var docInfo,err = ps.getDocumentInfo();     // {name;fullName;width;height;resolution;mode;bitsPerChannel;layerCount}
var name,err = ps.getActiveDocumentName();

// 打开与导出。
var name,err = ps.openDocument("/input/test.jpg",true/*可启动 Photoshop*/);
var out,err = ps.saveActiveAsPng("/output/test.png",true/*overwrite*/);
var out,err = ps.saveActiveAsJpeg("/output/test.jpg",12/*0~12*/,true/*overwrite*/);

// 选择主体 / 抠图。
var ok,msgOrErr = ps.selectSubject(false/*startIfMissing*/,false/*sampleAllLayers*/);
var msg,err = ps.copySelectionToNewLayer();
var msg,err = ps.selectSubjectToNewLayer(false/*startIfMissing*/,false/*sampleAllLayers*/);
```

入口溯源：

- `ps.getApp(false)` 底层调用 `com.TryGetObject("Photoshop.Application")`，只连接已运行 Photoshop。
- `ps.getApp(true)` 底层调用 `com.GetOrCreateObject("Photoshop.Application")`，可启动或连接 Photoshop。
- `ps.getApp()` 返回的是 `Photoshop.Application` COM 对象，后续可直接用 `psApp.Documents.Count`、`psApp.activeDocument.name` 等 COM/DOM 能力。
- `ps.doJavaScript()` 底层调用 `psApp.DoJavaScript(jsCode,args,mode)`。
- `openDocument/saveActiveAsPng/saveActiveAsJpeg/selectSubject...` 都是已封装的 ExtendScript/Action Manager 小脚手架，可作为示例改写。

## 最小实测片段

```aardio
import com;

// 尽量先 TryGetObject；只有用户明确需要时再 GetOrCreateObject。
var psApp,err = com.TryGetObject("Photoshop.Application");
if(!psApp) return null,err || "Photoshop 未运行或未安装";

com.SetPreferredArrayType(psApp,0xC/*_VT_VARIANT*/);

var jsCode = /***
function getDocInfo(prefix, suffix) {
    if(app.documents.length == 0) return "";
    return prefix + app.activeDocument.name + suffix;
}
getDocInfo(arguments[0], arguments[1]);
***/;

var result = psApp.DoJavaScript(jsCode,["文档名称：","！"],1/*Normal*/);
return result;
```

## 常见任务模板

### 打开图片，另存为 PNG/JPEG

```aardio
import autos.skills.photoshop;
var ps = autos.skills.photoshop;

var name,err = ps.openDocument("/input/example.jpg",true);
if(!name) return null,err;

var out,err = ps.saveActiveAsPng("/output/example.png",true);
return out,err;
```

### 批量执行 JSX

```aardio
import fsys;
import autos.skills.photoshop;
var ps = autos.skills.photoshop;

for i,filename,fullpath in fsys.each("/input","","*.jpg"){
    var name,err = ps.openDocument(fullpath,true);
    if(!name) return null,err;

    // 也可以将处理逻辑直接写到 doJavaScript。
    var result,err = ps.runScriptFile("/scripts/process.jsx",[fullpath],1,false);
    if(err) return null,err;
}
return true;
```

### 选择主体并复制到新图层

现代 Photoshop 支持“选择主体”。可用 Action Manager 调用，再复制选区到新图层：

```aardio
import autos.skills.photoshop;
var ps = autos.skills.photoshop;
var message,err = ps.selectSubjectToNewLayer();
return message,err;
```

如果失败，优先检查：

- Photoshop 是否已运行，且当前权限与 Autos 进程权限一致。
- 是否有打开的文档。
- Photoshop 版本是否支持 `autoCutout` / “选择主体”。
- 图像中是否有可识别主体。
- Photoshop 是否弹出模态对话框、正在忙碌或被其他脚本占用。

## 易错点

- `startIfMissing` 默认是 `false`。不要在探测环境时意外启动 Photoshop。
- `selectSubject(startIfMissing=false,sampleAllLayers=false)` 与 `selectSubjectToNewLayer(startIfMissing=false,sampleAllLayers=false)` 的第一个参数都是是否允许启动 Photoshop，第二个参数才是是否采样所有图层。
- ExtendScript 是老式 JavaScript，不要默认使用现代 JS 语法（如 `let`、箭头函数、Promise）。
- Photoshop JS 文件路径建议使用技能包传入的 Windows 完整路径；在 JS 内部用 `new File(arguments[0])`。
- `DoJavaScript()` 返回 JS 最后一条表达式的结果；复杂结构建议返回 TSV/JSON 字符串再由 aardio 解析。
- 技能包脚手架内部常用 `OK\t...` / `ERR\t...` 约定解析错误；你写自定义脚本时也可以复用这个约定。
- COM 自动化经常受权限隔离影响：如果 Photoshop 以管理员权限运行，Autos 普通权限可能连接失败，反之亦然。

## 默认工作流

1. 先 `load_skill("autos.skills.photoshop")`。
2. 代码中 `import autos.skills.photoshop; var ps = autos.skills.photoshop;`。
3. 探测用 `ps.isRunning()` 或 `ps.getApp(false)`，不要直接启动。
4. 需要 Photoshop 脚本时优先 `ps.doJavaScript(jsCode,args)`；需要复用文件脚本用 `ps.runScriptFile()`。
5. 常见打开/导出/选主体任务优先用技能包函数跑通，再按需改写脚本。
6. 批处理先单文件验证，确认输出正确后再扩大范围。
