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.
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
...
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.
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).
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.
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;
}
}
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".
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.
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.
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".
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.
Copy link to clipboard
Copied
Have a good one, I can't thank you enough.
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);
});
});
Copy link to clipboard
Copied
Flawless!
Thank you very much, it does the task.