Repeat Artboard

Script for Adobe Illustrator

Duplicate artboard multiple times.

  • Choose number across and down
  • Specify space between artboards
  • Option to increment digits in artboard name and content
  • Adapt open source to customize or create other scripts
Download
Repeat Artboard
Many scripts are free to download thanks to the support of users. Help me keep developing new scripts by supporting my work. Click the PayPal button to contribute any amount. Thank you.

How-to Video

How to use the script

The interface is a single section with five options. Set options as desired and click OK to begin. The last artboard of the document is repeated per the options and each repeated artboard name and/or content is incremented per options set.

CAUTION: the script does consider the dimensions of the result, or the complexity of the content repeated. The script will do as told even if it overloads what Illustrator can handle. This solution is best suited to small artboards of simple content, and/or a modest number of duplicates. Use with care.

Across — the total number across from left to right.

Down — the total number down from top to bottom.

Both values across and down are totals that include the artboard that is repeated. For example, the value 5 means 4 duplicate artboards are added for a total of 5.

Gutter — the space between artboards. Choose the measurement units in the drop-down list to the right.

Increment digits in artboard name — if digits are found in the artboard name, these are incremented each artboard repeated.

Increment digits in content — if digits are found in the artboard textual content, these are incremented each artboard repeated.

To change the default values the script uses, open the script in any text editor. Near the top look for “var defaults”. Edit the values as desired and save the script file.

Source code

(download button below)

/*

Repeat Artboard
Copyright 2022 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact

Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

(function () {

    var defaults = {
        across: 5,
        down: 5,
        gutter: 0.25,
        // units: "Pixels",
        // units: "Points",
        // units: "Picas",
        units: "Inches",
        // units: "Millimeters",
        // units: "Centimeters",
        incDigitsArtboard: true,
        incDigitsContent: true
    };

    var title = "Repeat Artboard";

    if (!/illustrator/i.test(app.name)) {
        alert("Script for Illustrator", title, false);
        return;
    }

    // Script variables.
    var across;
    var doc;
    var down;
    var measurementUnits;
    var measurementUnitsShort;

    // Reusable UI variables.
    var g; // group
    var gc1; // group (column)
    var gc2; // group (column)
    var p; // panel
    var w; // window

    // Permanent UI variables.
    var btnCancel;
    var btnOk;
    var cbIncDigitsArtboard;
    var cbIncDigitsContent;
    var inpAcross;
    var inpDown;
    var inpGutter;
    var listUnits;

    // SETUP

    measurementUnits = [
        "Pixels",
        "Points",
        "Picas",
        "Inches",
        "Millimeters",
        "Centimeters"
    ];
    measurementUnitsShort = [
        "px",
        "pt",
        "pc",
        "in",
        "mm",
        "cm"
    ];

    // Script requires open document.
    if (!app.documents.length) {
        alert("Open a document", title, false);
        return;
    }
    doc = app.activeDocument;
    // Script doesn't work in Isolation Mode.
    if (doc.layers[0].name == "Isolation Mode") {
        alert("Exit Isolation Mode before running script", title, false);
        return;
    }

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    w.alignChildren = "fill";
    // Panel.
    p = w.add("panel");
    p.alignChildren = "left";
    // Group of 2 columns.
    g = p.add("group");
    g.margins = [0, 0, 0, 9];
    // Groups, columns 1 and 2.
    gc1 = g.add("group");
    gc1.orientation = "column";
    gc1.alignChildren = "left";
    gc1.spacing = 12;
    gc2 = g.add("group");
    gc2.orientation = "column";
    gc2.alignChildren = "left";
    gc2.spacing = 6;
    // Rows.
    gc1.add("statictext", undefined, "Across:");
    inpAcross = gc2.add("edittext");
    inpAcross.preferredSize.width = 50;
    gc1.add("statictext", undefined, "Down:");
    inpDown = gc2.add("edittext");
    inpDown.preferredSize.width = 50;
    gc1.add("statictext", undefined, "Gutter:");
    g = gc2.add("group");
    inpGutter = g.add("edittext");
    inpGutter.preferredSize.width = 50;
    listUnits = g.add("dropdownlist", undefined, measurementUnits);
    listUnits.preferredSize = [100, -1];
    cbIncDigitsArtboard = p.add("checkbox", undefined, "Increment digits in artboard name");
    cbIncDigitsContent = p.add("checkbox", undefined, "Increment digits in content");

    // Action Buttons.
    g = w.add("group");
    g.alignment = "center";
    btnOk = g.add("button", undefined, "OK");
    btnCancel = g.add("button", undefined, "Cancel");
    // Panel Copyright.
    p = w.add("panel");
    p.add("statictext", undefined, "Copyright 2022 William Campbell");

    // SET UI VALUES

    inpAcross.text = defaults.across;
    inpDown.text = defaults.down;
    inpGutter.text = defaults.gutter;
    listUnits.selection = listUnits.find(defaults.units) || 0;
    listUnits.priorIndex = listUnits.selection.index;
    cbIncDigitsArtboard.value = defaults.incDigitsArtboard;
    cbIncDigitsContent.value = defaults.incDigitsContent;

    // UI ELEMENT EVENT HANDLERS

    inpAcross.onChange = function () {
        validateInteger(this);
    };
    inpDown.onChange = function () {
        validateInteger(this);
    };
    inpGutter.onChange = function () {
        validateFloat(this);
    };
    listUnits.onChange = function () {
        var index;
        var mu1;
        var mu2;
        var convert = function (uiEdit) {
            uiEdit.text = String(parseFloat(UnitValue(Number(uiEdit.text), mu1).as(mu2).toFixed(4)));
        };
        index = this.selection.index;
        if (index != this.priorIndex) {
            // Units have changed.
            mu1 = measurementUnitsShort[this.priorIndex];
            mu2 = measurementUnitsShort[index];
            convert(inpGutter);
        }
        this.priorIndex = index;
    };
    btnOk.onClick = function () {
        w.close(1);
    };
    btnCancel.onClick = function () {
        w.close(0);
    };

    // DISPLAY THE DIALOG

    if (w.show() == 1) {
        try {
            process();
        } catch (e) {
            alert("An error has occurred.\nLine " + e.line + ": " + e.message, title, true);
        }
    }

    //====================================================================
    //               END PROGRAM EXECUTION, BEGIN FUNCTIONS
    //====================================================================

    function padZero(value, len) {
        // Returns a string of 'value' left padded
        // with zeros to become at least 'len' digits.
        var s = value.toString();
        while (s.length < len) {
            s = "0" + s;
        }
        return s;
    }

    function process() {
        var ab1;
        var ab2;
        var abH;
        var abW;
        var col;
        var docScaleFactor;
        var gutter;
        var i;
        var ii;
        var item;
        var len;
        var matches;
        var rect;
        var row;
        var startNextRow;
        var stepH;
        var stepW;
        var val;
        var xOrigin;
        var preserve = {
            pasteRemembersLayers: app.pasteRemembersLayers
        };
        app.pasteRemembersLayers = true;
        across = Number(inpAcross.text);
        down = Number(inpDown.text);
        docScaleFactor = doc.scaleFactor || 1;
        gutter = valueAsPoints(inpGutter, listUnits) / docScaleFactor;
        startNextRow = false;
        try {
            for (row = 0; row < down; row++) {
                var columns = across - (row == 0 ? 1 : 0);
                for (col = 0; col < columns; col++) {
                    // Get last artboard.
                    doc.artboards.setActiveArtboardIndex(doc.artboards.length - 1);
                    ab1 = doc.artboards[doc.artboards.getActiveArtboardIndex()];
                    rect = ab1.artboardRect;
                    // rect = left top right bottom
                    if (row == 0 && col == 0) {
                        xOrigin = rect[0];
                        abW = (rect[2] - rect[0]);
                        abH = (rect[1] - rect[3]);
                    }
                    stepW = abW + gutter;
                    stepH = abH + gutter;
                    // Duplicate artboard.
                    doc.selectObjectsOnActiveArtboard();
                    app.copy();
                    ab2 = doc.artboards.add(rect);
                    if (startNextRow) {
                        ab2.artboardRect = [
                            xOrigin,
                            rect[1] - stepH,
                            xOrigin + abW,
                            rect[3] - stepH
                        ];
                        startNextRow = false;
                    } else {
                        ab2.artboardRect = [
                            rect[0] + stepW,
                            rect[1],
                            rect[2] + stepW,
                            rect[3]
                        ];
                    }
                    if (cbIncDigitsArtboard.value) {
                        // Look for digits in artboard name.
                        // Increment any found, else add "2" to get numbering started.
                        matches = ab1.name.match(/\d+/);
                        if (matches) {
                            len = matches[0].length;
                            val = Number(matches[0]);
                            ab2.name = ab1.name.replace(/\d+/, padZero(val + 1, len));
                        } else {
                            ab2.name = ab1.name + "2";
                        }
                    }
                    app.executeMenuCommand("pasteFront");
                    if (cbIncDigitsContent.value) {
                        // Look for content with digits.
                        // Increment any found.
                        doc.selectObjectsOnActiveArtboard();
                        for (i = 0; i < doc.selection.length; i++) {
                            item = doc.selection[i];
                            if (item.typename == "TextFrame") {
                                matches = item.contents.match(/\d+/g);
                                if (matches) {
                                    for (ii = 0; ii < matches.length; ii++) {
                                        len = matches[ii].length;
                                        val = Number(matches[ii]);
                                        item.contents = item.contents.replace(matches[ii], padZero(val + 1, len));
                                    }
                                }
                            }
                        }
                    }
                    doc.selection = null;
                }
                startNextRow = true;
            }
        } finally {
            app.pasteRemembersLayers = preserve.pasteRemembersLayers;
        }
    }

    function validateFloat(uiEdit) {
        var s;
        var v;
        // Remove non-digits.
        s = uiEdit.text.replace(/[^0-9.]/g, "");
        if (s != uiEdit.text) {
            alert("Numeric input only.\nNon-numeric characters removed.", " ", false);
        }
        // No more than one decimal point.
        s = s.replace(/\.{2,}/g, ".");
        v = parseFloat(s) || 0;
        uiEdit.text = v.toString();
    }

    function validateInteger(uiEdit) {
        var s;
        var v;
        // Remove non-digits.
        s = uiEdit.text.replace(/[^0-9]/g, "");
        if (s != uiEdit.text) {
            alert("Numeric input only.\nNon-numeric characters removed.", " ", false);
        }
        // Make integer.
        v = Math.round(Number(s) || 0);
        // Zero values not allowed.
        v = v || 1;
        // Back to string.
        uiEdit.text = v.toString();
    }

    function valueAsPoints(uiEdit, uiList) {
        var mu;
        var v;
        mu = measurementUnitsShort[uiList.selection.index];
        v = parseFloat(uiEdit.text) || 0;
        return UnitValue(v, mu).as("pt");
    }

})();
Many scripts are free to download thanks to the support of users. Help me keep developing new scripts by supporting my work. Click the PayPal button to contribute any amount. Thank you.
Download
Repeat Artboard

For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.

Also available for hire to program custom solutions. Contact William for more information.

IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.