Copy link to clipboard
Copied
Hi,
We've got a PDF that needs distribution to customers, and we'd like to include a CSV file as an attachment and also update one link in the PDF to open the attachment (by default, this link in the PDF is a web link to an external version of the CSV file that lives in the same directory as the PDF).
As this PDF gets rebuilt often, I'd like to be able to make a one button action/command to attach the CSV file and then update the link to open the attached file instead of a web link to the external file.
Part one I have managed to do easily enough. However, I can't work out how I could achieve the following with an Action/Command in Acrobat:
1. Find the link text in the PDF (my.csv)
2. Automatically change the link action from "Open a web link: my.csv" to
Run a Javascript:
/* Launch attached CSV file */
this.exportDataObject({cName: "my.csv", nLaunch: 2});
So, that little bit of JavaScript above does exactly what I want it to do, I just want to be able to click an Action/Command button and find the text and change the existing link to behave like in step two. It feels like I should be able to change the link behavior with JavaScript, but I am really struggling to work out how!
Any help, much appreciated.
Copy link to clipboard
Copied
You can override the link's action with a script. The problem is, you can't know what the original action was.
So if you want to convert all links in the file to this new action you can do it quite easily, but if you want to check first that they actually point to my.csv (and not to another file, or a web-page, etc.), then you will need an external tool to do it.
Copy link to clipboard
Copied
You can find all the links on a page with the "doc.getLinks()" function. Here's the reference entry (it includes an example):
https://opensource.adobe.com/dc-acrobat-sdk-docs/library/jsapiref/doc.html#getlinks
Each link object has a "setAction" command that sets the JavaScript action.
Finding the text under the link in order to verify the correct link is a more difficult issue. Once you have a link object, you can get it's rectangle. So it's a matter of searching the page words for any that are inside the link rectangle.
Use the doc.getPageNthWordQuads() function to find the quads associated with each word on the page. Some geometric calculations will be necessary to determine which quad is under the link rectangle.
Copy link to clipboard
Copied
Thank you both. The link that @Thom Parker provided actually had almost the exact code I needed. I setup our authoring software to not include any kind of link, so it just outputs plain text, and this scripts finds the text and adds a square link around it.
This works fine for something like "myfile", but ideally, I'd like the text in the PDF to look like "my_file.csv". However, if I set the script below to add a link to the text "my_file.csv" (ckWord == "my_file.csv") nothing happens. Based on what I read about the getPageNthWord function, maybe the function is breaking "my""_"file""." and "csv" into separate words, so won't find them. Any quick way to sort that issue?
Also, there will literally only ever be onone instance of this text in the PDF, I've made a rather rudimentary break once it has found the string it is looking for, but I'm wondering if there is a more efficient way of doing it. Currently the script takes about 30 seconds to run when the strict is about half way through an 88 page PDF.
for (var p = 0; p < this.numPages; p++) {
var numWords = this.getPageNumWords(p);
var foundit = 0;
for (var i = 0; i < numWords; i++) {
var ckWord = this.getPageNthWord(p, i, true);
if (ckWord == "portslist") {
var q = this.getPageNthWordQuads(p, i);
// Convert quads in default user space to rotated
// User space used by Links.
var m = (new Matrix2D).fromRotated(this, p);
var mInv = m.invert();
var r = mInv.transform(q);
r = r.toString();
r = r.split(",");
var l = addLink(p, [r[4], r[5], r[2], r[3]]);
l.borderColor = color.blue;
l.borderWidth = 1;
l.setAction("this.exportDataObject({cName: 'portslist.csv', nLaunch: 2})");
foundit = 1;
break; // Exit the inner loop
}
}
if (foundit == 1) {
break; // Exit the outer loop
}
}
Copy link to clipboard
Copied
The "getPageNthWord()" function returns a single word. In the Acrobat JS model a single word is any continous seqence of Alphnumeric characters. The word can also includes a single trailing non-alphnumeric character, i.e., punctuation or white space. So "my_file.csv" is actually two words. To catch this case the code would need to capture two words. But it can get much more complicated. What if there are more then 2 words in an entry in the CSV file? What if the number of words per entry is arbitrary? What if the puntuation and spacing between the CSV and document is different? What if there are differences in capitalization?
One solution is to reduce the csv entry into an array of single lower case words with no punctuation. Then the search starts by looking for the first word in the array. Once found it then tries to match the following words in the array against the following words on the page.