-
-
Save jmsdnns/f170599dc98f036ba72a2618797f525d to your computer and use it in GitHub Desktop.
/** | |
* 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); | |
} | |
@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
@jjaybyrd yes, only for evenly distributed gradients. But prior to this script, getting color stop values was a manual process for me.
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 . 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
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 . 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?
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.
@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