Change Letter Case

Script for Adobe InDesign

Change matching text to uppercase, lowercase, title case, or sentence case. A recent project I had to convert a directory created in InDesign to HTML for a website. In the layout, the categories and company names used a paragraph style set to all-caps, and the client wanted the same look online. But the Content Management System where it was going didn't have a way to set up classes for the all-caps text decoration. So I made this script to convert the text in the particular paragraph styles to actual capital letters, not just a styling assignment. Then I figured add the character style option and match with GREP, too. Could come in handy in the future.

  • Match with GREP
  • Match by character style
  • Match by paragraph style
  • Adapt open source to customize or create other scripts
Change Letter Case screen
Download
Change Letter Case

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

How-to Video

How to use the script

Set options described below, and click the OK button to proceed.

Document — searches all stories of the document that is currently open and the top-most window if multiple documents are open.

Story — searches only the selected story. If no story is selected, the choice is disabled. The user may also choose Document to increase the scope of text searched.

GREP — enter a GREP expression just as in the InDesign Find/Change dialog. Characters that match the GREP expression are changed to the letter case set below.

Character Style — select a character style and characters assigned the style are changed to the letter case set below.

Paragraph Style — select a paragraph style and paragraphs assigned the style are changed to the letter case set below. Changes the entire paragraph.

Change to — the desired letter case. Choices are UPPERCASE, lowercase, Title Case, and Sentence case.

Source code

(download button below)

