Copy link to clipboard
Copied
Hello,
This is a script from a previous post in 2011. It works great! But I have the following request: How to apply it to a specific paragraph style like "Heading 1" instead of selecting the text?
//DESCRIPTION: Converts selected text to title case smartly
var ignoreWords = ["a", "an", "and", "the", "to", "with", "in", "on", "as", "of", "or", "at", "into", "that",
"by", "from", "their", "then", "for", "are", "not","cannot", "be", "is", "which", "can"];
var intCaps = ["PineRidge","InDesign","NJ","UMC", "FCCLA", "SkillsUSA", "d’Oeuvres", "VAT", "VIES",];
// or by creating text files named ignoreWords.txt and intCaps.txt in the same folder as the script
ignoreWords = getIgnoreFile(ignoreWords);
intCaps = getIntCaps(intCaps);
try {
myText = app.selection[0].texts[0].contents;
} catch(e) {
exit();
}
theWordRanges = myText.split("/");
for (var i = theWordRanges.length - 1; i >= 0; i--) {
theWords = theWordRanges.toLowerCase().split(" ");
//First word must have a cap, but might have an internal cap
myNewText = "";
for (var j = 0; theWords.length > j; j++) {
k = isIn(intCaps,theWords)
if (k > -1) {
myNewText = myNewText + intCaps + " ";
continue;
} else {
if ((isIn(ignoreWords,theWords) > -1) && (j != 0)) {
myNewText = myNewText + theWords + " ";
} else {
myNewText = myNewText + InitCap(theWords) + " ";
}
}
}
theWordRanges = myNewText.substring(0,myNewText.length - 1)
}
app.selection[0].texts[0].contents = theWordRanges.join("/");
// +++++++ Functions Start Here +++++++++++++++++++++++
function getIgnoreFile(theWords) {
var myFile = File(File(getScriptPath()).parent.fsName + "/ignoreWords.txt");
if (!myFile.exists) { return theWords }
// File exists, so use it instead
myFile.open("r");
var importedWords = myFile.read();
myFile.close();
return importedWords.split("\n"); // Could filter these, but what's the point?
}
function getIntCaps(theWords) {
var myFile = File(File(getScriptPath()).parent.fsName + "/intCaps.txt");
if (!myFile.exists) { return theWords }
// File exists, so use it instead
myFile.open("r");
var importedWords = myFile.read();
myFile.close();
return importedWords.split("\n"); // Could filter these, but what's the point?
}
function getScriptPath() {
// This function returns the path to the active script, even when running ESTK
try {
return app.activeScript;
} catch(e) {
return e.fileName;
}
}
function isIn(aList,aWord) {
for (var i = 0; aList.length > i; i++) {
if (aList.toLowerCase() == aWord) {
return i;
}
}
return -1;
}
function InitCap(aWord) {
if (aWord.length == 1) {
return (aWord.toUpperCase());
}
return (aWord.substr(0,1).toUpperCase() + aWord.substring(1,aWord.length))
}
Thanks @joaquinm13464128, it's always a good idea to post a link to the original script, so people can see where it came from, and a nice courtesy to include the author's name if posting the script listing.
I have added a new function that finds multiple paragraph styles, and also streamlined the changeToTitleCase function. I realised there wasn't much sense in having the distinction between "ignore" words and "intCap" words and it was simpler to just have a list of "overrides". If the word is
...Copy link to clipboard
Copied
Hi @joaquinm13464128, did you write that script yourself? If so, you've made a great start. I found a few problems and adjusted a few parts of it. The big change I made was to make your script into a function that I could run for each found paragraph. I also changed the focus of the function to a single paragraph, and then iterate over the words. That's why you can see me using "word.contents" a bit. Please have a read through and see if it makes sense.
function main() {
var doc = app.activeDocument,
targetParagraphStyleName = 'My TitleCase Style';
// reset grep prefs
app.findGrepPreferences = NothingEnum.NOTHING;
app.changeGrepPreferences = NothingEnum.NOTHING;
app.findGrepPreferences.appliedParagraphStyle = targetParagraphStyleName;
// do the find
var found = doc.findGrep();
for (var i = 0; i < found.length; i++) {
var para = found[i];
changeToTitleCase(para);
}
/**
* Convert paragraph to title case
* with some smarts.
* based on script posted by @joaquinm13464128
* here: https://community.adobe.com/t5/indesign-discussions/smart-title-script/m-p/13268183
* @param {Paragraph} para - an Indesign Paragraph
*/
function changeToTitleCase(para) {
//DESCRIPTION: Converts selected text to title case smartly
var ignoreWords = ["a", "an", "and", "the", "to", "with", "in", "on", "as", "of", "or", "at", "into", "that",
"by", "from", "their", "then", "for", "are", "not", "cannot", "be", "is", "which", "can"];
var intCaps = ["PineRidge", "InDesign", "NJ", "UMC", "FCCLA", "SkillsUSA", "d’Oeuvres", "VAT", "VIES",];
// or by creating text files named ignoreWords.txt and intCaps.txt in the same folder as the script
ignoreWords = getIgnoreFile(ignoreWords);
intCaps = getIntCaps(intCaps);
var words = para.words;
for (var i = words.length - 1; i >= 0; i--) {
var word = words[i];
//First word must have a cap, but might have an internal cap
if (isIn(intCaps, word.contents) != -1)
continue;
if (
isIn(ignoreWords, word.contents) != -1
&& i > 0
)
continue;
word.contents = initalCap(word.contents);
}
}
// +++++++ Functions Start Here +++++++++++++++++++++++
function getIgnoreFile(theWords) {
var myFile = File(File(getScriptPath()).parent.fsName + "/ignoreWords.txt");
if (!myFile.exists) { return theWords }
// File exists, so use it instead
myFile.open("r");
var importedWords = myFile.read();
myFile.close();
return importedWords.split("\n"); // Could filter these, but what's the point?
}
function getIntCaps(theWords) {
var myFile = File(File(getScriptPath()).parent.fsName + "/intCaps.txt");
if (!myFile.exists) { return theWords }
// File exists, so use it instead
myFile.open("r");
var importedWords = myFile.read();
myFile.close();
return importedWords.split("\n"); // Could filter these, but what's the point?
}
function getScriptPath() {
// This function returns the path to the active script, even when running ESTK
try {
return app.activeScript;
} catch (e) {
return e.fileName;
}
}
function isIn(aList, aWord) {
aWord = aWord.toLowerCase();
for (var i = 0; aList.length > i; i++) {
if (aList[i].toLowerCase() == aWord) {
return i;
}
}
return -1;
}
function initalCap(aWord) {
if (aWord.length == 1) {
return (aWord.toUpperCase());
}
return (aWord.substr(0, 1).toUpperCase() + aWord.substring(1, aWord.length));
}
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Find & Smart TitleCase");
The first part of script does the find for the paragraph style. You will need to specify the paragraph style name that you have in your document. I did not test the text file loading functions.
- Mark
Copy link to clipboard
Copied
Hi Mark,
Thank you for your help. I just tested your script and, it works great with lower cases titles, but somehow it does not work on UPPERCASE titles, as the original script does. I'm cleaning up documents with a mix of upper and lowercase titles and sure it would help to change both with this script. Also, it could be possible to specify multiple paragraph styles instead of one, like for heading such as Heading1, Heading2, Heading3, Heading5, Heading6, up to Heading 7. And by the way, I did not write the code. Here is the link to the original post in 2005. Best - Joaquin
Copy link to clipboard
Copied
Thanks @joaquinm13464128, it's always a good idea to post a link to the original script, so people can see where it came from, and a nice courtesy to include the author's name if posting the script listing.
I have added a new function that finds multiple paragraph styles, and also streamlined the changeToTitleCase function. I realised there wasn't much sense in having the distinction between "ignore" words and "intCap" words and it was simpler to just have a list of "overrides". If the word is found in the overrides list, then that is how it should be capitalized (unless first word in paragraph). I have left a visual distinction in the array literal so you can see the split between "a, and, the" etc and "PineRidge, VAT" etc, but these are the same for the script's purpose.
See how this goes for you. You may have to expand the overrides list quite a bit.
- Mark
function main() {
// the paragraph style names
var styleNames = [
'Heading 1',
'Heading 2',
'Heading 3',
'Heading 4'
];
// gather all the paragraphs in these styles
var found = findTextInParagraphStyles(app.activeDocument, styleNames);
// change them all to Title Case
for (var i = 0; i < found.length; i++)
changeToTitleCase(found[i]);
/**
* Find text set in all multiple
* paragraph styles.
* @author m1b
* @version 2022-10-16
* @param {Document|Page|Story} findWhere - any Indesign Object with a findGrep method.
* @param {Array<String>} styleNames - the paragraph style names to find.
* @returns {Array<Paragraph>}
*/
function findTextInParagraphStyles(findWhere, styleNames) {
if (styleNames.constructor.name == 'String')
styleNames = [styleNames];
if (!findWhere.hasOwnProperty('findGrep'))
throw Error('findTextInParagraphStyles cannot find in "' + findWhere + '".');
// reset grep prefs
app.findGrepPreferences = NothingEnum.NOTHING;
app.changeGrepPreferences = NothingEnum.NOTHING;
var found = [];
for (var i = 0; i < styleNames.length; i++) {
try {
// find criterion
app.findGrepPreferences.appliedParagraphStyle = styleNames[i];
// do the find
found = found.concat(findWhere.findGrep());
} catch (error) {
/* paragraph style not found */
}
}
return found;
};
/**
* Convert paragraph to title case
* with some smarts.
* @author Dave Saunders
* @url http://jsid.blogspot.com/2005/08/script-of-day-smart-title-case.html
* @author m1b
* @version 2022-10-16
* @discussion https://community.adobe.com/t5/indesign-discussions/smart-title-script/m-p/13268183
* @param {Paragraph} para - an Indesign Paragraph.
*/
function changeToTitleCase(para) {
//DESCRIPTION: Converts selected text to title case smartly
var overrides = [
"a", "an", "and", "the", "to", "with", "in", "on", "as", "of", "or", "at", "into", "that",
"by", "from", "their", "then", "for", "are", "not", "cannot", "be", "is", "which", "can",
"PineRidge", "InDesign", "NJ", "UMC", "FCCLA", "SkillsUSA", "d’Oeuvres", "VAT", "VIES"
];
// or by creating text files named overrides.txt in the same folder as the script
var overrides = getOverrides(overrides);
var words = para.words;
for (var i = words.length - 1; i >= 0; i--) {
var word = words[i],
overrideIndex = isIn(overrides, word.contents);
if (overrideIndex != -1)
// override capitalization but do
// capitalize first word of paragraph.
word.contents = i == 0
? initialCap(overrides[overrideIndex])
: overrides[overrideIndex];
else
word.contents = initialCap(word.contents.toLowerCase());
}
};
// app.selection[0].texts[0].contents = words.join("/");
// +++++++ Functions Start Here +++++++++++++++++++++++
function getOverrides(theWords) {
var myFile = File(File(getScriptPath()).parent.fsName + "/overrides.txt");
if (!myFile.exists) { return theWords }
// File exists, so use it instead
myFile.open("r");
var importedWords = myFile.read();
myFile.close();
return importedWords.split("\n"); // Could filter these, but what's the point?
}
function getScriptPath() {
// This function returns the path to the active script, even when running ESTK
try {
return app.activeScript;
} catch (e) {
return e.fileName;
}
}
function isIn(aList, aWord) {
aWord = aWord.toLowerCase();
for (var i = 0; aList.length > i; i++) {
if (aList[i].toLowerCase() == aWord) {
return i;
}
}
return -1;
}
function initialCap(aWord) {
if (aWord.length == 1) {
return (aWord.toUpperCase());
}
return (aWord.substr(0, 1).toUpperCase() + aWord.substring(1, aWord.length));
}
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Find & Smart TitleCase");
Copy link to clipboard
Copied
Wow! It works perfectly. Thank you, Mark - appreciate the quick response. This version looks much cleaner. Having a single list of overrides will make it easier to expand as needed. This is my first post on this community and as you suggest, next time I'll include the source and author when posting again. Best - Joaquin
Copy link to clipboard
Copied
Glad to help!
Copy link to clipboard
Copied
Hi!
Just found this older thread in search of a solution for Adobe's failure to incorporate a Smart Title Case option themselves, but the script doesn't work (anymore?).
Both scripts –the original and Mark's edited version– only create a java script error:
Error Number: 24
Error String: theWordRanges.toLowerCase is not a function
Engine: main
File: /Users/donmilano/Library/Preferences/Adobe InDesign/Version
20.0/en_US/Scripts/Scripts Panel/SmartTitleCase.jsx
Line: 41
Source:
theWords = theWordRanges.toLowerCase().split(" ");
OK
OR:
Error Number: 30477
Error String: Invalid value for set property 'appliedParagraphStyle'.
Expected String, ParagraphStyle or NothingEnum enumerator, but received "My TitleCase Style".
Engine: main
File: /Users/donmilano/Library/Preferences/Adobe InDesign/Version
20.0/en_US/Scripts/Scripts Panel/SmartTitleCase.jsx
Line: 111
Source: app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined,
UndoModes. ENTIRE_SCRIPT, "Find & Smart TitleCase");
OK
Can you help me out here? I'm not at all familiar with script writing btw...
Thanks a lot in advance!
Milan
Copy link to clipboard
Copied
Hi Milan, I just tested in Indesign 20 and it works as expected for me. Perhaps your document has something that confuses the script. Are you able to post a short example .indd here that shows the error? Then I'll test it with your exact file.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Weird... but maybe I did something wrong saving the script file or did I have to add any other code to it?
Here's an Indesign doc and a screenshot of the error.
By @dafleXXX1969
For starters, you don't have the "My TitleCase Style" paragraph style in your document, therefore the script won't work.
Copy link to clipboard
Copied
@leo.r
I did not see that and as mentioned before, I'm not familiar with scrpt writing therefore I was looking for a finished script that resolves the on-board Indesign issue with Title Case where every word gets capitalized vs leaving out the adjectives.
But even the very first script from the OP only produces an error:
Error Number: 24
Error String: theWordRanges.toLowerCase is not a function
Engine: main
File: /Users/donmilano/Library/Preferences/Adobe InDesign/Version
20.0/en_US/Scripts/Scripts Panel/SmartTitleCase.jsx
Line: 41
Source:
theWords = theWordRanges.toLowerCase().split(" ");
Copy link to clipboard
Copied
@dafleXXX1969 that script (the one that I modified) searches for text set in particular paragraph style(s) and performs the smart title case operation on those texts. It sounds like what you expect is a script that does the same, but simply to the selected text.
Here is a reformulated script that does it. Let me know if that helps, or not.
- Mark
/**
* @file Smart TitleCase Selected Text.js
*
* @author m1b and Dave Saunders (see function documentation)
* @discussion https://community.adobe.com/t5/indesign-discussions/smart-title-script/m-p/15599697
*/
function main() {
var doc = app.activeDocument;
var text = doc.selection[0];
if (
!text
|| !text.hasOwnProperty('words')
)
return alert('Please select some text and try again.');
changeToTitleCase(text);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, "Smart TitleCase");
/**
* Convert paragraph to title case with some smarts.
* @author Dave Saunders
* @url http://jsid.blogspot.com/2005/08/script-of-day-smart-title-case.html
* @author m1b
* @version 2022-10-16
* @discussion https://community.adobe.com/t5/indesign-discussions/smart-title-script/m-p/13268183
* @param {Text} text - Indesign text object.
*/
function changeToTitleCase(text) {
var overrides = getOverrides() || [
"a", "an", "and", "the", "to", "with", "in", "on", "as", "of", "or", "at", "into", "that",
"by", "from", "their", "then", "for", "are", "not", "cannot", "be", "is", "which", "can",
"PineRidge", "InDesign", "NJ", "UMC", "FCCLA", "SkillsUSA", "d’Oeuvres", "VAT", "VIES"
];
var words = text.words;
for (var i = words.length - 1; i >= 0; i--) {
var word = words[i],
overrideIndex = isIn(overrides, word.contents);
if (overrideIndex != -1) {
// override capitalization but do
// capitalize first word of paragraph.
word.contents = word.index === word.paragraphs[0].index
? initialCap(overrides[overrideIndex])
: overrides[overrideIndex];
}
else
word.contents = initialCap(word.contents.toLowerCase());
};
/**
* Reads overrides from an "overrides.txt" file
* in the same folder as this script
* @returns {String?}
*/
function getOverrides() {
var myFile = File(File(getScriptPath()).parent.fsName + "/overrides.txt");
if (!myFile.exists)
return;
// File exists, so use it instead
myFile.open("r");
var importedWords = myFile.read();
myFile.close();
return importedWords.split("\n");
};
/**
* Returns the path to this script's file.
* @returns {String}
*/
function getScriptPath() {
try {
return app.activeScript;
} catch (e) {
return e.fileName;
}
};
function isIn(aList, aWord) {
aWord = aWord.toLowerCase();
for (var i = 0; aList.length > i; i++) {
if (aList[i].toLowerCase() == aWord) {
return i;
}
}
return -1;
};
function initialCap(aWord) {
if (aWord.length == 1) {
return (aWord.toUpperCase());
}
return (aWord.substr(0, 1).toUpperCase() + aWord.substring(1, aWord.length));
};
};
Edit 2025-11-22_0958: fixed bug where changeToTitleCase would incorrectly uppercase the first word of the selection when it wasn't the first word in the paragraph.
Copy link to clipboard
Copied
Hey @dafleXXX1969 I want to give you a bit more information about this script (and the original script). The real power of this script is the ability to customize which words are, or aren't, capitalized, or to override a word with custom capitalization.
To do this you should edit this part of the script:
"a", "an", "and", "the", "to", "with", "in", "on", "as", "of", "or", "at", "into", "that",
"by", "from", "their", "then", "for", "are", "not", "cannot", "be", "is", "which", "can",
"PineRidge", "InDesign", "NJ", "UMC", "FCCLA", "SkillsUSA", "d’Oeuvres", "VAT", "VIES"
Any words in this section (must be in quotes and with a comma between) will be overridden, and will appear exactly as they appear here, so if you have a commonly miscapitalized word, add it here. You can also create a text file called "overrides.txt" in the same folder as the script I think, if you prefer that approach.
Important note: like most computer programs, this procedure isn't as smart as we might like. For example the text "A vat of honey ... £2.99 + Vat" will change to "A VAT of Honey ... £2.99 + VAT", capitalizing the first "vat" wrongly.
- Mark
Get ready! An upgraded Adobe Community experience is coming in January.
Learn more