完美解决docx4j变量替换的问题

目录
  • 测试代码
  • docx4j版本
  • 下面是工具类

最近工作上需要自己完成word文档变量替换的问题

把里面的变量给替换成数据库里的值,但是由于在word文档渲染成xml的时候,会通过某些原因把字段放在不同层次的xml标签

上面是docx4j文档说的原因,大概是字体格式不同(我的问题是用了粗体 ${ 和 正常中文是不同格式的),拼写语法问题,编辑顺序。

在StackOverflow 找了很久解决方案,Variableprepare.prepare方法确实测试后能解决部分替换问题,但还是不能满足我的需求。

阅读源码后重新清扫了一下字符串。

测试代码

  public static void main(String[] args) throws Exception {        File file = new File("C:\\Users\\scoli\\Desktop\\t.docx");        WordprocessingMLPackage doc = WordprocessingMLPackage.load(new FileInputStream(file));        Docx4jUtils.cleanDocumentPart(doc.getMainDocumentPart());        Map map = new HashMap();        map.put("订单编号", "1");        OutputStream outputStream = new FileOutputStream(new File("C:\\Users\\scoli\\Desktop\\test1.docx"));        MainDocumentPart mainDocumentPart = doc.getMainDocumentPart();        if (!map.isEmpty()) {            // 替换文本内容            mainDocumentPart.variableReplace(map);        }        // 输出word文件        doc.save(outputStream);        outputStream.flush();    }

docx4j版本

 <!--docx4j支持 start-->        <dependency>            <groupId>org.docx4j</groupId>            <artifactId>docx4j</artifactId>            <version>3.3.6</version>            <exclusions>                <exclusion>                    <groupId>org.slf4j</groupId>                    <artifactId>slf4j-log4j12</artifactId>                </exclusion>            </exclusions>        </dependency>        <dependency>            <groupId>org.docx4j</groupId>            <artifactId>docx4j-export-fo</artifactId>            <version>3.3.6</version>        </dependency>        <!--docx4j支持 end-->

下面是工具类

package zwy.saas.common.util;  import org.docx4j.XmlUtils;import org.docx4j.jaxb.Context;import org.docx4j.openpackaging.packages.WordprocessingMLPackage;import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;import org.docx4j.wml.Document;import org.slf4j.Logger;import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletResponse;import javax.xml.bind.JAXBContext;import javax.xml.bind.JAXBException;import java.io.InputStream;import java.io.OutputStream;import java.net.URLEncoder;import java.util.Map;import java.util.regex.Pattern; /** * 关于文件操作的工具类 * * @author kaizen * @date 2018-10-23 17:21:36 */public final class Docx4jUtils {     private static final Logger logger = LoggerFactory.getLogger(Docx4jUtils.class);     /**     * 替换变量并下载word文档     *     * @param inputStream     * @param map     * @param response     * @param fileName     */    public static void downloadDocUseDoc4j(InputStream inputStream, Map<String, String> map,                                          HttpServletResponse response, String fileName) {         try {            // 设置响应头            fileName = URLEncoder.encode(fileName, "UTF-8");            response.setContentType("application/octet-stream;charset=UTF-8");            response.setCharacterEncoding("utf-8");            response.setHeader("Content-Disposition", "attachment; filename=" + fileName + ".docx");            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");             OutputStream outs  = response.getOutputStream();            Docx4jUtils.replaceDocUseDoc4j(inputStream,map,outs);         } catch (Exception e) {            logger.error(e.getMessage(), e);        }    }     /**     * 替换变量并输出word文档     * @param inputStream     * @param map     * @param outputStream     */    public static void replaceDocUseDoc4j(InputStream inputStream, Map<String, String> map,                                          OutputStream outputStream) {        try {            WordprocessingMLPackage doc = WordprocessingMLPackage.load(inputStream);            MainDocumentPart mainDocumentPart = doc.getMainDocumentPart();            if (null != map && !map.isEmpty()) {                // 将${}里的内容结构层次替换为一层                Docx4jUtils .cleanDocumentPart(mainDocumentPart);                // 替换文本内容                mainDocumentPart.variableReplace(map);            }             // 输出word文件            doc.save(outputStream);            outputStream.flush();        } catch (Exception e) {            logger.error(e.getMessage(), e);        }    }      /**     * cleanDocumentPart     *     * @param documentPart     */    public static boolean cleanDocumentPart(MainDocumentPart documentPart) throws Exception {        if (documentPart == null) {            return false;        }        Document document = documentPart.getContents();        String wmlTemplate =                XmlUtils.marshaltoString(document, true, false, Context.jc);        document = (Document) XmlUtils.unwrap(DocxVariableClearUtils.doCleanDocumentPart(wmlTemplate, Context.jc));        documentPart.setContents(document);        return true;    }     /**     * 清扫 docx4j 模板变量字符,通常以${variable}形式     * <p>     * XXX: 主要在上传模板时处理一下, 后续     *     * @author liliang     * @since 2018-11-07     */    private static class DocxVariableClearUtils {          /**         * 去任意XML标签         */        private static final Pattern XML_PATTERN = Pattern.compile("<[^>]*>");         private DocxVariableClearUtils() {        }         /**         * start符号         */        private static final char PREFIX = '$';         /**         * 中包含         */        private static final char LEFT_BRACE = '{';         /**         * 结尾         */        private static final char RIGHT_BRACE = '}';         /**         * 未开始         */        private static final int NONE_START = -1;         /**         * 未开始         */        private static final int NONE_START_INDEX = -1;         /**         * 开始         */        private static final int PREFIX_STATUS = 1;         /**         * 左括号         */        private static final int LEFT_BRACE_STATUS = 2;         /**         * 右括号         */        private static final int RIGHT_BRACE_STATUS = 3;          /**         * doCleanDocumentPart         *         * @param wmlTemplate         * @param jc         * @return         * @throws JAXBException         */        private static Object doCleanDocumentPart(String wmlTemplate, JAXBContext jc) throws JAXBException {            // 进入变量块位置            int curStatus = NONE_START;            // 开始位置            int keyStartIndex = NONE_START_INDEX;            // 当前位置            int curIndex = 0;            char[] textCharacters = wmlTemplate.toCharArray();            StringBuilder documentBuilder = new StringBuilder(textCharacters.length);            documentBuilder.append(textCharacters);            // 新文档            StringBuilder newDocumentBuilder = new StringBuilder(textCharacters.length);            // 最后一次写位置            int lastWriteIndex = 0;            for (char c : textCharacters) {                switch (c) {                    case PREFIX:                        // TODO 不管其何状态直接修改指针,这也意味着变量名称里面不能有PREFIX                        keyStartIndex = curIndex;                        curStatus = PREFIX_STATUS;                        break;                    case LEFT_BRACE:                        if (curStatus == PREFIX_STATUS) {                            curStatus = LEFT_BRACE_STATUS;                        }                        break;                    case RIGHT_BRACE:                        if (curStatus == LEFT_BRACE_STATUS) {                            // 接上之前的字符                            newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex, keyStartIndex));                            // 结束位置                            int keyEndIndex = curIndex + 1;                            // 替换                            String rawKey = documentBuilder.substring(keyStartIndex, keyEndIndex);                            // 干掉多余标签                            String mappingKey = XML_PATTERN.matcher(rawKey).replaceAll("");                            if (!mappingKey.equals(rawKey)) {                                char[] rawKeyChars = rawKey.toCharArray();                                // 保留原格式                                StringBuilder rawStringBuilder = new StringBuilder(rawKey.length());                                // 去掉变量引用字符                                for (char rawChar : rawKeyChars) {                                    if (rawChar == PREFIX || rawChar == LEFT_BRACE || rawChar == RIGHT_BRACE) {                                        continue;                                    }                                    rawStringBuilder.append(rawChar);                                }                                // FIXME 要求变量连在一起                                String variable = mappingKey.substring(2, mappingKey.length() - 1);                                int variableStart = rawStringBuilder.indexOf(variable);                                if (variableStart > 0) {                                    rawStringBuilder = rawStringBuilder.replace(variableStart, variableStart + variable.length(), mappingKey);                                }                                newDocumentBuilder.append(rawStringBuilder.toString());                            } else {                                newDocumentBuilder.append(mappingKey);                            }                            lastWriteIndex = keyEndIndex;                             curStatus = NONE_START;                            keyStartIndex = NONE_START_INDEX;                        }                    default:                        break;                }                curIndex++;            }            // 余部            if (lastWriteIndex < documentBuilder.length()) {                newDocumentBuilder.append(documentBuilder.substring(lastWriteIndex));            }            return XmlUtils.unmarshalString(newDocumentBuilder.toString(), jc);        }     }}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Java 替换word文档文字并指定位置插入图片

    先说下 需要的依赖包 <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-excelant</artifactId> <version>3.12</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <arti

  • 完美解决docx4j变量替换的问题

    目录 测试代码 docx4j版本 下面是工具类 最近工作上需要自己完成word文档变量替换的问题 把里面的变量给替换成数据库里的值,但是由于在word文档渲染成xml的时候,会通过某些原因把字段放在不同层次的xml标签 上面是docx4j文档说的原因,大概是字体格式不同(我的问题是用了粗体 ${ 和 正常中文是不同格式的),拼写语法问题,编辑顺序. 在StackOverflow 找了很久解决方案,Variableprepare.prepare方法确实测试后能解决部分替换问题,但还是不能满足我的需

  • ExtJs异步无法向外传值和赋值的完美解决办法

    1.Ext.data.Store.load();方法是异步的,下面的方式获得的reCount始终是0,因为还没等后台的方法执行完就赋值了,此时store的record还没获得值. var testStore = new Ext.data.GroupingStore({ proxy : new Ext.data.HttpProxy({ url : '' }), reader : new Ext.data.JsonReader({ root : 'hstamcx', totalProperty :

  • 完美解决node.js中使用https请求报CERT_UNTRUSTED的问题

    只要调用了没有受信的https就会报错:CERT_UNTRUSTED 简单的解决方法就是设置环境变量回避非授信证书的问题. 只要在请求的代码之前加上如下代码即可: process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; 结束!!! 以上就是小编为大家带来的完美解决node.js中使用https请求报CERT_UNTRUSTED的问题全部内容了,希望大家多多支持我们~

  • Mac OS X 下 IntelliJ IDEA、jEdit 等 Java 程序中文标点输入无效的完美解决方法

    Mac OS X 下基于 Java 的程序(如 IntelliJ IDEA.jEdit 等)会出现中文标点输入无效的问题,在中文输入法状态,可以输入中文字,但输入中文标点最后上去的是英文标点.查阅了相关资料,原来这是 Java 自己的 bug.从 Java 8u51 版本开始就出现了这个 bug,一直到现在最新的 Java 8u72 仍然如此,但是老版本 Java 8u45 是没有这个问题的.所以,可以采取变通的方法,在 Mac OS X 上同时装一个老版本的 JDK 8u45,不会影响已经安装

  • Oracle监听器被优化大师挂掉后的完美解决方法

    Oracle监听器被优化大师挂掉后的解决方法: 在启动oracleorahome90tnslistener服务 时出错,信息: 在本地计算机无法启动oracleorahome90tnslistener服务, 错误3:系统找不到指定路径. 我在网上查资料后,发现一般这些文件的位置由注册表的 ORACLE_HOME 变量决定. 先看一看注册表中 HKEY_LOACL_MACHINE/ SOFTWARE/ORACLE 下的 ORACLE_HOME值 如果没有这个值 ,那你一定要添加进去. ORACLE

  • 利用Anaconda完美解决Python 2与python 3的共存问题

    前言 现在Python3 被越来越多的开发者所接受,同时让人尴尬的是很多遗留的老系统依旧运行在 Python2 的环境中,因此有时你不得不同时在两个版本中进行开发,调试. 如何在系统中同时共存 Python2 和 Python3 是开发者不得不面对的问题,一个利好的消息是,Anaconda 能完美解决Python2 和 Python3 的共存问题,而且在 Windows 平台经常出现安装依赖包(比如 MySQL-python)失败的情况也得以解决. Anaconda 是什么? Anaconda

  • 完美解决js传递参数中加号和&号自动改变的方法

    在action中用get方法获得参数,如果参数里有"+",要做处理,否则到后台会变成空格. 解决方案: 1 .改用post方法: 2 .在 js 里用 url = encodeURI(encodeURI(XXX)) ,后台再解码一次: 3 .传递参数的时候直接替换转义,或者直接写转义后的代码 data =  "a + b": data = data.replace(/\+/g, "+"); data = data.replace(/\&/

  • ECSHOP完美解决Deprecated: preg_replace()报错的问题

    随着PHP5.5 的普及,ECSHOP系统又爆出了新的错误.PHP发展到PHP5.5版本以后,有了很多细微的变化.而ECSHOP官方更新又太慢,发现这些问题后也不及时升级,导致用户安装使用过程中错误百出.说了半天,这个新错误到底是什么呢,它的完整错误提示信息是这样的: Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead in....... 注意:不是所有人的ECS

  • 完美解决phpdoc导出文档中@package的warning及Error的错误

    今天在编写PHPDoc的导出文档的时候发现一个很郁闷的错误,虽然这个warning不是什么重要错误,但是看着总是很不爽的.于是就去网上找了很多相关的资料,可是郁闷的是不知道是我用的PHPDoc版本太新(1.4的版本),还是说很多人都没遇到这个问题,反正就是没有相关的这个资料找到,只是找到了一些从PHPDocumentor官方网倒出来的关于@package的使用注意事项,然后就只能一条一条检查,看了一个版本又一个版本,总算是被我解决了. 而且发现该方案可以解决@package之类相关的错误提示:

  • 完美解决Python2操作中文名文件乱码的问题

    Python2默认是不支持中文的,一般我们在程序的开头加上#-*-coding:utf-8-*-来解决这个问题,但是在我用open()方法打开文件时,中文名字却显示成了乱码. 我先给大家说说Python中的编码问题,Python中的字符串的大概分为为str和Unicode两种形式,其中str常用的编码类型为utf-8,gb2312,gbk等等,Python使用Unicode作为编码的基础类型.str记录的是字节数组,只是某种编码的存储格式,终于输出到文件或是打印出来是什么格式,完全取决于其解码的

随机推荐