Acrobat, JavaScript and VB walk into a bar…

OK, let’s just forget about that old joke and concentrate on how to combine all three into something that is quite useful.

As I’ve described in one of my previous posting, it is quite easy to automate Acrobat from VB or VBA. So how does JavaScript fit into this picture? As you may know, Acrobat comes with a very powerful JavaScript engine that provides access to a lot of functionality – more functions actually than what you have access to from your VB program. So, if you want to access some of these features, but you are stuck with VB, how can you do that?

Adobe provides a VB/JavaScript bridge with Acrobat – the JSObject, and the Acrobat SDK describes how to use that feature.

There is quite a bit of good information in the documentation. When you access the online documentation, expand the tree to “Acrobat Interapplication Communication > Developing Applications Using Interapplication Communication > Using OLE > Using the JSObject interface”.

JavaScript

In this example, I want to illustrate how you can create a folder level JavaScript function, instantiate the JSObject, and then call the custom function and display the result in VB. My plan was to use the JavaScript code from my last posting, but I found one small problem in the way I wrote the code (it works fine as a standalone JavaScript program, but we cannot use it in the VB context), so here is it’s replacement:

function CountBookmarks(bkm, nLevel)
{
    var count = 0;
    if (bkm.children != null)
    {
        count = bkm.children.length;
        for (var i = 0; i < bkm.children.length; i++)
        {
             count += CountBookmarks(bkm.children[i], nLevel + 1);
        }
    }

    return count;
}


function CountAllBookmarks()
{
    console.clear(); console.show();
    var n = CountBookmarks(this.bookmarkRoot, 0);
    console.println("Number of bookmarks found: " + n);

    return n;
}

// add the menu item
app.addMenuItem({
     cName: "countBookmarks",
     cUser: "Count Bookmarks",
     cParent: "Document",
     cExec: "CountAllBookmarks();",
     cEnable: "event.rc = (event.target != null);"
});

Save this JavaScript program as a folder level JavaScript file and make sure that it works.

So, why can't we just implement the whole algorithm with the JSObject? The problem is with how VB handles objects that are actually JavaScript objects - in this case the root bookmark object. I cannot figure out how to access it's "children" property through the JSObject. That's the reason why I'm "cheating" by calling our custom JavaScript function - being able to do that is pretty cool IMHO.

The VB Part

We start out just like with any other VB program, by declaring some objects, initializing them and then it gets interesting...

Here is some sample code that shows how to initialize the JSObject, and how to call our own JavaScript function.

Create a button on an Excel spreadsheet again, and put the following code into the button handler callback (just like before).

Dim gApp As Acrobat.CAcroApp
Dim gPDDoc As Acrobat.CAcroPDDoc
Dim jso As Object

Sub Button1_Click()
    Set gApp = CreateObject("AcroExch.App")
    Set gPDDoc = CreateObject("AcroExch.PDDoc")
    If gPDDoc.Open("c:\temp\test.pdf") Then
        Set jso = gPDDoc.GetJSObject
        MsgBox (jso.CountAllBookmarks())
    End If
End Sub

Now just make sure that you have a file c:\temp\test.pdf that has some bookmarks in it.

This entry was posted in Acrobat, JavaScript, PDF, Programming and tagged , , , , , , . Bookmark the permalink.

