Skip to main content
Shiv Allva
Known Participant
May 6, 2022
Question

Javacript: How to duplicate a table header row (in InDesign Server version)?

  • May 6, 2022
  • 5 replies
  • 2356 views

I created this code for duplicating a header row into body row, but unfortunately, this code doesn't go well with the InDesign server version, since the .select() method is not available (but works okay in desktop version) 

// create a simple table with one header row, put the cursor on the FIRST cell and run this code
table = app.selection[0].parent;
table.rows[0].select();
app.copy();
var newRow = table.rows.add(LocationOptions.AFTER, table.rows[0], {rowType:RowTypes.BODY_ROW});
newRow.select();
app.paste();


Is there any way to copy a HEADER_ROW, create a new BODY_ROW and paste the copied HEADER_ROW into the selected BODY_ROW? Later, I would do some changes to the body_row.

 

Any idea which will work in the Server version? Please let me know your thoughts?

This topic has been closed for replies.

5 replies

dublove
Legend
August 17, 2024

Duplicate table header rows?
Did you corner the problem?

Robert at ID-Tasker
Legend
August 17, 2024
quote

Duplicate table header rows?
Did you corner the problem?


By @dublove

 

This thread is about Server version of InDesign. 

 

dublove
Legend
August 17, 2024

For local InDesign
How to write the code to duplicate the first row of a table.
Then create a new row before those two rows.

Community Expert
June 20, 2022

Hi Mark,

just tested your new version of the code v2022-06-19 with one of my test tables.

It contents a special "condition" so to say. A graphic cell where I rotated the container frame for an image.

 

I get inconsistent results with two variations.

[1] Frame has no image

[2] Frame has an image

Both results are wrong. And different.

I could tell from some experiments with cell.select() and copy/paste that even that will not get you very far.

In fact, InDesign was crashing as I tried to paste one of the two columns. It was the "easier" one with two text cells and no contents at all to a column that contained a graphic cell.

 

Details below.

On the left the original table, on the right the result after running code v2022-06-19 with:

 

// Text frame with table selected:

var doc = app.documents[0];
var table = app.selection[0].parentStory.tables[0];

var movedColumn = 
moveRowOrColumn
(
	table.columns[1] , 
	LocationOptions.BEFORE , 
	table.columns[0]
);

 

Top table.

Container frame in graphic cell rotated to 15°.

Container frame selected.

 

Result top table. Container frame selected.

Note, that the red rectangle is now inside of that container frame!

 

Result top table. Contents of container frame selected:

 

 

Now on to the tables at the bottom where I filled the rotated container frame of the graphic with an image.

 

Bottom table.

Selected: Container frame in graphic cell rotated to 15°

 

Bottom table. Selected: Contents of the container frame in graphic cell:

 

Result of bottom table. Selected: Container frame in graphic cell

 

Result of bottom table. Selected: Contents of container frame in graphic cell

 

 

Well, food for thoughts…

 

Regards,
Uwe Laubender

( ACP )

Community Expert
June 20, 2022

Copy/paste selected columns with the GUI. Left the source, right the result:

This time this also was working for my second table without crashing InDesign 2022 version 17.3.0 on Windows 10. Maybe I did something differently? Perhaps I was changing my selection more slow or I had the order differently.

Phase 1, copy column 2 to columns 1:

Phase 2, copy column 1 to column 2:

 

Regards,
Uwe Laubender
( Adobe Community Professional )

Community Expert
June 20, 2022

Here the sample InDesign document from my Dropbox account:

https://www.dropbox.com/s/doyakxa5p8l46hj/DupTableColumnsOrRows-SAMPLES-2022.indd?dl=1

 

Regards,
Uwe Laubender
( Adobe Community Professional )

m1b
Community Expert
Community Expert
May 7, 2022

Hi @Shiv Allva, the problem here is that Table Rows (and Columns) don't have a duplicate method. You are using select/copy/paste to solve this problem, which doesn't work on the server version as you mention.

 

Have a look at this function I've written called duplicateRowOrColumn. It is my attempt at creating the equivalent functionality to the missing method, and I hope it will work in your case. See my example 1 in my code. (Example 2 is commented out but shows a different usage.) Script isn't well tested at all, so let me know if you have problems with it.

- Mark

(Screenshot above shows little sample table with example 1 AND example 2 both run at once.)

 

 

 

 

/*
    Move Or Duplicate Row Or Column
    for Adobe Indesign 2022

    Utility function to move or duplicate a table row or column

    by m1b
    here: https://community.adobe.com/t5/indesign-discussions/javacript-how-to-duplicate-a-table-header-row-in-indesign-server-version/m-p/12926231
    and here: https://community.adobe.com/t5/indesign-discussions/swapping-columns-in-indesign-with-a-java-script/m-p/13014567

    v2022-06-19
    • added move function
    • added idea by @rob day to simplify the setting of cell properties.
*/


