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
Hi Obi-wan,
one split() and one loop with another split() can get you there.
Depending on the line separator—\n or \r—that could be:
var array1 = string.split("\r");
var array2 = [];
for(var n=0;n<array1.length;n++)
{
array2[array2.length++] = array1
.split(";") };
Regards,
Uwe
Copy link to clipboard
Copied
Hi Uwe,
It seemed interesting but I can't make it work in my script.
The deal: I have 4 Layers [Layer_1, Layer_2, Layer_3 and Layer_4] and I want to change their name but not necessarily for all!
So I have a list (.txt file):
Layer_1;Layer_12
Layer_2;Layer_23
To validate the code, I've temporarily pasted this list directly into the .jsx:
myList = ["Layer_1;Layer_12","Layer_2;Layer_23"];
var myLayers = app.activeDocument.layers;
for (var L = 0; L < myLayers.length; L++) {
var myLayerName_0 = myLayers
.name; var mLength_0 = myLayerName_0.length;
for (var N = 0; N < myList.length; N++) {
var mList_0 = myList
; myConcordance = mList_0.slice(0,mLength_0);
myLayerName_1 = mList_0.slice(mLength_0+1,mList_0.length);
if ( myLayerName_0 == myConcordance ) var myLayerName_0 = myLayerName_1;
}
}
The writing seems logical but nothing happens in the Layers panel!
Tired! Thanks in advance!
(^/)
Copy link to clipboard
Copied
Hi Obi-wan,
before talking about your code, I want to talk about the separator string—";"—you are using with your list:
It should be one, that is not used in the listed names of layers. And that could become quite difficult since it seems like every possible character is allowed for forming a name of a layer. Even a tabulator character could be used in a layer name: "\t”. It could not be typed in, but it could be copy/pasted to the Name input field.
So the question boils down to:
What is a good separator string for a concordance list, old name|separator|new name for renaming layers?
How about:
"\u0016"
That is used as a special character for tables with InDesign.
I tried to copy/paste it to the Name input field of the Layers panel, but it was stripped out while pasting.
Still one could assign a name using "\u0016" as part of a string to a layer's name by scripting:
app.documents[0].layers[0].name = "N"+"\u0016"+"N";
Screenshot after running this line:
So it's not 100% save to use a delimiter like that.
Does someone has a better idea?
Hm.
Would a Windows user be able to insert "\u0016" to the Name edit field using the keyboard by pressing Alt+0016 ?
Or Alt 022 or Alt 22 ?
Cannot test this, currently I'm on Mac OSX.
Regards,
Uwe
Copy link to clipboard
Copied
Hi Uwe,
A little misunderstanding between us!
I've ";" in my .txt file because it's an export from Excel in .txt with as horizontal separator = ";".
The user wants to change layer names! So he creates a concordances table in Excel.
First column = actual layer name
2nd column = new layer name.
The next step of my writing will be to extract this list directly from the .txt file [I've already written the code].
Just have to insert it when I'll be able to manipulate its contents! Not yet apparently!
(^/)
Copy link to clipboard
Copied
I don't think, I misunderstood.
I spoke a bit in general terms about compiling a concordances table.
If your customer isn't using the horizontal separator ";" in a layer name, all is starting ok.
But sometimes a list is compiled by reading out a lot of documents by scripting where you cannot check individually what's in a layer's name. And then my notes on what would be the best delimiter would become relevant very soon.
Best regards,
Uwe
Copy link to clipboard
Copied
Back to your essential coding problems.
Here an example that could work for you:
/**
* @@@BUILDINFO@@@ RenamingLayers-Using-ConcordanceList.jsx !Version! Sat Nov 05 2016 15:05:59 GMT+0100
*/
// Uwe Laubender
// Make sure, that the separatorString cannot be 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.
// This could be a candidate:
var notUsedCharacterInNames = "\u0016";
// But for the sake of Obi-wan's example we use the following one:
notUsedCharacterInNames = ";";
// Scheme: oldName+separatorString+newName
var listArray =
[
"Layer_1"+notUsedCharacterInNames+"Layer_12",
"Layer_2"+notUsedCharacterInNames+"Layer_12" // I did this by purpose
];
var myDoc = app.documents[0];
// Execute function:
renameLayersFromList(myDoc , listArray , notUsedCharacterInNames);
// Arguments of function:
// 1. doc [object Document]
// 2. listArray [object Array] of concordance strings in the form:
// oldString+separatorString+newString
// 3. notUsedCharacterInNames [object String],
// the separator string from item 2, that has dual use in the function below:
// A. To split() the entry in the provided list to new and old
// B. To check in the layer names array of the document if the new name is already in use
function renameLayersFromList(doc , listArray , notUsedCharacterInNames)
{
// Loop through the listArray:
for(var n=0;n<listArray.length;n++)
{
// Implemented for preventing errors with the naming process.
// Note: Names of layers must be unique.
var error = false;
// Split the incoming item of the listArray:
var splitArray = listArray
.split(notUsedCharacterInNames);
// No error checking here on the result, but maybe should be done.
// The provided list could be malformed.
var oldName = splitArray[0];
var newName = splitArray[1];
// Before renaming we should check two things:
// 1. Does the old name listed correspond with a valid layer name?
if(!doc.layers.itemByName(oldName).isValid){error = true};
// 2. Is the new name in conflict with an already existing layer name?
if(app.documents[0].layers.everyItem().name.join(notUsedCharacterInNames).match(newName)){error = true};
// If an error ocsurred, we loop on.
// You could also track the error and write a detailed report.
if(error){continue};
// No errors? Fine. Do the renaming:
doc.layers.itemByName(oldName).name = newName;
}
} // function renameLayersFromList()
Have a nice weekend, Obi-wan!
Uwe
Copy link to clipboard
Copied
Uwe,
Nice learning for me WE!
See you soon! … and have a nice WE too!
(^/)
[ I've tested your code! Cool! Now, just need to study, understand and be able to replicate it in other situations! Thanks a lot! ]
Copy link to clipboard
Copied
Hi,
You forget somewhere in your script to modifiy the layer name.
Here another attempt with 2 arrays : old names = oNames and new names = nNames
(function () {
if (app.locale.toString() == "FRENCH_LOCALE") {
var infos = {
findFile:"Choisir le fichier contenant la liste de noms."
};
} else {
var infos = {
nodoc:"Select the text file for layer names."
};
}
var txtList = "layer_names.txt", oNames = [], nNames = [];
var myListFile = File(myFindFile(txtList));
// datas
var testNames = listNames();
if (testNames && oNames.length == nNames.length) {
// alert(oNames + "\n" + nNames);
var myLayers = app.activeDocument.layers;
for (var L = 0; L < myLayers.length; L++) {
var testN = false;
for (var n = 0; n < oNames.length && !testN; n++) {
if (myLayers
.name == oNames ) { myLayers
.name = nNames ; // ensure old name doesn't conflict with same new name :
oNames
= ""; testN = true;
}
} // for n
} // for L
}
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(";");
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;
}
}());
I hope this helped,
Swo.
Copy link to clipboard
Copied
Wow! Another code and more work for my saturday!
I'm going to work on it too and I'll give you comments soon!
Thanks a lot!
… and have a nice WE too!
(^/)
Copy link to clipboard
Copied
Michel -- Maybe you should tell us what you want to achieve. Rename layers, using the text file, so that Layer_1 is renamed Layer_12, etc.?
Copy link to clipboard
Copied
Hi Peter,
The idea is, using a list of concordances, as: old layer name / new layer name, joined as a .txt file [the list could be done in Excel and exported as I said], to change the layer names in ID using this list.
Some layer names have to be changed (concordance in the .txt file) but certains not.
I've already manipulated external lists as apply a char style to a list of words or make an index from a list of words.
I've never done it in this kind of situation.
In fact, I'm really interested by the way we could play it. I've actually 3 scripts, mine doesn't work!
Uwe's one seems to work fine! As I said, the most important for me is to study and understand codes and truly be able to replicate them.
I don't search somebody to write scripts for me! I just would like help to tell me why I'm wrong and learn more in right directions!
[JS] is really awesome and I think we are very lucky to have here true JS masters!
(^/)
Copy link to clipboard
Copied
Ok, then here's yet another one. The code you wrote and showed above (in comment 2) is very verbose. All you need is this:
myList = ["Layer_1;Layer_12","Layer_2;Layer_23"];
myLayers = app.activeDocument.layers;
for (L = 0; L < myLayers.length; L++) {
parts = myList
.split (';'); myLayers.item(parts[0]).name = myLayers.parts[1];
}
Peter
Copy link to clipboard
Copied
Hi Peter,
I've an error on "parts"!
(^/)
Copy link to clipboard
Copied
Hi Obi-wan,
About error handling:
What could go wrong?
Customer errors:
1. The layer name provided as "old name" does not match existing names.
2. The layer name provided as "old name" is provided more than one time.
3. The "new name" is provided more than one time in the provided list.
To tackle issue 1 and 2 the incoming list has to be pre-processed and should be matched against the existing names.
Same with issue 3, pre-process the incoming list. Write an error report and contact the customer before proceeding.
Structure problem (no customer error):
3. The "new name" is already in use with a layer that the loop did not come accross yet.
How to tackle the structure problem?
Before doing anything according to the provided list, rename all layers with e.g. a numbering scheme.
doc.layers
.name = n+uniqueSeparator+doc.layers .name;
Then you could start renaming using the provided list.
Log all layers that are not renamed according to the list.
Remove the numbering scheme for all layers not provided by the list.
Below an example.
"Layer_3", "Background_1 and "Background_2" should not be renamed and therefore are not provided by the list:
var notUsedCharacterInNames = ";"
var listArray =
[
"Layer_1"+notUsedCharacterInNames+"Layer_5",
"Layer_2"+notUsedCharacterInNames+"Layer_4",
"Layer_4"+notUsedCharacterInNames+"Layer_2",
"Layer_5"+notUsedCharacterInNames+"Layer_1"
];
After renaming:
Regards,
Uwe
Copy link to clipboard
Copied
@Uwe: Thanks for this interesting exemple where renaming "somethings" doesn't always means completely differents names.
@Obi:
About your first question: going from text file to [["oName 1", "nName 1"], ["oName 2", "nName 2"], ...]
it would be a function with only one array for names (allNames):
function oneListNames() {
// 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] !== "" && l[1] !== "") {
var names = l.split(";");
if (names.length > 1) {
allNames.push([names[0], names[1]]);
}
}
}
}
return true;
}
return false;
}
Swo
Copy link to clipboard
Copied
Uwe and Swo,
Very cool answers for me! Thanks a lot!
It's a very good learning for me to study how you think and how you write your own code!
I'm going to study more all about "array" and "error" use.
@Swo, I'm very interested by your approach about the external .txt file call! I'm going to study it too!
Maybe more questions on this point later!
(^/)
Copy link to clipboard
Copied
Yes Peter,
but I think there should be some error handling.
Maybe "Layer_12" is already used as name in the stack of layers, so "Layer_1" cannot be renamed?
Regards,
Uwe
Copy link to clipboard
Copied
I think we need a double loop as I did!
Actually, as I've corrected and inserted an "if", the code corrects first, the top layer, then next in the Layers panel.
(^/)
Copy link to clipboard
Copied
No, you don't need nested loops. Line 5 in my code should be as follows:
myLayers.item(parts[0]).name = parts[1];
And sure, error handling should be handled, but all that error-handling obscures the basics of what you're trying to illustrate.
P.
Copy link to clipboard
Copied
Peter,
I totally like your code!
I didn't know the "parts" syntax and the way you thought the loop is great!
… but I've an error even if the script correctly renames the layers! …
Do I need to write a try…catch to delete it?
Copy link to clipboard
Copied
Ugh... In line 4, 'myLayers.length' should be 'myList.length'.
P.
Copy link to clipboard
Copied
Yeap!
myList = ["Layer_1;Layer_12","Layer_2;Layer_23"];
myLayers = app.activeDocument.layers;
for (L = 0; L < myList.length; L++) {
parts = myList
.split (';'); myLayers.item(parts[0]).name = parts[1];
}
Previously, I've corrected as:
myList = ["Layer_1;Layer_12","Layer_2;Layer_23"];
myLayers = app.activeDocument.layers;
for (L = 0; L < myLayers.length; L++) {
try
{
parts = myList
.split (';'); myLayers.item(parts[0]).name = parts[1];
}
catch(myError){}
}
… and I've no error!
So, my big questions:
In a script writing, is it dangerous to systematically "mask" real errors with a try…catch (as I did)?
and:
do I need to systematically find why there's an error and where it is to avoid awful problems in the future?
(^/)
Copy link to clipboard
Copied
As you said : using
catch(myError){}
doesn't mean there isn't any error, but that you "mask" them.
There's a possible error if the new name is already used for a layer.
I usually don't hide error messages, but I try to thinks or test untill there's no more errors.
Uwe showed me in his example that I missed some possible errors.
When you'll test yours scripts with users, they'll find new errors you didn't thought about :S
(That's the way when working with creative people)
If you hide errors, how about in six month, or a year later... You'll use this script, and won't understand why it doesn't produce any result
...because you'll have forgotten about hiding errors.
If you don't have yet a solution, at least add an alert. Later, when you'll have more time, you'll be able to debug or complete the script.
I use this as a reminder, and because users feel betrayed or frightened (sort of) when you give a script that "doesn't work" or give cryptic error messages (inDesign error messages).
Just my 2 cents about this,
Swo.
Copy link to clipboard
Copied
Hi Obi-wan,
and be aware, that a provided list should always be prepocessed.
Otherwise some strange things could happen. 🙂
A "normal" user cannot:
1. Use a name with an empty string.
A script can set it. A list can provide it.
doc.activeLayer.name = "";
Here the UI is not sure what to tell:
2. Use names with white space only.
A script can set it. A list can provide it.
doc.activeLayer.name = " ";
3. Use names with trailing white space.
A script can set it. A list can provide it.
doc.activeLayer.name = " trailing white space ";
Hm:
"Ils doivent envisager qu’une grande responsabilité est la suite inséparable d’un grand pouvoir."
And also: "With Great Power Comes Great Responsibility".
Regards,
Uwe