缘起

最近客户要求把业务字段生成一个pdf,包含大量的表格,于是探究了两天版,终于找出一个比较完美的解决方案。本次采用的是docx4j,Libreoffice两个套件,docx4j本身有转PDF的功能,但是转换完成的PDF乱码,我没有找到解决乱码的方法(比较菜,勿喷),然后选择了Libreoffice的方式,期间也是解决了很多bug,然后最终成功

安装 Libreoffice

之前在网上看到也有使用openoffice的,我之前也试过,转成pdf格式全变了,尤其是表格,后来发现的兼容性更好的Libreoffice,下面开始安装。

  1. 官网地址:https://www.libreoffice.org/
  2. 点击download,然后选择系统,我的是win10 64位的,所以选择Windows x86_64
  3. 点下载后开始下载,如果感觉下载比较慢,在chrome(我使用的是谷歌浏览器)的下载内容里面,复制下载链接,拷贝到迅雷里面,我就是用的这种方法,下载速度还不错
  4. 下载下来是msi文件,开始安装,我自定义了自己的路径:D:\libreOffice6\program\soffice -headless --accept="socket,host=127.0.0.1,port=8100;urp;" -nologo -nofirststartwizard,host如果是127.0.0.1的话是只能本机访问,要是想让所有机器都能访问,就改为0.0.0.0,port是端口号。记住一定要加 -handless这个参数,不然在windows下调试一定会没法退出程序,这个参数的意思就是无界面运行。

coding

新建一个maven工程,pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>net.blf2</groupId><artifactId>test-docx4j</artifactId><version>0.1</version><name>Testdocx4j</name><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><skipTests>true</skipTests></properties><dependencies><!-- https://mvnrepository.com/artifact/org.docx4j/docx4j --><dependency><groupId>org.docx4j</groupId><artifactId>docx4j</artifactId><version>3.3.7</version></dependency><!-- https://mvnrepository.com/artifact/org.docx4j/docx4j-export-fo --><dependency><groupId>org.docx4j</groupId><artifactId>docx4j-export-fo</artifactId><version>3.3.1</version></dependency><!-- https://mvnrepository.com/artifact/org.openoffice/bootstrap-connector --><dependency><groupId>org.openoffice</groupId><artifactId>bootstrap-connector</artifactId><version>0.1.1</version></dependency></dependencies>
</project>

代码:

