Copy link to clipboard
Copied
I'm looking for a script or some way to easily assign different paragraph styles to hundreds of text boxes inside a document all based on the format of the text. All of the text is identical in format, but on different pages. Obviously, I could just go and create styles based on the first page's text and then go and assign it on each subsequent page, but that is hundreds of pages and I'd like to avoid that if I can.
The backstory: I have a designer who reports to me that is unfamiliar with Paragraph styles, but they just completed a document that I would like to make more evergreen. They ran a data merge, but didn't assign any styles prior to creating the merged document and then had hours of work afterwards adjusting graphics on each page. All of the text is the same and based on the initial text box, so is there a faster way than going through one by one to assign styles to all of the pages? That way, if anything needs changed moving foward we can easily adjust the style instead of having to adjust each page individually.
Any help would be greatly appreciated — I'm new to using scripts and have no idea how to make them myself, but after using a few to speedup my workflow I figure it's worth asking before doing it all manually.
This would get all of the document’s text frames where the first word is "Compliments" then style the next 5 lines:
var doc = app.activeDocument;
var api = doc.allPageItems;
var tf;
for (var i = 0; i < api.length; i++){
//gets all text frames where the first word of the frame is "Compliments"
if (api[i].constructor.name == "TextFrame" && api[i].paragraphs[0].words[0].contents == "Compliments") {
tf = api[i]
//Assigns Paragraph Styles named Name, Title, Email, Phone,
Copy link to clipboard
Copied
Can you show us an exemple of the text frames? Maybe you don't need a script, but a simple (or GREP) find and replace could work.
Copy link to clipboard
Copied
It's a single text frame per spread with the same basic information.
When they did the merge it went as follows:
Rep's Name
Title
Phone number
companyname.com
Each rep name woul need to be it's own style and then the info that follows could be one paragraph style — I would like some of the elements to be bolded but I know just enough GREP that I think I can easily create a character style and apply those. The format should be consistent on each page with maybe a few exceptions, but so long as I can do a majority automatically I wouldn't mind doing some manual formatting.
Copy link to clipboard
Copied
This would get all of the document’s text frames where the first word is "Compliments" then style the next 5 lines:
var doc = app.activeDocument;
var api = doc.allPageItems;
var tf;
for (var i = 0; i < api.length; i++){
//gets all text frames where the first word of the frame is "Compliments"
if (api[i].constructor.name == "TextFrame" && api[i].paragraphs[0].words[0].contents == "Compliments") {
tf = api[i]
//Assigns Paragraph Styles named Name, Title, Email, Phone, and Company to lines 2 thru 6
tf.paragraphs[1].appliedParagraphStyle = makeParaStyle(doc, "Name")
tf.paragraphs[2].appliedParagraphStyle = makeParaStyle(doc, "Title")
tf.paragraphs[3].appliedParagraphStyle = makeParaStyle(doc, "Email")
tf.paragraphs[4].appliedParagraphStyle = makeParaStyle(doc, "Phone")
tf.paragraphs[5].appliedParagraphStyle = makeParaStyle(doc, "Company")
}
};
/**
* Makes a new named ParagraphStyle if it does not already exist
* @ param the document to add the style to
* @ param style name
* @ return the new paragraph style
*/
function makeParaStyle(d, n){
if (d.paragraphStyles.itemByName(n).isValid) {
return d.paragraphStyles.itemByName(n);
} else {
return d.paragraphStyles.add({name:n});
}
}
Copy link to clipboard
Copied
Hi Rob!
Thank you so much for this script, it worked well for the specific document. I ended up playing around with ChatGPT and through many trials and errors I created a script that allows you to specify a font, style and size to apply a paragraph style throughout an entire document. I have 0 scripting experience so I can't say this code is as clean and simple as it should be, but it is exactly what I was looking for and I figured I should share it!
#target indesign
function applyParagraphStyleToAllText() {
var doc = app.activeDocument;
// Create a dialog window
var dlg = new Window('dialog', 'Apply Paragraph Style to All Text');
// Paragraph Style Listbox
dlg.add('statictext', undefined, 'Select Paragraph Style:');
var paragraphStyleListbox = dlg.add('listbox', [0, 0, 200, 200], getAllParagraphStyles(doc));
paragraphStyleListbox.selection = 0; // Default to the first style
// Font Family Listbox
dlg.add('statictext', undefined, 'Select Font Family:');
var fontFamilyListbox = dlg.add('listbox', [0, 0, 200, 200], getAllFontFamilies());
fontFamilyListbox.selection = 0; // Default to the first font family
// Font Style Dropdown
dlg.add('statictext', undefined, 'Select Font Style:');
var fontStyleDropdown = dlg.add('dropdownlist', undefined, getFontStyles(fontFamilyListbox.selection.text));
fontStyleDropdown.selection = 0; // Default to the first style
// Update font styles when the font family changes
fontFamilyListbox.onChange = function() {
var selectedFontFamily = fontFamilyListbox.selection.text;
fontStyleDropdown.removeAll();
var styles = getFontStyles(selectedFontFamily);
for (var i = 0; i < styles.length; i++) {
fontStyleDropdown.add('item', styles[i]);
}
fontStyleDropdown.selection = 0;
};
// Font Size Input
dlg.add('statictext', undefined, 'Enter Font Size:');
var fontSizeInput = dlg.add('edittext', [0, 0, 100, 20], '12'); // Default size is 12
// Add buttons
var btnGroup = dlg.add('group');
btnGroup.add('button', undefined, 'Apply', {name: 'ok'});
btnGroup.add('button', undefined, 'Cancel', {name: 'cancel'});
// Show the dialog
var dlgResult = dlg.show();
// Handle dialog response
if (dlgResult === 1) { // 1 indicates 'ok' was clicked
var selectedParagraphStyleName = paragraphStyleListbox.selection.text;
var selectedFontFamily = fontFamilyListbox.selection.text;
var selectedFontStyle = fontStyleDropdown.selection.text;
var enteredFontSize = parseFloat(fontSizeInput.text); // Read font size as a number
$.writeln("Selected Paragraph Style: " + selectedParagraphStyleName); // Log selected style
$.writeln("Selected Font Family: " + selectedFontFamily); // Log selected font family
$.writeln("Selected Font Style: " + selectedFontStyle); // Log selected font style
$.writeln("Entered Font Size: " + enteredFontSize); // Log entered font size
if (isNaN(enteredFontSize) || enteredFontSize <= 0) {
alert("Please enter a valid font size.");
return;
}
var paragraphStyle;
try {
paragraphStyle = findParagraphStyleByName(doc, selectedParagraphStyleName);
if (!paragraphStyle.isValid) {
throw new Error("The selected paragraph style does not exist.");
}
} catch (e) {
alert("Error: " + e.message);
return;
}
var textFrames = doc.textFrames.everyItem().getElements();
$.writeln("Number of text frames: " + textFrames.length); // Log number of text frames
var appliedCount = 0;
for (var i = 0; i < textFrames.length; i++) {
var textFrame = textFrames[i];
var paragraphs = textFrame.paragraphs.everyItem().getElements();
$.writeln("Text Frame " + i + " contains " + paragraphs.length + " paragraphs."); // Log paragraphs in each text frame
for (var j = 0; j < paragraphs.length; j++) {
var paragraph = paragraphs[j];
// Check if the paragraph has the specified character format
var characters = paragraph.characters.everyItem().getElements();
for (var k = 0; k < characters.length; k++) {
var character = characters[k];
if (character.appliedFont.fontFamily === selectedFontFamily && character.appliedFont.fontStyleName === selectedFontStyle && character.pointSize == enteredFontSize) {
$.writeln("Applying style to paragraph with contents: " + paragraph.contents); // Log paragraph contents
paragraph.appliedParagraphStyle = paragraphStyle;
appliedCount++;
break;
}
}
}
}
alert("Applied paragraph style to " + appliedCount + " paragraphs.");
}
}
function getAllParagraphStyles(doc) {
var paragraphStyles = [];
function addStyles(styles) {
for (var i = 0; i < styles.length; i++) {
var style = styles[i];
if (style.name !== "[Basic Paragraph]") { // Exclude the default style if desired
paragraphStyles.push(style.name);
}
}
}
function addStylesFromGroups(groups) {
for (var j = 0; j < groups.length; j++) {
var group = groups[j];
addStyles(group.paragraphStyles);
addStylesFromGroups(group.paragraphStyleGroups);
}
}
addStyles(doc.paragraphStyles);
addStylesFromGroups(doc.paragraphStyleGroups);
return paragraphStyles;
}
function findParagraphStyleByName(doc, name) {
var styles = doc.paragraphStyles;
var groups = doc.paragraphStyleGroups;
function searchStyles(styles) {
for (var i = 0; i < styles.length; i++) {
if (styles[i].name === name) {
return styles[i];
}
}
return null;
}
function searchGroups(groups) {
for (var j = 0; j < groups.length; j++) {
var group = groups[j];
var style = searchStyles(group.paragraphStyles);
if (style) {
return style;
}
style = searchGroups(group.paragraphStyleGroups);
if (style) {
return style;
}
}
return null;
}
var style = searchStyles(styles);
if (style) {
return style;
}
return searchGroups(groups);
}
function getAllFontFamilies() {
var fontFamilies = [];
var fontFamilyMap = {}; // Use an object to track unique font families
var fonts = app.fonts.everyItem().getElements();
for (var i = 0; i < fonts.length; i++) {
var fontFamily = fonts[i].fontFamily;
// Debugging: Check if fontFamily is a string
if (typeof fontFamily !== 'string') {
alert("fontFamily is not a string. Value: " + fontFamily);
continue; // Skip this iteration
}
// Use the object to track if the font family has already been added
if (!fontFamilyMap[fontFamily]) {
fontFamilyMap[fontFamily] = true; // Mark this font family as added
fontFamilies.push(fontFamily);
}
}
return fontFamilies;
}
function getFontStyles(fontFamily) {
var fontStyles = [];
var fonts = app.fonts.everyItem().getElements();
for (var i = 0; i < fonts.length; i++) {
if (fonts[i].fontFamily === fontFamily) {
fontStyles.push(fonts[i].fontStyleName);
}
}
return fontStyles;
}
function getFontSizes() {
var fontSizes = [];
for (var i = 6; i <= 72; i++) { // Common font sizes from 6 to 72
fontSizes.push(i.toString());
}
return fontSizes;
}
applyParagraphStyleToAllText();
Copy link to clipboard
Copied
This part:
var textFrames = doc.textFrames.everyItem().getElements();
$.writeln("Number of text frames: " + textFrames.length); // Log number of text frames
var appliedCount = 0;
for (var i = 0; i < textFrames.length; i++) {
var textFrame = textFrames[i];
var paragraphs = textFrame.paragraphs.everyItem().getElements();
...
} ;
should be done differently - if you want to process all texts in the document - you should iterate through doc.stories collection.
In InDesign's scripting DOM, TextFrame can't exist on its own - it's always associated with a Story - and Story always have at least one TextFrame.
If you have a Document with only single TextFrame Stories - no big deal - but if you'll have multi TextFrame Stories - you might end up with processing same paragraphs twice - when they split between TextFrames. No big deal - just waste of time.
So, it's a good practice to rather process Stories instead of TextFrames.
Copy link to clipboard
Copied
@Robert at ID-Tasker That's really good information to know — like I mentioned before, I have no knowledge of coding. To make this code I had to start from a very simple idea and add complexity with each step all while troubleshooting each new code. Without knowing the underlying mechanics of how InDesign works it's impossible to know why something is breaking and the AI seems to have a hard time making the most optimal choices. An example would be creating the scrollable menu for Paragraph styles: The first iteration only included styles that weren't grouped. Troubleshooting that alone was at least an hour of constant back and forths so hopefully as I continue my learning I can start understanding why something may be failing.
I appreciate your help and any additional resources you could recommned would be greatly appreciated as I being my journey into scripting!
Copy link to clipboard
Copied
You are welcome.
I would rather suggest that you stop wasting your time on using AI - and just post questions here.
Here is something that should help you a bit:
https://abg.ee/wp-content/uploads/2019/10/Adobe-Intro-To-Scripting.pdf
Then:
https://www.indesignjs.de/extendscriptAPI/indesign-latest/
Copy link to clipboard
Copied
Hi @Zach Field , Just to clarify, do all of the pages have the same number of text frames, and each text frame on page 1 is styled differently?
Copy link to clipboard
Copied
The original template that we used to generate the merge had one text frame.
It was formatted like this:
Rep's Name
Title
Phone number
companyname.com
The rep's name was stylized differently, but the remaining text was formatted the same and each has a paragraph break.
If it weren't for the edits and deadline , I would have them redo the merge with the proper styles. I may just need to redo the document, but if there was a way to apply styles using a script it would help me with this and other documents. Until I joined the team, no one used styles properly.Most documents were created by duplicating pages and formatting each copy block individually.
Copy link to clipboard
Copied
If you work on a PC - it would be a breeze with my ID-Tasker - not free, but I can give you access to the full version for a few days.
You could process only specific pages, layers, locations, etc. And you can easily change number of paragraphs to be styled and their order.
And you can of course process unlimited number of files automatically - even overnight.