• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Optimizing script for stray anchor point removal and setting objects to overprint

Community Beginner ,
Aug 22, 2023 Aug 22, 2023

Copy link to clipboard

Copied

Hi you scripting wizards,

 

I need some help. I have a script that's been great for preparing files for pre-press. I added a function to it recently that sets objects that use spot swatches with a "_" at the start of their name to overprint. I discovered that stray anchor points for some reason break this function.

 

Easy enough from the menu, Select > Object > Stray Points. Unfortunately from what I can tell, it's not so easy in script form. So it has to loop through every single path.

 

In smaller files, not a big deal and it works. Some of my files have CAD drawn objects though with include so many small paths. The looping takes forever on these. I only have a base understanding of what all the script is doing, so I could use help trying to optimize how this analyzes both the stray anchor points as well as looping through the objects that need to be set to overprint. If anyone has suggestions on how to clean this up, I'm all ears. Thanks!

 

function applyOverprint() {
updateStatus('Preparing Overprint'); w.update();

var allSwatches = doc.swatches;
var straypoints = [];

// Collect stray points
var allPageItems = doc.pageItems;
for (var j = 0; j < allPageItems.length; j++) {
var item = allPageItems[j];
if (item.typename === "PathItem" && item.pathPoints.length === 1) {
straypoints.push(item);
} else if (item.typename === "TextFrame" && item.textRange.length === 0) {
straypoints.push(item);
}
}

// Remove stray points
for (var l = 0; l < straypoints.length; l++) {
var strayItem = straypoints[l];
if (strayItem instanceof PathItem) {
strayItem.remove();
} else if (strayItem instanceof TextFrame) {
strayItem.remove();
}
}
 
function hasUnderscore(name) {
return name && name.charAt(0) === "_";
}

function applyOverprintToItems(items) {
updateStatus('Applying Overprint to Items'); w.update();
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (item.typename === "GroupItem" || item.typename === "CompoundPathItem") {
if (item.pageItems) {
// Recursively process groups and compound paths
applyOverprintToItems(item.pageItems);
}
} else {
// Process other items
if (item.typename === "TextFrame") {
applyOverprintToTextFrame(item);
} else {
applyOverprintToPathItem(item);
}
}
}
}

function applyOverprintToTextFrame(textFrame) {
updateStatus('Applying Overprint to Text'); w.update();
var textAttributes = textFrame.textRange.characterAttributes;
if (textAttributes.fillColor && textAttributes.fillColor.spot && hasUnderscore(textAttributes.fillColor.spot.name)) {
textAttributes.overprintFill = true;
}
if (textAttributes.strokeColor && textAttributes.strokeColor.spot && hasUnderscore(textAttributes.strokeColor.spot.name)) {
textAttributes.overprintStroke = true;
}
}

function applyOverprintToPathItem(pathItem) {
updateStatus('Applying Overprint to Paths'); w.update();
if (pathItem.filled && pathItem.fillColor && pathItem.fillColor.spot && hasUnderscore(pathItem.fillColor.spot.name)) {
pathItem.fillOverprint = true; // Apply fill overprint
}
if (pathItem.stroked && pathItem.strokeColor && pathItem.strokeColor.spot && hasUnderscore(pathItem.strokeColor.spot.name)) {
pathItem.strokeOverprint = true; // Apply stroke overprint
}
}

for (var s = 0; s < allSwatches.length; s++) {
var swatch = allSwatches[s];
if (swatch.spot && hasUnderscore(swatch.name)) {
swatch.color.overprint = true;
}
}
applyOverprintToItems(doc.pageItems);
}
TOPICS
Scripting

Views

293

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Adobe
Community Expert ,
Aug 22, 2023 Aug 22, 2023

Copy link to clipboard

Copied

Hi @padenh16167248, one way is using menu commands:

app.executeMenuCommand('Stray Points menu item');
app.executeMenuCommand('clear');

 - Mark

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Aug 22, 2023 Aug 22, 2023

Copy link to clipboard

Copied

I feel like an idiot. I didn't think that was a menu item that could be called from script. Thank you.

 