function main() {

    var doc = app.documents[0],
        table = doc.textFrames[0].parentStory.tables[0];


    /*
        example 1:
        duplicate first row after itself
    */
    var newRow = duplicateRowOrColumn(table.rows[0], LocationOptions.AFTER);
    // then convert to body row
    if (newRow != undefined && newRow.isValid)
        newRow.rowType = RowTypes.BODY_ROW;


    /*
        example 2:
        swap first and second columns
    */
    var movedColumn = moveRowOrColumn(table.columns[1], LocationOptions.BEFORE, table.columns[0]);



    /**
     * Duplicate a table Row or Column
     * @dependency moveRowOrColumn function
     * @param {Row|Column} rowOrColumn - the row or column to duplicate
     * @param {LocationOptions} [locationOption] - LocationOptions.AFTER or LocationOptions.BEFORE
     * @param {Row|Column} [referenceRowOrColumn] - after or before this row or column
     * @returns {Row|Column} - the duplicate row or column
     */
    function duplicateRowOrColumn(rowOrColumn, locationOption, referenceRowOrColumn) {
        return moveRowOrColumn(rowOrColumn, locationOption, referenceRowOrColumn, true);
    }

    /**
     * Move a table Row or Column
     * Note: because there is no way to natively
     * move a row or column, the function creates
     * a new, matching, column in the new position
     * and removes the old one. Therefore, anyone
     * using the function must replace their existing
     * reference with the returned row or column.
     * eg. var myColumn = moveRowOrColumn(myColumn);
     * @param {Row|Column} rowOrColumn - the row or column to duplicate
     * @param {LocationOptions} [locationOption] - LocationOptions.AFTER or LocationOptions.BEFORE
     * @param {Row|Column} [referenceRowOrColumn] - after or before this row or column
     * @param {Boolean} [duplicate] - if true, duplicate rowOrColumn
     * @returns {Row|Column} - the moved row or column
     */
    function moveRowOrColumn(rowOrColumn, locationOption, referenceRowOrColumn, duplicate) {
        // defaults:
        locationOption = locationOption || LocationOptions.AFTER;
        referenceRowOrColumn = referenceRowOrColumn || rowOrColumn;
        duplicate = (duplicate === true);

        if (!rowOrColumn.isValid) {
            alert('Duplicate Row or Column: rowOrColumn is invalid.');
            return;
        }

        var type = rowOrColumn.constructor.name,
            cells = rowOrColumn.cells.everyItem().getElements(),

            // make the new row or column
            newRowOrColumn = (
                type == 'Row'
                    ? table.rows
                    : table.columns
            ).add(locationOption, referenceRowOrColumn),

            newCells = newRowOrColumn.cells.everyItem().getElements();

        // loop over each cell
        for (var i = 0; i < newCells.length; i++) {

            // copy properties to new cell
            newCells[i].properties = cells[i].properties;

            // except contents, we need to do that manually
            newCells[i].contents = '';
            if (cells[i].cellType == CellTypeEnum.TEXT_TYPE_CELL) {
                // duplicate text
                cells[i].texts[0].duplicate(LocationOptions.AFTER, newCells[i].texts[0]);
            }

            else if (cells[i].cellType == CellTypeEnum.GRAPHIC_TYPE_CELL) {
                // duplicate graphic cell
                newCells[i].convertCellType(CellTypeEnum.GRAPHIC_TYPE_CELL);
                var rect = cells[i].allPageItems[0],
                    newRect = newCells[i].allPageItems[0];
                // duplicate content
                newRect.contentPlace(rect);

                // position content
                if (rect.pageItems[0].isValid) {
                    var delta = getRelativePosition(rect.pageItems[0], rect),
                        newDelta = getRelativePosition(newRect.pageItems[0], newRect),
                        deltaMove = getRelativePosition([delta[1], delta[0]], [newDelta[1], newDelta[0]]);
                    newRect.pageItems[0].move(undefined, deltaMove);
                }
            }
        }

        // remove old rowOrColumn
        if (duplicate !== true)
            rowOrColumn.remove();

        return newRowOrColumn;

        /**
         * Calculates relative position between two items or bounds
         * @param {PageItem|Array} item1 - can be PageItem or bounds [T,L,B,R]
         * @param {PageItem|Array} item2 - can be PageItem or bounds [T,L,B,R]
         * @returns {Array} - [x,y]
         */
        function getRelativePosition(item1, item2) {
            var b1 = item1.hasOwnProperty('geometricBounds')
                ? item1.geometricBounds
                : item1;
            var b2 = item2.hasOwnProperty('geometricBounds')
                ? item2.geometricBounds
                : item2;
            if (
                b1.constructor.name == 'Array'
                && b2.constructor.name == 'Array'
            )
                return [b2[1] - b1[1], b2[0] - b1[0]];
        }

    } // end duplicateRowOrColumn

}

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Move Or Duplicate Row Or Column");

 

 

 

Edit: Updated script to handle moving as well as duplicating, after answering this question, and added @rob day's way of applying cell properties.

m1b
Community Expert
Community Expert
May 29, 2022

Hi @Shiv Allva, did you solve this problem? - Mark

rob day
Community Expert
Community Expert
May 6, 2022

Hi @Shiv Allva , why not just make the new row and set its contents to the header’s contents, or do you need to style it the same way?

 

//a document with one table
var table = app.documents[0].textFrames[0].parentStory.tables[0];
var newRow = table.rows.add(LocationOptions.AFTER, table.rows[0]);
newRow.properties = {rowType:RowTypes.BODY_ROW, contents:table.rows[0].contents}
Community Expert
May 6, 2022

Hi @Shiv Allva,

You will have to think which table you want to select. In your current code you are working with selection to identify the table of interest but on the server you will have to think of another logic to identify the table.

If it's just a single table in the document then you can use something like the following

 

var table = app.documents[0].tables[0]

 

Alternatively, you can iterate all the tables in the document using the above collection and find the table that is of interest to you. The criteria to choose the table will have to be thought by you based on your workflow.

-Manan

-Manan
Shiv Allva
Known Participant
May 6, 2022

@Manan Joshi That was just a sample codesnippet. But the query is how to duplicate the first header_row as body_row? Problem here is Server version doesn't have .select(). So want to know if there is an alternative way to achieve this solution in server?