当前位置: 代码网 > it编程>编程语言>Java > Springboot如何根据docx填充生成word文件并导出pdf

Springboot如何根据docx填充生成word文件并导出pdf

2024年08月15日 Java 我要评论
在项目中碰见一个需求,需要将.doc的合同,转换为pdf实现打印与预览功能。将docx模板填充数据生成doc文件1、依赖引入填充docx模板,只需要引入一个pom依赖即可实现。<dependen

在项目中碰见一个需求,需要将.doc的合同,转换为pdf实现打印与预览功能。

将docx模板填充数据生成doc文件

1、依赖引入

填充docx模板,只需要引入一个pom依赖即可实现。

<dependency>
    <groupid>com.deepoove</groupid>
    <artifactid>poi-tl</artifactid>
    <version>1.5.0</version>
</dependency>

2、doc文件转换docx,并标注别名

用office或者wps,创建一个001.doc文件,绘制表格,保存。

更改后缀为.docx,确定后,在指定的位置,表示数据接受变量名称。

如下图所示:

3、编写java代码实现数据填充

import com.deepoove.poi.xwpftemplate;
import org.springframework.core.io.classpathresource;
import org.springframework.core.io.resource;
import java.io.file;
import java.io.fileoutputstream;
import java.io.ioexception;
import java.io.outputstream;
import java.util.hashmap;
import java.util.map;

/**
 * word 填充测试
 */
public class testword {
    public static void main(string[] args) throws ioexception {
        map<string, object> params = new hashmap<>();
        params.put("username","xiangjiao1");
        params.put("password","******");
        params.put("age",22);
        params.put("email","专注写bug测试中文");
        resource resource = new classpathresource("templates_report/001.docx");
        file file = resource.getfile();
        // 数据填充
        xwpftemplate template = xwpftemplate.compile(file).render(params);

        string docoutpath = system.getproperty("user.dir")+file.separator+"springboot-poi"+file.separator+"pdf"+file.separator+ "1.doc";
        outputstream outputstream = new fileoutputstream(docoutpath);
        template.write(outputstream);

    }
}

运行程序,查看结果。

测试项目结构如下:

docx文件填充数据导出pdf(web)

1、依赖引入

docx模板中填充数据,并导出pdf类型的文件,除了上面的pom依赖之外,还需要引入其他的依赖信息,完整依赖如下所示:

<!-- docx 数据填充生成 doc文件  这个是主要 -->
<dependency>
    <groupid>com.deepoove</groupid>
    <artifactid>poi-tl</artifactid>
    <version>1.5.0</version>
</dependency>
<!-- doc 转 pdf -->
<dependency>
    <groupid>com.itextpdf</groupid>
    <artifactid>itextpdf</artifactid>
    <version>5.5.13</version>
</dependency>
<!-- docx4j docx2pdf -->
<dependency>
    <groupid>org.docx4j</groupid>
    <artifactid>docx4j</artifactid>
    <version>6.1.2</version>
</dependency>
<dependency>
    <groupid>org.docx4j</groupid>
    <artifactid>docx4j-export-fo</artifactid>
    <version>6.0.0</version>
</dependency>

2、字体文件

src\main\resources下创建一个font文件夹,其中放入simsun.ttc字体文件。

3、编写工具类

思想很简单

  • 1、先使用上面的docx模板填充数据生成临时doc文件,
  • 2、再将doc文件转换为pdf文件
  • 3、删除临时文件

【注意:】

为了避免出现多人同时操作,导致文件误删的问题,需要尽可能地保证临时文件名称的唯一性。

import com.deepoove.poi.xwpftemplate;
import com.itextpdf.text.*;
import com.itextpdf.text.image;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.slf4j;
import org.apache.commons.io.fileutils;
import org.apache.commons.lang3.text.wordutils;
import org.docx4j.docx4j;
import org.docx4j.convert.out.fosettings;
import org.docx4j.fonts.identityplusmapper;
import org.docx4j.fonts.mapper;
import org.docx4j.fonts.physicalfonts;
import org.docx4j.openpackaging.packages.wordprocessingmlpackage;
import org.springframework.stereotype.component;
import java.io.*;
import java.util.map;
import java.util.uuid;
import java.util.zip.zipoutputstream;
 
/**
 * pdf 导出工具类
 */
@component
@slf4j
public final class freemarkutils {
 
