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!