OpenOffice.org is an open source
office suite available for Windows, Mac, Linux and Unix. Despite
several small issues, OpenOffice.org (OOo) is capable of opening,
modifying and saving Microsoft Office (MSO) documents. Besides that,
OOo has a very powerfull PDF exporter.For Java developers, the most
interesting part of OOo may be the SDK that comes with it. The SDK
exposes almost all OOo functionality and capabilities to Java through
an API.
In a series of several blogs I will describe a part of this
API. What part I will describe depends on my interest, my working
together with Geertjan Wielenga of NetBeans and possibly whatever requests I get.
In part I of the series, I described how to connect to OOo from Java.
This
is part II of the series. In this blog I will write about some basic Writer operations, like adding and modifying text, setting general text properties, saving, closing and printing documents and exporting them to PDF. I will do my best to describe everything for both Windows and
Linux, since I work on Windows but have my PC at home on Debian Linux.
Also, my main developing IDE at home is NetBeans, but I’ll try to make
everything as IDE independent as possible.
A Writer document has several components. First of all, there is the text area. This area contains all texts organized in paragraphs. Second, there is the service manager. This manager is attached to the document and document specific content, like tables drawing shapes or text frames, can be modified for the current document. Third is the DrawPage which lies on top of the text. he DrawPage is much like a glasspane in a Swing application. By using the DrawPage you can add drawings and make text wrap around it in several ways. Finally, there are several services that allow for document wide styling and structuring of the text (yes, that is literally from chapter 7 of the the Developer’s Guide). This blog will focus on text and tables.
Opening and creating documents
In my previous blog I showed some code to create a new Writer document. There it was only used to show that the code succesfully connects to OOo. The line actually creating the Writer document is
XComponent xComponent = xComponentLoader.loadComponentFromURL("private:factory/swriter", "_blank", 0, pPropValues);
There are four arguments to the loadComponentromURL method. The first one is the URL the document is loaded from. When the URL is "private:factory/swriter", a new document will be created. To load an existing document, simply specify the URL to the document, e.g. "file:///C:/Documents and Settings/wouter/My Documents/Test Document.doc" (on Windows) or "file:///home/wouter/Test Document.doc" (on Linux). Please note the extension ".doc" indicates this is a Word document, which OOo should be able to open. If you would specify an Excel file here, a Calc document would be opened instead of a Writer document. Of course you can open OOo documents as well The URL can start with file, ftp, http or private.
When creating a document, you don’t necessarily need to create a new empty document. It is possible to base the new document on a template. OOo by default creates a new document when a template is opened. Only by setting the AsTemplate property to false the template is opened so you can edit the template (is it me, or doesn’t this make sense at all? It’s true though…). So, basically, opening a template with the AsTemplate property set to true (this is the default value) will open the document fo editing as if it were an exisiting document. When saving the document, the location will not be set, so you’ll have to specify it (see the last part of this blog).
The second argument specifies the target frame in which the document is opened. Usually you will use _blank here to create a new frame. The third argument refers to the order in which the target frame is searched for. For the target frame and search flag options, see the discussion about Frames in chapter 6 of the Developer’s Guide.
The fourth argument specifies certain properties for the document that is opened or created. For instance, to open a document ReadOnly, use these lines
PropertyValue[] pPropValues = new PropertyValue[1];
pPropValues[0] = new PropertyValue();
pPropValues[0].Name = "ReadOnly";
pPropValues[0].Value = new Boolean(true);
This will make sure the document cannot be modified via the keyboard, but API calls WILL modify it. Another example is this:
PropertyValue[] pPropValues = new PropertyValue[2];
pPropValues[0] = new PropertyValue();
pPropValues[0].Name = "ReadOnly";
pPropValues[0].Value = new Boolean(true);
pPropValues[1] = new PropertyValue();
pPropValues[1].Name = "Hidden";
pPropValues[1].Value = new Boolean(true);
This will make sure no desktop is shown and all processing is done in the background. The combination of this property with the ReadOnly property doesn’t make much sense since hiding the desktop ensures noone can modify the document with a keyboard, but it does show that it is possible to combine properties. Please note that opening a document in hidden mode WILL NOT CLOSE IT! To close it, either close it via the API (more on this later), close all open documents that you can close by hand (by exiting OOo) or kill the OOo process. For more properties to set, see the discussion about Handling Documents in chapter 6 of the Developer’s Guide.
XText and cursors
In a Writer document, access to the text is gained through the XText component of the document. XText inherits from XSimpleText and this gives XText a few nice methods. First of all, there is insertString(XTextRange, String, boolean). This method inserts a String at the position of the XTextRange. If the boolean is true, the text in the XTextRange will be replaced, otherwise it will be inserted at the beginning of the XTextRange.
An XTextRange is a description of the position of the object usng the XTextRange in the text. An XTextRange may be zero or more characters wide. The most commonly use of XTextRange is by using a cursor. See below.
Another nice method on XText is insertControlCharacter(XTextRange, short, boolean). A ControlCharacter can be a paragraph break, a line break or some other control character. See the API documentation on ControlCharacter.
Finally, XText has the createTextCursor() and createTextCursorByRange(XTextRange) methods. Both return an XTextcursor (which can be tranformed into a cursor, please read on) but the first creates a new text range and the second copies the text range in XTextRange.
A Writer document knows of two kinds of cursors: visible and invisible. The visible cursor also is referred to as view cursor and it’s interface is XTextViewCursor. Invisible cursors come in four flavours: XTextcursor, XWordCursor, XSentenceCursor and XParagraphCursor. Since XTextViewCursor, XWordCursor, XSentenceCursor and XParagraphCursor inherit from XTextCursor, the all share a common set of operations. These operations include gotoStart(boolean), gotoEnd(boolean), collapseToStart() and collapseToEnd().
The boolean in gotoStart and gotoEnd specify whether or not the text between the current cursor position and start/end will be selected or not. The collapseToStart and collapseToEnd methods positions start or end at the current position. Basically, this resets the XTextRange defined by this cursor making it zeor characters wide. Calling gotoEnd or gotoStart will still go to the start or end of the entire document, however.
Besides that, each cursor type has its own gotoNextXXX, gotoPreviousXXX, gotoStartOfXXX and gotoEndOfXXX methods. Here, replace XXX either with Word, Sentence or Paragraph. These cursors will help you get around your document more easily. The alternative is to know exactly how many characters you have to move the cursor to get where you want to be (let’s call this amount x) and then either call XTextcursor.goLeft(x) or XTextcursor.goRight(x). Here’s an example. Use the code to connect locally to OOo from part I of the series and append this
XTextDocument xTextDocument = (XTextDocument)UnoRuntime.queryInterface(XTextDocument.class, xComponent);
XText xText = xTextDocument.getText();
XTextCursor xTextCursor = xText.createTextCursor();
xText.insertString(xTextCursor, "My first text!&q uot;, false);
xTextCursor.gotoEnd(false);
xText.insertControlCharacter(xTextCursor, ControlCharacter.PARAGRAPH_BREAK, false);
xText.insertString(xTextCursor, "My second text!", false);
XParagraphCursor xParagraphCursor = (XParagraphCursor)UnoRuntime.queryInterface(XParagraphCursor.class, xTextCursor);
xParagraphCursor.gotoPreviousParagraph(false);
xParagraphCursor.setString("My Third Text! ");
This code creates a single short sentence, then appends a paragraph break and a second sentence. Finally the text range is set to the first paragraph through an XParagraphCursor and another sentence is inserted. Since the sentence in the first paragraph is NOT selected, the third sentence is prepended. Modify the code and add this line
xParagraphCursor.gotoEndOfParagraph(true);
right after "xParagraphCursor.gotoPreviousParagraph(false);". This time the first sentence is overwritten.
Text properties
Text properties include whether the characters in a piece of text are italic or not, the weight (e.g. bold), the size, the color etc. Other properties, like whether the text is left justified or centered, line spacing, some margins, tab stops, etc. are paragraph properties. For all text properties, see chapter 7.3.2 of the eveloper’s Guide. Here’s some code you can put behind the code you already have
xParagraphCursor.gotoNextParagraph(false);
xParagraphCursor.gotoStartOfParagraph(false);
xParagraphCursor.gotoEndOfParagraph(true);
XPropertySet xCursorProps = (XPropertySet)UnoRuntime.queryInterface(XPropertySet.class, xParagraphCursor);
xCursorProps.setPropertyValue("CharPosture", com.sun.star.awt.FontSlant.ITALIC);
xCursorProps.setPropertyValue("CharWeight", new Float(com.sun.star.awt.FontWeight.BOLD));
xCursorProps.setPropertyValue("CharHeight", 20);
xCursorProps.setPropertyValue("CharColor", new Integer(0x007f007f));
xCursorProps.setPropertyValue("CharFontName", "Arial");
xCursorProps.setPropertyValue("ParaAdjust", ParagraphAdjust.CENTER);
Basically, this code gets the second paragraph and makes the text some purplish color and Bold Italic Arial of size 20. Also, it centers the paragraph. Thanks to Geertjan for some of this code. Please note that almost all properties may have predefined values. For some values, like the character height or the font name, these predefined values can easily be guessed or found. But some values, like colors, are a bit weird. As Geertjan points out in his blog, all colors have numerical values though I am not sure where to get those values. Colors can also be set by ARGB codes. An ARGB code exists of eight digits meaning four hexadecimal values, onde hex value for each of the four channels A(lpha), R(ed), G(reen) and B(lue). The digits for the A channel are optional. In this case, the value of 0x007f007f means the value 0 for A, 127 for R, 0 for G and 127 for B, which is the purple you’ve seen in your document. Any self respecting Cascading Style sheets programmer (or old fashioned HTML coder) would know how to get the values for the colors you want to set your characters to. For those that don’t: either use PhotoShop, Paint Shop or Gimp to get those values. Use a scientific calculator to transfor the decimal values to hex values.
Saving, printing, exporting, closing
Once you’re satisfied with your document, you probably want to preserve it for later usage. Before doing anything next, it’s probably a good idea to save the document to a hard disk. Especially when a Microsoft Office document is needed, having an OOo backup may come in handy in case the transformation doesn’t go well enough.
In order to save a hard disk, an XStorable object needs to be gotton from the document. Then, either one of store(), storeAsURL(String, properties) or storeToURL(String, properties) can be called. The store() method is the equivalent of the save operation. This method will only succeed if the document has a location, which can be checkd with the hasLocation() method. the location itself can be gotten via the getLocation() method. The storeToURL(String, properties) and storeAsURL(String, properties) methods save the document to the location specified by the String representing a URL (file:///, http:// or ftp://). The difference is, that storeAsURL will make the URL the new location of the file (i.e. equivalent to Save As) while storeToURL will save the file to the URL but will keep the old loation of the file. The properties can set, for example, the file type, e.g. "MS Word 97". This file type can be set through the FilterName property. See chapter 7.2.2 of the Developer’s Guide dealing with storing documents.
Closing a document can be done in a likewise manner. Request the XCloseable interface and call close(boolean) on it. The boolean specifies if anyone can take care of closing the document, possibly throwing a CloseVetoException, (true) of if you will take care of closing the document, therefore taking responsibility for any CloseVetoException (false). The basic difference is that if the boolean is true, anyone may close the document, while if false only you can. If the document, for whatever reason, cannot be closed you’ll have to wait and try again later.
Printing can be achieved by calling, you guessed it, the XPrintable interface. This interface supports the PropertyValue interface which allows you to set the printer name, paper size, paper orientation etc.
Exporting to PDF, finally, is the same as saving a document. The only thing you need to take care of is the correct FilterName property. This should be set to "writer_pdf_Export". Besides that, you NEED to use the storeToURL method.
This last example will take our new document, convert it to PDF and store it in the "C:\" directory under the name "mydoc.pdf". Finally, it will close the document. Since the Hidden property of the document is set to true, no OOo desktop will be shown. Please note this code is far from perfect (the filename and location are hard coded in the code so you NEED to modify that) but it does show how this all works.
First, using the code you already have, replace the line "PropertyValue[] pPropValues = new PropertyValue[0];" just prior to the loadComponentFromURL line with
PropertyValue[] pPropValues = new PropertyValue[2];
pPropValues[0] = new PropertyValue();
pPropValues[0].Name = "Hidden";
pPropValues[0].Value = new Boolean(true);
Then, append these lines to the end of your code
PropertyValue[] storeProps = new PropertyValue[2];
storeProps[0] = new PropertyValue();
storeProps[0].Name = "FilterName";
storeProps[0].Value = "writer_pdf_Export";
storeProps[1] = new PropertyValue();
storeProps[1].Name = "CompressMode";
storeProps[1].Value = "0";
xStorable.storeToURL("file:///c:/mydoc.pdf", storeProps);
System.out.println("Closing document");
XModel xModel = (XModel)UnoRuntime.queryInterface(XModel.class, xComponent);
XCloseable xCloseable = (XCloseable)UnoRuntime.queryInterface(XCloseable.class, xModel);
xCloseable.close(false);
Please make sure to replace the URL "file:///c:/mydoc.pdf" with something you like in case you don’t like the one I made up. Next, run the code and verify the PDF is created at the location you provided. On Windows, this should be enough to shutdown OOo after closing the document. On Linux, OOo continues to run and I’m not sure yet why. I’ll try to find out and get
back when I do. If you know, please let me know by adding a comment to this blog.
Could I export only the first page of a document?
Any help will be approcated.
Hai Wouter van Reeven,
thanks for the reply …..
sorry for not being grateful enough .
i was able to insert bookmarks like i wanted .. thanks to your suggestion.
after i made my first post …. i kinda moved out of openoffice api and all …
but now i am desperately in need of your help.
I will describe my problem as clearly as i can.
I am making an application.. which needs to embed open office within java awt/swing application
This i was able to do using some Officebean without much lines of code .(i used the office bean directly without subclassing it)
For this i have an awt frame with a menu bar.
at click of a menuitem i added a panel with some buttons and also the officebean.
now what i want is , when i click one of those buttons in the panel a particular bookmark should be inserted
in the document in the officebean
the examples i came across which insert bookmarks dont use officebean. So i am in a dilemma.
Also i would want to be able to list out the bookmarks in the document in the officebean in a listbox outside the
officebean
sorry if i have confused you …
I would be very grateful if you could help in any way
Hi Martin,
You should move the text cursor to the end first. By default the
cursor is at the start of the text. Please see my other code for how to
do this.
HTH,
Wouter
hey Wouter van Reeven
thank you for the 2-part article ….though i found the developer’s guide had useful examples , i wasnt sure what the methods were meant to do and what the arguments were for ..Your article helped me a lot ..
now could u help me a bit more
i need to insert text into bookmarked fields of a template file . from ur code samples i was able to find the bookmarked fields …but i cant seem to understand how to insert text after the bookmarked field…
say i have bookmarks
doctor
patient
…..
i want to insert a name after the bookmarks like
doctor martin joseph
patient raju
.
but my code (not actually mine combination of samples from ur article and a few other examples) inserts the name
like
martin josephdoctor
the portion of the code i want to edit is
String b1=”Clinic “;
XTextRange textRange = null;
Object bmk=null;
XNameAccess bookmarkNames = bookmarksSupplier.getBookmarks();
bmk= bookmarkNames.getByName(b1);
XTextContent bookmarkContent = (XTextContent)
UnoRuntime.queryInterface(XTextContent.class, bmk);
textRange =bookmarkContent.getAnchor();
xText.insertString(textRange,”Santa Clara” , false);
where b1 is the first bookmark
I am converting html to pdf. i saved the following html file http://www.chem.vt.edu/RVGS/ACT/notes/Types_of_Equations.html in my local drive. The First time i convert the file , it works perfectly but if i try it again on the same file, the last few pages are skipped, and when i restart my machine, again it will work perfectly the first time, however in the subsequent conversions the last few pages are skipped. I can send the code i am using
propertyValues[1].Name = “FilterName”;
propertyValues[1].Value = “writer_pdf_Export”; for output file storeToURl method
and i am using propertyValues[1].Name = “FilterName”; propertyValues[1].Value = “HTML (StarWriter)”; while opening the html file in the loadComponentFromURL method. help will be appreciated.
i am trying to wite a java program that opens a writer document and exports it to html….
XStorable xStorable = (XStorable)UnoRuntime.queryInterface(XStorable.class, xComp);
PropertyValue[] storeProps = new PropertyValue[1];
storeProps[0] = new PropertyValue();
storeProps[0].Name = “FilterName”;
storeProps[0].Value = “HTML (StarWriter)”;
xStorable.storeToURL(“C:\\test.html”,storeProps);
I have gone through all the sample code I could find, and I still get a com.sun.star.task.ErrorCodeIOException…
i also want to save this code just as an Openoffice document ……it still gives the same exception …
xStorable.storeAsURL(“C:\\test.odt”,storeProps);
plz help
Great, this article really did get me started with OOo Api. For a fortnight I tried deciphering the guide and well, wasted a whole lot of time. This does give the concepts so that I can put the delvelopers guide to good use
Thanks a lot
Rajeev
Thanks. This helped a lot… the storeToUrl as opposed to storeAsUrl is key to the pdf export.
Unfortunately the OOo develper’s guide, while having a huge quantity of information, does not contain some of the essentials such as tables of PropertyValues for each method. The API documentation is sparse as well. I have yet to find one example that works as advertised without some trial and error.
Once things are working in OOo, however, they are fast and reliable in contrast to the MSOffice suite.
Two small utility methods that I add to my OO access classes or a util class are:
private PropertyValue newPropertyValue(String name, Object value) {
PropertyValue pv = new PropertyValue();
pv.Name = name;
pv.Value = value;
return pv;
}
private PropertyValue newPropertyValue(String name, boolean value) {
return newPropertyValue(name, new Boolean(value));
}
This condenses the propertyValue creation to a little more readable form:
PropertyValue[] storeProps = new PropertyValue[] {
newPropertyValue(“FilterName”,”writer_pdf_Export”),
newPropertyValue(“CompressMode”,”0″)};
Chris
Hi Lucas,
Thanks. I didn’t put my presentation online yet as I got too much carried away writing these two articles. And I didn’t create a Powerpoint presentation, but an Impress presentation. I did convert it to Powerpoint and handed it over to the NL-JUG people during the conference so I suppose it will be put online there any time soon. Our next Knowledge Center session will be about OOo (if I’m not mistaken) so you’ll get to see my presentation there anyway.
Greets, Wouter
Wouter,
Great article and a very good way to get me started on using the OO API. This makes programmatic manipulation of high quality documents – and indirectly even Microsoft Office documents – a realistic option. The opportunities seem to be enormous.
Can we find the presentation you did on this for the NL-JUG conference on line somewhere? Did you really create your PowerPoint presentation for that session from Java?
regards
Lucas