[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6AD4FRA8D5JKFDX5N70PHV5":3,"skill-same:01M6AD4FRA8D5JKFDX5N70PHV5":97},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":48,"linkOnly":49,"markdownContent":50,"markdownBody":51,"frontMatter":52,"refName":53,"commitSha":54,"skillDirectory":55,"forksCount":56,"repositoryHtmlUrl":57,"repositoryHomepageUrl":58,"ownerHtmlUrl":59,"ownerBlogUrl":60,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6AD4FRA8D5JKFDX5N70PHV5","adobe-illustrator-scripting",null,"Write, debug, and optimize Adobe Illustrator automation scripts using ExtendScript (JavaScript\u002FJSX). Use when creating or modifying scripts that manipulate documents, layers, paths, text frames, colors, symbols, artboards, or any Illustrator DOM objects. Covers the complete JavaScript object model, coordinate system, measurement units, export workflows, and scripting best practices.","github\u002Fawesome-copilot","github","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F9919?v=4","01M6AC83DS133TPXFSP1DHNCC6","GitHub","01M6AD3PBDYPK7NVFJNJ66Y8RS",41133,"MIT","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fadobe-illustrator-scripting\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47],"adobe","adobe illustrator scripting","scripting","export","when","jsx","layers","measurement","automation scripts","paths","scripts","objects","optimize","coordinate","automation","write","model","text","system","best practices","documents","debug","workflows","javascript","2026-11-10 10:12:30",false,"---\nname: adobe-illustrator-scripting\ndescription: 'Write, debug, and optimize Adobe Illustrator automation scripts using ExtendScript (JavaScript\u002FJSX). Use when creating or modifying scripts that manipulate documents, layers, paths, text frames, colors, symbols, artboards, or any Illustrator DOM objects. Covers the complete JavaScript object model, coordinate system, measurement units, export workflows, and scripting best practices.'\n---\n\n# Adobe Illustrator Scripting\n\nExpert guidance for automating Adobe Illustrator through ExtendScript (JavaScript\u002FJSX). This skill covers the Illustrator scripting object model, all major API objects, code patterns, and best practices for writing production-quality `.jsx` scripts.\n\n## Bundled Assets\n\n- [`references\u002Fobject-model-quick-reference.md`](references\u002Fobject-model-quick-reference.md): Use this as a quick lookup for the Illustrator scripting object model, common document and page item types, and related DOM concepts while writing or debugging scripts.\n- `scripts\u002F`: Contains example Illustrator automation scripts you can use as starting points or implementation patterns for common tasks such as document manipulation, exports, batch processing, and DOM usage. Review and adapt these examples when you need working JSX patterns or want to compare behavior while debugging.\n## When to Use This Skill\n\n- Writing new Illustrator automation scripts (`.jsx` or `.js` files)\n- Debugging or fixing existing Illustrator ExtendScript code\n- Manipulating documents, layers, page items, paths, text, or colors programmatically\n- Batch-processing Illustrator files or generating artwork from data\n- Exporting documents to various formats (PDF, SVG, PNG, EPS, etc.)\n- Working with the Illustrator DOM (Application, Document, Layer, PathItem, TextFrame, etc.)\n- Creating data-driven graphics using variables and datasets\n- Automating print workflows with scripted print options\n\n## Prerequisites\n\n- Adobe Illustrator CC or later installed\n- Basic JavaScript knowledge (ExtendScript is ES3-based with Adobe extensions)\n- Scripts are executed via File > Scripts > Other Scripts, the Scripts menu, or placed in the Startup Scripts folder\n- The ExtendScript Toolkit (ESTK) or any text editor can be used to write `.jsx` files\n\n## Scripting Environment\n\n### Language and File Extensions\n\n| Language | Extension | Platform |\n|---|---|---|\n| ExtendScript\u002FJavaScript | `.jsx`, `.js` | Windows, macOS |\n| AppleScript | `.scpt` | macOS only |\n| VBScript | `.vbs` | Windows only |\n\n**This skill focuses on ExtendScript\u002FJavaScript** as the cross-platform, most widely used option.\n\n### Executing Scripts\n\n- **Scripts menu**: File > Scripts lists scripts from the application scripts folder\n- **Other Scripts**: File > Scripts > Other Scripts to browse and run any `.jsx` file\n- **Startup Scripts**: Place scripts in the Startup Scripts folder to run automatically on launch\n- **Target directive**: Begin scripts with `#target illustrator` when running from ESTK or external tools\n- **`#targetengine` directive**: Use `#targetengine \"session\"` to persist variables across script executions\n- **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).\n\n### Naming Conventions (JavaScript)\n\n- Objects and properties use **camelCase**: `activeDocument`, `pathItems`, `textFrames`\n- The `app` global references the `Application` object\n- Collection indices are **zero-based**: `documents[0]` is the frontmost document\n- Use `typename` property to identify object types at runtime\n\n## Object Model Overview\n\nThe Illustrator DOM follows a strict containment hierarchy:\n\n```\nApplication (app)\n├── activeDocument \u002F documents[]\n│   ├── layers[]\n│   │   ├── pageItems[] (all artwork)\n│   │   ├── pathItems[]\n│   │   ├── compoundPathItems[]\n│   │   ├── textFrames[]\n│   │   ├── placedItems[]\n│   │   ├── rasterItems[]\n│   │   ├── meshItems[]\n│   │   ├── pluginItems[]\n│   │   ├── graphItems[]\n│   │   ├── symbolItems[]\n│   │   ├── nonNativeItems[]\n│   │   ├── legacyTextItems[]\n│   │   └── groupItems[]\n│   ├── artboards[]\n│   ├── views[]\n│   ├── selection (array of selected items)\n│   ├── swatches[], spots[], gradients[], patterns[]\n│   ├── graphicStyles[], brushes[], symbols[]\n│   ├── textFonts[] (via app.textFonts)\n│   ├── stories[], characterStyles[], paragraphStyles[]\n│   ├── variables[], datasets[]\n│   └── inkList[], printOptions\n├── preferences\n├── printerList[]\n└── textFonts[]\n```\n\n### Top-Level Objects\n\n- **Application** (`app`): The root object. Provides access to documents, preferences, fonts, and printers. Key properties: `activeDocument`, `documents`, `textFonts`, `printerList`, `userInteractionLevel`, `version`.\n- **Document**: Represents an open `.ai` file. Key properties: `layers`, `pageItems`, `selection`, `activeLayer`, `width`, `height`, `rulerOrigin`, `documentColorSpace`. Key methods: `saveAs()`, `exportFile()`, `close()`, `print()`.\n- **Layer**: A drawing layer. Key properties: `pageItems`, `pathItems`, `textFrames`, `visible`, `locked`, `opacity`, `name`, `zOrderPosition`, `color`.\n\n## Measurement Units and Coordinates\n\n### Units\n\nAll scripting API values use **points** (72 points = 1 inch). Convert other units:\n\n| Unit | Conversion |\n|---|---|\n| Inches | multiply by 72 |\n| Centimeters | multiply by 28.346 |\n| Millimeters | multiply by 2.834645 |\n| Picas | multiply by 12 |\n\nKerning, tracking, and `aki` properties use **em units** (thousandths of an em, proportional to font size).\n\n### Coordinate System\n\n- For **scripted documents**, the origin `(0,0)` is at the **bottom-left** of the artboard\n- X increases left to right; Y increases bottom to top\n- The `position` property of a page item is the **top-left corner** of its bounding box as `[x, y]`\n- Maximum page item width\u002Fheight: 16348 points\n\n### Art Item Bounds\n\nEvery page item has three bounding rectangles:\n\n- `geometricBounds`: Excludes stroke width `[left, top, right, bottom]`\n- `visibleBounds`: Includes stroke width\n- `controlBounds`: Includes control\u002Fdirection points\n\n## Working with Documents\n\n### Creating and Opening\n\n```javascript\n\u002F\u002F Create a new document\nvar doc = app.documents.add();\n\n\u002F\u002F Create with a preset\nvar preset = new DocumentPreset();\npreset.width = 612;  \u002F\u002F 8.5 inches\npreset.height = 792; \u002F\u002F 11 inches\npreset.colorMode = DocumentColorSpace.CMYK;\nvar doc = app.documents.addDocument(\"Print\", preset);\n\n\u002F\u002F Open an existing file\nvar fileRef = new File(\"\u002Fpath\u002Fto\u002Ffile.ai\");\nvar doc = app.open(fileRef);\n```\n\n### Saving and Exporting\n\n```javascript\n\u002F\u002F Save as Illustrator format\nvar saveOpts = new IllustratorSaveOptions();\nsaveOpts.compatibility = Compatibility.ILLUSTRATOR17; \u002F\u002F CC\ndoc.saveAs(new File(\"\u002Fpath\u002Fto\u002Foutput.ai\"), saveOpts);\n\n\u002F\u002F Export as PDF\nvar pdfOpts = new PDFSaveOptions();\npdfOpts.compatibility = PDFCompatibility.ACROBAT7;\npdfOpts.preserveEditability = false;\ndoc.saveAs(new File(\"\u002Fpath\u002Fto\u002Foutput.pdf\"), pdfOpts);\n\n\u002F\u002F Export as PNG\nvar pngOpts = new ExportOptionsPNG24();\npngOpts.horizontalScale = 300;\npngOpts.verticalScale = 300;\npngOpts.transparency = true;\ndoc.exportFile(new File(\"\u002Fpath\u002Fto\u002Foutput.png\"), ExportType.PNG24, pngOpts);\n\n\u002F\u002F Export as SVG\nvar svgOpts = new ExportOptionsSVG();\nsvgOpts.fontType = SVGFontType.OUTLINEFONT;\ndoc.exportFile(new File(\"\u002Fpath\u002Fto\u002Foutput.svg\"), ExportType.SVG, svgOpts);\n```\n\n## Working with Paths and Shapes\n\n### Built-in Shape Methods\n\nThe `pathItems` collection provides convenience methods for common shapes:\n\n```javascript\nvar doc = app.activeDocument;\nvar layer = doc.activeLayer;\n\n\u002F\u002F Rectangle: rectangle(top, left, width, height)\nvar rect = layer.pathItems.rectangle(500, 100, 200, 150);\n\n\u002F\u002F Rounded rectangle: roundedRectangle(top, left, width, height, hRadius, vRadius)\nvar rrect = layer.pathItems.roundedRectangle(500, 100, 200, 150, 20, 20);\n\n\u002F\u002F Ellipse: ellipse(top, left, width, height)\nvar oval = layer.pathItems.ellipse(400, 200, 100, 100);\n\n\u002F\u002F Polygon: polygon(centerX, centerY, radius, sides)\nvar hex = layer.pathItems.polygon(300, 300, 50, 6);\n\n\u002F\u002F Star: star(centerX, centerY, radius, innerRadius, points)\nvar star = layer.pathItems.star(300, 300, 50, 25, 5);\n```\n\n### Freeform Paths Using Coordinate Arrays\n\n```javascript\nvar doc = app.activeDocument;\nvar path = doc.pathItems.add();\npath.setEntirePath([[100, 100], [200, 200], [300, 100]]);\npath.closed = false;\npath.stroked = true;\npath.strokeWidth = 2;\n```\n\n### Freeform Paths Using PathPoint Objects\n\n```javascript\nvar doc = app.activeDocument;\nvar path = doc.pathItems.add();\n\nvar point1 = path.pathPoints.add();\npoint1.anchor = [100, 100];\npoint1.leftDirection = [100, 100];\npoint1.rightDirection = [150, 150];\npoint1.pointType = PointType.SMOOTH;\n\nvar point2 = path.pathPoints.add();\npoint2.anchor = [300, 100];\npoint2.leftDirection = [250, 150];\npoint2.rightDirection = [300, 100];\npoint2.pointType = PointType.SMOOTH;\n\npath.closed = false;\n```\n\n### Path Properties\n\n```javascript\nvar item = doc.pathItems[0];\nitem.filled = true;\nitem.stroked = true;\nitem.strokeWidth = 1.5;\nitem.strokeCap = StrokeCap.ROUNDENDCAP;\nitem.strokeJoin = StrokeJoin.ROUNDENDJOIN;\nitem.opacity = 80;\nitem.closed = true;\n```\n\n## Working with Colors\n\n### Color Objects\n\n```javascript\n\u002F\u002F RGB Color (values 0-255)\nvar red = new RGBColor();\nred.red = 255;\nred.green = 0;\nred.blue = 0;\n\n\u002F\u002F CMYK Color (values 0-100)\nvar cyan = new CMYKColor();\ncyan.cyan = 100;\ncyan.magenta = 0;\ncyan.yellow = 0;\ncyan.black = 0;\n\n\u002F\u002F Grayscale (0-100, 0 = black)\nvar gray = new GrayColor();\ngray.gray = 50;\n\n\u002F\u002F Lab Color\nvar lab = new LabColor();\nlab.l = 50;\nlab.a = 20;\nlab.b = -30;\n\n\u002F\u002F No color (transparent)\nvar none = new NoColor();\n```\n\n### Applying Colors\n\n```javascript\nvar item = doc.pathItems[0];\nitem.fillColor = red;\nitem.strokeColor = cyan;\n\n\u002F\u002F Gradient fill\nvar gradient = doc.gradients.add();\ngradient.type = GradientType.LINEAR;\ngradient.gradientStops[0].color = red;\ngradient.gradientStops[1].color = cyan;\n\nvar gradColor = new GradientColor();\ngradColor.gradient = gradient;\nitem.fillColor = gradColor;\n```\n\n### Spot Colors and Swatches\n\n```javascript\n\u002F\u002F Create a spot color\nvar spot = doc.spots.add();\nspot.name = \"My Spot Color\";\nspot.color = red; \u002F\u002F Base color definition\n\nvar spotColor = new SpotColor();\nspotColor.spot = spot;\nspotColor.tint = 100;\n\nitem.fillColor = spotColor;\n\n\u002F\u002F Access a swatch by name\nvar swatch = doc.swatches.getByName(\"PANTONE 185 C\");\nitem.fillColor = swatch.color;\n```\n\n## Working with Text\n\n### Text Frame Types\n\n```javascript\nvar doc = app.activeDocument;\n\n\u002F\u002F Point text\nvar pointText = doc.textFrames.add();\npointText.contents = \"Hello World!\";\npointText.position = [100, 500];\n\n\u002F\u002F Area text (text inside a path)\nvar rectPath = doc.pathItems.rectangle(500, 100, 200, 100);\nvar areaText = doc.textFrames.areaText(rectPath);\nareaText.contents = \"Text inside a rectangle shape.\";\n\n\u002F\u002F Path text (text along a path)\nvar curvePath = doc.pathItems.add();\ncurvePath.setEntirePath([[50, 300], [150, 400], [250, 300]]);\nvar pathText = doc.textFrames.pathText(curvePath);\npathText.contents = \"Text on a path\";\n```\n\n### Character and Paragraph Formatting\n\n```javascript\nvar tf = doc.textFrames[0];\nvar textRange = tf.textRange;\n\n\u002F\u002F Character attributes\nvar charAttr = textRange.characterAttributes;\ncharAttr.size = 24;           \u002F\u002F Font size in points\ncharAttr.textFont = app.textFonts.getByName(\"ArialMT\");\ncharAttr.fillColor = red;\ncharAttr.tracking = 50;       \u002F\u002F Em units\ncharAttr.horizontalScale = 100;\ncharAttr.verticalScale = 100;\ncharAttr.baselineShift = 0;\n\n\u002F\u002F Paragraph attributes\nvar paraAttr = textRange.paragraphAttributes;\nparaAttr.justification = Justification.CENTER;\nparaAttr.firstLineIndent = 0;\nparaAttr.leftIndent = 0;\nparaAttr.spaceBefore = 0;\nparaAttr.spaceAfter = 0;\n```\n\n### Accessing Text Content\n\n```javascript\nvar tf = doc.textFrames[0];\n\n\u002F\u002F Access sub-ranges\nvar firstChar = tf.characters[0];\nvar firstWord = tf.words[0];\nvar firstPara = tf.paragraphs[0];\nvar firstLine = tf.lines[0];\n\n\u002F\u002F Modify specific ranges\ntf.words[0].characterAttributes.size = 36;\ntf.paragraphs[0].paragraphAttributes.justification = Justification.LEFT;\n```\n\n### Threading Text Frames\n\n```javascript\nvar frame1 = doc.textFrames.areaText(path1);\nvar frame2 = doc.textFrames.areaText(path2);\n\n\u002F\u002F Link frames so text flows from frame1 to frame2\nframe1.nextFrame = frame2;\n\n\u002F\u002F Stories represent the full text across threaded frames\nvar storyCount = doc.stories.length;\nvar fullText = doc.stories[0].textRange.contents;\n```\n\n## Working with Layers\n\n```javascript\nvar doc = app.activeDocument;\n\n\u002F\u002F Create a layer\nvar newLayer = doc.layers.add();\nnewLayer.name = \"Background\";\nnewLayer.visible = true;\nnewLayer.locked = false;\nnewLayer.opacity = 100;\n\n\u002F\u002F Access existing layers\nvar topLayer = doc.layers[0];\nvar layerByName = doc.layers.getByName(\"Background\");\n\n\u002F\u002F Move items between layers\nvar item = doc.pathItems[0];\nitem.move(newLayer, ElementPlacement.PLACEATBEGINNING);\n\n\u002F\u002F Reorder layers\nnewLayer.zOrder(ZOrderMethod.SENDTOBACK);\n```\n\n## Working with Selections\n\n```javascript\n\u002F\u002F Get current selection\nvar sel = app.activeDocument.selection;\n\n\u002F\u002F Iterate selected items\nfor (var i = 0; i \u003C sel.length; i++) {\n    var item = sel[i];\n    \u002F\u002F Check type using typename\n    if (item.typename === \"PathItem\") {\n        item.fillColor = red;\n    } else if (item.typename === \"TextFrame\") {\n        item.contents = \"Modified\";\n    }\n}\n\n\u002F\u002F Select an item programmatically\ndoc.pathItems[0].selected = true;\n\n\u002F\u002F Deselect all\ndoc.selection = null;\n```\n\n## Working with Symbols\n\n```javascript\n\u002F\u002F Place a symbol instance\nvar sym = doc.symbols.getByName(\"MySymbol\");\nvar instance = doc.symbolItems.add(sym);\ninstance.position = [200, 400];\n\n\u002F\u002F Access symbol definition\nvar symDef = instance.symbol;\n\n\u002F\u002F Break link to symbol (expand to regular art)\ninstance.breakLink();\n```\n\n## Transformations\n\n```javascript\nvar item = doc.pathItems[0];\n\n\u002F\u002F Rotate 45 degrees around center\nitem.rotate(45);\n\n\u002F\u002F Scale to 50% width, 75% height\nitem.resize(50, 75);\n\n\u002F\u002F Translate (move) by 100 points right and 50 points up\nitem.translate(100, 50);\n\n\u002F\u002F Using a transformation matrix\nvar matrix = app.getIdentityMatrix();\nmatrix = app.concatenateRotationMatrix(matrix, 30);\nmatrix = app.concatenateScaleMatrix(matrix, 150, 150);\nitem.transform(matrix);\n```\n\n## Working with Artboards\n\n```javascript\nvar doc = app.activeDocument;\n\n\u002F\u002F Access artboards\nvar ab = doc.artboards[0];\nvar rect = ab.artboardRect; \u002F\u002F [left, top, right, bottom]\n\n\u002F\u002F Create a new artboard\nvar newAB = doc.artboards.add([0, 0, 612, 792]); \u002F\u002F Letter size\nnewAB.name = \"Page 2\";\n\n\u002F\u002F Set active artboard\ndoc.artboards.setActiveArtboardIndex(1);\n```\n\n## Data-Driven Graphics (Variables and Datasets)\n\n```javascript\n\u002F\u002F Variables link document items to data fields\nvar v = doc.variables.add();\nv.kind = VariableKind.TEXTUAL;\nv.name = \"headline\";\n\n\u002F\u002F Link a text frame to the variable\nvar tf = doc.textFrames[0];\ntf.contentVariable = v;\n\n\u002F\u002F Create datasets for batch content\nvar ds = doc.dataSets.add();\nds.name = \"Version 1\";\n\u002F\u002F Dataset captures current variable bindings\n\n\u002F\u002F Switch datasets to swap content\ndoc.dataSets[0].display();\n```\n\n## Printing\n\n```javascript\nvar doc = app.activeDocument;\nvar opts = new PrintOptions();\n\nopts.printPreset = \"Default\";\n\n\u002F\u002F Paper options\nvar paperOpts = new PrintPaperOptions();\npaperOpts.name = \"Letter\";\nopts.paperOptions = paperOpts;\n\n\u002F\u002F Job options\nvar jobOpts = new PrintJobOptions();\njobOpts.copies = 1;\njobOpts.designation = PrintArtworkDesignation.VISIBLELAYERS;\nopts.jobOptions = jobOpts;\n\ndoc.print(opts);\n```\n\n## User Interaction Levels\n\nControl whether Illustrator shows dialogs during script execution:\n\n```javascript\n\u002F\u002F Suppress all dialogs\napp.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;\n\n\u002F\u002F Perform operations that might prompt dialogs...\ndoc.close(SaveOptions.DONOTSAVECHANGES);\n\n\u002F\u002F Restore dialog display\napp.userInteractionLevel = UserInteractionLevel.DISPLAYALERTS;\n```\n\n## Working with Methods (JavaScript-Specific)\n\nWhen calling methods with multiple optional parameters, use `undefined` to skip middle parameters:\n\n```javascript\n\u002F\u002F rotate(angle, [changePositions], [changeFillPatterns], [changeFillGradients], ...)\nitem.rotate(30, undefined, undefined, true);\n```\n\n## External Invocation & Argument Passing\n\nIllustrator scripts are routinely launched from outside the application —\nshell scripts, schedulers, build pipelines, ExtendScript Toolkit, or\n`BridgeTalk` messages from other Creative Cloud apps. The execution\nenvironment under those launchers differs from the in-application *File >\nScripts* path in several ways that frequently break otherwise-correct code.\n\n### `arguments[]` Is Unreliable Under External Launchers\n\nExtendScript Toolkit's `-run` invocation and `BridgeTalk.send()` do not\nforward arbitrary launcher arguments into the script's top-level\n`arguments[]` array. In many configurations the array contains a single\n`[object BridgeTalk]` element instead of the values the caller passed, as\ndemonstrated below:\n\n```javascript\n\u002F\u002F At top of script\nvar passed = (typeof arguments !== \"undefined\") ? arguments : [];\nfor (var i = 0; i \u003C passed.length; i++) {\n    $.writeln(\"arg[\" + i + \"] = \" + passed[i]);\n    \u002F\u002F Often prints: arg[0] = [object BridgeTalk]\n}\n```\n\n**Do not rely on `arguments[]` for required inputs when the script is\nlaunched externally.** Use one of the following more reliable channels.\n\n### Sidecar File for Parameters\n\nWhen a script fails under an external launcher and the source of the error\nis not obvious, fall back to a sidecar file: have the caller write a small\ntext file at a known absolute path, and read it on startup. This works\nregardless of launcher quirks and is easy to inspect after a failed run.\n\n```javascript\nvar SIDECAR_PATH = \"C:\u002FUsers\u002FuserName\u002Fjob.args.txt\";\n\nfunction readSidecar(path) {\n    var f = new File(path);\n    if (!f.exists || !f.open(\"r\")) return null;\n    var lines = [];\n    while (!f.eof) {\n        var ln = f.readln();\n        if (ln && !\u002F^\\s*$\u002F.test(ln)) lines.push(ln);\n    }\n    f.close();\n    return {\n        input:  lines[0],\n        output: lines[1],\n        mode:   lines[2]\n    };\n}\n```\n\nA `key=value` format is equally workable and avoids positional fragility:\n\n```text\ninput=C:\u002Fpath\u002Fto\u002Finput.ai\noutput=C:\u002Fpath\u002Fto\u002Foutput.pdf\nmode=preview\n```\n\n### Environment Variables\n\n`$.getenv(\"NAME\")` returns environment variables visible to **Illustrator's\nprocess**, not the launcher's. If the launcher needs Illustrator to see a\nvalue, it must set the variable system-wide or in Illustrator's parent\nenvironment before launching. For per-invocation values, prefer a sidecar\nfile.\n\n### `$.fileName` and `File($.fileName).parent`\n\nUnder in-application execution, `$.fileName` is the absolute path of the\nrunning script and `File($.fileName).parent` yields the script's folder.\nUnder some external launchers (notably ESTK `-run`) `$.fileName` can be\nempty, causing relative path resolution to silently fail.\n\n```javascript\n\u002F\u002F Fragile: returns null under some launchers\nvar here = $.fileName ? File($.fileName).parent : null;\nvar sidecar = here ? new File(here.fsName + \"\u002Fjob.args.txt\") : null;\n\n\u002F\u002F Robust: hardcode a known absolute path or fall back to a stable location\nvar sidecar = new File(\"C:\u002FUsers\u002FuserName\u002Fjob.args.txt\");\nif (!sidecar.exists) sidecar = new File(Folder.temp.fsName + \"\u002Fjob.args.txt\");\n```\n\n### Diagnostic Logging to an Absolute Path\n\nSilent failures are common because dialogs are suppressed and the launcher\nmay not surface `$.writeln` output. Write a plain-text log to a known\nabsolute path so a run can be inspected after the fact. Create the parent\nfolder on demand so the first call cannot fail for a missing directory.\n\n```javascript\nvar LOG_PATH = \"C:\u002FUsers\u002FuserName\u002Flogs\u002Fjob.log\";\n\nfunction log(msg) {\n    try {\n        var f = new File(LOG_PATH);\n        try { if (!f.parent.exists) f.parent.create(); } catch (eDir) {}\n        if (f.open(\"a\")) {\n            f.writeln(\"[\" + new Date() + \"] \" + msg);\n            f.close();\n        }\n    } catch (e) {}\n}\n```\n\n### Wrap the Entry Point in `try { ... } catch`\n\nExternally launched scripts often fail without any visible indication. A\ntop-level `try`\u002F`catch` that writes the error to the log file converts\nsilent failures into a single inspectable line.\n\n```javascript\ntry {\n    main();\n} catch (err) {\n    log(\"FATAL: \" + err + (err && err.line ? \" line=\" + err.line : \"\"));\n}\n```\n\n### Suppress User Interaction\n\nExternal callers cannot answer dialogs. Disable them before any DOM work\nand avoid `alert()` \u002F `confirm()` \u002F `prompt()` entirely in scripts that may\nbe launched headlessly.\n\n```javascript\napp.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;\n```\n\n### Save Explicitly\n\nClosing or letting Illustrator return to its idle state does not save the\nworking file. After all DOM edits, call `doc.saveAs(...)` (or `doc.save()`)\nexplicitly and log whether it succeeded.\n\n```javascript\nvar opts = new IllustratorSaveOptions();\nopts.compatibility = Compatibility.ILLUSTRATOR17;\ndoc.saveAs(new File(doc.fullName.fsName), opts);\n```\n\n## Common Patterns\n\n### Iterate All Page Items in a Document\n\n```javascript\nfunction processAllItems(doc) {\n    for (var i = 0; i \u003C doc.pageItems.length; i++) {\n        var item = doc.pageItems[i];\n        \u002F\u002F Process based on type\n        switch (item.typename) {\n            case \"PathItem\":\n                \u002F\u002F handle path\n                break;\n            case \"TextFrame\":\n                \u002F\u002F handle text\n                break;\n            case \"GroupItem\":\n                \u002F\u002F handle group (may contain nested items)\n                break;\n        }\n    }\n}\n```\n\n### Recursively Unlock Layers and Groups Before Editing\n\nA locked layer or any locked ancestor (parent group, clip group, sublayer)\nwill cause edits to throw `Error: Target layer cannot be modified`. Walk the\nfull hierarchy and clear `locked` \u002F `hidden` flags before performing DOM\nmodifications.\n\n```javascript\nfunction unlockAll(doc) {\n    function visitLayers(layers) {\n        for (var i = 0; i \u003C layers.length; i++) {\n            var lyr = layers[i];\n            try { lyr.locked = false; lyr.visible = true; } catch (e) {}\n            visitItems(lyr);\n            if (lyr.layers && lyr.layers.length) visitLayers(lyr.layers);\n        }\n    }\n    function visitItems(container) {\n        var items = container.pageItems;\n        for (var j = 0; j \u003C items.length; j++) {\n            var it = items[j];\n            try { it.locked = false; it.hidden = false; } catch (e) {}\n            if (it.typename === \"GroupItem\") visitItems(it);\n        }\n    }\n    visitLayers(doc.layers);\n}\n```\n\n### Replacing the File Behind a Linked Image (Relink)\n\n`PlacedItem.file = newFile` replaces a linked image while preserving the\nparent, stacking order, and (after re-applying) the bounds. **`RasterItem`\ndoes not expose a writable `file` property**, so when a placeholder is a\nraster you must add a fresh `PlacedItem` in the same parent, copy the bounds,\nthen remove the original.\n\n```javascript\nfunction relinkOrRebuild(item, newFile) {\n    var bounds = item.geometricBounds.slice();\n    var parent = item.parent;\n    var name   = item.name;\n\n    if (item.typename === \"PlacedItem\") {\n        item.file = newFile;\n        item.geometricBounds = bounds;\n        return item;\n    }\n\n    \u002F\u002F RasterItem path: rebuild as a linked PlacedItem in the same parent.\n    var fresh = parent.placedItems.add();\n    fresh.file = newFile;\n    fresh.geometricBounds = bounds;\n    if (name) try { fresh.name = name; } catch (e) {}\n    fresh.move(item, ElementPlacement.PLACEBEFORE);\n    item.remove();\n    return fresh;\n}\n```\n\n### Placing SVG Content (Copy\u002FPaste Pattern)\n\n`PlacedItem.file` accepts raster formats and AI\u002FPDF, **but not SVG**. Setting\nit to an `.svg` File throws `Unable to set placed item's file, is the file\npath provided valid?`. The reliable way to bring SVG artwork into a document\nis to open the SVG as a separate document, select all, copy, close, and paste\ninto the working document.\n\n```javascript\nfunction placeSVG(targetDoc, svgFile, targetLayer) {\n    var donor = app.open(svgFile);\n    app.executeMenuCommand(\"selectall\");\n    app.executeMenuCommand(\"copy\");\n    donor.close(SaveOptions.DONOTSAVECHANGES);\n\n    app.activeDocument = targetDoc;\n    targetDoc.activeLayer = targetLayer;\n    app.executeMenuCommand(\"pasteFront\");\n\n    var sel = targetDoc.selection;\n    if (!sel || sel.length === 0) return null;\n    if (sel.length === 1) return sel[0];\n\n    \u002F\u002F Multiple pasted items: group them so callers get a single handle.\n    var group = targetLayer.groupItems.add();\n    for (var i = sel.length - 1; i >= 0; i--) {\n        sel[i].move(group, ElementPlacement.PLACEATBEGINNING);\n    }\n    return group;\n}\n```\n\n### Finding a Clipping Path Inside a Mask Group\n\nClip groups expose their clipping shape as a child `PathItem` (or, less\ncommonly, a child of a `CompoundPathItem`) with `clipping === true`. The\nclip's `geometricBounds` give the visible frame to size or center content\nagainst.\n\n```javascript\nfunction findClipPath(group) {\n    var items = group.pageItems;\n    for (var i = 0; i \u003C items.length; i++) {\n        var it = items[i];\n        try {\n            if (it.typename === \"PathItem\" && it.clipping) return it;\n            if (it.typename === \"CompoundPathItem\") {\n                for (var j = 0; j \u003C it.pathItems.length; j++) {\n                    if (it.pathItems[j].clipping) return it;\n                }\n            }\n        } catch (e) {}\n    }\n    return null;\n}\n```\n\n### Cover-Fit and Contain-Fit Sizing\n\nTo make an image fully cover a rectangle (any overflow hidden by a mask), use\nthe larger of the width\u002Fheight ratios. To make it fit entirely inside, use\nthe smaller. A bleed factor (e.g. `1.10`) lets a cover image extend slightly\npast the clip edge.\n\n```javascript\nfunction fitItemToRect(item, rect, mode, bleed) {\n    \u002F\u002F rect = [L, T, R, B] (Illustrator: T > B)\n    var rw = rect[2] - rect[0];\n    var rh = rect[1] - rect[3];\n    var ib = item.geometricBounds;\n    var iw = ib[2] - ib[0];\n    var ih = ib[1] - ib[3];\n    if (iw \u003C= 0 || ih \u003C= 0) return;\n\n    var sx = rw \u002F iw;\n    var sy = rh \u002F ih;\n    var s  = (mode === \"cover\" ? Math.max(sx, sy) : Math.min(sx, sy))\n           * (bleed || 1);\n    item.resize(s * 100, s * 100);\n\n    var cx = (rect[0] + rect[2]) \u002F 2;\n    var cy = (rect[1] + rect[3]) \u002F 2;\n    var b  = item.geometricBounds;\n    var w  = b[2] - b[0];\n    var h  = b[1] - b[3];\n    item.position = [cx - w \u002F 2, cy + h \u002F 2];\n}\n```\n\n\n\n### Batch Process Files in a Folder\n\n```javascript\nvar folder = Folder.selectDialog(\"Select folder of .ai files\");\nif (folder) {\n    var files = folder.getFiles(\"*.ai\");\n    for (var i = 0; i \u003C files.length; i++) {\n        var doc = app.open(files[i]);\n        \u002F\u002F Process each document...\n        doc.close(SaveOptions.DONOTSAVECHANGES);\n    }\n}\n```\n\n### Error Handling\n\n```javascript\ntry {\n    var doc = app.activeDocument;\n    var layer = doc.layers.getByName(\"NonExistentLayer\");\n} catch (e) {\n    alert(\"Error: \" + e.message);\n    \u002F\u002F e.message, e.line, e.fileName available\n}\n```\n\n## Troubleshooting\n\n- **\"undefined is not an object\"**: Usually means the collection is empty or the index is out of bounds. Check `.length` before accessing items.\n- **Script runs but nothing changes visually**: Call `app.redraw()` to force a screen refresh after modifications.\n- **Color mode mismatch**: Document color space (RGB vs CMYK) must match color objects. Use `doc.documentColorSpace` to check.\n- **Position seems wrong**: Remember scripted documents use bottom-left origin with Y increasing upward. The `position` property is the top-left of the bounding box.\n- **Text not appearing**: Ensure the text frame has a non-zero size. For point text, set `position`; for area text, provide a valid path to `areaText()`.\n- **File paths on Windows**: Use forward slashes (`\u002F`) or double backslashes (`\\\\`) in path strings, or use the `File` object constructor.\n- **Dialog boxes interrupting batch scripts**: Set `app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS` before batch operations.\n- **Collections use `getByName()`**: Many collection objects support `getByName(\"name\")` which throws an error if not found; wrap in try\u002Fcatch.\n- **\"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).\n- **\"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 \u002F copy \u002F paste pattern](#placing-svg-content-copypaste-pattern) instead.\n- **`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.\n- **`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).\n- **`$.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`.\n- **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`\u002F`catch` that logs to an absolute path to confirm execution and capture the error.\n- **`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.\n\n## Scripting Constants Reference\n\nCommon enumeration constants used across the API:\n\n| Category | Constants |\n|---|---|\n| **Color Space** | `DocumentColorSpace.RGB`, `DocumentColorSpace.CMYK` |\n| **Justification** | `Justification.LEFT`, `Justification.CENTER`, `Justification.RIGHT`, `Justification.FULLJUSTIFY` |\n| **Point Type** | `PointType.SMOOTH`, `PointType.CORNER` |\n| **Stroke Cap** | `StrokeCap.BUTTENDCAP`, `StrokeCap.ROUNDENDCAP`, `StrokeCap.PROJECTINGENDCAP` |\n| **Stroke Join** | `StrokeJoin.MITERENDJOIN`, `StrokeJoin.ROUNDENDJOIN`, `StrokeJoin.BEVELENDJOIN` |\n| **Blend Mode** | `BlendModes.NORMAL`, `BlendModes.MULTIPLY`, `BlendModes.SCREEN`, `BlendModes.OVERLAY` |\n| **Save Options** | `SaveOptions.SAVECHANGES`, `SaveOptions.DONOTSAVECHANGES`, `SaveOptions.PROMPTTOSAVECHANGES` |\n| **Export Type** | `ExportType.PNG24`, `ExportType.PNG8`, `ExportType.JPEG`, `ExportType.SVG`, `ExportType.TIFF`, `ExportType.PHOTOSHOP`, `ExportType.AUTOCAD`, `ExportType.FLASH` |\n| **Element Placement** | `ElementPlacement.PLACEATBEGINNING`, `ElementPlacement.PLACEATEND`, `ElementPlacement.PLACEBEFORE`, `ElementPlacement.PLACEAFTER`, `ElementPlacement.INSIDE` |\n| **Z-Order** | `ZOrderMethod.BRINGTOFRONT`, `ZOrderMethod.SENDTOBACK`, `ZOrderMethod.BRINGFORWARD`, `ZOrderMethod.SENDBACKWARD` |\n| **Gradient Type** | `GradientType.LINEAR`, `GradientType.RADIAL` |\n| **Text Frame Kind** | `TextType.POINTTEXT`, `TextType.AREATEXT`, `TextType.PATHTEXT` |\n| **Variable Kind** | `VariableKind.TEXTUAL`, `VariableKind.IMAGE`, `VariableKind.VISIBILITY`, `VariableKind.GRAPH` |\n| **User Interaction** | `UserInteractionLevel.DISPLAYALERTS`, `UserInteractionLevel.DONTDISPLAYALERTS` |\n| **Compatibility** | `Compatibility.ILLUSTRATOR10` through `Compatibility.ILLUSTRATOR24` |\n\n## JavaScript Object Reference (Complete API Object List)\n\nThe Illustrator JavaScript API contains the following objects, grouped by category:\n\n### Core Objects\n\n`Application`, `Document`, `Documents`, `DocumentPreset`, `Layer`, `Layers`, `PageItem`, `PageItems`, `View`, `Views`, `Preferences`\n\n### Path and Shape Objects\n\n`PathItem`, `PathItems`, `PathPoint`, `PathPoints`, `CompoundPathItem`, `CompoundPathItems`, `GroupItem`, `GroupItems`\n\n### Text Objects\n\n`TextFrame`, `TextRange`, `TextRanges`, `TextPath`, `Characters`, `Words`, `Paragraphs`, `Lines`, `InsertionPoint`, `InsertionPoints`, `Story`, `Stories`, `CharacterAttributes`, `ParagraphAttributes`, `CharacterStyle`, `CharacterStyles`, `ParagraphStyle`, `ParagraphStyles`, `TextFont`, `TextFonts`, `TabStopInfo`\n\n### Color Objects\n\n`RGBColor`, `CMYKColor`, `GrayColor`, `LabColor`, `NoColor`, `SpotColor`, `Spot`, `Spots`, `PatternColor`, `GradientColor`, `Color`, `Gradient`, `Gradients`, `GradientStop`, `GradientStops`\n\n### Swatch and Style Objects\n\n`Swatch`, `Swatches`, `SwatchGroup`, `SwatchGroups`, `GraphicStyle`, `GraphicStyles`, `Pattern`, `Patterns`, `Brush`, `Brushes`\n\n### Symbol Objects\n\n`Symbol`, `Symbols`, `SymbolItem`, `SymbolItems`\n\n### Artboard Objects\n\n`Artboard`, `Artboards`\n\n### Placed and Raster Objects\n\n`PlacedItem`, `PlacedItems`, `RasterItem`, `RasterItems`, `MeshItem`, `MeshItems`, `GraphItem`, `GraphItems`, `PluginItem`, `PluginItems`, `NonNativeItem`, `NonNativeItems`, `LegacyTextItem`, `LegacyTextItems`\n\n### Data-Driven Objects\n\n`Variable`, `Variables`, `Dataset`, `Datasets`\n\n### Matrix and Transform Objects\n\n`Matrix`\n\n### Tag Objects\n\n`Tag`, `Tags`\n\n### Tracing Objects\n\n`TracingObject`, `TracingOptions`\n\n### Save and Export Options\n\n`IllustratorSaveOptions`, `EPSSaveOptions`, `PDFSaveOptions`, `FXGSaveOptions`, `ExportOptionsAutoCAD`, `ExportOptionsFlash`, `ExportOptionsGIF`, `ExportOptionsJPEG`, `ExportOptionsPhotoshop`, `ExportOptionsPNG8`, `ExportOptionsPNG24`, `ExportOptionsSVG`, `ExportOptionsTIFF`\n\n### Open Options\n\n`OpenOptions`, `OpenOptionsAutoCAD`, `OpenOptionsFreeHand`, `OpenOptionsPhotoshop`, `PDFFileOptions`, `PhotoshopFileOptions`\n\n### Print Objects\n\n`PrintOptions`, `PrintJobOptions`, `PrintPaperOptions`, `PrintColorManagementOptions`, `PrintColorSeparationOptions`, `PrintCoordinateOptions`, `PrintFlattenerOptions`, `PrintFontOptions`, `PrintPageMarksOptions`, `PrintPostScriptOptions`, `Printer`, `PrinterInfo`, `Paper`, `PaperInfo`, `PPDFile`, `PPDFileInfo`, `Ink`, `InkInfo`, `Screen`, `ScreenInfo`, `ScreenSpotFunction`\n\n### Image and Rasterize Options\n\n`ImageCaptureOptions`, `RasterEffectOptions`, `RasterizeOptions`\n\n## References\n\n- [Changelog](https:\u002F\u002Fai-scripting.docsforadobe.dev\u002Fintroduction\u002Fchangelog\u002F) - Recent scripting API changes (CC 2020 added `Document.getPageItemFromUuid` and `PageItem.uuid`; CC 2017 added `Application.getIsFileOpen`)\n- [Illustrator Scripting Guide](https:\u002F\u002Fai-scripting.docsforadobe.dev\u002F) - Full community-maintained documentation\n","# Adobe Illustrator Scripting\n\nExpert guidance for automating Adobe Illustrator through ExtendScript (JavaScript\u002FJSX). This skill covers the Illustrator scripting object model, all major API objects, code patterns, and best practices for writing production-quality `.jsx` scripts.\n\n## Bundled Assets\n\n- [`references\u002Fobject-model-quick-reference.md`](references\u002Fobject-model-quick-reference.md): Use this as a quick lookup for the Illustrator scripting object model, common document and page item types, and related DOM concepts while writing or debugging scripts.\n- `scripts\u002F`: Contains example Illustrator automation scripts you can use as starting points or implementation patterns for common tasks such as document manipulation, exports, batch processing, and DOM usage. Review and adapt these examples when you need working JSX patterns or want to compare behavior while debugging.\n## When to Use This Skill\n\n- Writing new Illustrator automation scripts (`.jsx` or `.js` files)\n- Debugging or fixing existing Illustrator ExtendScript code\n- Manipulating documents, layers, page items, paths, text, or colors programmatically\n- Batch-processing Illustrator files or generating artwork from data\n- Exporting documents to various formats (PDF, SVG, PNG, EPS, etc.)\n- Working with the Illustrator DOM (Application, Document, Layer, PathItem, TextFrame, etc.)\n- Creating data-driven graphics using variables and datasets\n- Automating print workflows with scripted print options\n\n## Prerequisites\n\n- Adobe Illustrator CC or later installed\n- Basic JavaScript knowledge (ExtendScript is ES3-based with Adobe extensions)\n- Scripts are executed via File > Scripts > Other Scripts, the Scripts menu, or placed in the Startup Scripts folder\n- The ExtendScript Toolkit (ESTK) or any text editor can be used to write `.jsx` files\n\n## Scripting Environment\n\n### Language and File Extensions\n\n| Language | Extension | Platform |\n|---|---|---|\n| ExtendScript\u002FJavaScript | `.jsx`, `.js` | Windows, macOS |\n| AppleScript | `.scpt` | macOS only |\n| VBScript | `.vbs` | Windows only |\n\n**This skill focuses on ExtendScript\u002FJavaScript** as the cross-platform, most widely used option.\n\n### Executing Scripts\n\n- **Scripts menu**: File > Scripts lists scripts from the application scripts folder\n- **Other Scripts**: File > Scripts > Other Scripts to browse and run any `.jsx` file\n- **Startup Scripts**: Place scripts in the Startup Scripts folder to run automatically on launch\n- **Target directive**: Begin scripts with `#target illustrator` when running from ESTK or external tools\n- **`#targetengine` directive**: Use `#targetengine \"session\"` to persist variables across script executions\n- **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).\n\n### Naming Conventions (JavaScript)\n\n- Objects and properties use **camelCase**: `activeDocument`, `pathItems`, `textFrames`\n- The `app` global references the `Application` object\n- Collection indices are **zero-based**: `documents[0]` is the frontmost document\n- Use `typename` property to identify object types at runtime\n\n## Object Model Overview\n\nThe Illustrator DOM follows a strict containment hierarchy:\n\n```\nApplication (app)\n├── activeDocument \u002F documents[]\n│   ├── layers[]\n│   │   ├── pageItems[] (all artwork)\n│   │   ├── pathItems[]\n│   │   ├── compoundPathItems[]\n│   │   ├── textFrames[]\n│   │   ├── placedItems[]\n│   │   ├── rasterItems[]\n│   │   ├── meshItems[]\n│   │   ├── pluginItems[]\n│   │   ├── graphItems[]\n│   │   ├── symbolItems[]\n│   │   ├── nonNativeItems[]\n│   │   ├── legacyTextItems[]\n│   │   └── groupItems[]\n│   ├── artboards[]\n│   ├── views[]\n│   ├── selection (array of selected items)\n│   ├── swatches[], spots[], gradients[], patterns[]\n│   ├── graphicStyles[], brushes[], symbols[]\n│   ├── textFonts[] (via app.textFonts)\n│   ├── stories[], characterStyles[], paragraphStyles[]\n│   ├── variables[], datasets[]\n│   └── inkList[], printOptions\n├── preferences\n├── printerList[]\n└── textFonts[]\n```\n\n### Top-Level Objects\n\n- **Application** (`app`): The root object. Provides access to documents, preferences, fonts, and printers. Key properties: `activeDocument`, `documents`, `textFonts`, `printerList`, `userInteractionLevel`, `version`.\n- **Document**: Represents an open `.ai` file. Key properties: `layers`, `pageItems`, `selection`, `activeLayer`, `width`, `height`, `rulerOrigin`, `documentColorSpace`. Key methods: `saveAs()`, `exportFile()`, `close()`, `print()`.\n- **Layer**: A drawing layer. Key properties: `pageItems`, `pathItems`, `textFrames`, `visible`, `locked`, `opacity`, `name`, `zOrderPosition`, `color`.\n\n## Measurement Units and Coordinates\n\n### Units\n\nAll scripting API values use **points** (72 points = 1 inch). Convert other units:\n\n| Unit | Conversion |\n|---|---|\n| Inches | multiply by 72 |\n| Centimeters | multiply by 28.346 |\n| Millimeters | multiply by 2.834645 |\n| Picas | multiply by 12 |\n\nKerning, tracking, and `aki` properties use **em units** (thousandths of an em, proportional to font size).\n\n### Coordinate System\n\n- For **scripted documents**, the origin `(0,0)` is at the **bottom-left** of the artboard\n- X increases left to right; Y increases bottom to top\n- The `position` property of a page item is the **top-left corner** of its bounding box as `[x, y]`\n- Maximum page item width\u002Fheight: 16348 points\n\n### Art Item Bounds\n\nEvery page item has three bounding rectangles:\n\n- `geometricBounds`: Excludes stroke width `[left, top, right, bottom]`\n- `visibleBounds`: Includes stroke width\n- `controlBounds`: Includes control\u002Fdirection points\n\n## Working with Documents\n\n### Creating and Opening\n\n```javascript\n\u002F\u002F Create a new document\nvar doc = app.documents.add();\n\n\u002F\u002F Create with a preset\nvar preset = new DocumentPreset();\npreset.width = 612;  \u002F\u002F 8.5 inches\npreset.height = 792; \u002F\u002F 11 inches\npreset.colorMode = DocumentColorSpace.CMYK;\nvar doc = app.documents.addDocument(\"Print\", preset);\n\n\u002F\u002F Open an existing file\nvar fileRef = new File(\"\u002Fpath\u002Fto\u002Ffile.ai\");\nvar doc = app.open(fileRef);\n```\n\n### Saving and Exporting\n\n```javascript\n\u002F\u002F Save as Illustrator format\nvar saveOpts = new IllustratorSaveOptions();\nsaveOpts.compatibility = Compatibility.ILLUSTRATOR17; \u002F\u002F CC\ndoc.saveAs(new File(\"\u002Fpath\u002Fto\u002Foutput.ai\"), saveOpts);\n\n\u002F\u002F Export as PDF\nvar pdfOpts = new PDFSaveOptions();\npdfOpts.compatibility = PDFCompatibility.ACROBAT7;\npdfOpts.preserveEditability = false;\ndoc.saveAs(new File(\"\u002Fpath\u002Fto\u002Foutput.pdf\"), pdfOpts);\n\n\u002F\u002F Export as PNG\nvar pngOpts = new ExportOptionsPNG24();\npngOpts.horizontalScale = 300;\npngOpts.verticalScale = 300;\npngOpts.transparency = true;\ndoc.exportFile(new File(\"\u002Fpath\u002Fto\u002Foutput.png\"), ExportType.PNG24, pngOpts);\n\n\u002F\u002F Export as SVG\nvar svgOpts = new ExportOptionsSVG();\nsvgOpts.fontType = SVGFontType.OUTLINEFONT;\ndoc.exportFile(new File(\"\u002Fpath\u002Fto\u002Foutput.svg\"), ExportType.SVG, svgOpts);\n```\n\n## Working with Paths and Shapes\n\n### Built-in Shape Methods\n\nThe `pathItems` collection provides convenience methods for common shapes:\n\n```javascript\nvar doc = app.activeDocument;\nvar layer = doc.activeLayer;\n\n\u002F\u002F Rectangle: rectangle(top, left, width, height)\nvar rect = layer.pathItems.rectangle(500, 100, 200, 150);\n\n\u002F\u002F Rounded rectangle: roundedRectangle(top, left, width, height, hRadius, vRadius)\nvar rrect = layer.pathItems.roundedRectangle(500, 100, 200, 150, 20, 20);\n\n\u002F\u002F Ellipse: ellipse(top, left, width, height)\nvar oval = layer.pathItems.ellipse(400, 200, 100, 100);\n\n\u002F\u002F Polygon: polygon(centerX, centerY, radius, sides)\nvar hex = layer.pathItems.polygon(300, 300, 50, 6);\n\n\u002F\u002F Star: star(centerX, centerY, radius, innerRadius, points)\nvar star = layer.pathItems.star(300, 300, 50, 25, 5);\n```\n\n### Freeform Paths Using Coordinate Arrays\n\n```javascript\nvar doc = app.activeDocument;\nvar path = doc.pathItems.add();\npath.setEntirePath([[100, 100], [200, 200], [300, 100]]);\npath.closed = false;\npath.stroked = true;\npath.strokeWidth = 2;\n```\n\n### Freeform Paths Using PathPoint Objects\n\n```javascript\nvar doc = app.activeDocument;\nvar path = doc.pathItems.add();\n\nvar point1 = path.pathPoints.add();\npoint1.anchor = [100, 100];\npoint1.leftDirection = [100, 100];\npoint1.rightDirection = [150, 150];\npoint1.pointType = PointType.SMOOTH;\n\nvar point2 = path.pathPoints.add();\npoint2.anchor = [300, 100];\npoint2.leftDirection = [250, 150];\npoint2.rightDirection = [300, 100];\npoint2.pointType = PointType.SMOOTH;\n\npath.closed = false;\n```\n\n### Path Properties\n\n```javascript\nvar item = doc.pathItems[0];\nitem.filled = true;\nitem.stroked = true;\nitem.strokeWidth = 1.5;\nitem.strokeCap = StrokeCap.ROUNDENDCAP;\nitem.strokeJoin = StrokeJoin.ROUNDENDJOIN;\nitem.opacity = 80;\nitem.closed = true;\n```\n\n## Working with Colors\n\n### Color Objects\n\n```javascript\n\u002F\u002F RGB Color (values 0-255)\nvar red = new RGBColor();\nred.red = 255;\nred.green = 0;\nred.blue = 0;\n\n\u002F\u002F CMYK Color (values 0-100)\nvar cyan = new CMYKColor();\ncyan.cyan = 100;\ncyan.magenta = 0;\ncyan.yellow = 0;\ncyan.black = 0;\n\n\u002F\u002F Grayscale (0-100, 0 = black)\nvar gray = new GrayColor();\ngray.gray = 50;\n\n\u002F\u002F Lab Color\nvar lab = new LabColor();\nlab.l = 50;\nlab.a = 20;\nlab.b = -30;\n\n\u002F\u002F No color (transparent)\nvar none = new NoColor();\n```\n\n### Applying Colors\n\n```javascript\nvar item = doc.pathItems[0];\nitem.fillColor = red;\nitem.strokeColor = cyan;\n\n\u002F\u002F Gradient fill\nvar gradient = doc.gradients.add();\ngradient.type = GradientType.LINEAR;\ngradient.gradientStops[0].color = red;\ngradient.gradientStops[1].color = cyan;\n\nvar gradColor = new GradientColor();\ngradColor.gradient = gradient;\nitem.fillColor = gradColor;\n```\n\n### Spot Colors and Swatches\n\n```javascript\n\u002F\u002F Create a spot color\nvar spot = doc.spots.add();\nspot.name = \"My Spot Color\";\nspot.color = red; \u002F\u002F Base color definition\n\nvar spotColor = new SpotColor();\nspotColor.spot = spot;\nspotColor.tint = 100;\n\nitem.fillColor = spotColor;\n\n\u002F\u002F Access a swatch by name\nvar swatch = doc.swatches.getByName(\"PANTONE 185 C\");\nitem.fillColor = swatch.color;\n```\n\n## Working with Text\n\n### Text Frame Types\n\n```javascript\nvar doc = app.activeDocument;\n\n\u002F\u002F Point text\nvar pointText = doc.textFrames.add();\npointText.contents = \"Hello World!\";\npointText.position = [100, 500];\n\n\u002F\u002F Area text (text inside a path)\nvar rectPath = doc.pathItems.rectangle(500, 100, 200, 100);\nvar areaText = doc.textFrames.areaText(rectPath);\nareaText.contents = \"Text inside a rectangle shape.\";\n\n\u002F\u002F Path text (text along a path)\nvar curvePath = doc.pathItems.add();\ncurvePath.setEntirePath([[50, 300], [150, 400], [250, 300]]);\nvar pathText = doc.textFrames.pathText(curvePath);\npathText.contents = \"Text on a path\";\n```\n\n### Character and Paragraph Formatting\n\n```javascript\nvar tf = doc.textFrames[0];\nvar textRange = tf.textRange;\n\n\u002F\u002F Character attributes\nvar charAttr = textRange.characterAttributes;\ncharAttr.size = 24;           \u002F\u002F Font size in points\ncharAttr.textFont = app.textFonts.getByName(\"ArialMT\");\ncharAttr.fillColor = red;\ncharAttr.tracking = 50;       \u002F\u002F Em units\ncharAttr.horizontalScale = 100;\ncharAttr.verticalScale = 100;\ncharAttr.baselineShift = 0;\n\n\u002F\u002F Paragraph attributes\nvar paraAttr = textRange.paragraphAttributes;\nparaAttr.justification = Justification.CENTER;\nparaAttr.firstLineIndent = 0;\nparaAttr.leftIndent = 0;\nparaAttr.spaceBefore = 0;\nparaAttr.spaceAfter = 0;\n```\n\n### Accessing Text Content\n\n```javascript\nvar tf = doc.textFrames[0];\n\n\u002F\u002F Access sub-ranges\nvar firstChar = tf.characters[0];\nvar firstWord = tf.words[0];\nvar firstPara = tf.paragraphs[0];\nvar firstLine = tf.lines[0];\n\n\u002F\u002F Modify specific ranges\ntf.words[0].characterAttributes.size = 36;\ntf.paragraphs[0].paragraphAttributes.justification = Justification.LEFT;\n```\n\n### Threading Text Frames\n\n```javascript\nvar frame1 = doc.textFrames.areaText(path1);\nvar frame2 = doc.textFrames.areaText(path2);\n\n\u002F\u002F Link frames so text flows from frame1 to frame2\nframe1.nextFrame = frame2;\n\n\u002F\u002F Stories represent the full text across threaded frames\nvar storyCount = doc.stories.length;\nvar fullText = doc.stories[0].textRange.contents;\n```\n\n## Working with Layers\n\n```javascript\nvar doc = app.activeDocument;\n\n\u002F\u002F Create a layer\nvar newLayer = doc.layers.add();\nnewLayer.name = \"Background\";\nnewLayer.visible = true;\nnewLayer.locked = false;\nnewLayer.opacity = 100;\n\n\u002F\u002F Access existing layers\nvar topLayer = doc.layers[0];\nvar layerByName = doc.layers.getByName(\"Background\");\n\n\u002F\u002F Move items between layers\nvar item = doc.pathItems[0];\nitem.move(newLayer, ElementPlacement.PLACEATBEGINNING);\n\n\u002F\u002F Reorder layers\nnewLayer.zOrder(ZOrderMethod.SENDTOBACK);\n```\n\n## Working with Selections\n\n```javascript\n\u002F\u002F Get current selection\nvar sel = app.activeDocument.selection;\n\n\u002F\u002F Iterate selected items\nfor (var i = 0; i \u003C sel.length; i++) {\n    var item = sel[i];\n    \u002F\u002F Check type using typename\n    if (item.typename === \"PathItem\") {\n        item.fillColor = red;\n    } else if (item.typename === \"TextFrame\") {\n        item.contents = \"Modified\";\n    }\n}\n\n\u002F\u002F Select an item programmatically\ndoc.pathItems[0].selected = true;\n\n\u002F\u002F Deselect all\ndoc.selection = null;\n```\n\n## Working with Symbols\n\n```javascript\n\u002F\u002F Place a symbol instance\nvar sym = doc.symbols.getByName(\"MySymbol\");\nvar instance = doc.symbolItems.add(sym);\ninstance.position = [200, 400];\n\n\u002F\u002F Access symbol definition\nvar symDef = instance.symbol;\n\n\u002F\u002F Break link to symbol (expand to regular art)\ninstance.breakLink();\n```\n\n## Transformations\n\n```javascript\nvar item = doc.pathItems[0];\n\n\u002F\u002F Rotate 45 degrees around center\nitem.rotate(45);\n\n\u002F\u002F Scale to 50% width, 75% height\nitem.resize(50, 75);\n\n\u002F\u002F Translate (move) by 100 points right and 50 points up\nitem.translate(100, 50);\n\n\u002F\u002F Using a transformation matrix\nvar matrix = app.getIdentityMatrix();\nmatrix = app.concatenateRotationMatrix(matrix, 30);\nmatrix = app.concatenateScaleMatrix(matrix, 150, 150);\nitem.transform(matrix);\n```\n\n## Working with Artboards\n\n```javascript\nvar doc = app.activeDocument;\n\n\u002F\u002F Access artboards\nvar ab = doc.artboards[0];\nvar rect = ab.artboardRect; \u002F\u002F [left, top, right, bottom]\n\n\u002F\u002F Create a new artboard\nvar newAB = doc.artboards.add([0, 0, 612, 792]); \u002F\u002F Letter size\nnewAB.name = \"Page 2\";\n\n\u002F\u002F Set active artboard\ndoc.artboards.setActiveArtboardIndex(1);\n```\n\n## Data-Driven Graphics (Variables and Datasets)\n\n```javascript\n\u002F\u002F Variables link document items to data fields\nvar v = doc.variables.add();\nv.kind = VariableKind.TEXTUAL;\nv.name = \"headline\";\n\n\u002F\u002F Link a text frame to the variable\nvar tf = doc.textFrames[0];\ntf.contentVariable = v;\n\n\u002F\u002F Create datasets for batch content\nvar ds = doc.dataSets.add();\nds.name = \"Version 1\";\n\u002F\u002F Dataset captures current variable bindings\n\n\u002F\u002F Switch datasets to swap content\ndoc.dataSets[0].display();\n```\n\n## Printing\n\n```javascript\nvar doc = app.activeDocument;\nvar opts = new PrintOptions();\n\nopts.printPreset = \"Default\";\n\n\u002F\u002F Paper options\nvar paperOpts = new PrintPaperOptions();\npaperOpts.name = \"Letter\";\nopts.paperOptions = paperOpts;\n\n\u002F\u002F Job options\nvar jobOpts = new PrintJobOptions();\njobOpts.copies = 1;\njobOpts.designation = PrintArtworkDesignation.VISIBLELAYERS;\nopts.jobOptions = jobOpts;\n\ndoc.print(opts);\n```\n\n## User Interaction Levels\n\nControl whether Illustrator shows dialogs during script execution:\n\n```javascript\n\u002F\u002F Suppress all dialogs\napp.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;\n\n\u002F\u002F Perform operations that might prompt dialogs...\ndoc.close(SaveOptions.DONOTSAVECHANGES);\n\n\u002F\u002F Restore dialog display\napp.userInteractionLevel = UserInteractionLevel.DISPLAYALERTS;\n```\n\n## Working with Methods (JavaScript-Specific)\n\nWhen calling methods with multiple optional parameters, use `undefined` to skip middle parameters:\n\n```javascript\n\u002F\u002F rotate(angle, [changePositions], [changeFillPatterns], [changeFillGradients], ...)\nitem.rotate(30, undefined, undefined, true);\n```\n\n## External Invocation & Argument Passing\n\nIllustrator scripts are routinely launched from outside the application —\nshell scripts, schedulers, build pipelines, ExtendScript Toolkit, or\n`BridgeTalk` messages from other Creative Cloud apps. The execution\nenvironment under those launchers differs from the in-application *File >\nScripts* path in several ways that frequently break otherwise-correct code.\n\n### `arguments[]` Is Unreliable Under External Launchers\n\nExtendScript Toolkit's `-run` invocation and `BridgeTalk.send()` do not\nforward arbitrary launcher arguments into the script's top-level\n`arguments[]` array. In many configurations the array contains a single\n`[object BridgeTalk]` element instead of the values the caller passed, as\ndemonstrated below:\n\n```javascript\n\u002F\u002F At top of script\nvar passed = (typeof arguments !== \"undefined\") ? arguments : [];\nfor (var i = 0; i \u003C passed.length; i++) {\n    $.writeln(\"arg[\" + i + \"] = \" + passed[i]);\n    \u002F\u002F Often prints: arg[0] = [object BridgeTalk]\n}\n```\n\n**Do not rely on `arguments[]` for required inputs when the script is\nlaunched externally.** Use one of the following more reliable channels.\n\n### Sidecar File for Parameters\n\nWhen a script fails under an external launcher and the source of the error\nis not obvious, fall back to a sidecar file: have the caller write a small\ntext file at a known absolute path, and read it on startup. This works\nregardless of launcher quirks and is easy to inspect after a failed run.\n\n```javascript\nvar SIDECAR_PATH = \"C:\u002FUsers\u002FuserName\u002Fjob.args.txt\";\n\nfunction readSidecar(path) {\n    var f = new File(path);\n    if (!f.exists || !f.open(\"r\")) return null;\n    var lines = [];\n    while (!f.eof) {\n        var ln = f.readln();\n        if (ln && !\u002F^\\s*$\u002F.test(ln)) lines.push(ln);\n    }\n    f.close();\n    return {\n        input:  lines[0],\n        output: lines[1],\n        mode:   lines[2]\n    };\n}\n```\n\nA `key=value` format is equally workable and avoids positional fragility:\n\n```text\ninput=C:\u002Fpath\u002Fto\u002Finput.ai\noutput=C:\u002Fpath\u002Fto\u002Foutput.pdf\nmode=preview\n```\n\n### Environment Variables\n\n`$.getenv(\"NAME\")` returns environment variables visible to **Illustrator's\nprocess**, not the launcher's. If the launcher needs Illustrator to see a\nvalue, it must set the variable system-wide or in Illustrator's parent\nenvironment before launching. For per-invocation values, prefer a sidecar\nfile.\n\n### `$.fileName` and `File($.fileName).parent`\n\nUnder in-application execution, `$.fileName` is the absolute path of the\nrunning script and `File($.fileName).parent` yields the script's folder.\nUnder some external launchers (notably ESTK `-run`) `$.fileName` can be\nempty, causing relative path resolution to silently fail.\n\n```javascript\n\u002F\u002F Fragile: returns null under some launchers\nvar here = $.fileName ? File($.fileName).parent : null;\nvar sidecar = here ? new File(here.fsName + \"\u002Fjob.args.txt\") : null;\n\n\u002F\u002F Robust: hardcode a known absolute path or fall back to a stable location\nvar sidecar = new File(\"C:\u002FUsers\u002FuserName\u002Fjob.args.txt\");\nif (!sidecar.exists) sidecar = new File(Folder.temp.fsName + \"\u002Fjob.args.txt\");\n```\n\n### Diagnostic Logging to an Absolute Path\n\nSilent failures are common because dialogs are suppressed and the launcher\nmay not surface `$.writeln` output. Write a plain-text log to a known\nabsolute path so a run can be inspected after the fact. Create the parent\nfolder on demand so the first call cannot fail for a missing directory.\n\n```javascript\nvar LOG_PATH = \"C:\u002FUsers\u002FuserName\u002Flogs\u002Fjob.log\";\n\nfunction log(msg) {\n    try {\n        var f = new File(LOG_PATH);\n        try { if (!f.parent.exists) f.parent.create(); } catch (eDir) {}\n        if (f.open(\"a\")) {\n            f.writeln(\"[\" + new Date() + \"] \" + msg);\n            f.close();\n        }\n    } catch (e) {}\n}\n```\n\n### Wrap the Entry Point in `try { ... } catch`\n\nExternally launched scripts often fail without any visible indication. A\ntop-level `try`\u002F`catch` that writes the error to the log file converts\nsilent failures into a single inspectable line.\n\n```javascript\ntry {\n    main();\n} catch (err) {\n    log(\"FATAL: \" + err + (err && err.line ? \" line=\" + err.line : \"\"));\n}\n```\n\n### Suppress User Interaction\n\nExternal callers cannot answer dialogs. Disable them before any DOM work\nand avoid `alert()` \u002F `confirm()` \u002F `prompt()` entirely in scripts that may\nbe launched headlessly.\n\n```javascript\napp.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;\n```\n\n### Save Explicitly\n\nClosing or letting Illustrator return to its idle state does not save the\nworking file. After all DOM edits, call `doc.saveAs(...)` (or `doc.save()`)\nexplicitly and log whether it succeeded.\n\n```javascript\nvar opts = new IllustratorSaveOptions();\nopts.compatibility = Compatibility.ILLUSTRATOR17;\ndoc.saveAs(new File(doc.fullName.fsName), opts);\n```\n\n## Common Patterns\n\n### Iterate All Page Items in a Document\n\n```javascript\nfunction processAllItems(doc) {\n    for (var i = 0; i \u003C doc.pageItems.length; i++) {\n        var item = doc.pageItems[i];\n        \u002F\u002F Process based on type\n        switch (item.typename) {\n            case \"PathItem\":\n                \u002F\u002F handle path\n                break;\n            case \"TextFrame\":\n                \u002F\u002F handle text\n                break;\n            case \"GroupItem\":\n                \u002F\u002F handle group (may contain nested items)\n                break;\n        }\n    }\n}\n```\n\n### Recursively Unlock Layers and Groups Before Editing\n\nA locked layer or any locked ancestor (parent group, clip group, sublayer)\nwill cause edits to throw `Error: Target layer cannot be modified`. Walk the\nfull hierarchy and clear `locked` \u002F `hidden` flags before performing DOM\nmodifications.\n\n```javascript\nfunction unlockAll(doc) {\n    function visitLayers(layers) {\n        for (var i = 0; i \u003C layers.length; i++) {\n            var lyr = layers[i];\n            try { lyr.locked = false; lyr.visible = true; } catch (e) {}\n            visitItems(lyr);\n            if (lyr.layers && lyr.layers.length) visitLayers(lyr.layers);\n        }\n    }\n    function visitItems(container) {\n        var items = container.pageItems;\n        for (var j = 0; j \u003C items.length; j++) {\n            var it = items[j];\n            try { it.locked = false; it.hidden = false; } catch (e) {}\n            if (it.typename === \"GroupItem\") visitItems(it);\n        }\n    }\n    visitLayers(doc.layers);\n}\n```\n\n### Replacing the File Behind a Linked Image (Relink)\n\n`PlacedItem.file = newFile` replaces a linked image while preserving the\nparent, stacking order, and (after re-applying) the bounds. **`RasterItem`\ndoes not expose a writable `file` property**, so when a placeholder is a\nraster you must add a fresh `PlacedItem` in the same parent, copy the bounds,\nthen remove the original.\n\n```javascript\nfunction relinkOrRebuild(item, newFile) {\n    var bounds = item.geometricBounds.slice();\n    var parent = item.parent;\n    var name   = item.name;\n\n    if (item.typename === \"PlacedItem\") {\n        item.file = newFile;\n        item.geometricBounds = bounds;\n        return item;\n    }\n\n    \u002F\u002F RasterItem path: rebuild as a linked PlacedItem in the same parent.\n    var fresh = parent.placedItems.add();\n    fresh.file = newFile;\n    fresh.geometricBounds = bounds;\n    if (name) try { fresh.name = name; } catch (e) {}\n    fresh.move(item, ElementPlacement.PLACEBEFORE);\n    item.remove();\n    return fresh;\n}\n```\n\n### Placing SVG Content (Copy\u002FPaste Pattern)\n\n`PlacedItem.file` accepts raster formats and AI\u002FPDF, **but not SVG**. Setting\nit to an `.svg` File throws `Unable to set placed item's file, is the file\npath provided valid?`. The reliable way to bring SVG artwork into a document\nis to open the SVG as a separate document, select all, copy, close, and paste\ninto the working document.\n\n```javascript\nfunction placeSVG(targetDoc, svgFile, targetLayer) {\n    var donor = app.open(svgFile);\n    app.executeMenuCommand(\"selectall\");\n    app.executeMenuCommand(\"copy\");\n    donor.close(SaveOptions.DONOTSAVECHANGES);\n\n    app.activeDocument = targetDoc;\n    targetDoc.activeLayer = targetLayer;\n    app.executeMenuCommand(\"pasteFront\");\n\n    var sel = targetDoc.selection;\n    if (!sel || sel.length === 0) return null;\n    if (sel.length === 1) return sel[0];\n\n    \u002F\u002F Multiple pasted items: group them so callers get a single handle.\n    var group = targetLayer.groupItems.add();\n    for (var i = sel.length - 1; i >= 0; i--) {\n        sel[i].move(group, ElementPlacement.PLACEATBEGINNING);\n    }\n    return group;\n}\n```\n\n### Finding a Clipping Path Inside a Mask Group\n\nClip groups expose their clipping shape as a child `PathItem` (or, less\ncommonly, a child of a `CompoundPathItem`) with `clipping === true`. The\nclip's `geometricBounds` give the visible frame to size or center content\nagainst.\n\n```javascript\nfunction findClipPath(group) {\n    var items = group.pageItems;\n    for (var i = 0; i \u003C items.length; i++) {\n        var it = items[i];\n        try {\n            if (it.typename === \"PathItem\" && it.clipping) return it;\n            if (it.typename === \"CompoundPathItem\") {\n                for (var j = 0; j \u003C it.pathItems.length; j++) {\n                    if (it.pathItems[j].clipping) return it;\n                }\n            }\n        } catch (e) {}\n    }\n    return null;\n}\n```\n\n### Cover-Fit and Contain-Fit Sizing\n\nTo make an image fully cover a rectangle (any overflow hidden by a mask), use\nthe larger of the width\u002Fheight ratios. To make it fit entirely inside, use\nthe smaller. A bleed factor (e.g. `1.10`) lets a cover image extend slightly\npast the clip edge.\n\n```javascript\nfunction fitItemToRect(item, rect, mode, bleed) {\n    \u002F\u002F rect = [L, T, R, B] (Illustrator: T > B)\n    var rw = rect[2] - rect[0];\n    var rh = rect[1] - rect[3];\n    var ib = item.geometricBounds;\n    var iw = ib[2] - ib[0];\n    var ih = ib[1] - ib[3];\n    if (iw \u003C= 0 || ih \u003C= 0) return;\n\n    var sx = rw \u002F iw;\n    var sy = rh \u002F ih;\n    var s  = (mode === \"cover\" ? Math.max(sx, sy) : Math.min(sx, sy))\n           * (bleed || 1);\n    item.resize(s * 100, s * 100);\n\n    var cx = (rect[0] + rect[2]) \u002F 2;\n    var cy = (rect[1] + rect[3]) \u002F 2;\n    var b  = item.geometricBounds;\n    var w  = b[2] - b[0];\n    var h  = b[1] - b[3];\n    item.position = [cx - w \u002F 2, cy + h \u002F 2];\n}\n```\n\n\n\n### Batch Process Files in a Folder\n\n```javascript\nvar folder = Folder.selectDialog(\"Select folder of .ai files\");\nif (folder) {\n    var files = folder.getFiles(\"*.ai\");\n    for (var i = 0; i \u003C files.length; i++) {\n        var doc = app.open(files[i]);\n        \u002F\u002F Process each document...\n        doc.close(SaveOptions.DONOTSAVECHANGES);\n    }\n}\n```\n\n### Error Handling\n\n```javascript\ntry {\n    var doc = app.activeDocument;\n    var layer = doc.layers.getByName(\"NonExistentLayer\");\n} catch (e) {\n    alert(\"Error: \" + e.message);\n    \u002F\u002F e.message, e.line, e.fileName available\n}\n```\n\n## Troubleshooting\n\n- **\"undefined is not an object\"**: Usually means the collection is empty or the index is out of bounds. Check `.length` before accessing items.\n- **Script runs but nothing changes visually**: Call `app.redraw()` to force a screen refresh after modifications.\n- **Color mode mismatch**: Document color space (RGB vs CMYK) must match color objects. Use `doc.documentColorSpace` to check.\n- **Position seems wrong**: Remember scripted documents use bottom-left origin with Y increasing upward. The `position` property is the top-left of the bounding box.\n- **Text not appearing**: Ensure the text frame has a non-zero size. For point text, set `position`; for area text, provide a valid path to `areaText()`.\n- **File paths on Windows**: Use forward slashes (`\u002F`) or double backslashes (`\\\\`) in path strings, or use the `File` object constructor.\n- **Dialog boxes interrupting batch scripts**: Set `app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS` before batch operations.\n- **Collections use `getByName()`**: Many collection objects support `getByName(\"name\")` which throws an error if not found; wrap in try\u002Fcatch.\n- **\"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).\n- **\"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 \u002F copy \u002F paste pattern](#placing-svg-content-copypaste-pattern) instead.\n- **`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.\n- **`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).\n- **`$.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`.\n- **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`\u002F`catch` that logs to an absolute path to confirm execution and capture the error.\n- **`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.\n\n## Scripting Constants Reference\n\nCommon enumeration constants used across the API:\n\n| Category | Constants |\n|---|---|\n| **Color Space** | `DocumentColorSpace.RGB`, `DocumentColorSpace.CMYK` |\n| **Justification** | `Justification.LEFT`, `Justification.CENTER`, `Justification.RIGHT`, `Justification.FULLJUSTIFY` |\n| **Point Type** | `PointType.SMOOTH`, `PointType.CORNER` |\n| **Stroke Cap** | `StrokeCap.BUTTENDCAP`, `StrokeCap.ROUNDENDCAP`, `StrokeCap.PROJECTINGENDCAP` |\n| **Stroke Join** | `StrokeJoin.MITERENDJOIN`, `StrokeJoin.ROUNDENDJOIN`, `StrokeJoin.BEVELENDJOIN` |\n| **Blend Mode** | `BlendModes.NORMAL`, `BlendModes.MULTIPLY`, `BlendModes.SCREEN`, `BlendModes.OVERLAY` |\n| **Save Options** | `SaveOptions.SAVECHANGES`, `SaveOptions.DONOTSAVECHANGES`, `SaveOptions.PROMPTTOSAVECHANGES` |\n| **Export Type** | `ExportType.PNG24`, `ExportType.PNG8`, `ExportType.JPEG`, `ExportType.SVG`, `ExportType.TIFF`, `ExportType.PHOTOSHOP`, `ExportType.AUTOCAD`, `ExportType.FLASH` |\n| **Element Placement** | `ElementPlacement.PLACEATBEGINNING`, `ElementPlacement.PLACEATEND`, `ElementPlacement.PLACEBEFORE`, `ElementPlacement.PLACEAFTER`, `ElementPlacement.INSIDE` |\n| **Z-Order** | `ZOrderMethod.BRINGTOFRONT`, `ZOrderMethod.SENDTOBACK`, `ZOrderMethod.BRINGFORWARD`, `ZOrderMethod.SENDBACKWARD` |\n| **Gradient Type** | `GradientType.LINEAR`, `GradientType.RADIAL` |\n| **Text Frame Kind** | `TextType.POINTTEXT`, `TextType.AREATEXT`, `TextType.PATHTEXT` |\n| **Variable Kind** | `VariableKind.TEXTUAL`, `VariableKind.IMAGE`, `VariableKind.VISIBILITY`, `VariableKind.GRAPH` |\n| **User Interaction** | `UserInteractionLevel.DISPLAYALERTS`, `UserInteractionLevel.DONTDISPLAYALERTS` |\n| **Compatibility** | `Compatibility.ILLUSTRATOR10` through `Compatibility.ILLUSTRATOR24` |\n\n## JavaScript Object Reference (Complete API Object List)\n\nThe Illustrator JavaScript API contains the following objects, grouped by category:\n\n### Core Objects\n\n`Application`, `Document`, `Documents`, `DocumentPreset`, `Layer`, `Layers`, `PageItem`, `PageItems`, `View`, `Views`, `Preferences`\n\n### Path and Shape Objects\n\n`PathItem`, `PathItems`, `PathPoint`, `PathPoints`, `CompoundPathItem`, `CompoundPathItems`, `GroupItem`, `GroupItems`\n\n### Text Objects\n\n`TextFrame`, `TextRange`, `TextRanges`, `TextPath`, `Characters`, `Words`, `Paragraphs`, `Lines`, `InsertionPoint`, `InsertionPoints`, `Story`, `Stories`, `CharacterAttributes`, `ParagraphAttributes`, `CharacterStyle`, `CharacterStyles`, `ParagraphStyle`, `ParagraphStyles`, `TextFont`, `TextFonts`, `TabStopInfo`\n\n### Color Objects\n\n`RGBColor`, `CMYKColor`, `GrayColor`, `LabColor`, `NoColor`, `SpotColor`, `Spot`, `Spots`, `PatternColor`, `GradientColor`, `Color`, `Gradient`, `Gradients`, `GradientStop`, `GradientStops`\n\n### Swatch and Style Objects\n\n`Swatch`, `Swatches`, `SwatchGroup`, `SwatchGroups`, `GraphicStyle`, `GraphicStyles`, `Pattern`, `Patterns`, `Brush`, `Brushes`\n\n### Symbol Objects\n\n`Symbol`, `Symbols`, `SymbolItem`, `SymbolItems`\n\n### Artboard Objects\n\n`Artboard`, `Artboards`\n\n### Placed and Raster Objects\n\n`PlacedItem`, `PlacedItems`, `RasterItem`, `RasterItems`, `MeshItem`, `MeshItems`, `GraphItem`, `GraphItems`, `PluginItem`, `PluginItems`, `NonNativeItem`, `NonNativeItems`, `LegacyTextItem`, `LegacyTextItems`\n\n### Data-Driven Objects\n\n`Variable`, `Variables`, `Dataset`, `Datasets`\n\n### Matrix and Transform Objects\n\n`Matrix`\n\n### Tag Objects\n\n`Tag`, `Tags`\n\n### Tracing Objects\n\n`TracingObject`, `TracingOptions`\n\n### Save and Export Options\n\n`IllustratorSaveOptions`, `EPSSaveOptions`, `PDFSaveOptions`, `FXGSaveOptions`, `ExportOptionsAutoCAD`, `ExportOptionsFlash`, `ExportOptionsGIF`, `ExportOptionsJPEG`, `ExportOptionsPhotoshop`, `ExportOptionsPNG8`, `ExportOptionsPNG24`, `ExportOptionsSVG`, `ExportOptionsTIFF`\n\n### Open Options\n\n`OpenOptions`, `OpenOptionsAutoCAD`, `OpenOptionsFreeHand`, `OpenOptionsPhotoshop`, `PDFFileOptions`, `PhotoshopFileOptions`\n\n### Print Objects\n\n`PrintOptions`, `PrintJobOptions`, `PrintPaperOptions`, `PrintColorManagementOptions`, `PrintColorSeparationOptions`, `PrintCoordinateOptions`, `PrintFlattenerOptions`, `PrintFontOptions`, `PrintPageMarksOptions`, `PrintPostScriptOptions`, `Printer`, `PrinterInfo`, `Paper`, `PaperInfo`, `PPDFile`, `PPDFileInfo`, `Ink`, `InkInfo`, `Screen`, `ScreenInfo`, `ScreenSpotFunction`\n\n### Image and Rasterize Options\n\n`ImageCaptureOptions`, `RasterEffectOptions`, `RasterizeOptions`\n\n## References\n\n- [Changelog](https:\u002F\u002Fai-scripting.docsforadobe.dev\u002Fintroduction\u002Fchangelog\u002F) - Recent scripting API changes (CC 2020 added `Document.getPageItemFromUuid` and `PageItem.uuid`; CC 2017 added `Application.getIsFileOpen`)\n- [Illustrator Scripting Guide](https:\u002F\u002Fai-scripting.docsforadobe.dev\u002F) - Full community-maintained documentation\n",{"name":5,"description":7},"main","5269f7ea2d6c3a9bfda5ed878b6726cf509121fd","skills\u002Fadobe-illustrator-scripting",5354,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot","https:\u002F\u002Fawesome-copilot.github.com\u002F","https:\u002F\u002Fgithub.com\u002Fgithub","https:\u002F\u002Fgithub.com\u002Fabout","How people build software.","2026-11-14 05:37:52",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add github\u002Fawesome-copilot --skill adobe-illustrator-scripting",{"tool":69,"command":70},"bunx","bunx skills add github\u002Fawesome-copilot --skill adobe-illustrator-scripting",{"tool":72,"command":73},"pnpm","pnpm dlx skills add github\u002Fawesome-copilot --skill adobe-illustrator-scripting","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fadobe-illustrator-scripting\u002FSKILL.md",true,[77,83,89,93],{"path":78,"resourceType":79,"resourceTypeDesc":80,"sizeBytes":81,"sourceHtmlUrl":82},"references\u002Fobject-model-quick-reference.md","REFERENCE","参考文档",5013,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fadobe-illustrator-scripting\u002Freferences\u002Fobject-model-quick-reference.md",{"path":84,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":87,"sourceHtmlUrl":88},"scripts\u002Fbatch-export-png.jsx","SCRIPT","脚本",1339,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fadobe-illustrator-scripting\u002Fscripts\u002Fbatch-export-png.jsx",{"path":90,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":91,"sourceHtmlUrl":92},"scripts\u002Fcreate-color-grid.jsx",1146,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fadobe-illustrator-scripting\u002Fscripts\u002Fcreate-color-grid.jsx",{"path":94,"resourceType":85,"resourceTypeDesc":86,"sizeBytes":95,"sourceHtmlUrl":96},"scripts\u002Ffind-replace-text.jsx",893,"https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fadobe-illustrator-scripting\u002Fscripts\u002Ffind-replace-text.jsx",[98,127,155,184,218,235],{"skillNo":99,"name":100,"nameZh":6,"description":101,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":102,"categories":103,"keywords":108,"publishedAt":48},"01M70KXZNX9ES38N2A8M58ZZMZ","supabase-rls-policies","Write, review, and test Supabase Row Level Security (RLS) policies so that one user cannot read or change another user's rows. Use when creating or changing tables, policies, views, or security definer functions in a Supabase project, when reviewing a migration that touches RLS, or when asked to prove user isolation with pgTAP tests that sign in as two users.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fsupabase-rls-policies\u002FSKILL.md",[104,107],{"categoryNo":105,"categoryName":106,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[109,110,111,112,113,28,114,115,116,39,117,118,119,120,121,122,123,124,125,126],"supabase","rls","supabase rls","supabase rls policies","project","review","change","read","security","isolation","tables","as","tests","prove","use","users","functions","with",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":134,"publishedAt":48},"01M70KWA5YWXDV6V8DZ19E3T2Y","skill-distribution-audit","Audit a skill collection before release or investigate installer omissions, archived\u002Ftemplate leakage, duplicate identities, and manifest drift. Reconcile the intended public source set, distribution manifest, and actual discovery output for a specified consumer version. Includes a deterministic set-comparison helper.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fskill-distribution-audit\u002FSKILL.md",[133],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154],"distribution","skill distribution audit","audit","SET","deterministic","installer","for","discovery","comparison","helper","manifest","version","collection","drift","output","investigate","duplicate","template","release","reconcile",{"skillNo":156,"name":157,"nameZh":6,"description":158,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":159,"sourceHtmlUrl":160,"categories":161,"keywords":167,"publishedAt":48},"01M70KCJPFVW7PPQ49CC5R8JGK","logicapps-custom-code-net10-migration","Migrate Azure Logic Apps Standard local-function custom-code projects from .NET 8 to .NET 10. Use for legacy Logic Apps custom-code workspace upgrades, project\u002Fpackage configuration, custom-worker settings, publication, and integrated VS Code debugging. Not for ordinary .NET applications, standalone Azure Functions, Consumption workflows, or cloud deployment.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Flogicapps-custom-code-net10-migration\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Flogicapps-custom-code-net10-migration\u002FSKILL.md",[162],{"categoryNo":163,"categoryName":164,"taxonomyType":165,"taxonomyTypeDesc":166},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[168,169,170,171,172,173,141,113,174,175,176,177,178,179,180,123,181,182,183,46],"custom","code","logicapps custom code net10 migration","migration","projects","standalone","deployment","applications","local","azure","worker","upgrades","package","apps","Legacy","debugging",{"skillNo":185,"name":186,"nameZh":6,"description":187,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":188,"sourceHtmlUrl":189,"categories":190,"keywords":195,"publishedAt":48},"01M6AEBN37RQMXJ3298PEY7PG1","winmd-api-search","Find and explore Windows desktop APIs. Use when building features that need platform capabilities — camera, file access, notifications, UI controls, AI\u002FML, sensors, networking, etc. Discovers the right API for a task and retrieves full type details (methods, properties, events, enumeration values).","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fwinmd-api-search\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fwinmd-api-search\u002FSKILL.md",[191,194],{"categoryNo":192,"categoryName":193,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":163,"categoryName":164,"taxonomyType":165,"taxonomyTypeDesc":166},[196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,28,211,212,213,214,215,141,216,217],"winmd api search","search","api","windows","desktop","file access","enumeration","ui","ml","file","networking","capabilities","notifications","explore","access","features","full","camera","events","platform","methods","controls",{"skillNo":219,"name":220,"nameZh":6,"description":221,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":222,"licenseFileUrl":223,"sourceHtmlUrl":224,"categories":225,"keywords":231,"publishedAt":48},"01M6AE571R3SC1ZGE202B3NB5S","resemble-detect","Deepfake detection and media safety — detect AI-generated audio, images, video, and text, trace synthesis sources, apply watermarks, verify speaker identity, and analyze media intelligence using Resemble AI","Apache-2.0","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fresemble-detect\u002FLICENSE","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fresemble-detect\u002FSKILL.md",[226,227,230],{"categoryNo":192,"categoryName":193,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":228,"categoryName":229,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000016","图像与多媒体",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[232,138,205,233,234],"resemble detect","https","identity",{"skillNo":236,"name":237,"nameZh":6,"description":238,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":222,"licenseFileUrl":239,"sourceHtmlUrl":240,"categories":241,"keywords":246,"publishedAt":48},"01M6AE30BSK2HCY4TQQ22Z8JRD","quality-playbook","Run a complete quality engineering audit on any codebase. Derives behavioral requirements from the code, generates spec-traced functional tests, runs a three-pass code review with regression tests, executes a multi-model spec audit (Council of Three), and produces a consolidated bug report with TDD-verified patches. Finds the 35% of real defects that structural code review alone cannot catch. Works with any language. Trigger on 'quality playbook', 'spec audit', 'Council of Three', 'fitness-to-purpose', or 'coverage theater'.","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fquality-playbook\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002Fgithub\u002Fawesome-copilot\u002Fblob\u002Fmain\u002Fskills\u002Fquality-playbook\u002FSKILL.md",[242,245],{"categoryNo":243,"categoryName":244,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000007","代码审查",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[247,248,249,250,169,114,9,137,251,252,233,253,254,255,256,257,258,259,260,261,262,263,121,264,40,265,266],"playbook","quality playbook","quality","code review","tdd","run","report","spec","bug report","council","engineering","functional","spec audit","bug","verified","multi-model","three","coverage","regression","structural"]