Copy link to clipboard
Copied
I can't create a java script to align all objects to the base of the artboard because it always gives an error on the Y axis. I've tried several approaches but so far nothing
Does anyone have an example script how to align multiple objects to the base?
on the x axis it works perfectly to center without losing the relative distance between the objects, however I have not had success in relation to the Y axis, either to measure the size of the grouping, to center or to move
Copy link to clipboard
Copied
Can you share your script that works perfectly with the x axis?
Copy link to clipboard
Copied
Hi @Luiz Vigraf there are some complexities to this issue, for example: getting the actual bounds of an item is difficult in many cases, such as text or clipping masked items. I have written a function to help with this (see in script below), which makes things much easier, and made writing this script today super quick. I also wrote a function to get the most likely artboard for a given page item, which will be handy, too. I included an option for left justifying the items (you might not need this, but I did it for fun—turn it off by editing `settings.justifyLeft` to false in the script.
Let me know if it's useful.
- Mark
/**
* @file Move Selected Items To Bottom Of Artboard.js
*
* Moves the selected items to the bottom of its artboard,
* with the option to left justify with space between.
*
* @author m1b
* @version 2025-01-16
* @discussion https://community.adobe.com/t5/illustrator-discussions/create-script-to-move-on-the-y-axis-using-artborad-as-a-reference/m-p/15091849
*/
(function () {
var settings = {
justifyLeft: true,
spaceBetween: 0,
};
var doc = app.activeDocument,
items = doc.selection,
xPositions = {};
if (settings.justifyLeft)
items.sort(sortByLeft);
for (var i = 0; i < items.length; i++) {
var item = items[i],
bounds = getItemBoundsIllustrator(item, false),
artboard = getArtboardWithMostOverlap(doc.artboards, bounds);
if (!artboard)
// item not on any artboard
continue;
var artboardBounds = artboard.artboardRect;
// keep track of x position per artboard
xPositions[artboard.name] = undefined == xPositions[artboard.name] ? artboardBounds[0] : xPositions[artboard.name];
var dx = settings.justifyLeft ? xPositions[artboard.name] - bounds[0] : 0,
dy = artboardBounds[3] - bounds[3];
// advance x position for this artboard
xPositions[artboard.name] += bounds[2] - bounds[0] + settings.spaceBetween;
// move the item into position
item.translate(dx, dy, true, true, true, true);
}
})();
/**
* Returns the artboard that most overlaps the given bounds.
* Will return nothing if no overlap at all.
* @author m1b
* @version 2025-01-16
* @param {Array<Artboard>|Artboards} artboards - the artboards to search.
* @param {Array<Number>} bounds - the bounds to check against [L, T, R, B].
* @returns {Artboard?} - the artboard with the most overlap.
*/
function getArtboardWithMostOverlap(artboards, bounds) {
if (
undefined == artboards
|| 0 === artboards.length
)
return;
var maxOverlap = 0,
bestArtboard = undefined;
for (var i = 0; i < artboards.length; i++) {
var artboard = artboards[i];
var artboardBounds = artboard.artboardRect; // [L, T, R, B]
// calculate the overlap
var overlapL = Math.max(bounds[0], artboardBounds[0]),
overlapT = Math.min(bounds[1], artboardBounds[1]),
overlapR = Math.min(bounds[2], artboardBounds[2]),
overlapB = Math.max(bounds[3], artboardBounds[3]);
// overlap area
var overlapWidth = Math.max(0, overlapR - overlapL),
overlapHeight = Math.max(0, overlapT - overlapB),
overlapArea = overlapWidth * overlapHeight;
if (overlapArea > maxOverlap) {
// the largest overlapping artboard so far
maxOverlap = overlapArea;
bestArtboard = artboard;
}
}
return bestArtboard;
};
/**
* Sort by left bounds.
* @author m1b
* @version 2025-01-16
* @param {PageItem} a - item to sort.
* @param {PageItem} b - item to sort.
* @returns {Number} - the sort code.
*/
function sortByLeft(a, b) {
return (
// left bounds value rounded to 3 decimal places
Math.round((a.visibleBounds[0] - b.visibleBounds[0]) * 1000) / 1000
// same left, so we'll sort by top bounds value
|| a.visibleBounds[1] - b.visibleBounds[1]
);
};
// from here down is just code to get a page item's bounds
/**
* Returns bounds of item(s).
* @author m1b
* @version 2024-03-10
* @param {PageItem|Array<PageItem>} item - an Illustrator PageItem or array of PageItems.
* @param {Boolean} [geometric] - if false, returns visible bounds.
* @param {Array} [bounds] - private parameter, used when recursing.
* @returns {Array} - the calculated bounds.
*/
function getItemBoundsIllustrator(item, geometric, bounds) {
var newBounds = [],
boundsKey = geometric ? 'geometricBounds' : 'visibleBounds';
if (undefined == item)
return;
if (
item.typename == 'GroupItem'
|| item.constructor.name == 'Array'
) {
var children = item.typename == 'GroupItem' ? item.pageItems : item,
contentBounds = [],
isClippingGroup = (item.hasOwnProperty('clipped') && item.clipped == true),
clipBounds;
for (var i = 0, child; i < children.length; i++) {
child = children[i];
if (
child.hasOwnProperty('clipping')
&& true === child.clipping
)
// the clipping item
clipBounds = child.geometricBounds;
else
contentBounds.push(getItemBoundsIllustrator(child, geometric, bounds));
}
newBounds = combineBounds(contentBounds);
if (isClippingGroup)
newBounds = intersectionOfBounds([clipBounds, newBounds]);
}
else if (
'TextFrame' === item.constructor.name
&& TextType.AREATEXT !== item.kind
) {
// get bounds of outlined text
var dup = item.duplicate().createOutline();
newBounds = dup[boundsKey];
dup.remove();
}
else if (item.hasOwnProperty(boundsKey)) {
newBounds = item[boundsKey];
}
// `bounds` will exist if this is a recursive execution
bounds = (undefined == bounds)
? bounds = newBounds
: bounds = combineBounds([newBounds, bounds]);
return bounds;
};
/**
* Returns the combined bounds of all bounds supplied.
* Works with Illustrator or Indesign bounds.
* @author m1b
* @version 2024-03-09
* @param {Array<bounds>} boundsArray - an array of bounds [L, T, R, B] or [T, L , B, R].
* @returns {bounds?} - the combined bounds.
*/
function combineBounds(boundsArray) {
var combinedBounds = boundsArray[0],
comparator;
if (/indesign/i.test(app.name))
comparator = [Math.min, Math.min, Math.max, Math.max];
else if (APP_IS_ILLUSTRATOR)
comparator = [Math.min, Math.max, Math.max, Math.min];
// iterate through the rest of the bounds
for (var i = 1; i < boundsArray.length; i++) {
var bounds = boundsArray[i];
combinedBounds = [
comparator[0](combinedBounds[0], bounds[0]),
comparator[1](combinedBounds[1], bounds[1]),
comparator[2](combinedBounds[2], bounds[2]),
comparator[3](combinedBounds[3], bounds[3]),
];
}
return combinedBounds;
};
/**
* Returns the overlapping rectangle
* of two or more rectangles.
* NOTE: Returns undefined if ANY
* rectangles do not intersect.
* @author m1b
* @version 2024-09-05
* @param {Array<bounds>} arrayOfBounds - an array of bounds [L, T, R, B] or [T, L , B, R].
* @returns {bounds?} - intersecting bounds.
*/
function intersectionOfBounds(arrayOfBounds) {
var comparator;
if (/indesign/i.test(app.name))
comparator = [Math.max, Math.max, Math.min, Math.min];
else if (APP_IS_ILLUSTRATOR)
comparator = [Math.max, Math.min, Math.min, Math.max];
// sort a copy of array
var bounds = arrayOfBounds
.slice(0)
.sort(function (a, b) { return b[0] - a[0] || a[1] - b[1] });
// start with first bounds
var intersection = bounds.shift(),
b;
// compare each bounds, getting smaller
while (b = bounds.shift()) {
// if doesn't intersect, bail out
if (!boundsDoIntersect(intersection, b))
return;
intersection = [
comparator[0](intersection[0], b[0]),
comparator[1](intersection[1], b[1]),
comparator[2](intersection[2], b[2]),
comparator[3](intersection[3], b[3]),
];
}
return intersection;
};
/**
* Returns true if the two bounds intersect.
* @author m1b
* @version 2024-03-10
* @param {Array} bounds1 - bounds array.
* @param {Array} bounds2 - bounds array.
* @param {Boolean} [TLBR] - whether bounds arrays are interpreted as [t, l, b, r] or [l, t, r, b] (default: based on app).
* @returns {Boolean}
*/
function boundsDoIntersect(bounds1, bounds2, TLBR) {
if (undefined == TLBR)
TLBR = (/indesign/i.test(app.name));
return !(
TLBR
// TLBR
? (
bounds2[0] > bounds1[2]
|| bounds2[1] > bounds1[3]
|| bounds2[2] < bounds1[0]
|| bounds2[3] < bounds1[1]
)
// LTRB
: (
bounds2[0] > bounds1[2]
|| bounds2[1] < bounds1[3]
|| bounds2[2] < bounds1[0]
|| bounds2[3] > bounds1[1]
)
);
};
Edit 2025-01-16: improved variable name.