デザインカンプから画像を書き出す作業が、毎回しんどい。
特に校了データが回ってきたときの「また全部やり直し」がつらかったので、
アートボード化して画像を書き出す時のリネームをJSXスクリプト化しました。
そもそも何が面倒なのか
Illustratorのデザインカンプから、Web用の画像を書き出す。
それだけの話なのですが、実案件だと画像点数が90点を超えることもあります。
しかも初稿でひととおり書き出した後、校了データが新しいファイルで回ってくる。
そこでまた同じ作業をやり直すことになります。
アセットの書き出しは、意外とめんどくさい
Illustratorには「アセットの書き出し」パネルがあります。
オブジェクトをドラッグして放り込み、名前を付けて一括書き出しできる機能です。
便利ではあるのですが、実際に使うと引っかかる点がいくつかあります。
一度入れたら並び替えができない
これが一番きつい。
パネルに追加した順に並ぶだけで、後から並び替える手段がありません。
90点を追加していく途中で「あ、この順番だと分かりにくい」と気づいても、もう直せない。
結果、リネームや整理のときに目的のアセットを探し回ることになります。
グループを掘るのが大変
マスクにマスクを何重も重ねたような構造になっていると、
目的のまとまりを選択するまでにレイヤーパネルを何段も掘る必要があります。

<グループ> が延々と並んでいて、どれが目的のものか分からない。
デザイナーさんが作ったデータだと、なおさら構造が読めません。
はみ出しをマスクする必要がある
アセットに追加すると、そのオブジェクトの外接矩形で書き出されます。
はみ出している要素があると、意図しない余白が付いてくる。
これを防ぐにはあらかじめマスクを掛けておく必要があり、ひと手間増えます。
アートボード化して画像を書き出す方が楽
アートボードなら、
- パネル上で並び替えができる(ドラッグで順番を入れ替えられる)
- 範囲を矩形で決められるので、はみ出しは自動的に切れる
- グループを掘る必要がない
- 「スクリーン用に書き出し」で2xなど倍率指定して一括出力できる
書き出し設定もアートボード単位でそのまま使えるので、素直です。

でも、1個ずつリネームするのが面倒
アートボード運用でも、結局、リネームはめんどくさいです。
アートボードパネルで名前をダブルクリック → 入力 → Enter。
連番を振るスクリプトは公開されているものがありますが、
「どこからどこまで」の範囲指定ができなかったり、不規則な名前には対応できなかったりします。
設計書に使用画像一覧を書いているので、そのリストをそのまま流し込みたい。
Claude Codeにレイアウトを正確に再現させる「中間設計書」記法
JSX(スクリプト)で一括リネームする
というわけで作りました。
やりたかったこと
- アートボードの範囲を指定(例: 10〜16)
- スクリプト起動でダイアログ表示
- 改行区切りで名前を入力
- ボタン一発でリネーム
個数が合わないときの挙動も決めておきます。
- 名前が少ない → 残りは連番でリネーム
- 範囲が少ない → 範囲の分だけリネーム
実際の画面

プレビュー欄に「現在の名前 → 新しい名前」が出るので、
実行前に対応がズレていないか確認できます。
実行後

一発で命名完了。90点でも数秒です。
コード
#target "illustrator"
/*
* RenameArtboardsFromList.jsx ver.1.1
* --------------------------------------------------------------
* アートボードを、改行区切りで入力した名前リストで一括リネームする。
*
* 使い方:
* 1. ファイル > スクリプト > その他のスクリプト でこれを実行
* 2. 対象アートボードの範囲を指定(例: 10 〜 16)
* 3. 名前を改行区切りで貼り付け
* 4. プレビューを確認して[リネーム]
*
* 個数が合わない場合:
* 名前の方が少ない → 自動連番で続きを埋める(オフにも切替可)
* 範囲の方が少ない → 範囲の分だけリネーム(余った名前は無視)
* --------------------------------------------------------------
*/
(function () {
if (app.documents.length === 0) {
alert("ドキュメントが開かれていません。");
return;
}
var doc = app.activeDocument;
var abs = doc.artboards;
var abCount = abs.length;
var activeIdx = 1;
try { activeIdx = abs.getActiveArtboardIndex() + 1; } catch (e) {}
// ============================================================
// 連番ユーティリティ
// ============================================================
/*
* 名前の末尾の数字を解析する
* "frntClbL-Unorato_pht2" → prefix:"frntClbL-Unorato_pht" num:2 pad:1
* "frntClbL-Unorato_pht" → prefix:"frntClbL-Unorato_pht" num:1 pad:0(数字なし)
* "cnvnClbL-Convenience_pht01" → prefix:"..._pht" num:1 pad:2(ゼロ埋め維持)
* "frntClbL-cov_bg1.jpg" → prefix:"frntClbL-cov_bg" num:1 suffix:".jpg"
*/
function parseTail(name) {
var m = name.match(/^(.*?)(\d+)(\D*)$/);
if (m) {
return {
prefix: m[1],
num: parseInt(m[2], 10),
pad: m[2].length,
suffix: m[3],
hadNum: true
};
}
// 末尾に数字がない場合は「1」とみなす(次は 2 から)
return { prefix: name, num: 1, pad: 0, suffix: "", hadNum: false };
}
function buildName(p, n) {
var s = String(n);
if (p.pad > 1) {
while (s.length < p.pad) s = "0" + s;
}
return p.prefix + s + p.suffix;
}
/*
* 対象枚数ぶんの名前を確定させる
* 戻り値: [{ name: "...", auto: true/false }, ...]
*/
function resolveNames(span, names, useAuto) {
var out = [], i;
for (i = 0; i < span; i++) {
if (i < names.length) {
out.push({ name: names[i], auto: false });
} else if (useAuto && names.length > 0) {
var base = parseTail(names[names.length - 1]);
var offset = i - names.length + 1;
out.push({ name: buildName(base, base.num + offset), auto: true });
} else {
out.push(null); // 変更しない
}
}
return out;
}
// ============================================================
// ダイアログ
// ============================================================
var win = new Window("dialog", "アートボードを一括リネーム");
win.orientation = "column";
win.alignChildren = "fill";
win.spacing = 10;
win.margins = 16;
// --- 対象範囲 -------------------------------------------------
var pnlRange = win.add("panel", undefined, "対象アートボード");
pnlRange.orientation = "column";
pnlRange.alignChildren = "left";
pnlRange.margins = 14;
pnlRange.spacing = 8;
var rowR = pnlRange.add("group");
rowR.add("statictext", undefined, "開始");
var etFrom = rowR.add("edittext", undefined, String(activeIdx));
etFrom.characters = 5;
rowR.add("statictext", undefined, "〜 終了");
var etTo = rowR.add("edittext", undefined, String(abCount));
etTo.characters = 5;
rowR.add("statictext", undefined, "(全 " + abCount + " 枚)");
var rowR2 = pnlRange.add("group");
var btnActive = rowR2.add("button", undefined, "現在のアートボードを開始に");
var btnAll = rowR2.add("button", undefined, "すべて");
// --- 名前リスト -----------------------------------------------
var pnlNames = win.add("panel", undefined, "名前(改行区切り)");
pnlNames.orientation = "column";
pnlNames.alignChildren = "fill";
pnlNames.margins = 14;
pnlNames.spacing = 8;
var etNames = pnlNames.add("edittext", undefined, "", {
multiline: true,
scrolling: true
});
etNames.preferredSize = [440, 160];
var optRow = pnlNames.add("group");
var cbSkipEmpty = optRow.add("checkbox", undefined, "空行を無視する");
cbSkipEmpty.value = true;
var cbTrim = optRow.add("checkbox", undefined, "前後の空白を除去");
cbTrim.value = true;
var optRow2 = pnlNames.add("group");
var cbAuto = optRow2.add("checkbox", undefined,
"名前が足りない分は、最後の名前から続きの連番を振る");
cbAuto.value = true;
// --- プレビュー -----------------------------------------------
var pnlPrev = win.add("panel", undefined, "プレビュー");
pnlPrev.orientation = "column";
pnlPrev.alignChildren = "fill";
pnlPrev.margins = 14;
var lstPrev = pnlPrev.add("listbox", undefined, [], {
numberOfColumns: 4,
showHeaders: true,
columnTitles: ["#", "現在の名前", "→ 新しい名前", "種別"],
columnWidths: [36, 170, 200, 70]
});
lstPrev.preferredSize = [500, 170];
var stInfo = pnlPrev.add("statictext", undefined, "");
stInfo.characters = 68;
// --- ボタン ---------------------------------------------------
var rowBtn = win.add("group");
rowBtn.alignment = "right";
rowBtn.add("button", undefined, "キャンセル", { name: "cancel" });
var btnOK = rowBtn.add("button", undefined, "リネーム", { name: "ok" });
// ============================================================
// ロジック
// ============================================================
function trimStr(s) {
return s.replace(/^[\s\u3000]+/, "").replace(/[\s\u3000]+$/, "");
}
function getNames() {
var raw = etNames.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
var out = [], i, v;
for (i = 0; i < raw.length; i++) {
v = cbTrim.value ? trimStr(raw[i]) : raw[i];
if (cbSkipEmpty.value && v === "") continue;
out.push(v);
}
return out;
}
function getRange() {
var f = parseInt(etFrom.text, 10);
var t = parseInt(etTo.text, 10);
if (isNaN(f) || f < 1) f = 1;
if (isNaN(t) || t > abCount) t = abCount;
if (t < f) t = f;
return { from: f, to: t };
}
function refresh() {
lstPrev.removeAll();
var names = getNames();
var rg = getRange();
var span = rg.to - rg.from + 1;
var plan = resolveNames(span, names, cbAuto.value);
var applied = 0, autoCount = 0, i, item;
for (i = 0; i < span; i++) {
var abIndex = rg.from + i;
item = lstPrev.add("item", String(abIndex));
item.subItems[0].text = abs[abIndex - 1].name;
if (plan[i]) {
item.subItems[1].text = plan[i].name;
item.subItems[2].text = plan[i].auto ? "自動連番" : "入力";
applied++;
if (plan[i].auto) autoCount++;
} else {
item.subItems[1].text = "(変更なし)";
item.subItems[2].text = "—";
}
}
var msg = "リネーム対象 " + applied + " 枚";
if (autoCount > 0) msg += "(うち自動連番 " + autoCount + " 枚)";
if (names.length > span) {
msg += " / 名前が " + (names.length - span) + " 件余ります(無視されます)";
} else if (names.length < span && !cbAuto.value) {
msg += " / " + (span - names.length) + " 枚は変更されません";
}
stInfo.text = msg;
btnOK.enabled = (applied > 0);
}
etNames.onChanging = refresh;
etFrom.onChanging = refresh;
etTo.onChanging = refresh;
cbSkipEmpty.onClick = refresh;
cbTrim.onClick = refresh;
cbAuto.onClick = refresh;
btnActive.onClick = function () {
try { etFrom.text = String(abs.getActiveArtboardIndex() + 1); } catch (e) {}
refresh();
};
btnAll.onClick = function () {
etFrom.text = "1";
etTo.text = String(abCount);
refresh();
};
refresh();
// ============================================================
// 実行
// ============================================================
if (win.show() !== 1) return;
var names = getNames();
var rg = getRange();
var span = rg.to - rg.from + 1;
var plan = resolveNames(span, names, cbAuto.value);
var done = [], i;
for (i = 0; i < span; i++) {
if (!plan[i]) continue;
var ab = abs[rg.from + i - 1];
var before = ab.name;
try {
ab.name = plan[i].name;
done.push((rg.from + i) + ": " + before + " → " + plan[i].name +
(plan[i].auto ? " [自動]" : ""));
} catch (e) {
done.push((rg.from + i) + ": " + before + " → 失敗 (" + e.message + ")");
}
}
alert("リネーム完了: " + done.length + " 枚\n\n" + done.join("\n"));
}());
補足:アートボードの複数選択について
アートボードパネルでの複数選択は、ExtendScriptから取得できません。
パネル上で選択状態にしても、その情報はスクリプト側に渡ってこないためです。
そのため、このスクリプトでは番号による範囲指定にしています。
「現在のアートボードを開始に」ボタンを付けたので、
開始したいアートボードをパネルでクリック → ボタン押下、で番号が入ります。
「その他のスクリプト」のショートカット:command + F12

