AnalysisColourConvertor.jsx (336 lines of code) (raw):
/*
Description: Script for generating dark mode artboards for Guardian graphics
Requirements: Adobe Illustrator CC and later
Date: March, 2022
Author: The Guardian, Garry Blight
Based on Duplicate_Artboards_Light.jsx for Adobe Illustrator
Description: Script for copying the selected Artboard with his artwork
Requirements: Adobe Illustrator CS6 and later
Date: October, 2020
Author: Sergey Osokin, email: hi@sergosokin.ru
*/
var i, artboardOriginal, artboardOriginalRect, artboardCopy, artboardCopyRect, abHeight;
var doc = app.activeDocument;
var artboards = doc.artboards;
var abLength = artboards.length;
var mode = 0;
var convertedArtboardsTotal = 0;
guardianNeutralsLookup = {};
var neutralThreshold = 13;
// var skippedColors = [];
var guardianNeutralsMap = [];
// analysis colours
guardianNeutralsMap.push( { analysis: "#a19a99", standard: "#a1a1a1" } );
guardianNeutralsMap.push( { analysis: "#bab2b1", standard: "#bababa" } );
guardianNeutralsMap.push( { analysis: "#dcd3d1", standard: "#dcdcdc" } );
guardianNeutralsMap.push( { analysis: "#f3e8e7", standard: "#f3f3f3" } );
guardianNeutralsMap.push( { analysis: "#fff4f2", standard: "#ffffff" } );
for (var i = 0; i < guardianNeutralsMap.length; i++) {
var standardColRGB = hexToRgb(guardianNeutralsMap[i].standard);
var analysisColRGB = hexToRgb(guardianNeutralsMap[i].analysis);
var key = String(analysisColRGB.r);
guardianNeutralsLookup[key] = { standardHex: guardianNeutralsMap[i].standard, analysisHex: guardianNeutralsMap[i].analysis, standardRGB: standardColRGB, analysisRGB: analysisColRGB };
}
// Main Window
var dialog = new Window('dialog', "Analysis Colour Convertor");
dialog.orientation = 'column';
dialog.alignChildren = ['fill', 'center'];
// Input fields
var abGroup = dialog.add('group');
abGroup.orientation = 'column';
abGroup.alignChildren = ['fill', 'top'];
abGroup.add('statictext', undefined, "Artboard conversion mode");
var modeList = abGroup.add('dropdownlist', [0, 0, 280, 30], ["Standard to Analysis", "Analysis to Standard"]);
modeList.selection = 0;
// Buttons
var btnsGroup = dialog.add('group');
btnsGroup.orientation = 'row';
btnsGroup.alignChildren = ['fill', 'center'];
var cancel = btnsGroup.add('button', undefined, "Cancel", { name: 'cancel' });
var ok = btnsGroup.add('button', undefined, "OK", { name: 'ok' });
// Change listeners
modeList.onChange = function() {
mode = modeList.selection.index;
}
cancel.onClick = dialog.close;
ok.onClick = okClick;
dialog.center();
dialog.show();
function okClick() {
dialog.close();
convert();
}
function convertToNum(str, def) {
// Remove unnecessary characters
str = str.replace(/,/g, '.').replace(/[^\d.]/g, '');
// Remove duplicate Point
str = str.split('.');
str = str[0] ? str[0] + '.' + str.slice(1).join('') : '';
if (isNaN(str) || str.length == 0) return parseFloat(def);
return parseFloat(str);
}
// Main wrapper function
function convert() {
//alert("Mode=" + mode + " Y offset=" + spacing + " Invert white text=" + invertWhiteText);
selection = null;
unlockLayers(doc.layers);
saveItemsState(doc.layers, '%isLocked', '%isHidden');
for (i = 0; i < abLength; i++) {
doc.artboards.setActiveArtboardIndex(i);
// Copy Artwork
doc.selectObjectsOnActiveArtboard();
var abItems = selection;
convertArtboard(i, abItems);
convertedArtboardsTotal++;
}
restoreItemsState(doc.layers, '%isLocked', '%isHidden');
selection = null;
showCompletionAlert();
function showCompletionAlert() {
var rule = "\n================\n";
var alertText = "", alertHed;
if (mode == 1) {
fromToString = "Analysis to Standard";
} else {
fromToString = "Standard to Analysis";
}
alertHed = "The script has converted " + convertedArtboardsTotal + " artboards from " + fromToString;
// alertText = "\n";
// alertText += skippedColors.length + " neutral colours skipped:\n";
// for (var i = 0; i < skippedColors.length; i++) {
// alertText += skippedColors[i];
// if (i != skippedColors.length-1) {
// alertText += ", ";
// }
// }
//alertText = makeList(errors, "Error", "Errors");
//alertText += makeList(warnings, "Warning", "Warnings");
//alertText += makeList(feedback, "Information", "Information");
//alertText += "\n";
alert(alertHed + alertText);
}
}
/**
* Unlock all Layers & Sublayers
* @param {object} _layers - the collection of layers
*/
function unlockLayers(_layers) {
for (var i = 0, len = _layers.length; i < len; i++) {
if (_layers[i].locked) _layers[i].locked = false;
if (_layers[i].layers.length) unlockLayers(_layers[i].layers);
}
}
/**
* Collect items
* @param {object} obj - collection of items
* @param {array} arr - output array with childrens
*/
function getItems(obj, arr) {
for (var i = 0, len = obj.length; i < len; i++) {
var currItem = obj[i];
try {
switch (currItem.typename) {
case 'GroupItem':
arr.push(currItem);
getItems(currItem.pageItems, arr);
break;
default:
arr.push(currItem);
break;
}
} catch (e) {}
}
}
/**
* Save information about locked & hidden pageItems & layers
* @param {object} _layers - the collection of layers
* @param {string} lKey - keyword for locked items
* @param {string} hKey - keyword for hidden items
*/
function saveItemsState(_layers, lKey, hKey) {
var allItems = [];
for (var i = 0, len = _layers.length; i < len; i++) {
var currLayer = _layers[i];
if (currLayer.layers.length > 0) {
saveItemsState(currLayer.layers, lKey, hKey);
}
getItems(currLayer.pageItems, allItems);
for (var j = 0, iLen = allItems.length; j < iLen; j++) {
var currItem = allItems[j];
if (currItem.locked) {
currItem.locked = false;
currItem.note += lKey;
}
if (currItem.hidden) {
currItem.hidden = false;
currItem.note += hKey;
}
}
}
redraw();
}
/**
* Restoring locked & hidden pageItems & layers
* @param {object} _layers - the collection of layers
* @param {string} lKey - keyword for locked items
* @param {string} hKey - keyword for hidden items
*/
function restoreItemsState(_layers, lKey, hKey) {
var allItems = [],
regexp = new RegExp(lKey + '|' + hKey, 'gi');
for (var i = 0, len = _layers.length; i < len; i++) {
var currLayer = _layers[i];
if (currLayer.layers.length > 0) {
restoreItemsState(currLayer.layers, lKey, hKey);
}
getItems(currLayer.pageItems, allItems);
for (var j = 0, iLen = allItems.length; j < iLen; j++) {
var currItem = allItems[j];
if (currItem.note.match(lKey) != null) {
currItem.note = currItem.note.replace(regexp, '');
currItem.locked = true;
}
if (currItem.note.match(hKey) != null) {
currItem.note = currItem.note.replace(regexp, '');
currItem.hidden = true;
}
}
}
}
/**
* Duplicate the selected artboard. Based on the idea of @Silly-V
* @param {number} i - current artboard index
* @param {object} items - collection of items on the artboard
* @param {string} suffix - copy name suffix
*
*/
function convertArtboard(i, items) {
//var thisAb = doc.artboards[i];
convertColours(items);
}
// Cycle through items and tweak colours
function convertColours(collection) {
var arr = [];
for (var i = 0, ii, len = collection.length; i < len; i++) {
if(collection[i].typename == "GroupItem") {
collection[i].pageItems = convertColours(collection[i].pageItems);
}
if(collection[i].typename == "TextFrame") {
changeCharacterColors(collection[i]);
}
if(collection[i].typename == "PathItem") {
changePathColors(collection[i]);
}
if(collection[i].typename == "CompoundPathItem" && collection[i].pathItems.length) {
for (ii = 0; ii < collection[i].pathItems.length; ii ++) {
changePathColors(collection[i].pathItems[ii]);
}
}
}
//return collection;
}
function changeCharacterColors(textObject) {
if(textObject.textRange.length > 0) {
var textRange = textObject.textRange;
var paras = textRange.paragraphs;
for (var iii=0; iii<paras.length; iii++) {
if (paras != undefined && paras.length != 0) {
try {
var p = paras[iii];
for (var ii=0, n=p.characters.length; ii<n; ii++) {
var c = p.characters[ii];
c.fillColor = convertTextColor(c.fillColor);
}
} catch(error) {
//alert(error);
}
}
}
}
return textObject
}
function changePathColors(pathObject) {
try {
//if (pathObject.stroked) {
pathObject.strokeColor = convertPathColor(pathObject.strokeColor);
//}
//if (pathObject.filled) {
pathObject.fillColor = convertPathColor(pathObject.fillColor);
//}
} catch(error) {
//alert(error);
}
}
function getConvertedColor(r, g, b, isText) {
var originalCol = {r:r, g:g, b:b }, newCol = originalCol;
if (mode == 0) {
newCol = guardianNeutralsLookup[String(r)].analysisRGB;
if (newCol === undefined) {
newCol = originalCol;
//skippedColors.push(rgbToHex(r, g, b));
}
} else if (mode == 1) {
newCol = guardianNeutralsLookup[String(r)].standardRGB;
if (newCol === undefined) {
newCol = originalCol;
//skippedColors.push(rgbToHex(r, g, b));
}
}
return newCol;
}
function convertTextColor(col) {
if (col.typename == 'RGBColor') {
r = col.red;
g = col.green;
b = col.blue;
// white text set to remain white - this should probably be made optional
if (isApproxMatch(r, g) && isApproxMatch(g, b)) { // looks like a neutral
var newColor = getConvertedColor(r, g, b, true) // invert value
col.red = newColor.r;
col.green = newColor.g;
col.blue = newColor.b;
}
}
return col;
}
function convertPathColor(col) {
if (col.typename == 'RGBColor') {
r = col.red;
g = col.green;
b = col.blue;
if (isApproxMatch(r, g) && isApproxMatch(g, b)) { // looks like a neutral
var newColor = getConvertedColor(r, g, b, false) // invert value
col.red = newColor.r;
col.green = newColor.g;
col.blue = newColor.b;
}
}
return col;
}
function isApproxMatch(n1, n2) {
if (Math.abs(n1-n2) <= neutralThreshold) {
return true;
} else {
return false;
}
}
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
function padZero(str, len) {
len = len || 2;
var zeros = new Array(len).join('0');
return (zeros + str).slice(-len);
}