PDF Export Folder

JavaScript for Adobe InDesign

The purpose of the script is twofold:

1. Export a folder of InDesign documents to PDF as simply as possible. Other scripts perform the same and more, and with more options. Users are encouraged to use those scripts when further capability is needed. The point of this script was to accomplish one specific task without excess.

2. Act as a template to create other scripts that process InDesign documents, whatever the process might be. The script can perform other tasks by altering the processFile() function, called for each InDesign document found in the selected folder. Also further options are easily added to the interface.

  • Option to include subfolders
  • Export using any defined PDF Preset
  • Option to add suffix to output file names
  • Option to replace existing output files.
  • Adapt open source to create other scripts
PDF Export Folder screen
Download
PDF Export Folder

You decide. Reward the author an
amount the solution is worth to you.

Instructions for use

The interface has two sections: Process and Options. Set desired options then click the OK button to begin. A progress bar is displayed as documents are processed.

Section 1: Process

Folder — select a folder that contains InDesign documents. Each will be exported to a PDF in the same folder. Only files with the .indd extension are processed.

Include subfolders — if enabled, documents in all subfolders are also processed.

Section 2: Options

PDF preset — the PDF preset used to export the InDesign files.

Original name + — an optional suffix of characters appended to output file names, prior to the file extension. The characters chosen must be legal to use in file names.

Replace existing output files — when enabled, existing output files are replaced without user intervention. When disabled, the user is prompted to confirm the replacement of each existing output file.

Source code

(download button below)

/*

PDF Export Folder
Copyright 2021 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact

Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

*/

//@target indesign

