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 22, 2023

Ahhh that makes more sense. I am trying to work out what the "isContains" trick is and tried this:

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

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

for (var i = 0, len = docAbs.length; i < len; i++) {
    
    docAbs.setActiveArtboardIndex(i);

    for (var j = 0, len2 = selectionRef.length; j < len2; j++) {

        var item = selectionRef[j];

        if (!CFG.isContains || isContains(item, CFG.tag)) {
            alert(item + " is contained on artboard index " + i);

                var abBnds = docAbs[i].artboardRect;
                app.coordinateSystem = CoordinateSystem.ARTBOARDCOORDINATESYSTEM;
                centerToArtboard(item, abBnds, CFG.isFlipY);
                app.coordinateSystem = coord;

            }
        }
    }

 
But the alert is saying every item is contained on every artboard, hah. I must be missing something.

My logic was:

1. Iterate through every artboard (i)
2. For each artboard, iterate through each selected item (j)
3. Test if isContains returns true - this means the item (j) belongs to artboard (i)
4. Center the object if above is true

I think I'm just too tired and not thinking clearly at the moment.