Skip to main content
Participating Frequently
July 17, 2021
Answered

Arrange an array of different text boxes to fit in pages

  • July 17, 2021
  • 4 replies
  • 1028 views

Hi,

I have 40 text boxes which vary in height. 

I was looking for a script that automatically arranges the text boxes to fit in a page (A4 size). 

(1) The order of text boxes doesn't matter.

(2) The number of text boxes fitted in a page doesn't matter: Some pages can have, say, two text boxes, and some seven to ten boxes. 

(3) The text boxes arranged in a page do not have to fill the page exactly. 

 

I'd appreciate any clues. Thanks. 

This topic has been closed for replies.
Correct answer m1b

Hi again, I've had a play with some code and got this. It will accept an array of values and output an array of arrays of values whose sum do not exceed the specified capacity. By entering values denoting lengths in mm and capacity of 297 (length of A4 page in mm), you should have a basis to do your text frame layout script. Haven't tested much myself, so please let us know how it works out, or if you need more help.

 

 

/*
    by m1b at https://community.adobe.com/t5/indesign/arrange-an-array-of-different-text-boxes-to-fit-in-pages/m-p/12182653#M437483
    adapted from code found at https://www.geeksforgeeks.org/bin-packing-problem-minimize-number-of-used-bins/
*/

// example values (here representing lengths in mm)
var values = [125, 200, 205, 280, 30, 40, 170, 45, 77];

// capacity of each bin (here representing length of A4 page in mm)
var capacity = 297;

// get the result
var fittedLengths = getPackedBins(values, capacity);

// show the result as A4 pages
$.writeln("Number of pages required: " + fittedLengths.length);

for (var i = 0; i < fittedLengths.length; i++) {
    $.writeln('Page ' + (i + 1) + ' lengths: ' + fittedLengths[i].join(' mm + ') + ' mm  (' + (capacity - sumArray(fittedLengths[i])) + 'mm remaining)');
}


function getPackedBins(values, binCapacity) {

    // sort values, descending order
    values = values.sort(function (a, b) { return b - a });

    // start with no bins
    var bins = [];
    // array to keep track of remaining capacity
    var binRemainder = new Array(values.length); for (var i = 0; i < values.length; ++i) binRemainder[i] = 0;

    // place each item in a bin
    for (i = 0; i < values.length; i++) {

        var j,
            value = values[i],
            freeSpace = binCapacity,
            bestBinIndex = 0;

        // find the best bin that can accommodate value
        if (value > binCapacity) $.writeln('Value "' + value + '" cannot fit into an empty bin.');

        // initialize minimum space left and index of best bin

        for (j = 0; j < bins.length; j++) {
            if (binRemainder[j] >= value && binRemainder[j] - value < freeSpace) {
                bestBinIndex = j;
                freeSpace = binRemainder[j] - value;
            }
        }

        // can't fit value in any bin, so create a new bin
        if (freeSpace == binCapacity) {
            binRemainder[bins.length] = binCapacity - value;
            bins.push([value]);
        } else {
            // put value in best bin
            binRemainder[bestBinIndex] -= value;
            bins[bestBinIndex].push(value);
        }

        // uncomment this line to see the bins array populate over time
        // $.writeln('bins = [' + bins.join('], [') + ']');
    }
    return bins;
}

// only used for displaying the remainders
function sumArray(a) {
    var sum = 0;
    for (var i = 0; i < a.length; ++i) sum += a[i];
    return sum;
}

 

 

By the way, this is the result for me, written to the console:

Number of pages required: 5
Page 1 lengths: 280 mm  (17 mm remaining)
Page 2 lengths: 205 mm + 77 mm  (15 mm remaining)
Page 3 lengths: 200 mm + 45 mm + 40 mm  (12 mm remaining)
Page 4 lengths: 170 mm + 125 mm  (2 mm remaining)
Page 5 lengths: 30 mm  (267 mm remaining)

4 replies

Michael Bullo
Community Expert
Community Expert
July 18, 2021

The following is not a script and it doesn't quite do what you want but it could prove useful if you didn't already know...

 

Click and drag out a text box. Before releasing the mouse, tap the up/down arrow keys to add/remove text boxes in the vertical direction and tap the right/left arrow keys to add/remove text boxes in the horizontal direction.

m1b
Community Expert
Community Expert
July 18, 2021

Thanks Michael, that's really cool! Is there a place to edit the default gutter between boxes?

Michael Bullo
Community Expert
Community Expert
July 18, 2021

Thanks mate. I don't know how to edit the default gutter. I'd be keen to know that one myself.

 

The Gap Tool could help you out here. By default, the Gap Tool will move gaps. However, if you hold down Command/Control you can resize gaps, even down to 0 extremely quickly.

m1b
Community Expert
m1bCommunity ExpertCorrect answer
Community Expert
July 18, 2021

Hi again, I've had a play with some code and got this. It will accept an array of values and output an array of arrays of values whose sum do not exceed the specified capacity. By entering values denoting lengths in mm and capacity of 297 (length of A4 page in mm), you should have a basis to do your text frame layout script. Haven't tested much myself, so please let us know how it works out, or if you need more help.

 

 

/*
    by m1b at https://community.adobe.com/t5/indesign/arrange-an-array-of-different-text-boxes-to-fit-in-pages/m-p/12182653#M437483
    adapted from code found at https://www.geeksforgeeks.org/bin-packing-problem-minimize-number-of-used-bins/
*/

// example values (here representing lengths in mm)
var values = [125, 200, 205, 280, 30, 40, 170, 45, 77];

// capacity of each bin (here representing length of A4 page in mm)
var capacity = 297;

// get the result
var fittedLengths = getPackedBins(values, capacity);

// show the result as A4 pages
$.writeln("Number of pages required: " + fittedLengths.length);

for (var i = 0; i < fittedLengths.length; i++) {
    $.writeln('Page ' + (i + 1) + ' lengths: ' + fittedLengths[i].join(' mm + ') + ' mm  (' + (capacity - sumArray(fittedLengths[i])) + 'mm remaining)');
}


function getPackedBins(values, binCapacity) {

    // sort values, descending order
    values = values.sort(function (a, b) { return b - a });

    // start with no bins
    var bins = [];
    // array to keep track of remaining capacity
    var binRemainder = new Array(values.length); for (var i = 0; i < values.length; ++i) binRemainder[i] = 0;

    // place each item in a bin
    for (i = 0; i < values.length; i++) {

        var j,
            value = values[i],
            freeSpace = binCapacity,
            bestBinIndex = 0;

        // find the best bin that can accommodate value
        if (value > binCapacity) $.writeln('Value "' + value + '" cannot fit into an empty bin.');

        // initialize minimum space left and index of best bin

        for (j = 0; j < bins.length; j++) {
            if (binRemainder[j] >= value && binRemainder[j] - value < freeSpace) {
                bestBinIndex = j;
                freeSpace = binRemainder[j] - value;
            }
        }

        // can't fit value in any bin, so create a new bin
        if (freeSpace == binCapacity) {
            binRemainder[bins.length] = binCapacity - value;
            bins.push([value]);
        } else {
            // put value in best bin
            binRemainder[bestBinIndex] -= value;
            bins[bestBinIndex].push(value);
        }

        // uncomment this line to see the bins array populate over time
        // $.writeln('bins = [' + bins.join('], [') + ']');
    }
    return bins;
}

// only used for displaying the remainders
function sumArray(a) {
    var sum = 0;
    for (var i = 0; i < a.length; ++i) sum += a[i];
    return sum;
}

 

 

By the way, this is the result for me, written to the console:

Number of pages required: 5
Page 1 lengths: 280 mm  (17 mm remaining)
Page 2 lengths: 205 mm + 77 mm  (15 mm remaining)
Page 3 lengths: 200 mm + 45 mm + 40 mm  (12 mm remaining)
Page 4 lengths: 170 mm + 125 mm  (2 mm remaining)
Page 5 lengths: 30 mm  (267 mm remaining)
m1b
Community Expert
Community Expert
July 17, 2021

You could try googling something like "one dimensional bin packing problem javascript" as a starting point. Interesting problem!

Participating Frequently
July 17, 2021

Thanks for the suggestion. Will do.

Participating Frequently
July 17, 2021

Forgot to mention one more thing: The text boxes must touch each other.