Skip to main content
Participating Frequently
August 24, 2024
Question

Apply character style based on Chapter Number. GREP maybe?

  • August 24, 2024
  • 5 replies
  • 3049 views

I have a long document with chapters. Each chapter has different color elements. One of those is the headings in the chapter are that color. Rather than creating a style for ever single chapter, I was wondering if there was a way to have the base paragraph style and then character styles to change the color. Is there some way to automate applying the character styles based on chapter? 

 

Something like  if chapter is 1 apply chapter 1 heading style, if 2 then apply chapter 2 heading style. 

 

Maybe GREP or a script but i'm no good at writing either of those. Any suggestions?

5 replies

m1b
Adobe Expert
August 25, 2024

Hi @Tommy30053592w60v, I think this is an interesting script project, so have a look at this. You have to first configure this part of the script to match your document:

 

var delimiterParagraphStyleName = "Chapter Heading";
var colorMeStyleName = "Color Me";
var colorMyTableStyleName = "Color My Table";
var colorNames = [
    'Color1',
    'Color2',
    'Color3',
    'Color4',
    'Color5',
];

 

Then see further instructions in the script documentation.

 

Try it out on the attached "demo.indd".

 

This concept of this script is to faciliate various changes—coloring is just a simple example—based on chapter divisions. So this could be a starting point for a more elaborate script in the future. Let me know if you end up trying it out.

- Mark

 

/**
 * @file Color By Chapter.js
 *
 * Apply colors based on "chapter number", derived by
 * searching for a "Chapter Heading" Paragraph Style.
 * 
 * Includes two example implementations:
 * 1. color texts set in character style "Color Me"
 * 2. color tables containing text set in character style "Color My Table"
 * 
 * Configure for your own document, or try with "demo.indd" at URL below.
 *
 * @author m1b
 * @version 2024-08-25
 * @discussion https://community.adobe.com/t5/indesign-discussions/apply-character-style-based-on-chapter-number-grep-maybe/m-p/14819501
 */
function main() {

    // set these to match your document:

    var delimiterParagraphStyleName = "Chapter Heading";
    var colorMeStyleName = "Color Me";
    var colorMyTableStyleName = "Color My Table";
    var colorNames = [
        'Color1',
        'Color2',
        'Color3',
        'Color4',
        'Color5',
        // add extra colors here
    ];

    // script starts here:

    var doc = app.activeDocument;

    // a color for each chapter
    var chapterColors = getColors(doc, colorNames);
    if (!chapterColors)
        return;

    // this paragraph style delimits chapters
    var chapterStyle = doc.paragraphStyles.itemByName(delimiterParagraphStyleName);

    if (!chapterStyle.isValid)
        return alert('Paragraph Style "' + delimiterParagraphStyleName + '" is invalid.');

    // find the chapter headings
    var chapterHeadings = findGrep(doc, { appliedParagraphStyle: chapterStyle });

    if (!chapterHeadings)
        return alert('No chapter paragraphs found!');

    // an array of all the chapter heading pages
    var chapterPages = getPages(chapterHeadings);

    // we make an allocator function based on chapter pages
    var allocateToChapters = makePageAllocator(doc.pages, chapterPages);

    // allocate the colors to all the pages
    var colorsByPage = allocateToChapters(chapterColors);

    /* ----------------------------- *
     * Example 1: color the text   *
     * ----------------------------- */
    colorMe();

    /* ----------------------------- *
     * Example 2: color the tables   *
     * ----------------------------- */
    colorTables();


    /**
     * Example 1: Color any text in "Color Me" character style.
     */
    function colorMe() {

        // get the "Color Me" character style
        var colorMeStyle = doc.characterStyles.itemByName(colorMeStyleName);

        if (!colorMeStyle.isValid)
            return alert('Character Style "' + colorMeStyleName + '" is invalid.');

        // find the "Color Me" texts
        var texts = findGrep(doc, { appliedCharacterStyle: colorMeStyle });

        if (texts) {

            for (var i = 0, color, page; i < texts.length; i++) {

                page = getPage(texts[i]);

                if (!page)
                    // on pasteboard
                    continue;

                color = colorsByPage[page.documentOffset];

                if (!color)
                    // before the first chapter heading
                    continue;

                // color the text
                texts[i].fillColor = color;

            }

        }

    };


    /**
     * Example 2: Color any table containing text in "Color My Table" character style.
     */
    function colorTables() {

        // get the "Color My Table" character style
        var colorMyTableStyle = doc.characterStyles.itemByName(colorMyTableStyleName);

        if (!colorMyTableStyle.isValid)
            return; // alert('Character Style "' + colorMyTableStyleName + '" is invalid.');

        // find the "Color My Table" texts
        var texts = findGrep(doc, { appliedCharacterStyle: colorMyTableStyle });

        if (!texts)
            return;

        for (var i = 0, color, page; i < texts.length; i++) {

            page = getPage(texts[i]);

            if (!page)
                // on pasteboard
                continue;

            color = colorsByPage[page.documentOffset];

            if (!color)
                // before the first chapter heading
                continue;

            // color the text's table
            var table = texts[i].parent.parent;

            if ('Table' !== table.constructor.name)
                return;

            // color the text's cell
            texts[i].parent.fillColor = color;

            // color the cell borders
            table.cells.everyItem().properties = {
                topEdgeStrokeColor: color,
                leftEdgeStrokeColor: color,
                bottomEdgeStrokeColor: color,
                rightEdgeStrokeColor: color,
            };

        }

    };

};

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Color By Chapter');

/**
 * Returns an array with members corresponding to `allPages`,
 * and with payload distributed within, such that:
 *   - no payload before the first dividerPage
 *   - the first payload repeated at each array index
 *     until the second dividerPage
 *   - the second payload repeated at each array index
 *     until the third dividerPage
 *   - and so on ...
 *
 * Example: allocate colors across a document, using
 * chapter start pages as dividers. Each chapter will
 * have its own color.
 *
 * @author m1b
 * @version 2024-08-25
 * @param {Array<Page>|Pages} allPages - every page of the distribution.
 * @param {Array<Page>|Pages} dividerPages - pages which delimit areas, eg. chapters.
 * @param {Array<*>} items - the things to distribute.
 * @returns {Function}
 */
function makePageAllocator(allPages, dividerPages) {

    return (function (pages, dividers) {

        return function allocate(items) {

            var allocated = [];

            var itemIndex = -1;
            var dividerIndex = 0;
            var dividerPage = dividers[dividerIndex];

            pageLoop:
            for (var i = 0, page; i < pages.length; i++) {

                page = pages[i];

                while (
                    dividerIndex < dividers.length
                    && page.documentOffset >= dividerPage.documentOffset
                ) {

                    // next divider, next item
                    dividerIndex++;
                    itemIndex++;

                    if (itemIndex >= items.length)
                        // ran out of items
                        break pageLoop;

                    if (dividerIndex < dividers.length)
                        dividerPage = dividers[dividerIndex];

                }

                if (itemIndex < 0) {
                    // no item before the first divider
                    continue;
                }

                // add the item here
                allocated[i] = items[itemIndex];

            }

            return allocated;

        };

    })(allPages, dividerPages);

};

/**
 * Returns an array of `things` pages.
 * @author m1b
 * @version 2024-08-25
 * @param {Array<*>} things - array of things on a page.
 * @returns {Array<Page>}
 */
function getPages(things) {

    var pages = [];

    for (var i = 0, page; i < things.length; i++) {

        page = getPage(things[i]);

        if (page && page.isValid)
            pages.push(page);

    }

    return pages;

};

/**
 * Returns `thing`'s Page, if possible.
 * @author m1b
 * @version 2024-08-25
 * @param {*} thing - a thing with a Page.
 * @returns {Page?}
 */
function getPage(thing) {

    var page;

    if (thing.hasOwnProperty('parentPage'))
        page = thing.parentPage;

    else if (
        thing.hasOwnProperty('parentTextFrames')
        && thing.parentTextFrames.length > 0
    )
        page = thing.parentTextFrames[0].parentPage;

    else if (thing.hasOwnProperty('parent'))
        return getPage(thing.parent);

    if (
        undefined != page
        && page.isValid
    )
        return page;

};

/**
 * Returns `target`.findGrep result with the given `props`.
 * @author m1b
 * @version 2024-08-25
 * @param {App|Document|Story|TextFrame} target - the findGrep target.
 * @param {Object} props - an object with findGrepPreferences properties.
 * @returns {Array<Text>}
 */
function findGrep(target, props) {

    resetFindGrep();
    app.findGrepPreferences.properties = props;

    return target.findGrep();

};

/**
 * Resets grep preferences.
 */
