Introduction
There are some open source projects which can create Word documents in Java, like Apache poi, itext, etc. But using COM is a simple way to create Word documents in Java under Windows platform. So I will show you how to use JNI technology to call Windows COM in this article.
Setup Development Environment
- Download the latest version of Eclipse from http://www.eclipse.org, and extract compressed file into C:\Eclipse.
- Download the latest version of
Code::Blocks
from http://www.codeblocks.org, and install it into C:\CodeBlocks. - Download the latest version of vole from http://sourceforge.net/projects/vole, which is a neat C++ COM/Automation driver, we can use it to create Word documents easily. And vole depends on the STLSoft libraries, so we need download STLSoft - http://stlsoft.org, too.
- Extract file vole-0.7.2.zip into C:\vole-0.7.2, and copy directory “vole” into C:\CodeBlocks\MinGW\include in C:\vole-0.7.2\include.
- Extract file stlsoft-1.9.100-hdrs.zip into C:\stlsoft-1.9.100-hdrs, and copy all directories into C:\CodeBlocks\MinGW\include in C:\stlsoft-1.9.100-hdrs\include.
Program Development
- Create a new Java project
zzutils
in Eclipse, and create a new class named Utils
, package name is org.zerozone.util
. We will define a native method named generateWordDocument
, which holds 5 parameters. The method definition is listed below:
public static native void generateWordDocument(String fileName,
String[] bookmarkNames, int bookmarkCount, String[] values,
int valueCount);
The parameters description:
fileName
, the file which saves process result bookmarkNames
, an array which hold bookmarks what you want to insert into bookmarkCount
, a number which indicates how many bookmarks will be processed values
, an array which holds values that will be inserted into the right bookmark valueCount
, a number which indicates how many values will be inserted
- Generate C++ header file with
javah
which is a tool in jdk. Create a batch file named generate_header.bat, the contents of which are listed below:
"C:\Program Files\Java\jdk1.6.0_10\bin\javah"
-classpath .;./bin org.zerozone.util.Utils
Run this bat file, then we can get a C++ header file named org_zerozone_util_Utils.h under zztuils project directory.
- Create a new dynamic library project named
zzutils
in Code::Blocks.
First, copy header file org_zerozone_util_Utils.h into project directory, and add this header file into project, then create a new CPP file named utils.cpp, the contents of which are listed below:
#include <windows.h>
#include "org_zerozone_utils_Utils.h"
#include "cn_net_ynst_kjjl_util_Utils.h"
#include <vole/vole.hpp>
#include <comstl/util/initialisers.hpp>
using vole::object;
using vole::collection;
char* jstringToWindows(JNIEnv *env, jstring jstr)
{
int length = env->GetStringLength(jstr);
const jchar* jcstr = env->GetStringChars(jstr, 0);
char* rtn = (char*)malloc(length*2+1);
int size = 0;
size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length,
rtn,(length*2+1), NULL, NULL);
if(size <= 0)
{
free(rtn);
rtn = NULL;
return NULL;
}
env->ReleaseStringChars(jstr, jcstr);
rtn[size] = 0;
return rtn;
}
jstring windowsToJstring(JNIEnv* env, char* str)
{
jstring rtn = 0;
int slen = strlen(str);
unsigned short* buffer = 0;
if(slen == 0)
{
rtn = env->NewStringUTF(str);
}
else
{
int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, NULL, 0);
buffer = (unsigned short*)malloc(length*2 + 1);
if( MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str,
slen, (LPWSTR)buffer, length ) > 0)
rtn = env->NewString((jchar*)buffer, length);
}
if(buffer)
{
free(buffer);
buffer = NULL;
}
return rtn;
}
}
JNIEXPORT void JNICALL Java_cn_net_ynst_kjjl_util_Utils_generateWordDocument
(JNIEnv *env, jclass clazz, jstring fileName, jobjectArray bookmarkNames,
jint bookmarkCount, jobjectArray values, jint valueCount)
{
if(bookmarkCount != valueCount)
return;
int len = 0;
char* _fileName = jstringToWindows(env, fileName);
len = MultiByteToWideChar(CP_ACP, 0,
(LPCSTR)_fileName, strlen(_fileName) + 1, NULL, 0);
wchar_t* _w_fileName = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_fileName, -1, _w_fileName, len);
comstl::com_init init;
object word = object::create("Word.Application", CLSCTX_LOCAL_SERVER,
vole::coercion_level::naturalPromotion);
word.put_property(L"Visible", false);
collection documents = word.get_property<collection>(L"Documents");
object document = documents.invoke_method<object>
(L"Open", (LPCWSTR)_w_fileName);
collection bookmarks = document.get_property<collection>(L"Bookmarks");
int count = bookmarks.get_property<int>(L"Count");
for(int i = 1; i <= count; i++)
{
object bookmark = bookmarks.invoke_method<object>(L"Item", i);
std::string name = bookmark.get_property<std::string>(L"Name");
for(int j = 0; j < bookmarkCount; j++)
{
jstring bookmarkName =
(jstring)env->GetObjectArrayElement(bookmarkNames, j);
const char* _bookmarkName = env->GetStringUTFChars(bookmarkName, 0);
if(strcmp(name.c_str(), _bookmarkName) == 0)
{
jstring value = (jstring)env->GetObjectArrayElement(values, j);
char* _value = jstringToWindows(env, value);
len = MultiByteToWideChar(CP_ACP, 0,
(LPCSTR)_value, strlen(_value) + 1, NULL, 0);
wchar_t* _w_value = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)_value, -1, _w_value, len);
object range = bookmark.get_property<object>(L"Range");
range.invoke_method_v(L"InsertAfter", (LPCWSTR)_w_value);
free(_value);
free(_w_value);
_value = NULL;
_w_value = NULL;
env->ReleaseStringUTFChars(bookmarkName, _bookmarkName);
break;
}
env->ReleaseStringUTFChars(bookmarkName, _bookmarkName);
}
}
document.invoke_method_v(L"Save");
document.invoke_method_v(L"Close", false);
word.invoke_method_v(L"Quit");
free(_fileName);
free(_w_fileName);
}
- Create a new Word document in Word, and insert two bookmarks named
title
and year
, save it and name it as 1.doc, path is C:\. We will use it as a template file.
When all the above steps have been completed, we can compile it in Code::Blocks
, and get a file named zztuils.dll, please copy this file into project zzutils
which is created by eclipse. Then we could run main
method in class Utils
.
Advantages & Shortcomings
Advantages
- We can use features of COM technology to create Word document easily.
- We can code less than what projects which use apache poi or something else do.
Shortcomings
- This program can run under Windows platform only.
- Deploy it into tomcat and it could be crashed when called 5-6 times. I will try web service way to avoid this problem.
History
- 2nd December, 2010: Initial post