/*

Change Letter Case
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 = "Change Letter Case";

    // Script variables.
    var changecaseModes = [
        ChangecaseMode.UPPERCASE,
        ChangecaseMode.LOWERCASE,
        ChangecaseMode.TITLECASE,
        ChangecaseMode.SENTENCECASE
    ];
    var characterStyles;
    var doc;
    var error;
    var i;
    var letterCase = [
        "UPPERCASE",
        "lowercase",
        "Title Case",
        "Sentence case"
    ];
    var paragraphStyles;

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

    // Permanent UI variables.
    // Prefixes:
    //   btn  = "button"
    //   inp  = "edittext"
    //   list = "dropdownlist"
    //   rb   = "radiobutton"
    var btnCancel;
    var btnOk;
    var inpGrep;
    var listCharStyle;
    var listLetterCase;
    var listParaStyle;
    var rbSearchDoc;
    var rbSearchStory;

    // SETUP

    // Check for valid doc.
    if (!app.documents.length) {
        alert("Open a document.", title, false);
        return;
    }
    doc = app.activeDocument;

    // Load character styles.
    characterStyles = [];
    (function () {
        var i;
        // Root styles.
        for (i = 0; i < doc.characterStyles.length; i++) {
            characterStyles.push(doc.characterStyles[i]);
        }
        // Style groups.
        for (i = 0; i < doc.characterStyleGroups.length; i++) {
            substyle(doc.characterStyleGroups[i]);
        }

        function substyle(o) {
            var x;
            // Root styles.
            for (x = 0; x < o.characterStyles.length; x++) {
                characterStyles.push(o.characterStyles[x]);
            }
            // Style groups.
            for (x = 0; x < o.characterStyleGroups.length; x++) {
                // Recursive; calls itself.
                substyle(o.characterStyleGroups[x]);
            }
        }

    })();

    // Load paragraph styles.
    paragraphStyles = [];
    (function () {
        var i;
        // Root styles.
        for (i = 0; i < doc.paragraphStyles.length; i++) {
            paragraphStyles.push(doc.paragraphStyles[i]);
        }
        // Style groups.
        for (i = 0; i < doc.paragraphStyleGroups.length; i++) {
            substyle(doc.paragraphStyleGroups[i]);
        }

        function substyle(o) {
            var x;
            // Root styles.
            for (x = 0; x < o.paragraphStyles.length; x++) {
                paragraphStyles.push(o.paragraphStyles[x]);
            }
            // Style groups.
            for (x = 0; x < o.paragraphStyleGroups.length; x++) {
                // Recursive; calls itself.
                substyle(o.paragraphStyleGroups[x]);
            }
        }

    })();

    // CREATE USER INTERFACE

    w = new Window("dialog", title);
    p = w.add("panel");
    p.alignChildren = "fill";
    g = p.add("group");
    g.add("statictext", undefined, "Search:").preferredSize = [100, -1];
    g = g.add("group");
    rbSearchDoc = g.add("radiobutton", undefined, "Document");
    rbSearchStory = g.add("radiobutton", undefined, "Story");
    g = p.add("group");
    g.add("statictext", undefined, "GREP:").preferredSize = [100, -1];
    inpGrep = g.add("edittext");
    inpGrep.preferredSize = [250, -1];
    g = p.add("group");
    g.add("statictext", undefined, "Character Style:").preferredSize = [100, -1];
    listCharStyle = g.add("dropdownlist");
    listCharStyle.preferredSize = [250, -1];
    g = p.add("group");
    g.add("statictext", undefined, "Paragraph Style:").preferredSize = [100, -1];
    listParaStyle = g.add("dropdownlist");
    listParaStyle.preferredSize = [250, -1];
    g = p.add("group");
    g.add("statictext", undefined, "Change to:").preferredSize = [100, -1];
    listLetterCase = g.add("dropdownlist", undefined, undefined, {
        items: letterCase
    });
    listLetterCase.preferredSize = [250, -1];

    // 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");

    // Load lists.
    (function () {
        var i;
        // Paragraph styles.
        for (i = 0; i < paragraphStyles.length; i++) {
            listParaStyle.add("item", paragraphStyles[i].name);
        }
        // Character styles.
        for (i = 0; i < characterStyles.length; i++) {
            listCharStyle.add("item", characterStyles[i].name);
        }
    })();

    // Event handlers
    btnOk.onClick = function () {
        w.close(1);
    };
    btnCancel.onClick = function () {
        w.close(0);
    };

    // SET SEARCH SCOPE

    rbSearchDoc.value = true;
    rbSearchStory.enabled = false;
    if (app.selection.length) {
        // Get story if one is selected.
        try {
            if (app.selection[0].parentStory) {
                // Enable and set story choice.
                rbSearchStory.enabled = true;
                rbSearchStory.value = true;
            }
        } catch (e) {
            // Ignore.
        }
    }

    // DISPLAY THE DIALOG

    if (w.show() === 1) {
        try {
            app.doScript(process, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, title);
        } catch (_) {
            alert("An error has occurred.\nLine " + error.line + ": " + error.message, title, true);
        }
    }

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

    function process() {
        var changecaseMode;
        var story;
        // Preserve preferences.
        var preserve = {
            findChangeTextOptions: app.findChangeTextOptions.properties,
            findGrepPreferences: app.findGrepPreferences.properties,
            findTextPreferences: app.findTextPreferences.properties
        };
        try {
            // Set find change text options.
            app.findChangeTextOptions.properties = {
                includeFootnotes: true,
                includeHiddenLayers: true,
                includeLockedLayersForFind: true,
                includeLockedStoriesForFind: true,
                includeMasterPages: false
            };
            app.findGrepPreferences.findWhat = inpGrep.text;
            if (listCharStyle.selection) {
                app.findGrepPreferences.appliedCharacterStyle = characterStyles[listCharStyle.selection.index];
            }
            if (listParaStyle.selection) {
                app.findGrepPreferences.appliedParagraphStyle = paragraphStyles[listParaStyle.selection.index];
            }
            if (listLetterCase.selection) {
                changecaseMode = changecaseModes[listLetterCase.selection.index];
                if (rbSearchStory.value) {
                    // Story only.
                    story = app.selection[0].parentStory;
                    processStory();
                } else {
                    // Entire document.
                    for (i = 0; i < doc.stories.length; i++) {
                        story = doc.stories[i];
                        if (story.length) {
                            processStory();
                        }
                    }
                }
            }
        } catch (e) {
            error = e;
            throw e;
        } finally {
            // Restore preferences.
            app.findChangeTextOptions.properties = preserve.findChangeTextOptions;
            app.findGrepPreferences.properties = preserve.findGrepPreferences;
            app.findTextPreferences.properties = preserve.findTextPreferences;
        }

        function processStory() {
            var m;
            var matches;
            matches = story.findGrep();
            for (m = 0; m < matches.length; m++) {
                matches[m].texts[0].changecase(changecaseMode);
            }
        }

    }

})();
Download
Change Letter Case

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.