Copy link to clipboard
Copied
Hi,
I need to get exact left, top, width, height for selected items using extendscript.
i try,
var selectedItems = app.activeDocument.selection;
var item = selectedItems[0];
// Get the bounds of the selected items
var bounds = item.geometricBounds;
var left = bounds[0];
var top = bounds[1];
var right = bounds[2];
var bottom = bounds[3];
// Calculate width and height
var width = Math.abs(right - left);
var height = Math.abs(top - bottom);
left: 238.013587617332
top: 583.391458053209
right: 345.268957635682
bottom: 526.611861697349
width: 107.255370018351
height: 56.7795963558601
but the values are differenct. selected item contains pathitem and 3 clip group items.
Please help to get correct value(left, top, width and height)..
Hi @psar12345, calculating the exact bounds of a page item is surprisingly involved. I wrote a function that makes a valiant attempt at doing it. You can see it working in the following demo script. The function `getItemBoundsIllustrator` returns [L, T, R, B]. Give it a try if you like.
- Mark
/**
* @file Get Item Bounds.js
*
* Demonstration of calculating the bounds
* of the selected item(s) and draws a
* box to show the bounds.
*
* @author m1b
* @version 2025-03-07
*/
(function ()
...
Copy link to clipboard
Copied
geometricBounds takes all nested items into account. The bounds you're getting is all items, even those nested deep inside groups and clipgroups.
Copy link to clipboard
Copied
If you need to determine the bounds of what you perceive, you should think about what you are actually looking at and why that is the case. Are your perceivable bounds determined by a clipping mask somewhere within the group etc?
Copy link to clipboard
Copied
Hi @psar12345, calculating the exact bounds of a page item is surprisingly involved. I wrote a function that makes a valiant attempt at doing it. You can see it working in the following demo script. The function `getItemBoundsIllustrator` returns [L, T, R, B]. Give it a try if you like.
- Mark
/**
* @file Get Item Bounds.js
*
* Demonstration of calculating the bounds
* of the selected item(s) and draws a
* box to show the bounds.
*
* @author m1b
* @version 2025-03-07
*/
(function () {
// enum to make the function call below more readable
var BoundsType = {
GEOMETRIC_BOUNDS: true,
VISIBLE_BOUNDS: false,
};
var doc = app.activeDocument;
if (0 === doc.selection.length)
return alert('Please select and item and try again.');
// calculate the bounds of the selection
var bounds = getItemBoundsIllustrator(doc.selection, BoundsType.VISIBLE_BOUNDS);
if (bounds)
// for demonstration purposes, draw a box to show bounds
var box = drawRectangle(doc, bounds, { opacity: 30, fillColor: makeColor([255, 50, 150]) });
})();
/**
* Draws a rectangle to the document.
* @param {Document|Layer|GroupItem} container - an Illustrator container.
* @param {Array<Number>} bounds - [T, L, B, R].
* @param {Object} props - properties to assign to the rectangle.
* @return {PathItem} - the rectangle.
*/
function drawRectangle(container, bounds, properties) {
properties = properties || {};
var rectangle = container.pathItems.rectangle(bounds[1], bounds[0], bounds[2] - bounds[0], -(bounds[3] - bounds[1])); // TLWH
// defaults
rectangle.filled = true;
rectangle.stroked = false;
// apply properties
for (var key in properties)
if (properties.hasOwnProperty(key))
rectangle[key] = properties[key];
return rectangle;
};
/**
* Return an RGB color.
* @param {Array<Number>} breakdown - the color breakdown.
* @returns {RGBColor}
*/
function makeColor(breakdown) {
var colr = new RGBColor();
colr.red = breakdown[0];
colr.green = breakdown[1];
colr.blue = breakdown[2];
return colr;
};
/**
* Returns bounds of item(s).
* @author m1b
* @version 2025-03-11
* @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 [L, T, R, B].
*/
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
&& true !== child.stroked
&& true !== child.filled
)
// the clipping item
clipBounds = child.geometricBounds;
else
contentBounds.push(getItemBoundsIllustrator(child, geometric, bounds));
}
newBounds = combineBounds(contentBounds);
if (
isClippingGroup
&& clipBounds
&& newBounds
)
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 2025-03-11
* @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
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];
if (!bounds)
continue;
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
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-03-11: added handling for empty clipping groups.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
s
Copy link to clipboard
Copied
Interesting! According to the predicate && newBounds, if `newBounds` is undefined, the next line shouldn't run and shouldn't throw an error. Can you share a sample document that shows the problem?
Copy link to clipboard
Copied
Hi @m1b,
Sometime, below mention line through undefined is not an object error.
var bounds = arrayOfBounds .slice(0) .sort(function (a, b) { return b[0] - a[0] || a[1] - b[1] });
Copy link to clipboard
Copied
Hi @psar12345 I would love to fix that error. Can you post an Illustrator file (saved as pdf) with the object that causes the error?
- Mark
Copy link to clipboard
Copied
Hi @m1b,
For testing, i created the two clipping path groups without path item.
now i changed the code like this
var bounds = arrayOfBounds
.slice(0)
.sort(function (a, b) {
if(typeof a == 'undefined' || typeof b == 'undefined'){
return 1;
}else{
return b[0] - a[0] || a[1] - b[1];
}
});
it is working. Any suggestion?
Copy link to clipboard
Copied
Thanks @psar12345 I have updated the script above so that it handles empty clipping groups.
- Mark
Copy link to clipboard
Copied
Thanks @m1b
Copy link to clipboard
Copied
Hi @m1b ,
Sometime, below mention function through undefined is not an object error.
getItemBoundsIllustrator
i check in the function below mention line newbounds is undefined but clipBounds value is there
if (
isClippingGroup
&& clipBounds
&& newBounds
)
newBounds = intersectionOfBounds([clipBounds, newBounds]);
Copy link to clipboard
Copied
Interesting! According to the predicate && newBounds, if `newBounds` is undefined, the next line shouldn't run and shouldn't throw an error. Can you share a sample document that shows the problem?
Copy link to clipboard
Copied
Hi @m1b,
I attached the screenshort, testing data, pdf file here.
isClippingGroup: true
clipBounds: [210.175409534158,422.538454790807,401.928486569737,370.006999754647]
newBounds: undefined
isClippingGroup: false
clipBounds: undefined
newBounds: undefined
in this function (getItemBoundsIllustrator) return undefined
Copy link to clipboard
Copied
@psar12345 I tried that pdf but it only had the green rectangle, not the clipping group. Perhaps you didn't save it with "Illustrator compatibility"?
- Mark
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Hi @psar12345 I see... this is the same scenario you told me before: an empty clipping group. So, yes, the function returns "undefined". That is correct operation in my opinion because it is empty—there is no bounds—but tell me: what would you like it to return?
Or are you saying you are seeing an error? if so I don't see it. Maybe you didn't copy my updated script in that case.
- Mark
Copy link to clipboard
Copied
Copy link to clipboard
Copied
You're welcome. I hope your project goes well!
Copy link to clipboard
Copied
Bonjour @psar12345 ,
j'ai une remarque à formuler sur le fait que certaines valeurs ne correspondent pas.
ex pour gauche (vous avez sélectionner le centre de l'objet)
René
Copy link to clipboard
Copied
Find more inspiration, events, and resources on the new Adobe Community
Explore Now