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

How to preserve character styles when merging text via javascript

Community Beginner ,
Feb 11, 2025 Feb 11, 2025

I'm using a script to merge multiple lines into one paragraph.

 

This is the start of a paragraph.

This line needs running back.

This line needs running back.

 

Result: 

This is the start of a paragraph. This line needs running back. This line needs running back.

 

I have code to produce this and it works perfectly. However, when there are characters that are bold or use a bold character style, when merged the bold characters are not retained or preserved.

 

I am attempting to use a custom indexof function (as i know idnesign does not support indexof) to try and match the characters or words when they shift position from their line to a different one.

 

For whatever reason i just can't get this to work. Does anyone have any suggestions i can feed to chatgpt as im inexperienced with code.

 

Please see below code that im using. Just to note, the code only works within the defined paragraph styles CAUTION, WARNING or NOTE.

 

Thanks,

// Custom indexOf function for word searching
function customIndexOf(text, searchWord) {
    var index = -1;

    // Ensure text and searchWord are strings
    if (typeof text !== "string" || typeof searchWord !== "string") {
        throw new Error("Both text and searchWord must be strings");
    }

    var wordLength = searchWord.length;

    // Iterate through the text to find exact match
    for (var i = 0; i <= text.length - wordLength; i++) {
        if (text.substr(i, wordLength) === searchWord) {
            index = i; // Found exact match
            break;
        }
    }
    return index;
}

// Function to merge paragraphs
function mergeParagraphs(paragraphsToMerge) {
    var firstParagraph = paragraphsToMerge[0];
    var mergedText = firstParagraph.contents;

    var boldedWords = [];

    // Collect all bolded words within the paragraphs to be merged
    for (var p = 1; p < paragraphsToMerge.length; p++) {  // Start from 1 to avoid duplicating first paragraph
        var paragraph = paragraphsToMerge[p];
        var paragraphText = paragraph.contents;

        var boldText = [];
        for (var i = 0; i < paragraph.texts.length; i++) {
            var text = paragraph.texts[i];
            if (text.appliedCharacterStyle.name === "Bold Copy") {
                boldText.push(text.contents); // Collect bold text
            }
        }

        // Store the bold words and their position
        boldedWords.push({
            text: paragraphText,
            bold: boldText
        });

        mergedText += " " + paragraphText;
        paragraph.remove();
    }

    mergedText = mergedText.replace(/\s{2,}/g, ' '); // Remove excessive spaces
    firstParagraph.contents = mergedText;

    // Now we need to apply the bold text in the merged paragraph
    for (var i = 0; i < boldedWords.length; i++) {
        var boldedWord = boldedWords[i].bold;
        for (var j = 0; j < boldedWord.length; j++) {
            // Using customIndexOf to find the word in the merged paragraph
            var index = customIndexOf(firstParagraph.contents, boldedWord[j]);

            if (index !== -1) {
                var charRange = firstParagraph.characters.itemByRange(index, index + boldedWord[j].length - 1);
                charRange.appliedCharacterStyle = app.activeDocument.characterStyles.itemByName("Bold Copy");
            }
        }
    }

    // Preserve the paragraph style (the style of the first paragraph)
    var firstParagraphStyle = paragraphsToMerge[0].appliedParagraphStyle;
    firstParagraph.appliedParagraphStyle = firstParagraphStyle;
}

// Function to apply style and merge paragraphs based on target style
function applyStyleAndMerge(targetStyle, stopStyles) {
    if (app.documents.length > 0) {
        var doc = app.activeDocument;

        if (app.selection.length > 0 && app.selection[0].constructor.name === "Text") {
            var selectedText = app.selection[0];
            var paragraphs = selectedText.paragraphs.everyItem().getElements();

            var applyingStyle = false;
            var rangeToMerge = [];
            var lastTargetStyleIndex = -1;

            function isInStopStyles(styleName) {
                for (var i = 0; i < stopStyles.length; i++) {
                    if (stopStyles[i] === styleName) {
                        return true;
                    }
                }
                return false;
            }

            for (var p = 0; p < paragraphs.length; p++) {
                if (paragraphs[p].appliedParagraphStyle.name === targetStyle) {
                    lastTargetStyleIndex = p;
                }
            }

            for (var p = 0; p < paragraphs.length; p++) {
                var paragraph = paragraphs[p];
                var isStopStyle = isInStopStyles(paragraph.appliedParagraphStyle.name);

                if (isStopStyle && applyingStyle) {
                    applyingStyle = false;
                    if (rangeToMerge.length > 1) {
                        mergeParagraphs(rangeToMerge);
                    }
                    rangeToMerge = [];
                }

                if (paragraph.appliedParagraphStyle.name === targetStyle) {
                    applyingStyle = true;
                    rangeToMerge.push(paragraph);
                } else if (applyingStyle && !isStopStyle) {
                    rangeToMerge.push(paragraph);
                }

                if (p === lastTargetStyleIndex && applyingStyle) {
                    applyingStyle = false;
                    if (rangeToMerge.length > 1) {
                        mergeParagraphs(rangeToMerge);
                    }
                    rangeToMerge = [];
                }
            }

            if (rangeToMerge.length > 1) {
                mergeParagraphs(rangeToMerge);
            }
        }
    }
}

var cautionStopStyles = ["WARNING", "NOTE", "Body Header", "Body Copy Bullet", "Safety Header", "Body Header CAPS", "Body Copy"];
applyStyleAndMerge("CAUTION", cautionStopStyles);

var warningStopStyles = ["CAUTION", "NOTE", "Body Header", "Body Copy Bullet", "Safety Header", "Body Header CAPS", "Body Copy"];
applyStyleAndMerge("WARNING", warningStopStyles);

var noteStopStyles = ["CAUTION", "WARNING", "Body Header", "Body Copy Bullet", "Safety Header", "Body Header CAPS", "Body Copy"];
applyStyleAndMerge("NOTE", noteStopStyles);

 

TOPICS
Scripting
514
Translate
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 ,
Feb 11, 2025 Feb 11, 2025

Are you tracking whole words?

Instead of tracking whole words, track the character indices of bold text before merging.
Reapply the bold character style using these indices after merging.

Translate
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 ,
Feb 11, 2025 Feb 11, 2025

There are two ways of applying a paragraph style. You use this:

firstParagraph.appliedParagraphStyle = firstParagraphStyle;

which, as you found, removes local formatting. To preserve local formatting, use this:

firstParagraph.applyParagraphStyle (firstParagraphStyle, false);

where 'false' means 'leave local overrides alone'.

 

So there's no need any longer for looking for those bold words, but if you wanted to, note that JavaScript (not to be confused with InDesign) does have myString.indexOf(). Check he object model.

Translate
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
LEGEND ,
Feb 11, 2025 Feb 11, 2025

@Niko32511349sm76

 

This isn't all of the code? 

 

How do you find paragraphs to merge? 

 

Which style should stay as the final ParaStyle? 

 

Translate
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
LEGEND ,
Feb 11, 2025 Feb 11, 2025

@Niko32511349sm76

 

OK, found it. Sorry, I'm on my phone so it's not very convenient.

 

If I may suggest - my personal way of coding: 

- instead of iterating though the array of the "stop styles" - use indexOf() on the string - you can join the array first, if you prefer to keep list of paragraphs as an array - then you've one line instead of whole loop - will be also faster,

- can't check right now - but I would just go over the paragraphs - and if next one is "correct" and should be merged - just remove "enter" in between them(*). 

 

The above - will make your code shorter and faster. 

 

But I'm not JS guy - maybe JS requires a different approach. 

 

Translate
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
LEGEND ,
Feb 11, 2025 Feb 11, 2025

(*) this way - you'll achieve even more than what @Peter Kahrel suggested - you're completely shifting "responsibility" of keeping stuff organised on InDesign.

 

It's the same as if would you've done in UI manually - following paragraph gets formatting - and applied ParaStyle - from the preceding paragraph. 

 

Translate
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
LEGEND ,
Feb 11, 2025 Feb 11, 2025

The second point - this way you also avoid pushing things to a collection and processing this collection again. 

 

Translate
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 ,
Feb 12, 2025 Feb 12, 2025

Hi @Niko32511349sm76, I've read the posts on this page and they are all good answers, but I didn't see my specific approach so I'll share a demo script here. It sounds like it might not exactly be what you need, but maybe it helps your understanding? Test first with the attached demo.indd if you like. I apply the paragraph style without overriding local styling, as @Peter Kahrel showed, and remove the line break as @Robert at ID-Tasker showed.

- Mark

demo.gif

 

/**
 * Demonstration of merging paragraphs.
 *
 * @author m1b
 * @version 2025-02-13
 * @discussion https://community.adobe.com/t5/indesign-discussions/how-to-preserve-character-styles-when-merging-text-via-javascript/m-p/15145001
 */
function main() {

    var doc = app.activeDocument,
        testParagraphs = doc.stories[0].paragraphs.everyItem().getElements();

    var mergedParagraph = mergeParagraphs(testParagraphs);

    if (mergedParagraph.isValid)
        // applying a demonstration paragraph style
        mergedParagraph.applyParagraphStyle(doc.paragraphStyles.itemByName('Demo Style'), false);

};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Merge Paragraphs');

/**
 * Merge multiple paragraphs into one.
 * 
 * Important: assumes paragraphs are
 * continguous in the same story.
 * @author m1b
 * @version 2025-02-13
 * @param {Array<Paragraph>} paragraphs - the paragraphs to merge.
 * @param {String} [delimiter] - a string to replace line breaks.
 * @returns {Paragraph} - the merged paragraph;
 */
function mergeParagraphs(paragraphs, delimiter) {

    var endingLinefeed = /\s*[\n\r]$/,
        para;

    for (var i = paragraphs.length - 1, match; i >= 0; i--) {

        para = paragraphs[i];
        match = para.contents.match(endingLinefeed);

        if (!match)
            continue;

        // remove the line feed and replace with delimiter
        para.characters
            .itemByRange(para.characters[-match[0].length], para.characters[-1])
            .contents = delimiter || '\u0020';
    }

    // return the first paragraph, which is the merged paragraph
    return para;

};

 

 

Translate
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
LEGEND ,
Feb 12, 2025 Feb 12, 2025

@m1b

 

Haven't seen INDD file - but don't see in the code any references to stop styles? 

 

Translate
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 ,
Feb 12, 2025 Feb 12, 2025

Robert, it's just a quick demo of merging paragraphs, not collecting the paragraphs.

Translate
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
LEGEND ,
Feb 12, 2025 Feb 12, 2025
LATEST

Not sure why you're looking for "\n" and remove extra spaces? Just last character should be replaced with a space - or whatever separator OP wants.

 

Or simply F/C can be executed on the block of Paragraphs?

 

But it's still less optimal - the same paragraphs are processed twice - or in this case - six times - than going through all Paragraphs and merging them at the same time. 

 

All three types of blocks - caution warning, note - can be checked in one pass. 

 

Unless I'm missing something? 

 

Translate
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