Skip to main content
Known Participant
March 1, 2025
Question

Script needs to work across unconnected pages

  • March 1, 2025
  • 3 replies
  • 549 views

Hello, I have a swap script which works. But if a user selects two objects on unconnected pages it fails to work. Any hints would be great. 

(Also any hints of how to swap the object layer positions too, as currently if something is index x and the other is index y it doesnt swap)

 

Best, 

 

Smyth

 

//AutoSwap
app.doScript(function () {
    if (app.selection.length !== 2) {
        alert("Please select exactly two objects.");
    } else {
        var firstObject = app.selection[0];
        var secondObject = app.selection[1];

        // Check if either object is a GraphicLine
        if (firstObject.constructor.name === "GraphicLine" || secondObject.constructor.name === "GraphicLine") {
            alert("Cannot swap selection with a rule.");
            return; // Exit the script if a GraphicLine is detected
        }

        // Ensure both objects are valid
        if (!firstObject.isValid || !secondObject.isValid) {
            alert("Invalid selection. Please select two valid objects.");
        } else {
            // Function to remember text frame settings
            function rememberTextFrameSettings(object) {
                if (object.constructor.name === "TextFrame") {
                    return {
                        verticalJustification: object.textFramePreferences.verticalJustification,
                        columnCount: object.textFramePreferences.textColumnCount,
                        isMultiColumn: object.textFramePreferences.textColumnCount > 1
                    };
                }
                return null;
            }

            // Function to apply text frame settings
            function applyTextFrameSettings(object, settings) {
                if (settings !== null) {
                    object.textFramePreferences.verticalJustification = settings.verticalJustification;
                    object.textFramePreferences.textColumnCount = settings.columnCount;
                }
            }

            // Remember text frame settings
            var firstSettings = rememberTextFrameSettings(firstObject);
            var secondSettings = rememberTextFrameSettings(secondObject);

            // Get the geometric bounds of both objects
            var firstBounds = firstObject.geometricBounds;
            var secondBounds = secondObject.geometricBounds;

            // Calculate width and height from geometric bounds
            var firstWidth = firstBounds[3] - firstBounds[1];
            var firstHeight = firstBounds[2] - firstBounds[0];
            var secondWidth = secondBounds[3] - secondBounds[1];
            var secondHeight = secondBounds[2] - secondBounds[0];

            // Function to compare width and height
            function areDimensionsEqual(width1, height1, width2, height2) {
                return Math.abs(width1 - width2) < 6 && Math.abs(height1 - height2) < 6; // Tolerance for floating-point precision
            }

            // Check if either object is a multi-column text frame
            var isFirstMultiColumn = firstSettings && firstSettings.isMultiColumn;
            var isSecondMultiColumn = secondSettings && secondSettings.isMultiColumn;

            // Handle swapping based on the type of objects
            if ((isFirstMultiColumn && !isSecondMultiColumn && secondObject.constructor.name === "Rectangle") ||
                (!isFirstMultiColumn && isSecondMultiColumn && firstObject.constructor.name === "Rectangle")) {
                // Swap geometric bounds only for multi-column text frame with picture
                firstObject.geometricBounds = secondBounds;
                secondObject.geometricBounds = firstBounds;
            } else {
                // Swap geometric bounds and apply text frame settings
                firstObject.geometricBounds = secondBounds;
                secondObject.geometricBounds = firstBounds;

                // Apply text frame settings only if both are text frames
                if (firstSettings && secondSettings) {
                    applyTextFrameSettings(firstObject, secondSettings);
                    applyTextFrameSettings(secondObject, firstSettings);
                }
            }

            // Handle grouped objects specifically
            function adjustGroupImagesPosition(group, offsetX, offsetY) {
                if (group.constructor.name === "Group") {
                    for (var i = 0; i < group.allPageItems.length; i++) {
                        adjustMediaPosition(group.allPageItems[i], offsetX, offsetY);
                    }
                }
            }

            // Function to adjust media position (images and graphics) within the new frame
            function adjustMediaPosition(object, offsetX, offsetY) {
                if ((object.constructor.name === "Rectangle" || 
                     object.constructor.name === "Oval" || 
                     object.constructor.name === "Polygon") && 
                    (object.images.length > 0 || object.graphics.length > 0)) {
                    
                    // Handle both images and graphics (including .eps)
                    var media = object.images.length > 0 ? object.images[0] : object.graphics[0];

                    // Adjust geometric bounds for all shapes
                    media.geometricBounds = [
                        media.geometricBounds[0] + offsetY,
                        media.geometricBounds[1] + offsetX,
                        media.geometricBounds[2] + offsetY,
                        media.geometricBounds[3] + offsetX
                    ];
                }
            }

            // Function to fit content in images/graphics with proportional adjustment for all shapes
            function fitContentInMediaProportionally(object, fitOption) {
                if ((object.constructor.name === "Rectangle" || 
                     object.constructor.name === "Oval" || 
                     object.constructor.name === "Polygon") && 
                    (object.images.length > 0 || object.graphics.length > 0)) {

                    object.fit(fitOption);  // Use proportional fitting
                } else if (object.constructor.name === "Group") {
                    for (var i = 0; i < object.allPageItems.length; i++) {
                        fitContentInMediaProportionally(object.allPageItems[i], fitOption);
                    }
                }
            }

            // Check if the dimensions (width and height) are equal
            if (areDimensionsEqual(firstWidth, firstHeight, secondWidth, secondHeight)) {
                // Calculate the offset between the first and second object
                var offsetX = secondBounds[1] - firstBounds[1];
                var offsetY = secondBounds[0] - firstBounds[0];

                // Adjust the image/graphic position for both objects
                adjustMediaPosition(firstObject, offsetX, offsetY);
                adjustMediaPosition(secondObject, -offsetX, -offsetY);

                // Adjust media positions within groups if necessary
                adjustGroupImagesPosition(firstObject, offsetX, offsetY);
                adjustGroupImagesPosition(secondObject, -offsetX, -offsetY);

            } else {
                // Use FILL_PROPORTIONALLY option for fitting media without stretching
                var fitOptions = FitOptions.FILL_PROPORTIONALLY;

                // Fit content in media in both objects using proportional fitting for all shapes
                fitContentInMediaProportionally(firstObject, fitOptions);
                fitContentInMediaProportionally(secondObject, fitOptions);

                // Adjust media positions within groups if necessary
                adjustGroupImagesPosition(firstObject, 0, 0);
                adjustGroupImagesPosition(secondObject, 0, 0);
            }
        }
    }
}, ScriptLanguage.JAVASCRIPT, null, UndoModes.ENTIRE_SCRIPT, "Swap Object Sizes and Positions");

3 replies

Robert at ID-Tasker
Legend
March 1, 2025

@SmythWharf

 

I can't check your code right now, but it's possible that your script is going high-wire, if you've "ruler origin" set to "per page" - and not "per Spread". 

 

https://www.indesignjs.de/extendscriptAPI/indesign-latest/#RulerOrigin.html 

 

Known Participant
March 1, 2025

Hello, 

 

The script fails to run when two items are selected on unconnected pages. It flags this the built in error claiming there needs to be two things selected. in short, it cant read one of the selections. (I never tested this as I could maybe add a debug at the start to print out all selections), but this is my hunch. 

It seems to only be able to pick up selections from any connect page(s), i.e. page single or spread

Known Participant
March 1, 2025

Hello, 

 

Silly me, it's an InDesign limitation, you simply cannot make selections of items which span across unconnected pages. 

 

For example I can only select one image at a time.

 

 

Robert at ID-Tasker
Legend
March 1, 2025

@SmythWharf

 

What error do you get? 

 

If by "swap" you mean "change location" - why not just move the selected objects? 

 

m1b
Community Expert
Community Expert
March 1, 2025

Hi @SmythWharf, just for starters, are *you* able to select items on different pages (edit: I was thinking spreads) at the same time? I don't know how to do that. - Mark

 

Edit: Ah I think I understand now... you mean two items on unconnected pages in the same spread. Of course. Sorry.

Known Participant
March 1, 2025

As in, two items from pages which have no connections. The quickest way to think about it is the pages panels, if there is a gap from one page to another or spread to a spread, I am unable to recognise both of these things as a selection in the script. It believes only one thing is being selected.

 

Hope this helps.

 

Robert at ID-Tasker
Legend
March 1, 2025

@SmythWharf

 

It's not possible to select items on two separate SPREADS.

 

Can you post some screenshots showing what you're trying to select?