Scripting Illustrator Part 2 - How to Meld a Gradient into a Flat Process Color
In Part 1 of this two part tutorial series, we learned how to code a script which converts a flat process color into its matching gradient. In this tutorial, we will learn to code a script that converts a gradient fill into a flat process color. We will melt the available gradient color into a flat process color, which will be a mixture of all the colors available in that gradient.
This entire task will be performed via JavaScript script for Illustrator. The tutorial assumes that you're familiar with the basics of scripting. For those who've directly landed on this tutorial, we have a little know-how covered about Illustrator's Javascripts in Part 1 of this series. So without further delays, let's get started!
Vector Plus
Want access to the full Vector Source files and downloadable copies of every tutorial, including this one? Join Vector Plus for just 9$ a month.
Tutorial Details
- Program: Adobe Illustrator and ExtendedScript Toolkit
- Version: CS3
- Difficulty: Intermediate
- Estimated Completion Time: 3 to 4 Hrs
Purpose of the Script
We want this script to perform a very simple task. In Adobe Illustrator, when a user selects some objects filled with a CMYK Gradient Color, and executes this Script; the objects shall get converted into a Flat CMYK fill. This flat fill will be the mixture of all the colors available in the former gradient. See the image below for clarification.



Hence, the aim of our script is to convert a Gradient CMYK Fill into a Flat CMYK Fill.
Logic and Algorithm
The logic for melting the colors of a gradient into a single color is simple and straightforward. We’ll pick all the colors from the gradient, find their average and assign it to the object as a new color. We can understand this in five steps, as shown below:
- Step 1: Pick the color of the current object. i.e. currentColor = color of currently selected object.
- Step 2: Count the number of gradient stops in the current color.
- Step 3: At each gradient stop, pick the associated color and its CMYK values.
- Step 4: Calculate the average CMYK value for all colors available at different stops.
-
Step 5:
Assign this average CMYK value as a new color to the object.
The above algorithm can be easily understood from the pictorial representation below.



We have seen a brief overview of the logic. Let's get started with the coding.
Step 1 - Starting with the Code Structure
Open ExtendedScript Toolkit and create a new Javascript file (Command + N). Next, select Adobe Illustrator for the target application.



In the code editing area, add the following code structure for certain validations and pre-requisite checks.
1 |
|
2 |
if ( app.documents.length > 0 && app.activeDocument.pathItems.length > 0) |
3 |
{
|
4 |
if(app.activeDocument.documentColorSpace == DocumentColorSpace.CMYK) |
5 |
{
|
6 |
convertToFlat(); |
7 |
}
|
8 |
else
|
9 |
{
|
10 |
alert("Convert the Objects into CMYK First", "CMYK Conversion required"); |
11 |
}
|
12 |
}//end main if |
13 |
|
14 |
else
|
15 |
{
|
16 |
alert("Either no document is available or the document is empty"); |
17 |
}
|
We are checking if at least one document with at least one object exists, so that we can work upon it. Next, we are checking whether the document color mode is CMYK or not. This is an essential step because all the logic for color conversion in this script is based upon CMYK colors. convertToFlat()
is the main function which will contain all the logic. Next, save this file as test.jsx.
Step 2
Let's now start with the main function – convertToFlat()
. We'll check if any item is selected or not, because this script will work on the selected objects only. Hence, add the following lines of code to "test.jsx."
1 |
|
2 |
function convertToFlat() |
3 |
{
|
4 |
|
5 |
var items = selection; |
6 |
var totalSelected = items.length; |
7 |
|
8 |
if(totalSelected > 0) |
9 |
{
|
10 |
// proceed with the main logic
|
11 |
}
|
12 |
|
13 |
else
|
14 |
{
|
15 |
alert("Please select atleast one object"); |
16 |
}
|
17 |
|
18 |
}//end convertToGrad |
Step 3
Next, we will start a loop inside the "if(totalSelected > 0)
" block. This loop will contain the color conversion logic, which is repeated for each selected item. But just before the color conversion logic, let's add some more validations and checks inside that loop.
1 |
|
2 |
if(totalSelected > 0) |
3 |
{
|
4 |
|
5 |
for(var j=0; j < totalSelected; j++) |
6 |
{
|
7 |
var currentObject = app.activeDocument.selection[j]; |
8 |
|
9 |
if(currentObject.typename != "CompoundPathItem" && |
10 |
currentObject.typename != "GroupItem") |
11 |
{
|
12 |
if(currentObject.filled==true && |
13 |
currentObject.fillColor.typename != "CMYKColor" && |
14 |
currentObject.fillColor.typename != "PatternColor" && |
15 |
currentObject.fillColor.typename != "SpotColor") |
16 |
{
|
17 |
|
18 |
// Color conversion Block
|
19 |
|
20 |
} //endif |
21 |
|
22 |
else
|
23 |
{
|
24 |
alert("Fill an object with CMYK or Grayscale Gradient. Flat Colors, Patterns, Spot Colors and Empty Fills are not allowed."," Only Gradients Allowed"); |
25 |
}
|
26 |
} //endif |
27 |
|
28 |
else
|
29 |
{
|
30 |
alert("This script only works with Non-Compound Objects or Isolated Group items.\nAny items with Groups or Compound Objects will be omitted.", "Ungroup or Isolate the Group Items"); |
31 |
}
|
32 |
|
33 |
}//endfor |
34 |
|
35 |
}// endif |
For each selected item, we are checking whether it is a Compound Path or a Group Item. If so, the script shall not execute anymore and return an alert message. Further, we are checking if the fill-type of the selected item is something other than Gradient Fill. i.e. If it is a spot color, flat CMYK color or a pattern; the script shall return an alert. These checks are performed because our script will only work for gradient filled non-compound path items.
Next, we will modify the Color conversion Block.
Step 4
At this stage, we need to extract individual C, M, Y and K values for the colors residing at different gradient stops. For that, we will create some variables which will hold individual CMYK values. So, add the following variable declarations just inside the Color conversion block:
1 |
|
2 |
var currentColor = currentObject.fillColor; |
3 |
var numOfStops = currentColor.gradient.gradientStops.length; |
4 |
var colorBox=[]; |
5 |
var cyanBox=[]; |
6 |
var magentaBox=[]; |
7 |
var yellowBox=[]; |
8 |
var blackBox=[]; |
9 |
var grayBox =[]; |
10 |
var cyanTotal = 0; |
11 |
var magentaTotal = 0; |
12 |
var yellowTotal = 0; |
13 |
var blackTotal = 0; |
14 |
var grayTotal = 0; |
We will see the role of each variable one-by-one:
-
currentColor
will store the fill color (gradient) of the currently selected object. -
numOfStops
contains the total number of gradient stops available in currently selected object. This will be used to find the average of color values in later stages. -
colorBox
is an array which will hold the fill-color value for all the gradient stops. i.e. ifnumOfStops
is four. -
colorBox
array will contain four colors. -
cyanBox
is an array which holds cyan values for each color on different gradient stops. - Similarly,
magentaBox
,yellowBox
and
blackBox
hold their respective color values for each color on different gradient stops. -
grayBox
is an array which holds gray values for the color on any gradient stop. This is essential because the gray color is a different specification than CMYK color specification. In case, an object contains a gray gradient, we will handle the situation separately by making use of this variable. - Finally, we have
cyanTotal
,magentaTotal
,yellowTotal
,blackTotal
andgrayTotal
. These variables contain the summation of all the cyan, magenta, yellow, black or gray values respectively.
Step 5
We have performed the validations and checks. We have also created necessary containers to hold color values. Next, we will run a loop which reads each gradient stop one-by-one. For that, create a loop, as shown below:
1 |
|
2 |
for(var k=0; k < numOfStops; k++) |
3 |
{
|
4 |
colorBox[k] = currentColor.gradient.gradientStops[k].color; |
5 |
if(colorBox[k].typename == "GrayColor") |
6 |
{
|
7 |
|
8 |
// Extract Gray Color values
|
9 |
|
10 |
}
|
11 |
|
12 |
else
|
13 |
{
|
14 |
|
15 |
// Extract CMYK Color values
|
16 |
|
17 |
}
|
18 |
|
19 |
}//end for k |
currentColor.gradient.gradientStops[k].color
returns the color of a particular gradient stop for each iteration of k.
For each gradient stop, we are checking if the available color is a GrayColor
specification or a CMYKColor
specification. Depending upon that, we will implement our logic.
Step 6 - Summation of GrayColor Values
Inside the "if block
" for GrayColor
specification, add the following lines of code:
1 |
|
2 |
grayBox[k] = Math.round(colorBox[k].gray); |
3 |
grayTotal = grayTotal + grayBox[k]; |
Hence, grayBox[k]
will hold all the gray values for each color at their respective gradient stops.
Next, grayTotal
will be the summation of these gray color values, which will be used later.
Step 7 - Summation of CMYK Color values
Inside the "else block
" for CMYKColor
specification, add the following lines of code:
1 |
|
2 |
cyanBox[k] = Math.round(colorBox[k].cyan); |
3 |
magentaBox[k] = Math.round(colorBox[k].magenta); |
4 |
yellowBox[k] = Math.round(colorBox[k].yellow); |
5 |
blackBox[k] = Math.round(colorBox[k].black); |
6 |
|
7 |
cyanTotal = cyanTotal + cyanBox[k]; |
8 |
magentaTotal = magentaTotal + magentaBox[k]; |
9 |
yellowTotal = yellowTotal + yellowBox[k]; |
10 |
blackTotal = blackTotal + blackBox[k]; |
To understand what is being performed here, we will take an example of Cyan color. cyanBox[k]
is storing the cyan values for all the colors residing at different gradient stops. Next, cyanTotal
is the summation of all these cyan values that are stored in cyanBox[k]
.
A similar operation is performed for magenta, yellow and black too. Once the summation is done, we can come out of the loop and proceed ahead for average.
Step 8 - Averaging the Color Values
We have gathered the individual summation of grayColor
and CMYKColor
for all the gradient stops. Now we need to average them. For that, close the "for k loop
" and enter the following lines of code just after the closing bracelet of "for k loop
", as shown below:
1 |
|
2 |
} // end for k loop |
3 |
|
4 |
var finalBlack = blackTotal + grayTotal; |
5 |
|
6 |
var newCyan = Math.round(cyanTotal / numOfStops); |
7 |
var newMagenta = Math.round(magentaTotal / numOfStops); |
8 |
var newYellow = Math.round(yellowTotal / numOfStops); |
9 |
var newBlack = Math.round(finalBlack / numOfStops); |
In the above lines of code, we are dividing individual summations of C, M, Y and K by numOfStops
. i.e. If there were five gradient stops, we will divide the summation of individual C, M, Y and K values with five. Thereby, returning an average of five values. These averaged values are stored as newCyan
, newMagenta
, newYellow
and newBlack
respectively.
Note the tricky case of gray and black here. The summation of K is not just blackTotal
. Rather, it's a sum of grayTotal
and blackTotal
.
Why we have done that? There are cases when a gradient fill may contain both GrayColor stops and CMYK stops. In that case, the gray Color values are added to the K value of the CMYK color. Gray can not be added to cyan, magenta or yellow. It will fall in the category of K only.
Now we have all the new averaged values for C, M, Y and K in hand. In the next step, we will implement these values as a new CMYK color on the current object.
Step 9 - Implementing the New Color
To implement the new color, we will create a new CMYKColor
object and modify its C, M, Y and K values to the ones that we just calculated in Step 7. To do that, add the following lines of code:
1 |
|
2 |
var newColor = new CMYKColor(); |
3 |
|
4 |
newColor.cyan = newCyan; |
5 |
newColor.magenta = newMagenta; |
6 |
newColor.yellow = newYellow; |
7 |
newColor.black = newBlack; |
8 |
|
9 |
currentObject.fillColor = newColor; |
We have created a newColor
object and assigned the values of newCyan
, newMagenta
, newYellow
and newBlack
as its C, M, Y and K values respectively.
Next, we have assigned the newColor
as a fillColor
to the current object. Hence, our final code will now look like the one below:
1 |
|
2 |
if ( app.documents.length > 0 && app.activeDocument.pathItems.length > 0) |
3 |
{
|
4 |
if(app.activeDocument.documentColorSpace == DocumentColorSpace.CMYK) |
5 |
{
|
6 |
convertToFlat(); |
7 |
}
|
8 |
else
|
9 |
{
|
10 |
alert("Convert the Objects into CMYK First", "CMYK Conversion required"); |
11 |
}
|
12 |
}//end main if |
13 |
|
14 |
else
|
15 |
{
|
16 |
alert("Either no document is available or the document is empty"); |
17 |
}
|
18 |
|
19 |
function convertToFlat() { |
20 |
|
21 |
var items = selection; |
22 |
var totalSelected = items.length; |
23 |
if(totalSelected > 0) |
24 |
{
|
25 |
|
26 |
for(var j=0; j < totalSelected; j++) |
27 |
{
|
28 |
var currentObject = app.activeDocument.selection[j]; |
29 |
if(currentObject.typename != "CompoundPathItem" && |
30 |
currentObject.typename != "GroupItem") |
31 |
{
|
32 |
if(currentObject.filled == true && |
33 |
currentObject.fillColor.typename != "CMYKColor" && |
34 |
currentObject.fillColor.typename != "PatternColor" && |
35 |
currentObject.fillColor.typename != "SpotColor") |
36 |
{
|
37 |
|
38 |
var currentColor = currentObject.fillColor; |
39 |
var numOfStops = currentColor.gradient.gradientStops.length; |
40 |
var colorBox=[]; |
41 |
var cyanBox=[]; |
42 |
var magentaBox=[]; |
43 |
var yellowBox=[]; |
44 |
var blackBox=[]; |
45 |
var grayBox =[]; |
46 |
var cyanTotal = 0; |
47 |
var magentaTotal = 0; |
48 |
var yellowTotal = 0; |
49 |
var blackTotal = 0; |
50 |
var grayTotal = 0; |
51 |
|
52 |
|
53 |
for(var k=0; k < numOfStops; k++) |
54 |
{
|
55 |
colorBox[k] = currentColor.gradient.gradientStops[k].color; |
56 |
if(colorBox[k].typename == "GrayColor") |
57 |
{
|
58 |
grayBox[k] = Math.round(colorBox[k].gray); |
59 |
grayTotal = grayTotal + grayBox[k]; |
60 |
}
|
61 |
|
62 |
else
|
63 |
{
|
64 |
cyanBox[k] = Math.round(colorBox[k].cyan); |
65 |
magentaBox[k] = Math.round(colorBox[k].magenta); |
66 |
yellowBox[k] = Math.round(colorBox[k].yellow); |
67 |
blackBox[k] = Math.round(colorBox[k].black); |
68 |
|
69 |
cyanTotal = cyanTotal + cyanBox[k]; |
70 |
magentaTotal = magentaTotal + magentaBox[k]; |
71 |
yellowTotal = yellowTotal + yellowBox[k]; |
72 |
blackTotal = blackTotal + blackBox[k]; |
73 |
}
|
74 |
|
75 |
}//end for k |
76 |
|
77 |
var finalBlack = blackTotal + grayTotal; |
78 |
|
79 |
var newCyan = Math.round(cyanTotal / numOfStops); |
80 |
var newMagenta = Math.round(magentaTotal / numOfStops); |
81 |
var newYellow = Math.round(yellowTotal / numOfStops); |
82 |
var newBlack = Math.round(finalBlack / numOfStops); |
83 |
|
84 |
var newColor = new CMYKColor(); |
85 |
newColor.cyan = newCyan; |
86 |
newColor.magenta = newMagenta; |
87 |
newColor.yellow = newYellow; |
88 |
newColor.black = newBlack; |
89 |
|
90 |
currentObject.fillColor = newColor; |
91 |
|
92 |
|
93 |
} //endif |
94 |
|
95 |
else
|
96 |
{
|
97 |
alert("Fill an object with CMYK or Grayscale Gradient. Flat Colors, Patterns, Spot Colors and Empty Fills are not allowed."," Only Gradients Allowed"); |
98 |
}
|
99 |
|
100 |
}// endif |
101 |
|
102 |
else
|
103 |
{
|
104 |
alert("This script only works with Non-Compound Objects or Isolated Group items.\nAny items with Groups or Compound Objects will be omitted.", "Ungroup or Isolate the Group Items"); |
105 |
}
|
106 |
|
107 |
}//end for j |
108 |
|
109 |
}// endif |
110 |
|
111 |
else
|
112 |
{
|
113 |
alert("Please select atleast one object"); |
114 |
}
|
115 |
|
116 |
}//endFunction |
Step 10 - Executing the Script
Save this script as "test.jsx" and open Adobe Illustrator. Next, create some objects with colorful gradient fills. Now to test this script, select some objects and go to File > Scripts > Other Script (Command + F12) and locate this script.



After successful execution of the script, you should see a melt of the gradients; something similar to this:



Conclusion and Scope
In this tutorial, we have seen how to melt a gradient into a flat color fill with the help of scripts. This script may be found useful in cases when you need to find the average of two or more colors. But the fruit of this tutorial is to understand the basics of scripting and their implementation with Illustrator.
There is a wide scope of vector creativity and innovation through scripting. Hope this two-part tutorial series will inspire readers and authors in bringing forward the essence of scripting. Thanks for your valuable time in reading this tutorial.
Subscribe to the Vectortuts+ RSS Feed to stay up to date with the latest vector tutorials and articles.
