Copy link to clipboard
Copied
Hi:
We want to create a Script that scans each cell in a given table, calculates the width of the text within each cell based on the number of characters, and then assigns that width to the cell.
Copy link to clipboard
Copied
Hi @Imaginative_Charm5E91, I had previously written a script that performs an autofit operation on a whole table. It is ExtendScript, not UXP, so my apologies if you specifically wanted UXP, however it may help you anyway.
This is the script:
/**
* Auto-fit Selected Table
* @author m1b
* @version 2023-02-25
* @discussion https://community.adobe.com/t5/indesign-discussions/fit-table-using-frame-to-content/m-p/13569385
*/
function main() {
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
var selectedTable = getTable(app.selection[0]);
if (
selectedTable == undefined
|| !selectedTable.isValid
) {
alert('Please select a table or inside a table and run script again.');
return;
}
autoFitTable(selectedTable);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Auto-fit Table');
/**
* Auto-fits the table: attempts to minimize
* the width of each column, without changing
* existing line breaks, similar to Microsoft
* Excel's auto-fit function.
* @author m1b
* @version 2023-02-12
* @param {Table} table - an Indesign Table.
* @param {Number} [minimumWidth] - the minimum desired table width, in pts (default: 0).
*/
function autoFitTable(table, minimumWidth) {
minimumWidth = minimumWidth || 0;
var bounds = getCellBounds(table.cells[0], 'geometricBounds'),
tableWidth = 0,
columnData = [];
// in this loop we collect all the
// measurements we'll need later on
columnsLoop:
for (var c = 0, left = bounds[1]; c < table.columns.length; c++) {
var column = table.columns[c],
columnCells = column.cells,
columnWidth = Number(table.columns[c].width);
columnData[c] = {
left: left,
leftInset: Math.max.apply(null, columnCells.everyItem().textLeftInset),
right: left + columnWidth,
rightInset: Math.max.apply(null, columnCells.everyItem().textRightInset),
maxLineMeasure: -Infinity,
maxLineIndex: undefined,
maxLineCellID: undefined,
maxLineContents: undefined,
width: columnWidth,
};
cellsLoop:
for (var i = 0; i < columnCells.length; i++) {
linesLoop:
for (var j = 0; j < columnCells[i].lines.length; j++) {
var cell = columnCells[i];
if (cell.texts[0].parentTextFrames.length == 0) {
alert('Some rows of this table appear to be overset. Please fix and try again.');
return;
}
var line = cell.lines[j],
start = line.insertionPoints[0].horizontalOffset,
end = start;
if (line.insertionPoints.length == 1)
end = line.insertionPoints[-1].horizontalOffset;
else
end = Math.max(line.insertionPoints[-1].horizontalOffset, line.insertionPoints[-2].horizontalOffset);
var lineMeasure = end - start;
if (lineMeasure > columnData[c].maxLineMeasure) {
columnData[c].maxLineMeasure = lineMeasure;
columnData[c].maxLineIndex = j;
columnData[c].maxLineCellID = cell.id;
columnData[c].maxLineContents = line.contents;
}
}
}
// set the column width
columnData[c].width = (columnData[c].maxLineMeasure + columnData[c].leftInset + columnData[c].rightInset);
// advance for the next column
left += columnWidth;
}
// this loop is to actually change the column widths
for (var c = table.columns.length - 1; c >= 0; c--) {
table.columns[c].width = columnData[c].width;
// If the longest line's contents no longer matches,
// gradually open up the column again until it matches.
// This is mostly to fix hyphenated words no longer
// being hyphenated at the new width.
var cell = table.cells.itemByID(columnData[c].maxLineCellID),
line = cell.lines[columnData[c].maxLineIndex],
counter = 100; // count down for safety
while (
line.contents !== columnData[c].maxLineContents
&& counter--
) {
table.columns[c].width += 0.5;
line = cell.lines[columnData[c].maxLineIndex];
}
// update the column data, in case the width changes
columnData[c].width = Number(table.columns[c].width);
// update the table width
tableWidth += columnData[c].width;
}
if (minimumWidth > tableWidth) {
// take the edge strokes into account so that
// a minimum width means the table will fit exactly
// in a textframe of that size.
var leftEdgeStrokeWeight = Math.max.apply(null, (table.columns.item(0).cells.everyItem().leftEdgeStrokeWeight)),
rightEdgeStrokeWeight = Math.max.apply(null, (table.columns.item(-1).cells.everyItem().rightEdgeStrokeWeight));
minimumWidth -= (leftEdgeStrokeWeight / 2) + (rightEdgeStrokeWeight / 2);
// the amount to add to each column
var adjustment = (minimumWidth - tableWidth) / table.columns.length;
// make the adjustment
for (var c = table.columns.length - 1; c >= 0; c--)
table.columns[c].width += adjustment;
}
};
/**
* Returns the geometricBounds of the cell;
* @author m1b
* @version 2023-02-06
* @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;
var cellIndex = cell.index,
tableIndex = indexOf(table, table.storyOffset.parentTextFrames[0].tables),
// duplicate the textframe because we are going to convert the cell
dupTextFrame = table.storyOffset.parentTextFrames[0].duplicate(),
dupCell = dupTextFrame.tables[tableIndex].cells[cellIndex];
// convert cell to graphic, so we can get the bounds
dupCell.convertCellType(CellTypeEnum.GRAPHIC_TYPE_CELL);
var bounds = dupCell.rectangles[0].geometricBounds;
// clean up
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;
};
/**
* Returns index of obj in arr.
* Returns -1 if not found.
* @param {any} obj
* @param {Array} arr
* @returns {Number}
*/
function indexOf(obj, arr) {
for (var i = 0; i < arr.length; i++)
if (arr[i] === obj)
return i;
return -1;
};
/**
* 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 getTable(obj) {
if (obj == undefined)
return;
if (obj.constructor.name == 'Cell')
return obj.parent;
if (obj.parent.constructor.name == 'Cell')
return obj.parent.parent;
if (
obj.hasOwnProperty('cells')
&& obj.cells.length > 0
)
return obj.cells[0].parent;
};
- Mark
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more