Copy link to clipboard
Copied
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);
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
This isn't all of the code?
How do you find paragraphs to merge?
Which style should stay as the final ParaStyle?
Copy link to clipboard
Copied
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.
Copy link to clipboard
Copied
(*) 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.
Copy link to clipboard
Copied
The second point - this way you also avoid pushing things to a collection and processing this collection again.
Copy link to clipboard
Copied
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
/**
* 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;
};
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Robert, it's just a quick demo of merging paragraphs, not collecting the paragraphs.
Copy link to clipboard
Copied
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?
Find more inspiration, events, and resources on the new Adobe Community
Explore Now