• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
1

JavaScript to copy text from one Illustrator file to another Illustrator file

Explorer ,
May 13, 2024 May 13, 2024

Copy link to clipboard

Copied

I have this ExtendScript to copy text from one Illustrator file to another Illustrator file. The data in the illustrator file is on different layers. The template has text-frames on separate layers. The script copies the text from one layer of the Data file and pastes it into the text frame matching the source layer. i.e. the Data and the Template files have the same layer structure. If any layer is missing, in the Template file, then that text is ignored and doesn't get copied.

 

Now, I have a different situation:

Data File:

1) The entire text is on a single layer called 'excel data'. The text is distributed in multiple text frames. Each text-frame has specific text and are tagged accordingly. This can be viewed by twirl-opening the layer in the layer panel.

 

Template File:

2) The template has text frames all over the artwork. The text frames contains tag-names as text. All the text frames in the template are on one singel layer called 'artwork'.

3) The order of the layer or text-frame is not fixed.

 

I want a Javascript (modified version of my current script) to read the data from the Data file and paste it into the requisite text-frame in the Template file.

 

Link to Illustrator files:
https://drive.google.com/drive/folders/136h80mYPvTJLxQRw1hQCHDDyqULZ8aHc?usp=sharing

 

Please let me know if more details are required.

 

Here's my current script.

// Script to copy Text from Illustrator file and pasting into the Illustrator Template File.

// PREREQUISITES:
// Illustrator Data file containing data on different layers. Layer names should match the layer names in the Template file.
// Illustrator Template file, containing Text-Frames to get text from the Data file. Text-Frames should be on the specic layer matching the layer names as that in the Data file. For example:
// Data File layer name: 'Main Text'
// Template File Layer name: Text Frame placed on the 'Main Text' layer.

function showDialog() {
  var dialog = new Window(
    'dialog',
    'Copy/Paste Text between Illustrator File(s)'
  );
  dialog.alignChildren = 'fill';
  dialog.alignChildren = 'left';

  // Panel for template file selection
  var templateFilePanel = dialog.add('panel', undefined, '');
  templateFilePanel.add('statictext', undefined, 'Template File:');
  templateFilePanel.orientation = 'row';
  var templateFilePathInput = templateFilePanel.add('edittext', undefined, '', {
    readonly: true
  });
  templateFilePathInput.size = [302, 20];
  var templateFileBrowseBtn = templateFilePanel.add(
    'button',
    undefined,
    'Browse'
  );
  templateFileBrowseBtn.onClick = function() {
    var file = File.openDialog('Select the Template File', '*.ai', false);
    if (file) {
      templateFilePathInput.text = file.fsName;
    }
  };

  // Panel for data file selection
  var dataFilePanel = dialog.add('panel', undefined, '');
  dataFilePanel.add('statictext', undefined, 'Data File:');
  dataFilePanel.orientation = 'row';
  var dataFilePathInput = dataFilePanel.add('edittext', undefined, '', {
    readonly: true
  });
  dataFilePathInput.size = [330, 20];
  var dataFileBrowseBtn = dataFilePanel.add('button', undefined, 'Browse');
  dataFileBrowseBtn.onClick = function() {
    var file = File.openDialog('Select the Data File', '*.ai', false);
    if (file) {
      dataFilePathInput.text = file.fsName;
    }
  };

  // Panel for output folder selection
  var outputPanel = dialog.add('panel', undefined, '');
  outputPanel.add('statictext', undefined, 'Select Folder to Save New File:');
  outputPanel.orientation = 'row';
  var outputPathInput = outputPanel.add('edittext', undefined, '', {
    readonly: true
  });
  outputPathInput.size = [210, 20];
  var outputBrowseBtn = outputPanel.add('button', undefined, 'Browse');
  outputBrowseBtn.onClick = function() {
    var folder = Folder.selectDialog('Select Folder to Save New File');
    if (folder) {
      outputPathInput.text = folder.fsName;
    }
  };

  // Dialog buttons
  var buttonGroup = dialog.add('group');
  buttonGroup.alignment = 'center';
  buttonGroup.add('button', undefined, 'OK', { name: 'ok' });
  buttonGroup.add('button', undefined, 'Cancel', { name: 'cancel' });

  if (dialog.show() === 1) {
    // User clicked OK
    return {
      dataFilePath: dataFilePathInput.text,
      templateFilePath: templateFilePathInput.text,
      outputPath: outputPathInput.text
    };
  } else {
    return null; // User clicked Cancel or closed the dialog
  }
}

