Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

How to get exact left, top, width, height for selected items using extendscript?

New Here ,
Mar 06, 2025 Mar 06, 2025

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

 

psar12345_0-1741275361363.png

 

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)..

TOPICS
How-to , Scripting
1.7K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Mar 06, 2025 Mar 06, 2025

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 ()
...
Translate
Adobe
Participant ,
Mar 06, 2025 Mar 06, 2025

geometricBounds takes all nested items into account. The bounds you're getting is all items, even those nested deep inside groups and clipgroups.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Mar 06, 2025 Mar 06, 2025

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 06, 2025 Mar 06, 2025

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.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 07, 2025 Mar 07, 2025

Hi @m1b,

 

Thanks for your support

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 14, 2025 Mar 14, 2025

s

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 14, 2025 Mar 14, 2025

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 10, 2025 Mar 10, 2025

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] });

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 10, 2025 Mar 10, 2025

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 10, 2025 Mar 10, 2025

Hi @m1b,

 

For testing, i created the two clipping path groups without path item.

 

psar12345_0-1741620478762.png

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?

 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 10, 2025 Mar 10, 2025

Thanks @psar12345 I have updated the script above so that it handles empty clipping groups.

- Mark

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 11, 2025 Mar 11, 2025

Thanks @m1b 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 14, 2025 Mar 14, 2025

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]);

 

 

 

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 14, 2025 Mar 14, 2025

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?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 18, 2025 Mar 18, 2025

Hi @m1b,

 

I attached the screenshort, testing data, pdf file here.

psar12345_0-1742313089728.png

 

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

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 18, 2025 Mar 18, 2025

@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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 18, 2025 Mar 18, 2025
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 18, 2025 Mar 18, 2025

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

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 24, 2025 Mar 24, 2025

Hi @m1b,

 

Thanks for your support and cooperation.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Mar 24, 2025 Mar 24, 2025

You're welcome. I hope your project goes well!

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Advocate ,
Mar 15, 2025 Mar 15, 2025

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)

renl80416020_1-1742059968048.png

 

renl80416020_0-1742059751427.png

René

 

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
New Here ,
Mar 24, 2025 Mar 24, 2025
LATEST

Hi @renél80416020,

 

Yes, you are right it is the center reference point.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines