Copy link to clipboard
Copied
Hi ! I am making a small zine, where I have a long text without paragraphs and I am looking for a script transitions the text fill colour of a section from one colour to the other with each line. I am not good with code and still learning, so its a bit too complicated for me. Does someone have a solution?
Copy link to clipboard
Copied
> a long text without paragraphs
Do you perhaps mean 'a long text in a single paragraph'?
Copy link to clipboard
Copied
yes
Copy link to clipboard
Copied
You don't need any script. Use nested lines styles.
Copy link to clipboard
Copied
Hi @default1zn7l8u4ete5, I love playing with these things, and I had some functions already written for other projects, so I've put together a script that (hopefully) will do what you want. To use it, just color the first line and last line of text in the colors/tints that you want to blend between, then select that text (or text frame) and run script. Paragraphs don't count, just Lines, so it doesn't matter whether there are multiple paragraphs or just one paragraph. I haven't tested much at all, so please give it a try, then post back here if you find any bugs and I'll update with a fix.
- Mark
P.S. There is no reason you can't run the script more than once and blend between several colors.
/**
* Applies a color blend to lines of text,
* by interpolating colors of first character
* of first line of selected text with first
* character of last line of selected text.
* @file Blend Text Line Colors.js
* @author m1b
* @discussion https://community.adobe.com/t5/indesign-discussions/a-script-that-transitions-the-text-fill-colour-of-a-section-from-one-colour-to-other-with-each-line/m-p/13890952
*/
function main() {
var doc = app.activeDocument;
blendTextLineColors(doc, doc.selection[0]);
}
app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Do Script');
/**
* Apply a fill color to each line of `text`
* such that each line has a color derived
* by interpolation between the colors of
* the first character of the first and
* last lines of text.
* @author m1b
* @version 2023-06-26
* @param {Document} doc - an Indesign Document.
* @param {Text|Textframe} text - the text to color.
*/
function blendTextLineColors(doc, text) {
if (text == undefined)
return;
if (text.constructor.name == 'TextFrame')
text = text.texts[0];
if (!text.hasOwnProperty('lines'))
return;
var lineCount = text.lines.length,
startColor = text.lines[0].characters[0].fillColor,
startTint = text.lines[0].characters[0].fillTint,
endColor = text.lines[-1].characters[0].fillColor,
endTint = text.lines[-1].characters[0].fillTint,
hasTints = startTint !== endTint,
breakdowns = interpolateArrays(startColor.colorValue, endColor.colorValue, lineCount),
tints;
if (hasTints) {
if (startTint == -1)
startTint = 100;
if (endTint == -1)
endTint = 100;
tints = interpolateArrays([startTint], [endTint], lineCount);
}
for (var i = 0; i < lineCount; i++) {
var c = makeColor(doc, breakdowns[i]);
if (!c || !c.isValid)
continue;
text.lines[i].fillColor = makeColor(doc, breakdowns[i]);
if (hasTints)
text.lines[i].fillTint = tints[i][0];
}
};
/**
* Returns `n` interpolations of the
* two supplied Arrays, inclusive.
* @author m1b
* @version 2022-10-03
* @param {Array<Number>} arr1
* @param {Array<Number>} arr2
* @param {Number} n - the number of interpolated arrays returns, inclusive of arr1 and arr2.
* @returns {Array<Array>} - an array of arrays [arr1, inter1, inter2, ..., arr2] where length == n.
*/
function interpolateArrays(arr1, arr2, n) {
if (
arr1 == undefined
|| arr2 == undefined
)
throw Error('interpolateArrays: missing argument(s).');
if (arr1.length !== arr2.length)
throw Error('interpolateArrays: array lengths don\'t match.');
if (
n.constructor.name != 'Number'
|| n < 0
|| n !== n
)
throw Error('interpolateArrays: bad argument for "n".');
// calculate the interpolations
var results = [];
for (var j = 0; j < n; j++)
results[j] = [];
for (var i = 0; i < arr1.length; i++) {
var s = arr1[i],
e = arr2[i];
for (var j = 0; j < n; j++)
results[j].push(s + ((s - e) / (n - 1)) * -j);
}
return results;
};
/**
* Returns a new, unnamed color.
* @param {Document} doc - an Indesign Document.
* @param {Array<Number>} breakdown - the color values.
* @returns {Color}
*/
function makeColor(doc, breakdown) {
var color,
colorSpace;
if (breakdown.length === 4)
colorSpace = ColorSpace.CMYK;
else if (breakdown.length == 3)
colorSpace = ColorSpace.RGB;
else
return;
if (!doc.colors[-1].isValid)
return;
// this will make an unnamed color, ie. it won't show up in document
// thanks to https://community.adobe.com/t5/indesign-discussions/coloring-a-font-with-a-rgb-etc-without-adding-the-color-to-the-document-swatches/m-p/3655060
color = doc.colors[-1].duplicate();
color.properties = {
space: colorSpace,
colorValue: breakdown,
};
return color;
};
Copy link to clipboard
Copied
Hi Mark, Thanks for posting your script. I have an old AppleScript that makes a group of blend swatches from two selected swatches, which I translated for the example below. In this case I’m converting the start and end colors to Lab, so it doesn’t matter what the mode of the two colors are—you could blend a CMYK and RGB color and maintain a color managed appearance:
if (app.activeDocument.selection > 0) {
app.doScript(makeDialog, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Blend Text Color');
}
var sc, ec;
function makeDialog(){
var s = app.activeDocument.selection[0];
if (s.constructor.name == "TextColumn" || s.constructor.name == "Text" || s.constructor.name == "Paragraph" ) {
var d = app.dialogs.add({name:"Blend Colors", canCancel:true});
with(d.dialogColumns.add()){
staticTexts.add({staticLabel:"Start Color:"});
staticTexts.add({staticLabel:"End Color:"});
}
with(d.dialogColumns.add()){
sc = dropdowns.add({stringList:getSwatchNames(), selectedIndex:0, minWidth:80});
ec = dropdowns.add({stringList:getSwatchNames(), selectedIndex:0, minWidth:80})
}
if(d.show() == true){
sc = getSwatchNames()[sc.selectedIndex];
ec = getSwatchNames()[ec.selectedIndex];
blendTextColor(s)
d.destroy();
}
}else{
alert("Please Select More Than One Line of Text")
return
}
}
function blendTextColor(sel){
var doc = app.activeDocument;
var ln = sel.lines;
//duplicate the selected swatches and set them to process Lab
//skips Gradients, Mixed Ink, Tint Colors
try {
var sColor = doc.colors.itemByName(sc).duplicate();
var eColor = doc.colors.itemByName(ec).duplicate();
}catch(e) {
alert("Illegal Swatch Choice");
return
}
sColor.space = ColorSpace.LAB;
sColor.model = ColorModel.PROCESS;
eColor.space = ColorSpace.LAB;
eColor.model = ColorModel.PROCESS;
var inc = getIncrement(sColor.colorValue, eColor.colorValue, ln.length);
ln[0].fillColor = makeColor(doc, ColorSpace.LAB, sColor.colorValue)
var ncv;
for (var i = 1; i < ln.length; i++){
ncv = getBlendColor(sColor.colorValue, inc, i)
ln[i].fillColor = makeColor(doc, ColorSpace.LAB, ncv)
};
sColor.remove();
eColor.remove();
}
/**
* The amount to increment each color channel
* @ param cv1 the starting Lab color
* @ param cv2 the end Lab color
* @ param n the number of increments
* @ return an array of channel increments
*
*/
function getIncrement(cv1, cv2, n){
var a = [0,0,0]
for (var i = 0; i < a.length; i++){
a[i] = (cv2[i] - cv1[i])/n
};
return a
}
/**
* Get the incremented blend value
* @ param c the starting value array
* @ param n the array of increment amounts
* @ param m the increment multiplier
* @ return new color value array
*
*/
function getBlendColor(c,n,m){
var a = []
for (var i = 0; i < c.length; i++){
a.push( parseFloat((c[i] + (n[i] * m))));
}
return a
}
/**
* Makes an unnamed color
* @ param the document to add the color to
* @ param color space
* @ param color value
* @ return the color
*/
function makeColor(d, s, cv){
var c;
if (d.colors[-1].isValid) {
c = d.colors[-1].duplicate();
c.space = s;
c.colorValue = cv
}
return c;
}
function getSwatchNames(){
var arr = app.activeDocument.swatches.everyItem().getElements();
var da = ["None", "Registration", "Paper", "Black"];
var a = new Array;
for(var i = 0; i < arr.length; i++){
if (!checkItem(da, arr[i].name)) {
a.push(arr[i].name);
}
}
return a
}
function checkItem(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}