// Function to copy data from source to target based on layer names
function transferData(source, target) {
  // Create a dictionary to check target layers quickly
  var targetLayerNames = {};
  for (var k = 0; k < target.layers.length; k++) {
    targetLayerNames[target.layers[k].name] = target.layers[k];
  }

  // Iterate over each layer in the source document
  for (var i = 0; i < source.layers.length; i++) {
    var sourceLayer = source.layers[i];
    // Check if the layer exists in the target document by looking up the dictionary
    if (targetLayerNames.hasOwnProperty(sourceLayer.name)) {
      var targetLayer = targetLayerNames[sourceLayer.name];

      // Iterate over each text frame in the source layer
      for (var j = 0; j < sourceLayer.textFrames.length; j++) {
        if (j < targetLayer.textFrames.length) {
          // Copy text from source to target if corresponding text frame exists
          var sourceText = sourceLayer.textFrames[j];
          var targetText = targetLayer.textFrames[j];
          targetText.contents = sourceText.contents;
        } else {
          // Log error or information when source has more text frames than target
          $.writeln(
            "Info: Additional text frame in source layer '" +
              sourceLayer.name +
              "' at position " +
              j +
              ' has no corresponding frame in target.'
          );
        }
      }
    } else {
      // Log information when no corresponding layer is found in the target document
      $.writeln(
        "Info: No corresponding layer found in target for source layer '" +
          sourceLayer.name +
          "'. Layer ignored."
      );
    }
  }
}

function main() {
  var fileSelections = showDialog();
  if (fileSelections) {
    var dataDoc = app.open(File(fileSelections.dataFilePath)); // Open the selected data file
    var templateDoc = app.open(File(fileSelections.templateFilePath)); // Open the selected template file
    transferData(dataDoc, templateDoc); // Run the transfer function

    // Save the new document
    var dataFileName = decodeURI(File(fileSelections.dataFilePath).name);

    // To save as 'templateFileName_dataFileName.ai'
    var templateFileName = decodeURI(
      File(fileSelections.templateFilePath).name
    );
    var newFileName = templateFileName.replace('.ai', '') + '_' + dataFileName;

    // // To save as 'dataFileName.ai_Output'
    // var newFileName = dataFileName.replace('.ai', '') + '_Output';

    var saveFile = new File(fileSelections.outputPath + '/' + newFileName);
    templateDoc.saveAs(saveFile);

    templateDoc.close();
    dataDoc.close();
    alert('Data successfully transferred and file saved as: \n' + newFileName);
  }
}

main();

 

TOPICS
Scripting

Views

223

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
Adobe
Guide ,
May 13, 2024 May 13, 2024

Copy link to clipboard

Copied

