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
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.
Special handling of Title Case
Letter case is changed using methods that InDesign uses, built-in to the program. Unfortunately, InDesign isn’t too intelligent about converting to Title Case, as it simply capitalizes every word without consideration for articles, prepositions, etc., that should remain lowercase. To overcome this flaw, the script has a list of words to make lowercase after applying Title Case. The script is open source so that anyone may edit the code, including the list of words to make lowercase. Open the script in any text editor and search for “lowerTitleCase”, the name of the variable that is the list of words to keep lowercase in titles (it is very near the top of the code, the second variable declared, after the script title). It’s a simple matter to add additional words if desired. Just ensure each word is on its own line, is wrapped in quotation marks, and all except the last word has a trailing comma. Any words with a period, for example “etc.”, must have the period double-escaped because the character is a regular expression command. The word “etc.” is already in the list. Have a look at the code and see how the period is handled. Do the same for any other words with a period. Any questions contact the author William at any time.
Source code
(download button below)
/*
Change Letter Case
Copyright 2023 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.
*/
(function () {
var lowerTitleCase = [
// articles
"a",
"an",
"the",
// coordinating conjunctions
"and",
"but",
"of",
"or",
// prepositions
"at",
"by",
"to",
// other
"etc\\."
];
var title = "Change Letter Case";
if (!/indesign/i.test(app.name)) {
alert("Script for InDesign", title, false);
return;
}
// Script variables.
var changecaseModes;
var characterStyleNames;
var doc;
var error;
var i;
var letterCase;
var paragraphStyleNames;
// Reusable UI variables.
var g; // group
var gc1; // group (column)
var gc2; // group (column)
var p; // panel
var w; // window
// Permanent UI variables.
var btnCancel;
var btnOk;
var inpGrep;
var listCharacterStyle;
var listLetterCase;
var listParagraphStyle;
var rbSearchDoc;
var rbSearchStory;
// SETUP
// Define script variables.
changecaseModes = [
ChangecaseMode.UPPERCASE,
ChangecaseMode.LOWERCASE,
ChangecaseMode.TITLECASE,
ChangecaseMode.SENTENCECASE
];
letterCase = [
"UPPERCASE",
"lowercase",
"Title Case",
"Sentence case"
];
// Script requires open document.
if (!app.documents.length) {
alert("Open a document", title, false);
return;
}
doc = app.activeDocument;
// Load styles.
characterStyleNames = getStyleNames(doc.allCharacterStyles);
paragraphStyleNames = getStyleNames(doc.allParagraphStyles);
// CREATE USER INTERFACE
w = new Window("dialog", title);
w.alignChildren = "fill";
p = w.add("panel");
p.alignChildren = "fill";
// Group of 2 columns.
g = p.add("group");
// Groups, columns 1 and 2.
gc1 = g.add("group");
gc1.orientation = "column";
gc1.alignChildren = "left";
gc2 = g.add("group");
gc2.orientation = "column";
gc2.alignChildren = "left";
// Rows.
gc1.add("statictext", undefined, "Search:").preferredSize.height = 23;
g = gc2.add("group");
g.margins = [0, 4, 0, -1];
rbSearchDoc = g.add("radiobutton", undefined, "Document");
rbSearchStory = g.add("radiobutton", undefined, "Story");
gc1.add("statictext", undefined, "GREP:").preferredSize.height = 23;
inpGrep = gc2.add("edittext");
inpGrep.preferredSize = [250, 23];
gc1.add("statictext", undefined, "Character Style:").preferredSize.height = 23;
listCharacterStyle = gc2.add("dropdownlist", undefined, characterStyleNames);
listCharacterStyle.preferredSize = [250, 23];
// Add empty items so list is minimum two items.
listCharacterStyle.add("item", " ");
listCharacterStyle.add("item", " ");
gc1.add("statictext", undefined, "Paragraph Style:").preferredSize.height = 23;
listParagraphStyle = gc2.add("dropdownlist", undefined, paragraphStyleNames);
listParagraphStyle.preferredSize = [250, 23];
// Add empty items so list is minimum two items.
listParagraphStyle.add("item", " ");
listParagraphStyle.add("item", " ");
gc1.add("statictext", undefined, "Change to:").preferredSize.height = 23;
listLetterCase = gc2.add("dropdownlist", undefined, letterCase);
listLetterCase.preferredSize = [250, 23];
// Action Buttons
g = w.add("group");
g.alignment = "center";
btnOk = g.add("button", undefined, "OK");
btnCancel = g.add("button", undefined, "Cancel");
// Panel Copyright
p = w.add("panel");
p.add("statictext", undefined, "Copyright 2023 William Campbell");
// Event handlers
listParagraphStyle.onChange = function () {
if (this.selection.text === " ") {
this.selection = null;
}
};
listCharacterStyle.onChange = function () {
if (this.selection.text === " ") {
this.selection = null;
}
};
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 (_) {
// Ignore.
}
}
// DISPLAY THE DIALOG
if (w.show() == 1) {
try {
app.doScript(process, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, title);
} catch (e) {
error = error || e;
alert("An error has occurred.\nLine " + error.line + ": " + error.message, title, true);
}
}
//====================================================================
// END PROGRAM EXECUTION, BEGIN FUNCTIONS
//====================================================================
function getStyleNames(o) {
var a = [];
var i;
var name;
for (i = 0; i < o.length; i++) {
name = o[i].name;
// Ignore names that begin with bracket.
if (/^\[/.test(name)) continue;
a.push(name);
}
a.sort(function (a, b) {
// Case insensitive.
var aLow = a.toLowerCase();
var bLow = b.toLowerCase();
if (aLow < bLow) return -1;
if (aLow > bLow) return 1;
return 0;
});
return a;
}
function itemByName(o, name) {
var i;
for (i = 0; i < o.length; i++) {
if (name == o[i].name) {
return o[i];
}
}
return null;
}
function process() {
var changecaseMode;
var story;
var titleCase;
// Preserve preferences.
var preserve = {
findChangeTextOptions: app.findChangeTextOptions.properties,
findGrepPreferences: app.findGrepPreferences.properties,
findTextPreferences: app.findTextPreferences.properties
};
try {
// Set find change text options.
app.findChangeTextOptions.includeFootnotes = true;
app.findChangeTextOptions.includeHiddenLayers = true;
app.findChangeTextOptions.includeLockedLayersForFind = true;
app.findChangeTextOptions.includeLockedStoriesForFind = true;
app.findChangeTextOptions.includeMasterPages = false;
app.findGrepPreferences.findWhat = inpGrep.text;
if (listCharacterStyle.selection) {
app.findGrepPreferences.appliedCharacterStyle = itemByName(doc.allCharacterStyles, listCharacterStyle.selection.text);
}
if (listParagraphStyle.selection) {
app.findGrepPreferences.appliedParagraphStyle = itemByName(doc.allParagraphStyles, listParagraphStyle.selection.text);
}
if (listLetterCase.selection) {
changecaseMode = changecaseModes[listLetterCase.selection.index];
titleCase = (listLetterCase.selection.index == 2);
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 i;
var m;
var matches;
var pattern;
matches = story.findGrep();
for (m = 0; m < matches.length; m++) {
matches[m].texts[0].changecase(changecaseMode);
if (titleCase) {
for (i = 0; i < lowerTitleCase.length; i++) {
pattern = new RegExp("(\\s)" + lowerTitleCase[i] + "(\\s)", "gi");
if (pattern.test(matches[m].contents)) {
matches[m].contents = matches[m].contents.replace(pattern, RegExp.$1 + lowerTitleCase[i] + RegExp.$2);
}
}
}
}
}
}
})();
Change Letter Case
For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.
IMPORTANT: scripts are developed for the latest Adobe Creative Cloud applications. Many scripts work in CC 2018 and later, even some as far back as CS6, but may not perform as expected, or run at all, when used in versions prior to 2018. Photoshop features Select Subject and Preserve Details 2.0 definitely fail prior to CC 2018 (version 19) as the features do not exist in earlier versions. For best results use the latest versions of Adobe Creative Cloud applications.
IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.
IMPORTANT: fees paid for software products are the purchase of a non-exclusive license to use the software product and do not grant the purchaser any degree of ownership of the software code. Author of the intellectual property and copyright holder William Campbell retains 100% ownership of all code used in all software products regardless of the inspiration for the software product design or functionality.