Copy link to clipboard
Copied
We are often tasked to create printed squares to closely match a PMS color. While there are many good starting points, in the end we have to produce a range of 5-6 variations. While Illustrator does offer some tricks to employ, you still have to go through and label each square with the CMYK call outs. Tedious and prone to error. Let me introduce "Swatch-nado". Make and select two new swatches or select two from your existing library. Then run the script. It will create all the squares, blending between the swatches and labeling them underneath each square. Try it out. I do have a request. It fails on Global Swatches. And I can't seem to make the exact number of squares at input. It always adds 1. Anyway here it is with attribution in the script.
Copy link to clipboard
Copied
Hi @RoyBoySolorio, fantastic work getting this script together and working. I had a look through the code and, to be honest, it is a bit complicated for me to easily fix the remaining problems. I have a little script object called SwatchCollection that I wrote for laying out swatches in a similar way and I could fairly easily extend it to do the interpolation that you need. Is that something you'd be interested in? If so, I can post when I get a chance. Meanwhile someone else might have a quicker answer.
- Mark
Copy link to clipboard
Copied
Thanks M1b
I looked at your script while gathering info, so thank again.
Copy link to clipboard
Copied
Hi @RoyBoySolorio, I have adapted a bit with some functions that you discuss but I haven't tested it much. Have a read through the code and you can get some ideas for your script, or try it out for your situation. You can configure it by setting the ColorChip properties at line 40. Let me know how you go.
- Mark
P.S. one thing it doesn't do it multi-row chips.
/**
* @file Sample usage of ColorChips.js
* @author m1b
* @discussion https://community.adobe.com/t5/illustrator-discussions/here-s-a-script-that-makes-blended-squares-between-two-selected-swatches/m-p/13253249
*/
(function () {
// normally ColorChips is a separate library
// file but, for the forum post, I'll include
// it at the end of this script
var ColorChips = loadColorChips();
// 1. get two selected colors
var doc = app.activeDocument,
selectedSwatches = doc.swatches.getSelected();
// selectedSwatches = [doc.swatches[5], doc.swatches[7]];
if (selectedSwatches.length != 2) {
alert('Please select two swatches and try again.');
return;
}
// 2. make interpolated breakdowns of the two swatches
var numberOfChips = Number(prompt('Create how many color chips?', 7) || 7),
breakdowns = interpolateArrays(ColorChips.breakdownFromColor(selectedSwatches[0].color), ColorChips.breakdownFromColor(selectedSwatches[1].color), numberOfChips);
// 3. make the ColorChips
var chips = new ColorChips(
// edit any of the following settings:
{
doc: doc,
position: [100, 100],
showName: false,
showBreakdown: true,
nameFilter: function (c, i) { return c.name.replace(/^PANTONE\s/, '') },
labelFont: 'Gotham-Book',
labelFontSize: 6,
labelColor: undefined,
labelGap: 5,
chipWidth: 30,
chipHeight: 30,
chipStrokeWidth: 0.5,
chipStrokeColor: undefined,
chipGap: 5,
makeSwatches: true,
makeGlobalSwatches: true,
makeColorGroup: true,
colorGroupName: 'My Colors',
showBorder: true,
borderColor: undefined,
margin: 10,
breakdowns: breakdowns,
}
);
// draw the ColorChips:
// horizontal layout
chips.drawHorizontally();
// vertical layout
// chips.drawVertically();
/**
* 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, inter3, ..., interN, arr2]
*/
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;
}
})();
function loadColorChips() {
/**
* Makes a "Color Chips" group item
* with can be drawn horizontally or
* vertically, with one chip per
* supplied breakdown or swatch.
* @author m1b
* @version 2022-10-03
* All positioning measurements are in points.
* @constructor
* @param {Object} options
* @param {Document} [options.doc] - an Illustrator Document (default: activeDocument).
* @param {Array<Number>} [options.position] - [x, y] position of top left of swatch collection (default: center of view).
* @param {Number} [options.chipWidth] - the color chip width (default: 30).
* @param {Number} [options.chipHeight] - the color chip height (default: 30).
* @param {Number} [options.chipStrokeWidth] - the color chip stroke width (default: 1).
* @param {Number} [options.chipGap] - the space between color chips (default: 10).
* @param {Number} [options.labelFont] - the label font (default: Illustrator's default).
* @param {Number} [options.labelFontSize] - the label text size (default: 7).
* @param {Number} [options.labelGap] - the space between the chip and the label (default: 5).
* @param {Number} [options.margin] - the space between the border and the chips (default: 10).
* @param {Boolean} [options.showBorder] - whether to show the outer border (default: true).
* @param {Boolean} [options.showName] - whether to show labels (default: true).
* @param {Boolean} [options.showBreakdown] - whether to show labels (default: true).
* @param {Color} [options.borderColor] - the color of the border (default: non-global black).
* @param {Color} [options.chipStrokeColor] - the color of the strokes (default: non-global black).
* @param {Color} [options.labelColor] - the color of the labels (default: non-global black).
* @param {Boolean} [options.makeSwatches] - whether to make swatches (default: false).
* @param {Boolean} [options.makeGlobalSwatches] - whether swatches made are global (default: false).
* @param {Boolean} [options.makeColorGroup] - whether to put swatches in a ColorGroup (default: false).
* @param {String} [options.colorGroupName] - name for the ColorGroup (default: 'Chips').
* @param {Array<Swatch>|Swatches|Array<Number>} [options.swatches] - an array of Swatches or a Swatches object (default: all swatches in document).
* @param {Function} [options.swatchFilter] - a function that filters swatches (default: no filter).
* @param {Function} [options.nameFilter] - a function that returns the color name (default: returns unmodified name).
*/
function ColorChips(options) {
var black = new GrayColor()
black.gray = 100;
var self = this,
doc = self.doc = options.doc || app.activeDocument,
position = self.position = options.position,
chipWidth = self.chipWidth = options.chipWidth || 30,
chipHeight = self.chipHeight = options.chipHeight || 30,
chipStrokeWidth = self.chipStrokeWidth = options.chipStrokeWidth != undefined ? options.chipStrokeWidth : 1,
chipGap = self.chipGap = options.chipGap != undefined ? options.chipGap : 0,
labelFont = self.labelFont = options.labelFont,
labelGap = self.labelGap = options.labelGap != undefined ? options.labelGap : 5,
showBorder = self.showBorder = options.showBorder !== false,
margin = self.margin = options.margin != undefined ? options.margin : 0,
borderColor = self.borderColor = options.borderColor || black,
chipStrokeColor = self.chipStrokeColor = options.chipStrokeColor || black,
nameFilter = self.nameFilter = options.nameFilter || function (c) { return c.name },
showName = self.showName = options.showName !== false,
showBreakdown = self.showBreakdown = options.showBreakdown !== false,
labelColor = self.labelColor = options.labelColor || black,
labelFontSize = self.labelFontSize = options.labelFontSize || 7,
unfilteredSwatches = options.swatches || doc.swatches,
swatchFilter = options.swatchFilter || function (c, index) { return index > 1 },
colors = self.colors = [],
breakdowns = self.breakdowns = options.breakdowns || [],
makeSwatches = options.makeSwatches !== false,
makeGlobalSwatches = options.makeGlobalSwatches !== false,
makeColorGroup = options.makeColorGroup !== false,
colorGroupName = self.colorGroupName = options.colorGroupName || 'Untitled';
self.colorChipsGroupItems = [];
if (breakdowns != undefined) {
// make colors from breakdowns
for (var i = 0; i < breakdowns.length; i++) {
var c = ColorChips.colorFromBreakdown(breakdowns[i]);
if (c != undefined)
colors.push(c);
}
}
else {
// filter swatches
for (var i = 0; i < unfilteredSwatches.length; i++)
if (
options.swatchFilter == undefined
|| options.swatchFilter(unfilteredSwatches[i], i)
) {
colors.push(unfilteredSwatches[i]);
}
}
if (makeSwatches === true)
self.colorGroup = self.makeSwatches(makeGlobalSwatches, makeColorGroup, colorGroupName)
if (self.labelFont != undefined)
self.labelFont = app.textFonts[self.labelFont];
}
/**
* Draw the ColorChips to document.
* @author m1b
* @version 2022-10-03
* @param {Bool} isHorizontal - whether to draw in horizontal or vertical format (default: true).
* @param {Array<Number>} position - the coordinates of the top-left of the swatch panel item.
* @returns {GroupItem} - the ColorChips page item.
*/
ColorChips.prototype.draw = function (isHorizontal, position) {
var self = this,
group = self.doc.groupItems.add(),
doc = self.doc,
chipWidth = self.chipWidth,
chipHeight = self.chipHeight,
chipGap = self.chipGap,
chipStrokeWidth = self.chipStrokeWidth,
makeLabel = self.showName === true || self.showBreakdown === true,
labelGap = makeLabel ? self.labelGap : 0,
margin = self.margin,
labelFunction = self.labelFunction || isHorizontal ? basicLabelHorizontal : basicLabelVertical;
var x = 0,
y = 0,
textWidth = 0,
textHeight = 0;
for (var i = 0; i < self.colors.length; i++) {
var c = self.colors[i];
// make the label textFrame
if (makeLabel) {
var textFrame = labelFunction(doc, x, y, c, i, self);
if (textFrame.width > textWidth)
textWidth = textFrame.width;
if (textFrame.height > textHeight)
textHeight = textFrame.height;
textFrame.moveToEnd(group);
}
// make the chip rectangle
var rect = doc.pathItems.rectangle(y, x, chipWidth, chipHeight);
rect.filled = true;
rect.fillColor = c.hasOwnProperty('color') ? c.color : c;
rect.stroked = self.chipStrokeWidth > 0;
rect.strokeWidth = chipStrokeWidth;
rect.strokeColor = self.chipStrokeColor;
rect.moveToEnd(group);
if (isHorizontal)
x += self.chipWidth + self.chipGap;
else
y -= self.chipHeight + self.chipGap;
}
var totalWidth,
totalHeight;
if (isHorizontal) {
totalWidth = margin + (chipWidth * self.colors.length) + (chipGap * (self.colors.length - 1)) + margin;
totalHeight = margin + chipHeight + labelGap + textHeight + margin;
}
else {
totalWidth = margin + chipWidth + labelGap + textWidth + margin;
totalHeight = margin + chipHeight * self.colors.length + chipGap * (self.colors.length - 1) + margin;
}
// outer border
if (self.showBorder) {
rect = doc.pathItems.rectangle(margin, - margin, totalWidth, totalHeight);
rect.strokeColor = self.borderColor;
rect.strokeWidth = self.chipStrokeWidth;
rect.filled = false;
rect.moveToEnd(group);
}
if (position != undefined)
position = doc.convertCoordinate([position[0] + margin, -position[1] - margin], CoordinateSystem.ARTBOARDCOORDINATESYSTEM, CoordinateSystem.DOCUMENTCOORDINATESYSTEM);
else if (self.position != undefined)
position = doc.convertCoordinate([self.position[0], -self.position[1]], CoordinateSystem.ARTBOARDCOORDINATESYSTEM, CoordinateSystem.DOCUMENTCOORDINATESYSTEM);
else
position = [doc.activeView.centerPoint[0] - totalWidth / 2, doc.activeView.centerPoint[1] + totalHeight / 2];
// final positioning
group.position = position;
self.colorChipsGroupItems.push(group);
return group;
};
/**
* Draw the ColorChips to document, horizontally.
* @author m1b
* @version 2022-10-01
* @param {Array<Number>} position - the coordinates of the top-left of the swatch panel item.
* @returns {GroupItem} - the ColorChips page item.
*/
ColorChips.prototype.drawHorizontally = function (position) {
return this.draw(true, position);
};
/**
* Draw the ColorChips to document, vertically.
* @author m1b
* @version 2022-10-01
* @param {Array<Number>} position - the coordinates of the top-left of the swatch panel item.
* @returns {GroupItem} - the ColorChips page item.
*/
ColorChips.prototype.drawVertically = function (position) {
return this.draw(false, position);
};
/**
* Returns Color object when given an array of values.
* @author m1b
* @version 2022-10-08
* eg. [50] = GrayColor
* [255,128,0] = RGBColor
* [10,20,30,0] = CMYKColor
* @param {Array<Number>} breakdown - an array of color breakdown values.
* @returns {Color}
*/
ColorChips.colorFromBreakdown = function colorFromBreakdown(breakdown) {
if (breakdown == undefined)
return;
// sanity check
for (var i = 0; i < breakdown.length; i++)
if (breakdown[i] < 0)
breakdown[i] = 0;
var colr;
switch (breakdown.length) {
case 1: // [K]
colr = new GrayColor();
colr.gray = breakdown[0];
break;
case 3: // [R,G,B]
colr = new RGBColor();
colr.red = breakdown[0];
colr.green = breakdown[1];
colr.blue = breakdown[2];
break;
case 4: // [C,M,Y,K]
colr = new CMYKColor();
colr.cyan = breakdown[0];
colr.magenta = breakdown[1];
colr.yellow = breakdown[2];
colr.black = breakdown[3];
break;
default:
throw Error('ColorChips.colorFromBreakdown: couldn\'t parse color (' + breakdown + ')');
}
return colr;
};
/**
* Returns the color breakdown of a given Color.
* eg. GrayColor = [50]
* RGBColor = [255,128,0]
* CMYKColor = [10,20,30,0]
* @author m1b
* @version 2022-10-08
* @param {Color} colr - an Illustrator Color.
* @returns {Array<Number>}
*/
ColorChips.breakdownFromColor = function breakdownFromColor(colr) {
if (colr == undefined)
return;
else if (colr.constructor.name == 'Swatch')
colr = colr.color;
else if (colr.constructor.name == 'SpotColor')
colr = colr.spot.color;
else if (colr.constructor.name == 'GradientColor')
colr = colr.gradient.gradientStops[0].color;
var breakdown;
switch (colr.constructor.name) {
case 'GrayColor': // [K]
breakdown = [colr.gray];
break;
case 'RGBColor': // [R,G,B]
breakdown = [colr.red, colr.green, colr.blue];
break;
case 'CMYKColor': // [C,M,Y,K]
breakdown = [colr.cyan, colr.magenta, colr.yellow, colr.black];
break;
default:
throw Error('ColorChips.breakdownFromColor: couldn\'t parse breakdown from "' + colr + '".');
}
return breakdown;
};
/**
* Returns array of Colors by interpolating
* between the two supplied colors.
* eg. 20K and 50K with n = 2 -> [20K, 30K, 40K, 50K]
* @author m1b
* @version 2022-10-08
* @param {Color} c1 - an Illustrator Color.
* @param {Color} c2 - an Illustrator Color.
* @param {Number} n - the number of steps in between.
* @returns {Array<Number>}
*/
ColorChips.colorsByInterpolation = function colorsByInterpolation(c1, c2, n) {
var colrs = [],
breakdown1 = ColorChips.breakdownFromColor(c1),
breakdown2 = ColorChips.breakdownFromColor(c2);
var breakdowns = interpolateArrays(breakdown1, breakdown2, n);
// make colors
for (var i = 0; i < breakdowns.length; i++) {
var c = ColorChips.colorFromBreakdown(breakdowns[i]);
if (c != undefined)
colrs.push(c);
}
};
/**
* Create a ColorGroup containing
* the ColorChip's colors.
* @author m1b
* @version 2022-10-03
* @param {Boolean} makeGlobal - whether the swatches created will be global (default: false).
* @param {Boolean} makeColorGroup - whether the swatches created will be global (default: false).
* @param {String} colorGroupName - the name of the colorGroup.
* @returns {SwatchGroup}
*/
ColorChips.prototype.makeSwatches = function makeSwatches(makeGlobal, makeColorGroup, colorGroupName) {
var self = this,
colorGroup;
if (makeColorGroup === true) {
for (var i = 0; i < self.doc.swatchGroups.length; i++)
if (self.doc.swatchGroups[i].name == colorGroupName)
colorGroup = self.doc.swatchGroups[i];
if (colorGroup == undefined) {
colorGroup = self.doc.swatchGroups.add();
colorGroup.name = colorGroupName;
}
if (colorGroup == undefined)
makeColorGroup = false;
}
for (var i = 0; i < self.colors.length; i++) {
var c = self.colors[i],
colorName = basicLabelForColor(c, '=', ' '),
existingColor = getSwatch(self.doc, colorName),
sw;
if (existingColor != undefined)
existingColor.remove();
if (makeGlobal === true) {
sw = self.doc.spots.add();
sw.colorType = ColorModel.PROCESS;
}
else {
sw = self.doc.swatches.add();
}
sw.name = colorName;
sw.color = c;
// store the swatch of the color
for (var j = 0; j < self.doc.swatches.length; j++)
if (self.doc.swatches[j].name == colorName)
self.colors[i] = self.doc.swatches[j];
if (makeColorGroup)
colorGroup.addSwatch(self.colors[i]);
// self.colors[i] = self.doc.swatches.itemByName(colorName);
}
return colorGroup;
/**
* Returns a document swatch by name.
* @author m1b
* @version 2022-10-10
* @param {Document} doc - and Illustrator Document.
* @param {String} name - the swatch name.
* @returns {Swatch}
*/
function getSwatch(doc, name) {
for (var i = 0; i < doc.swatches.length; i++)
if (doc.swatches[i].name == name)
return doc.swatches[i];
};
};
/**
* Creates and formats a text label
* for horizontally drawn ColorChips.
* @author m1b
* @version 2022-10-09
* @param {Document} doc - an Illustrator Document.
* @param {Number} x - the label position x.
* @param {Number} y - the label position y.
* @param {Color} c - an Illustrator Color.
* @param {any} self - a parent object, eg. a ColorChips.
* @returns {TextFrame} - the label.
*/
function basicLabelHorizontal(doc, x, y, c, i, self) {
var textFrame = doc.textFrames.pointText([x, y]);
if (self.labelFont != undefined)
textFrame.textRange.characterAttributes.textFont = self.labelFont;
var nameParts = [];
if (self.showName)
nameParts.push(self.nameFilter(c, i));
if (self.showBreakdown)
nameParts.push(basicLabelForColor(c, '\t', '\n'));
textFrame.contents = nameParts.join('\n');
textFrame.textRange.fillColor = self.labelColor;
textFrame.textRange.size = self.labelFontSize;
// JUSTIFIED LEFT
textFrame.position = [x, y - self.chipHeight - self.labelGap];
textFrame.textRange.justification = Justification.LEFT;
// JUSTIFIED CENTRED
// textFrame.position = [x + self.chipWidth / 2, y - self.chipHeight - self.labelGap];
// textFrame.textRange.justification = Justification.CENTER;
// set tab stop to align value
var tstops = [new TabStopInfo()];
tstops[0].position = self.labelFontSize * 1.2;
textFrame.textRange.tabStops = tstops;
return textFrame;
};
/**
* Creates and formats a text label
* for vertically drawn ColorChips.
* @author m1b
* @version 2022-10-09
* @param {Document} doc - an Illustrator Document.
* @param {Number} x - the label position x.
* @param {Number} y - the label position y.
* @param {Color} c - an Illustrator Color.
* @param {any} self - a parent object, eg. a ColorChips.
* @returns {TextFrame} - the label.
*/
function basicLabelVertical(doc, x, y, c, i, self) {
var textFrame = doc.textFrames.pointText([x, y]);
if (self.labelFont != undefined)
textFrame.textRange.characterAttributes.textFont = self.labelFont;
var nameParts = [];
if (self.showName)
nameParts.push(self.nameFilter(c, i));
if (self.showBreakdown)
nameParts.push(basicLabelForColor(c, '\t', '\n'));
textFrame.contents = nameParts.join('\n');
textFrame.textRange.fillColor = self.labelColor;
textFrame.textRange.size = self.labelFontSize;
// JUSTIFIED LEFT
textFrame.position = [x + self.chipWidth + self.labelGap, y];
textFrame.textRange.justification = Justification.LEFT;
// set tab stop to align value
var tstops = [new TabStopInfo()];
tstops[0].position = self.labelFontSize * 1.2;
textFrame.textRange.tabStops = tstops;
return textFrame;
};
/**
* Returns a string suitable for
* labelling a color.
* @author m1b
* @version 2022-10-09
* Example output:
* 'C=100 M=50 Y=0 K=10' (to match Color.toString)
* 'C\t100\nM\t50\nY\t0\nK\t10' (align value with tabs, one per line)
* @param {Color} cc - an Illustrator Color.
* @param {String} delim1 - a string that delimits channel values (default: '=').
* @param {String} delim2 - a string that divides the channel reference and the value (default: ' ').
* @param {Number} [decimalPlaces] - how many decimal places to show in value (default: 0).
* @returns {String}
*/
function basicLabelForColor(c, delim1, delim2, decimalPlaces) {
var label,
cc = c.hasOwnProperty('color') ? c.color : c;
if (cc.constructor.name == 'SpotColor')
cc = cc.spot.color;
if (
cc.hasOwnProperty('red')
|| cc.length == 3
) {
label = symbolValueString(cc, delim1, delim2, decimalPlaces, [
{ symbol: 'R', property: 'red' },
{ symbol: 'G', property: 'green' },
{ symbol: 'B', property: 'blue' }
]);
}
else if (
cc.hasOwnProperty('cyan')
|| cc.length == 4
) {
label = symbolValueString(cc, delim1, delim2, decimalPlaces, [
{ symbol: 'C', property: 'cyan' },
{ symbol: 'M', property: 'magenta' },
{ symbol: 'Y', property: 'yellow' },
{ symbol: 'K', property: 'black' }
]);
}
else if (
cc.hasOwnProperty('gray')
|| cc.length == 1
) {
label = symbolValueString(cc, delim1, delim2, decimalPlaces, [
{ symbol: 'K', property: 'gray' }
]);
}
else
label = '#ERROR';
return label;
};
/**
* Returns a text label derived
* from a color's breakdown.
* @author m1b
* @version 2022-10-09
* The `obj` argument can be any
* object having properties expressed
* in `map`, eg. an Illustrator Color
* with properties expressed in the map,
* eg. 'red' which gets the value '210';
* or it can be an array of values,
* eg. [210, 255, 33] for RGB and the
* map's properties can be '0', '1', '2'.
* Example output:
* "R=210 G=255 B=33"
* @param {Object} obj - an object or an array of values.
* @param {String} [delim1] - first delimiter (default: '=').
* @param {String} [delim2] - second delimiter, between values (default: ' ').
* @param {Number} [decimalPlaces] - how many decimal places to show in value (default: 0).
* @param {Object} map - label info object {symbol: 'R', property: 'red'}
*/
function symbolValueString(obj, delim1, delim2, decimalPlaces, map) {
if (delim1 == undefined)
delim1 = '=';
if (delim2 == undefined)
delim2 = ' ';
if (decimalPlaces == undefined)
decimalPlaces = 0;
var label = [];
for (var i = 0; i < map.length; i++)
label.push(map[i].symbol + delim1 + (obj[map[i].property] != undefined ? round(obj[map[i].property], decimalPlaces) : round(obj[i])));
return label.join(delim2);
};
/**
* Rounds a number to n decimal places.
* @author m1b
* @version 2022-10-09
* @param {Number} num - the number to round.
* @param {Number} [places] - the number of decimal places (default: 0)
*/
function round(num, places) {
places = Math.pow(10, places || 0);
return Math.round(num * places) / places;
};
return ColorChips;
}
Copy link to clipboard
Copied
This great, m1b! Love the ways you can show or hide frames/labels and change size pretty easily. Very impressive code.
Copy link to clipboard
Copied
I do have a request. It fails on Global Swatches. And I can't seem to make the exact number of squares at input. It always adds 1.
When I tick the "global" box in a swatch's options and run the script, I get the message "requires 2 swatches of the same type, either RGB or CMYK". I may be misunderstanding the question or the concept of a global color, but ticking the "global" box makes a script read a color as a spot color. So it should be a matter of adding a block to the script to work with a spot color (similar to the ones for CMYK and RGB).
Copy link to clipboard
Copied
I did find a script that converts the selected swatches into global swatches, which is a starting point. Your idea to build the next block with those spots in mind could be the solution. Thanks