Skip to main content
Participating Frequently
May 13, 2023
Answered

Copy Color Values to Clipboard

  • May 13, 2023
  • 2 replies
  • 4021 views

On a daily bases, I need to copy color values (specifically spot PMS) from a project and paste them as text to notify my decorators what colors I'm using in my design. So it would be great if there was a script I could run that would grab all the color values from the selected object.

 

For example, if I had a group of selected objects, it would give me those spot PMS colors as a text value in my clipboard that I could then paste into other applications (eg. PMS 186 C, PMS 102 C, PMS 299 C).

 

It would also be helpful if it could give me CMYK or RGB values if there aren't any spot colors being used (eg. CMYK=0,100,100,0, CMYK=0,0,100,0, PMS 299 C).

This topic has been closed for replies.
Correct answer Inventsable

This might look like a lot, but it really isn't once you look past the polyfills at the beginning and just look through the copyColorsToClipboard function. I took what you did and refactored it a bit:

 

Object.prototype.keys = function (obj) {
    var keys = [];
    for (var key in obj) keys.push(key);
    return keys;
};
Array.prototype.map = function (callback) {
    var mappedParam = [];
    for (var i = 0; i < this.length; i++)
        mappedParam.push(callback(this[i], i, this));
    return mappedParam;
};
Array.prototype.indexOf = function (item) {
    for (var i = 0; i < this.length; i++) if (this[i] == item) return i;
    return -1;
};
Array.prototype.filter = function (callback) {
    var filtered = [];
    for (var i = 0; i < this.length; i++)
        if (callback(this[i], i, this)) filtered.push(this[i]);
    return filtered;
};
RGBColor.prototype.getString = CMYKColor.prototype.getString = SpotColor.prototype.getString = LabColor.prototype.getString = function () {
    var result = this.typename.replace(/color$/i, "").toUpperCase() + "=";
    var self = this; // Prevent namespace conflicts from scoping
    if (this.spot) return this.spot.name
    else result += Object.keys(self)
        .filter(function (key) {
            return !/typename|getString/.test(key);
        })
        .map(function (key) {
            // Color keys are always in order, so just return them rounded:
            return Math.round(self[key]);
        })
        .join(",");
    return result;
};
function get(type, parent, deep) {
    if (arguments.length == 1 || !parent) {
        parent = app.activeDocument;
        deep = true;
    }
    var result = [];
    if (!parent[type]) return [];
    for (var i = 0; i < parent[type].length; i++) {
        result.push(parent[type][i]);
        if (parent[type][i][type] && deep)
            result = [].concat(result, get(type, parent[type][i], deep));
    }
    return result;
}
function setClipboard(str) {
    var prev = get('selection');
    selection = null;
    var idoc = app.activeDocument;
    var tlayer = idoc.layers.add();
    var tframe = tlayer.textFrames.add();
    // Had faulty unicode whitespace characters showing, so strip them:
    tframe.contents = str.replace(/^\s*||\s*$/, "");
    tframe.translate(10);
    tframe.selected = true;
    app.copy();
    tlayer.remove();
    app.selection = prev;
}
function copyColorsToClipboard() {
    try {
        // Get selection as real array instead of Array-like
        var list = get("selection");
        // Compile array of all fills and strokes
        var colors = []
            .concat(
                list.map(function (item) {
                    return item.filled ? item.fillColor : null;
                }),
                list.map(function (item) {
                    return item.stroked ? item.strokeColor : null;
                })
            ).filter(function (color) {
                return !!color; // Remove any null values
            }).map(function (color) {
                return color.getString(); // Replace values with string in form [TYPE]=[VALUES]
            }).filter(function (colorString, index, arr) {
                return arr.indexOf(colorString) == index; // Remove duplicates
            })
        setClipboard(colors.join(", "))
    } catch (err) {
        alert(err);
    }
}
copyColorsToClipboard();

In the above, these are identical colors in value but the left is a Spot color swatch named PMS 186 C whereas the right is a pure RGB.

2 replies

c.pfaffenbichler
Community Expert
Community Expert
May 14, 2023

I expect I may have overlooked some scenario but does this help (using @CarlosCanto ’s text clipboard solution)? 

 

// 2023, use it at your own risk;
#target illustrator
if (app.documents.length > 0) {
var myDoc = activeDocument;
var mySel = myDoc.selection;
var theArray = new Array;
for (var m = 0; m < mySel.length; m++) {
    var theFill = mySel[m].fillColor;
    var theStroke = mySel[m].strokeColor;
    theArray.push(getColorInfo (theFill));
    theArray.push(getColorInfo (theStroke))
};
};
// remove multiples;
for (var n = theArray.length-1; n >= 0; n--) {
    var theCheck = false;
    for (var o = theArray.length-1; o >= 0; o--) {
        if (n != o) {
            if (theArray[n] == theArray[o]) {theCheck = true}
        }
    };
    if (theArray[n] == undefined) {theCheck = true};
    if (theCheck == true) {theArray.splice(n,1)};
};
theArray.sort();
setClipboard (theArray.join(","));
for (var m = 0; m < mySel.length; m++) {
    mySel[m].selected = true
};
////// by CarlosCanto //////
function setClipboard(str) {
    selection = null;
    var idoc = app.activeDocument;
    var tlayer = idoc.layers.add();
    var tframe = tlayer.textFrames.add();
    tframe.contents = str;
    tframe.translate(10);
    tframe.selected = true;
  
    app.copy();
    tlayer.remove();
  };
////// get color info //////
function getColorInfo (theColor) {
    switch (theColor.typename) {
        case "SpotColor":
            if (theColor.spot.colorType == "ColorModel.SPOT") {return(String(theColor.spot.name))};
            if (theColor.spot.colorType == "ColorModel.PROCESS") {
                if (theColor.spot.colorType == "SpotColorKind.SPOTCMYK") {return("CMYK="+theColor.spot.getInternalColor().join(","))};
                if (theColor.spot.colorType == "SpotColorKind.SPOTRGB") {return("RGB="+theColor.spot.getInternalColor().join(","))};
            };
        break;
        case "CMYKColor":
            return("CMYK="+Number(theColor.cyan)+","+Number(theColor.magenta)+","+Number(theColor.yellow)+","+Number(theColor.black));
        break;
        case "RGBColor":
            return("RGB="+Number(theColor.red)+","+Number(theColor.green)+","+Number(theColor.blue));
        break;
        default:
            return undefined
        break;
    }
};

 

edited

Inventsable
InventsableCorrect answer
Legend
May 14, 2023

This might look like a lot, but it really isn't once you look past the polyfills at the beginning and just look through the copyColorsToClipboard function. I took what you did and refactored it a bit:

 

