Copy link to clipboard
Copied
Hi,
I have multiple textframes threaded. Page as been setup properly as I wanted. I want to put a soft return at the end of line. If there is a new paragraph change, it will ignore that. I have marked it with red circles, the places where I want soft-returns. How to do this with a script. I want to target a particular para style.
1 Correct answer
Hi @Bedazzled532, try this script. You need to set the name of the target paragraph style in the settings object. Let me know if it works for you.
- Mark
/**
* @file Force Line Breaks.js
*
* Adds a line break at the end of every line of text
* of selected items or, optionally, the whole document.
*
* Notes:
* - only processes text in the target paragraph style
* (edit the settings object below).
* - makes a valiant attempt at hardening justified text,
* but only handles l
...
Copy link to clipboard
Copied
Hi @Bedazzled532, try this script. You need to set the name of the target paragraph style in the settings object. Let me know if it works for you.
- Mark
/**
* @file Force Line Breaks.js
*
* Adds a line break at the end of every line of text
* of selected items or, optionally, the whole document.
*
* Notes:
* - only processes text in the target paragraph style
* (edit the settings object below).
* - makes a valiant attempt at hardening justified text,
* but only handles left-justified text.
*
* @author m1b
* @version 2025-03-29
* @discussion https://community.adobe.com/t5/indesign-discussions/soft-return-at-end-of-line/m-p/15236469
*/
function main() {
var settings = {
/* only paragraphs in this style will be processed (set to `undefined` to disable filtering) */
paragraphStyleName: 'My Best Paragraph Style',
};
app.scriptPreferences.measurementUnit = MeasurementUnits.POINTS;
const EXPAND_JUSTIFIED_TEXT_FRAME = 3,
SQUASH_AMOUNT = -100;
var doc = app.activeDocument,
stories = getSelectedStories(doc.selection);
if (0 === stories.length) {
// check with user
if (!confirm('Do you want to apply to whole document?'))
return;
// no selected stories, so get every story from document
stories = doc.stories.everyItem().getElements();
}
var spaceCharacter = /\s/,
linefeeds = /[\n\r]/;
// we'll slightly expand the text frames later on
var framesToAdjust = {};
storyLoop:
for (var i = stories.length - 1; i >= 0; i--) {
var paragraphs = stories[i].paragraphs.everyItem().getElements();
paragraphsLoop:
for (var j = paragraphs.length - 1; j >= 0; j--) {
var paragraph = paragraphs[j];
if (
undefined != settings.paragraphStyleName
&& settings.paragraphStyleName !== paragraph.appliedParagraphStyle.name
)
continue paragraphsLoop;
var isJustified = (Justification.LEFT_JUSTIFIED === paragraph.justification);
var lines = paragraph.lines.everyItem().getElements(),
// store character positions
positions = [];
linesLoop:
for (var k = lines.length - 1; k >= 0; k--) {
var line = lines[k],
suffix;
if (0 === line.parentTextFrames.length)
continue linesLoop;
if (
linefeeds.test(line.characters[-1].contents)
|| SpecialCharacters.FORCED_LINE_BREAK === line.characters[-1].contents
|| !line.parentStory.characters[line.characters[-1].index + 1].isValid
)
// last line in paragraph or story
continue linesLoop;
suffix = !spaceCharacter.test(line.characters[-1].contents)
? suffix = '-\n' // hyphenated word
: suffix = '\n';
if (isJustified)
// store positions for later
positions.unshift(line.characters.everyItem().horizontalOffset);
// force line break
line.insertionPoints[-1].contents = suffix;
}
if (!isJustified)
continue paragraphsLoop;
// now we harden the justification, line by line
// start by aligning left (let me know if you need RTL)
paragraph.justification = Justification.LEFT_ALIGN;
// collect all the parent text frames
for (var f = 0; f < paragraph.parentTextFrames.length; f++) {
var tf = paragraph.parentTextFrames[f];
if (!framesToAdjust[tf.id])
framesToAdjust[tf.id] = tf;
}
// temporarily squash text up a bit to stop unwanted line breaking
squashLoop:
for (var k = paragraph.lines.length - 2; k >= 0; k--) {
if (
!paragraph.lines[k].isValid
|| 0 === paragraph.lines[k].parentTextFrames.length
)
continue squashLoop;
paragraph.lines[k].characters.itemByRange(
paragraph.lines[k].characters[0],
paragraph.lines[k].characters[-4]
).tracking = SQUASH_AMOUNT;
}
// refresh the lines reference
lines = paragraph.lines.everyItem().getElements();
linePositionsLoop:
for (var l = 0; l < lines.length; l++) {
var linePositions = positions[l];
if (!linePositions)
continue linePositionsLoop;
var len = Math.min(lines[l].characters.length, linePositions.length);
charactersLoop:
for (var c = 1; c < len; c++) {
var ch0 = paragraph.lines[l].characters[c - 1],
ch1 = paragraph.lines[l].characters[c],
pos = ch1.horizontalOffset,
target = linePositions[c];
if (undefined == target)
continue charactersLoop;
ch0.tracking += (target - pos) * (1000 / ch0.pointSize);
}
}
}
}
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Force Line Breaks');
/**
* Returns the document's selected stories.
* @author m1b
* @version 2025-03-26
* @param {Array<PageItem>} items - the items from which to gather stories.
* @returns {Array<Story>}
*/
function getSelectedStories(items) {
var stories = [],
unique = {};
for (var i = 0; i < items.length; i++) {
if ('TextFrame' === items[i].constructor.name) {
if (unique[items[i].parentStory.id])
// already collected this story
continue;
unique[items[i].parentStory.id] = true;
items[i] = items[i].parentStory;
}
if ('function' === typeof items[i].changeGrep)
stories.push(items[i]);
}
return stories;
};
Edit 2025-03-29: added handling of left-justified text; it will "harden" the justification using custom tracking and the lines are fixed, even if you expand the text frame. Like this:
Copy link to clipboard
Copied
Copy link to clipboard
Copied
@m1b Thanks it worked!!
Copy link to clipboard
Copied
@Bedazzled532 I notice in your example screenshot you have justified text. Just for fun I've updated the script above to handle left-justified text so that the frame can be expanded without interfering with the spacing.
- Mark
Copy link to clipboard
Copied
Thanks a ton!!
Copy link to clipboard
Copied
You're welcome. It might need some changes to work with Arabic or other RTL text though.
Copy link to clipboard
Copied
Whalt purpose should those soft return have?
Copy link to clipboard
Copied
Whalt purpose should those soft return have?
By Willi Adelberger
It's a mockup - but it probably has something to do with the other thread - and changing Word spacing.
Copy link to clipboard
Copied
Copy link to clipboard
Copied
It will be a same paragraph and yet in new line. That's why soft return.
By Bedazzled532
Yes, but normally, you don't have to do this - only in some special circumstances - that's what @Willi Adelberger would like to know - your special reason.
Copy link to clipboard
Copied
But without these soft returns you would get the same result with less work. When the lne ends the text goes on on the next line in the same paragraph.,
Copy link to clipboard
Copied
@Willi Adelberger Actually I want to apply this on a placeholder threaded frame. It actually has 3 setions. First threaded frame where I want to apply this is in Arabic Language. All arabic threaded together. Second section is the translations threaded together and 3rd section is transliteration frames threaded together. I want to use different arabic fonts in section 1 and also I want to hard code the breaks. This will prevent me from error if any arabic word goes to next frame, I can identify it easily.
Copy link to clipboard
Copied
This one-liner does pretty much what you need - but as it modifies ALL text lines - it requires some cleaning:
app.activeDocument.stories.everyItem().lines.everyItem().insertionPoints[-1].contents = "\n";
app.findTextPreferences = NothingEnum.nothing;
app.changeTextPreferences = NothingEnum.nothing;
app.findTextPreferences.findWhat = "\r\n";
app.changeTextPreferences.changeTo = "\r";
app.activeDocument.changeText();
Copy link to clipboard
Copied
@Robert at ID-Tasker Thanks it worked.
Copy link to clipboard
Copied
Great 😉 but I'll have to "fix" it 😉 as you need to only modify a specific ParaStyle - piece of cake - but I'm not at home right now.

