Skip to content

Instantly share code, notes, and snippets.

@jmsdnns
Last active May 1, 2024 21:14
Show Gist options
  • Save jmsdnns/f170599dc98f036ba72a2618797f525d to your computer and use it in GitHub Desktop.
Save jmsdnns/f170599dc98f036ba72a2618797f525d to your computer and use it in GitHub Desktop.
Illustrator script—Exports CMYK, RGB, and Hex values for all the color swatches in a document. CMYK to RGB conversion using Adobe’s default US Web Coated SWOP2 to sRGB.
/**
* jd-export.jsx
* -------------
* Illustrator script—Exports CMYK, RGB, and Hex values for all the color
* swatches in a document. CMYK to RGB conversion using Adobe’s default US
* Web Coated SWOP2 to sRGB.
*
*
* LICENSE
* -------
* Copyright 2017 Jms Dnns
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
try {
//
// Function Library
//
/**
* decimalToHex takes a number and returns it converted base 16, aka hex
* @param {number} decimal number to convert to hex
* @returns {String} a hex string
*/
function decimalToHex(decimal) {
var hex = decimal.toString(16);
return hex;
};
/**
* hexToDecimal takes a base 16 number and returns it converted to decimal
* @param {String} hex a hex string
* @returns {number} hex as decimal
*/
function hexToDecimal(hex) {
var decimal = parseInt(hex, 16);
return decimal;
};
/**
* padPrefix takes a string `s`, a desired width `width`, and a padding
* character `padChar`, and returns a string. If the length of `s` is
* less than `width`, `padChar` will be used to fill in the space.
* @param {String} s string to be padded
* @param {number} width desired width for the string
* @param {String} padChar character to pad string
* @returns {String} the padded string
*/
function padPrefix(s, width, padChar) {
var paddedS = s;
if(s.length < width) {
var padding = new Array(width - paddedS.length + 1).join(padChar);
var paddedS = padding + s;
}
return paddedS;
};
/**
* RGBToHex takes numeric values for red, green, and blue, and returns
* a hex string.
* @param {number} r red
* @param {number} g green
* @param {number} b blue
* @returns {String} a hex string
*/
function RGBToHex(r, g, b) {
if (r < 0 || r > 255) alert("Red value should be between 0-255: " + r);
if (g < 0 || g > 255) alert("Green value should be between 0-255: " + g);
if (b < 0 || b > 255) alert("Blue value should be between 0-255: " + b);
// Convert to hex
r_hex = decimalToHex(r);
g_hex = decimalToHex(g);
b_hex = decimalToHex(b);
// Pad any values that are a single character
r_hex = padPrefix(r_hex, 2, "0");
g_hex = padPrefix(g_hex, 2, "0");
b_hex = padPrefix(b_hex, 2, "0");
// Create hex string
hex = "#" + r_hex + g_hex + b_hex;
return hex;
};
/**
* hexToRGB takes a hex string and returns a structure with r, g, and b
* fields.
* @param {String} hex hex string representing an html color value
* @returns {Object} Object with values for r, g, and b
*/
function hexToRGB(hex) {
// Remove leading "#" from "#000" or "#000000"
if ((hex.length == 4 || hex.length == 7) && hex[0] == "#") {
hex = hex.slice(1);
}
// Convert "RGB" to "RRGGBB"
if (hex.length == 3) {
var new_hex = "";
for(var i=0; i < hex.length; i++) {
new_hex += hex [i] + hex[i];
}
hex = new_hex;
}
// Extract color values
var r = hex.slice(0,2);
var g = hex.slice(2,4);
var b = hex.slice(4,6);
// Convert to decimal
var r_decimal = hexToDecimal(r);
var g_decimal = hexToDecimal(g);
var b_decimal = hexToDecimal(b);
return { r: r_decimal, g: g_decimal, b: b_decimal };
};
/**
* convertColor Translates color values from one format, like CMYK, to
* another, like RGB. If the destFormat is RGB, the return value is an
* array with 3 items: R, G, and B.
* @param {String} srcFormat Color format of the source data
* @param {String} destFormat Color format we are converting to
* @param {Object} colorArray Source data
* @returns {Object} An array of colors
*/
function convertColor(srcFormat, destFormat, colorArray) {
var srcColorSpace = ImageColorSpace[srcFormat];
var destColorSpace = ImageColorSpace[destFormat];
return app.convertSampleColor(srcColorSpace, colorArray, destColorSpace, ColorConvertPurpose.defaultpurpose);
}
/**
* RGBtoCMYK converts r, g, and b values into CMYK and returns an
* object with c, m, y, and k fields.
* @param {number} r red
* @param {number} g green
* @param {number} b blue
* @returns {Object} Object with c, m, y, and k fields
*/
function RGBToCMYK(r, g, b) {
var colors = [Math.round(r), Math.round(g), Math.round(b)];
var cmyk = convertColor("RGB", "CMYK", colors);
return { c: cmyk[0], m: cmyk[1], y: cmyk[2], k: cmyk[3] }
}
/**
* CMYKtoRGB converts c, m, y, and k values into RGB and returns a
* structure with r, g, and b fields.
* @param {number} c cyan
* @param {number} m magenta
* @param {number} y yellow
* @param {number} k black
* @returns {Object} Object with r, g, and b fields
*/
function CMYKToRGB(c, m, y, k) {
var colors = [Math.round(c), Math.round(m), Math.round(y), Math.round(k)]
var rgb = convertColor("CMYK", "RGB", colors);
return { r: rgb[0], g: rgb[1], b: rgb[2] };
}
/**
* fromRGB is a conversion function to go from RGB to every format
* @param {number} r red
* @param {number} g green
* @param {number} b blue
* @returns {Object} Object with values for rgb, hex, and cmyk
*/
function fromRGB(r, g, b) {
var hex = RGBToHex(r, g, b);
var cmyk = RGBToCMYK(r, g, b);
var c = Math.round(cmyk.c);
var m = Math.round(cmyk.m);
var y = Math.round(cmyk.y);
var k = Math.round(cmyk.k);
return {
r: r, g: g, b: b,
hex: hex,
c: c, m: m, y: y, k: k,
};
};
/**
* fromHex is a conversion function to go from Hex to every format
* @param {String} hex hex string representing an html color value
* @returns {Object} Object with values for rgb, hex, and cmyk
*/
function fromHex(hex) {
var rgb = HexToRGB(hex);
var r = Math.round(rgb.r);
var g = Math.round(rgb.g);
var b = Math.round(rgb.b);
var cmyk = RGBToCMYK(r, g, b);
var c = Math.round(cmyk.c);
var m = Math.round(cmyk.m);
var y = Math.round(cmyk.y);
var k = Math.round(cmyk.k);
return {
r: r, g: g, b: b,
hex: hex,
c: c, m: m, y: y, k: k,
};
};
/**
* fromCMYK is a conversion function to go from CMYK to every format
* @param {number} c cyan
* @param {number} m magenta
* @param {number} y yellow
* @param {number} k black
* @returns {Object} Object with values for rgb, hex, and cmyk
*/
function fromCMYK(c, m, y, k) {
var rgb = CMYKToRGB(c, m, y, k);
var r = Math.round(rgb.r);
var g = Math.round(rgb.g);
var b = Math.round(rgb.b);
var hex = RGBToHex(r, g, b);
return {
r: r, g: g, b: b,
hex: hex,
c: c, m: m, y: y, k: k,
};
};
//
// App Logic
//
if (app.documents.length > 0) {
var destFolder = null;
destFolder = Folder.selectDialog('Select destination folder for CSV', '~');
if (destFolder != null) {
var doc = activeDocument;
var swatches = doc.swatches;
var colors = new Array();
// Document is CMYK
if (doc.documentColorSpace == DocumentColorSpace.CMYK) {
for (var i=0; i < swatches.length; i++) {
if (swatches[i].color == "[CMYKColor]") {
var c = Math.round(swatches[i].color.cyan);
var m = Math.round(swatches[i].color.magenta);
var y = Math.round(swatches[i].color.yellow);
var k = Math.round(swatches[i].color.black);
var color = fromCMYK(c, m, y, k);
var na = swatches[i].name;
color.name = na;
colors.push(color);
} else {
// Ignore anything that is not CMYK
}
}
}
// Document is RGB
else if (doc.documentColorSpace == DocumentColorSpace.RGB) {
for (var i = 0; i < col.length; i++) {
if (col[i].color == "[RGBColor]") {
var r = Math.round(col[i].color.red);
var g = Math.round(col[i].color.green);
var b = Math.round(col[i].color.blue);
var color = fromRGB(r, g, b);
var na = col[i].name;
color.name = na;
colors.push(color);
} else {
// Ignore anything that is not RGB
}
}
}
// Open output file
var listName = doc.name + ".csv";
var listFile = destFolder + "/" + listName;
var theFile = new File(listFile);
var isOpen = theFile.open("w");
if (isOpen) {
theFile.seek(0, 0);
// Write headers
theFile.writeln("I.D.,name,C,M,Y,K,hex,R,G,B");
// Print every item in colors.
for (var i = 0; i < colors.length; i++) {
var color = colors[i];
// color.name is handled specifically below to add quotation marks
var color_items = [
color.c,
color.m,
color.y,
color.k,
color.hex,
color.r,
color.g,
color.b
];
var line = i + "," + "\"" + color.name + "\"," + color_items.join(',');
theFile.writeln(line);
}
theFile.close();
}
alert('Export Complete');
}
else {
alert('Export Aborted');
}
}
else {
throw new Error('There are no documents open!');
}
}
catch (e) {
alert(e.message, "Script Alert", true);
}
@fuzenco
Copy link

