Skip to main content
Inspiring
June 22, 2023
Answered

ExtendScript to center selected objects to their parent artboards.

  • June 22, 2023
  • 3 replies
  • 1955 views

Hi all,

Looking for a script again. I have a script from Pixeden that centers everything in every artboard. Unfortunately it's hardcoded to work only with everything and not selected art. I just want a script that will center the selection (or just a single object if there is only one thing selected) to its parent artboard.

Here's the Pixeden script:

#target Illustrator

//  script.name = center-in-artboard.js;  
//  script.description = Loop into each Artboard and center its content;  
//  script.requirement = one document with at least one artboard;  
//  script.parent = pixeden;  
//  script.elegant = false; 

if (app.documents.length > 0) {
  var doc = app.activeDocument
  app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM
  centerInArtboard()
} else {
  alert ("There is no open document.")
}

function centerInArtboard () {
  // Loop into each Artboard
  for (var i = 0; i < doc.artboards.length; i++) {
    selectArtboardByIndex(i)
    // Group content to center as is
    var newGroupItem = doc.groupItems.add()
    for (var s = 0; s < doc.selection.length; s++) {
      doc.selection[s].move(newGroupItem, ElementPlacement.PLACEATEND)
    }
    centerSelected(newGroupItem, doc.artboards[i].artboardRect)
  }
}

function centerSelected (obj, artboardBound) {
  obj.position = new Array(
    (artboardBound[2] - artboardBound[0]) / 2 - obj.width / 2, 
    (artboardBound[3] - artboardBound[1]) / 2  + obj.height /2
  )
}

function selectArtboardByIndex (index) {
  doc.selection = null
  doc.artboards.setActiveArtboardIndex(index)
  doc.selectObjectsOnActiveArtboard()
}


Anyone have a script or a way to modify this to work only with what's selected?

To clarify:
1. If a selected object has a parent artboard, center it.
2. If a selected object has no parent artboard, do nothing.
3. If only one object is selected, center it.

I'm in the process of trying to hack and slash my way to a script but I don't really have a good grasp on what I'm doing. It's also heavily derivative of Sergey Osokin's work, I basically just copy-pasted some of his functions:

//@target illustrator

function main() {
    var CFG = {
        units: getUnits(), // Active document units
    };

    if (!documents.length) {
        alert('Error\nOpen a document and try again');
        return;
    }

    if (!selection.length || selection.typename == 'TextRange') {
        alert('Error\nPlease select atleast one object');
        return;
    }

    var isRulerTopLeft = preferences.getBooleanPreference('isRulerOriginTopLeft');
    var isRulerInFourthQuad = preferences.getBooleanPreference('isRulerIn4thQuad');

    CFG.isFlipY = (isRulerTopLeft && isRulerInFourthQuad) ? true : false;

    var doc = app.activeDocument,
        docAbs = doc.artboards,
        abIdx = docAbs.getActiveArtboardIndex(),
        abBnds = docAbs[abIdx].artboardRect,
        docSel = selection,
        item = docSel[0],
        coord = app.coordinateSystem;

    for (var i = 0, len = selection.length; i < len; i++) {
        var item = selection[i];
        
        // I have no idea how to get the artboard bounds
        // of the selected item to pass to centerToArtboard

        //centerToArtboard(item,abBnds,true);

    }

}

// Place the item in the center of the artboard
function centerToArtboard(item, abBnds, isFlipY) {
    var bnds = item.geometricBounds,
        itemSize = {
            left: bnds[0],
            top: bnds[1],
            inLeft: bnds[0],
            inTop: bnds[1],
            inRight: bnds[2],
            inBottom: bnds[3],
            h: 0,
            w: 0
        };

    if (isType(item, 'group') && item.clipped) {
        var mask = getMaskPath(item);
        bnds = mask.geometricBounds,
            itemSize.inLeft = bnds[0];
        itemSize.inTop = bnds[1];
        itemSize.inRight = bnds[2];
        itemSize.inBottom = bnds[3];
    }

    abWidth = Math.abs(abBnds[2] - abBnds[0]);
    abHeight = Math.abs(abBnds[1] - abBnds[3]);
    itemSize.h = Math.abs(itemSize.inTop - itemSize.inBottom);
    itemSize.w = Math.abs(itemSize.inRight - itemSize.inLeft);

    var left = itemSize.left - itemSize.inLeft,
        top = itemSize.top - itemSize.inTop,
        centerX = left + (abWidth - itemSize.w) / 2,
        centerY = top + (itemSize.h + (isFlipY ? -1 : 1) * abHeight) / 2;

    item.position = [centerX, centerY];
}
// Get active document ruler units
function getUnits() {
    if (!documents.length) return '';
    var key = activeDocument.rulerUnits.toString().replace('RulerUnits.', '');
    switch (key) {
        case 'Pixels':
            return 'px';
        case 'Points':
            return 'pt';
        case 'Picas':
            return 'pc';
        case 'Inches':
            return 'in';
        case 'Millimeters':
            return 'mm';
        case 'Centimeters':
            return 'cm';
            // Added in CC 2023 v27.1.1
        case 'Meters':
            return 'm';
        case 'Feet':
            return 'ft';
        case 'FeetInches':
            return 'ft';
        case 'Yards':
            return 'yd';
            // Parse new units in CC 2020-2023 if a document is saved
        case 'Unknown':
            var xmp = activeDocument.XMPString;
            if (/stDim:unit/i.test(xmp)) {
                var units = /<stDim:unit>(.*?)<\/stDim:unit>/g.exec(xmp)[1];
                if (units == 'Meters') return 'm';
                if (units == 'Feet') return 'ft';
                if (units == 'FeetInches') return 'ft';
                if (units == 'Yards') return 'yd';
                return 'px';
            }
            break;
        default:
            return 'px';
    }
}