function resetFindGrep() {

    // reset grep prefs
    app.findGrepPreferences = NothingEnum.NOTHING;
    app.changeGrepPreferences = NothingEnum.NOTHING;

    // find grep options
    app.findChangeGrepOptions.includeFootnotes = false;
    app.findChangeGrepOptions.includeHiddenLayers = false;
    app.findChangeGrepOptions.includeLockedLayersForFind = false;
    app.findChangeGrepOptions.includeLockedStoriesForFind = false;
    app.findChangeGrepOptions.includeMasterPages = false;

};

/**
 * Returns array of swatches matching `names`.
 * @author m1b
 * @version 2024-08-25
 * @param {Document} doc - an Indesign Document.
 * @param {Array<String>} names - the color names.
 * @returns {Array<Swatch>}
 */
function getColors(doc, names) {

    var found = [];

    for (var i = 0, c; i < names.length; i++) {

        c = doc.swatches.itemByName(names[i]);

        if (!c.isValid)
            return alert('Swatch "' + names[i] + '" is invalid.');

        found.push(c)

    }

    return found;

};

 

 

Robert at ID-Tasker
Brainiac
August 25, 2024

@m1b

 

What's the point of making bunch of local overrides? 

 

Also, as OP said:

 

Each Chapter has a key color that headings, Chapter Title, pull-out quote background, etc. would use the key color.  

 

So the only sensible solution is either Book - or complete duplication of all styles & redefiniton, for each Chapter. 

 

m1b
Adobe Expert
August 26, 2024

The point of local overrides? To avoid making variants of styles for each chapter, as OP said. No more to it.

Participating Frequently
August 25, 2024

To give more information: 

The book (which is actually a workbook) has 19 chapters not counting the Introduction and Front Matter. Each chapter has approximately 3-6 pages in it. Each Chapter has a key color that headings, Chapter Title, pull-out quote background, etc. would use the key color. 

 

After someone explained in more detail how to use a book  to achieve what i'm looking for. I think it probably is the best way to achieve it as "automatically" as possible since I can't script. So I'm going to try it. 

Robert at ID-Tasker
Brainiac
August 25, 2024

@Tommy30053592w60v

 

Yes, Book would be the easiest option.

 

Robert at ID-Tasker
Brainiac
August 24, 2024

@Tommy30053592w60v

 

Script would be the simplest solution.

 

Dave Creamer of IDEAS
Adobe Expert
August 24, 2024

I think the simplest solution would be to create a parent style and make a series of child styles. 

David Creamer: Community Expert (ACI and ACE 1995-2023)
Robert at ID-Tasker
Brainiac
August 24, 2024
quote

I think the simplest solution would be to create a parent style and make a series of child styles. 


By @Dave Creamer of IDEAS

 

Yes, but that's a lot of manual work. 

 

Script could go through all Char/ParaStyles used in each Story / Section - and create copies and base them on an extra "master" style for each Story / Section - unless there is a proper BasedOn structure already - which would require only one "master" style. 

 

Barb Binder
Adobe Expert
August 24, 2024

Hi @Tommy30053592w60v:

 

If you are comfortable using books, they really would work well for this workflow. 

 

Back to your original question, you can assign the character style based on the chapter number, but if you have a lot of chapters, this will dramatically slow down InDesign, because GREP styles are assign in realtime, as you edit.

 

This shows 3 GREP styles at play, and it's zippy. If you have 30 chapters or 300 chapters, it would not be a good way to go. 

 

 

Don't give up on scripting. We have a few talented folks here who just haven't seen this question yet. 

 

~Barb

Dave Creamer of IDEAS
Adobe Expert
August 24, 2024

Sorry, Barb. I was testing a sample file and didn't see your post until I refreshed! Great minds...

 

David Creamer: Community Expert (ACI and ACE 1995-2023)
Barb Binder
Adobe Expert
August 24, 2024

I like being included on your list of great minds! 😊

 

~Barb

Willi Adelberger
Adobe Expert
August 24, 2024

I recommend to make for each chapter an independent INDD document and asamble all in an INDB book dokument. All styles can be the same. But use in each chapter a different defined chapter color.

James Gifford—NitroPress
Brainiac
August 24, 2024

This is a good, simple approach — define one key color and change it for each chapter. The overall need is one of those "one and done" tasks that doesn't really need a script or GREP or automation unless the need (as for newsletters or reports is continual and ongoing. Just create a new chapter file, set the key color, and move on to the important stuff. 🙂

 

The one catch is that you will need to be very careful synchronizing the Book — be sure to omit colors from the sync list!