11 Mar 2009

Acrobat, JavaScript and VB walk into a bar…

Posted by khk

Update:

Please visit the same post on my business site. The comments are closed here, so if you want to comment, you have to head over to http://khkonsulting.com/2009/03/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.

Subscribe to Comments

18 Responses to “Acrobat, JavaScript and VB walk into a bar…”

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

     

    pab

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

     

    Ernst

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

     

    khk

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

     

    khk

  5. Kindly tell me where to store that given java script Please ..

     

    shankyee

  6. Take a look at a blog post I wrote yesterday: http://khkonsulting.com/2010/12/acrobat-javascripts-where-do-they-go/
    It will give you a tool to find out where you should store the script.

     

    khk

  7. Dear friend actually I have a java script & I want to call it on various pdf files. Java script having some function with parameters. Please help me..

     

    shankyee

  8. Hi Karl,

    I have two newbie questions for you. First of all, how do I test the js in Acrobat? I did save it in the right folder, but I can’t seem to view the script (let alone execute it) from within Acrobat. I have ver. 9 Pro Extended. “Edit All Javascripts” doesn’t bring it up.

    Second, the VBA code chokes on the line

    MsgBox (jso.CountAllBookmarks())

    The javascript I copied from your site I pasted into a text editor and renamed CountAllBookmarks.js. It’s stored in the right folder, but maybe I didn’t name it correctly.

    Thanks very much for any help. Also, thank you very much for your posting on combining PDF files from Excel. It was simple and quick. There is a lot of information out there on Javascript and Acrobat and bridges, but most of it starts with a lengthy history and overview, and it’s too time-consuming for amateurs to wade through. Yours was perfect.

     

    Meg

  9. CreateObject(“AcroExch.App”)
    acr=CreateObject(“AFormAut.App”)
    ? acr.fields.count
    what=acr.fields(“c.1”).value
    what=acr.fields(“C.1”).value

    this code works in Foxpro calling the api and I can use all the properties like textfont etc

    But I cannot after hours figure out how to get the name of a field

    like I would think the following should work but it doesn’t

    what=acr.fields(1).name

    anyway to get a field name with vba???

     

    Craig Lytle

  10. here is working foxpro code to show all fields – you kind of have to use the loop

    public what, whtn, formapp
    cr=chr(13)+chr(10)

    FormApp = CreateObject(“AFormAut.App”)
    what=’ ‘
    i=1
    For Each Field In FormApp.Fields
    ? field.name + ‘ ‘+field.value

    * you don’t need the rest of this except the endfor but it shows what you can do
    whf=field.name
    chrl= formapp.fields(“&whf”).charlimit

    whtn=field.name
    if !left(whtn,1)=’C’
    *and len(alltrim(field.value))>1
    i=val(substr(whtn,at(‘.’,whtn)+1,3))
    *i=str(substr(whtn,at(‘.’,whtn)+1,3))
    *sm(i)=field.value
    *substr(whtn,at(‘.’,whtn)+1,3)
    what=what+substr(whtn,at(‘.’,whtn)+1)+’ ‘+field.value+str(chrl,2)+cr
    endif
    if i=30 or i=60 or i=90 or i=120
    wait
    endif
    i=i+1
    endfor
    return

     

    Craig Lytle

  11. Meg this was for u and if ur using a field named C.1 ur probably using CEB Jud forms. If so you can easily do all of it with foxpro
    like

    public what, whtn
    FormApp = CreateObject(“AFormAut.App”)
    what=’ ‘
    i=1
    For Each Field In FormApp.Fields
    ? field.name + ‘ ‘+field.value
    whtn=field.name
    if substr(whtn,1,1)’C’
    i=val(substr(whtn,at(‘.’,whtn)+1,3))
    if !empty(sm(i))
    field.value=sm(i)
    field.textfont=”Times-Bold”

    endif
    if i=30 or i=60 or i=90 or i=120
    wait
    endif
    endif
    i=i+1
    endfor
    return

    obvious sm is an array but you could use a dbf instead

    these forms use C.? as the header with case info and then the form name like fl120.?? as the rest of the fields

     

    Craig Lytle

  12. Hi,

    I havé a question for you. Is it possible to include actions in a pdf, Such as file open … Without using a javasript? I nées to do That in a pdf files Without usiing javasripts functionality.

    Tanks.

     

    Christian

  13. Sorry, but for actions, you need JavaScript, there is no way around it.

     

    khk

  14. Thanks for your blogs like: http://www.khk.net/wordpress/2009/03/11/acrobat-javascript-and-vb-walk-into-a-bar/

    Have you tried JScript in VBA?

    e.g.
    ‘http://www.mcpher.com/Home/excelquirks/snippets/scriptcontrol
    ‘ Note: you’ll need to add a reference to Microsoft Script Control v1.0 in Tools > References
    Public Sub x()
    Dim o As New ScriptControl
    o.Language = “JScript”
    With o
    .AddCode “function x(a,b) {return ‘the answer is:’ +(a+b);}”
    Debug.Print .Run(“x”, 1, 2)
    End With
    End Sub

     

    Kenneth Hobson

  15. Kenneth,

    JScript in VBA does not help you with the Acrobat JavaScript implementation – you would be using a completely different interpreter, and would not have access to the Acrobat domain specific extensions to JavaScript that Adobe provides.

     

    khk

  16. I have used this code for merging and adding text to PDF files. I have upgraded to Adobe Acrobat XI Pro, the script no longer works. Do you know a fix for this.
    Thanks

     

    vickie

  17. Do you have any examples of using .getlinks method in VBA to retrive the list of links for a page in a PDF? The follwoing code snipet is what I attempted to do, however I get a run time error (object required) at the Set arrynks = jos.getLinks(.. ) statement below. Any ideas?

    Dim AcroApp As Acrobat.CAcroApp
    Dim Part1Document As Acrobat.CAcroPDDoc
    Dim jso As Object
    Dim numPages, numLinks As Integer
    Dim arraylnks() As Object
    Dim rect(0 To 3) As Integer
    Dim retval As Integer

    Set AcroApp = CreateObject(“AcroExch.App”)
    Set Part1Document = CreateObject(“AcroExch.PDDoc”)
    retval = Part1Document.Open(NewName)
    Set jso = Part1Document.GetJSObject

    rect(0) = 0
    rect(1) = 792
    rect(2) = 614
    rect(3) = 0
    Set arraylnks = jso.getLinks(0, rect)
    numLinks = arraylnks.Length

     

    Larry

  18. Larry, unfortunately I don’t have any sample code, and I’ve never used the getLinks() method myself from VB/VBA, so I also don’t have any guidance in how to get around this error. Sorry.

     

    khk