Copy link to clipboard
Copied
Hi all scripters,
The beginning is a .txt file containing data as:
text_1;text_12
text_2;text_23
text_3;text_39
…
I'm able to get an array as:
[text_1;text_12, text_2;text_23, text_3;text_39, …]
But I need that:
[ ["text_1","text_12"], ["text_2","text_23"], ["text_3","text_39"], …]
I've absolutely no idea about the way to get this array of arrays!!
Thanks for your ideas!
(^/)
Copy link to clipboard
Copied
Uwe and Swo,
I agree with both of you!
Uwe is totally right! "With Great Power Comes Great Responsibility!"
My Master Yoda would have said:
"Powerful the Force is! Powerful Javascript and Grep are also!
Easy is to go to the dark side and errors make!
The errors we must tame!"
(^/)
I think the user has to first control his list quality! [as Uwe enumerated greatly!]
An alert [Ok to continue, Cancel to abort] could tell him: "Your list must be as …!"
Imho, the real problem the script could encounter is when a layer name already exists, as:
The concordances list is:
Layer_1;Layer_8
Layer_2;Layer_9
As you can see, there's a problem with Layer_8!
So, as Uwe did, I've taken his code and inserted a double if:
myList = ["Layer_1;Layer_8","Layer_2;Layer_9"];
myLayers = app.activeDocument.layers;
for ( L = 0; L < myList.length; L++ ) {
var error = false;
parts = myList
.split (';'); if ( myLayers.everyItem().name.join(";").match(parts[1]) ){ error = true };
if( error ){
alert ( "This layer name '" + parts[1] + "' already exist! \rThe script is going to abort! Correct it!\r(^/) ;-)" );
exit();
}
myLayers.item(parts[0]).name = parts[1];
}
Thanks for your comments! [I go on to study!]
(/^)
Copy link to clipboard
Copied
Uwe,
I need to study more what you wrote in post#19!
My way above is not really cool!
(^/)
Copy link to clipboard
Copied
That seems to be an interesting, surely bad written, approach:
app.doScript(main, ScriptLanguage.JAVASCRIPT, [], UndoModes.ENTIRE_SCRIPT, "Rename Layers By List! …");
function main ()
{
var myLayers = app.activeDocument.layers,
P = myLayers.length,
myList = ["Layer_3;Layer_2","Layer_2;Layer_8","Layer_8;Layer_1","Layer_1;Layer_4","Layer_4;Layer_7","Layer_7;Layer_5","Layer_6;Layer_6","Layer_5;Layer_3"],
N = myList.length;
while ( N-- ) myList
= myList + " "; for ( N = 0; N < myList.length; N++ ) {
parts = myList
.split (';'); myLayers.item(parts[0]).name = parts[1];
}
while ( P-- && myLayers
.name.slice(-1) == "x" ) myLayers
.name = myLayers
.name.slice(0,-1);
}
Before/After:
Of course, if bad list! … So, I suppose by default the list is correct!
Thanks for your comments!
(^/)
Copy link to clipboard
Copied
Hu...
Are you adding spaces to new names ?
while ( N-- ) myList = myList + " ";
If thoses layers are renamed and you add invisible spaces in those new names, and those new layers names are part of a more complexe script used later, it will fail.
Swo
Copy link to clipboard
Copied
Yes! but I remove them at the end [code line 15]! [new script version, previous is wrong!]
app.doScript(main, ScriptLanguage.JAVASCRIPT, [], UndoModes.ENTIRE_SCRIPT, "Rename Layers By List! …");
function main ()
{
var myLayers = app.activeDocument.layers,
P = myLayers.length,
myList = ["Layer_3;Layer_2","Layer_2;Layer_8","Layer_8;Layer_1","Layer_1;Layer_4","Layer_4;Layer_7","Layer_7;Layer_5","Layer_6;Layer_6","Layer_5;Layer_3"],
N = myList.length;
while ( N-- ) myList
= myList + " "; for ( N = 0; N < myList.length; N++ ) {
parts = myList
.split (';'); myLayers.item(parts[0]).name = parts[1];
}
while ( P-- ) if ( myLayers
.name.slice(-1) == " " ) myLayers
.name = myLayers
.name.slice(0,-1);
}
I've added 2 new layers (9 & 10) not included in the list.
(^/) Thanks for your script last version [post#33]!
Copy link to clipboard
Copied
while-loops are used when you don't know how many items you're processing. If you do know the number of items (e.g. in an array), then use a for-loop because for-loops execute quicker than while-loops. What you're up to with those two while loops is beyond me, but never mind.
What you do in that last while-loop (which should really be a for-loop), this:
if ( myLayers
.name.slice(-1) == " " ) myLayers
.name = myLayers
.name.slice(0,-1);
can be done as follows, which is probably quicker:
myLayers
.name = myLayers
.name.replace(/ $/,'');
Peter
Copy link to clipboard
Copied
Thanks Peter!
// ID is close for me at the moment! I can't test!
If I want to add a space at the end of the layers name, can I use this:
myLayers
.name = myLayers
.name.replace(/$/,' ');
(^/)
Copy link to clipboard
Copied
Yes, you can. If only InDesign's grep could do things like that.
Copy link to clipboard
Copied
Aha! So not this! …
(^/)
Copy link to clipboard
Copied
Ok, sorry, I didn't saw the line.
Won't you get an error if there's another layer with the same name ?
Didn't you forget the space before $ ?
myLayers.name = myLayers
.name.replace(/ $/,'');
Swo
(hightlight syntax is messy with this line)
Copy link to clipboard
Copied
Swo,
As I said, I suppose that the Concordances list is correct!
So, no layer with the same name!
About the syntax Peter show at post#35, I totally like it because it treats Layer_1 to 8 (contained in the list), not Layers_9 and 10.
That means this code includes in itself the "if". It's really cool!
About my last question, It's about this line:
myList
… when I add a space at the end of each item of the "Concordances list" array!
Could we write this differently with a similar grep syntax as Peter's one? Good question!
(^/)
Copy link to clipboard
Copied
You could use e grep to add a space at the end:
myList
but that would be pretty inefficient. The quickest (both to type and to execute) I'm sure is this:
myList
Copy link to clipboard
Copied
myList
Yes, of course!
Thanks a lot Peter!
(^/)
Copy link to clipboard
Copied
> Won't you get an error if there's another layer with the same name ?
Absolutely.
> Didn't you forget the space before $ ?
With JavaScript's GREP you can insert characters at an insertion point, so to speak, which isn't possible in InDesign's GREP. So for example you can do this in JavaScript:
'abc123'.replace(/(?=\d+)/,'#');
to add a # before a number. And you can do this:
'abc123'.replace(/^/,'@');
to add a # at the beginning of a string.
Copy link to clipboard
Copied
That's really cool!
… So, if I want to add a # at the end:
'abc123'.replace(/$/,'#')
Correct?
(^/)
Copy link to clipboard
Copied
Just try it, mon brave -- be brave!
Copy link to clipboard
Copied
Hello,
That's really interesting Peter, I can't remember reading about this.
Now I've got a new exercice: reveiwing my scripts and searching to modify accordingly if needed
Swo
Copy link to clipboard
Copied
Obi-wan,
I really like :
"Powerful the Force is! Powerful Javascript and Grep are also!
Easy is to go to the dark side and errors make!
The errors we must tame!"
And I would add :
"And great scripts must have!"
An alert ([Ok to continue, Cancel to abort] could tell him: "Your list must be as …!") would teach us users to be carefull with the text file content.
Too many can be anoying too...
Perhaps you can play with a "while" (be carefull, those are tricky), to rename the layer adding a +" 2", +" 3" or +" copy 1", +" copy 2"... in the name.
if (myLayers
.name == oNames && !app.activeDocument.layers.itemByName(nNames ).isValid) { // renaming, etc.
...
}
This part would change for:
if (myLayers
.name == oNames ) { // "a" will increase until there's no more layer with the same name
var a = "2";
// testName will change value until it's an unique layer name
var testName = nNames
; while (app.activeDocument.layers.itemByName(testName).isValid) {
testName = nNames
+ " " + a; a++;
}
myLayers
.name = testName; // list renamed layers
rNames.push(oNames
+ " => " + testName); // ensure old name doesn't conflict with same new name :
oNames
= ""; testN = true;
c++;
}
This feels more like a puzzle now
Have fun,
Swo
Copy link to clipboard
Copied
Swo,
Right! A puzzle now!
Could you post your last version! Thanks!
(^/)
Copy link to clipboard
Copied
Here it is:
//DESCRIPTION:Rename layers from text file
/*
Text file should contains 2 columns (old name;new name), line by line
separator is ";", can be changed at line 27
If script is unable to find layer_names.txt, it asks for a file.
No check for forbidden characters in names:
you need to check the text file before running the script.
*/
(function () {
if (app.locale.toString() == "FRENCH_LOCALE") {
var infos = {
findFile:"Choisir le fichier contenant la liste de noms :",
msg:" calque(s) renommé(s) :"
};
} else {
var infos = {
findFile:"Select the text file for layer names:",
msg:" layer(s) renamed:"
};
}
// Modify text file's name and separator (sepa) if needed
var txtList = "layer_names.txt",
sepa= ";";
var oNames = [], nNames = [], rNames = [];
var myListFile = File(myFindFile(txtList));
// datas
var testNames = listNames();
if (testNames && oNames.length == nNames.length) {
var myLayers = app.activeDocument.layers, c = 0;
for (var L = 0; L < myLayers.length; L++) {
var testN = false;
for (var n = 0; n < oNames.length && !testN; n++) {
if (myLayers
.name == oNames ) { // "a" will increase until there's no more layer with the same name
var a = "2";
// testName will change value until it's an unique layer name
var testName = nNames
; while (app.activeDocument.layers.itemByName(testName).isValid) {
testName = nNames
+ " copy " + a; a++;
}
myLayers
.name = testName; // list renamed layers
rNames.push(oNames
+ " => " + testName); // ensure old name doesn't conflict with same new name :
oNames
= ""; testN = true;
c++;
}
} // for n
} // for L
alert(c + infos.msg + "\n" + rNames.join("\n"));
}
function listNames() {
// Open the file for reading
myListFile.open("r");
var text = myListFile.read();
var lines = text.split("\n");
if (lines.length > 0) {
for (var y =0; y < lines.length; y++) {
var l = lines
; if (l.length != 0) {
if (l[0] !== "") {
var names = l.split(sepa);
if (names.length == 2) {
oNames.push(names[0]);
nNames.push(names[1]);
}
}
} // l.length
} // for
return true;
} // lines.length
return false;
}
//////// FUNCTIONS ////////////
function myFindFile(myFilePath) {
var myScriptFile = myGetScriptPath();
var myScriptFile = File(myScriptFile);
var myScriptFolder = myScriptFile.path;
myFilePath = myScriptFolder + "/" + myFilePath;
if(File(myFilePath).exists == false) {
//Display a dialog.
myFilePath = File.openDialog(infos.findFile);
}
// else alert("ok");
return myFilePath;
}
function myGetScriptPath() {
try {
myFile = app.activeScript;
}
catch(myError){
myFile = myError.fileName;
}
return myFile;
}
}());
Copy link to clipboard
Copied
Hi Obi-wan,
in answer # 19 I showed a use case.
Here the script that did the renaming:
/**
* @@@BUILDINFO@@@ RenamingLayers-Using-LIST-OldName-NewName.jsx !Version! Sun Nov 06 2016 13:45:33 GMT+0100
*/
// Uwe Laubender
// Make sure, that the separator character below is not used in a regular layer name.
// "oldName;newName" would perhaps not work, because ; could be part of oldName ( or newName ).
// I've seen a lot when it comes to naming layers with customer documents.
// There might be better separator characters than this one:
var notUsedCharacterInNames = ";";
// Scheme: oldName+notUsedCharacterInNames+newName
var listArray =
[
"Layer_1"+notUsedCharacterInNames+"Layer_5",
"Layer_2"+notUsedCharacterInNames+"Layer_4",
"Layer_4"+notUsedCharacterInNames+"Layer_2",
"Layer_5"+notUsedCharacterInNames+"Layer_1"
];
var myDoc = app.documents[0];
var allLayersOfDoc = myDoc.layers.everyItem().getElements();
// Explained later:
var associativeArray = [];
var notRenamedLayersIDs = [];
var regExp = new RegExp("^\\d+"+notUsedCharacterInNames);
// IMPORTANT:
// Be aware to preprocess the list entries.
// There is no trailing white space allowed in layer names with the UI.
// Also no one can copy/paste trailing white space to layer names!
// And using an empty string "" as name is only possible by scripting.
// AND A LIST CAN PROVIDE YOU WITH ALL SORT OF CHARACTERS OR AN EMPTY STRING.
// WARNING! This script does no preprocessing of the provided list!
// 1. Feed the associativeArray with a key/value pair by looping the listArray:
for(var n=0;n<listArray.length;n++)
{
var oldName = listArray
.split(notUsedCharacterInNames)[0]; var newName = listArray
.split(notUsedCharacterInNames)[1];
associativeArray[oldName] = newName;
};
// 2. Rename existing layers.
// Naming scheme: n+_oldName
for(var n=0;n<allLayersOfDoc.length;n++)
{
allLayersOfDoc
.name = n+notUsedCharacterInNames+allLayersOfDoc
.name; };
// 2. Loop the layers of the document
// 2.1 Check, if the old name is in the associative array
// 2.2 If not, store its ID number in the notRenamedLayersIDs array.
// And loop on with continue.
// 2.3. If it can be found in the associative array,
// rename it with the new name stored in the associative array.
for(var n=0;n<allLayersOfDoc.length;n++)
{
var nameToTest = allLayersOfDoc
.name.replace(regExp,""); // Is that name NOT in the associative array?
if(associativeArray[nameToTest] == undefined)
{
// Then do not rename that layer, but remember it:
notRenamedLayersIDs[notRenamedLayersIDs.length++] = allLayersOfDoc
.id; // Loop on:
continue
};
// Alright, the name was found as key,
// let's rename the layer to the key's value:
allLayersOfDoc
.name = associativeArray[nameToTest];
};
// What's left to do?
// Setting back the name of the layers that need no new names according to the provided list:
for(var n=0;n<notRenamedLayersIDs.length;n++)
{
myDoc.layers.itemByID(notRenamedLayersIDs
).name = myDoc.layers.itemByID(notRenamedLayersIDs
).name.replace(regExp,""); };
Result:
Regards,
Uwe
Copy link to clipboard
Copied
Hello,
And nice script...
If I had to use this script, I would need more :
Too bad I don't need such a script
Swo
Copy link to clipboard
Copied
I made a typo :
please correct line 11 :
nodoc:"Select the text file for layer names."
should be :
findFile:"Select the text file for layer names."
and line 28 would be better checking for existing layer:
if (myLayers
Copy link to clipboard
Copied
Hi,
Here my “obscure” version with error-handling and a final pop-up listing which layer was renamed.
I hope I'll be able to write as fine scripts as you do Peter,
Swo
//DESCRIPTION:Rename layers from text file
/*
Text file should contains 2 columns (old name;new name), line by line
separator is ";", can be changed at line 27
If script is unable to find layer_names.txt, it asks for a file.
No check for forbidden characters in names:
you need to check the text file before running the script.
*/
(function () {
if (app.locale.toString() == "FRENCH_LOCALE") {
var infos = {
findFile:"Choisir le fichier contenant la liste de noms :",
msg:" calque(s) renommé(s) :"
};
} else {
var infos = {
findFile:"Select the text file for layer names:",
msg:" layer(s) renamed:"
};
}
// Modify text file's name and separator (sepa) if needed
var txtList = "layer_names.txt",
sepa= ";";
var oNames = [], nNames = [], rNames = [];
var myListFile = File(myFindFile(txtList));
// datas
var testNames = listNames();
if (testNames && oNames.length == nNames.length) {
var myLayers = app.activeDocument.layers, c = 0;
for (var L = 0; L < myLayers.length; L++) {
var testN = false;
for (var n = 0; n < oNames.length && !testN; n++) {
if (myLayers
.name == oNames && !app.activeDocument.layers.itemByName(nNames ).isValid) { myLayers
.name = nNames ; // list renamed layers
rNames.push(oNames
+ " => " + nNames ); // ensure old name doesn't conflict with same new name :
oNames
= ""; testN = true;
c++;
}
} // for n
} // for L
alert(c + infos.msg + "\n" + rNames.join("\n"));
}
function listNames() {
// Open the file for reading
myListFile.open("r");
var text = myListFile.read();
var lines = text.split("\n");
if (lines.length > 0) {
for (var y =0; y < lines.length; y++) {
var l = lines
; if (l.length != 0) {
if (l[0] !== "") {
var names = l.split(sepa);
if (names.length == 2) {
oNames.push(names[0]);
nNames.push(names[1]);
}
}
} // l.length
} // for
return true;
} // lines.length
return false;
}
//////// FUNCTIONS ////////////
function myFindFile(myFilePath) {
var myScriptFile = myGetScriptPath();
var myScriptFile = File(myScriptFile);
var myScriptFolder = myScriptFile.path;
myFilePath = myScriptFolder + "/" + myFilePath;
if(File(myFilePath).exists == false) {
//Display a dialog.
myFilePath = File.openDialog(infos.findFile);
}
// else alert("ok");
return myFilePath;
}
function myGetScriptPath() {
try {
myFile = app.activeScript;
}
catch(myError){
myFile = myError.fileName;
}
return myFile;
}
}());