Any suggestions on improving the loops for the overprint of path and text items?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 22, 2023 Aug 22, 2023

Copy link to clipboard

Copied

Hi @padenh16167248, I haven't looked deeply into your code, but one thing you could try, is to log the uuid of each item you are processing to the console. Then after running the script, copy it to the clipboard, paste into your code editor and sort, and see if there are duplicates—if there are duplicates, then there are likely to be a lot of them in a large file. In that case, carefully check your recursive function.

- Mark

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 22, 2023 Aug 22, 2023

Copy link to clipboard

Copied

Just for an experiment, I've hacked together a script that sets the overprint on the selection (or you can change it to all page items in document). The script has some slightly strange structure because I adapted it on the fly from a script I wrote for a different purpose, but it might do the trick. Can you tell me if it runs faster than what you are doing? There are some obvious ways it could be sped up, such as rather than gathering the overprintables, just set them immediately. - Mark

 

/**
 * Turn on overprint for selected items.
 * @discussion https://community.adobe.com/t5/illustrator-discussions/optimizing-script-for-stray-anchor-point-removal-and-setting-objects-to-overprint/m-p/14029154
 */
(function () {

    var doc = app.activeDocument;

    // selection? OR all page items in document?
    var items = itemsInsideGroupItems(doc.selection);
    // var items = itemsInsideGroupItems(doc.pageItems);

    for (var i = 0; i < items.length; i++)
        setOverprintOfItem(items[i]);


})();


function setOverprint(item, value) {

    // most basic page items
    if (item.hasOwnProperty('fillOverprint')) {
        item.fillOverprint = value;
        // if (item.hasOwnProperty('strokeOverprint'))
        item.strokeOverprint = value;
    }

    // text style ranges
    else if (item.hasOwnProperty('overprintFill')) {
        item.overprintFill = value;
        // if (item.hasOwnProperty('overprintStroke'))
        item.overprintStroke = value;
    }

};

/**
 * Returns an array containing items,
 * including items found inside GroupItems.
 * Optionally can supply a filter function.
 * @author m1b
 * @Param {Array<PageItem>} items - the starting items, may include group items.
 * @Param {Function} [filter] - whether to collect item (default: collect all).
 * @Returns {Array<PageItem>}
 */
function itemsInsideGroupItems(items, filter) {

    var found = [];

    for (var i = 0; i < items.length; i++) {

        var item = items[i];

        if (item.typename == 'GroupItem')
            found = found.concat(itemsInsideGroupItems(item.pageItems, filter));

        else if (
            filter == undefined
            || filter(item)
        )
            found.push(item);

    }

    return found;

};




/**
 * Returns array of overprintable items.
 * @author m1b
 * @version 2022-10-11
 * @Param {PageItem} item - an Illustrator page item.
 * @Returns {Object} -  {fillColors: Array<Color>, strokeColors: Array<Color>}
 */
function setOverprintOfItem(item) {

    if (item == undefined)
        throw Error('getBasicColorablesFromItem: No `item` supplied.');

    // collect all the overprintables
    if (item.constructor.name == 'PathItem') {
        setOverprint(item, true);
    }

    else if (
        item.constructor.name == 'CompoundPathItem'
        && item.pathItems
    ) {
        setOverprint(item.pathItems[0], true);
    }

    else if (
        item.constructor.name == 'TextFrame'
        && item.textRanges
    ) {
        var ranges = getTextStyleRanges(item.textRange, ['overprintFill', 'overprintStroke']);
        for (var i = ranges.length - 1; i >= 0; i--)
            setOverprint(ranges[i], true);
    }

    else if (item.constructor.name == 'GroupItem') {
        for (var i = 0; i < item.pageItems.length; i++)
            setOverprint(item.pageItems[i], true);

    }

};


/**
 * Returns an array of TextRanges,
 * determined by mapping changes
 * in supplied keys. For example,
 * supplying the 'fillColor' key
 * will return ranges divided when
 * the text's fillColor changes.
 * @author m1b
 * @version 2023-04-26
 * @Param {TextRange} textRange - an Illustrator TextRange.
 * @Param {Array<String>} keys - an array of keys to match, eg ['fillColor'].
 */
