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

Table's Position & Scripting

Participant ,
Feb 05, 2023 Feb 05, 2023

Copy link to clipboard

Copied

Hi,

 

I've got a table within a text frame.

I'd like to my script to figure out the table's position (its x & y coordinates).

How do I do that?

 

Thanks! 

TOPICS
Scripting

Views

1.1K

Translate

Translate

Report

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 2 Correct answers

Community Expert , Feb 05, 2023 Feb 05, 2023

Hi @orib7317974, you would think it would be straightforward, but... nope. I've written a script that (I hope) does it. Have a read through and see if you can tell how it works. It was fun to write. You can just add the relevant functions from my script to yours.

- Mark

 

/**
 * Gets the bounds of the selected table.
 * @author m1b
 * @discussion https://community.adobe.com/t5/indesign-discussions/table-s-position-amp-scripting/m-p/13555533
 */

function main() {

    var doc = app.activeDocumen
...

Votes

Translate

Translate
Community Expert , Feb 06, 2023 Feb 06, 2023

Hi @m1b , I’m not sure if this would always work, but a simpler approach might be to work off of the insertion point before the table (the table’s .storyOffset property). Its baseline minus the table height minus the top border stroke width would get Y, and its horizontalOffset plus 1 point would get X. With some limited testing this seems to work:

 

 


var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
var pos = getTableXY(et[0])

alert("X: " + pos[0] + "   Y: " +
...

Votes

Translate

Translate
Community Expert ,
Feb 05, 2023 Feb 05, 2023

Copy link to clipboard

Copied

If there is no text before the table - and no insets in the parent TF - then it's easy:

RobertTkaczyk_0-1675634160328.png

Table's top-left corner will be the same as TF's.

 

If there will be some text - then a bit more math is needed 😉

 

RobertTkaczyk_1-1675634248560.png

 

If you can remove insets in one of the cells - easier math - then from the baseline of the "a" you can subtract height of the row/cell and then stroke - to get Y. And from the HorizontalOffset of the 1st InsertionPoint you can get X - after subtracting stroke.

 

Votes

Translate

Translate

Report

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 ,
Feb 05, 2023 Feb 05, 2023

Copy link to clipboard

Copied

Hi @orib7317974, you would think it would be straightforward, but... nope. I've written a script that (I hope) does it. Have a read through and see if you can tell how it works. It was fun to write. You can just add the relevant functions from my script to yours.

- Mark

 

/**
 * Gets the bounds of the selected table.
 * @author m1b
 * @discussion https://community.adobe.com/t5/indesign-discussions/table-s-position-amp-scripting/m-p/13555533
 */

function main() {

    var doc = app.activeDocument,
        selectedCell = getCell(app.selection[0]);

    if (
        selectedCell == undefined
        || !selectedCell.isValid
    ) {
        alert('Please select a table or inside a table and run script again.');
        return;
    }

    var table = selectedCell.parent;
    var bounds = getTableBounds(table, 'geometricBounds');

    if (!bounds)
        return;

    // draw a rectangle with the calculated bounds
    var justForShowing = doc.layoutWindows[0].activePage.rectangles.add({ geometricBounds: bounds, fillColor: doc.swatches[4] });
    justForShowing.transparencySettings.blendingSettings.opacity = 50;

}; // end main

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Get Table Position Demo");



/**
 * Gets the table's bounds.
 * @author m1b
 * @version 2023-02-11
 * @param {Table} table - an Indesign Table.
 * @param {String} boundsType - can be 'geometricBounds' or 'visibleBounds'.
 * @returns {Array<Number>}
 */
function getTableBounds(table, boundsType) {

    if (table.cells.lastItem().texts[0].parentTextFrames.length == 0) {
        alert('Cannot getTableBounds: table has overset cells.');
        return;
    }

    var topLeft = getCellBounds(table.cells.firstItem(), boundsType),
        bottomRight = getCellBounds(table.cells.lastItem(), boundsType);

    return [topLeft[0], topLeft[1], bottomRight[2], bottomRight[3]];

}


/**
 * Returns the bounds of the cell;
 * @author m1b
 * @version 2023-06-07
 * @param {Cell} cell - an Indesign Table Cell.
 * @param {String} [boundsType] - can be 'geometricBounds' or 'visibleBounds' (default: 'geometricBounds').
 * @returns {Array<Number>}
 */
function getCellBounds(cell, boundsType) {

    app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;

    boundsType = boundsType || 'geometricBounds';

    if (!cell.hasOwnProperty('index'))
        throw Error('getCellBounds failed: bad cell parameter.');

    var table = cell.parent;
    while (table.constructor.name != 'Table')
        table = table.parent;

    table.insertLabel('findMe', '1');

    var textFrame = table.parent;

    while (textFrame.parent.constructor.name == 'Character') {
        // inside anchored frame
        textFrame = textFrame.parent.parentTextFrames[0];
    }

    var cellID = cell.id,
        dupTextFrame = textFrame.duplicate(),
        dupTable;

    // find the table in dup text frame
    tableSearch:
    for (var i = 0; i < dupTextFrame.tables.length; i++) {
        if (dupTextFrame.tables[i].extractLabel('findMe') == '1') {
            dupTable = dupTextFrame.tables[i];
            break tableSearch;
        }
    }

    if (dupTable == undefined) {

        // find the table in anchored text frames
        tableSearch:
        for (var i = 0; i < dupTextFrame.textFrames.length; i++) {
            for (var j = 0; j < dupTextFrame.textFrames[i].tables.length; j++)
                if (dupTextFrame.textFrames[i].tables[j].extractLabel('findMe') == '1') {
                    dupTable = dupTextFrame.textFrames[i].tables[j];
                    break tableSearch;
                }
        }

    }

    if (dupTable == undefined)
        throw Error('getCellBounds failed: Could not find table in duplicate.');

    // convert cell to graphic, so we can get the bounds
    var dupCell = dupTable.cells.itemByID(cellID);
    dupCell.convertCellType(CellTypeEnum.GRAPHIC_TYPE_CELL);

    var bounds = dupCell.rectangles[0].geometricBounds;

    // clean up
    table.insertLabel('findMe', '');
    dupTextFrame.remove();

    // add strokeWidths
    if (boundsType === 'visibleBounds') {
        bounds[0] -= cell.topEdgeStrokeWeight;
        bounds[1] -= cell.leftEdgeStrokeWeight;
        bounds[2] += cell.bottomEdgeStrokeWeight;
        bounds[3] += cell.rightEdgeStrokeWeight;
    }
    else if (boundsType === 'geometricBounds') {
        bounds[0] -= cell.topEdgeStrokeWeight / 2;
        bounds[1] -= cell.leftEdgeStrokeWeight / 2;
        bounds[2] += cell.bottomEdgeStrokeWeight / 2;
        bounds[3] += cell.rightEdgeStrokeWeight / 2;
    }

    return bounds;

};


/**
 * Attempts to return a Cell, given an object.
 * @author m1b
 * @version 2023-02-06
 * @param {InsertionPoint|Text|Cells|Table} obj - an object related to a Cell.
 * @returns {Cell}
 */
function getCell(obj) {

    if (obj == undefined)
        return;

    if (obj.constructor.name == 'Cell')
        return obj;

    if (obj.parent.constructor.name == 'Cell')
        return obj.parent;

    if (
        obj.hasOwnProperty('cells')
        && obj.cells.length > 0
    )
        return obj.cells[0];

};

 

 

Edit 2023-06-07: added handling of tables in anchored text frames, and multiple tables in same text frame.

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

Hey @Robert Tkaczyk  and @m1b, thank you for these suggestions! @m1b, I really appreciate the time and effort you've put into this, I wasn't expecting somethin like this 🙂 Cheers!

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

Hi @m1b , I’m not sure if this would always work, but a simpler approach might be to work off of the insertion point before the table (the table’s .storyOffset property). Its baseline minus the table height minus the top border stroke width would get Y, and its horizontalOffset plus 1 point would get X. With some limited testing this seems to work:

 

 


var et = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
var pos = getTableXY(et[0])

alert("X: " + pos[0] + "   Y: " + pos[1])

/**
* Get a table’s X,Y position 
* t.storyOffset is the insertion point before the table 
* get its baseline - the table h - the top border for Y
* the insertion point’s horizontal position gets X
*
* @ param the table  
* @ return x,y as an array 
* 
*/
function getTableXY(t){
    app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
    var tr = t.rows[0].cells.everyItem().getElements()
    var ti = t.topBorderStrokeWeight;
    for (var i = 0; i < tr.length; i++){
        if (tr[i].topEdgeStrokeWeight > ti) {
            ti = tr[i].topEdgeStrokeWeight
        } 
    };    
    var ty = t.storyOffset.baseline - t.height - ti;
    var tx = t.storyOffset.horizontalOffset+1
    app.scriptPreferences.measurementUnit = AutoEnum.AUTO_VALUE;
    return [tx,ty]
}

 

 

 

 

 

 

Margin guides at 1". The table is pushed to the right of the text frame via a 1" Left Indent, and pushed down via Space After in the paragraph above:

Screen Shot 30.png

 

EDIT had X,Y reversed—the returned array is [X,Y]

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

Hi @rob day and @Peter Kahrel, thank you both. Yes that was my initial direction for solving this problem, too, but, prior to having experts like yourselves endorse it, I decided to switch tracks for two reasons: (1) I wasn't confident that I was taking all the edge cases into account and your method seemed a bit shaky in that regard, and (2) my way was more fun to write 🙂

 

For the OP, and others reading this thread, Rob/Peter's method would be quicker to execute, because it does less DOM manipulation (would only be noticable to a human if performing many of executions of the function) and also calculates the maximum strokewidth (see Rob's example where the top left cell has thinner strokes than some other cells in the row/column).

- Mark

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

I wasn't confident that I was taking all the edge cases into account and your method seemed a bit shaky in that regard

 

Hi Mark, I’ve tried to break it and haven’t found away maybe you can. Doesn’t seem like the storyOffset insertion point relative to the table can be changed, so I think it reliably find’s the lower left table corner x,y. I was hoping the insertion point’s .ascent property would be at the top of the table including strokes (the UI cursor is) but it always returns as 0.

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

Hi @rob day and @Peter Kahrel for my learning I made a quick version of my script but using your technique. Well it was supposed to be quick. I now realise why I wrote my script the way I did—because my test file happened to show a glitch (I didn't know it was a glitch at the time and just thought the technique was unreliable) which I've reported as a bug. I found that if the last header row of the table has a bottom edge stroke (if greater than 1, I think), it will increase the height of the first body row to accommodate the thickness of the stroke, but thereafter the height property of both the table and the row will not reflect that addition. This meant that the top edge calculation was off by that amount. I've attached my test document if you want to test if you can reproduce the problem on your system.

Screenshot 2023-02-07 at 14.15.11.png

I've added a "getAnomalousExtraTableHeight" function to address this issue. Hopefully will not always be needed.

 

Here is the script. It will draw a rectangle showing the table's bounds. Change true to false to show geometricBounds.

 

/**
 * Get selected table's bounds
 * @discussion https://community.adobe.com/t5/indesign-discussions/table-s-position-amp-scripting/m-p/13555533
 */

function main() {

    var doc = app.activeDocument,
        cell = getCell(app.selection[0]);

    if (
        cell == undefined
        || !cell.isValid
    ) {
        alert('Please select inside a table and run script again.')
        return;
    }

    var bounds = getTableBounds(cell.parent, true);

    // just for displaying the bounds
    var justForShowing = doc.rectangles.add({ geometricBounds: bounds, strokeColor: doc.swatches[0], fillColor: doc.swatches[4] });
    justForShowing.transparencySettings.blendingSettings.opacity = 50;


    /**
     * Returns the table's bounds.
     * @author m1b and rob day
     * @param {Table} table - an Indesign table.
     * @param {Boolean} [includeStroke] - whether to include stroke bounds (default: true).
     * @returns {Array<Number>} - [t, l, b, r]
     */
    function getTableBounds(table, includeStroke) {

        app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;

        // a 1 pt discrepancy between the table's insertionPoint's horizontalOffset and the table
        var horizontalAnomaly = 1;

        var topEdgeStrokeWeight = Math.max.apply(null, (table.rows.item(0).cells.everyItem().topEdgeStrokeWeight)),
            bottomEdgeStrokeWeight = Math.max.apply(null, (table.rows.item(-1).cells.everyItem().bottomEdgeStrokeWeight)),
            leftEdgeStrokeWeight = Math.max.apply(null, (table.columns.item(0).cells.everyItem().leftEdgeStrokeWeight)),
            rightEdgeStrokeWeight = Math.max.apply(null, (table.columns.item(-1).cells.everyItem().rightEdgeStrokeWeight)),

            // I think this is due to a bug in Indesign
            anomalousExtraTableHeight = getAnomalousExtraTableHeight(table),

            // calculate the bounds
            l = table.storyOffset.horizontalOffset + horizontalAnomaly,
            b = table.storyOffset.baseline,
            r = l + Number(table.width) + rightEdgeStrokeWeight,
            t = b - table.height - ((topEdgeStrokeWeight + bottomEdgeStrokeWeight) / 2) - anomalousExtraTableHeight,
            bounds = [t, l, b, r];

        // subtract strokeWidths to get the geometric bounds
        if (includeStroke === false) {

            bounds[0] += topEdgeStrokeWeight / 2;
            bounds[1] += leftEdgeStrokeWeight / 2;
            bounds[2] -= bottomEdgeStrokeWeight / 2;
            bounds[3] -= rightEdgeStrokeWeight / 2;

        }

        return bounds;

    };


    /**
     * Returns a value based on the bottomEdgeStrokeWeight
     * of last header row of table. Because of the bug
     * https://indesign.uservoice.com/forums/601180-adobe-indesign-bugs/suggestions/46272076--script-api-indesign-table-height-is-incorrect-wh
     * where table.height is incorrect.
     * @version 2023-02-07
     * @param {Table} table - an Indesign table.
     * @returns {Number}
     */
    function getAnomalousExtraTableHeight(table) {

        var bottomEdgeStrokeWeight,
            anotherAnomalousPt = 1;

        for (var r = 0; r < table.rows.length; r++)
            if (table.rows[r].rowType == RowTypes.HEADER_ROW)
                bottomEdgeStrokeWeight = Math.max.apply(null, (table.rows[r].cells.everyItem().bottomEdgeStrokeWeight))
            else
                break;

        return bottomEdgeStrokeWeight ? (bottomEdgeStrokeWeight - anotherAnomalousPt) / 2 : 0;

    };


    /**
     * Attempts to return a Cell, given an object.
     * @author m1b
     * @version 2023-02-06
     * @param {InsertionPoint|Text|Cells|Table} obj - an object related to a Cell.
     * @returns {Cell}
     */
    function getCell(obj) {

        if (obj == undefined)
            return;

        if (obj.constructor.name == 'Cell')
            return obj;

        if (obj.parent.constructor.name == 'Cell')
            return obj.parent;

        if (
            obj.hasOwnProperty('cells')
            && obj.cells.length > 0
        )
            return obj.cells[0];

    };


};


app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Draw Rectangle Around Table");

 

 

Votes

Translate

Translate

Report

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 ,
Feb 07, 2023 Feb 07, 2023

Copy link to clipboard

Copied

Looks like a bug indeed.  When you convert the first row to a header row then set the header's bottom stroke, the first row after the headeris made taller to accommodate the stroke. But when you set the first row's bottom stroke, then convert the row to a header, the space isn't added to the first data row.

Votes

Translate

Translate

Report

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 ,
Feb 10, 2023 Feb 10, 2023

Copy link to clipboard

Copied

Good spotting! Well that means my script will sometimes incorrectly adjust for the anomaly! I guess I'll go back to my original technique, which seems to bypass the issue. - Mark

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

I've used Rob's method in the past and it works well. I've always wondered about that 1 point horizontal offset, it's probably some artefact. 

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

Hi Peter, it seems like a nominal padding —the insertion point before the table seems to be unusual in that you can’t change the kerning amount the way you can with other insertion points.

Votes

Translate

Translate

Report

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 ,
Feb 06, 2023 Feb 06, 2023

Copy link to clipboard

Copied

Yes, maybe. Just to be able to show the cursor. Anyway, it never really bothered me, just curious.

Votes

Translate

Translate

Report

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 ,
Apr 20, 2023 Apr 20, 2023

Copy link to clipboard

Copied

Hi @orib7317974 ,

see also in this discussion here where I posted a link to the TableCellBox.jsx script by Marc Autret:

https://community.adobe.com/t5/indesign-discussions/how-to-get-coordinates-of-table-column-or-cell/m...

 

Regards,
Uwe Laubender
( Adobe Community Expert )

Votes

Translate

Translate

Report

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 ,
Apr 21, 2023 Apr 21, 2023

Copy link to clipboard

Copied

LATEST

Thank you 🙂

Votes

Translate

Translate

Report

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