Tutorial: Jacob & Microsoft Word

@for Developers
@author Kai Ruhl
@since 2003-06

"Am I the only person on this planet who wants to write MS Word files with Java?"
- Me, after researching Jacob, POI, WordBean and others, all to no informational avail.

Introduction

Jacob is a Java/COM bridge provided by Dan Adler under a semi GPL license (may not be used in a commercial product targetted at java developers, e.g. virtual machines, debuggers. The chance that you are not allowed to use it is very slim).

There is no documentation available concerning the practical use of any Microsoft applications; it is, so Adler, intended as a generic Java/COM bridge and not some MS Office API. However, M. Bigatti made a FAQ, which IMHO is not too useful when it comes to MS Word; and there is a Jacob Mailing List, where I got most of my information, even if it was tedious work.

Now, this is a tutorial entirely dedicated to the handling of Microsoft Word with Jacob. If you want Excel stuff, I would rather recommend POI, hosted at the Apache Foundation; they have good excel support, but only word scratchpad stuff. If you just need to insert some unformatted text, an easier solution is the WordBean by Müller&Stein.

A good alternative to using Jacob may be Jawin, which follows exactly the same goal, namely dispatching calls to COM objects.

Authors

This document is far from complete; I am always open for suggestions, tips and any enhancements. If you know something, please tell me. The absence of another site like this, in contrast to all the questions on JDC Search and Jacob Mailing list, imply that my page will be of some usability. My mail adress is kain at the above domain.

Authors are so far Kai Ruhl.

Update 2006-04: I had a nice email exchange with a guy named Jean Helou; he summarised his experiences with Jacob in a wiki documentation: it contains a section on macros, and is based on ms word xp. Also, he provided me with a link to the useful ms office object model documentation.

Update 2006-08: A nice girl named Kathrin Eichler emailed me a section on hyperlinks; it is included below. She is using Office XP. Thanks Kathrin!

Scenario

I want to be very specific here, so I will only describe one solution. This is the following:

I have a document file_in.doc which is my "template"; actually it is a complete document which needs to be enhanced by text, enumerations, and tables.

This file is processed by my java program and written to file_out.doc. Both files are in c:\java\jacob.

1 Preparations

You need to have two files: jacob.jar and jacob.dll. You put the former in your classpath and the latter in c:\windows\system32 or your equivalent. I tested jacob both win 98 and win xp, both with ms office 97.

Then, I assume you create a new java class, make a new main(String[] asArgs) method and are at its beginning.

2 Lets Play

First, I will create some variables; you can change them almost arbitrarily. They are pretty self explaining.

    String sDir = "c:\\java\\jacob\\";
    String sInputDoc = sDir + "file_in.doc";
    String sOutputDoc = sDir + "file_out.doc";
    String sOldText = "[label:import:1]";
    String sNewText = "I am some horribly long sentence, so long that [insert bullshit here]";
    boolean tVisible = true;
    boolean tSaveOnExit = false;

sOldText holds the label that I will search and replace. tVisible is only true for debugging purposes, to see whats going on. tSaveOnExit is false since I save explicitly.

Now, we will open word and read the document as well as some base variables.

    ActiveXComponent oWord = new ActiveXComponent("Word.Application");
    oWord.setProperty("Visible", new Variant(tVisible));
    Object oDocuments = oWord.getProperty("Documents").toDispatch();
    Object oDocument = Dispatch.call(oDocuments, "Open", sInputDoc).toDispatch();
    Object oSelection = oWord.getProperty("Selection").toDispatch();
    Object oFind = oWord.call(oSelection, "Find").toDispatch();

Run this. It should open word, but dont do something cool.

oDocuments holds the list of documents. oDocument holds our specific document file_in.doc. oSelection and oFind are objects we need for the next step, selecting and inserting.

    Dispatch.put(oFind, "Text", sOldText);
    Dispatch.call(oFind, "Execute");
    Dispatch.put(oSelection, "Text", sNewText);

Now we search for sOldText, execute the search (which results in the label being selected inside Word), and replace that selection with the new text (which, in turn, is also selected).

So next, we leave that select stuff.

    Dispatch.call(oSelection, "MoveDown");
    Dispatch.put(oSelection, "Text", "\nSo we got the next line including BR.\n");

We move the cursor down, effectively leaving the selection (yes, it works just like a VB macro inside Word; works also with MoveUp, MoveLeft, MoveRight). Then, we insert other text.

Now we want to format text. Since we always operate with selected text (the whole "TypeText" directive mentioned at the mailing list didnt quite work for me), we make the format afterwards (unto the selected text, not unto the next-to-be-typed text).

    Object oFont = Dispatch.get(oSelection, "Font").toDispatch();
    Dispatch.put(oFont, "Bold", "1");
    Dispatch.put(oFont, "Italic", "1");
    Dispatch.put(oFont, "Underline", "0");

Now the selected text (the "\nSo we got ... BR.\n") is both bold and italic.

    Object oAlign = Dispatch.get(oSelection, "ParagraphFormat").toDispatch();
    Dispatch.put(oAlign, "Alignment", "3");

And now the alignment is block (0 - Left, 1 - Center, 2 - Right, 3 - Block; at least I hope so ;-). For now, this is the minimal thing that can be useful for you. Using the MoveDown and Text directives you can do the basics.

3 Save and Close

Well, there were a lot of suggestions on the mailing list, but that one worked for me.

    Object oWordBasic = Dispatch.call(oWord, "WordBasic").getDispatch();
    Dispatch.call(oWordBasic, "FileSaveAs", sOutputDoc);

Dont ask me why. It just works.

    Dispatch.call(oDocument, "Close", new Variant(tSaveOnExit));
    oWord.invoke("Quit", new Variant[0]);

This is straigthforward. No sweat.

4 Images

Yes its possible to embed images pretty easy.

    String sImgFile = sDir + "image.png";
    Dispatch.call(oSelection, "MoveDown");
    Object oImage = Dispatch.get(oSelection, "InLineShapes").toDispatch();
    Dispatch.call(oImage, "AddPicture", sImgFile);

Well, it just works the way shown by the mailing list. Dont ask me about the image format (text flow and such) though. Better, if you know it, mail me.

5 Hyperlinks

Hyperlinks are also pretty straightforward (courtesy Kathrin Eichler, under Office XP):

    String sHyperlink = "http://www.google.com";
    Dispatch.put(oSelection, "Text", "Text for the link to Google");
    Object oRange = Dispatch.call(oSelection, "Range");
    Object oLink = Dispatch.get(oDocument, "Hyperlinks").toDispatch();
    Dispatch.call(oLink, "Add", oRange, sHyperlink);

I have not tried that personally yet (under Office 97), so your mileage may vary.

6 Tables

Holy shlamoly, I got no idea yet. Am researching VB code and stuff. I got some suggestions from the mailing list how to add a row to a table and how to navigate a table, but creating... Well, still to come.

7 List / Enumeration

The VB code looks like shit. I have no idea how to work here. Still to come.

Summary

While working word with Jacob is a bit complicated, it still is the mightiest possibility aside from linking some VB to JNI or to an Runtime exe. I hope I could give you an introduction into this theme.

Thanks for reading me.

EOF (Aug:2006)