// Check if item is in the active artboard area
function isContains(item, tag) {
    var isContains = false;

    addTag(item, tag);

    selection = null;
    redraw();

    activeDocument.selectObjectsOnActiveArtboard();
    for (var i = 0, len = selection.length; i < len; i++) {
        var item = selection[i];
        if (isTagExists(item, tag)) {
            isContains = true;
            break;
        }
    }

    removeTag(item, tag);

    return isContains;
}

// Get the clipping mask
function getMaskPath(group) {
    for (var i = 0, len = group.pageItems.length; i < len; i++) {
        var currItem = group.pageItems[i];
        if (isClippingPath(currItem)) {
            return currItem;
        }
    }
}

// Check the clipping mask
function isClippingPath(item) {
    var clipText = (isType(item, 'text') &&
        item.textRange.characterAttributes.fillColor == '[NoColor]' &&
        item.textRange.characterAttributes.strokeColor == '[NoColor]');
    return (isType(item, 'compound') && item.pathItems[0].clipping) ||
        item.clipping || clipText;
}

// Check the item typename by short name
function isType(item, type) {
    var regexp = new RegExp(type, 'i');
    return regexp.test(item.typename);
}

try {
    main();
} catch (e) {}


Basically, I don't know how to retrieve the artboard bounds for each individual selected object. I was thinking:

1. Store all selected items into an array ("selectedItems").
2. Deselect everything.
3. Loop through the "selectedItems" array. (for loop)
4. Select each item one by one and pass it to the centerToArtboard function

But the centerToArtboard function requires you to pass it the item and the artboard bounds. I know that I can do this:

docAbs = doc.artboards,
 abIdx = docAbs.getActiveArtboardIndex(),
abBnds = docAbs[abIdx].artboardRect,


But I'm confused with "docAbs[abIdx].artboardRect" and how it relates to the document "selection" collection. Does this function return the artboard dimensions of the current selection?

How do I get the artboard bounds for the currently selected pageitem?

Sorry for post length.

Any help really appreciated.

This topic has been closed for replies.

3 replies

Sergey Osokin
Sergey OsokinCorrect answer
Inspiring
June 28, 2023
Inspiring
July 1, 2023

You are an absolute legend! Thank you so much for making this. Throw me a donation URL and I'll happily compensate you.

Sergey Osokin
Inspiring
July 1, 2023

The script file includes a Buymeacoffee URL in its header.

Kurt Gold
Community Expert
Community Expert
June 25, 2023

You may also try the script PositionOnArtboard, provided by Sergey Anosov.

 

https://sites.google.com/site/dtpscripting/illustrator-scripts/positiononartboard

 

Sergey Osokin
Inspiring
June 22, 2023

Since Illustrator doesn't define a parent artboard for an object, we must first check each artboard in the document and compare its contents to our selected objects. To do this, we can use a trick from isContains(). I can try to customize my script later.

Inspiring
June 24, 2023

I got it kind-of working with this:

 

for (var i = 0; i < selectedItems.length; i++) {

        var item = selectedItems[i];
        item.selected = true;

        //alert("New Iteration: " + item);

        for (var j = 0; j < app.activeDocument.artboards.length; j++) {

            app.activeDocument.artboards.setActiveArtboardIndex(j);
            app.redraw();

            if (isContains(item, CFG.tag)) {

                // alert("Item: " + item + " is Contained by artboard index " + app.activeDocument.artboards.getActiveArtboardIndex());

                app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM;

                var rect = app.activeDocument.artboards[j].artboardRect;
                centerToArtboard(item, rect, CFG.isFlipY);

                app.coordinateSystem = CFG.coord;
                app.redraw();

                // alert("BREAK! Current item: " + item);
                break;
            }
        }
    }

 

 

But it's really slow since it's checking all artboards every iteration. Also, sometimes it freaks out and centers an item to a different artboard that it doesn't belong in. It's the best I can do.

Here's a screen recording: https://streamable.com/kqmsb4

Sergey Osokin
Inspiring
June 24, 2023

Checking the artboard objects still needs to be done. As I said, objects don't have built-in attributes of the parent artboard. The options are few: use tricks and accept the shortcomings, or have no script at all. I set out to create a comprehensive version of this script. And with an example like yours, my computer runs fast. The slowdown will only occur on artboards with a lot of objects.