Skip to main content
dersenn
Participating Frequently
October 25, 2022
Question

Customized script to open/edit linked files is not working as expected

  • October 25, 2022
  • 6 replies
  • 4039 views

Dear community
I am at a loss here and can't find a solution. This is part of a script which opens linked InDesign files, changes the layer visibility on the documents placed in the opened links.

 

If I put an alert() somewhere between opening and saving the document, everything works fine. If I remove the alert() I get an error, telling me that the file is already open.  The error points to the last command in the changeLayerVisibility() Function: layer.currentVisibility = checklist[ft][k].checkedState;

 

I'm thinking it has something to do with async or timeouts. The alert slowing the process, allowing the loop to finish. But I can't understand why it would ever try to open the same file again...

 

Here's the relevant part of the script. I hope someone sees where I'm going wrong. If not I can try to create a reduced version of the thing.

 

function changeNestedLayers(linksArr) {
  var thisLink,
      thisFile,
      thisFileName,
      thisDoc;

  for (var l = 0; l < linksArr.length; l++) {
    thisLink = linksArr[l];
    thisFile = File(thisLink.filePath);
    thisFileName = thisFile.fullName;
    thisDoc = app.open(thisFile, true);

    var currentLink, 
        currentLinkname,
        currentLayer,
        currentChecklistItem;

    for(var s = 0; s < thisDoc.links.length; s++) {
      currentLink = thisDoc.links[s];
      currentLinkname = currentLink.parent.itemLink.name;

      if (currentLink.parent.constructor.name == "ImportedPage" && currentLink.parent.itemLink != null && currentLink.parent.itemLink.linkType == "InDesign Format Name") {

        if(hasSubstring(currentLinkname, "FiletypeA")) {
          changeLayerVisibility(currentLink, "FiletypeA");
          count++;
        }
        else {
          changeLayerVisibility(currentLink, "FiletypeB");
          count++;
        }
      }
    }

    thisDoc.save(new File(thisFileName));
    thisDoc.close(SaveOptions.NO);

  }
}

function changeLayerVisibility(thisLink, filetype) {
  var ft = filetype.toLowerCase();
  var layer;
  for (var k = 0; k < layertypes[ft].length; k++) {
    layer = thisLink.parent.graphicLayerOptions.graphicLayers.item(layertypes[ft][k].name);
    if (layer.isValid) {
      layer.currentVisibility = checklist[ft][k].checkedState;
    }
  }
}

 

 

Thanks!

This topic has been closed for replies.

6 replies

dersenn
dersennAuthor
Participating Frequently
September 3, 2024

Hello everybody. In case anybody is still watching this. Last week I came across this script again and thought I'd give it another try as some time has passed and InDesign has been updated a few times. And it worked! Albeit just with color profile warnings on open turned on (which I had on lately). Turning it off again brought back the warning about the doc being already open. Unfortunately it doesn't even work by setting "app.colorSettings.mismatchAskWhenOpening" temporarily to "true" in the script. So, not really a solution, but another direction to look into. But I'm suspecting unless InDesign adresses this bug, we're out of luck. Thanks everybody!

m1b
Braniac
November 4, 2022

Hi @dersenn, I had a bit of a look into this issue and there are some complications including a bug in Indesign. Have you had a look at this answer? It includes a great script by crazyPanda and also a bug report noted by @Laubender.

- Mark

dersenn
dersennAuthor
Participating Frequently
November 4, 2022

Hey @m1b, thanks for looking into this. I'm pretty sure I was looking into the thread above and crazyPandas script when I did the first Versions of the script (for direct links). In the end I ended up with a modified Version for Kasyans batch processor and eventually wrote a simplified standalone version. But it has been a few years.
I will look into that thread, script and bug report in detail. Thanks!

rob day
Braniac
November 4, 2022

I can see the bug when I recursively open the linked ID files. With this I get 6 documents reported when only 3 are open:

 

 

var doc = app.documents[0]
getImported(doc);
alert("There are " +app.documents.length + " documents open")



/**
* Gets imported ID layers 
* @ param the document 
* @ return value 
* 
*/
function getImported(d){
    var ld, ldl, fp, fn;
    var p = d.rectangles.everyItem().importedPages.everyItem().getElements();

    for (var i = 0; i < p.length; i++){
        fp = p[i].parent.allGraphics[0].itemLink.filePath;
        fn = p[i].parent.allGraphics[0].itemLink.name;

        ld = app.open(fp);
        if (ld.links.length == 0) {
            ld.close();
        }else{
            ldl = ld.rectangles.everyItem().importedPages.everyItem().getElements();
            if (ldl.length > 0) {
                getImported(ld)
            }
        }
    };   
}

 

 

 

Braniac
October 26, 2022