Object.prototype.keys = function (obj) {
    var keys = [];
    for (var key in obj) keys.push(key);
    return keys;
};
Array.prototype.map = function (callback) {
    var mappedParam = [];
    for (var i = 0; i < this.length; i++)
        mappedParam.push(callback(this[i], i, this));
    return mappedParam;
};
Array.prototype.indexOf = function (item) {
    for (var i = 0; i < this.length; i++) if (this[i] == item) return i;
    return -1;
};
Array.prototype.filter = function (callback) {
    var filtered = [];
    for (var i = 0; i < this.length; i++)
        if (callback(this[i], i, this)) filtered.push(this[i]);
    return filtered;
};
RGBColor.prototype.getString = CMYKColor.prototype.getString = SpotColor.prototype.getString = LabColor.prototype.getString = function () {
    var result = this.typename.replace(/color$/i, "").toUpperCase() + "=";
    var self = this; // Prevent namespace conflicts from scoping
    if (this.spot) return this.spot.name
    else result += Object.keys(self)
        .filter(function (key) {
            return !/typename|getString/.test(key);
        })
        .map(function (key) {
            // Color keys are always in order, so just return them rounded:
            return Math.round(self[key]);
        })
        .join(",");
    return result;
};
function get(type, parent, deep) {
    if (arguments.length == 1 || !parent) {
        parent = app.activeDocument;
        deep = true;
    }
    var result = [];
    if (!parent[type]) return [];
    for (var i = 0; i < parent[type].length; i++) {
        result.push(parent[type][i]);
        if (parent[type][i][type] && deep)
            result = [].concat(result, get(type, parent[type][i], deep));
    }
    return result;
}
function setClipboard(str) {
    var prev = get('selection');
    selection = null;
    var idoc = app.activeDocument;
    var tlayer = idoc.layers.add();
    var tframe = tlayer.textFrames.add();
    // Had faulty unicode whitespace characters showing, so strip them:
    tframe.contents = str.replace(/^\s*||\s*$/, "");
    tframe.translate(10);
    tframe.selected = true;
    app.copy();
    tlayer.remove();
    app.selection = prev;
}
function copyColorsToClipboard() {
    try {
        // Get selection as real array instead of Array-like
        var list = get("selection");
        // Compile array of all fills and strokes
        var colors = []
            .concat(
                list.map(function (item) {
                    return item.filled ? item.fillColor : null;
                }),
                list.map(function (item) {
                    return item.stroked ? item.strokeColor : null;
                })
            ).filter(function (color) {
                return !!color; // Remove any null values
            }).map(function (color) {
                return color.getString(); // Replace values with string in form [TYPE]=[VALUES]
            }).filter(function (colorString, index, arr) {
                return arr.indexOf(colorString) == index; // Remove duplicates
            })
        setClipboard(colors.join(", "))
    } catch (err) {
        alert(err);
    }
}
copyColorsToClipboard();

In the above, these are identical colors in value but the left is a Spot color swatch named PMS 186 C whereas the right is a pure RGB.

Sergey Osokin
Inspiring
May 17, 2023

It looks great. But I made a few changes of my own copyColorsToClipboard.jsx:
- added path collection within groups
- added gradient and grayscale color support
- added flags to get Spot color values and Spot tint values
- color values are written to the clipboard in order of object coordinates.
If you think these changes are appropriate, you can add them to your GitHub repository.


Inventsable
Legend
May 13, 2023

This isn't a full solution because I don't have the time to do the color collection part, but I've gotten setting clipboard content to work (only tested on Windows) like this:

 

function setClipboard(string) {
  var isWin = /win/i.test($.os);
  var tmp = isWin
    ? File(Folder.desktop + "/tmp.bat")
    : File(Folder.desktop + "/tmp.sh");
  tmp.open("w");
  tmp.write(
    isWin
      ? 'echo off\r\necho | set /p="' + string + '" | clip'
      : '#!/bin/sh\r\necho "' + string + '"  | pbcopy\\r\\nexit 0'
  );
  tmp.close();
  while (!tmp.exists) $.sleep(100);
  tmp.execute();
  tmp.remove();
}

 

 If it doesn't work or any one else knows of a better solution definitely let me know, unfortunately I've never been able to get the opposite to work of retrieving clipboard contents without other solutions like Autohotkey. The idea would essentially be to construct a bat/sh file to echo clipboard contents to a raw text file then immediately read the text file and delete both but executing a sh file that creates another file never works in scripting for me even when the exact same file does work manually executed.

CarlosCanto
Community Expert
Community Expert
May 13, 2023

here's an alternative way of placing a string to clipboard

 

function setClipboard(str) {
  selection = null;
  var idoc = app.activeDocument;
  var tlayer = idoc.layers.add();
  var tframe = tlayer.textFrames.add();
  tframe.contents = str;
  tframe.translate(10);
  tframe.selected = true;

  app.copy();
  tlayer.remove();
}

setClipboard ("hello world");