function getTextStyleRanges(textRange, keys) {

    if (
        textRange == undefined
        || textRange.constructor.name != 'TextRange'
    )
        throw Error('getTextStyleRanges: bad `textRange` supplied.');

    if (
        keys == undefined
        || keys.constructor.name != 'Array'
    )
        throw Error('getTextStyleRanges: bad `textRange` supplied.');

    // check keys are valid
    for (var j = 0; j < keys.length; j++)
        if (!textRange.characterAttributes.hasOwnProperty(keys[j]))
            throw Error('getTextStyleRanges: bad key supplied ("' + keys[j] + '")');

    var ranges = [],
        start = 0,
        currentValues = {};

    charactersLoop:
    for (var i = 0; i < textRange.length; i++) {

        var tr = textRange.textRanges[i],
            matches = true;

        // check each key
        keysLoop:
        for (var j = 0; j < keys.length; j++) {

            if (i == 0)
                currentValues[keys[j]] = tr.characterAttributes[keys[j]];

            else if (stringify(tr.characterAttributes[keys[j]]) !== stringify(currentValues[keys[j]])) {
                matches = false;
                break keysLoop;
            }
        }

        currentValues[keys[j]] = tr.characterAttributes[keys[j]];

        if (
            i == textRange.length - 1
            || !matches
        ) {
            // start a new range
            var newTextRange = textRange.textRanges[start];
            newTextRange.end = i == textRange.length - 1 ? i + 1 : i;
            ranges.push(newTextRange);
            start = i;
        }

    }

    return ranges;

};


/**
 * Stringify tailored for the purpose of
 * identifying overprintable text ranges.
 * @author m1b
 * @version 2023-04-26
 * @Param {Object} obj - the object to stringify.
 * @Returns {String}
 */
function stringify(obj) {

    var str = obj.toString();

    for (var key in obj) {

        if (!obj.hasOwnProperty(key))
            continue;

        if (
            key == 'overprintFill'
            || key == 'overprintStroke'
        )
            str += stringify(obj[key]);

        else
            str += obj[key];

    }

    return str;

};

 

Edit 2023-08-23: removed the worst over-complications left from the original script. Still not perfect. Takes about 12 seconds on my computer to process around 4000 page items, including 20% text frames with multiple fill/strokes.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Beginner ,
Aug 23, 2023 Aug 23, 2023

Copy link to clipboard

Copied

m1b, thanks for looking into this. I tried out your script, and it looks like this only works when everything is selected and everything is set to overprint fill or stroke. What I'm working on is parsing out items that are filled with a spot swatch that has an _ at the start of the swatch name, then setting only those items to overprint. That way when I have a file with a dieline or dimensions, I can have those elements use a _Dieline or _Dims spot swatch. That way they will automatically get set to overprint by the script, thus not cutting into the separations of the artwork.

 

Unfortunately, I don't think there's much of any way around having the script loop through every single page item, I just don't know if the script as I have it is the most efficient way to do that scan through of the page items or if the group and compound shape recursive search is causing extra looping.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Aug 23, 2023 Aug 23, 2023

Copy link to clipboard

Copied

LATEST

making global color changes like this is hard, for sure. because tons of different pageItems have different ways to access their colors. pathItems have a fillColor and strokeColor property.. but compoundPathItems do not (you have to access the first pathItem of the compound pathItem, which can cause errors because its possible to create a compoundPath out of groupItems. then you cant access the color without rebuilding the compoundPathItem).

then you can have linkedItems or symbolItems where you cant get the colors out of them without breaking the link/embedding or breaking the symbol. you just cant access the art inside of these items unless you give up some of their handy features.

 

then theres the issue where items can have multiple fills/strokes in the appearance panel. theres no way to access an items appearance, so all you can do is access the fillColor and strokeColor properies, which will not show you or let you edit the other fills/strokes.

 

then theres gradients where you have to access colors via PathItem.fillColor.gradient.gradientStops[index].color

 

Im not aware of any way to do this via script without looping every page item. And even then, some items cant be reliably updated with this method. =(

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines