Tackling Tables through Scripting
Tackle tables through Javascript, for instant formatting. And a bonus: download Jongware's Auto Layout Columns script!
With InDesign, you can make tables look pretty awesome, as there are many options to play with. But it’s a lot of work to apply this consistently to all of your tables, even if you use Table Styles. Table Styles also have their limitations, as they cannot apply all formatting. This article outlines how to use Javascript to do just about anything that you want, on just a single table or on every table throughout your document. Yes, this gets into some geeky scripting (don’t fear it! embrace it!), but even if you don’t want to learn how to script, you can download a great script that will help you manage tables, below.
Find which table to process
When you process tables, you need to make a decision: either you want to work on one single table and leave the rest unchanged, or you want something to apply to all tables. A single script can do both! For example, if you have a table selected, it could work on just that table; if none is selected, all are processed. How do you know if a table is selected? Let’s take a look at the Document Object Model when the text cursor is inside a table. Each of the framed objects is the parent object of the one above it.
There are different modes of selection in a table. You can have a simple text cursor blinking inside a cell; you can select a single cell, or an entire row or column; and you can have the whole table selected. To check where you are you can inspect the type of selection. If you find the user did not select the entire table, you move down the hierarchy until you find the ultimate parent ‘Table’, or in case the cursor wasn’t ‘inside’ a table after all something else. A short code fragment:
function checkWhichTable()
{
// ensure the user made a selection
if (app.selection.length != 1)
return null;
var currentTable = app.selection[0];
if (currentTable.hasOwnProperty("baseline"))
{
currentTable = app.selection[0].parent;
}
while (currentTable instanceof Cell || currentTable instanceof Row || currentTable instanceof Column)
currentTable = currentTable.parent;
if (!(currentTable instanceof Table))
{
// No table selected
return null;
}
return currentTable;
}
This code first assumes the text cursor is inside some text, so it would have the property ‘baseline’ (you can check for any property, as long as it’s one that is unique for the ‘Text’ object). If it is, the variable is updated to its parent, which — inside a table — should be a Cell. Now, as long as the variable points to either a Cell, Row, or Column, we keep moving down. At the very end, we should end up at an object of type Table.
This function tells us if you are ‘inside’ a single table or not. If you are, you can process this one; if you are not, you might want to stop and alert the user, or continue and process all tables. First, add the following, above the previous function:
app.doScript(checkUserSelection, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Process Table");
function checkUserSelection ()
{
var a_table = checkWhichTable();
if (a_table == null)
{
if (confirm("No table selected. Do you want to process *all* tables?") == false)
return;
allTables = app.activeDocument.stories.everyItem().tables.everyItem().getElements();
for (aTable=0; aTable<allTables.length; aTable++)
{
processTable (allTables[aTable]);
}
} else
{
processTable (a_table);
}
}
function processTable(table)
{
// do something here!
}
Then all you need to add is add code function processTable, as this is where you do ‘something’. So what can you do? Just about anything you want!
All of the code snippet examples below need to appear inside this function (I’m not repeating the function code for each of the snippets).
Set Table Options
In the function processTable you can change basic table parameters — this is equal to making changes in InDesign’s ‘Table Options’ dialog box. For instance, to set the ‘Space Before’ value, use this:
table.spaceBefore = "2mm";
See https://jongware.mit.edu/idcs6js/pc_Table.html for the entire list of table properties.
Handle Rows and Columns
If you have a variable pointing to a table, you can use simple indexing to access its columns and rows. For example, table.rows[0] indicates the very first row, so to set its fill color to a swatch called ‘Header’, you can do this:
table.rows[0].fillColor = "Header";
Other useful functions: add an empty row at the bottom
table.rows.add(LocationOptions.AT_END);
set the height of all rows
table.rows.everyItem().height = "36pt";
add a line below the first row
table.rows[0].bottomEdgeStrokeColor = "Black";
table.rows[0].bottomEdgeStrokeWeight = 0.75;
Change Individual Cells
Cells contain an enormous amount of formatting: the full list at https://jongware.mit.edu/idcs6js/pc_Cell.html shows there are more than 80 different properties to play with. Actually, the list is longer than that, but remember that you can only change the ones that are marked ‘r/w’ (for ‘read/write’).
Basic properties you might want to change are these:
Similar to leftInset, you can also use rightInset, topInset, and bottomInset. In the place of leftEdgeXxx (and the other three directions), you can change any of the properties StrokeColor, StrokeTint, StrokeWeight, StrokeType and some more (see the web page for the full list) — this could be topEdgeStrokeWeight, rightEdgeStrokeColor, etc. Note that the same rules apply as when changing strokes in the interface: changing the left stroke of a cell is the same as changing the right stroke of the cell to its left.
Cells are counted left-to-right, top-to-bottom. To add a diagonal line to the very first cell, use this:
table.cells[0].topLeftDiagonalLine = true;
You can apply the same formatting to all cells like this:
table.cells.everyItem().topInset = "1mm";
If you want to apply more than one change, you don’t have to repeat this line for every single change; you can apply them all at once.
table.cells.everyItem().properties = {
topInset:"1mm", bottomInset:"1.5mm",
leftEdgeStrokeColor:"Black"
};
You can still use and apply Cell Styles, of course. For instance:
table.cells.everyItem().appliedCellStyle = "MyCellStyle";
or event apply them selectively, such as on all even rows:
for (i=0; i<table.rows.length; i+=2)
{
table.rows[i].cells.everyItem().appliedCellStyle = "MyCellStyle";
}
Text Inside a Cell
Sometimes you need to act on the text inside a cell. For example, perhaps you want to set the background fill to red if it contains a negative number. You can access text using the cell’s .texts property:
for (i=0; i<table.cells.length; i++)
{
if (table.cells[i].texts[0].contents[0] == "-")
table.cells[i].fillColor = "Red";
}
However, note that the .texts property is an array (for unknown reasons)! It always has one single element, whether or not there is actually some text in the cell. If you need to process empty cells only (or the reverse, only cells that contain text), you can test its length:
for (i=0; i<table.cells.length; i++)
{
if (table.cells[i].texts[0].length == 0)
table.cells[i].fillColor = 'Red';
}
Resizing a Table
Now you know a script can access the text inside a table, and you know it can read and change the width of each column. Therefore, it is possible to check the width of the text inside each column and adjust the column widths so any remaining space is divided evenly across the columns — instant auto-layout! However, it’s a large script, so I’m not going to type it here. Instead, here is a download link: AutoformatTables.jsx (it’s a ZIP file, so you might need to unpack it manually after downloading).
The script makes all non-empty columns as narrow as possible without any line breaks. It then checks if this makes the table fit inside the original margins, and if so it divides the remaining space and is done. If not, it makes all columns as narrow as possible without having any overset text. If that’s not possible either, then there is nothing the script can do and it gives up.
Totally empty columns are kept unchanged; that way, you can manually add spacer columns where necessary.
Because the contents of each cell needs to be checked several times, the script may take a while to work on larger tables. It works best with ‘simple’ tables; it cannot work with rotated tables, lots of rotated single cells, or complicated split-and-merged combinations.
That’s it! You can freely combine the code snippets above, and write either a fully automated table formatting script, or just a small script to adjust one tiny aspect of all of your tables. Have fun!
Please remember that it’s not very useful to ask in the ‘Comments’ section below how to do a specific task which I didn’t discuss. Any questions that require more than a single line of code are better put in the Scripting Forum.
This article was last modified on April 11, 2022
This article was first published on April 8, 2013


