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

How to autofit cell in table InDesign?

New Here ,
Oct 18, 2024 Oct 18, 2024

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.

 
TOPICS
UXP Scripting
305
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 ,
Oct 18, 2024 Oct 18, 2024
LATEST

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.

Screenshot-2023-02-11-at-20.56.24.gif

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

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