(function () {

    var title = "PDF Export Folder";

    // Script variables.
    var doneMessage; // String
    var folderInput; // Folder
    var pdfPreset; // PDFExportPreset
    var pdfPresetNames = []; // Array of Strings

    // Reusable UI variables.
    var g; // group
    var p; // panel
    var w; // window

    // Permanent UI variables.
    // Prefixes:
    //   btn  = "button"
    //   cb   = "checkbox"
    //   txt  = "statictext"
    //   inp  = "edittext"
    //   list = "dropdownlist"
    //   rb   = "radiobutton"
    var btnCancel;
    var btnOk;
    var btnFolderInput;
    var cbReplaceOutput;
    var cbSubfolders;
    var inpSuffix;
    var listPdfPresets;
    var txtFolderInput;

    // Load application PDF presets.
    pdfPresetNames = app.pdfExportPresets.everyItem().name;
    pdfPresetNames.sort();

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    w.alignChildren = "fill";

    // Panel 'Process'
    p = w.add("panel", undefined, "Process");
    p.alignChildren = "left";
    p.margins = [18, 24, 18, 12];
    g = p.add("group");
    btnFolderInput = g.add("button", undefined, "Folder...");
    txtFolderInput = g.add("statictext", undefined, "", {
        truncate: "middle"
    });
    txtFolderInput.characters = 41;
    g = p.add("group");
    g.margins = [36, 6, 0, 0];
    cbSubfolders = g.add("checkbox", undefined, "Include subfolders");

    // Panel 'Options'
    p = w.add("panel", undefined, "Options");
    p.alignChildren = "left";
    p.margins = [18, 18, 18, 12];
    g = p.add("group");
    g.add("statictext", undefined, "PDF Preset:");
    listPdfPresets = g.add("dropdownlist", undefined, undefined, {
        items: pdfPresetNames
    });
    listPdfPresets.preferredSize = [246, -1]; // 246 pixels wide, default height.
    g = p.add("group");
    g.add("statictext", undefined, "Original file name  +");
    inpSuffix = g.add("edittext");
    inpSuffix.characters = 18;
    g = p.add("group");
    g.margins = [0, 6, 0, 0];
    cbReplaceOutput = g.add("checkbox", undefined, "Replace existing output files");

    // Action Buttons
    g = w.add("group");
    g.alignment = "center";
    g.margins = [0, 0, 0, 12];
    btnOk = g.add("button", undefined, "OK");
    btnCancel = g.add("button", undefined, "Cancel");

    // UI ELEMENT EVENT HANDLERS

    // Panel 'Process'
    btnFolderInput.onClick = function () {
        var f = Folder.selectDialog("Select folder to process");
        if (f) {
            txtFolderInput.text = Folder.decode(f.fsName);
            folderInput = f;
        }
    };

    // Panel 'Options'
    inpSuffix.onChange = function () {
        // Trim.
        this.text = this.text.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
        // Remove any periods.
        this.text = this.text.replace(/\.+/g, "");
        // Remove illegal characters.
        var s = this.text.replace(/[\/\\:*?"<>|]/g, "");
        if (this.text != s) {
            this.text = s;
            alert("Illegal characters detected and removed.", " ", false);
        }
    };

    // Action Buttons
    btnOk.onClick = function () {
        if (!txtFolderInput.text) {
            alert("Select folder to processs.", " ", false);
            return;
        }
        if (!listPdfPresets.selection) {
            alert("Select PDF Preset.", " ", false);
            return;
        }
        w.close(1);
    };
    btnCancel.onClick = function () {
        w.close(0);
    };

    // DISPLAY THE DIALOG

    if (w.show() == 1) {
        try {
            process();
            if (!doneMessage) {
                doneMessage = "Processing complete.";
            }
            alert(doneMessage, title, false);
        } catch (e) {
            alert("An error has occurred.\nLine " + e.line + ": " + e.message, title, true);
        }
    }

    //====================================================================
    //               END PROGRAM EXECUTION, BEGIN FUNCTIONS
    //====================================================================

    function getFiles(folder, subfolders, extension) {
        // folder = folder object, not folder name.
        // subfolders = bool, true = include subfolders.
        // extension = string, extension to include.
        // extension undefined = any.
        // Combine multiple extensions with regex OR, i.e. indd|tif|txt
        // Performs natural sort ("2" precedes "10", for example).
        // Ignores hidden files.
        var d = [];
        var f;
        var files;
        var i;
        var pattern = new RegExp("\." + extension + "$");
        files = folder.getFiles();
        // Natural sort (i.e "2" precedes "10") case insensitive.
        files.sort(function (a, b) {
            var aa;
            var bb;
            var c;
            var d;
            var chunkify = function (t) {
                var i;
                var j;
                var m;
                var n = 0;
                var tz = [];
                var x = 0;
                var y = -1;
                while ((i = (j = t.charAt(x++)).charCodeAt(0))) {
                    m = (i == 46 || (i >= 48 && i <= 57));
                    if (m !== n) {
                        tz[++y] = "";
                        n = m;
                    }
                    tz[y] += j;
                }
                return tz;
            };
            var x;
            aa = chunkify(a.name.toLowerCase()); // <---- case insensitive
            bb = chunkify(b.name.toLowerCase()); // <---- case insensitive
            for (x = 0; aa[x] && bb[x]; x++) {
                if (aa[x] !== bb[x]) {
                    c = Number(aa[x]);
                    d = Number(bb[x]);
                    if (c == aa[x] && d == bb[x]) {
                        return c - d;
                    } else {
                        return (aa[x] > bb[x]) ? 1 : -1;
                    }
                }
            }
            return aa.length - bb.length;
        });
        for (i = 0; i < files.length; i++) {
            f = files[i];
            if (f instanceof Folder && subfolders) {
                // Recursive (function calls itself).
                d = d.concat(getFiles(f, subfolders, extension));
            } else if (f instanceof File && !f.hidden && (extension == undefined || pattern.test(f.name))) {
                d.push(f);
            }
        }
        return d;
    }

    function process() {
        // var doneMessage script global
        // var folderInput script global
        // var pdfPreset script global
        var files;
        var i;

        // Preserve preferences.
        var preserve = {
            pdfExportPreferencesViewPDF: app.pdfExportPreferences.viewPDF
        };
        // Suppress app messages, i.e. profile mismatch.
        app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
        // Get PDF Preset to use.
        pdfPreset = app.pdfExportPresets.item(String(listPdfPresets.selection));
        files = getFiles(folderInput, cbSubfolders.value, "indd");
        if (!files.length) {
            // Nothing to process.
            doneMessage = "No InDesign files found in selected folder.";
            return;
        }
        // Don't view PDFs after export.
        app.pdfExportPreferences.viewPDF = false;
        try {
            progress(files.length);
            for (i = 0; i < files.length; i++) {
                processFile(files[i]);
                progress.increment();
            }
        } finally {
            progress.close();
            // Restore preferences.
            app.pdfExportPreferences.viewPDF = preserve.pdfExportPreferencesViewPDF;
            // Restore PDF export preferences to all pages.
            // (must set twice, once text another enumerated value).
            app.pdfExportPreferences.pageRange = "All Pages";
            app.pdfExportPreferences.pageRange = PageRange.ALL_PAGES;
        }
    }

    function processFile(file) {
        // var pdfPreset script global
        var doc;
        var filePdf;
        var name;
        var replace;
        doc = app.open(file);
        filePdf = new File(file.fullName.replace(/\.indd$/i, "") + inpSuffix.text + ".pdf");
        name = File.decode(filePdf.name);
        progress.message("Exporting " + name);
        replace = true;
        if (!cbReplaceOutput.value && filePdf.exists) {
            replace = confirm("File exists. Replace?\n" + name, true, title);
        }
        if (replace) {
            app.pdfExportPreferences.pageRange = PageRange.ALL_PAGES;
            doc.exportFile(ExportFormat.PDF_TYPE, filePdf, false, pdfPreset);
        }
        doc.close(SaveOptions.NO);
    }

    function progress(steps) {
        var b;
        var t;
        var w;
        w = new Window("palette", "Progress", undefined, {
            closeButton: false
        });
        t = w.add("statictext");
        t.preferredSize = [450, -1]; // 450 pixels wide, default height.
        if (steps) {
            b = w.add("progressbar", undefined, 0, steps);
            b.preferredSize = [450, -1]; // 450 pixels wide, default height.
        }
        progress.close = function () {
            w.close();
        };
        progress.increment = function () {
            b.value++;
        };
        progress.message = function (message) {
            t.text = message;
            w.update();
        };
        w.show();
    }

})();
Download
PDF Export Folder

License details included in download

For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.

IMPORTANT: by downloading the script you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.