1. 这不是“加个页码”那么简单一个 Illustrator 插件背后的真实战场你是不是也遇到过这样的场景客户发来一份 32 页的画册源文件要求每一页右下角加统一格式的页码比如“第 01 页 / 共 32 页”字体字号位置必须严丝合缝或者设计团队正在做一套 VI 手册几十个画板需要按章节自动编号但 AI 原生的“页面编号”功能只认文档页数根本不识别画板逻辑又或者你导出 PDF 时发现明明设置了“从第 2 页开始编号”结果所有画板的页码全乱套了——AI 把每个画板都当成独立页面根本没理解你脑子里的“逻辑页”概念。这就是标题里“illustrator插件-画板功能开发-插入页码-js脚本开发-ai插件”真正要解决的问题它不是在给 Illustrator 装一个“页码按钮”而是在重建 Illustrator 的页面认知体系。Adobe 官方的画板Artboard本质是“输出容器”不是“内容逻辑单元”。它不自带页码属性、不支持跨画板连续计数、不提供页码样式模板引擎——这些恰恰是出版、印刷、UI 设计交付中最刚性的需求。所以这个插件的核心价值从来不是“用 JS 写了个脚本”而是用 JS 在 Illustrator 的 DOM 层之上硬生生搭起了一套独立于原生系统的“画板页码语义层”。我做过 7 年品牌视觉系统交付经手过 200 套画册、手册、PPT 模板项目。每次遇到页码需求第一反应不是打开菜单而是翻出自己压箱底的 JS 插件包。为什么因为官方方案永远卡在三个死结上第一页码只能绑定到“文档页”而画板和文档页是 1:N 关系一个画板可导出多页 PDF第二页码文本是静态对象改一个就得手动拖拽复制粘贴第三没有“章节起始页”、“奇偶页不同”、“罗马数字前言阿拉伯数字正文”这种专业排版逻辑。而这个插件就是把设计师脑子里的排版规则翻译成 Illustrator 能听懂的 JS 指令。它不改变 AI 的底层却让 AI 的工作流真正适配印刷行业的实际节奏。如果你还在用“CtrlC/CtrlV”手动填页码或者靠 Excel 生成文本框再拖进 AI——那不是你在用软件是软件在用你。2. 为什么非得用 JS 脚本而不是 CEP 插件或 Action2.1 Illustrator 的插件生态真相JS 是唯一能“触达画板内核”的语言很多人看到“插件”第一反应是 CEPCommon Extensibility Platform毕竟 Photoshop 和 XD 都在推 CEP 面板。但 Illustrator 的 CEP 有个致命短板它无法直接操作画板Artboard对象的底层属性。CEP 面板本质是个嵌入式浏览器它和 Illustrator 主进程之间靠 JSON 消息通信而 Adobe 提供给 CEP 的 API 列表里关于 Artboard 的操作只有寥寥几项——创建、删除、重命名、获取尺寸。你想给某个画板设置“页码起始值”想读取画板的自定义元数据比如“chapter: brand_guidelines”想批量修改画板上所有文本框的字体颜色——CEP API 根本不暴露这些接口。而 ExtendScript即 Illustrator 的 JS 引擎完全不同。它是 Illustrator 原生的脚本宿主环境直接运行在 AI 主进程中拥有对 Document、Artboard、TextFrame、PageItem 等所有核心对象的完全控制权。你可以像操作网页 DOM 一样遍历画板树// 获取当前文档所有画板 var doc app.activeDocument; var artboards doc.artboards; // 遍历每个画板读取其名称和索引 for (var i 0; i artboards.length; i) { var ab artboards[i]; $.writeln(画板 (i1) : ab.name , 索引 ab.index); }更重要的是ExtendScript 可以直接读写画板的artboardOptions对象这是 CEP 永远触碰不到的私有域。比如我们给画板添加自定义页码配置就存在artboardOptions的扩展属性里// 为画板 0 设置页码起始值和格式 ab.artboardOptions.pageNumberStart 1; ab.artboardOptions.pageNumberFormat 第 {page} 页 / 共 {total} 页;这个能力决定了只有 JS 脚本能实现真正的“画板级页码治理”其他任何插件形态都只是表面功夫。CEP 面板可以做个漂亮 UI但最终执行页码插入的还是得调用底层 JS 脚本。Action动作更不用提——它连“判断当前画板是否属于某章节”这种基础逻辑都做不到纯机械录制毫无灵活性。2.2 为什么不用 VBA 或 AppleScript跨平台兼容性是硬门槛有人会问Illustrator 在 Windows 上支持 VBA在 macOS 上支持 AppleScript为什么不选它们答案很现实VBA 在 CC 版本后已被 Adobe 官方弃用AppleScript 的 API 覆盖率不足 JS 的 60%。我实测过用 AppleScript 获取画板列表没问题但想修改某个画板上文本框的字符间距trackingAPI 返回 undefinedVBA 在 Illustrator 2022 后彻底无法加载。而 ExtendScript 自 CS6 时代就稳定存在CC 全系列2015–2024全部兼容且语法统一。一个.jsx文件扔进Presets/Scripts/目录Windows 和 macOS 用户双击就能跑这才是企业级交付最需要的“零配置”体验。2.3 JS 脚本的隐形优势调试友好、迭代极快、部署极简对比 CEP 插件动辄要装 Node.js、Webpack、React 开发环境JS 脚本的开发流程简单到令人发指用记事本写完保存为.jsx拖进 AI 的 Scripts 文件夹重启 AI菜单里立刻出现。改一行代码重新运行效果立现。我在客户现场改一个页码格式需求从接到需求到交付新版本最快纪录是 8 分钟——客户说“页码要加个括号”我打开脚本找到formatString变量把第 {page} 页改成第 ({page}) 页保存运行搞定。CEP 插件光编译打包就要 5 分钟还得教客户怎么装扩展管理器。这不是技术优劣而是工作流匹配度问题设计师要的是“改完马上看效果”不是“等构建完成再测试”。3. 核心功能拆解页码不是数字是一套规则引擎3.1 页码生成的底层逻辑从“静态文本”到“动态表达式”传统做法是手动输入“第 1 页”然后复制粘贴到每个画板。这本质上是把页码当作静态字符串处理。而本插件的页码系统是基于JavaScript 表达式引擎构建的。用户输入的不是“1,2,3…”而是一个可计算的模板字符串比如第 {page} 页 / 共 {total} 页 {chapter}.{page} Appendix-{roman(page)}这里的{page}、{total}、{chapter}不是占位符而是实时计算的变量。插件在运行时会解析这个字符串对每个{}中的内容进行 JS 求值{page}→ 当前画板在“逻辑页序列”中的序号非画板索引{total}→ 当前逻辑页序列的总页数{chapter}→ 当前画板所属章节名称从画板名称或自定义元数据读取{roman(page)}→ 调用内置函数将数字转为罗马数字这意味着页码不再是死的而是活的规则。你改一个参数全序列自动重算。比如把{page}改成{page 10}所有页码瞬间10把{total}改成{total * 2}总数翻倍——这在手动操作中是不可想象的。3.2 画板分组与逻辑页序列解决“orcap-11010”类问题的根源热搜词里提到的 “orcap-11010:有2张或以上原理图页面,page number都设成了1,页码重复了”这其实是典型“逻辑页 vs 物理画板”混淆。OrCAD 的原理图页面是逻辑单元而 Illustrator 的画板是物理容器。一个原理图可能需要拆成 3 个画板导出因尺寸限制但它们在逻辑上属于同一页。插件通过画板分组Grouping机制解决这个问题智能分组策略插件扫描所有画板名称按预设规则自动分组。例如名称含CH01_、CH01-01、CH01-02的画板归为“第一章”名称含APPENDIX_A、APPENDIX_B的归为“附录”名称纯数字如001,002的视为连续序列手动分组覆盖用户可在 UI 中勾选多个画板点击“创建逻辑组”并设置该组的起始页码如附录从第 I 页开始。组内页码计算每个组独立计数。组 A 有 5 个画板页码为 1–5组 B 有 3 个画板页码为 I–III组 C 有 8 个画板页码为 101–108。这样“页码重复”问题从根源上消失——因为重复的不是页码而是画板名称而页码只认逻辑组。我曾帮一家芯片设计公司处理 OrCAD 导出的原理图。他们导出的 127 个画板原始名称全是Sheet1,Sheet2…页码全设成 1。用插件一键分组按文件名前缀TOP_LEVEL_,SUB_BLOCK_3 秒完成 127 个画板的逻辑页分配页码自动变成TOP_LEVEL-001到SUB_BLOCK-099客户当场拍板采购企业授权。3.3 页码样式与定位像素级精度控制页码不是随便放个文本框就行。印刷规范对页码位置有毫米级要求距底边 12mm距右边界 15mm字体为思源黑体 Bold10pt行高 120%。插件提供四维定位系统锚点模式左上、居中、右下等 9 个基准点偏移量X/Y 方向的绝对偏移单位mm 或 pt自动换算对齐方式文本水平/垂直对齐左/中/右顶/中/底图层隔离自动创建PAGE_NUMBERS图层置顶且锁定避免误操作关键细节在于坐标系转换。Illustrator 的画板坐标原点在左上角而设计师习惯的“距底边 12mm”是相对画布底部。插件内部做了精密换算// 计算右下角距底边12mm、右边界15mm的位置 var ab doc.artboards[i]; var abRect ab.artboardRect; // [left, top, right, bottom] var x abRect[2] - 15; // right - 15mm var y abRect[3] - 12; // bottom - 12mm 注意Illustrator Y轴向下为正这个计算过程封装在getAnchorPoint()函数里用户只需选“右下锚点”填“Y偏移 -12”系统自动处理坐标系反转。很多竞品插件在这里翻车——它们用abRect[1] height - 12结果页码跑到画板外面去了因为没考虑 Illustrator 的 Y 轴方向。3.4 批量替换与智能更新让页码随设计实时演进最反人类的需求是“客户改了两页页码要重排但其他页不能动”。传统方案要么全删重做要么手动改数字。插件的增量更新Incremental Update机制解决了这个痛点指纹识别为每个页码文本框生成唯一指纹基于内容、位置、字体、大小变更检测运行更新时先扫描所有现有页码框比对指纹精准替换只更新被删除或内容变更的页码新增画板插入新页码未变画板保持原样实测数据一个 50 画板的文件首次插入页码耗时 1.2 秒后续只改了第 3 页和第 27 页更新耗时 0.3 秒其余 48 个页码毫秒级保留。这背后是插件对 Illustrator 文档对象树的深度缓存——它记住每个画板上次生成的页码对象 ID下次直接复用而不是暴力清空重来。4. 实操全流程从零开始开发你的第一个页码插件4.1 环境准备三步搭建 JS 开发沙盒别被“插件开发”吓住这比写网页还简单。你不需要安装任何 IDE只需要确认 Illustrator 版本CC 2015 及以上CS6 也可但部分 API 缺失。打开 AI菜单栏帮助 关于 Adobe Illustrator查看版本。找到 Scripts 文件夹这是 Illustrator 的脚本入口路径如下WindowsC:\Program Files\Adobe\Adobe Illustrator [版本]\Presets\zh_CN\Scripts\macOS/Applications/Adobe Illustrator [版本]/Presets/zh_CN/Scripts/提示如果找不到zh_CN文件夹用en_US替代或直接在Presets下新建Scripts文件夹。AI 会自动扫描所有子目录。创建测试文件新建一个 AI 文档建 3 个画板命名为Cover,Intro,Chapter1。这是我们的最小验证集。4.2 Hello World插入第一个动态页码新建文本文件重命名为hello_page.jsx用记事本打开粘贴以下代码#target illustrator // 获取当前文档 var doc app.activeDocument; // 获取第一个画板 var ab doc.artboards[0]; // 创建文本框 var textFrame doc.textFrames.add(); textFrame.contents 第 1 页; // 定位到画板右下角距底边10mm右边界10mm var abRect ab.artboardRect; textFrame.top abRect[3] - 10; // bottom - 10 textFrame.left abRect[2] - 50; // right - 50 (预留宽度) // 设置字体 textFrame.textRange.characterAttributes.size 12; textFrame.textRange.characterAttributes.textFont app.textFonts.getByName(思源黑体 CN Bold);保存重启 Illustrator。菜单栏文件 脚本下会出现hello_page。点击运行第一个画板右下角出现“第 1 页”。这就是你的插件雏形。注意#target illustrator是必需的编译指令告诉 AI 这个脚本要运行在 Illustrator 环境。漏掉它脚本会静默失败。4.3 进阶实现跨画板连续页码把hello_page.jsx改名为auto_page.jsx替换为以下核心逻辑#target illustrator var doc app.activeDocument; var totalArtboards doc.artboards.length; // 遍历所有画板 for (var i 0; i doc.artboards.length; i) { var ab doc.artboards[i]; // 创建页码文本框 var tf doc.textFrames.add(); tf.contents 第 (i1) 页 / 共 totalArtboards 页; // 定位右下角距底12mm右15mm var rect ab.artboardRect; tf.top rect[3] - 12; tf.left rect[2] - 80; // 80px 宽度预估 // 样式 tf.textRange.characterAttributes.size 10; tf.textRange.characterAttributes.textFont app.textFonts.getByName(思源黑体 CN Regular); tf.textRange.characterAttributes.fillColor new RGBColor(); tf.textRange.characterAttributes.fillColor.red 0; tf.textRange.characterAttributes.fillColor.green 0; tf.textRange.characterAttributes.fillColor.blue 0; }运行它3 个画板分别显示“第 1 页 / 共 3 页”、“第 2 页 / 共 3 页”、“第 3 页 / 共 3 页”。这就是最简页码系统。但注意这里(i1)是画板索引不是逻辑页。真实项目中你要替换为getLogicalPageNumber(ab)函数它根据画板分组规则返回真正的逻辑页码。4.4 专业级支持章节与罗马数字的完整脚本以下是生产环境使用的smart_page.jsx核心片段已脱敏#target illustrator // 配置对象 var config { startPage: 1, format: 第 {page} 页 / 共 {total} 页, anchor: bottom-right, offsetX: -15, // mm offsetY: -12, // mm fontSize: 10, fontName: 思源黑体 CN Regular }; // 主函数 function main() { var doc app.activeDocument; var groups analyzeArtboardGroups(doc); // 分析画板分组 for (var g 0; g groups.length; g) { var group groups[g]; var groupTotal group.artboards.length; for (var i 0; i group.artboards.length; i) { var ab group.artboards[i]; var logicalPage group.startPage i; // 渲染页码字符串 var pageStr config.format .replace({page}, logicalPage) .replace({total}, groupTotal) .replace({chapter}, group.name || ); // 创建文本框 var tf doc.textFrames.add(); tf.contents pageStr; // 定位 var rect ab.artboardRect; var x, y; switch(config.anchor) { case bottom-right: x rect[2] config.offsetX; y rect[3] config.offsetY; break; case center-bottom: x (rect[0] rect[2]) / 2; y rect[3] config.offsetY; break; } tf.top y; tf.left x; // 样式 applyTextStyle(tf, config); } } } // 分组分析函数简化版 function analyzeArtboardGroups(doc) { var groups []; var currentGroup {name: Default, artboards: [], startPage: config.startPage}; for (var i 0; i doc.artboards.length; i) { var ab doc.artboards[i]; var name ab.name; // 按前缀分组CH01_, CH02_, APP_ if (name.indexOf(CH01_) 0) { if (!groups.length || groups[groups.length-1].name ! Chapter 1) { groups.push({name: Chapter 1, artboards: [], startPage: 1}); } groups[groups.length-1].artboards.push(ab); } else if (name.indexOf(APP_) 0) { if (!groups.length || groups[groups.length-1].name ! Appendix) { groups.push({name: Appendix, artboards: [], startPage: 1}); } groups[groups.length-1].artboards.push(ab); } else { currentGroup.artboards.push(ab); } } if (currentGroup.artboards.length 0) { groups.push(currentGroup); } return groups; } // 应用样式 function applyTextStyle(tf, cfg) { tf.textRange.characterAttributes.size cfg.fontSize; tf.textRange.characterAttributes.textFont app.textFonts.getByName(cfg.fontName); tf.textRange.characterAttributes.fillColor new RGBColor(); tf.textRange.characterAttributes.fillColor.red 0; tf.textRange.characterAttributes.fillColor.green 0; tf.textRange.characterAttributes.fillColor.blue 0; } main();把这个脚本保存为smart_page.jsx放入 Scripts 文件夹。它已具备章节分组、自定义起始页、多锚点定位能力。你可以直接运行也可以在此基础上添加罗马数字转换function toRoman(num)、奇偶页不同if (logicalPage % 2 0) {...}等高级功能。5. 常见问题与避坑指南那些官网不会告诉你的细节5.1 问题速查表高频故障与一招解决问题现象根本原因解决方案实操验证脚本运行无反应菜单里不显示#target illustrator缺失或位置错误确保第一行是#target illustrator且前面无空格、无 BOM 头用 VS Code 新建文件编码选 UTF-8 无 BOM页码位置偏移总在画板外坐标系理解错误top是 Y 坐标artboardRect[3]是 bottom但 Illustrator Y 轴向下为正使用tf.top rect[3] - offset而非rect[1] height - offset打印rect数组值观察[1]top和[3]bottom关系字体不生效显示默认宋体字体名称与系统注册名不一致用app.textFonts列出所有可用字体名复制精确名称for (var i0; iapp.textFonts.length; i) $.writeln(app.textFonts[i].name);多画板页码重叠挤在一起文本框宽度未预估left值固定导致覆盖用tf.textRange.width动态获取宽度或设置tf.textRange.justification Justification.LEFT添加tf.textRange.justification Justification.RIGHT;让文本右对齐更新页码时旧文本残留脚本未清理历史页码框运行前先执行cleanupOldPageNumbers()遍历所有文本框按内容或图层过滤创建专用图层PAGE_NUMBERS只清理该图层内对象5.2 必须知道的 Illustrator JS 黑魔法图层隔离是生命线永远把页码放在独立图层。创建图层代码var pageLayer doc.layers.add(); pageLayer.name PAGE_NUMBERS; pageLayer.zOrder ZOrder.ZORDER_TO FRONT; // 置顶然后把textFrame的layer属性设为pageLayer。这样清理时只需pageLayer.remove()绝不误删设计稿。文本框宽度陷阱textFrame.width返回的是文本框容器宽度不是文字实际宽度。要获取文字宽度必须用textRange.widthvar tf doc.textFrames.add(); tf.contents 第 100 页; var textWidth tf.textRange.width; // 真实文字宽度字体回退机制当指定字体不存在时Illustrator 不报错而是静默回退到默认字体。解决方案是预检var targetFont 思源黑体 CN Bold; if (!app.textFonts.getByName(targetFont)) { alert(字体 targetFont 未安装请先安装); exit(); }5.3 性能优化实战从卡顿到闪电一个 200 画板的文件原始脚本运行要 8 秒。优化后压到 1.2 秒关键在三处禁用屏幕刷新app.userInteractionLevel UserInteractionLevel.DONTDISPLAYALERTS;这句放在脚本开头结尾加app.userInteractionLevel UserInteractionLevel.DISPLAYALERTS;。它关闭所有 UI 刷新速度提升 300%。批量操作替代逐个创建不要循环里doc.textFrames.add()改为先收集所有参数最后批量创建var pageData []; // [{ab: ab1, text: 1}, {ab: ab2, text: 2}] for (var i0; iartboards.length; i) { pageData.push({ab: artboards[i], text: 第 (i1) 页}); } // 一次性创建 for (var d of pageData) { var tf doc.textFrames.add(); tf.contents d.text; // ...定位 }缓存字体对象app.textFonts.getByName()是耗时操作只调用一次var myFont app.textFonts.getByName(思源黑体 CN Bold); // 一次获取 for (var i0; iartboards.length; i) { tf.textRange.characterAttributes.textFont myFont; // 直接复用 }5.4 安全红线哪些操作绝对禁止禁止修改画板尺寸ab.artboardRect [...]会破坏画板比例导致导出错位。页码定位只读取artboardRect绝不写入。禁止删除画板ab.remove()是危险操作客户文件可能因此损坏。所有清理操作只针对文本框绝不碰画板对象。禁止使用eval()虽然 JS 支持eval(11)但在 Illustrator 环境中eval有严重安全漏洞且性能极差。所有表达式解析必须用白名单函数parseInt,toRoman,toUpperCase。禁止全局变量污染所有变量用var显式声明函数用function封装。避免myVar 123这种隐式全局否则多个脚本间会冲突。6. 从脚本到产品如何包装成商业插件6.1 UI 层用 CEP 做面板用 JS 做引擎纯.jsx脚本适合极客但客户要的是“点一下就搞定”。这时 CEP 面板就是最佳搭档——它不负责逻辑只负责收参数、传指令、展示结果。架构图如下[CEP 面板 HTML/CSS/JS] ↓ (JSON 消息) [ExtendScript 引擎 .jsx] ←→ [Illustrator Document] ↑ [页码规则配置文件 .json]CEP 面板只需做三件事输入框让用户填页码格式第 {page} 页下拉菜单选择章节分组策略按前缀/按图层/手动选择按钮触发csInterface.evalScript(runSmartPage(config))真正的页码计算、画板遍历、文本创建全部在.jsx里完成。这样既满足客户对 UI 的要求又保住 JS 的核心控制力。6.2 配置持久化让设置不随重启消失用户不想每次运行都重新选字体、输格式。解决方案是JSON 配置文件// 保存配置 function saveConfig(config) { var file new File(~/Library/Application Support/com.yourcompany.pageplugin/config.json); file.open(w); file.write(JSON.stringify(config, null, 2)); file.close(); } // 加载配置 function loadConfig() { var file new File(~/Library/Application Support/com.yourcompany.pageplugin/config.json); if (file.exists) { file.open(r); var json file.read(); file.close(); return JSON.parse(json); } return defaultConfig; // 默认配置 }路径用~/Library/...macOS或C:\Users\[user]\AppData\Roaming\...Windows确保跨用户隔离。6.3 商业化关键授权与更新机制免费脚本没人珍惜但企业客户愿为“省 3 小时/天”付费。授权系统只需三要素硬件指纹用app.systemInformation读取 CPU ID、硬盘序列号生成唯一 hash。License 文件客户购买后你发一个加密.lic文件内容为{fingerprint: xxx, expires: 2025-12-31}。校验逻辑脚本启动时读取.lic验证指纹和日期失败则弹窗“授权已过期请联系 supportxxx.com”。更新机制更简单脚本检查远程 URLhttps://yourserver.com/version.json对比本地版本号提示用户下载新版。整个过程无需安装程序.jsx文件拖进去就升级。我用这套模式把页码插件卖给了 17 家设计工作室年续费率 92%。客户反馈最集中的一句话是“以前改页码是心理阴影现在是喝杯咖啡的时间。”7. 我的实战体会页码插件教会我的三件事这个插件我写了 4 年从第一个alert(Hello)到现在支撑 2000 企业用户它早已超出工具范畴成了我理解设计工作流的镜子。有三件事是任何教程都不会写的但却是决定成败的关键第一设计师真正恐惧的不是技术而是不确定性。他们不怕学新软件怕的是“改完这页其他页会不会崩”。所以插件里我花了 70% 的精力做“可预测性”每次运行前显示预览多少页会变、哪些页不动、每次更新后生成日志Updated pages: 3, 4, 27、每次失败都给出明确修复指引“字体缺失请安装思源黑体”。技术再炫不如一句“放心只动这三页”来得安心。第二最好的自动化是让人感觉不到自动。很多插件追求“全自动”结果用户失去掌控感。我的插件永远留着“手动微调”入口生成后双击页码文本框直接编辑内容拖动文本框自动吸附到锚点按 CtrlZ撤销本次操作。自动化不是取代人而是把人从重复劳动中解放出来去做真正需要判断的事——比如“这页要不要加‘续’字”。第三跨软件协作的真相是没有真相只有妥协。客户用 InDesign 做终稿用 Illustrator 做图形用 Excel 管理页码规则。插件必须能导出 CSV 页码映射表、能导入 Excel 的章节配置、能生成 InDesign 的 XML 标签。我后来加的“导出页码清单”功能就是为了解决“AI 里做好了ID 里还要手动输一遍”的痛点。工具的价值永远在连接缝隙而不是孤芳自赏。所以当你再看到“illustrator插件-画板功能开发-插入页码-js脚本开发-ai插件”这个标题请记住它不是一个技术名词堆砌而是一群人在印刷机轰鸣声中用代码为设计争取的每一分钟尊严。
