A simple way to create word documents by template in java under windows platform
Introduction
Here have some open source projects which can create word documents in java, like apache poi, itext, etc. But use COM is a simple way to create word documents in java under windows platform. So I will show how to use JNI technology call windows COM in this article.
Setup development environment
1. Download the latest version of Eclipse from http://www.eclipse.org, and extract compressed file into C:\Eclipse.
2. Download the latest version of Code::Blocks from http://www.codeblocks.org, and install it into C:\CodeBlocks.
3. Download the latest version of vole from http://sourceforge.net/projects/vole, which is a neat C++ COM/Automation driver, we can use it create word documents easily. And vole need depend on the STLSoft libraries, so we need download STLSoft - http://stlsoft.org, too.
4. Extract file vole-0.7.2.zip into C:\vole-0.7.2, and copy directory “vole” into C:\CodeBlocks\MinGW\include which in C:\vole-0.7.2\include.
5. Extract file stlsoft-1.9.100-hdrs.zip into C:\stlsoft-1.9.100-hdrs, and copy all directories into C:\CodeBlocks\MinGW\include which in C:\stlsoft-1.9.100-hdrs\include.
Program development
1. 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 hold 5 parameters, method definition listed below.
public static native void generateWordDocument(String fileName, String[] bookmarkNames, int bookmarkCount, String[] values, int valueCount);
The parameters description:
fileName, the file which save process result.
bookmarkNames, an array which hold bookmarks what you want to insert into.
bookmarkCount, a number which indicate how many bookmarks will be processed.
values, an array which hold values will be insert into right bookmark.
valueCount, a number which indicate how many values will be inserted.
2. generate C++ header file with javah which is a tool in jdk. Create a batch file named generate_header.bat, which content 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.
3. 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, which content 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); }
4. 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 template file.
When complete above all steps, we can compile it in Code::Blocks, and get a file named zztuils.dll, please copy this file into project zzutils which created by eclipse. Then we could run main method in class Utils.
Advantages & Shortcomings
Advantages
1. We can use features of COM technology to create word document easily.
2. We can code less than what projects which use apache poi or something else.
Shortcomings
1. This program can run under windows platform only.
2. Deploy it into tomcat and could be crashed when call 5-6 times. I will try web service way to avoid this problem.