毎回、ファイル > スクリプト > その他のスクリプト > ファイル選択 は長いですが、
「その他のスクリプト」には、デフォルトで command + F12 が設定されています。
ファイル選択ダイアログは出ますが、前回開いた場所を覚えているので、
実質2アクションで実行できます。
「選択オブジェクトに合わせる」にショートカットを設定
アートボード化して画像を書き出す際、もうひとつ便利なのがこれです。
オブジェクト > アートボード > 選択オブジェクトに合わせる
アートボードのサイズを、選択中のオブジェクトにぴったり合わせてくれます。
便利ですが、デフォルトではショートカットが割り当たっていません。
編集 > キーボードショートカット から、メニューコマンドを開いて
オブジェクト > アートボード > 選択オブジェクトに合わせる に割り当てます。
私は option + command + F にしています。

まとめ
| アセットの書き出し | アートボード運用 | |
| 並び替え | できない | ◎ ドラッグで自由 |
| 範囲指定 | オブジェクトの外接矩形 | ◎ 矩形で自由に |
| はみ出し | 事前にマスクが必要 | ◎ 自動で切れる |
| グループ選択 | 深いと掘るのが大変 | ◎ 不要 |
| 一括リネーム | 1個ずつ | ◎ JSXで一発 |
アセットの書き出しは「少数の素材をサッと出す」には向いています。
点数が増えて管理が必要になったら、アートボード運用の方がラクです。
校了データが来ても、アートボードさえ作れば、名前リストを貼り付けて一発でリネームできるので、以前のような「また全部やり直し」感がなくなりました。
スクリプトは Illustrator 2026(macOS)で動作確認しています。バージョンによって挙動が異なる場合があります。
関連記事
illustratorからWeb用画像を書き出す際の注意点
IllustratorのデザインカンプからCSSを抽出する方法