Exploring Link.relink() function in Javascript and COM
Some days ago I needed to change my automation tool written in C# as Windows application. The reason was that we use Dropbox now and all pages opened on another computer have a whole bunch of missing links due to different Dropbox folder locations.
Updating links in Javascript is easy and if I do it from Indesign I use following script:
var alllinks = app.activeDocument.links;
for (var i = 0; i < alllinks.length; i++)
{
var cur_link = alllinks[i];
{
var m = cur_link.filePath.match(/.+Dropbox\\(.+)/);
if (m)
{
try
{
cur_link.relink(File('W:\\' + m[1]));
}
catch (e)
{ }
}
}
}
where W: is my Dropbox folder mapped as drive letter.
But as I tried to implement the same logic in my application using COM access
var allLinks = Doc.Links;
for (int i = 1; i <= allLinks.Count; i++)
{
Link l = allLinks[i];
if (l.Status == idLinkStatus.idLinkMissing)
{
string lp = (string) l.FilePath;
if (lp.IndexOf("Dropbox", StringComparison.Ordinal) > -1)
try
{
l.Relink(Regex.Replace(lp, ".+Dropbox", "W:"));
}
catch (COMException)
{
// ignored
}
}
}
I found that it doesn't work - relink function doesn't accept the new filename as string.
So I decided to look a little closer at the Javascript relink() function.
DOM defines it as following:
Link.relink (to:varies)
Adobe InDesign 2020 (15.0) Object Model
Points the link to a new source file.
to: Data Type: varies
The full path name of the new source file. Can accept: File or String.
In case of File it works perfectly and I am sure all of you use it in your scripts:
relink(File(newfilename));
But what about String?
I made a test page with missing image (d:\test\missing.jpg) and a test script:
var alllinks = app.activeDocument.links;
var newlink = "d:\\test\\test.jpg";
for (var i = 0; i < alllinks.length; i++)
{
var cur_link = alllinks[i];
$.writeln(cur_link.filePath + ' - ' + cur_link.status.toString());
if (cur_link.status == LinkStatus.LINK_MISSING)
{
try
{
cur_link.relink(newlink);
$.writeln(cur_link.filePath + ' - ' + cur_link.status.toString());
}
catch (e)
{ }
}
}
where I used a filename string as argument for relink().
Result:
d:\test\missing.jpg - LINK_MISSING
The function shows no error but does nothing and just goes to next loop iteration.
Changing the line
cur_link.relink(newlink);
to
cur_link.relink(File(newlink));
I got a working function:
d:\test\missing.jpg - LINK_MISSING
D:\test\test.jpg - NORMAL
So what does Adobe mean with the String and how could I get the right string in C#?
After some tests I have found the answer!
DOM should define the function argument as following:
The full path name of the new source file. Can accept: File or Uri String.
Indeed, if I change the filename string to Uri
cur_link.relink('file:' + newlink);
the script is working again:
d:\test\missing.jpg - LINK_MISSING
d:\test\test.jpg - NORMAL
Now I can use it in my C# code:
l.Relink($"file:{Regex.Replace(lp, ".+Dropbox", "W:")}");
Is the problem solved now? No.
The above solution works only if the new filename/path has no spaces.
As I deal with German filenames, it means
The above solution works only if the new filename/path has no umlauts.
So it wasn't a solution at all.
I needed to convert the new path to a valid Uri string.
That wasn't too complicate. In Javascript:
cur_link.relink('file:' + encodeURI(newlink));
In C#:
l.Relink($"file:{Uri.EscapeUriString(Regex.Replace(lp, ".+Dropbox", "W:"))}");
Result:
d:\test\missing.jpg - LINK_MISSING
d:\test\test.jpg - NORMAL
I added another missing image to my test page to test the solution in a loop.
Result:
d:\test\missing.jpg - LINK_MISSING
d:\test\test.jpg - NORMAL
d:\test\missing.jpg - LINK_MISSING
d:\test\test.jpg - NORMAL
Victory! 🙂
To make my research complete, I tested the new reinitLink() function, which is stated as experimental.
cur_link.reinitLink('file:' + encodeURI(newlink));
Result:
d:\test\missing.jpg - LINK_MISSING
d:\test\test.jpg - LINK_OUT_OF_DATE
d:\test\test.jpg - LINK_OUT_OF_DATE
So, this function replaces link paths but doesn't update links.
I hope, this information might be useful. 🙂
