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

Find And Change in an Action

Explorer ,
Apr 15, 2025 Apr 15, 2025

I want to make an action for the Find and Change option, but that doesn't work.

For example I want an action for: find 'Space' change to 'Nothing'

Is there another way to automate this?

TOPICS
Feature request , How-to , Scripting
114
Translate
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 ,
Apr 15, 2025 Apr 15, 2025

Hi @Adriaan_Metz Another way to automate this would be to use a script. I have previously written a script that performs a find/change grep (a bit like in Indesign) which you could use for this task. Here it is below, and I've configured the settings object to search for a space and replace with nothing (empty string) with no UI showing. You can run this script from an action and assign a keyboard shortcut, or choose it from the scripts menu as usual for scripts. Let me know if it helps.

- Mark

/**
 * @file Find Change RegExp.js
 *
 * Perform a find/change operation on textRanges of target object(s).
 *
 * Notes
 *   - `findWhat` is a RegExp and is always global.
 *
 * @author m1b
 * @version 2025-02-09
 * @discussion https://community.adobe.com/t5/illustrator-discussions/illustrator-jsx-to-delete-lt-b-gt-lt-b-gt-tags-in-paragraph-text-an-keep-the-differents-font-style/m-p/14269543
 */
(function () {

    if (0 === app.documents.length)
        return alert('Please open a document and try again.');

    var doc = app.activeDocument;

    var settings = {

        // what to search (can be a document, a story, a text frame etc.)
        target: doc.selection,

        // find using RegExp
        findWhat: / /,

        // replacement string
        changeTo: '',

        // whether to search lock and/or hidden texts
        doNotSearchLockedOrHidden: true,

        // whether to show a UI
        showUI: false,

        // whether to show results after script is finished
        showResult: true,

    };

    // show UI
    if (
        settings.showUI
        && 2 === ui(settings)
    )
        // user cancelled
        return;

    if (
        undefined != settings.findWhat
        && 'String' === settings.findWhat.constructor.name
    )
        // convert to RegExp
        settings.findWhat = new RegExp(settings.findWhat, 'g');

    if (
        undefined == settings.findWhat
        || settings.findWhat.constructor.name !== 'RegExp'
    )
        return alert('Sorry, "' + (settings.findWhat || 'findWhat') + '" was an invalid RegExp.');

    // findWhat regex must be global!
    if (!settings.findWhat.global)
        settings.findWhat = new RegExp(settings.findWhat.source, 'g'
            + (settings.findWhat.ignoreCase ? 'i' : '')
            + (settings.findWhat.multiline ? 'm' : ''));

    var textRanges = getTexts(settings.target, settings.doNotSearchLockedOrHidden),
        counter = 0;

    for (var i = textRanges.length - 1; i >= 0; i--) {

        if (
            settings.doNotSearchLockedOrHidden
            && isLockedOrHidden(textRanges[i].story.textFrames[0])
        )
            // ignore this text
            continue;

        // do the find/change
        counter += findChangeRegExp(textRanges[i], settings.findWhat, settings.changeTo);

    }

    if (settings.showResult) {
        app.redraw();
        alert('Performed ' + counter + ' find/change operations.');
    }

})();

/**
 * Returns array of TextRanges, given `target`.
 * @author m1b
 * @version 2025-02-04
 * @param {TextRange|TextFrame|Document|GroupItem} target - the item to search for texts.
 * @returns {Array<TextRange>}
 */
function getTexts(target) {

    var texts = [];

    if (target == undefined)
        throw Error('findChange: bad `text` supplied. Expected [TextRange] or [TextFrame]');

    if ('Document' === target.constructor.name)
        target = asArray(target.stories);

    if (
        (
            // to bypass bug in TextFrames object:
            undefined != target.typename
            && 'TextFrames' === target.typename
        )
        || 'Stories' === target.constructor.name
    )
        target = asArray(target);

    if ('Array' === target.constructor.name) {
        // handle array of text frames
        for (var i = target.length - 1; i >= 0; i--)
            texts = texts.concat(getTexts(target[i]));
    }

    else if (
        'GroupItem' === target.constructor.name
        || 'Layer' === target.constructor.name
    ) {

        // handle the group's items
        for (var i = target.pageItems.length - 1; i >= 0; i--)
            texts = texts.concat(getTexts(target.pageItems[i]));

    }

    if (
        'Story' === target.constructor.name
        || 'TextFrame' === target.constructor.name
    )
        target = target.textRange;

    if ('TextRange' === target.constructor.name)
        texts.push(target)

    return texts;

};