    /**
     * 根据docx模板填充数据  并生成pdf文件
     *
     * @param datamap      数据源
     * @param docxfile     docx模板的文件名
     * @return 生成的文件路径
     */
    public static byte[] createdocx2pdf(map<string, object> datamap, string docxfile) {
        //输出word文件路径和名称 (临时文件名,本次为测试,最好使用雪花算法生成,或者用uuid)
        string filename = uuid.randomuuid().tostring() + ".docx";

        // word 数据填充
        // 生成docx临时文件
        final file temppath = new file(filename);
        final file docxtempfile = gettempfile(docxfile);
        xwpftemplate template = xwpftemplate.compile(docxtempfile).render(datamap);
        try {
            template.write(new fileoutputstream(temppath));
        } catch (ioexception e) {
            e.printstacktrace();
        }

        // word转pdf
        final string pdffile = convertdocx2pdf(filename);
        return getfileoutputstream(new file(pdffile)).tobytearray();
    }
 
    /**
     * word(doc)转pdf
     *
     * @param wordpath doc 生成的临时文件路径
     * @return 生成的带水印的pdf路径
     */
    public static string convertdocx2pdf(string wordpath) {
        outputstream os = null;
        inputstream is = null;
        //输出pdf文件路径和名称  (临时文件  尽可能保证文件名称的唯一性)
        final string filename = uuid.randomuuid().tostring() + ".pdf";
        try {
            is = new fileinputstream(wordpath);
            wordprocessingmlpackage mlpackage = wordprocessingmlpackage.load(is);
            mapper fontmapper = new identityplusmapper();
            fontmapper.put("隶书", physicalfonts.get("lisu"));
            fontmapper.put("宋体", physicalfonts.get("simsun"));
            fontmapper.put("微软雅黑", physicalfonts.get("microsoft yahei"));
            fontmapper.put("黑体", physicalfonts.get("simhei"));
            fontmapper.put("楷体", physicalfonts.get("kaiti"));
            fontmapper.put("新宋体", physicalfonts.get("nsimsun"));
            fontmapper.put("华文行楷", physicalfonts.get("stxingkai"));
            fontmapper.put("华文仿宋", physicalfonts.get("stfangsong"));
            fontmapper.put("宋体扩展", physicalfonts.get("simsun-extb"));
            fontmapper.put("仿宋", physicalfonts.get("fangsong"));
            fontmapper.put("仿宋_gb2312", physicalfonts.get("fangsong_gb2312"));
            fontmapper.put("幼圆", physicalfonts.get("youyuan"));
            fontmapper.put("华文宋体", physicalfonts.get("stsong"));
            fontmapper.put("华文中宋", physicalfonts.get("stzhongsong"));
            //解决宋体(正文)和宋体(标题)的乱码问题
            physicalfonts.put("pmingliu", physicalfonts.get("simsun"));
            physicalfonts.put("新細明體", physicalfonts.get("simsun"));
            // 字体文件
            physicalfonts.addphysicalfonts("simsun", wordutils.class.getresource("/font/simsun.ttc"));
 
            mlpackage.setfontmapper(fontmapper);
            os = new fileoutputstream(filename);
 
            //docx4j  docx转pdf
            fosettings fosettings = docx4j.createfosettings();
            fosettings.setwmlpackage(mlpackage);
            docx4j.tofo(fosettings, os, docx4j.flag_export_prefer_xsl);
            is.close();//关闭输入流
            os.close();//关闭输出流
        } catch (exception e) {
            e.printstacktrace();
        } finally {
            // 删除docx 临时文件
            file file = new file(wordpath);
            if (file != null && file.isfile() && file.exists()) {
                file.delete();
            }
            try {
                if (is != null) {
                    is.close();
                }
                if (os != null) {
                    os.close();
                }
            } catch (exception ex) {
                ex.printstacktrace();
            }
        }
        return filename;
    }
 
    /**
     * 文件转字节输出流
     *
     * @param outfile 文件
     * @return
     */
    public static bytearrayoutputstream getfileoutputstream(file outfile) {
        // 获取生成临时文件的输出流
        inputstream input = null;
        bytearrayoutputstream bytestream = null;
        try {
            input = new fileinputstream(outfile);
            bytestream = new bytearrayoutputstream();
            int ch;
            while ((ch = input.read()) != -1) {
                bytestream.write(ch);
            }
        } catch (filenotfoundexception e) {
            e.printstacktrace();
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            try {
                bytestream.close();
                input.close();
                log.info("删除临时文件");
                if (outfile.exists()) {
                    outfile.delete();
                }
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
        return bytestream;
    }
 
    /**
     * 获取资源文件的临时文件
     * 资源文件打jar包后,不能直接获取,需要通过流获取生成临时文件
     *
     * @param filename 文件路径 templates/xxx.docx
     * @return
     */
    public static file gettempfile(string filename) {
        final file tempfile = new file(filename);
        inputstream fonttempstream = null;
        try {
            fonttempstream = freemarkutils.class.getclassloader().getresourceasstream(filename);
            fileutils.copyinputstreamtofile(fonttempstream, tempfile);
        } catch (exception e) {
            e.printstacktrace();
        } finally {
            try {
                if (fonttempstream != null) {
                    fonttempstream.close();
                }
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
        return tempfile;
    }
 
 
    /**
     * 插入图片水印
     * @param srcbyte 已生成pdf的字节数组(流转字节)
     * @param destfile 生成有水印的临时文件 temp.pdf
     * @return
     */
    public static fileoutputstream addwatermark(byte[] srcbyte, string destfile) {
        // 待加水印的文件
        pdfreader reader = null;
        // 加完水印的文件
        pdfstamper stamper = null;
        fileoutputstream fileoutputstream = null;
        try {
            reader = new pdfreader(srcbyte);
            fileoutputstream = new fileoutputstream(destfile);
            stamper = new pdfstamper(reader, fileoutputstream);
            int total = reader.getnumberofpages() + 1;
            pdfcontentbyte content;
            // 设置字体
            //basefont font = basefont.createfont();
            // 循环对每页插入水印
            for (int i = 1; i < total; i++) {
                final pdfgstate gs = new pdfgstate();
                // 水印的起始
                content = stamper.getundercontent(i);
                // 开始
                content.begintext();
                // 设置颜色 默认为蓝色
                //content.setcolorfill(basecolor.blue);
                // content.setcolorfill(color.gray);
                // 设置字体及字号
                //content.setfontandsize(font, 38);
                // 设置起始位置
                // content.settextmatrix(400, 880);
                //content.settextmatrix(textwidth, textheight);
                // 开始写入水印
                //content.showtextaligned(element.align_left, text, textwidth, textheight, 45);
 
                // 设置水印透明度
                // 设置笔触字体不透明度为0.4f
                gs.setstrokeopacity(0f);
                image image = null;
                image = image.getinstance("url");
                // 设置坐标 绝对位置 x y 这个位置大约在 a4纸 右上角展示logo
                image.setabsoluteposition(472, 785);
                // 设置旋转弧度
                image.setrotation(0);// 旋转 弧度
                // 设置旋转角度
                image.setrotationdegrees(0);// 旋转 角度
                // 设置等比缩放 图片大小
                image.scalepercent(4);// 依照比例缩放
                // image.scaleabsolute(200,100);//自定义大小
                // 设置透明度
                content.setgstate(gs);
                // 添加水印图片
                content.addimage(image);
                // 设置透明度
                content.setgstate(gs);
                //结束设置
                content.endtext();
                content.stroke();
            }
        } catch (ioexception e) {
            e.printstacktrace();
        } catch (documentexception e) {
            e.printstacktrace();
        } finally {
            try {
                stamper.close();
                fileoutputstream.close();
                reader.close();
            } catch (documentexception e) {
                e.printstacktrace();
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
        return fileoutputstream;
    }
}

4、编写测试接口

import cn.xj.util.freemarkutils;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.restcontroller;

import javax.servlet.http.httpservletresponse;
import java.io.ioexception;
import java.util.hashmap;
import java.util.map;
import java.util.uuid;

@restcontroller
@requestmapping("/report")
public class reportcontroller {

    @getmapping("/doc2pdf")
    public void doc2pdf(httpservletresponse response) {
        map<string, object> params = new hashmap<>();
        params.put("username","xiangjiao1");
        params.put("password","******");
        params.put("age",22);
        params.put("email","专注写bug测试中文");

        final byte[] data = freemarkutils.createdocx2pdf(params, "templates_report/001.docx");
        string filename = uuid.randomuuid().tostring() + "_001_test.pdf";
        generatefile(response, data, filename);
    }

    /**
     * 下载文件
     * @param response 相应
     * @param data 数据
     * @param filename 文件名
     */
    private void generatefile(httpservletresponse response, byte[] data, string filename) {
        response.setheader("content-type", "application/octet-stream");
        response.setcharacterencoding("utf-8");
        response.setheader("content-disposition", "attachment;filename=" + filename);
        try {
            response.getoutputstream().write(data);
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            try {
                response.getoutputstream().close();
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
    }
}

请求测试

http://localhost/report/doc2pdf

docx4j 复杂docx文件转pdf碰见的坑总结

转pdf出现空格压缩、中文缩减等问题,可以考虑将半角替换成全角,将模板中的空格使用全角空格替换。

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com