Copy link to clipboard
Copied
Currently working in InDesign cc2022.
I found this older thread that seems like the exact thing I need, but the code didn't work for me.
https://community.adobe.com/t5/indesign-discussions/script-random-colours-on-letters/td-p/7073766
Hi @Paperback Pat, okay so on private message you've added a couple of new parts: (1) target a paragraph style, and (2) specify and a color order with no repetitions. By supplying a demo document (I've attached it here for anyone to follow along) you've also confirmed that you only want colors from a specific color group. That's enough information for me to configure the script. I'll list it again, rather than update the original one, because this also has the extra function `getTexts` which is
...Copy link to clipboard
Copied
Forum migration stripped iterators a while back. In the last line, it should read "chars[i]" at the start of the line, without quotes. I think that's the only change needed.
Copy link to clipboard
Copied
Thank you so much, that worked!
Same as the OP from the link above, I wonder how to limit this to a Color Group within Swatches.
Any ideas?
Copy link to clipboard
Copied
Any ideas?
Hi @Paperback Pat , The OP wanted to skip the default Paper, None, Black, and Registration swatchesāyou could do that like this:
var tf = app.activeDocument.selection[0];
var chars = tf.characters.everyItem().getElements();
var col = app.activeDocument.swatches.length-1;
var c
for (var i=0; i<chars.length; i++){
c = app.activeDocument.swatches[Math.floor((Math.random() * col) + 1)]
if (c.name!="Black" || c.name!="Paper" || c.name!="Registration"|| c.name!="None") {
chars[i].fillColor=c
}
}
Or to use a group you could do this where "MyGroup" is the name of the group you want to use:
var cg = app.activeDocument.colorGroups.itemByName("MyGroup").colorGroupSwatches.everyItem().getElements()
var tf = app.activeDocument.selection[0];
var chars = tf.characters.everyItem().getElements();
var col = cg.length-1;
var c
for (var i=0; i<chars.length; i++){
c = cg[Math.floor((Math.random() * col) + 1)]
chars[i].fillColor = c.swatchItemRef
}
Copy link to clipboard
Copied
@Paperback Pat I wrote a function `getDocumentColors` for getting colors from a document. I think this will be handy for many cases. It gives option for including and excluding both Swatches and ColorGroups, or a filter option.
Also the `colorCharacters` function is a bit flexible by way of using custom characterPicker or colorPicker functions as parameters. But you can ignore all those details if not relevent to your situation. Just try it out.
Let me know if it helps, or isn't quite working the way you wantāthere's a good chance it'll be easy to adjust.
- Mark
/**
* @file Color Characters.js
*
* There are two working parts to this script:
*
* 1. `getDocumentColors` function, which will collect colors
* from the document based on the parameters specified; and
*
* 2. `colorCharacters` function, which assigns the colors
* from (1) to the characters of the selected items.
*
* See function documentation below for info.
*
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/indesign-script-to-randomly-apply-color-swatches-to-text-characters/m-p/14567096
*/
function main() {
if (0 === app.documents.length)
return alert('Please select some text and try again.');
var doc = app.activeDocument,
colors = getDocumentColors({
doc: doc,
// includeSwatches: ['C=100 M=0 Y=0 K=0', 'C=0 M=100 Y=0 K=0'],
excludeSwatches: ['None', 'Registration', 'Paper'],
// includeColorGroups: ['MyWantedColorGroup'],
// excludeColorGroups: ['MyUnwantedColorGroup'],
// filter: function (swatchName) { return /^PANTONE/.test(swatchName) },
});
if (0 === colors.length)
return alert('No colors were found with the current options.');
// do the coloring
colorCharacters(doc.selection, colors);
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Color Characters');
/**
* Returns an array of colors from a document.
* ----------------------------------------------
* General usage notes:
* - leave `includeSwatches` undefined to use every swatch
* - using `filter` will override all other parameters.
* ----------------------------------------------
* Example 1 - Get all swatches, except some:
*
* var colors = getDocumentColors({
* doc: doc,
* excludeSwatches: ['None', 'Registration', 'Paper'],
* });
* ----------------------------------------------
* Example 3 - Get all swatches in a color group:
*
* var colors = getDocumentColors({
* doc: doc,
* includeColorGroup: 'My Favourite Colours',
* });
* ----------------------------------------------
* Example 2 - Get all swatches with name starting "PANTONE":
*
* var colors = getDocumentColors({
* doc: doc,
* filter: function (swatchName) {
* return /^PANTONE/.test(swatchName);
* },
* });
* ----------------------------------------------
* @author m1b
* @version 2024-04-20
* @param {Object} options
* @param {Array<String>} [options.includeSwatches] - swatch names to specifically include (default: include all swatches).
* @param {Array<String>} [options.excludeSwatches] - swatch names to specifically exclude (default: no exclusions).
* @param {Array<String>} [options.includeColorGroups] - color group names to specifically include (default: include all color groups).
* @param {Array<String>} [options.excludeColorGroups] - color group names to specifically exclude (default: no exclusions).
* @param {Function} [options.filter] - function that returns true to include swatch (default: no filter).
* @returns {Array<Color>}
*/
function getDocumentColors(options) {
options = options || {};
var doc = options.doc || app.activeDocument,
includeSwatches = options.includeSwatches || [],
excludeSwatches = options.excludeSwatches || [],
includeColorGroups = options.includeColorGroups || [],
excludeColorGroups = options.excludeColorGroups || [],
filter = options.filter;
const INCLUDE_ALL_SWATCHES = (
0 === includeColorGroups.length
&& 0 === includeSwatches.length
&& undefined == filter
);
var allSwatches = doc.colorGroups.everyItem().colorGroupSwatches.everyItem().swatchItemRef,
swatches = [];
for (var i = 0, sw; i < allSwatches.length; i++) {
sw = allSwatches[i];
if (undefined != filter) {
// include according to filter function
if (filter(sw.name))
swatches.push(sw);
else
continue;
}
if (
getThing(excludeSwatches, undefined, sw.name)
|| getThing(excludeColorGroups, undefined, sw.parentColorGroup.name)
)
// ignore specifically-excluded swatch
continue;
if (
// include all swatches by default
INCLUDE_ALL_SWATCHES
// include swatch in specific color group
|| getThing(includeColorGroups, undefined, sw.parentColorGroup.name)
// include specific swatch
|| getThing(includeSwatches, undefined, sw.name)
)
swatches.push(sw);
}
return swatches;
};
/**
* Assigns fill color to selected text randomly.
* ----------------------------------------------
* Example `colorIndexPicker` functions:
*
* function cycler(ch, index, max) {
* // return the next value, in order, cycling
* return index % max;
* };
*
* ----------------------------------------------
* Example `characterPicker` functions:
*
* function pickHalfCharacters() {
* return Math.random() < 0.5;
* }
*
* function pickEveryNthCharacter(ch, index) {
* var n = 2;
* return (index + 1) % n === 0;
* }
* ----------------------------------------------
* @author m1b
* @version 2024-04-20
* @param {Array|Collection<TextFrame|Text>} things - array or collection of DOM object's with characters.
* @param {Array<Color|null>} colors - array of colors to use.
* @param {Function} [colorIndexPicker] - function that determines which color to use (default: pick random color).
* @param {Function} [characterPicker] - function that determines if a character is colored (default: pick every character).
*/
function colorCharacters(things, colors, colorIndexPicker, characterPicker) {
colorIndexPicker = colorIndexPicker || defaultColorPicker;
characterPicker = characterPicker || defaultCharacterpPicker;
for (var i = 0, characters; i < things.length; i++) {
if (!things[i].hasOwnProperty('characters'))
continue;
if (things[i].hasOwnProperty('pageItems'))
colorCharacters(things[i].pageItems, colors, colorIndexPicker, characterPicker);
if (things[i].hasOwnProperty('tables'))
colorCharacters(things[i].tables.everyItem().cells, colors, colorIndexPicker, characterPicker);
if (things[i].hasOwnProperty('footnotes'))
colorCharacters(things[i].footnotes, colors, colorIndexPicker, characterPicker);
characters = things[i].characters;
characterLoop:
for (var j = 0, r, len = characters.length; j < len; j++) {
if (!characterPicker(characters[j], j, len - 1))
continue characterLoop;
r = colorIndexPicker(characters[j], j, colors.length);
characters[j].fillColor = colors[r];
}
}
function defaultColorPicker(ch, index, max) {
return Math.floor(Math.random() * max);
};
function defaultCharacterpPicker() {
return true;
};
};
/**
* Returns a thing with a `key` matching `value`.
* If `key` is undefined, evaluate the object itself.
* @author m1b
* @version 2024-04-20
* @param {Array|Collection} things - the things to look through.
* @param {String} [key] - the property name (default: undefined).
* @param {*} value - the value to match.
* @returns {*}
*/
function getThing(things, key, value) {
for (var i = 0, obj; i < things.length; i++)
if ((undefined == key ? things[i] : thing[i][key]) == value)
return things[i];
};
Copy link to clipboard
Copied
I very much appreciate the feedback, @m1b @rob day
I wasn't able to get any of these working well. Please keep in mind, I'm not a programmer. I was able to create a script off of the following code that works, but I'd like to limit it the swatches to a color group of 5 colors, that it randomly, and even (ideally) applies.
@m1b ā I couldn't get any of your scripts to run for me. Not sure what I'm doing wrong, but it also didn't return any errors, just didn't affect the colors or provide any feedback.
@rob day ā I was able to get yours to run, but the results were
I think I'm getting some of the brackets wrong when I input your codes. Would it be possible to receive the full code to copy/paste, so that I'm not missing the correct beginining/end brackets?
This is the original code that @brian_p_dts helped with that's working, but I need to limit or exclude certain swatches.
Also, being able to undo the entire script sequence could be helpful?
(function(){
var tf=app.activeDocument.selection[0];
var chars=tf.characters.everyItem().getElements();
var col=app.activeDocument.swatches.length-1;
for (var i=0; i<chars.length; i++){
var rand=Math.floor((Math.random() * col) + 1);
chars[i].fillColor=app.activeDocument.swatches[rand];
}
}());
Copy link to clipboard
Copied
@rob day - to complete my incomplete thought above. I was able to get your scripts to run, but they didn't work as expected. The first script still included Paper, None, Black, and Registration colors.
The second script did limit to a color group of 5 colors, however it only applied 4 of those 5 colors and not very randomly.
Thanks for the all the ideas, but so far no bullseyes.
Copy link to clipboard
Copied
The first script still included Paper, None, Black, and Registration colors.
Sorry, I should have checkedāthe || need to be &&:
Before:
After:
This lets you undo:
function randomFill(){
var tf = app.activeDocument.selection[0];
var chars = tf.characters.everyItem().getElements();
var col = app.activeDocument.swatches.length-1;
var c
for (var i=0; i<chars.length; i++){
c = app.activeDocument.swatches[Math.floor((Math.random() * col) + 1)]
if (c.name!="Black" && c.name!="Paper" && c.name!="Registration" && c.name!="None") {
chars[i].fillColor=c;
}
}
}
app.doScript(randomFill, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Random Character Colors');
Copy link to clipboard
Copied
I appreciate this @rob day ā the undo works great!
Still getting Black in the results, though. I'm not getting Paper, Registration, or None, but still getting black ...
Copy link to clipboard
Copied
Right, sorry again, I got caught up in editing the code from the post you linked to.
If the random color is one of the colors you are trying to skip it just leaves the character with its starting colorāBlack in your case. Youāll need to create a custom array of colors:
function randomFill(){
var tf = app.activeDocument.selection[0];
var chars = tf.characters.everyItem().getElements();
var c = app.activeDocument.swatches.everyItem().getElements();
var ca = [];
for (var i = 0; i < c.length; i++){
if (c[i].name!="Black" && c[i].name!="Paper" && c[i].name!="Registration" && c[i].name!="None") {
ca.push(c[i])
}
};
for (var i=0; i<chars.length; i++){
chars[i].fillColor=ca[Math.floor((Math.random() * ca.length))];
}
}
app.doScript(randomFill, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Random Character Colors');
Copy link to clipboard
Copied
Hi @Paperback Pat, did you configure my script to suit your requirements? If you are having trouble, let me know what colors or color group you want to use and I'll tell you how to configure for that. Including a demo .indd would be good, too.
- Mark
Copy link to clipboard
Copied
@m1b I wasn't able to get your script working for me, but it's probably my lack of knowledge with the code. Either nothing would happen (your original code), or I'd get code errors when running the script (because I was trying to insert your code into the original code or something).
Is the code you supplied me complete, or truncated? Do I just copy paste the exact thing into my .jsx file? That's what I tried, and nothing happened, so then I tried just sections of it, or merging it with other code, and then I started running into error messages.
I'll provide a demo file when I can!
Copy link to clipboard
Copied
Hi @Paperback Pat my code is complete, but not configured. You need to do that part. I can do it for you, if you post a demo.indd and tell me which color group to use or which colors to pick.
Copy link to clipboard
Copied
thanks so much! I only know enough about this to get into a little trouble, so thank you for your patience!āØāØI sent you a link to a demo file.
āØOne more note, and something to add to the wishlist would be a way to control the color order so it's pink, orange, green, navy, turquoise.
Copy link to clipboard
Copied
Hi @Paperback Pat, okay so on private message you've added a couple of new parts: (1) target a paragraph style, and (2) specify and a color order with no repetitions. By supplying a demo document (I've attached it here for anyone to follow along) you've also confirmed that you only want colors from a specific color group. That's enough information for me to configure the script. I'll list it again, rather than update the original one, because this also has the extra function `getTexts` which is the function that collects the headings to color.
Also, to make it easier for you to see what you can adjust, I've moved the configuration details into a `settings` object at the start of the script. You will see what I mean when you read the script below.
Oh, I've also added a `shuffler` function for randomizing the colors such that they are shuffled around in batchesāso each batch of colours is shuffled each time they cycle, with a quick check to make sure that the first and last colors in the batches don't match. The result should be that no two colors are adjacent. You can change "colorPicker: shuffler" to "colorPicker: cycler" if you want to see what happens if each color is applied in turn.
There's bound to be bugs, because I only tested on your simple document, but it's a starting point. Let me know how it goes.
- Mark
/**
* @file Color Characters.js
*
* There are three working parts to this script, each of which
* can be configured:
*
* 1. `getTexts` function which collects texts from the document.
*
* 2. `getColors` function, which will collect colors
* from the document based on the parameters specified; and
*
* 3. `colorCharacters` function, which assigns colors to characters.
*
* See each function's documentation below.
*
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/indesign-script-to-randomly-apply-color-swatches-to-text-characters/m-p/14567096
*/
function main() {
var settings = {
paragraphStyleName: 'Chap_Title',
colorGroupName: 'Chap_Title_Colors',
colorPicker: shuffler,
};
if (0 === app.documents.length)
return alert('Please select some text and try again.');
var doc = app.activeDocument;
var texts = getTexts({
items: doc,
findGrepPreferences: {
appliedParagraphStyle: settings.paragraphStyleName,
},
});
if (0 === texts.length)
return alert('No texts were found with the current options.');
var colors = getColors({
doc: doc,
includeColorGroups: [settings.colorGroupName],
});
if (0 === colors.length)
return alert('No colors were found with the current options.');
// do the coloring
colorCharacters(texts, colors, settings.colorPicker);
// finished!
/**
* Returns a random number between 0 and `max`,
* cycling when `index` exceeds `max`.
* @author m1b
* @version 2024-04-24
* @param {Character} [ch] - a character (not used)
* @param {Number} index - the character index.
* @param {Number} max - the maximum index.
* @returns {Number} - a random index
*/
function shuffler(ch, index, max) {
index = index % max;
var self = callee;
if (0 === index) {
// randomize once every cycle
self.arr = [];
for (var i = 0; i < max; i++)
self.arr[i] = i;
var i = max,
r,
temp;
while (i--) {
r = Math.floor(Math.random() * (i + 1));
// swap randomly chosen element with current element
temp = self.arr[i];
self.arr[i] = self.arr[r];
self.arr[r] = temp;
if (0 === i && self.previousIndex === self.arr[0]) {
// swap first two to avoid adjacent values
temp = self.arr[0];
self.arr[0] = self.arr[1];
self.arr[1] = temp;
}
}
self.previousIndex = self.arr[max - 1];
}
return self.arr[index];
};
};
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Color Characters');
/**
* Returns an array of colors from a document.
* ----------------------------------------------
* General usage notes:
* - leave `includeSwatches` undefined to use every swatch
* - using `filter` will override all other parameters.
* ----------------------------------------------
* Example 1 - Get all swatches, except some:
*
* var colors = getColors({
* doc: doc,
* excludeSwatches: ['None', 'Registration', 'Paper'],
* });
* ----------------------------------------------
* Example 3 - Get all swatches in a color group:
*
* var colors = getColors({
* doc: doc,
* includeColorGroup: 'My Favourite Colours',
* });
* ----------------------------------------------
* Example 2 - Get all swatches with name starting "PANTONE":
*
* var colors = getColors({
* doc: doc,
* filter: function (swatchName) {
* return /^PANTONE/.test(swatchName);
* },
* });
* ----------------------------------------------
* @author m1b
* @version 2024-04-20
* @param {Object} options
* @param {Array<String>} [options.includeSwatches] - swatch names to specifically include (default: include all swatches).
* @param {Array<String>} [options.excludeSwatches] - swatch names to specifically exclude (default: no exclusions).
* @param {Array<String>} [options.includeColorGroups] - color group names to specifically include (default: include all color groups).
* @param {Array<String>} [options.excludeColorGroups] - color group names to specifically exclude (default: no exclusions).
* @param {Function} [options.filter] - function that returns true to include swatch (default: no filter).
* @returns {Array<Color>}
*/
function getColors(options) {
options = options || {};
var doc = options.doc || app.activeDocument,
includeSwatches = options.includeSwatches || [],
excludeSwatches = options.excludeSwatches || [],
includeColorGroups = options.includeColorGroups || [],
excludeColorGroups = options.excludeColorGroups || [],
filter = options.filter;
const INCLUDE_ALL_SWATCHES = (
0 === includeColorGroups.length
&& 0 === includeSwatches.length
&& undefined == filter
);
var allSwatches = doc.colorGroups.everyItem().colorGroupSwatches.everyItem().swatchItemRef,
swatches = [];
for (var i = 0, sw; i < allSwatches.length; i++) {
sw = allSwatches[i];
if (undefined != filter) {
// include according to filter function
if (filter(sw.name))
swatches.push(sw);
else
continue;
}
if (
getThing(excludeSwatches, undefined, sw.name)
|| getThing(excludeColorGroups, undefined, sw.parentColorGroup.name)
)
// ignore specifically-excluded swatch
continue;
if (
// include all swatches by default
INCLUDE_ALL_SWATCHES
// include swatch in specific color group
|| getThing(includeColorGroups, undefined, sw.parentColorGroup.name)
// include specific swatch
|| getThing(includeSwatches, undefined, sw.name)
)
swatches.push(sw);
}
return swatches;
};
/**
* Assigns fill color to selected text randomly.
* ----------------------------------------------
* Example `colorIndexPicker` functions:
*
* function cycler(ch, index, max) {
* // return the next value, in order, cycling
* return index % max;
* };
*
* ----------------------------------------------
* Example `characterPicker` functions:
*
* function pickHalfCharacters() {
* return Math.random() < 0.5;
* }
*
* function pickEveryNthCharacter(ch, index) {
* var n = 2;
* return (index + 1) % n === 0;
* }
* ----------------------------------------------
* @author m1b
* @version 2024-04-20
* @param {Array|Collection<TextFrame|Text>} things - array or collection of DOM object's with characters.
* @param {Array<Color|null>} colors - array of colors to use.
* @param {Function} [colorIndexPicker] - function that determines which color to use (default: pick random color).
* @param {Function} [characterPicker] - function that determines if a character is colored (default: pick every character).
*/
function colorCharacters(things, colors, colorIndexPicker, characterPicker) {
colorIndexPicker = colorIndexPicker || defaultColorPicker;
characterPicker = characterPicker || defaultCharacterpPicker;
if (undefined == things[0])
things = [things];
for (var i = 0, characters; i < things.length; i++) {
if (!things[i].hasOwnProperty('characters'))
continue;
if (things[i].hasOwnProperty('pageItems'))
colorCharacters(things[i].pageItems, colors, colorIndexPicker, characterPicker);
if (things[i].hasOwnProperty('tables'))
colorCharacters(things[i].tables.everyItem().cells, colors, colorIndexPicker, characterPicker);
if (things[i].hasOwnProperty('footnotes'))
colorCharacters(things[i].footnotes, colors, colorIndexPicker, characterPicker);
characters = things[i].characters;
characterLoop:
for (var j = 0, r, len = characters.length; j < len; j++) {
if (!characterPicker(characters[j], j, len - 1))
continue characterLoop;
// get the next color index
r = colorIndexPicker(characters[j], j, colors.length);
// color the character
characters[j].fillColor = colors[r];
}
}
function defaultColorPicker(ch, index, max) {
return Math.floor(Math.random() * max);
};
function defaultCharacterpPicker() {
return true;
};
};
/**
* Returns a thing with a `key` matching `value`.
* If `key` is undefined, evaluate the object itself.
* @author m1b
* @version 2024-04-20
* @param {Array|Collection} things - the things to look through.
* @param {String} [key] - the property name (default: undefined).
* @param {*} value - the value to match.
* @returns {*}
*/
function getThing(things, key, value) {
for (var i = 0, obj; i < things.length; i++)
if ((undefined == key ? things[i] : thing[i][key]) == value)
return things[i];
};
/**
* Returns texts found in `items`.
*
* Note: this is intended to use with
* findGrep - it won't work without
* a `findGrepPreferences` object.
* ----------------------------------------------
* Example - styled paragraphs of document:
*
* var texts = getTexts({
* items: doc,
* findGrepPreferences: {
* appliedParagraphStyle: 'My Style',
* },
* });
* ----------------------------------------------
* @author m1b
* @version 2024-04-24
* @param {Object} options
* @param {*} options.items - the DOM objects to extract text from, eg. Document or Document.selection.
* @param {Object} options.findGrepPreferences - properties to configure findGrep.
* @param {Function} [options.filter] - function that, given the object, returns true to collect its text (default: no filter).
* @returns {Array<Text>}
*/
function getTexts(options) {
options = options || {};
if (undefined == options.items)
throw Error('getTexts: expected `options.items` parameter.');
if (undefined == options.findGrepPreferences)
throw Error('getTexts: expected `options.findGrepPreferences` parameter.');
var items = options.items,
texts = [];
if ('Document' === items.constructor.name)
items = items.stories;
if (!items.hasOwnProperty(0))
items = [items];
// set up find grep
app.findGrepPreferences = NothingEnum.NOTHING;
app.changeGrepPreferences = NothingEnum.NOTHING;
for (var key in options.findGrepPreferences)
if (options.findGrepPreferences.hasOwnProperty(key))
app.findGrepPreferences[key] = options.findGrepPreferences[key];
for (var i = 0; i < items.length; i++) {
if (
undefined != options.filter
&& true !== options.filter(items[i])
)
continue;
if (
undefined != items[i].contents
&& 0 === items[i].contents.length
)
continue;
if ('function' !== typeof items[i].findGrep)
continue;
texts = texts.concat(items[i].findGrep());
}
return texts;
};
Copy link to clipboard
Copied
Thank you so much, to everyone here who helped. This script is great!
Find more inspiration, events, and resources on the new Adobe Community
Explore Now