chore: publish from staged

This commit is contained in:
github-actions[bot]
2026-05-28 00:10:20 +00:00
parent 58d82917b4
commit 6941e57afd
2 changed files with 356 additions and 0 deletions
+305
View File
@@ -48,6 +48,7 @@ Expert guidance for automating Adobe Illustrator through ExtendScript (JavaScrip
- **Startup Scripts**: Place scripts in the Startup Scripts folder to run automatically on launch
- **Target directive**: Begin scripts with `#target illustrator` when running from ESTK or external tools
- **`#targetengine` directive**: Use `#targetengine "session"` to persist variables across script executions
- **External invocation**: Scripts are frequently launched from outside Illustrator — by shell scripts, task runners, CI jobs, ExtendScript Toolkit (`ExtendScript Toolkit.exe -run script.jsx`), or `BridgeTalk` messages from other Adobe apps. See [External Invocation & Argument Passing](#external-invocation--argument-passing).
### Naming Conventions (JavaScript)
@@ -561,6 +562,152 @@ When calling methods with multiple optional parameters, use `undefined` to skip
item.rotate(30, undefined, undefined, true);
```
## External Invocation & Argument Passing
Illustrator scripts are routinely launched from outside the application —
shell scripts, schedulers, build pipelines, ExtendScript Toolkit, or
`BridgeTalk` messages from other Creative Cloud apps. The execution
environment under those launchers differs from the in-application *File >
Scripts* path in several ways that frequently break otherwise-correct code.
### `arguments[]` Is Unreliable Under External Launchers
ExtendScript Toolkit's `-run` invocation and `BridgeTalk.send()` do not
forward arbitrary launcher arguments into the script's top-level
`arguments[]` array. In many configurations the array contains a single
`[object BridgeTalk]` element instead of the values the caller passed, as
demonstrated below:
```javascript
// At top of script
var passed = (typeof arguments !== "undefined") ? arguments : [];
for (var i = 0; i < passed.length; i++) {
$.writeln("arg[" + i + "] = " + passed[i]);
// Often prints: arg[0] = [object BridgeTalk]
}
```
**Do not rely on `arguments[]` for required inputs when the script is
launched externally.** Use one of the following more reliable channels.
### Sidecar File for Parameters
When a script fails under an external launcher and the source of the error
is not obvious, fall back to a sidecar file: have the caller write a small
text file at a known absolute path, and read it on startup. This works
regardless of launcher quirks and is easy to inspect after a failed run.
```javascript
var SIDECAR_PATH = "C:/Users/userName/job.args.txt";
function readSidecar(path) {
var f = new File(path);
if (!f.exists || !f.open("r")) return null;
var lines = [];
while (!f.eof) {
var ln = f.readln();
if (ln && !/^\s*$/.test(ln)) lines.push(ln);
}
f.close();
return {
input: lines[0],
output: lines[1],
mode: lines[2]
};
}
```
A `key=value` format is equally workable and avoids positional fragility:
```text
input=C:/path/to/input.ai
output=C:/path/to/output.pdf
mode=preview
```
### Environment Variables
`$.getenv("NAME")` returns environment variables visible to **Illustrator's
process**, not the launcher's. If the launcher needs Illustrator to see a
value, it must set the variable system-wide or in Illustrator's parent
environment before launching. For per-invocation values, prefer a sidecar
file.
### `$.fileName` and `File($.fileName).parent`
Under in-application execution, `$.fileName` is the absolute path of the
running script and `File($.fileName).parent` yields the script's folder.
Under some external launchers (notably ESTK `-run`) `$.fileName` can be
empty, causing relative path resolution to silently fail.
```javascript
// Fragile: returns null under some launchers
var here = $.fileName ? File($.fileName).parent : null;
var sidecar = here ? new File(here.fsName + "/job.args.txt") : null;
// Robust: hardcode a known absolute path or fall back to a stable location
var sidecar = new File("C:/Users/userName/job.args.txt");
if (!sidecar.exists) sidecar = new File(Folder.temp.fsName + "/job.args.txt");
```
### Diagnostic Logging to an Absolute Path
Silent failures are common because dialogs are suppressed and the launcher
may not surface `$.writeln` output. Write a plain-text log to a known
absolute path so a run can be inspected after the fact. Create the parent
folder on demand so the first call cannot fail for a missing directory.
```javascript
var LOG_PATH = "C:/Users/userName/logs/job.log";
function log(msg) {
try {
var f = new File(LOG_PATH);
try { if (!f.parent.exists) f.parent.create(); } catch (eDir) {}
if (f.open("a")) {
f.writeln("[" + new Date() + "] " + msg);
f.close();
}
} catch (e) {}
}
```
### Wrap the Entry Point in `try { ... } catch`
Externally launched scripts often fail without any visible indication. A
top-level `try`/`catch` that writes the error to the log file converts
silent failures into a single inspectable line.
```javascript
try {
main();
} catch (err) {
log("FATAL: " + err + (err && err.line ? " line=" + err.line : ""));
}
```
### Suppress User Interaction
External callers cannot answer dialogs. Disable them before any DOM work
and avoid `alert()` / `confirm()` / `prompt()` entirely in scripts that may
be launched headlessly.
```javascript
app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
```
### Save Explicitly
Closing or letting Illustrator return to its idle state does not save the
working file. After all DOM edits, call `doc.saveAs(...)` (or `doc.save()`)
explicitly and log whether it succeeded.
```javascript
var opts = new IllustratorSaveOptions();
opts.compatibility = Compatibility.ILLUSTRATOR17;
doc.saveAs(new File(doc.fullName.fsName), opts);
```
## Common Patterns
### Iterate All Page Items in a Document
@@ -585,6 +732,157 @@ function processAllItems(doc) {
}
```
### Recursively Unlock Layers and Groups Before Editing
A locked layer or any locked ancestor (parent group, clip group, sublayer)
will cause edits to throw `Error: Target layer cannot be modified`. Walk the
full hierarchy and clear `locked` / `hidden` flags before performing DOM
modifications.
```javascript
function unlockAll(doc) {
function visitLayers(layers) {
for (var i = 0; i < layers.length; i++) {
var lyr = layers[i];
try { lyr.locked = false; lyr.visible = true; } catch (e) {}
visitItems(lyr);
if (lyr.layers && lyr.layers.length) visitLayers(lyr.layers);
}
}
function visitItems(container) {
var items = container.pageItems;
for (var j = 0; j < items.length; j++) {
var it = items[j];
try { it.locked = false; it.hidden = false; } catch (e) {}
if (it.typename === "GroupItem") visitItems(it);
}
}
visitLayers(doc.layers);
}
```
### Replacing the File Behind a Linked Image (Relink)
`PlacedItem.file = newFile` replaces a linked image while preserving the
parent, stacking order, and (after re-applying) the bounds. **`RasterItem`
does not expose a writable `file` property**, so when a placeholder is a
raster you must add a fresh `PlacedItem` in the same parent, copy the bounds,
then remove the original.
```javascript
function relinkOrRebuild(item, newFile) {
var bounds = item.geometricBounds.slice();
var parent = item.parent;
var name = item.name;
if (item.typename === "PlacedItem") {
item.file = newFile;
item.geometricBounds = bounds;
return item;
}
// RasterItem path: rebuild as a linked PlacedItem in the same parent.
var fresh = parent.placedItems.add();
fresh.file = newFile;
fresh.geometricBounds = bounds;
if (name) try { fresh.name = name; } catch (e) {}
fresh.move(item, ElementPlacement.PLACEBEFORE);
item.remove();
return fresh;
}
```
### Placing SVG Content (Copy/Paste Pattern)
`PlacedItem.file` accepts raster formats and AI/PDF, **but not SVG**. Setting
it to an `.svg` File throws `Unable to set placed item's file, is the file
path provided valid?`. The reliable way to bring SVG artwork into a document
is to open the SVG as a separate document, select all, copy, close, and paste
into the working document.
```javascript
function placeSVG(targetDoc, svgFile, targetLayer) {
var donor = app.open(svgFile);
app.executeMenuCommand("selectall");
app.executeMenuCommand("copy");
donor.close(SaveOptions.DONOTSAVECHANGES);
app.activeDocument = targetDoc;
targetDoc.activeLayer = targetLayer;
app.executeMenuCommand("pasteFront");
var sel = targetDoc.selection;
if (!sel || sel.length === 0) return null;
if (sel.length === 1) return sel[0];
// Multiple pasted items: group them so callers get a single handle.
var group = targetLayer.groupItems.add();
for (var i = sel.length - 1; i >= 0; i--) {
sel[i].move(group, ElementPlacement.PLACEATBEGINNING);
}
return group;
}
```
### Finding a Clipping Path Inside a Mask Group
Clip groups expose their clipping shape as a child `PathItem` (or, less
commonly, a child of a `CompoundPathItem`) with `clipping === true`. The
clip's `geometricBounds` give the visible frame to size or center content
against.
```javascript
function findClipPath(group) {
var items = group.pageItems;
for (var i = 0; i < items.length; i++) {
var it = items[i];
try {
if (it.typename === "PathItem" && it.clipping) return it;
if (it.typename === "CompoundPathItem") {
for (var j = 0; j < it.pathItems.length; j++) {
if (it.pathItems[j].clipping) return it;
}
}
} catch (e) {}
}
return null;
}
```
### Cover-Fit and Contain-Fit Sizing
To make an image fully cover a rectangle (any overflow hidden by a mask), use
the larger of the width/height ratios. To make it fit entirely inside, use
the smaller. A bleed factor (e.g. `1.10`) lets a cover image extend slightly
past the clip edge.
```javascript
function fitItemToRect(item, rect, mode, bleed) {
// rect = [L, T, R, B] (Illustrator: T > B)
var rw = rect[2] - rect[0];
var rh = rect[1] - rect[3];
var ib = item.geometricBounds;
var iw = ib[2] - ib[0];
var ih = ib[1] - ib[3];
if (iw <= 0 || ih <= 0) return;
var sx = rw / iw;
var sy = rh / ih;
var s = (mode === "cover" ? Math.max(sx, sy) : Math.min(sx, sy))
* (bleed || 1);
item.resize(s * 100, s * 100);
var cx = (rect[0] + rect[2]) / 2;
var cy = (rect[1] + rect[3]) / 2;
var b = item.geometricBounds;
var w = b[2] - b[0];
var h = b[1] - b[3];
item.position = [cx - w / 2, cy + h / 2];
}
```
### Batch Process Files in a Folder
```javascript
@@ -621,6 +919,13 @@ try {
- **File paths on Windows**: Use forward slashes (`/`) or double backslashes (`\\`) in path strings, or use the `File` object constructor.
- **Dialog boxes interrupting batch scripts**: Set `app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS` before batch operations.
- **Collections use `getByName()`**: Many collection objects support `getByName("name")` which throws an error if not found; wrap in try/catch.
- **"Target layer cannot be modified"**: A locked layer, sublayer, or parent group (often a clip group like `Cover_Mask`) is blocking the edit. Recursively clear `locked` and `hidden` across the document before modifying. See [Recursively Unlock Layers and Groups](#recursively-unlock-layers-and-groups-before-editing).
- **"Unable to set placed item's file, is the file path provided valid?"**: The file exists and the path is correct, but `PlacedItem.file` does not accept the format. SVG is the most common cause — use the [open / copy / paste pattern](#placing-svg-content-copypaste-pattern) instead.
- **`RasterItem.file = newFile` does nothing or throws**: `RasterItem` does not expose a writable `file` property. Add a new `PlacedItem` to the same parent, restore the bounds and name, then `.remove()` the raster.
- **`arguments[0]` is `[object BridgeTalk]`** (or empty): The script was launched through ESTK `-run` or a `BridgeTalk` message; positional arguments are not forwarded. Use a sidecar file at a known absolute path. See [External Invocation & Argument Passing](#external-invocation--argument-passing).
- **`$.fileName` is empty**: Same external-launcher cause. Do not derive resource paths from `$.fileName` in scripts that may be invoked headlessly — use absolute paths or `Folder.temp`.
- **Script appears to do nothing**: Almost always either a locked ancestor, suppressed dialogs swallowing the error, or a missing explicit `saveAs` after edits. Add a top-level `try`/`catch` that logs to an absolute path to confirm execution and capture the error.
- **`item.resize(sx, sy)` recentered the artwork unexpectedly**: `resize` defaults to scaling around the item's center (`Transformation.CENTER`). Pass an explicit `scaleAbout` argument or follow with `translate(dx, dy)` to reposition.
## Scripting Constants Reference