package main.java.net.blf2;import com.sun.star.beans.PropertyValue;
import com.sun.star.frame.XComponentLoader;
import com.sun.star.frame.XDesktop;
import com.sun.star.frame.XStorable;
import com.sun.star.lang.XComponent;
import com.sun.star.lang.XMultiComponentFactory;
import com.sun.star.uno.UnoRuntime;
import com.sun.star.uno.XComponentContext;
import ooo.connector.BootstrapSocketConnector;
import org.docx4j.jaxb.Context;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.File;
import java.math.BigInteger;public class Docx4jUtil {private static final Logger logger = LoggerFactory.getLogger(Docx4jUtil.class);public static WordprocessingMLPackage wordPackage = null;public static MainDocumentPart mainDocumentPart = null;public static ObjectFactory objectFactory = null;/*** 创建word文档*/public static void createDocx() {try {wordPackage = WordprocessingMLPackage.createPackage();mainDocumentPart = wordPackage.getMainDocumentPart();objectFactory = Context.getWmlObjectFactory();} catch (InvalidFormatException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}if (wordPackage == null || mainDocumentPart == null || objectFactory == null) {logger.error("创建word文档失败!!!");wordPackage = null;mainDocumentPart = null;objectFactory = null;}}/*** 横向合并单元格* @param mergeTable 需要合并的table* @param beginColumn 开始列(从0开始算起)* @param endColumn 结束列(从0开始算起)* @param row 需要合并的行(从0开始算起)* @param content 合并后内容*/public static void mergeTdCell(Tbl mergeTable, int beginColumn, int endColumn, int row, String content) {Tr tr = (Tr) mergeTable.getContent().get(row);Tc beginTd = (Tc) tr.getContent().get(beginColumn);CTVerticalJc vjc = new CTVerticalJc();vjc.setVal(STVerticalJc.CENTER);beginTd.getTcPr().setVAlign(vjc);//上下垂直居中TcPrInner.HMerge beginMerge = new TcPrInner.HMerge();beginMerge.setVal("restart");beginTd.getTcPr().setHMerge(beginMerge);for (int i = beginColumn + 1; i <= endColumn; i++) {Tc endTd = (Tc) tr.getContent().get(i);TcPrInner.HMerge endMerge = new TcPrInner.HMerge();endMerge.setVal("continue");endTd.getTcPr().setHMerge(endMerge);}beginTd.getContent().clear();beginTd.getContent().add(mainDocumentPart.createParagraphOfText(content));}/*** 纵向合并单元格** @param mergeTable 需要合并的表格* @param beginRow   开始行(从0开始算起)* @param endRow     结束行(从0算起)* @param column     列号(从0开始)*/public static void mergeMdCell(Tbl mergeTable, int beginRow, int endRow, int column, String content) {CTVerticalJc vjc = new CTVerticalJc();vjc.setVal(STVerticalJc.CENTER);Tc beginTd = null;for(int i = beginRow;i <= endRow;i++) {Tr tr = (Tr) mergeTable.getContent().get(i);beginTd = (Tc) tr.getContent().get(column);VMerge merge = new VMerge();merge.setVal("restart");beginTd.getTcPr().setVMerge(merge);beginTd.getTcPr().setVAlign(vjc);//上下垂直居中}beginTd.getContent().clear();beginTd.getContent().add(mainDocumentPart.createParagraphOfText(content));}/*** 根据一个二维数据创建表格* @param content 二维数组 带内容* @return*/public static Tbl createDataTable(String[][] content) {Tbl table = objectFactory.createTbl();PPr ppr = new PPr();Jc jc = new Jc();jc.setVal(JcEnumeration.CENTER);ppr.setJc(jc);//单元格文本居中使用for (int i = 0; i < content.length; i++) {Tr dataTr = objectFactory.createTr();for (int j = 0; j < content[i].length; j++) {Tc tc = objectFactory.createTc();tc.setTcPr(new TcPr());tc.getContent().add(mainDocumentPart.createParagraphOfText(content[i][j]));dataTr.getContent().add(tc);}table.getContent().add(dataTr);}setTcBorders(table);return table;}/*** 保存word文件* @param outPathFileName 输出目录+文件名 例如E:\\test\\test.docx*/public static void save(String outPathFileName) {try {wordPackage.save(new File(outPathFileName));} catch (Docx4JException e) {e.printStackTrace();}}/*** 把初始化的实例置为空*/public static void cleanResource() {wordPackage.clone();mainDocumentPart = null;objectFactory = null;logger.info("清理完成...");}/*** 设置单元格边框* @param table*/private static void setTcBorders(Tbl table) {table.setTblPr(new TblPr());// 必须设置一个TblPr,否则最后会报空指针异常CTBorder border = new CTBorder();border.setColor("auto");border.setSz(new BigInteger("4"));border.setSpace(new BigInteger("0"));border.setVal(STBorder.SINGLE);TblBorders borders = new TblBorders();borders.setBottom(border);borders.setLeft(border);borders.setRight(border);borders.setTop(border);borders.setInsideH(border);borders.setInsideV(border);// 获取其内部的TblPr属性设置属性table.getTblPr().setTblBorders(borders);}/*** 把word转为pdf* @param workingDir 工作目录 就是word和pdf所在目录* @param docxFileName word文件名* @param pdfFileName pdf文件名* @return*/public static void convertDocxToPDF2(String workingDir,String docxFileName,String pdfFileName) {try {// InitialiseString oooExeFolder = "D:\\libreOffice6\\program";XComponentContext xContext = BootstrapSocketConnector.bootstrap(oooExeFolder);XMultiComponentFactory xMCF = xContext.getServiceManager();Object oDesktop = xMCF.createInstanceWithContext("com.sun.star.frame.Desktop", xContext);XDesktop xDesktop = (XDesktop) UnoRuntime.queryInterface(XDesktop.class, oDesktop);// Load the DocumentString myTemplate = docxFileName;if (!new File(workingDir + myTemplate).canRead()) {throw new RuntimeException("Cannot load template:" + new File(workingDir + myTemplate));}XComponentLoader xCompLoader = (XComponentLoader) UnoRuntime.queryInterface(com.sun.star.frame.XComponentLoader.class, xDesktop);String sUrl = "file:///" + workingDir + myTemplate;PropertyValue[] propertyValues = new PropertyValue[0];propertyValues = new PropertyValue[1];propertyValues[0] = new PropertyValue();propertyValues[0].Name = "Hidden";propertyValues[0].Value = new Boolean(true);XComponent xComp = xCompLoader.loadComponentFromURL(sUrl, "_blank", 0, propertyValues);// save as a PDFXStorable xStorable = (XStorable) UnoRuntime.queryInterface(XStorable.class, xComp);propertyValues = new PropertyValue[2];propertyValues[0] = new PropertyValue();propertyValues[0].Name = "Overwrite";propertyValues[0].Value = new Boolean(true);propertyValues[1] = new PropertyValue();propertyValues[1].Name = "FilterName";propertyValues[1].Value = "writer_pdf_Export";// Appending the favoured extension to the origin document nameString myResult = workingDir + pdfFileName;xStorable.storeToURL("file:///" + myResult, propertyValues);System.out.println("Saved " + myResult);// shutdownxComp.dispose();xDesktop.terminate();}catch (Exception ex){ex.printStackTrace();}}public static void main(String[] args) {int row = 8;int column = 4;createDocx();String[][] content = new String[row][column];for (int i = 0; i < row; i++) {for (int j = 0; j < column; j++) {content[i][j] = "test" + i + "-" + j;}}Tbl table = createDataTable(content);mainDocumentPart.addObject(table);//mergeTdCell(table,1,3,3,"测试合并");mergeMdCell(table, 3, 5, 1, "测试纵向合并");String workDir = "E:/document/";String docxFileName = "test.docx";save(workDir + docxFileName);cleanResource();convertDocxToPDF2(workDir,docxFileName,docxFileName.replaceAll("docx","pdf"));}}

上文中注释比较全面,比较好理解,搞了三天的成果。

遗憾

一直没能探索出Libreoffice部署在远程机器,使用ip调用的方法,如果谁知道怎么做,请留言告诉我。

java docx4j动态生成表格,保存为word,并通过Libreoffice转PDF相关推荐

  1. java swing 动态生成表格_6 个曾经牛逼哄哄的 Java 技术,你用过吗?

    大家好啊,今天给大家分享下我的开发历程中,我知道的那些被淘汰的技术或者框架,有些我甚至都没有用过,但我知道它曾经风光过. 废话不多说,下面我要开始吹了-- 1.Swing 下面这个是用 swing 开 ...

  2. 应用Java程序片段动态生成表格

    通过Jsp页面动态来显示数据库中的数据,在根据条件进行查询时,将调查结果显示在jsp页面中,使用java程序片段(Scriptlet)动态生成表格 在jsp文件中,可以在"<%&quo ...

  3. 一段动态生成表格的JSP代码讲解

    一段动态生成表格的JSP代码讲解 <table border="1" width="600px" align="center"> ...

  4. JS实现动态生成表格并提交表格数据向后端 表格中数据转为json

    原文地址 需求:在web页面上动态的生成表格,并可以对表格中的数据进行编辑,然后把表格中的数据提交至后端服务器保存. 首先动态生成表格. 1.首先我们需要导入JS库文件.jQuery 2.然后在页面d ...

  5. django通过ajax请求接口返回多条数据,并动态生成表格,请求表单后将表格数据并入库

    一.最近在做接口相关的开发,需求是这样的,通过一个接口所需要传递的参数,调用接口后,处理接口响应的参数,返回多条数据,并动态生成表格,请求表单后将表格的数据入库,下面是我改过的代码,跟实际代码有些出入 ...

  6. JSTL标签库动态生成表格

    项目中遇到一个动态生成表格的问题,由于表格的行和列都不是固定的,而是从数据库中取得的,因此需要动态的创建表格. 由于规范中要求使用JSTL标签库,避免JSP页面冗余java代码,而我的数据库暂时又连不 ...

  7. php输出动态表格,PHP动态生成表格

    好文网为大家准备了关于PHP动态生成表格范文,好文网里面收集了五十多篇关于好PHP动态生成表格好文,希望可以帮助大家.更多关于PHP动态生成表格内容请关注好文网篇一:PHP生成静态网页的通用代码最近研 ...

  8. 动态生成表格之新增学生信息

    动态生成表格之新增学生信息: 功能需求: 当点击新增按钮时弹出模态框 不允许其中一项为空 点击"保存"时,数据保存并添加成功后关闭模态框 前期准备工作: 1.引入bootstrap ...

  9. Extjs 动态生成表格

    在web显示数据时,会遇到grid的列数和行数不确定的这种情况.如何来根据数据动态的创建表格呢? Extjs 的json data给我们带来了一个很好的比较简单的方法. 要创建一个grid需要确定它的 ...

最新文章

  1. c++排查线程hang住_Kafka学习笔记之kafka高版本Client连接0.9Server引发的血案排查 - 时光飞逝,逝者如斯...
  2. HTML使用vue的 event,vue-js 特殊变量$event常识
  3. MacOS 下如何创建文本文件
  4. tdms打开闪退问题
  5. phpstudy配置oracle,phpStudy配置sql、oracle---博主摘录
  6. 使用java理解程序逻辑 第十二章_Java多线程中锁的理解与使用(二)
  7. idea搭建java openCV环境
  8. 手把手教你做酷炫的数据可视化大屏,零基础的你仅需6步
  9. ae图片无缝循环滚动_HTML图片滚动
  10. [高光谱] Hyperspectral-Classification-master 网络模型解析
  11. webgis之相关工具
  12. 计算机应用中英文缩写ai表示,2010黑龙江省全国计算机等级考试二级VB笔试试卷及参考答案考试重点和考试技巧...
  13. 金山文字应用技巧两则(转)
  14. 桌面计算机未响应,电脑软件总是未响应 电脑应用程序经常无响应,
  15. threejs效果记录
  16. 多种网络请求方式 ,这么骚气的操作确定不来看看嘛?
  17. Mac OS X 窗口操作快捷键
  18. 在setTimeout或者ajax等异步方法中回调函数的写法与调用
  19. 【大众点评评论爬虫】一键获取大众点评完整评论工具批量爬取保存为excel数据
  20. 关于Md5加密算法的原理及应用

热门文章

  1. vue组件间通信之兄弟组件通信(vue3.0$EventBus)
  2. 基于NodeJS健身房会员管理小程序的设计与实现
  3. 尚硅谷Python自学笔记
  4. Arduino使用高感度声音模块
  5. Java驼峰转换工具
  6. 成功解决windows server 2012 r2不显示桌面计算机图标
  7. POJ 3669-Meteor Shower [bfs] 《挑战程序设计竞赛》2.1
  8. SSM毕设项目社区生鲜团购系统v45x6(java+VUE+Mybatis+Maven+Mysql)
  9. 文库发表评论奖励规则说明
  10. 上课笔记-营销管理(一)