12、借助Jacob实现Java打印报表(Excel、Word)
12、使用Jacob来处理文档
Word或Excel程序是以一种COM组件形式存在的。如果能够在Java中调用相应组件,便能使用它的方法来获取文档中的文本信息。Jacob是一个JAVA到微软的COM接口的桥梁。Jacob允许任何JVM访问COM对象,从而使JAVA应用程序能够调用COM对象。如果要对 MS Word、Excel 进行处理,Jacob 是一个好的选择。
12.1、Jacob的下载
Jacob 是Java-COM Bridge的缩写,它在Java与微软的COM组件之间构建一座桥梁。使用Jacob自带的DLL动态链接库,并通过JNI的方式实现了在Java平台上对COM程序的调用。Jacob下载的地址为:
http://sourceforge.net/project/showfiles.php?group_id=109543&package_id=118368。
12.2、在Eclipse中的配置
(1) 将jacob.jar导入工程的Build Path,然后确认自己机器的CPU类型(X86或AMD64),并选择不同目录下的jacob.dll文件。
(2) 将jacob.dll放到%JAVA_HOME%\jre\bin目录下,其中,%JAVA_HOME%就是JDK的安装目录。注意这个的jre目录必须是Eclipse当前正在使用的目录,在Eclipse中选择“window->Preferences”菜单,在弹出的对话框中选择“Java->Installed JREs”项。
(3) 当前选择的JRE是“C:\Program Files\Java\jdk1.5.0_07\jre”目录下的,所以需要把jacob.dll复制到“C:\Program Files\Java\jdk1.5.0_07\jre\bin”目录下面。
(4) 在工程中新建一个ch7.jacob包,并在包中创建WordReader类。该类将提供一个静态的extractDoc()方法。它接收两个参数,一个是要处理的DOC文件名,另一个则是输出的文件名,然后通过JNI调用Word的API转换内容,该函数的代码如下。
1 public static void extractDoc(String inputFIle, String outputFile) { 2 3 boolean flag = false; 4 5 6 7 // 打开Word应用程序 8 9 ActiveXComponent app = new ActiveXComponent("Word.Application"); 10 11 try { 12 13 // 设置word不可见 14 15 app.setProperty("Visible", new Variant(false)); 16 17 // 打开word文件 18 19 Dispatch doc1 = app.getProperty("Documents").toDispatch(); 20 21 Dispatch doc2 = Dispatch.invoke( 22 23 doc1, 24 25 "Open", 26 27 Dispatch.Method, 28 29 new Object[] { inputFIle, new Variant(false), 30 31 new Variant(true) }, new int[1]).toDispatch(); 32 33 // 作为txt格式保存到临时文件 34 35 Dispatch.invoke(doc2, "SaveAs", Dispatch.Method, new Object[] { 36 37 outputFile, new Variant(7) }, new int[1]); 38 39 // 关闭word 40 41 Variant f = new Variant(false); 42 43 Dispatch.call(doc2, "Close", f); 44 45 flag = true; 46 47 } catch (Exception e) { 48 49 e.printStackTrace(); 50 51 } finally { 52 53 app.invoke("Quit", new Variant[] {}); 54 55 } 56 57 if (flag == true) { 58 59 System.out.println("Transformed Successfully"); 60 61 } else { 62 63 System.out.println("Transform Failed"); 64 65 } 66 67 }
注意:在使用Jacob时,很重要的一点是,用户本地系统中必须安装有Word的应用程序。否则也就无法建立Java-COM桥,进而无法解析了。
12.3、Jacob中常用方法
(1) 初始化com的线程,很重要,否则第二次创建com对象的时候会出现can't co-create object异常,完成操作com组件后要调用release方法。
ComThread.InitSTA();// 初始化com的线程
(2) 初始化word应用程序,新建一个空白文档,取得文档内容对象
//Instantiate objWord //Declare word object
ActiveXComponent objWord = new ActiveXComponent("Word.Application");
//Assign a local word object
Dispatch wordObject = (Dispatch) objWord.getObject();
//Create a Dispatch Parameter to show the document that is opened
Dispatch.put((Dispatch) wordObject, "Visible", new Variant(true));
// new Variant(true)表示word应用程序可见
Tip:设置一个对象的属性的时候,利用Dispatch的put方法,给属性赋值。上面这行语句相当于vb的wordObject.Visible=true语句。
//Instantiate the Documents Property
Dispatch documents = objWord.getProperty("Documents").toDispatch();
//documents表示word的所有文档窗口。
//Add a new word document, Current Active Document
Dispatch document = Dispatch.call(documents, "Add").toDispatch();
// 使用Add命令创建一个新文档,用Open命令可以打开一个现有文档
Tip:调用一个对象的方法的时候,利用Dispatch的call方法,上面的语句相当于vb的document = documents.Add() 语句。
Dispatch wordContent = Dispatch.get(document, "Content").toDispatch();
// 取得word文件的内容
Tip:取得一个对象的成员变量(属性)时,利用Dispatch的get方法,上面的语句相当于vb的wordContent = document.Content语句。
(3) 取得word文档的内容后,可以对其内容进行操作
Dispatch.call(wordContent, "InsertAfter", "这里是一个段落的内容");//插入一个段落
(4) 设置刚插入的段落的文字格式
Dispatch paragraphs = Dispatch.get(wordContent, "Paragraphs").toDispatch();
// 获取所有段落
int paragraphCount = Dispatch.get(paragraphs, "Count").toInt();
// 总的段落数
Dispatch lastParagraph = Dispatch.call(paragraphs, "Item",new Variant(paragraphCount)).toDispatch();
// 最后一段
Dispatch lastParagraphRange = Dispatch.get(lastParagraph, "Range").toDispatch();
Dispatch font = Dispatch.get(lastParagraphRange, "Font").toDispatch();
Dispatch.put(font, "Bold", new Variant(true));
// 设置为黑体
Dispatch.put(font, "Italic", new Variant(true));
// 设置为斜体
Dispatch.put(font, "Name", new Variant("宋体")); //
Dispatch.put(font, "Size", new Variant(12)); //小四
注意:如果想插入一个新的空白行,也需要设置段落的文字格式,否则新插入行的文字格式会于刚插入的段落的格式相同。
(5) 将当前文档保存
Dispatch.call(document, "SaveAs", new Variant("C:abc.doc")); // 保存一个新文档
(6) 释放COM线程
ComThread.Release();//释放com线程。根据Jacob的帮助文档,com的线程回收不由java的垃圾回收器处理。
12.4、Jacob中常用模块
(1) 打印模块
1 ActiveXComponent axc = null; 2 try { 3 ComThread.InitSTA(); 4 axc = new ActiveXComponent("Excel.Application"); 5 Dispatch.put(axc, "Visible", new Variant(false)); 6 Dispatch workbooks = axc.getProperty("Workbooks").toDispatch(); 7 Dispatch workbook = Dispatch.call(workbooks, "Open", fileFullPath, 8 new Integer(0), Boolean.FALSE).toDispatch(); 9 Dispatch sheets = Dispatch.call(workbook, "Worksheets").toDispatch(); 10 Dispatch sheet = Dispatch.call(sheets, "Item", new Integer(1)) 11 .toDispatch(); 12 Dispatch.call(sheet, "PrintOut", new Integer(beginPage), new Integer(endPage), new Integer(copys)); 13 Dispatch.call(workbook, "Close", Variant.VT_FALSE); 14 } finally { 15 axc.invoke("Quit", new Variant[] {}); 16 ComThread.Release(); 17 }
(2) 纸张大小设置
1 Dispatch pageSetup = Dispatch.call(sheet, "PageSetup").toDispatch(); 2 Dispatch.put(pageSetup, "PaperSize", new Integer(8));//A3是8,A4是9,A5是11等等
可通过如下方式获取纸张大小所对应的整数值
1 int ps = Dispatch.get(pageSetup, "PaperSize").toInt(); 2 System.out.println("ps=" + ps);
(3)页边距设置
1 Dispatch.put(pageSetup, "LeftMargin", new Variant(left)); //左 2 Dispatch.put(pageSetup, "TopMargin", new Variant(top)); //上 3 Dispatch.put(pageSetup, "RightMargin", new Variant(right)); //右 4 Dispatch.put(pageSetup, "BottomMargin", new Variant(bottom)); //下 5 Dispatch.put(pageSetup, "HeaderMargin", new Variant(header)); //页眉 6 Dispatch.put(pageSetup, "FooterMargin", new Variant(footer)); //页脚
注: left、top、right、bottom、header和footer是double类型的数值,并且如果单位是厘米的话,需乘以28.35。
(4) 统计Excel已使用的行数
1 Dispatch userRange=Dispatch.call(sheet, "UsedRange").toDispatch(); 2 Dispatch row=Dispatch.call(userRange, "Rows").toDispatch(); 3 int rowCount=Dispatch.get(row,"Count").getInt(); 4 System.out.println("rowCount=" + rowCount);
(5) 在指定的行号上插入一空行
1 Dispatch rowSheet = Dispatch.call(sheet, "Rows", new Variant(i)).toDispatch();//这里的i是行号,int类型 2 rowSheet.call(rowSheet, "Insert");
(6) 根据不同版本的Excel做不同的处理
1 System.out.println("version=" + axc.getProperty("Version"));
12.5、程序实例
(1)实例一:
1 package com.xfzx.test.POI.main; 2 3 import com.jacob.activeX.ActiveXComponent; 4 import com.jacob.com.ComThread; 5 import com.jacob.com.Dispatch; 6 import com.jacob.com.Variant; 7 8 public class JacobPress { 9 10 /** 11 * @param args 12 */ 13 public static void main(String[] args) { 14 // TODO Auto-generated method stub 15 printWord("D:/txt.docx"); 16 // printExcel("D:/提醒通知明细通用模板.xlsx"); 17 } 18 19 public static void printExcel(String filePath) { 20 /** 21 * 功能:实现打印工作 22 */ 23 ComThread.InitSTA(); 24 ActiveXComponent xl = new ActiveXComponent("Excel.Application"); 25 try { 26 // System.out.println("version=" + xl.getProperty("Version")); 27 // 不打开文档 28 Dispatch.put(xl, "Visible", new Variant(true)); 29 Dispatch workbooks = xl.getProperty("Workbooks").toDispatch(); 30 // 打开文档 31 Dispatch excel = Dispatch.call(workbooks, "Open", filePath) 32 .toDispatch(); 33 // 开始打印 34 Dispatch.call(excel, "PrintOut"); 35 xl.invoke("Quit", new Variant[] {}); 36 } catch (Exception e) { 37 e.printStackTrace(); 38 } finally { 39 // 始终释放资源 40 ComThread.Release(); 41 } 42 } 43 44 public static void printWord(String filePath) { 45 ComThread.InitSTA(); 46 ActiveXComponent wd = new ActiveXComponent("Word.Application"); 47 try { 48 // 不打开文档 49 Dispatch.put(wd, "Visible", new Variant(true)); 50 Dispatch document = wd.getProperty("Documents").toDispatch(); 51 // 打开文档 52 Dispatch doc = Dispatch.invoke(document, "Open", Dispatch.Method, 53 new Object[] { filePath }, new int[1]).toDispatch(); 54 // 开始打印 55 Dispatch.callN(doc, "PrintOut"); 56 wd.invoke("Quit", new Variant[] {}); 57 } catch (Exception e) { 58 e.printStackTrace(); 59 } finally { 60 // 始终释放资源 61 ComThread.Release(); 62 } 63 } 64 65 // 获得文件后缀名 66 public static String getPostfix(String inputFilePath) { 67 String[] p = inputFilePath.split("\\."); 68 if (p.length > 0) {// 判断文件有无扩展名 69 // 比较文件扩展名 70 return p[p.length - 1]; 71 } else { 72 return null; 73 } 74 } 75 76 }
(2)实例二:
1 public static boolean printOfficeFile(File f) { 2 if (f != null && f.exists()) { 3 String fileNameString = f.getName(); 4 String postfixString = Utils.getPostfix(fileNameString); 5 if (postfixString.equalsIgnoreCase("xls") 6 || postfixString.equalsIgnoreCase("xlsx")) { 7 /** 8 * 功能:实现excel打印工作 9 */ 10 ComThread.InitSTA(); 11 ActiveXComponent xl = new ActiveXComponent("Excel.Application"); 12 try { 13 // System.out.println("version=" + 14 // xl.getProperty("Version")); 15 // 不打开文档 16 Dispatch.put(xl, "Visible", new Variant(false)); 17 Dispatch workbooks = xl.getProperty("Workbooks") 18 .toDispatch(); 19 // 打开文档 20 Dispatch excel = Dispatch.call(workbooks, "Open", 21 f.getAbsolutePath()).toDispatch(); 22 // 横向打印(2013/05/24) 23 // Dispatch currentSheet = Dispatch.get(excel, "ActiveSheet") 24 // .toDispatch(); 25 // Dispatch pageSetup = Dispatch 26 // .get(currentSheet, "PageSetup").toDispatch(); 27 // Dispatch.put(pageSetup, "Orientation", new Variant(2)); 28 //每张表都横向打印2013-10-31 29 Dispatch sheets = Dispatch.get((Dispatch) excel, "Sheets") 30 .toDispatch(); 31 // 获得几个sheet 32 int count = Dispatch.get(sheets, "Count").getInt(); 33 // System.out.println(count); 34 for (int j = 1; j <=count; j++) { 35 Dispatch sheet = Dispatch.invoke(sheets, "Item", 36 Dispatch.Get, new Object[] { new Integer(j) }, 37 new int[1]).toDispatch(); 38 Dispatch pageSetup = Dispatch.get(sheet, "PageSetup").toDispatch(); 39 Dispatch.put(pageSetup, "Orientation", new Variant(2)); 40 Dispatch.call(sheet, "PrintOut"); 41 } 42 // 开始打印 43 if (excel != null) { 44 //Dispatch.call(excel, "PrintOut"); 45 //增加以下三行代码解决文件无法删除bug 46 Dispatch.call(excel, "save"); 47 Dispatch.call(excel, "Close" , new Variant(true)); 48 excel=null; 49 } 50 xl.invoke("Quit", new Variant[] {}); 51 xl=null; 52 return true; 53 } catch (Exception e) { 54 e.printStackTrace(); 55 return false; 56 } finally { 57 // 始终释放资源 58 ComThread.Release(); 59 } 60 } else if (postfixString.equalsIgnoreCase("doc") 61 || postfixString.equalsIgnoreCase("docx")) { 62 ComThread.InitSTA(); 63 ActiveXComponent wd = new ActiveXComponent("Word.Application"); 64 try { 65 // 不打开文档 66 Dispatch.put(wd, "Visible", new Variant(false)); 67 Dispatch document = wd.getProperty("Documents") 68 .toDispatch(); 69 // 打开文档 70 Dispatch doc = Dispatch.invoke(document, "Open", 71 Dispatch.Method, new Object[] { f.getAbsolutePath() }, 72 new int[1]).toDispatch(); 73 // 开始打印 74 if (doc != null) { 75 Dispatch.call(doc, "PrintOut"); 76 //增加以下三行代码解决文件无法删除bug 77 Dispatch.call(doc, "save"); 78 Dispatch.call(doc, "Close" , new Variant(true)); 79 doc=null; 80 } 81 wd.invoke("Quit", new Variant[] {}); 82 wd=null; 83 return true; 84 } catch (Exception e) { 85 e.printStackTrace(); 86 return false; 87 } finally { 88 // 始终释放资源 89 ComThread.Release(); 90 } 91 } else { 92 return false; 93 } 94 } else { 95 return false; 96 } 97 }