Skip to main content
dersenn
Participating Frequently
Oct 25, 2022 2:12 PM UTC
Question

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

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 is closed to new replies. Start a new post to keep the conversation going.

43 replies

dersenn
dersennAuthor
Participating Frequently
Sep 3, 2024 9:32 AM UTC

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!

Translate
m1b
Community Expert
Community Expert
Nov 4, 2022 12:54 AM UTC

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

Translate
dersenn
dersennAuthor
Participating Frequently
Nov 4, 2022 8:57 AM UTC

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!

Translate
rob day
Community Expert
Community Expert
Nov 4, 2022 1:09 PM UTC

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)
            }
        }
    };   
}

 

 

 

Translate
Legend
Oct 26, 2022 10:14 PM UTC

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];

 

Translate
dersenn
dersennAuthor
Participating Frequently
Oct 27, 2022 12:35 PM UTC

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

Translate
Participating Frequently
Oct 26, 2022 7:53 PM UTC

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. 
Translate
dersenn
dersennAuthor
Participating Frequently
Oct 27, 2022 12:33 PM UTC

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.

Translate
dersenn
dersennAuthor
Participating Frequently
Nov 3, 2022 1:23 PM UTC

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;
}

 

Translate
rob day
Community Expert
Community Expert
Oct 25, 2022 4:03 PM UTC

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)

 

Translate
dersenn
dersennAuthor
Participating Frequently
Oct 25, 2022 4:25 PM UTC

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...

Translate
rob day
Community Expert
Community Expert
Oct 25, 2022 4:57 PM UTC

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
    }
  ]
}

 

 

 

 

Translate
Robert at ID-Tasker
Legend
Oct 25, 2022 2:26 PM UTC

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. 

Translate
dersenn
dersennAuthor
Participating Frequently
Oct 25, 2022 3:16 PM UTC

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...

Translate
Robert at ID-Tasker
Legend
Oct 25, 2022 4:05 PM UTC

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 😞

Translate