Search and replace a text, only in visible layers

Community Beginner ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

Hello,

 

Please I need a script to search for a textA in visible layers only and replace it by textB. Could you help.

 

Thanks in advance.

TOPICS
How to , Scripting

Views

288

Likes

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

correct answers 1 Correct answer

Enthusiast , Mar 12, 2022 Mar 12, 2022
Here's my attempt. Recursive lookup of ancestors and sublayers, supports any amount of find/replace options, also has options for enabling whether to affect locked or hidden layers: var config = { // Support for any amount of find/replace parameters here targets: [ { find: "foo", replace: "bar", }, { find: /(locked|hidden)/i, // including support for Regex patterns replace: "Success", }, ], affects: { // names: false, // Whether it changes name...

Likes

Translate

Translate
Engaged ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

Should the text in a written text (TextFrame) be searched and replaced or the names of objects in the current layer?

Both would be programmable without much effort.

- j.

Likes

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 ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

Hello,

 

I want to change only the content of the textframe

 

For example, replace text "Sky" with "mountain"..without any other names change (if not necessary).

Likes

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
Engaged ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

I love programming new functions or possibilities. However, Illustrator has a pretty good system for searching and replacing text.

( A ) In my example there are two texts in two different layers.
( B ) The red text is on layer 2 and this layer is locked.
( C ) With "Edit > Find and Replace" you can call up a text search.
( D ) If I search for "xy" here in the example ...
( E ) and the "Check Locked Layers" is not checked and ...
( F ) start the search,
( G ) then AI finds nothing that is correct.

So you can exclude certain layers from the search via the properties of the layers, whether hidden or locked.

Using Javascript to search only in the active layer would be practical because it would be faster, but programming the same capabilities as in "Find and Replace" would be quite time-consuming.

What bothers you about the normal "Find and Replace"?

 

– j.

 

screen1.png

 

Likes

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 ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

Thank you for your reply,

 

I already use the "Find and Replace" option. But it would require a LOT of handling with the quantity of words I need to replace across the file. 

 

That's why I need to automate that task so that I work on the script  and add as many words as I need. I have tried to use the below script (by locking the invisible Layers), but it didn't  work:

 

layers = app.activeDocument.layers;
var search_string = /textA/gi;
var replace_string = "textB";
var myDoc=app.activeDocument;
var text_frames = myDoc.textFrames;

var layerCount=myDoc.layers.length;
for (var ii = layerCount - 1; ii >= 0; ii--) {
var currentLayer = myDoc.layers[ii];

if (currentLayer.visible == false){

currentLayer.locked = true;
}
}

 

 

if (text_frames.length > 0)
{
for (var i = 0 ; i < text_frames.length; i++)
{
var this_text_frame = text_frames[i];
var new_string = this_text_frame.contents.replace(search_string, replace_string);

if (new_string != this_text_frame.contents)
{

this_text_frame.contents = new_string;

}
}
}

for (var ii = layerCount - 1; ii >= 0; ii--) {
var currentLayer = myDoc.layers[ii];

if (currentLayer.visible == false){

currentLayer.locked = false;
}
}

 

Likes

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
Engaged ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

try this one:

 

 

// search and replace
var myDoc = app.activeDocument;
var mySelection = myDoc.selection;
var myLayers = myDoc.layers;
var layerCount= myDoc.layers.length;

var search_string = /textA/gi;
var replace_string = "textB";

for (var k = 0; k < layerCount; k++) {
	if ((myLayers[k].locked == false) && (myLayers[k].visible == true)) {
		var text_frames = myLayers[k].textFrames;
		if (text_frames.length > 0) {
			for (var i = 0 ; i < text_frames.length; i++) {
				var this_text_frame = text_frames[i];
				var new_string = this_text_frame.contents.replace(search_string, replace_string);
				if (new_string != this_text_frame.contents) { this_text_frame.contents = new_string; }
			}
		}
	}
}

 

This will replace "textA" to "textB" but also "textAB" to "textBB".

Likes

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 ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

Thank you again for your help, 

 

It did not work for my file, I constantly got the same results:

-For one visible layer, it replaced all "textA" except one.

-For the rest of visible layers, it did not replace them.

 

But, I tried your script on a simple file (4 layers, 5 "textA"), and it worked 100% as intended.

 

I don't know what I am missing.

Likes

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
Engaged ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied


@Amine23526370y7t4  schrieb:

It did not work for my file, I constantly got the same results:

-For one visible layer, it replaced all "textA" except one.

-For the rest of visible layers, it did not replace them.


 

can you send my (a link to) your ai-file=? I like to check it or correct my script.

 

I tried my script with 2 layers.

Likes

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 ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

I am sorry I can't send it because of confidentiality restriction. It pertains to a customer.

 

I think I found the issue: The script does not apply to sublayers containing "textA".

 

Likes

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
Engaged ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

ok. sublayers. 🙂 I guessed so.

i can expand the script accordingly, but not until tomorrow. it is now 10:50 pm here in germany and time to sleep.

till tomorrow,

– j.

Likes

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 ,
Mar 11, 2022 Mar 11, 2022

Copy link to clipboard

Copied

Have a good one, I can't thank you enough.

Likes

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
Enthusiast ,
Mar 12, 2022 Mar 12, 2022

Copy link to clipboard

Copied

Here's my attempt. Recursive lookup of ancestors and sublayers, supports any amount of find/replace options, also has options for enabling whether to affect locked or hidden layers:

 

var config = {
  // Support for any amount of find/replace parameters here
  targets: [
    {
      find: "foo",
      replace: "bar",
    },
    {
      find: /(locked|hidden)/i, // including support for Regex patterns
      replace: "Success",
    },
  ],
  affects: {
    // names: false, // Whether it changes names, not implemented but possible
    content: true, // Must be true at this basic state of the script
  },
  searchIn: {
    hidden: false, // Whether it changes hidden layer contents
    locked: false, // Whether it changes locked layer contents
  },
};
//
// Don't alter the below unless you know what you're doing.
//
// Very simple ES6 polyfills for Array methods to make code more concise:
Array.prototype.filter = function (callback) {
  var filtered = [];
  for (var i = 0; i < this.length; i++)
    if (callback(this[i], i, this)) filtered.push(this[i]);
  return filtered;
};
Array.prototype.forEach = function (callback) {
  for (var i = 0; i < this.length; i++) callback(this[i], i, this);
};
// This is a bit too verbose for what we need here, but as-is can recurse to find sublayers no matter the depth if we use:
// var list = get("layers"); <-- This is *all* layers and sublayers, not just the root-level / depth 0 ones from doc.layers.
function get(type, parent, deep) {
  if (arguments.length == 1 || !parent) {
    parent = app.activeDocument;
    deep = true;
  }
  var result = [];
  if (!parent[type]) return [];
  for (var i = 0; i < parent[type].length; i++) {
    result.push(parent[type][i]);
    if (parent[type][i][type] && deep)
      result = [].concat(result, get(type, parent[type][i], deep));
  }
  return result;
}

// The main obstacle is getting a recursive parent chain, then determining if any part of this chain doesn't meet requirements:
function checkAncestry(frame) {
  // So we'll split the logic into just constructing a recursive Array containing data about parents when applicable:
  function getAncestry(frame) {
    // And have a dedicated recursive function to look into the parent then recurse and concat an Array when needed:
    function getParentChain(item, data) {
      if (item.parent && /layer/i.test(item.parent.typename))
        data = getParentChain(
          item.parent,
          [].concat(
            { hidden: !item.parent.visible, locked: item.parent.locked },
            data
          )
        );
      return data;
    }
    // For ease, we start the chain with the textFrame's hidden/locked data:
    return getParentChain(frame, [
      { hidden: frame.hidden, locked: frame.locked },
    ]);
  }
  // Now that we have a list like [ { locked: true, hidden: false }, {locked: false, hidden: false }, ... ]
  // We can filter it to see if there are any hidden/lockeds present:
  var chain = getAncestry(frame),
    hiddenStatus =
      chain.filter(function (i) {
        return i.hidden;
      }).length > 0,
    lockedStatus =
      chain.filter(function (i) {
        return i.locked;
      }).length > 0;
  // AI doesn't handle short circuiting well, so if no locked/hiddens exist, we'll explicitly return:
  if (!lockedStatus && !hiddenStatus) return true;
  // Otherwise we need to ensure the chain contents either match the queries or returned empty:
  else
    return (
      (hiddenStatus == config.searchIn.hidden || !hiddenStatus) &&
      (lockedStatus == config.searchIn.locked || !lockedStatus)
    );
}

// Our main function can now be very concise, we get all text frames:
get("textFrames")
  // But remove any that don't match our search queries for hidden/locked
  .filter(function (i) {
    return checkAncestry(i);
    // Then for every remaining textFrame:
  })
  .forEach(function (frame) {
    // And for every find/replace option:
    config.targets.forEach(function (target) {
      // Replace the text content of the frame:
      frame.contents = frame.contents.replace(target.find, target.replace);
    });
  });

Likes

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 ,
Mar 12, 2022 Mar 12, 2022

Copy link to clipboard

Copied

LATEST

Flawless!

 

Thank you very much, it does the task.

Likes

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