Skip to main content
Aprking
Inspiring
April 4, 2024
Answered

Why is manual link folder replacement faster than script in InDesign?

  • April 4, 2024
  • 11 replies
  • 7476 views

Hello everyone,

I have a question to ask. When I use this script to change the target folder of links, if there are many linked files, the entire script takes a long time to complete. However, if I rename the source directory first and then use InDesign's Links panel to replace the target folder for finding missing files, this method is much faster, only a fraction of the time it takes for the script to run.

What could be the reason for this significant difference in speed?

Is there any way to optimize this script?
Thank you all.

 

var originalFolderPath = "F:/source";
var targetFolderPath = "e:/target";

var doc = app.activeDocument;
var links = doc.links;
var updatedLinksCount = 0;
for (var i = 0; i < links.length; i++) {
    var link = links[i];
    var originalLinkPath = link.filePath;
    if (originalLinkPath.indexOf(originalFolderPath) === 0 || originalLinkPath.indexOf(originalFolderPath.replace(/\//g, "\\")) === 0) {
        var originalFileName = originalLinkPath.substring(originalLinkPath.lastIndexOf('/') + 1);
        if (originalFileName == originalLinkPath) {
            originalFileName = originalLinkPath.substring(originalLinkPath.lastIndexOf('\\') + 1);
        }    
        var targetFilePath = targetFolderPath + "/" + originalFileName; 
        //$.writeln("Target File Path: " + targetFilePath);
        var targetFile = new File(targetFilePath);
        if (targetFile.exists) {
            link.relink(targetFile);
            updatedLinksCount++;
        }
    }
}

if (updatedLinksCount > 0) {
    alert("Changed: " + updatedLinksCount + " links to files with the same names in the target folder!");
} else {
    alert("No links to update found!");
}

 

This topic has been closed for replies.
Correct answer rob day

Local hard drive, of course。

This is my test data: 20page×40links/4.52s

50pagex40links/14.752s

100pagex40links / 32.159s

365pagex40links/835.581s

The key is the number of links, but the processing time increases exponentially when it reaches a threshold.


Maybe try upating the links by page so you avoid such large arrays. This was 6x faster for me on a doc with 40 links per page:

 

vs. all doc links

 

 

var st = new Date().getTime ()
var targetFolderPath = "~/Desktop/target/"

var p = app.activeDocument.pages.everyItem().getElements()
var updatedLinksCount = 0;
var pg, targetFile;
for (var i = 0; i < p.length; i++){
    pg = p[i].allGraphics
    for (var j = 0; j < pg.length; j++){
        if (pg[j].itemLink != null) {
            targetFile = File (targetFolderPath +  pg[j].itemLink.name);
            if (targetFile.exists) {
                pg[j].itemLink.relink(targetFile);
                updatedLinksCount++;
            }
        } 
    };   
};   
var et = new Date().getTime () 
alert((et-st)/1000 + ": Seconds")

11 replies

m1b
Community Expert
Community Expert
April 9, 2024

@Aprking this is nothing you don't know, but I realised we have not explicitly answered your main question:

> Why is manual link folder replacement faster than script in InDesign?

 

The unsatisfying answer is: native features are always faster, in terms of raw speed, than going via the scripting API, which introduces performance overhead, sometimes quite significantly. The "manual link folder replacement" you mention relies on a native update links feature so it is faster.

 

We have given the topic a good workout on this thread though.

- Mark

Aprking
AprkingAuthor
Inspiring
April 9, 2024

From my testing, I found that using UndoModes, regardless of the parameter value, significantly increases processing time. Therefore, I have no choice but to abandon its usage.

Regarding the topic discussed by @m1b , "Why is manual link folder replacement faster than script in InDesign?", we have successfully reduced the processing time of the script compared to the native functionality. So, we should express our gratitude for your participation and assistance.

m1b
Community Expert
Community Expert
April 9, 2024

Hi @Aprking

> From my testing, I found that using UndoModes, regardless of the parameter value, significantly increases processing time.

 

Do you mean that you don't use app.doScript? Sorry that I'm not understanding.

 

we have successfully reduced the processing time of the script compared to the native functionality

 

Oh that's great news! After you post about it taking 9724.496 seconds to execute, I was worried that on the real file it still wasn't fast enough.

- Mark

m1b
Community Expert
Community Expert
April 9, 2024

Hi @Aprking and everyone,

 

Well, I'll mention this because it hasn't been discussed, and I'm not sure if it's sensible or not, but I just did a proof-of-concept test of editing the links as plain text via .idml file. I'm using MacOS so you will have to replace command line tools with Windows equivalents (eg. 7zip?).

 

Disclaimer: I know hardly anything about IDML files! What worked for me may not work in your case for reasons I didn't consider, so, for example, in step 4 your files might be differently-named or -structured. Thanks to @Peter Kahrel for teaching me recently that IDML files were .zip files.

 

What I did:

1. export demo.idml

2. make a folder called "expanded" in the same directory as 1.

3. decompress idml contents into the "expanded" folder—in a terminal: unzip demo.idml -d expanded

4. opened "expanded/Spreads/Spread_uce.xml" in VSCode (is this always the same file?)

5. performed find/replace targeting the link file paths (this would probably match the string replacement you did in your script).

6. save and close Spread_uce.xml file.

7. re-compress the .idml file—in terminal again: cd expanded; ls | zip -@ -r ../demo2.idml (be careful with this step, the re-zipped file must have the exact same structure as the original, eg. the default behaviour of zip is to compress with a root directory—which we don't want, and Indesign won't open an IDML with an unexpected structure).

8. open demo2.idml (created in step 7).

9. the links paths will be now changed but they won't be updated (similar to using reinitLink), so you must ask Indesign to do that in one operation.

 

So that's the idea—good or bad. It rests on the fact that VSCode is very fast at performing the find/replace. However step 9 (updating modified links) will be slow no matter what, so ultimately this may not be so fast for you. Also if this is something you want to fully automate, then this is probably too much of a stretch. Still it adds to the discussion. 🙂

- Mark

Aprking
AprkingAuthor
Inspiring
April 9, 2024

 

I tried using the method of editing .idml files five or six years ago, and it has two issues: Firstly, saving as an idml file takes a long time, and secondly, opening the idml file after editing takes even longer. So, this method might not be the best approach for modifying links.

Editing idml files is typically applied in cases where third-party plugins are used, and some hidden characters or links cannot be modified. In such cases, editing idml files is a good method.

Additionally, if the code we're discussing includes:

app.doScript(main, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.FAST_ENTIRE_SCRIPT, "Update Links");

This code will also significantly increase the processing time. Therefore, when dealing with a large number of link changes, I tend to avoid using UndoModes.

m1b
Community Expert
Community Expert
April 9, 2024

Ah yes, of course. I should have considered the huge amount of time to generate the idml, etc. etc. Oh well. Thanks for letting me know @Aprking.

 

I'm quite curious about your comment regarding avoiding using UndoModes. Are you actually saying that the script is faster by avoiding using app.doScript? That seems plausible. Or are you specifically saying that UndoModes.FAST_ENTIRE_SCRIPT is slower than UndoModes.ENTIRE_SCRIPT? That would surprise me!

- Mark

Legend
April 7, 2024

Calling your script from another script with an appropriate undo mode might be worth a test.

P.

 

===

It doesn't need to another script, just the function.

Aprking
AprkingAuthor
Inspiring
April 7, 2024

 

Final statistical report: Document size 752,016KB, 365 pages, with 40 links per page, totaling 14,595 links. As per guidance from @Peter Kahrel , among these, 729 are actual links, while the remaining 13,866 are pointers.

=======================================================================

Using @m1b  method, due to the ineffectiveness of directly using link.update() in the code, the reinitLink method was first employed to change the links, taking 26.133s. Then, updating the links using link.update() in a subsequent loop took 295.888s, totaling 322.021s, occupying 1GB of disk space. Since direct use of link.update() in the code wasn't possible, the method had to be abandoned after two script executions. Although this method was fast, I couldn't fully grasp the technical details, rendering it impractical.

=======================================================================

I employed a method of splitting links.length:

var links = doc.links.everyItem().getElements();
var batchSize = 100; 
var totalLinks = links.length;
for (var i = 0; i < totalLinks; i += batchSize) {
    var batchLinks = [];
    var endIndex = Math.min(i + batchSize, totalLinks);

    for (var j = i; j < endIndex; j++) {
        batchLinks.push(links[j]);
    }
    
    for (var k = 0; k < batchLinks.length; k++) {
        var link = batchLinks[k];

This method took 275.817s and occupied approximately 1GB of disk space.

=======================================================================

Using @rob day  code:

var p = app.activeDocument.pages.everyItem().getElements()

This method successfully reduced the processing time from over an hour to 280.275s and occupied approximately 1.1GB of disk space.

=======================================================================

@rob day  solution had the shortest processing time and minimal disk space usage, making it the champion and the most technically superior solution.

=======================================================================

My solution is experimental and requires extensive testing to ensure there are no bugs. However, its advantage lies in its flexibility in splitting links.length and adjusting batchSize according to different document requirements.

=======================================================================

In conclusion, the three solutions differ mainly in specific details, but their techniques are similar, all involving dividing the entire document into smaller chunks for processing. The observed differences in processing time are not stable and should be taken as reference only.

Once again, I extend my gratitude to @rob day @m1b @Robert at ID-Tasker @leo.r  and @Peter Kahrel  for their guidance and participation.

Thank you.

Peter Kahrel
Community Expert
Community Expert
April 7, 2024

Here's another thing that might speed up things: A script can open a document without showing it. This makes a difference because InDesign's screen writes are disabled (among other things). Use this code:

 

 

// Close all documents, then:

var doc = app.open (myIndd, false);

// Now work on doc

 

Aprking
AprkingAuthor
Inspiring
April 7, 2024

I have already attempted this method in another script called "InDesign Batch Export PDF". It noticeably improves speed but also has limitations, such as the inability to use Preflight Check.

Aprking
AprkingAuthor
Inspiring
April 6, 2024

@rob day @Robert at ID-Tasker @leo.r  @m1b @Peter Kahrel This topic may be the most lively in the InDesign community recently. Thanks to all the teachers for their participation. I will test various solutions promptly and provide detailed test data.

m1b
Community Expert
Community Expert
April 6, 2024

Thanks @Aprking, I'm interested in hearing your results!

Peter Kahrel
Community Expert
Community Expert
April 5, 2024

the processing time increases exponentially when it reaches a threshold

 

I've seen that in other areas as well, e.g. adding hyperlinks. After creating about 500 links InDesign starts to slow down dramatically. There's not really very much that you can do about it.

Robert at ID-Tasker
Legend
April 6, 2024
quote

the processing time increases exponentially when it reaches a threshold

 

I've seen that in other areas as well, e.g. adding hyperlinks. After creating about 500 links InDesign starts to slow down dramatically. There's not really very much that you can do about it.


By @Peter Kahrel

 

That's because of the UNDO history.

 

But there is a way - save to a new file after processing a set number of operations - let's say after every 200 operations - or close to whatever bottleneck you'll discover.

 

@Aprking 

 

Modify your code to save your INDD file to a new file with a new name every page or few pages - when you switch to processing links by pages - you'll purge UNDO history and you won't have exponential slowdown.

 

Robert at ID-Tasker
Legend
April 6, 2024
quote

 

@Aprking 

 

Modify your code to save your INDD file to a new file with a new name every page or few pages - when you switch to processing links by pages - you'll purge UNDO history and you won't have exponential slowdown.

 

Extra bonus - if it crashed on say page 9 - you don't have to process again first 8 pages - you can just open 8th file and start proccessing from page 9.

 

leo.r
Community Expert
Community Expert
April 5, 2024

@Aprking :

> What could be the reason for this significant difference in speed?

 

Scripts will always be slower for these kind of operations than native InDesign actions (as others also mentioned). To match the speed of InDesign you'll need to write a C++ plug-in (while, of course, all the great suggestions for script optimization you received here can help speed it up).

Aprking
AprkingAuthor
Inspiring
April 5, 2024

I have tested it countless times. When the document is under 100 pages, the processing time still increases linearly, typically ranging from a few seconds to a few tens of seconds. However, when the page count exceeds 300 pages and the number of links exceeds 12,000, the processing time increases exponentially. It usually takes several tens of minutes or even over an hour. The script optimizations provided by everyone can only reduce the processing time by a few seconds. I still haven't found the reason for this. Nevertheless, I appreciate everyone's help.

rob day
Community Expert
Community Expert
April 5, 2024

Please copy and paste the number of pages in the document you're testing to 300 pages and then test it again.

 

320 pages took 15sec with this code and the target folder on my startup’s desktop folder. I’m using this code:

 

var targetFolderPath = "~/Desktop/target/" 
var links = app.activeDocument.links.everyItem().getElements();
var updatedLinksCount = 0;
var targetFile;
for (var i = 0; i < links.length; i++) {
    targetFile = File (targetFolderPath +  links[i].name);
    if (targetFile.exists) {
        //links[i].parent.place(targetFile)
        links[i].relink(targetFile);
        updatedLinksCount++;
    }
}  
Peter Kahrel
Community Expert
Community Expert
April 5, 2024

And some changes in the code to reduce the number of actions. If this code doesn't execute faster, at least it reads quicker 🙂

 

 

// Add the slash to the path names

var originalFolderPath = "F:/source/";
var targetFolderPath = "f:/target/";

// Work on arrays as much as possible, 
// collections process much slower

var links = app.activeDocument.links.everyItem().getElements();

var updatedLinksCount = 0;
var targetFile;
var regex = /.+[\\\/]/;  // Precompile regular expressions
for (var i = 0; i < links.length; i++) {
  targetFile = File (targetFolderPath + links[i].filePath.replace(regex,''));
  if (targetFile.exists) {
    links[i].relink(targetFile);
    updatedLinksCount++;
  }
}

 

 

Aprking
AprkingAuthor
Inspiring
April 5, 2024

After actual execution, the original code's Execution Time is 32.037s, while this code's Execution Time is 29.917s. However, there's an issue: this code replaces links not belonging to originalFolderPath with targetFolderPath. This is a bug, and the fast speed is probably due to the lack of filtering based on originalFolderPath.

Peter Kahrel
Community Expert
Community Expert
April 5, 2024

The first step to speed up the script is to change this line:

var links = doc.links;

to

var links = doc.links.everyItem().getElements();
Aprking
AprkingAuthor
Inspiring
April 5, 2024

@Peter Kahrel Thank you for your participation and response.
After actual testing:

This code when testing a 100-page document:
var links = doc.links.everyItem().getElements(); // Execution Time: 90.085s
This code when testing a 100-page document:
var links = doc.links; // Execution Time: 66.253s
This code when testing a 360-page document:
var links = doc.links; // Execution Time: 5956.596s

The difference is astonishing; too many pages and links result in disproportionate time expenses.
Note: The content and number of links are almost the same on each page.

Robert at ID-Tasker
Legend
April 5, 2024

@Peter Kahrel , @rob day - would you like to pitch in?