16 Responses to Acrobat, JavaScript and VB walk into a bar…

  1. pab says:

    khk, I have been looking for how to do this for aa numbe rof hours this last week or so. your posting was really helpful. I tried your code / example… on my windows XP machine its falls over when I step inot the open first pdf line: Part1Document.Open (“C:tempPart1.pdf”)..

    excel closes and the system wants to send a message to microosft.

    any ideas whats wrong?

    the type library is acrobat 8 on my system.

    I checked in the register and AcroExch.PDDoc was there.

    Paul

  2. Ernst says:

    Thanks very much fot the info on Acrobat, JavaScript and VB. How would one go about to do the same with Photoshop, Javascript and VB?

    I have created a VB6 app to copy photo files from mem cards and would like to extend it to include running Javascrips fot Photoshop.

    Thanks.

    Regards.

  3. khk says:

    Ernst,
    you can find information about Photoshop scripting here: http://www.adobe.com/devnet/photoshop/scripting/ and a tutorial for example here http://www.kirupa.com/motiongraphics/ps_scripting.htm
    What exactly are you trying to accomplish with a Photoshop script?

  4. khk says:

    Ah, now I’m getting it, you are asking if Photoshop also has a VB/JavaScript bridge… As far as I know, it does not support the same functionality. You can run JavaScripts via the DoJavaScript and DoJavaScriptFile functions, but it’s not bridged like in Acrobat. Does that help?

  5. Braden Rowe says:

    I just want to say you’re brilliant! Thanks.

  6. Maxim Sannikov says:

    I want to add a different sort of a beast into my vba code – and I’ve created the following function as a folder-level jscript.

    function coder1 (){
    var n = this.addAnnot({page:0,type:”Stamp”,rect:[100,100,350,350],AP:”#kZBRdNJ8NdlGCzWSCKIn3A”});
    return n;
    }

    It doesnt work as intended, and I think it is due to my use of this onject. One solution would be to use a doc type variable as an input, but then how would I pass the doc type object into js from VBA? Any help would be greatly appreciated.

  7. Karl Heinz Kremer says:

    Maxim, the “JSObject” is your document object. You should be able to call “addAnnot()” for the JSObject – you may have to provide all the parameters, even the ones you are not using for this to work. Based on experience, the addAnnot() method is a bit tricky to get to work via VBA. Keep in mind that you can also use the OLE function to add annotations: http://help.adobe.com/en_US/acrobat/acrobat_dc_sdk/2015/HTMLHelp/index.html#t=Acro12_MasterBook%2FIAC_API_OLE_Objects%2FAddAnnot.htm

  8. Maxim Sannikov says:

    I am having trouble passing the AP property from VBA to excel. I have learned how to add a simple text annotation with OLE functions, but I cannot transmit the AP property of the Stamp type annotation.

  9. Karl Heinz Kremer says:

    Maxim, as I said, this is not trivial. Here is some sample code that adds the “Approved” stamp to a document:

        If gPDDoc.Open("c:\temp\test.pdf") Then
            Dim stampRect(3) As Integer
            Dim pageRect As Object ' if you want to base the stamp location on the page size
            Dim annot As Object
            Dim props As Object
    
            Set jso = gPDDoc.GetJSObject
            
            ' Get size for page 0 and setup arrays
            Set Page = gPDDoc.AcquirePage(0)
            Set pageRect = Page.GetSize ' now use pageRect.x and/or pageRect.y for stamp placement
    
            stampRect(0) = 100 ' taking a shortcut here
            stampRect(1) = 100
            stampRect(2) = 200
            stampRect(3) = 200
            
            ' Create a new stamp annot
            Set annot = jso.AddAnnot
            
            ' Get the stamp properties, change the type and then push it to the stamp object again
            Set props = annot.getprops
            props.Type = "Stamp"
            annot.setProps props
            
            ' Fill in the necessary fields
            Set props = annot.getprops
            props.Page = 0
            props.rect = stampRect
            props.Author = "My Name"
            props.AP = "Approved" ' this would be your own AP string - "Approved" uses one of the standard stamps
            annot.setProps props
        End If
    
  10. Sameer Kakkar says:

    Can you please help me to push digital signature to pdf fillable form from excel using vba as i have multiple pdfs forms with signature option and basis on region i have to publish digital signature on those pdfs using vba for excel

  11. Karl Heinz Kremer says:

    Sameer, this is well beyond the scope of what can be done in a reply to a blog post. If you are interested in my professional consulting services, please feel free to get in touch with me via email. My email address is on the About page.

  12. Kiran says:

    Hi

    I tried the code above, it always adds draft stamp,

    Can we add ‘Approved’ stamp

  13. Karl Heinz Kremer says:

    Kiran, this is a bug in Acrobat: You cannot modify the stamp once it is placed, but you also cannot specify the stamp type at the time the stamp is placed. You will have to resort to Javascript to do that, and then call the Javascript from your VBA application.

  14. Rajesh Chawda says:

    KHK,
    I came across your website and few codes for accessing information from a PDF file.
    It’s awesome. The way you explain is superb.
    I am looking for some VBA code to resolve one problem.
    We have some 1000+ invoice copies in PDF format saved in some folder on our computer. Every month, the user has to open each of these pdf invoices (one by one) and insert the Delivery/Manufacturing site address and then save the PDF.
    The delivery address for first 800 invoices is the same (say Address-A) while the address for the balance 200+ invoices is again same (say address-B).
    Doing it manually takes almost one full day (about 9-10 hours).
    I am a novice in Excel VBA coding and absolutely zero in Java.
    However, browsing through the web (and of course your web-site too) I came to understand that this can be automated by writing some Excel VBA code (may be some support of Java script would be required.
    Can you Help pls ?

  15. Mark says:

    Karl,

    This is great! Is there any way to do the same with FoxIt Phantom PDF?
    I’d like to automatically flatten documents using VBA and Phantom PDF and this seems like the way to go.

  16. Karl Heinz Kremer says:

    Mark, I don’t know enough about Foxit to answer your question. You may want to ask their customer support.

Leave a Reply

Your email address will not be published. Required fields are marked *