/**
 * Performs a Find/Change operation on `text` and
 * returns a count of how many changes were made.
 * @author m1b
 * @version 2023-12-01
 * @param {TextRange} text - the text to search.
 * @param {RegExp} findWhat - the RegExp to find with.
 * @param {String} [changeTo] - the change-to string.
 * @param {Boolean} [adjustSelection] - whether to adjust the selection (default: true when `text` has textSelection).
 * @returns {Number}
 */
function findChangeRegExp(text, findWhat, changeTo, adjustSelection) {

    var counter = 0;

    if (
        undefined == findWhat
        || 'RegExp' !== findWhat.constructor.name
    )
        throw TypeError('findChange: Bad `findWhat` supplied.');

    // reset findWhat index
    findWhat.lastIndex = 0;

    if (changeTo == undefined)
        changeTo = '';

    var contents = text.contents,
        selectLength = 0,
        selectStart,
        selectEnd,
        match,
        found = [];

    if (
        text.hasOwnProperty('textSelection')
        && text.textSelection.length > 0
    ) {
        // record the selection for later
        selectStart = text.textSelection[0].start;
        selectEnd = text.textSelection[0].end;
        selectLength = selectEnd - selectStart;
    }

    while (match = findWhat.exec(contents))
        found.push({
            start: match.index,
            end: findWhat.lastIndex,
            str: contents.slice(match.index, findWhat.lastIndex),
        });

    counter = found.length;

    // process each found, going backwards
    var s, e;

    while (match = found.pop()) {

        // remove the found text, except first character
        for (e = match.end - 1, s = match.start; s < e; e--) {
            text.characters[e].remove();
            selectEnd--;
        }

        var replacement = match.str.replace(findWhat, changeTo);

        if (replacement) {
            // set the contents of the first character
            text.characters[e].contents = replacement;
            selectEnd += replacement.length - match.str.length;
        }
        else {
            // no contents to replace with
            text.characters[e].remove();
            selectEnd--;
        }

    }

    if (
        !adjustSelection
        && selectLength > 0
    ) {
        // adjust the selection
        app.selection = [];
        for (var s = selectStart; s < selectEnd; s++)
            text.story.characters[s].select(true);
    }

    return counter;

};

/**
 * Returns a collection as an Array.
 * @param {Collection} objs - the collection object.
 * @returns {Array<*>}
 */
function asArray(objs) {

    var arr = [];

    try {

        for (var i = 0; i < objs.length; i++)
            arr.push(objs[i]);

    } catch (error) { }

    return arr;

};

/**
 * Returns true if item
 * is locked or hidden.
 * @param {PageItem} item - an Illustrator PageItem.
 * @returns {Boolean}
 */
function isLockedOrHidden(item) {

    return (
        (
            item.locked != undefined
            && item.locked == true
        )
        || (
            item.hidden != undefined
            && item.hidden == true
        )
        || (
            item.layer != undefined
            && (
                item.layer.locked == true
                || item.layer.visible == false
            )
        )
    );
};

/**
 * Shows UI suitable for Find/Change
 * @param {Object} settings
 * @returns {1|2} - ScriptUI result code.
 */