fuzenco commented Jan 22, 2024

Thank you, @schroef , the update works great for me 💯 @fuzenco appreciate I wasn't alone in this, hope that the script works for you too

@jjaybyrd Welcome. We’re back to using this great script.

@schroef
Copy link

schroef commented Jan 22, 2024

@jjaybyrd @fuzenco
Perhaps i could add Gradients as well, but thats is more complicated and im not sure how we should tackle that. I mean a gradient can be very complicated and can have many colors. Not see i see value in adding it.

@fuzenco
Copy link

fuzenco commented Jan 22, 2024

I think my ideal feature for this script would be to export color groups (as in folders in the color palette) as separated groups in the CSV file. Now that would be awesome.

@jjaybyrd
Copy link

jjaybyrd commented Jan 22, 2024

@jjaybyrd @fuzenco Perhaps i could add Gradients as well, but thats is more complicated and im not sure how we should tackle that. I mean a gradient can be very complicated and can have many colors. Not see i see value in adding it.

@schroef It's an interesting idea, but I'm considering its usefulness for graphic design purposes 🤔 Illustrator's classification and handling of gradients is in my opinion pretty clunky. There isn't really a super short-handed way communicate gradient information (other than maybe sharing swatches). I typically have to give specs for combined colors and percentages for placement. If anybody else has a better way of doing that though, would be super interested to hear

@fuzenco
Copy link

fuzenco commented Jan 22, 2024

@schroef ill test it out

@schroef
Copy link

schroef commented Jan 22, 2024

@jjaybyrd @fuzenco

Okay, i got gradients also working now. So it shows the name and then below that all the gradient color stops
Not sure you guys are interested in that

GradientColor-stops-v002

@schroef
Copy link

schroef commented Jan 22, 2024

@jjaybyrd @fuzenco Perhaps i could add Gradients as well, but thats is more complicated and im not sure how we should tackle that. I mean a gradient can be very complicated and can have many colors. Not see i see value in adding it.

@schroef It's an interesting idea, but I'm considering its usefulness for graphic design purposes 🤔 Illustrator's classification and handling of gradients is in my opinion pretty clunky. There isn't really a super short-handed way communicate gradient information (other than maybe sharing swatches). I typically have to give specs for combined colors and percentages for placement. If anybody else has a better way of doing that though, would be super interested to hear

Im not sure i follow? Could you explain that different. Im not sure what is wrong with current method of sharing gradient?

@jjaybyrd
Copy link

@schroef sorry i think i was just venting about what i perceive as an illustrator problem in that you can communicate color stops but not easily their placement on the color slider. Your added approach above actually really helps me with a current project I'm working on (i was having to manually select gradient color stops), honestly thank you for all your great work 😃

@schroef
Copy link

schroef commented Jan 22, 2024

@jjaybyrd
Well editing color stops is not that difficult, especially in the last couple of illustratie versions. It's always best to to use global colors, because if you would edit those color, all your gradients in the document will be adjusted. It's always better to use global colors for ease of use.

I'll update the current version tomorrow with this new edit

@fuzenco
Copy link

fuzenco commented Jan 23, 2024

Guys, I’ve been designing for close to 30 years. One simple way of creating gradients and saving them as a color palette is to create a blend.

  1. Create two squares (or whatever) with the first and end color you want to use
  2. Select them and choose Object > Blend > Options
  3. Enter Specified Steps (in this case 8)
  4. Go back and choose Object > Blend > Make
  5. The gradient gets created and then…
  6. Choose Object > Blend > Expand
  7. Then distributing the items via Horizontal Distribution Evenly

I usually then just select the entire row and save to my palette. It's a quick and easy way to create gradients but more importantly to have the color stops at your disposal for using as needed.

Sorry if this derails this conversation but @schroef addition of gradients is cool in my opinion. I would just need to test what he's done.

In my case, if I used the blend method AND there were a way to group palette folders together in the CSV (maybe a blank line between groups), that would be a great addition to this script.

ss 2024-01-22 at 8 32 39 PM

@schroef
Copy link

schroef commented Jan 23, 2024

@jjaybyrd @fuzenco

okay i added updated version, this one can also do gradient colors
I will add "Gradient Color", so user know where it starts, then i add "ColorStop X" for each colorstop. I first had the color value as name, but that made the colorstops below the gradient color much longer in name. It works in both RGB and CMYK documents

https://raw.githubusercontent.com/schroef/Illustrator-Scripts/master/jd-export.jsx

GradientColor-stops-v0021

@fuzenco
Copy link

fuzenco commented Jan 23, 2024

This is masterful @schroef. I will test it out this afternoon. Thank you 👍

@schroef
Copy link

schroef commented Jan 23, 2024

p palette folders together in the CS

Yes i know that method, but one would only use that if they needs to color stops. Perhaps that is usefull GUI designers which want tints or inbetween colors of a gradient. Downside it adds a lot colors. PS i also have a script which can turn those colors into a real gradient. If i recall correct

PS i dont see how i could add colorgroups clearly in the CSV without changing the formatting to much. Basically adding the gradient is already changing that.
If i recall correct, the colorgroups are not exposed to script. ExtendScript doesnt know if a color is inside a color group

@jjaybyrd
Copy link

@schroef this is great, much clearer. it's been fantastic to see this progress after my silly little comment lol, looking forward to testing it out
@fuzenco thanks for the helpful tip, it seems like this might only work for evenly distributed gradients though? correct me if I'm wrong

@fuzenco
Copy link

fuzenco commented Jan 23, 2024

@schroef . Tested it. Gradient export works perfect. However…

I do see a bug in the script, though. After creating my gradients and properly exporting, if I add additional standard swatches (in my case 2 colors in a color group/folder), those two new colors do not get exported when the same file name is used for the CSV. In my case, I had to duplicate the file and rename it. Running the script did include the two new swatches this time around.

Here’s the file I used: http://fuzen.co/dropbox/test2.ai.zip

@fuzenco
Copy link

fuzenco commented Jan 23, 2024

@jjaybyrd yes, only for evenly distributed gradients. But prior to this script, getting color stop values was a manual process for me.

@fuzenco
Copy link

fuzenco commented Jan 23, 2024

PS i dont see how i could add colorgroups clearly in the CSV without changing the formatting to much. Basically adding the gradient is already changing that. If i recall correct, the colorgroups are not exposed to script. ExtendScript doesnt know if a color is inside a color group

Understood. You’ve done a masterful job.

That script to convert colors to a gradient would be cool. Is it somewhere I can download?

@schroef
Copy link

schroef commented Jan 25, 2024

@schroef . Tested it. Gradient export works perfect. However…

I do see a bug in the script, though. After creating my gradients and properly exporting, if I add additional standard swatches (in my case 2 colors in a color group/folder), those two new colors do not get exported when the same file name is used for the CSV. In my case, I had to duplicate the file and rename it. Running the script did include the two new swatches this time around.

Here’s the file I used: http://fuzen.co/dropbox/test2.ai.zip

@fuzenco
You mean it doesn't overwrite the existing CSV file?. I'll test that file
I don't have issues with that on my windows system

@schroef
Copy link

schroef commented Jan 25, 2024

PS i dont see how i could add colorgroups clearly in the CSV without changing the formatting to much. Basically adding the gradient is already changing that. If i recall correct, the colorgroups are not exposed to script. ExtendScript doesnt know if a color is inside a color group

Understood. You’ve done a masterful job.

That script to convert colors to a gradient would be cool. Is it somewhere I can download?

@fuzenco
Actually swatchgroups are exposed. I see what I can do about that. I would need to check if can test if a swath or spot is a child of a search group, than it would be possible
..

I did found it, it was made by someone else. It only does CMYK now. I'll need to update it so it will do all kinds of document color modes

@schroef
Copy link

schroef commented Jan 26, 2024

@schroef . Tested it. Gradient export works perfect. However…

I do see a bug in the script, though. After creating my gradients and properly exporting, if I add additional standard swatches (in my case 2 colors in a color group/folder), those two new colors do not get exported when the same file name is used for the CSV. In my case, I had to duplicate the file and rename it. Running the script did include the two new swatches this time around.

Here’s the file I used: http://fuzen.co/dropbox/test2.ai.zip

I just tested your file and it works just fine with v002. I opened your test doc and add a new swatchgroup and added 3 extra colors. I had the csv export from earlier open in Visual Studio Code and after a new export, those 3 colors where added.

You do have v002 right?

@fuzenco
Copy link

fuzenco commented Jan 26, 2024

I do but it must have been an intermittent bug. 3x I tried to overwrite that initial file. The new file was saved (which overwrote the test2 file), but the new colors were not being added. However, today everything seems to be working fine although I had 002 all along. So disregard this report and if it pops up again, I’ll notify you. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment