Skip to main content

Dynamically generate OpenOffice documents on the client side with XPCOM

A portable and cost-effective alternative to server dependence

Stefanus Wiguna (wiguna@us.ibm.com), Advisory Software Engineer, IBM
Stefanus Wiguna
Stefanus Wiguna is an advisory software engineer with the IBM Software Group at Research Triangle Park, N.C. He is a Principal Certified Lotus Professional and has over 10 years' experience in solution development and integration using Lotus, Web 2.0, and Java technology.

Summary:  With the Cross Platform Component Object Model (XPCOM) framework from Mozilla, you can dynamically export existing XML content into an OpenOffice document. The process also works for any other type of content supported by the transformation mechanism, such as XSLT. In this article, learn about a portable and cost-effective alternative to server-side solutions.

Date:  07 Jul 2009
Level:  Introductory
Also available in:   Japanese  Portuguese

Activity:  5130 views
Comments:  

Introduction

OpenOffice is an increasingly popular — and free — open source alternative to expensive commercial office software suites. Creating content and exporting existing content into OpenOffice can be cumbersome if you don't have the right tools.

Typically, dynamically creating an OpenOffice document or exporting your existing content into an OpenOffice document involves:

  • A Web application running on a server to transform your content to the OpenDocument Format (ODF).
  • Compressing the resulting content into the OpenOffice document archive.
  • Storing the file somewhere or serving it to the user.

But what if you don't have access to server-side applications? Or you simply want to minimize server load by moving the process to the client?

Detailed discussion of XPCOM, Firefox Extension, OpenOffice, OpenDocument Format, and XSLT is outside the scope of this article. See Resources for more on these topics.

In this article, learn how to use the Cross Platform Component Object Model (XPCOM) framework from Mozilla to speed development of your application. Examples show how to dynamically export existing XML content into an OpenOffice document using XPCOM components within the Firefox Extension. And, you're not limited to exporting just XML; you can process other types of content as long as it's supported by the transformation mechanism (for example, Extensible Stylesheet Language Transformations (XSLT)).

Before actually dynamically exporting content into OpenOffice, the next section covers the requirements for building an OpenOffice document.


OpenOffice documents

The OpenOffice document is basically a compressed file consisting of a set of XML files to describe the different parts of the document, such as content and styles; a manifest; and a thumbnail.

Figure 1 shows the file structure you will get if you take an OpenOffice text document (.odt), rename it with a .zip extension, and extract it.


Figure 1. OpenOffice document structure


Table 1 briefly explains some of the files.


Table 1. OpenOffice document files
FileDescription
content.xmlContains the actual content of the document.
meta.xml Contains metadata information, such as creation date and author.
styles.xml Defines formatting styles for paragraphs, characters, etc.

To create a new OpenOffice document, the easiest and perhaps safest way is to edit an existing document. In this article, you'll only update content.xml where the actual content of the document is stored. The content.xml itself looks like Figure 2.


Figure 2. OpenOffice document's content.xml


For our examples, let's call the section before the actual content the content head and the section after the actual content the content tail. The actual content itself is the content body. You'll use these sections every time you build a new OpenOffice document.


Preparing the development environment

You need to do a couple of things in the Firefox Extension development environment in order to export to OpenOffice. Since you'll be creating a new OpenOffice document by editing an existing one, you need to:

  1. Include all the files in Figure 1 in the Firefox Extension package. You will use these files to build the new OpenOffice document.
  2. Store these files outside the chrome folder of the Firefox Extension. This is to ensure that the files are not compressed during extension packaging so you can retrieve them easily later on.

An example of how the files are stored within the Firefox Extension is shown below.


Figure 3. OpenOffice document files in the Firefox Extension structure


All files can be stored in the same folder. You'll put each file in the appropriate place within the archive when you build the OpenOffice document. xml2odt.xsl is the custom stylesheet file you will use to transform existing content into ODF.

The next step is to set several variables and constants to be used for file operations throughout the OpenOffice document generation process, such as locating and retrieving files. Listing 1 shows an example. You'll use the nsIExtensionManager XPCOM interface to set variables and constants.


Listing 1. Setting up variables and constants

var eid = "export2OO"; // extension id
var em = Components.classes["@mozilla.org/extensions/manager;1"]
 .getService(Components.interfaces.nsIExtensionManager);

var oopath = "export/oo/"; // path to the OpenOffice document files
var ooxslt = "xml2odt.xsl";

const PR_WRONLY = 0x02;
const PR_RDWR = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_APPEND = 0x10;
const PR_TRUNCATE = 0x20;
const PR_USEC_PER_MSEC = 1000;
const time = Date.now();
            

All of the code in this article can be put anywhere as long as it can be accessed by Firefox Extension. Typically, you would store it in a JavaScript file (.js) inside the chrome folder and include it in the Firefox Extension main XUL file, as shown below.


Listing 2. Including code in Firefox Extension

<overlay id="export2OO"
 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 <script type="application/x-javascript;version=1.7"
           src="chrome://export2OO/content/export.js" />
            

Now that the development environment is prepared, the next section gets down to the real business.


Programmatically building an OpenOffice document

Three are three main tasks involved in dynamically exporting your existing content into an OpenOffice document:

Transform existing content into OpenDocument format

You can easily transform existing content into ODF with Extensible Stylesheet Language Transformations (XSLT). For example, Listing 3 shows some content in XML format.


Listing 3. Content in XML

<h1>This is Heading 1</h1>
<h3>This is Heading 3</h3>

Listing 4 shows the same content presented in ODF.


Listing 4. Content in ODF
<text:h xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
 text:style="Heading1" text:outline-level="1">
 This is Heading 1
</text:h>
<text:h xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
 text:style="Heading3" text:outline-level="3">
 This is Heading 3
</text:h>
            

The ODF specification provides all sorts of information on defining and formatting styles (see Resources). Once your stylesheet file is ready, you can perform the transformation inside the Firefox Extension environment. Using the extension manager, locate the stylesheet file that will be used for the transformation. Listing 5 shows an example.


Listing 5. Locating XSLT file
// locate stylesheet file
var xslfile = em.getInstallLocation(eid).getItemFile(eid, oopath + ooxslt);

            

Next, load the stylesheet file.


Listing 6. Loading XSLT file

var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
 .createInstance(Components.interfaces.nsIFileInputStream);
var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
 .createInstance(Components.interfaces.nsIScriptableInputStream);

var xsldata = "";
fstream.init(xslfile, -1, 0, 0);
sstream.init(fstream); 
var tmp = sstream.read(4096);
while (tmp.length > 0) {
 xsldata += tmp;
 tmp = sstream.read(4096);
}
sstream.close();
fstream.close();

var parser = new DOMParser();
var xsldoc = parser.parseFromString(xsldata,"text/xml");
            

You can use the stylesheet to transform the content into ODF. In the following example, xmldoc is the XML document of the existing content.


Listing 7. Transforming content into ODF with XSLT

var xsltprocessor = new XSLTProcessor();
xsltprocessor.importStylesheet(xsldoc);
var newcontentdoc = xsltprocessor.transformToFragment(xmldoc,document);
            

At this point, it's time to update content.xml with the new content. To make a valid OpenOffice content.xml, you need to include the appropriate content head and content tail (see Figure 2).


Listing 8. Updating content.xml with new content

// prepare new content
var contentbody = (new XMLSerializer()).serializeToString(newcontentdoc);
var newcontent = xmlheader + contenthead + contentbody + contenttail

// update content.xml
var contentfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "content.xml");
var fstream = Components.classes["@mozilla.org/network/file-output-stream;1"]
 .createInstance(Components.interfaces.nsIFileOutputStream);
fstream.init(contentfile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0666, 0);
fstream.write(newcontent, newcontent.length);
fstream.close();
            

You now have an updated content.xml. In the next section, you build the new OpenOffice document.

Build the OpenOffice document

To build a new OpenOffice document, you need to create a new OpenOffice archive and add content.xml and the other required files to the archive. The example uses the nsIZipWriter XPCOM interface for this purpose.

The first step is to create the archive file and store it somewhere on the user's machine. The following example stores the new file in the desktop area, as shown in Listing 9. Depending on requirements, you might need to create a temporary file or store it in other places.


Listing 9. Creating new OpenOffice document archive

var odtfile = Components.classes["@mozilla.org/file/directory_service;1"]
 .getService(Components.interfaces.nsIProperties)
 .get("Desk", Components.interfaces.nsIFile);
odtfile.append("new.odt");
odtfile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0666);

var zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
var odt = new zipWriter ();
odt.open(odtfile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE | PR_APPEND);

Table 2 shows some commonly used values you can use in place of Desk.


Table 2. Common directories for storing files
FileDescription
ProfDFirefox Profile directory
Desk Desktop directory (for example, ~/Desktop on Linux®, C:\Documents and Settings\username\Desktop on Windows®)
Home Operating system root directory (for example, /root on Linux, C:\ on Windows)
TmpD Operating system temporary directory

The second step is to add the required OpenOffice document files to the archive. Keep in mind that you need to add each file to its appropriate place in the structure shown in Figure 1.


Listing 10. Adding OpenOffice document files to the archive

// locate file
var contentfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "content.xml");
// add file to archive
odt.addEntryFile("content.xml", 
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE,
 contentfile, false);

// create directory
odt.addEntryDirectory("META-INF", time * PR_USEC_PER_MSEC, false);
// locate file
var manifestfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "manifest.xml");
// add file to specific directory in archive
odt.addEntryFile("META-INF/manifest.xml",
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE, 
 manifestfile, false);

odt.addEntryDirectory("Thumbnails", time * PR_USEC_PER_MSEC, false);
var thumbfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "thumbnail.png");
odt.addEntryFile("Thumbnails/thumbnail.png", 
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE, 
 thumbfile, false);

var metafile = em.getInstallLocation(eid).getItemFile(eid, oopath + "meta.xml");
odt.addEntryFile("meta.xml", 
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE, 
 metafile, false);

var mimetypefile = em.getInstallLocation(eid).getItemFile(eid, oopath + "mimetype");
odt.addEntryFile("mimetype", 
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE, 
 mimetypefile, false);

var settingsfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "settings.xml");
odt.addEntryFile("settings.xml", 
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE, 
 settingsfile, false);

var stylesfile = em.getInstallLocation(eid).getItemFile(eid, oopath + "styles.xml");
odt.addEntryFile("styles.xml", 
 Components.interfaces.nsIZipWriter.COMPRESSION_NONE, 
 stylesfile, false);

// close the archive
odt.close();
            

Now that the new OpenOffice document creation is complete and the document is ready to use, you can launch it.

Launch the OpenOffice document

Launching is optional, since the new document has already been saved on your desktop area. The new document can be opened at any time manually, or it can be used as input for other processes.

If file association has been set properly, the code in the following example will automatically open the newly created document in OpenOffice.


Listing 11. Launching OpenOffice document automatically

var ios = Components.classes["@mozilla.org/network/io-service;1"]
	   .getService(Components.interfaces.nsIIOService);
var odtURL = ios.newFileURI(odt);
location.href = odtURL.spec;
            

If file association has not been set, you are prompted by a window similar to the one shown below.


Figure 4. Opening OpenOffice document


That's all there is to it.

Once you build and install your Firefox Extension, the code in this article will give you an Export to OpenOffice functions right on the client.


Conclusion

XPCOM's rich libraries speed up development of your application and let you do things on the client side that were previously only possible on the server side. You now have a portable and cost-effective alternative to server-side solutions.


Resources

Learn

Get products and technologies

Discuss

About the author

Stefanus Wiguna

Stefanus Wiguna is an advisory software engineer with the IBM Software Group at Research Triangle Park, N.C. He is a Principal Certified Lotus Professional and has over 10 years' experience in solution development and integration using Lotus, Web 2.0, and Java technology.

Comments



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=405552
ArticleTitle=Dynamically generate OpenOffice documents on the client side with XPCOM
publish-date=07072009
author1-email=wiguna@us.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers