Search code examples
javascriptadobeadobe-indesignextendscript

Indesign - Error 27 "Stack overrun" with app.doScript


I'm trying to automate most of my workplace tasks, but I'm somehow stuck.
Part of job consists in placing various photos into an InDesign document for nesting them and creating pdfs to be printed on special custom machines.
Each photo is accompanied by a paper order marked with a order number ranging from 5 to 8 digits and a barcode containing this order number and other information.
The filename of the images contains the order number and I have to place them manually.
Since it's a very time consuming task I thought I'd use a barcode scanner to enter the order barcode (deleting the information I don't need and keeping only the order number) and search inside the photo folder the corresponding image to insert it automatically.
To make it faster I thought of continuously restarting the script in order to insert them one after the other, but after inserting exactly 39 images, this gives me error 27: Stack overrun.
Is there any way to fix this error? In case I have to rewrite the whole script, how do you recommend to do it?
This is the script:

    #target "InDesign"
    #targetengine "session"
    if (app.documents.length == 0) {MyImages
        alert ("Open a document first");
        exit ();
        }

//  I've made some research on internet and apparently some user solved the problem increasing the memCache, but it doesn't work in my case

    $.memCache = 9999999999;
    app.scriptPreferences.enableRedraw = false;
    var MyDoc = app.activeDocument;
    MyDoc.viewPreferences.horizontalMeasurementUnits = MeasurementUnits.millimeters;
    MyDoc.viewPreferences.verticalMeasurementUnits = MeasurementUnits.millimeters;
    app.viewPreferences.rulerOrigin = RulerOrigin.PAGE_ORIGIN;
    MyDoc.viewPreferences.rulerOrigin = RulerOrigin.PAGE_ORIGIN;
    MyDoc.zeroPoint = [0,0];
    var MyFolder, MyImages, ImgNumber;

//  RestartScript is used to prevent the script from restarting with app.doScript (line 70) if the cancel button is pressed

    var RestartScript = 0;
    var MyPanel = new Window ("dialog", "Enter the barcode");
    var MyText = MyPanel.add ("edittext");
    MyText.preferredSize.width = 150;
    MyText.onChanging = function () {
        MyText.text = MyText.text.replace (/[^0-9]/g, "");

//  This part allows the script to continue automatically once the barcode scanner enters the code which is 15 characters long

        if (MyText.text.length >= 15) {
            BarcodeConversion ();
            MyPanel.close ();
            }
        }

//  The EventListener allows a manual insertion of the image number (if the barcode is not present on the order)

    MyText.addEventListener ("keydown", function (KeyEvent) {
        if (KeyEvent.keyName == "Enter") {
            if (MyText.text != "") {
                ImgNumber = MyText.text;
                MyPanel.close ();
                }
            }
        });

//  The focus is on the edittext for practical purposes

    MyText.active = true;
    var Browse = MyPanel.add ("button", undefined, "Browse");
    Browse.onClick = function () {
        MyFolder = Folder.selectDialog ("Select a folder", " ");
        MyImages = MyFolder.getFiles (/.+\.(?:gif|jpe?g|eps|tiff?|psd|pdf|bmp|png)$/i);
        if (MyImages.length <= 0) {
            alert ("There are no images in this folder");
            return;
            }

//  This is for focusing on the edittext automatically after clicking the browse button, but it doesn't work (I don't know why)

        MyText.active = true;
        }
    var Cancel = MyPanel.add ("button", undefined, "Cancel");
    Cancel.onClick = function () {
        MyPanel.close ();
        }
    MyPanel.show ();
    if (ImgNumber != undefined) {
        Search ();
        }

//  I'm using app.doScript to restart the same script and automatically insert multiple images in a continuous way (I've tried with MyPanel.show() instead of this to reopen the input panel but it doesn't display well)
//  This is what causes the stack overrun error

    if (RestartScript == 1) {
        app.doScript (new File (app.activeScript.parent.fsName + "\\" + app.activeScript.name) ,ScriptLanguage.JAVASCRIPT);
        }


    function BarcodeConversion () {
        if (MyFolder == undefined) {
            alert ("Folder not selected");
            MyText.text = "";
            return;
            }

//  Here I use slice to remove the part of the barcode I don't need, obtaining the order's number

        ImgNumber = MyText.text.slice (4, 12);
        }


    function Search () {

//  In this function I'm placing all the images with the filename who correspond to the order's number in an array (in case I have multiple images with the same number)

        var ImgFound = [];
        for (var a = 0; a < MyImages.length; a++) {
            if (MyImages[a].name.toLowerCase ().indexOf (ImgNumber.toString ()) != -1) {
                ImgFound.push (MyImages[a]);

//  If there are more images with the same number an alert is triggered and the manual selection is done with openDlg (with the * wildcard character as I work on windows )

                if (ImgFound.length > 1) {

//  A sound alert is triggered, but the volume is too low and doesn't work properly on windows 11 (if someone kwow how to do it in a different way, feel free to share the method)

                    beep ();
                    alert ("There are multiple images with the number " + ImgNumber);
                    var ManualSelection = (File (MyFolder + "/*" + ImgNumber + "*")).openDlg ("Place", undefined, true);
                    if (ManualSelection == null) {
                        Reset ();
                        return;
                        }
                    ImgFound = [];
                    for (var b = 0; b < ManualSelection.length; b++) {
                        ImgFound.push (ManualSelection[b]);
                        }
                    break;
                    }
                }
            }

//  Here i place all the images from the array in the current page and center them

        for (var c = 0; c < ImgFound.length; c++) {
            app.activeWindow.activePage.place (ImgFound[c]);
            MyDoc.align (app.activeWindow.activePage.rectangles[0], AlignOptions.VERTICAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
            MyDoc.align (app.activeWindow.activePage.rectangles[0], AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
            }
        if (ImgFound == 0) {
            alert ("Image " + ImgNumber + " not present");
            }
        Reset ();
        }


    function Reset () {

//  This function serves to reset the variables to avoid conflicts / errors

        ImgNumber = undefined;
        ImgFound = 0;
        RestartScript = 1;
        }

UPDATE

After some testing, I updated the script with the palette method as Yuri suggested.

    #target "InDesign"
    #targetengine "session"
    if (app.documents.length == 0) {
        beep ();
        alert ("Open a document first", " ");
        exit ();
        }
    app.scriptPreferences.enableRedraw = false;
    var MyDoc = app.activeDocument;
    MyDoc.viewPreferences.horizontalMeasurementUnits = MeasurementUnits.millimeters;
    MyDoc.viewPreferences.verticalMeasurementUnits = MeasurementUnits.millimeters;
    app.viewPreferences.rulerOrigin = RulerOrigin.PAGE_ORIGIN;
    MyDoc.viewPreferences.rulerOrigin = RulerOrigin.PAGE_ORIGIN;
    MyDoc.zeroPoint = [0,0];
    var MyFolder, MyImages, ImgNumber;
    var MyPanel = new Window ("palette", "Enter the barcode");
    MyPanel.orientation = "column";
    var Group1 = MyPanel.add ("group");
    Group1.orientation = "row";
    var MyPath = Group1.add ("statictext");
    MyPath.preferredSize.width = 150;
    if (MyFolder != undefined) {
        MyPath.text = decodeURI (MyFolder);
        }
    var BrowseButton = Group1.add ("button", undefined, "Browse");
    BrowseButton.onClick = function () {
        MyFolder = Folder.selectDialog ("Select a folder");
        if (MyFolder == null) {
            MyFolder = null;
            MyPath.text = "";
            return;
            }
        MyImages = MyFolder.getFiles (/.+\.(?:gif|jpe?g|eps|tiff?|psd|pdf|bmp|png)$/i);
        if (MyImages.length <= 0) {
            beep ();
            alert ("There are no images in the selected folder", " ");
            return;
            }
        MyPath.text = decodeURI (MyFolder);
        }
    var Group2 = MyPanel.add ("group");
    Group2.orientation = "row";
    var MyText = Group2.add ("edittext");
    MyText.preferredSize.width = 150;
    MyText.onChanging = function () {
        MyText.text = MyText.text.replace (/[^0-9]/g, "");
        if (MyText.text.length >= 15) {
            BarcodeConversion ();
            }
        }
    MyText.addEventListener ("keydown", EnterEvent);
    var CancelButton = Group2.add ("button", undefined, "Cancel");
    CancelButton.onClick = function () {
        MyPanel.close ();
        }
    MyText.active = true;
    MyPanel.addEventListener ("keydown", EscapeEvent);
    MyPanel.onClose = function () {
        ImgNumber = undefined;
        MyText.removeEventListener ("keydown", EnterEvent);
        MyPanel.removeEventListener ("keydown", EscapeEvent);
        }
    MyPanel.show ();
    function Warning () {
        if (MyFolder == undefined) {
            alert ("No folder selected", " ");
            MyText.text = "";
            return;
            }
        }
    function EnterEvent (EventA) {
        Warning ();
        if (EventA.keyName === "Enter" && MyText.text != "") {
            ImgNumber = MyText.text;
            MyText.text = "";
            Search ();
            }
        }
    function EscapeEvent (EventB) {
        if (EventB.keyName == "Escape") {
            MyPanel.close ();
            }
        }
    function BarcodeConversion () {
        Warning ();
        ImgNumber = MyText.text.slice (4, 12);
        while (ImgNumber.match (new RegExp (/^\d/)) == 0) {
            ImgNumber = ImgNumber.slice (1);
            }
        MyText.text = "";
        if (ImgNumber != undefined) {
            Search ();
            }
        }
    function Search () {
        var ImgFound = [];
        for (var a = 0; a < MyImages.length; a++) {
            if (MyImages[a].name.toLowerCase ().indexOf (ImgNumber.toString ()) != -1) {
                ImgFound.push (MyImages[a]);
                if (ImgFound.length > 1) {
                    beep ();
                    alert ("There are multiple images with the number " + ImgNumber, " ");
                    var ManualSelection = (File (MyFolder + "/*" + ImgNumber + "*")).openDlg ("Inserisci", undefined, true);
                    if (ManualSelection == null) {
                        ImgNumber = undefined;
                        return;
                        }
                    ImgFound = [];
                    for (var b = 0; b < ManualSelection.length; b++) {
                        ImgFound.push (ManualSelection[b]);
                        }
                    ManualSelection = undefined;
                    break;
                    }
                }
            }
        if (ImgFound == 0) {
            beep ();
            alert ("Image " + ImgNumber + " not present", " ");
            }
        for (var c = 0; c < ImgFound.length; c++) {
            app.activeWindow.activePage.place (ImgFound[c]);
            MyDoc.align (app.activeWindow.activePage.rectangles[0], AlignOptions.VERTICAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
            MyDoc.align (app.activeWindow.activePage.rectangles[0], AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
            }
        ImgFound = [];
        ImgNumber = undefined;
        }

Solution

  • I'd try to use a palette instead of a dialogue. Here is the short working example how it can be done:

    #target "InDesign"
    #targetengine "session"
    
    var palette = new Window('palette', 'Enter the barcode');
    
    palette.add('statictext', undefined, 'Folder:');
    var folder = palette.add('edittext');
    folder.text = 'd:\\temp';
    folder.characters = 30;
    
    palette.add('statictext', undefined, 'File:');
    var file = palette.add('edittext');
    file.characters = 30;
    file.active = true;
    
    file.onChanging = function() {
        file.text = file.text.replace(/\D/g, '');
    }
    
    file.addEventListener('keydown', function(k) {
        if (k.keyName == 'Enter' && file.text != '') {
            place_image(folder.text, file.text);
            file.text = '';
        }
    });
    
    palette.addEventListener('keydown', function(k) {
        if (k.keyName === 'Escape') {
            palette.close();  
            // palette = null // for Win CS6
        }
    });
    
    palette.show();
    // palette.onClose = function() { palette = null } // for Win CS6
    
    //------------------------------------------------------------------
    function place_image(folder_name, file_name) {
        var folder = Folder(folder_name);
        var file = File(folder + '/' + file_name + '.jpg');
        if (file.exists) app.activeWindow.activePage.place(file);
    }
    

    It will show you a palette like this:

    enter image description here

    You can input a file name into the second field, press Enter and it will place the jpeg image with this name from the given folder into your document.

    As far as I can tell it works fine for this case. No need the doScript clumsy trick. At the very least it got me no errors after I placed about hundred images this way.

    You can add any additional functions into it by yourself.