So the only difference is that iterating and checking layers is no longer required, all the text frames being in one layer in the source (excel data) and target (artwork)?  If so, see if this works (I've added two lines, 95 and 96, and commented-out the loop and conditional immediately below): 

function showDialog() {
  var dialog = new Window(
    'dialog',
    'Copy/Paste Text between Illustrator File(s)'
  );
  dialog.alignChildren = 'fill';
  dialog.alignChildren = 'left';

  // Panel for template file selection
  var templateFilePanel = dialog.add('panel', undefined, '');
  templateFilePanel.add('statictext', undefined, 'Template File:');
  templateFilePanel.orientation = 'row';
  var templateFilePathInput = templateFilePanel.add('edittext', undefined, '', {
    readonly: true
  });
  templateFilePathInput.size = [302, 20];
  var templateFileBrowseBtn = templateFilePanel.add(
    'button',
    undefined,
    'Browse'
  );
  templateFileBrowseBtn.onClick = function() {
    var file = File.openDialog('Select the Template File', '*.ai', false);
    if (file) {
      templateFilePathInput.text = file.fsName;
    }
  };

  // Panel for data file selection
  var dataFilePanel = dialog.add('panel', undefined, '');
  dataFilePanel.add('statictext', undefined, 'Data File:');
  dataFilePanel.orientation = 'row';
  var dataFilePathInput = dataFilePanel.add('edittext', undefined, '', {
    readonly: true
  });
  dataFilePathInput.size = [330, 20];
  var dataFileBrowseBtn = dataFilePanel.add('button', undefined, 'Browse');
  dataFileBrowseBtn.onClick = function() {
    var file = File.openDialog('Select the Data File', '*.ai', false);
    if (file) {
      dataFilePathInput.text = file.fsName;
    }
  };

  // Panel for output folder selection
  var outputPanel = dialog.add('panel', undefined, '');
  outputPanel.add('statictext', undefined, 'Select Folder to Save New File:');
  outputPanel.orientation = 'row';
  var outputPathInput = outputPanel.add('edittext', undefined, '', {
    readonly: true
  });
  outputPathInput.size = [210, 20];
  var outputBrowseBtn = outputPanel.add('button', undefined, 'Browse');
  outputBrowseBtn.onClick = function() {
    var folder = Folder.selectDialog('Select Folder to Save New File');
    if (folder) {
      outputPathInput.text = folder.fsName;
    }
  };

  // Dialog buttons
  var buttonGroup = dialog.add('group');
  buttonGroup.alignment = 'center';
  buttonGroup.add('button', undefined, 'OK', { name: 'ok' });
  buttonGroup.add('button', undefined, 'Cancel', { name: 'cancel' });

  if (dialog.show() === 1) {
    // User clicked OK
    return {
      dataFilePath: dataFilePathInput.text,
      templateFilePath: templateFilePathInput.text,
      outputPath: outputPathInput.text
    };
  } else {
    return null; // User clicked Cancel or closed the dialog
  }
}

// Function to copy data from source to target based on layer names
function transferData(source, target) {
  // Create a dictionary to check target layers quickly
  var targetLayerNames = {};
  for (var k = 0; k < target.layers.length; k++) {
    targetLayerNames[target.layers[k].name] = target.layers[k];
  }

  var sourceLayer = source.layers["excel data"];
  var targetLayer = target.layers["artwork"];
  // Iterate over each layer in the source document
  // for (var i = 0; i < source.layers.length; i++) {
  //   var sourceLayer = source.layers[i];
    // Check if the layer exists in the target document by looking up the dictionary
    // if (targetLayerNames.hasOwnProperty(sourceLayer.name)) {
    //   var targetLayer = targetLayerNames[sourceLayer.name];

      // Iterate over each text frame in the source layer
      for (var j = 0; j < sourceLayer.textFrames.length; j++) {
        if (j < targetLayer.textFrames.length) {
          // Copy text from source to target if corresponding text frame exists
          var sourceText = sourceLayer.textFrames[j];
          var targetText = targetLayer.textFrames[j];
          targetText.contents = sourceText.contents;
        } else {
          // Log error or information when source has more text frames than target
          $.writeln(
            "Info: Additional text frame in source layer '" +
              sourceLayer.name +
              "' at position " +
              j +
              ' has no corresponding frame in target.'
          );
        }
      }
    // } else {
    //   // Log information when no corresponding layer is found in the target document
    //   $.writeln(
    //     "Info: No corresponding layer found in target for source layer '" +
    //       sourceLayer.name +
    //       "'. Layer ignored."
    //   );
    // }
  // }
}

function main() {
  var fileSelections = showDialog();
  if (fileSelections) {
    var dataDoc = app.open(File(fileSelections.dataFilePath)); // Open the selected data file
    var templateDoc = app.open(File(fileSelections.templateFilePath)); // Open the selected template file
    transferData(dataDoc, templateDoc); // Run the transfer function

    // Save the new document
    var dataFileName = decodeURI(File(fileSelections.dataFilePath).name);

    // To save as 'templateFileName_dataFileName.ai'
    var templateFileName = decodeURI(
      File(fileSelections.templateFilePath).name
    );
    var newFileName = templateFileName.replace('.ai', '') + '_' + dataFileName;

    // // To save as 'dataFileName.ai_Output'
    // var newFileName = dataFileName.replace('.ai', '') + '_Output';

    var saveFile = new File(fileSelections.outputPath + '/' + newFileName);
    templateDoc.saveAs(saveFile);

    templateDoc.close();
    dataDoc.close();
    alert('Data successfully transferred and file saved as: \n' + newFileName);
  }
}

main();

Votes

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
Explorer ,
May 14, 2024 May 14, 2024

Copy link to clipboard

Copied

Thanks, this is closer to what I was looking. But I noticed that the script didn't work efficiently. Some data didn't transfer and some came wrong. For example. in the Template, there are multiple text frames for tag-names like Brand, Guarantee, etc. Text-frames for 'BOP_Text[cs]', and 'Brand' didn't get the new copy, whereas, the text for 'Guarantee' came in correct. The other placeholder text-frame for 'Barcode' get wrong text.

Here are the Before and After snapshots for your review, and thanks in advance for all your cooperation.



Votes

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
Explorer ,
May 15, 2024 May 15, 2024

Copy link to clipboard

Copied

Any suggestions, please

Votes

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 ,
May 15, 2024 May 15, 2024

Copy link to clipboard

Copied

If I had to guess, some of the "tags" in one file aren't in the other. Any change you can also share the template Ai file?

Votes

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
Explorer ,
May 16, 2024 May 16, 2024

Copy link to clipboard

Copied

Here's the link to the data and template ai files. both the files might contains additional layers, so that should be ignored. The source text is on the 'excel data' layer in the data file which will go to the 'artwork' layer in the Template file. The template might contains multiple text-frames for some of the tags, so the text needs to go in all the text-frames.

https://drive.google.com/drive/folders/13LzIVJ9oQLLC4jw2jlJ4fRhYcmRsEzAC?usp=sharing

Votes

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 ,
May 16, 2024 May 16, 2024

Copy link to clipboard

Copied

Okay, just update your `transferData()` function to what I have pasted below and all should work.

// Function to copy data from source to target based on layer names
function transferData(source, target) {
  // Create a dictionary to lookup data from data file
  var dataLayer = source.layers.getByName("excel data");
  var dataLookup = {};
  for (var i = 0; i < dataLayer.textFrames.length; i++) {
    dataLookup[dataLayer.textFrames[i].name] = dataLayer.textFrames[i].contents;
  }

  // iterate over all template text frames
  targetLayer = target.layers.getByName("artwork");
  for (var j = 0; j < targetLayer.textFrames.length; j++) {
    for (var prop in dataLookup) {
      if (prop === targetLayer.textFrames[j].name) {
        targetLayer.textFrames[j].contents = dataLookup[prop];
      }
    }
  }
}

 

Votes

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
Explorer ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

Thanks, it worked, however there's some glitch. As I mentioned in my initial post that the layer order is not fixed in the data and template file. There could be additional or less layers in both the files. So, the script should copy the text from the data file and paste it into the relevant text-frames in the Template file irrespective of how many times the text-frames appear in the Template file.

The revised script you shared did copy the text in the multiple text frames. This is happening because the layer order in both the 'data3.ai' and 'Temp5.ai' is same. I kept this intentionally, to proceed step by step in the development of the script.

 

However, when I tried with the other data file namely 'data-fileD1.ai' and Template files namely 'TemplateD1_Single.ai', 'TemplateD1_Single.ai', and 'TemplateD3_Multi.ai', I get zero output. Please see here: https://drive.google.com/drive/folders/12sN0f4Xf67Q9eD92P5yOvVHGf5iXZHVH?usp=sharing

 

Acutually, I tried to minimize the data transfer by simplifying the data-file. The original data file looks like: '5059340881553.ai'. where the first & third columns of text contains the tag-names and the second & fourth columns of text contains actual text associated with the tag-names. So in order to write a complex script for this, I prefer deleting the unnecessary columns etc.

 

Once this is achieved, I also look forward to call/place the images in the template ai file. The data-file contains eps/ai/jpg image names as data.

 

I have copied another set of test files on the drive, can you please look again and help me sort out my problem.

 

Thanks in advance.

-Masood

Votes

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
Explorer ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

I also noticed, that if I'm creating a new text-frame, and putting a placeholder in it with some tag-name, and also renaming the item in the layer's panel with the same tag-name, then after runing the script, no data is coming in that text-frame. However, if I'm duplicating the existing text-frame with placeholder and putting a different placeholder, then data is successfully coming in that text-frame. Any suggestions, what is wrong where.

Votes

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 ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

As for adding a new textFrame, that works fine for me. As you can see in the demo screen recording below, I add a new textFrame to the template file, change it's name "tag" to one that is found in the datafile, save the template, run the script, and the textFrame is updated correctly. Am I not understanding what you are doing? 

demo.gif

Votes

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 ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

So, the updated snippet I shared should work no matter the layer order from one file to the other.

 

The script simply goes through the data file and builds a dictionary of every textFrame. The dictionary keys are the "tag" names and the dictionary values are the corresponding text content from the textFrame. The script then goes through every textFrame inside of the template file and checks within the dictionary to see if there is a matching "tag". If there is a match, the text contents of the matching textFrame are updated to text from the data file.

 

If the script encounters a "tag" (textFrame name) inside of the template file that doesn't correspond to a key in the dictionary it just skips it. This means you "tags" must be identical (including letter case) between files but the actual order doesn't matter.

 

Now, if you have a different data file layout, this script will not work as it was designed to work with the initial files you provided.

Votes

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
Explorer ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

Thanks, for looking into it, however, I'm experiencing something different, Please see the latest OUTPUT file here:
https://drive.google.com/drive/folders/15cPTmTC-u5dkYk6TOm68y6WZC8sOiCXq?usp=drive_link

I have encircled the areas where the data didn't get transferred.

Votes

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 ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

Turns out some of your template textFrames don't have the name property set and that is why the script is missing them. To fix it, I also check the textFrame contents to see if they match any of the frames from the data file. The example you just provided works for me know with the updated `transferData()` function below.

// Function to copy data from source to target based on layer names
function transferData(source, target) {
  // Create a dictionary to lookup data from data file
  var dataLayer = source.layers.getByName("excel data");
  var dataLookup = {};
  for (var i = 0; i < dataLayer.textFrames.length; i++) {
    dataLookup[dataLayer.textFrames[i].name] = dataLayer.textFrames[i].contents;
  }

  // iterate over all template text frames
  targetLayer = target.layers.getByName("artwork");
  for (var j = 0; j < targetLayer.textFrames.length; j++) {
    for (var prop in dataLookup) {
      if (
        prop === targetLayer.textFrames[j].name ||
        prop === targetLayer.textFrames[j].contents
      ) {
        targetLayer.textFrames[j].contents = dataLookup[prop];
      }
    }
  }
}

 

Votes

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
Explorer ,
May 17, 2024 May 17, 2024

Copy link to clipboard

Copied

LATEST

Thanks, it worked. I'll test it further in a day or two and let you know if there's anything. Happy Weekend 🙂

Could you please let me know where I'll find the name property:
"textFrames don't have the name property set"

Votes

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