デザインカンプから画像を書き出す作業が、毎回しんどい。
特に校了データが回ってきたときの「また全部やり直し」がつらかったので、
アートボード化して画像を書き出す時のリネームをJSXスクリプト化しました。


    そもそも何が面倒なのか

    Illustratorのデザインカンプから、Web用の画像を書き出す。
    それだけの話なのですが、実案件だと画像点数が90点を超えることもあります。

    しかも初稿でひととおり書き出した後、校了データが新しいファイルで回ってくる。
    そこでまた同じ作業をやり直すことになります。


    アセットの書き出しは、意外とめんどくさい

    Illustratorには「アセットの書き出し」パネルがあります。
    オブジェクトをドラッグして放り込み、名前を付けて一括書き出しできる機能です。

    便利ではあるのですが、実際に使うと引っかかる点がいくつかあります。

    一度入れたら並び替えができない

    これが一番きつい。

    パネルに追加した順に並ぶだけで、後から並び替える手段がありません。
    90点を追加していく途中で「あ、この順番だと分かりにくい」と気づいても、もう直せない。

    結果、リネームや整理のときに目的のアセットを探し回ることになります。

    グループを掘るのが大変

    マスクにマスクを何重も重ねたような構造になっていると、
    目的のまとまりを選択するまでにレイヤーパネルを何段も掘る必要があります。

    Illustratorのレイヤーパネルで深くネストしたグループ構造

    <グループ> が延々と並んでいて、どれが目的のものか分からない。
    デザイナーさんが作ったデータだと、なおさら構造が読めません。

    はみ出しをマスクする必要がある

    アセットに追加すると、そのオブジェクトの外接矩形で書き出されます。
    はみ出している要素があると、意図しない余白が付いてくる。

    これを防ぐにはあらかじめマスクを掛けておく必要があり、ひと手間増えます。


    アートボード化して画像を書き出す方が楽

    アートボードなら、

    • パネル上で並び替えができる(ドラッグで順番を入れ替えられる)
    • 範囲を矩形で決められるので、はみ出しは自動的に切れる
    • グループを掘る必要がない
    • 「スクリーン用に書き出し」で2xなど倍率指定して一括出力できる

    書き出し設定もアートボード単位でそのまま使えるので、素直です。

    命名済みのアートボードが並んだIllustratorのアートボードパネル


    でも、1個ずつリネームするのが面倒

    アートボード運用でも、結局、リネームはめんどくさいです。

    アートボードパネルで名前をダブルクリック → 入力 → Enter。

    連番を振るスクリプトは公開されているものがありますが、
    「どこからどこまで」の範囲指定ができなかったり、不規則な名前には対応できなかったりします。

    設計書に使用画像一覧を書いているので、そのリストをそのまま流し込みたい。

    Claude Codeにレイアウトを正確に再現させる「中間設計書」記法 デザインカンプとAIだけでコーディングさせると、横並びや表示順がうまく再現できない。そこで「カンプ→中間設計書→コーディング」の3段階に分け、人間もAIも読める独自のレイアウト記法を作りました。記号設計の試行錯誤と、設計書ルールの全体像を解説します...  続きを読む

    JSX(スクリプト)で一括リネームする

    というわけで作りました。

    やりたかったこと

    1. アートボードの範囲を指定(例: 10〜16)
    2. スクリプト起動でダイアログ表示
    3. 改行区切りで名前を入力
    4. ボタン一発でリネーム

    個数が合わないときの挙動も決めておきます。

    • 名前が少ない → 残りは連番でリネーム
    • 範囲が少ない → 範囲の分だけリネーム

    実際の画面

    アートボード一括リネームスクリプトのダイアログ

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

    実行後

    一括リネーム後のアートボードパネル

    一発で命名完了。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

    Illustratorのその他のスクリプトのファイル選択ダイアログ

    毎回、ファイル > スクリプト > その他のスクリプト > ファイル選択 は長いですが、
    「その他のスクリプト」には、デフォルトで command + F12 が設定されています。

    ファイル選択ダイアログは出ますが、前回開いた場所を覚えているので、
    実質2アクションで実行できます。


    「選択オブジェクトに合わせる」にショートカットを設定

    アートボード化して画像を書き出す際、もうひとつ便利なのがこれです。

    オブジェクト > アートボード > 選択オブジェクトに合わせる

    アートボードのサイズを、選択中のオブジェクトにぴったり合わせてくれます。
    便利ですが、デフォルトではショートカットが割り当たっていません。

    編集 > キーボードショートカット から、メニューコマンドを開いて
    オブジェクト > アートボード > 選択オブジェクトに合わせる に割り当てます。

    私は option + command + F にしています。

    Illustratorのキーボードショートカット設定画面


    まとめ

    アセットの書き出し アートボード運用
    並び替え できない ◎ ドラッグで自由
    範囲指定 オブジェクトの外接矩形 ◎ 矩形で自由に
    はみ出し 事前にマスクが必要 ◎ 自動で切れる
    グループ選択 深いと掘るのが大変 ◎ 不要
    一括リネーム 1個ずつ ◎ JSXで一発

    アセットの書き出しは「少数の素材をサッと出す」には向いています。
    点数が増えて管理が必要になったら、アートボード運用の方がラクです。

    校了データが来ても、アートボードさえ作れば、名前リストを貼り付けて一発でリネームできるので、以前のような「また全部やり直し」感がなくなりました。

    スクリプトは Illustrator 2026(macOS)で動作確認しています。バージョンによって挙動が異なる場合があります。


    関連記事

    illustratorからWeb用画像を書き出す際の注意点 Web用の画像を書き出す際、いくつかの設定を確認することで、より良い品質の画像を作成することができます。特にデザインデータを受け取って画像を書き出す際に確認しておきたいポイントをまとめました。カラーモードとプロファイルの設定まずはカラーモードとプロ...  続きを読む IllustratorのデザインカンプからCSSを抽出する方法 IllustratorのCSSプロパティパネルを使ってCSSを書き出す手順を解説。グラデーションや角丸の抽出に実用的な反面、ドロップシャドウや点線は取れないなど、コーダー目線での正直な評価をまとめました。  続きを読む