• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers

Smart Title Script

Community Beginner ,
Oct 14, 2022 Oct 14, 2022

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))

}

 

TOPICS
Scripting

Views

133

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Community Expert , Oct 15, 2022 Oct 15, 2022

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

...

Likes

Translate

Translate
Community Expert ,
Oct 14, 2022 Oct 14, 2022

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 15, 2022 Oct 15, 2022

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 15, 2022 Oct 15, 2022

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Oct 16, 2022 Oct 16, 2022

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

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Oct 16, 2022 Oct 16, 2022

Copy link to clipboard

Copied

LATEST

Glad to help!

Likes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines