Skip to main content
This topic has been closed for replies.

2 replies

m1b
Community Expert
Community Expert
February 11, 2023

Hi @MonishaRajendran, I had a look at your script, but I didn't quite understand your approach. Instead I have written a script to do an "autofit" type of operation on the table, which I think might be what you want.

Here is your sample document:

Note that I put line breaks in a couple of the header cells to match your desired result screenshot. Do this before running the script. Also make sure that the table isn't overset (ie. make sure the end of the table is visible in a text frame).

- Mark

 

The script:

 

/**
 * Auto-fit Selected Table
 * @author m1b
 * @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.hasOwnProperty('parent')
        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;

};

 

Edit 2023-02-12: I've added a "minimumWidth" parameter. If you use this, then the script will first auto-fit, so there is no wasted space, then will extend every column by the same amount so that the table matches the minimumWidth amount.

Inspiring
February 15, 2023

Hi,

I need to fit the table to the page but as of now I can't able to fit the table using the script I have used.

 

 

-Monisha
m1b
Community Expert
Community Expert
February 15, 2023

Hi @MonishaRajendran, I'm afraid your script is too complicated for me to debug. I suggest you either create a simple version that just has the section that you want to ask about, or look at my script and see how I did it. Or maybe I am not understanding your needs at all, in which case my apologies, and please try to be much more specific with your question.

- Mark

m1b
Community Expert
Community Expert
February 10, 2023

Hi @MonishaRajendran, I had a look at your sample file and script. What exactly are you trying to do to the table? Scale it down to fit margins width? Adjust every column width to fit margins width?

 

When you are asking for help with a script it is often helpful for you to post a screenshot of your desired result, also. That way we don't have to guess what you mean.

- Mark

Inspiring
February 10, 2023

Hi, 

My first goal is to fit the table into the frame. Currently, I am unable to fit the table inside the frame. I have attached the screenshot that I wanted to achieve using my script.

 

 

-Monisha