function ui(settings) {

    var targets = [];

    if (app.activeDocument.selection.length > 0)
        targets.push(
            { label: 'Selection', get: function () { return app.activeDocument.selection } }
        );

    targets.push(
        { label: 'Document', get: function () { return app.activeDocument } },
    );

    var labelWidth = 100,
        columnWidth = 200;

    var w = new Window("dialog { text:'Find/Change RegExp' }"),

        fields = w.add("Group { orientation: 'column', alignment: ['fill','fill'] }"),
        findWhatGroup = fields.add("Group { orientation: 'row', alignment: ['fill','fill'] }"),
        findWhatLabel = findWhatGroup.add("StaticText { text: 'Find what:', justify: 'right' }"),
        findWhatField = findWhatGroup.add("EditText { text:'', alignment:['fill','top'] }"),
        changeToGroup = fields.add("Group { orientation: 'row', alignment: ['fill','fill'] }"),
        changeToLabel = changeToGroup.add("StaticText { text: 'Change to:', justify: 'right' }"),
        changeToField = changeToGroup.add("EditText { text:'', alignment:['fill','top'] }"),

        targetMenuGroup = w.add("Group { orientation: 'row', alignment: ['fill','fill'] }"),
        targetLabel = targetMenuGroup.add("StaticText { text: 'Target', justify: 'right' }"),
        targetMenu = targetMenuGroup.add("Dropdownlist {alignment:['right','center'] }"),

        checkBoxes = w.add("group {orientation:'row', alignment:['fill','bottom'], alignChildren:'left', margins:[5,0,5,0] }"),
        ignoreCaseCheckBox = checkBoxes.add("Checkbox { text:'Ignore case', alignment:'left', value:false }"),
        doNotSearchLockedOrHiddenCheckBox = checkBoxes.add("Checkbox { text:'Ignore locked/hidden text', alignment:'left', value:false }"),

        bottomUI = w.add("group {orientation:'row', alignment:['fill','fill'], margins:[0,20,0,0] }"),
        buttons = bottomUI.add("group {orientation:'row', alignment:['right','center'], alignChildren:'right' }"),
        cancelButton = buttons.add("Button { text: 'Cancel', properties: {name:'cancel'} }"),
        okayButton = buttons.add("Button { text:'Change', enabled: true, properties: {name:'ok'} }");

    targetLabel.preferredSize.width = labelWidth;
    targetMenu.preferredSize.width = columnWidth;

    findWhatLabel.preferredSize.width = labelWidth;
    findWhatField.preferredSize.width = columnWidth;
    findWhatField.text = settings.findWhat.source;

    changeToLabel.preferredSize.width = labelWidth;
    changeToField.preferredSize.width = columnWidth;
    changeToField.text = settings.changeTo;

    ignoreCaseCheckBox.value = settings.findWhat.ignoreCase;
    doNotSearchLockedOrHiddenCheckBox.value = settings.doNotSearchLockedOrHidden;

    var targetTypes = [];
    for (var i = 0; i < targets.length; i++)
        targetTypes.push(targets[i].label);

    buildMenu(targetMenu, targetTypes, settings.targetType);

    okayButton.onClick = function () { return update() && w.close(1) };
    cancelButton.onClick = function () { w.close(2) };

    w.center();
    return w.show();

    /**
     * Builds dropdown menu items.
     */
    function buildMenu(menu, arr, index) {
        for (var i = 0; i < arr.length; i++)
            menu.add('item', arr[i]);
        menu.selection = [index || 0];
    };

    /**
     * Updates settings.
     * @returns {Boolean} - valid RegExp?
     */
    function update() {

        settings.target = targets[targetMenu.selection.index].get();
        settings.changeTo = changeToField.text;
        settings.findText = findWhatField.text;
        settings.ignoreCase = !!ignoreCaseCheckBox.value;
        settings.doNotSearchLockedOrHidden = !!doNotSearchLockedOrHiddenCheckBox.value;

        try {
            settings.findWhat = RegExp(findWhatField.text, 'g' + (ignoreCaseCheckBox.value ? 'i' : ''));
        } catch (error) {
            settings.findWhat = undefined;
        }

        return settings.findWhat !== undefined;

    };

};

 

Translate
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
Explorer ,
Apr 16, 2025 Apr 16, 2025

Thank you for your solution, but scripting is not my cup off tea...

Translate
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 ,
Apr 17, 2025 Apr 17, 2025

Okay, all good, but just checking ... are you saying that (a) you're not interested in a solution that involves a script, or are you saying that (b) you need help using a script because you are inexperienced?

Translate
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
Explorer ,
Apr 17, 2025 Apr 17, 2025
LATEST

No, I want to learn, but I didn't do anything with scripting sofar. So if you can help or if you know a tutorial where it will be explaned easily for me, it's welcom

Translate
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