While the SaveOptions change sounds like the solution, you can solve the problem suspected with the .links collection by iterating a copy of that collection, produced by the getElements() method.

    var thisDocLinks = thisDoc.links.getElements();
    for(var s = 0; s < thisDocLinks.length; s++) {
      currentLink = thisDocLinks[s];

 

dersenn
dersennAuthor
Participating Frequently
October 27, 2022

This could be something. I'll try it later.

Participating Frequently
October 26, 2022

replace:

 

thisDoc.save(new File(thisFileName));
thisDoc.close(SaveOptions.NO);
 
with:
 
thisDoc.close(SaveOptions.YES);
 
thisDoc is already a document reference.
There's no need to save to a new file... even because thisFileName is the same parent file of the documentthisDoc. 
dersenn
dersennAuthor
Participating Frequently
October 27, 2022

Thanks for this input. Although I doubt it is the culprit. The error happens before that and it doesn't even get to the saving part.

 

A little background on why it is saved as a new file with the same name. This is a solution I came across for another script (which updates nested links). Nowadays InDesign gets updated fairly regularly, so files that have not been touched for a few weeks/months get opened as a copy and you need to save them again. You've certainly done this and you need to overwrite the file. Since they get opened as a copy ([converted]), they don't have their original filename and path information. That's why this gets stored before the file is opened.

dersenn
dersennAuthor
Participating Frequently
November 3, 2022

That might be good. Can you share the simplified file and script that isn't working? It'll be quicker for us to troubleshoot. 
- Mark


Ok. So here is the simplified (well, as simple as possible) example. Open the "Overview.indd" and run the script on that file. Which should open the "Product" files and change layer visibility on placed files therein.

 

Right now apparently it only creates an error when there's a difference. So you need to uncheck something in the Dialog.

 

And here's the script, thanks for any help!

// object containing the layers we have in the different kinds of files.
var layertypes = {
    techinfo: [
      {name: "Container", showDefault: true},
      {name: "Information", showDefault: true}
      ],
    layout: [
      {name: "Badge", showDefault: true},
      {name: "Artwork", showDefault: true}
      ]
};

// stores the desired visibility.
var checklist = {
    techinfo: [],
    layout: []
    };

var doc,
    settings;

// Collection for the "Product" files we want to open.
var products = [];

var thisLink,
    thisFile,
    thisFileName,
    thisDoc,
    thisDocLinks;

var currentLink, 
    currentLinkname,
    currentLayer,
    currentChecklistItem;

var ft,
    layer;

Main();

function Main() {
  if(app.documents.length == 0){
    alert("No documents are open. Please open a document and try again.");
    exit();
  }
  else {
    doc = app.activeDocument;
    settings = saveSettings();
    app.linkingPreferences.checkLinksAtOpen = false;

    myDialog();

    restoreSettings(settings);
  }
}

// Dialog gets constructe from the layertypes object.
function myDialog() {
  var myDialog = app.dialogs.add({name:"Show / Hide Layers", canCancel: true});
  with(myDialog) {
    with(dialogColumns.add()) {
      with(dialogRows.add()) {
        staticTexts.add({ staticLabel: "Techinfo" });
        for (var i = 0; i < layertypes.techinfo.length; i++) {
          with(dialogRows.add())
            checklist.techinfo.push(checkboxControls.add({ 
              staticLabel: layertypes.techinfo[i].name, 
              checkedState: layertypes.techinfo[i].showDefault 
            }));
        }
      }
    }
    with(dialogColumns.add()) {
      with(dialogRows.add()) {
        staticTexts.add({ staticLabel: "Layout" });
        for (var i = 0; i < layertypes.layout.length; i++) {
          with(dialogRows.add())
            checklist.layout.push(checkboxControls.add({ 
              staticLabel: layertypes.layout[i].name, 
              checkedState: layertypes.layout[i].showDefault 
            }));
        }
      }
    }
  }
  if (myDialog.show() == true) {

    checkLinks(doc.links);
    
    if(products.length > 0) {
      changeNestedLayers(products);
    }

    alert("Done!");
  } else {
    myDialog.destroy();
  }
}

// The problematic function.
function changeNestedLayers(linksArr) {
  for (l = linksArr.length - 1; l >= 0; l--) {

    thisLink = linksArr[l];
    thisFile = File(thisLink.filePath);
    thisFileFullPath = thisFile.fullName;

    thisDoc = app.open(thisFile, true);
    $.writeln("Opened: " + thisFile);


    thisDocLinks = thisDoc.links;

    for(var s = thisDocLinks.length - 1; s >= 0; s--) {
      currentLink = thisDocLinks[s];
      currentLinkname = currentLink.parent.itemLink.name;

      if (currentLink.parent.constructor.name == "ImportedPage" && currentLink.parent.itemLink != null && currentLink.parent.itemLink.linkType == "InDesign Format Name") {

        if(hasSubstring(currentLinkname, "Techinfo")) {
          changeLayerVisibility(currentLink, "Techinfo");
        }
        if(hasSubstring(currentLinkname, "Layout")) {
          changeLayerVisibility(currentLink, "Layout");
        }

      }
    }

    if (thisDoc.converted) {
      thisDoc.save(File(thisFileFullPath))
    } else {
      thisDoc.save()
    }    
    $.writeln("Closing: " + thisDoc.fullName);
    thisDoc.close(SaveOptions.NO);

    if (thisLink.status == LinkStatus.LINK_OUT_OF_DATE) {
      thisLink.update();
    }
  }
}

// the other problematic function.
function changeLayerVisibility(thisLink, filetype) {
  ft = filetype.toLowerCase();
  for (var k = 0; k < layertypes[ft].length; k++) {
    layer = thisLink.parent.graphicLayerOptions.graphicLayers.item(layertypes[ft][k].name);
    if (layer.isValid) {
      $.writeln(layer.currentVisibility);
      $.writeln(checklist[ft][k].checkedState);
      // If the following line is commented out, all goes well.
      // Otherwise error, pointing to line 149. File already open...
      layer.currentVisibility = checklist[ft][k].checkedState;
    }
  }
}

// check and change layer visibility for direct links (Not in use right now).
// push products to separate list for opening (in use).
function checkLinks(linksArr) {
  for (var l = 0; l < linksArr.length; l++) {

    var thisLink = linksArr[l];
    var linkname = thisLink.parent.itemLink.name;

    if (thisLink.parent.constructor.name == "ImportedPage" && thisLink.parent.itemLink != null && thisLink.parent.itemLink.linkType == "InDesign Format Name") {

      if(hasSubstring(linkname, "Product")) {
        products.push(thisLink);
      } 
      else if(hasSubstring(linkname, "Techinfo")) {
        changeLayerVisibility(thisLink, "Techinfo");
      } 
      else if(hasSubstring(linkname, "Layout")) {
        changeLayerVisibility(thisLink, "Layout");
      } 
    }
  }
}

// Utility Functions
function hasSubstring(string, substring) {
  if(string.indexOf(substring) !== -1) return true;
}

function saveSettings() {
  var settingsTemp = {
    checkLinksAtOpen: app.linkingPreferences.checkLinksAtOpen
  };
  return settingsTemp;
}

function restoreSettings(settings) {
  app.linkingPreferences.checkLinksAtOpen = settings.checkLinksAtOpen;
  app.scriptPreferences.userInteractionLevel = UserInteractionLevels.INTERACT_WITH_ALL;
}

 

rob day
Braniac
October 25, 2022

Hi @dersenn , we can’t test your snippet, but have you tried adding some writeIn trace statements to check the file and link names in the debug console?

 

//before the thisDoc.save line
$.writeln(thisDoc.name)

//at the beginning of the changeLayerVisibility function
$.writeln(thisLink.name)

 

dersenn
dersennAuthor
Participating Frequently
October 25, 2022

Hey @rob day , thanks for the input. I haven't tried any writeln statements, since there's no toolkit anymore and I have been a bit reluctant to change from my current editor to Visual Studio... But might be a good moment to give it a try.

 

Still I find it strange that it works perfectly if there simply is an alert somewhere...

rob day
Braniac
October 25, 2022

I would give it a try, the install is a bit tricky but in the end it is better than ESTK. The thing that tripped me up at first was the required .json file at the root of your scripts folder, and the built-in Explorer/Finder. I use this for the launch.json file, but I still don’t fully understand how it works, so someone else might have a better version:

 

{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Session", 
      "type": "extendscript-debug",
      "request": "launch",
      "hostAppSpecifier": "indesign",
      "engineName": "session",
    }
    {
      "name": "Main", 
      "type": "extendscript-debug",
      "request": "launch", 
      "hostAppSpecifier": "indesign",
      "engineName": "main",
    }
  ],
  "compounds": [
    {
      "name": "Session/Main",
      "configurations": ["Session", "Main"],
      "preLaunchTask": "${defaultBuildTask}",
      "stopAll": true
    }
  ]
}

 

 

 

 

Robert at ID-Tasker
Braniac
October 25, 2022

I was using similar approach in one of my tools to build catalogs from database - placing INDD files - but I've switched to PDF instead of INDD file - works quicker.

 

I'm not JS guy so can't help you with your code - but in case of linked files - of any type - you NEED to iterate the collection backwards as every change to the element on the links collection - moves it to the end - looks like InDesign removes original and creates new one - so you'll skip some and process some twice. 

dersenn
dersennAuthor
Participating Frequently
October 25, 2022

Hey AdobeScripts, thanks for your reply.
In general our workflow with the placed INDD files works like charm. And we have a single source of truth which we can export when needed.
Iterating backwards... interesting. I remember rewriting the for loops to my liking when adapting the scripts. I quickly tried reversing the outermost loop. Doesn't change anything though. Also, I'm not iterating over the general links collection, but rather an array of collected links.
And the script works exactly as advertised IF there is any alert() somewhere between opening and saving the file. This is what's really bugging me...

Robert at ID-Tasker
Braniac
October 25, 2022

But you ARE iterating through links collection?? 

 

quote

 

for(var s = 0; s < thisDoc.links.length; s++) {
      currentLink = thisDoc.links[s];

 

 

Yes, you're creating a reference to the item on the links collection - but anything you do to this reference - will modify original object - reference is just for your convenience. 

 

I've had the same problem with the need for alert() in the script I was converting from VBA to JS - script to format Kanji - but I've no idea what was the problem 😞