Does anyone have a script that utilizes a CSV to create text layers?
Copy link to clipboard
Copied
I work in Post-Production and am trying to streamline my legal line creation process using a CSV. I have very limited Javascript experience and am hopeful that someone has a method to auto create text layers using a CSV OR could help point me in the right direction.
Thank you
Explore related tutorials & articles
Copy link to clipboard
Copied
Photoshop has a built in Variable feature to populate existing text placeholder layers with content from a comma or tab delimited text file.
Otherwise, yes, a script can read from the .csv and create a text layer and content.
It would be helpful if you could provide sample files or before and after screenshots to illustrate, as there are more questions than answers at the moment.
Copy link to clipboard
Copied
I know how to use a CSV to replace exisiting variables. What I am hoping to accomplish is have a blank canvas with no layers, have photoshop read a CSV and create multiple text layers for me to export. Rather than me creating a new layer and then copy/pasting each new legal line from a word document.
Copy link to clipboard
Copied
Attached images
Copy link to clipboard
Copied
I have a number of scripts that do things with CSV data, for Photoshop and other apps, but not any published yet that do precisely what you describe. It is certainly possible. The one Photoshop script of mine that is close is described in this video: https://youtu.be/zsaVUHEi6X0
I have two other scripts in progress at this time for some clients that work on multiple text layers from multiple CSV columns. Nothing that creates the layer, but that's not a big deal. The scripts I've made the clients had the layer already because they wanted to style it with effects. That's a good reason to have a layer exist already. I do custom scripts for hire to exact client needs. If interested message me through my website http://www.marspremedia.com/contact/
Copy link to clipboard
Copied
Wow, this is really cool for me, I have been watching your tutorials all week. Thank you for your response! I'll keep trying with your tutorials for now.
Copy link to clipboard
Copied
That's great! Thanks for watching. Is this a script you want to write yourself? And just need some help? I don't usually post entire scripts because some of mine are thousands of lines, but I'll be happy to post code for a function that parses CSV if you want. Or other specifics tasks. Making a layer isn't too difficult. I think my script tutorial #6 video has some about that. But if not enough, we're always ready to answer questions.
Copy link to clipboard
Copied
automate more tasks that come up in the future. But I need a script
because recording actions wouldn't really be scalable for the company I am
working for. That would be very helpful! I am using scripting listener as
recommended by your videos. But anything would be helpful!
Copy link to clipboard
Copied
Writing your own code is a great way to go. I'll be glad to help however possible. To start, here is my function for parsing CSV:
function parseCsv(data, delimiter) {
// data: String = contents of a CSV file
// delimiter: character that separates columns
// undefined defaults to comma
// Returns: Array [[String row, String column]]
var c = ""; // Character at index.
var d = delimiter || ","; // Default to comma.
var endIndex = data.length;
var index = 0;
var maxIndex = endIndex - 1;
var q = false; // "Are we in quotes?"
var result = []; // Array of rows (array of column arrays).
var row = []; // Array of columns.
var v = ""; // Column value.
while (index < endIndex) {
c = data[index];
if (q) { // In quotes.
if (c == "\"") {
// Found quote; look ahead for another.
if (index < maxIndex && data[index + 1] == "\"") {
// Found another quote means escaped.
// Increment and add to column value.
index++;
v += c;
} else {
// Next character not a quote; last quote not escaped.
q = !q; // Toggle "Are we in quotes?"
}
} else {
// Add character to column value.
v += c;
}
} else { // Not in quotes.
if (c == "\"") {
// Found quote.
q = !q; // Toggle "Are we in quotes?"
} else if (c == "\n" || c == "\r") {
// Reached end of line.
// Test for CRLF.
if (c == "\r" && index < maxIndex) {
if (data[index + 1] == "\n") {
// Skip trailing newline.
index++;
}
}
// Column and row complete.
row.push(v);
v = "";
// Add row to result if first row or length matches first row.
if (result.length == 0 || row.length == result[0].length) {
result.push(row);
}
row = [];
} else if (c == d) {
// Found comma; column complete.
row.push(v);
v = "";
} else {
// Add character to column value.
v += c;
}
}
if (index == maxIndex) {
// Reached end of data; flush.
if (v.length || c == d) {
row.push(v);
}
// Add row to result if length matches first row.
if (row.length == result[0].length) {
result.push(row);
}
break;
}
index++;
}
return result;
}
To use, open the CSV file and read it into a string. Then pass the string to the function. The delimiter is optional. If null it defaults to comma (some European countries use semicolon instead).
var file = new File("path/to/yourfile.csv");
file.open("r");
var data = file.read();
file.close();
var dataArray = parseCsv(data);
// Extract header row.
var header = dataArray.shift();
The result is a two-dimensional array [[row, column]]. Most likely the file has a header row describing what the columns are. So I usually extract that first, and either use the values if they might vary, but likely you know what the column heads are so just dump it. Now row zero of the data array is the first row of actual data to process. Now to do something with it. Let's say the two columns are 'file name' and 'text'.
var fileName;
var text;
for (var i = 0; i < dataArray.length; i++) {
fileName = dataArray[i][0];
text = dataArray[i][1];
// Do some process here.
// For example, open 'fileName'
// Or dupe a master doc to save as 'fileName'
// Create a text layer and set the contents to 'text' for example.
}
My tutorial #6 shows a working example of adding a text layer to a folder of images, but not with CSV. It helps illustrate the idea though. Have a look at that.
Copy link to clipboard
Copied
@willcampbell7 / @Lumigraphics ... or anybody else who can help:
I am hoping that you can help me to make use of your CSV parser...
Let's say I have the following simple table:
Name |
Value1 |
Value2 |
Value3 |
Apple |
A1 |
A2 |
A3 |
Banana |
B1 |
B2 |
B3 |
Cherry |
C1 |
C2 |
C3 |
Name,Value1,Value2,Value3
Apple,A1,A2,A3
Banana,B1,B2,B3
Cherry,C1,C2,C3
I don't need the first row. And I wish to find out if the Name "Banana" exists. If it does exist, then extract the values to variables from that row – Value1, Value2, Value3 (ignoring the Name).
var value1 = 'B1';
var value2 = 'B2';
var value3 = 'B3';
Even if the entire row matching "Banana" was extracted to a variable, I could use RegEx to isolate the required parts, however, I know there must be a more "pure" way than via regex.
I have managed to make it as far as:
var file = new File("~/Desktop/Test-CSV/test.csv");
file.open("r");
var data = file.read();
file.close();
var dataArray = parseCsv(data);
// Extract header row.
var header = dataArray.shift();
for (var i = 0; i < dataArray.length; i++) {
var Name = dataArray[i][0];
var Value1 = dataArray[i][1];
var Value2 = dataArray[i][2];
var Value3 = dataArray[i][3];
// Do some process here.
var theValue = "Banana"; // this would come from a variable, not a string
if (data.match(theValue)) {
//alert("Banana exists");
// Mock result that I wish to achieve...
alert("Banana,B1,B2,B3");
// Or even better - B1,B2,B3 as separate variables for Value1,Value2,Value3
} else {
alert("Banana doesn't exist!");
}
}
/////
function parseCsv(data, delimiter) {
// data: String = contents of a CSV file
// delimiter: character that separates columns
// undefined defaults to comma
// Returns: Array [[String row, String column]]
var c = ""; // Character at index.
var d = delimiter || ","; // Default to comma.
var endIndex = data.length;
var index = 0;
var maxIndex = endIndex - 1;
var q = false; // "Are we in quotes?"
var result = []; // Array of rows (array of column arrays).
var row = []; // Array of columns.
var v = ""; // Column value.
while (index < endIndex) {
c = data[index];
if (q) { // In quotes.
if (c == "\"") {
// Found quote; look ahead for another.
if (index < maxIndex && data[index + 1] == "\"") {
// Found another quote means escaped.
// Increment and add to column value.
index++;
v += c;
} else {
// Next character not a quote; last quote not escaped.
q = !q; // Toggle "Are we in quotes?"
}
} else {
// Add character to column value.
v += c;
}
} else { // Not in quotes.
if (c == "\"") {
// Found quote.
q = !q; // Toggle "Are we in quotes?"
} else if (c == "\n" || c == "\r") {
// Reached end of line.
// Test for CRLF.
if (c == "\r" && index < maxIndex) {
if (data[index + 1] == "\n") {
// Skip trailing newline.
index++;
}
}
// Column and row complete.
row.push(v);
v = "";
// Add row to result if first row or length matches first row.
if (result.length == 0 || row.length == result[0].length) {
result.push(row);
}
row = [];
} else if (c == d) {
// Found comma; column complete.
row.push(v);
v = "";
} else {
// Add character to column value.
v += c;
}
}
if (index == maxIndex) {
// Reached end of data; flush.
if (v.length || c == d) {
row.push(v);
}
// Add row to result if length matches first row.
if (row.length == result[0].length) {
result.push(row);
}
break;
}
index++;
}
return result;
}
Whether searching for Apple, Banana or Cherry, or another fruit yet to be added to the CSV, the idea is to extract the values to a variable so that the variable can be used elsewhere in the script. Hope this makes sense.
Thank you in advance. I haven't done much work with arrays and I realise that this is hindering things.
Copy link to clipboard
Copied
You're definitely on the right track.
The only problem is when looking for 'theValue' which is defined as "Banana".
Currently the code is
if (data.match(theValue)) {
That looks for a match within the entire contents of the file that was read, even before parsed.
(the var 'data' in the code is the raw input of the file.read method).
Rather than the entire contents of the file, check only the first column (second dimension of dataArray, index zero) of the current row of data (the first dimension of dataArray, index defined by the loop iterator 'i', incremented each row).
The code already has this value loaded into a variable. That's good:
var Name = dataArray[i][0];
So test if 'Name' is "Banana" in place of testing if 'theValue' exists anywhere in the file contents.
// remove --> if (data.match(theValue)) { // replace with below...
if (Name == theValue) {
alert(Name + "\n" + Value1 + "\n" + Value2 + "\n" + Value3);
}
I wouldn't recommend the else block. If no alert, it wasn't found. That should be good enough for now. You'll likely do something else anyway ultimately.
Another way to find "Banana" is use a regular expression.
if (/banana/i.test(Name)) {
// true that variable 'Name' contains "banana", any letter case
}
This is like the 'match' method you had used. This is true if the text "banana" exists anywhere in the value, and the "i" flag tells it to match case insensitive. So it matches Banana, banana, and BANANA. That is usually helpful.
Disclaimer: code I've typed here is straight from my thoughts to my fingers... not tested.
Copy link to clipboard
Copied
Rather than the entire contents of the file, check only the first column ...
The code already has this value loaded into a variable. That's good:
I wouldn't recommend the else block. If no alert, it wasn't found. That should be good enough for now. You'll likely do something else anyway ultimately.
By willcampbell7
Thank you for the pointers William!
Great tip, seems obvious in hindsight about searching only the first column! :]
Yes, the if/else block will be fleshed out with other code, I am just trying to simplify the example so that it is concise. I do like an alert in the else block when testing, just to ensure that the if block is correct as I test matching/non-matching input. If there is nothing there I don't have any feedback.
Thanks for the tip, yes, I often switch between using .test() and .match() as they are both regular expression based.
I will be passing a variable to the test/match and not a static string, that was just an example. The script will be looping over multiple input values and checking if they exist in the CSV and then grabbing the values from the CSV for use in the script. I need the basic structure working before I add the extra stuff, it is useless without the CSV stuff.
Thanks again!
Copy link to clipboard
Copied
This reads line by line rather than use a CSVfile, but it does what you ask.
#target photoshop
addText();
function addText(){
if(documents.length > 0){
var originalDialogMode = app.displayDialogs;
app.displayDialogs = DialogModes.ERROR;
var originalRulerUnits = preferences.rulerUnits;
preferences.rulerUnits = Units.PIXELS;
try{
var testFile = new File('~/Desktop').openDlg('Select import file', '*.txt');
if(testFile != null){
testFile.open('r');
var textLine = testFile.readln();
var docRef = activeDocument;
var LayerRef = null;
var TextRef = null;
var pos = 200;
while(textLine != ''){ //read in text one line at a time
LayerRef = docRef.artLayers.add();
LayerRef.kind = LayerKind.TEXT;
TextRef = LayerRef.textItem;
TextRef.contents = textLine;
//optional text styling, can be customized to suit
pos = pos + 50;
TextRef.position = new Array(pos, pos);
preferences.rulerUnits = Units.POINTS;
TextRef.size = 24;
TextRef.useAutoLeading = false;
TextRef.leading = 24;
TextRef.font = 'Calibri-Bold';
TextRef.justification = Justification.CENTER;
TextRef.autoKerning = AutoKernType.METRICS;
//end optional text styling
textLine = testFile.readln();
}
testFile.close();
}
}
catch(e){
alert(e + e.line);
preferences.rulerUnits = originalRulerUnits;
app.displayDialogs = originalDialogMode;
return;
}
preferences.rulerUnits = originalRulerUnits;
app.displayDialogs = originalDialogMode;
}
else{
alert('You must have a document open to run this script.');
return;
}
return;
}
Copy link to clipboard
Copied
@Lumigraphics – Thank you, I'll see if I can make something from what you have offered.

