javascriptadobe-illustrator

A script that runs a command based on selected artwork colors


I tried to make a script that will do an action a couple of times (loop) based on the number of colors that the selected art have. And if there is a way to store the color names in an array it would be great too.

I can't figure it out on my own and I only have the basics of the script:

var doc = app.activeDocument;
var sel = doc.selection[0];
var selcolors = 0;

//====================
//Here I need to get how many colors the selected art have and set it to selcolors
//====================

for(i = 0; i < selcolors; i++)
{
   //Set of actions
}

Any help will be appreciated!

Example of my graphics: enter image description here


Solution

  • It depends on what exactly you're trying to do with the colors and what kind of colors you're using.

    For example for spot colors it can be something like this:

    var doc = app.activeDocument;
    var sel = doc.selection;
    
    // get all path items from the selection
    var pathItems = [];
    for (var i=0; i<sel.length; i++) get_all_pathItems(sel[i], pathItems);
    
    // put all the colors into the object
    var obj = {};
    for (var i=0; i<pathItems.length; i++) {
        var item = pathItems[i];
        if (item.filled) {
            var color = item.fillColor;
            if (color instanceof SpotColor) obj[color.spot.name] = color;
        }
        if (item.stroked) {
            var color = item.strokeColor;
            if (color instanceof SpotColor) obj[color.spot.name] = color;
        }
    }
    
    // get a number of the colors from the object 
    var n = 0;
    for (key in obj) n++;
    
    // updated lines below:
    
    // run some actions, perhaps you don't even need the number
    for (key in obj) {
        if (key == 'White') continue; // skip White color
        // action 1
        // action 2
        // etc...
    }
    
    // ----------------------------------------------------------
    
    // recursive function to fill the given array with all pathItems of given item
    function get_all_pathItems(item, arr) {
        try { if (item instanceof PathItem) arr.push(item) } catch(e) {}
        try {
            if (item instanceof GroupItem) {
                var paths = item.pathItems;
                for (var i=0; i<paths.length; i++) arr.push(paths[i]);
                var groups = item.groupItems;
                for (var i=0; i<groups.length; i++) get_all_pathItems(groups[i], arr);
            }
        } catch(e) {}
    }
    

    And there is one problem. In Illustrator a group of items can have its own color (fill and stroke). As far as I know you can't get the color of the group with a script. A script can get only colors of a path items, not groups.

    Update

    Here is the the updated code that selects objects with the same color of fill or stroke and do actions in the loop:

    var doc = app.activeDocument;
    var sel = doc.selection;
    
    // get all path items from the selection
    var pathItems = [];
    for (var i=0; i<sel.length; i++) get_all_pathItems(sel[i], pathItems);
    
    // put all the colors into the object
    var obj = {};
    for (var i=0; i<pathItems.length; i++) {
        var item = pathItems[i];
        if (item.filled) {
            var color = item.fillColor;
            if (color instanceof SpotColor) obj[color.spot.name] = color;
        }
        if (item.stroked) {
            var color = item.strokeColor;
            if (color instanceof SpotColor) obj[color.spot.name] = color;
        }
    }
    
    delete obj['White'] // remove White from the object
    
    // add empty arrays to every color in the object
    for (color in obj) obj[color].items = [];
    
    // fill the arrays of the object with items of the same fill or stroke color
    for (var i=0; i<pathItems.length; i++) {
        var item = pathItems[i];
        if (item.filled) {
            var color = item.fillColor;
            if (color instanceof SpotColor) {
                if (color.spot.name in obj) obj[color.spot.name].items.push(item);
            }
        }
        if (item.stroked) {
            var color = item.strokeColor;
            if (color instanceof SpotColor) {
                if (color.spot.name in obj) obj[color.spot.name].items.push(item);
            }
        }
    }
    
    for (color in obj) {
        app.selection = obj[color].items; // select all object from the color's array
        // do action with the selected objects
    }
    
    
    // ----------------------------------------------------------
    
    // recursive function to fill the given array with all pathItems of given item
    function get_all_pathItems(item, arr) {
        try { if (item instanceof PathItem) arr.push(item) } catch(e) {}
        try {
            if (item instanceof GroupItem) {
                var paths = item.pathItems;
                for (var i=0; i<paths.length; i++) arr.push(paths[i]);
                var groups = item.groupItems;
                for (var i=0; i<groups.length; i++) get_all_pathItems(groups[i], arr);
            }
        } catch(e) {}
    }
    

    Just in case. If you need to get the "collective" size (width and height) of the items with the same color you can do something like this:

    // loop through the colors and get sizes of items with the same colors
    var report = [];
    
    for (color in obj) {
        var items = obj[color].items
        var size = get_size(items);
    
        report.push([
            'Color: '           + color,
            'Number of items: ' + items.length,
            'Width: '           + size.width,
            'Height: '          + size.height,
        ].join('\n'));
    }
    
    alert(report.join('\n\n')); // show the report
    
    // ------------------------------------------------------------------
    // function to get the collective size {width, height} of given items
    function get_size(array) {
        var tops = [], lefts = [], rights = [], bottoms = [];
    
        for (var i=0; i<array.length; i++) {
            if (array[i].guides) continue; // skip guides just in case
               tops.push(array[i].visibleBounds[1]);
              lefts.push(array[i].visibleBounds[0]);
             rights.push(array[i].visibleBounds[2]);
            bottoms.push(array[i].visibleBounds[3]);
        };
    
        var top    = Math.max.apply(null, tops);
        var left   = Math.min.apply(null, lefts);
        var right  = Math.max.apply(null, rights);
        var bottom = Math.min.apply(null, bottoms);
    
        return {width:right-left, height:bottom-top};
    }
    

    It will show you the sizes (the alert window) for every spot color of selected artwork. Of course you can save the report into a text file as well.


    Update 2

    Here is the full implementation of the code which draws marks for every spot color of selected items. The code considered masks and text objects. The marks are aligned by the center of top selected object.

    if (app.activeDocument.selection.length != 0) main();
    
    function main() {
    
        // disable warnings
        var uInterration = app.userInteractionLevel;
        app.userInteractionLevel = UserInteractionLevel.DONTDISPLAYALERTS;
    
        // save selection and deselect all
        var orig_selection = app.selection;
        app.selection = null;
    
        // duplicate the objects that were selected and select the duplicates
        var duplicates = [];
        for (var i=0; i<orig_selection.length; i++) duplicates.push(orig_selection[i].duplicate());
        app.selection = duplicates;
    
        // UPDATED get the horizontal and vertical axes for the marks from the top object
        var top_object = app.selection[0];
        if (top_object.clipped || top_object instanceof GroupItem) {
            var top_inner_object = top_object.pageItems[0];
            if (top_inner_object.clipped) top_object = top_inner_object.pageItems[0];
        }
        var vertical_axis   = top_object.position[0] + top_object.width/2;
        var horizontal_axis = top_object.position[1] - top_object.height/2;
    
        // expand the selected objects
        // ungroup all groups, flat all masks, outline texts, release compound paths)
        expand_selected_items();
    
        // create the object with colors and pathItems
        var obj = make_color_object(app.activeDocument.selection);
    
        // loop through the object and make the marks for every color
        for (color in obj) {
            var size = get_size(obj[color].items); // get sizes
            draw_marks(size, horizontal_axis, vertical_axis, obj[color]);
        }
    
        // remove the temp objects and restore the original selection
        for (var i=app.selection.length-1; i>=0; i--) app.selection[i].remove();
        app.selection = orig_selection;
    
        // restore warnings
        app.userInteractionLevel = uInterration;
    
    }
    
    // ----------------------------------------------------------------------------
    
    // function returns the 'collective' size and edges of given array of items
    // {left, top, right, bottom, width, height}
    function get_size(items) {
        var tops = [], lefts = [], rights = [], bottoms = [];
    
        for (var i=0; i<items.length; i++) {
            if (items[i].guides) continue;
               tops.push(items[i].visibleBounds[1]);
              lefts.push(items[i].visibleBounds[0]);
             rights.push(items[i].visibleBounds[2]);
            bottoms.push(items[i].visibleBounds[3]);
        };
    
        var top    = Math.max.apply(null, tops);
        var left   = Math.min.apply(null, lefts);
        var right  = Math.max.apply(null, rights);
        var bottom = Math.min.apply(null, bottoms);
    
        return {left:left, top:top, right:right, bottom:bottom, width:right-left, height:top-bottom};
    }
    
    // function 'expands' all selected objects
    // (releases masks and compound paths, ungroups groups, outlines strokes and texts)
    function expand_selected_items() {
        app.executeMenuCommand('ungroup');
        app.executeMenuCommand('ungroup');
        app.executeMenuCommand('outline');                // text to curves
        app.executeMenuCommand('ungroup');
        app.executeMenuCommand('OffsetPath v22');         // expand stroke
        app.executeMenuCommand('Live Pathfinder Divide'); // expand masks (Live)
        app.executeMenuCommand('expandStyle');            // Live --> plain
        app.executeMenuCommand('noCompoundPath');         // release compound paths
        app.executeMenuCommand('ungroup');
    }
    
    // function makes the color object from given array of pathItems:
    // {color1:{color, items:[pathItems]}, color2:{color, items:[pathItems]}, ...}
    function make_color_object(items) {
        var obj = {};
    
        var add_color_into_obj = function(color, obj) {
            if (color instanceof SpotColor)
                obj[color.spot.name] = color;
        }
    
        var put_item_into_color = function(color, obj, item) {
            if (color instanceof SpotColor && color.spot.name in obj)
                obj[color.spot.name].items.push(item);
        }
    
        // add colors into the object
        for (var i=0; i<items.length; i++) {
            if (items[i].filled) add_color_into_obj(items[i].fillColor, obj);
            // if (items[i].stroked) add_color_into_obj(items[i].strokeColor, obj); // there is no stroked objects anyway
        }
    
        delete obj['White']; // remove White color from the object
    
        // add empty arrays to every color in the object
        for (color in obj) obj[color].items = [];
    
        // fill the arrays of the object with items of the same fill or stroke color
        for (var i=0; i<items.length; i++) {
            if (items[i].filled) put_item_into_color(items[i].fillColor, obj, items[i]);
            // if (items[i].stroked) put_item_into_color(items[i].strokeColor, obj, items[i]); // there is no stroked objects anyway
        }
    
        return obj;
    }
    
    // function draws the four marks around the given area
    // the marks will be aligned by the given horizontal and vertical axes
    function draw_marks(area, horiz, vert, color) {
        var mm = 2.83464566929134; // mm/pt
        var space = 5 * mm;
        var len = 3 * mm;
        var strokeWidth = 1; // pt
        var x1,x2,y1,y2;
    
        // select or make the group
        var group_name = 'Marks';
        try {
            var group = app.activeDocument.groupItems.getByName(group_name) }
        catch(e) {
            var group = app.activeDocument.groupItems.add();
            group.name = group_name;
        }
    
        // subfunction to draw the mark with given coords
        var draw_mark = function(x1,y1,x2,y2,label) {
            var mark = app.activeDocument.pathItems.add();
                mark.setEntirePath([[x1,y1],[x2,y2]]);
                mark.filled = false;
                mark.stroked = true;
                mark.strokeWidth = strokeWidth;
                mark.strokeOverprint = true;
                mark.strokeColor = color;
    
                mark.name = color.spot.name + ' - ' + label;
            return mark;
        }
    
        y1 = y2 = horiz;
        x1 = area.left - len - space;
        x2 = x1 + len;
        draw_mark(x1,y1,x2,y2,'left').moveToEnd(group);
    
        x1 = area.right + space;
        x2 = x1 + len;
        draw_mark(x1,y1,x2,y2,'right').moveToEnd(group);
    
        x1 = x2 = vert;
        y1 = area.top + space + len;
        y2 = y1 - len;
        draw_mark(x1,y1,x2,y2,'top').moveToEnd(group);
    
        y1 = area.bottom - space;
        y2 = y1 - len;
        draw_mark(x1,y1,x2,y2,'bottom').moveToEnd(group);
    }
    

    Result (the dashed lines were added manually to show the logic behind the mark positions):

    enter image description here