java基于word模板动态生成word及转pdf实践

在项目中很容易会遇到需要动态生成pdf的应用场景,其实现方式也比较多

由于项目的关系,对于这种组件性的开发方式我想的是怎么方便怎么来,怎么快就怎么来

在咨询了之前做政务系统的同学后,他们都一致推荐我使用aspose框架来实现,因为它的效率高并且简单方便使用

在看了下aspose的官网和对demo简单体验了之后感觉确实很方便,为了后面项目中万一又遇到有需要的场景可以信手拈来,这里我简单在此处记录一下

下载地址

https://downloads.aspose.com/words/java

选择版本:Aspose.Words for Java 19.1

(之所以选择那个版本,是因为网上有很多这个版本的pj教程,太新的担心pj不了,当然不pj也可以用但是有水印)

如果直接下载jar的话也可通过aspose官方的maven仓库地址进行下载:

https://repository.aspose.com/repo/com/aspose/aspose-words/19.1/aspose-words-19.1-jdk16.jar

pj教程:

https://www.jianshu.com/p/be0cbdc389cc

也可以直接下载已破解好的jar,如果自己能找到的话

使用示例

这里演示一个目前我的项目中可能需要满足的一个需求,通过一个word模板生成pdf,动态填充word中的内容,同时支持生成多个pdf但要求pdf合并到一个pdf中

添加maven依赖

将下载好的jar放到项目中,然后添加依赖

        <!--aspose-words依赖,直接依赖本地项目中的jar-->
        <dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>aspose-words-19.1-jdk16.jar</version>
            <scope>system</scope>
            <systemPath>${pom.basedir}/libs/aspose-words-19.1-jdk16-crack.jar</systemPath>
        </dependency>

如果用本地jar打包时记得添加资源配置:

    <build>
        <resources>
            <resource>
                <directory>libs</directory>
                <targetPath>BOOT-INF/lib/</targetPath>
                <includes>
                    <include>**/*.jar</include>
                </includes>
            </resource>
        </resources>
    </build>

word模板准备

我们的最终目的是生成pdf,但为了方便还是先通过word模板生成word之后再输出为pdf。

为了排版方便建议还是使用word中的表格来进行排版,对于可变的地方可以添加【文字型窗体域】便于后面程序的文字进行填充
word模板编辑
在word模板准备好了之后就可以直接开始写代码了

测试代码

import com.aspose.words.*;

import java.io.File;
import java.io.FileOutputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;

/**
 * @author puhaiyang
 * @date 2020/10/11 12:12
 * 测试aspose生成pdf工具类
 */
public class AsposeWordsUtils {
    /**
     * Word转PDF操作
     *
     * @param doc        源文件
     * @param targetFile 目标文件
     */
    public static void doc2pdf(Document doc, String targetFile) {
        try {
            long old = System.currentTimeMillis();
            //新建一个空白pdf文档
            File file = new File(targetFile);
            FileOutputStream os = new FileOutputStream(file);
            //全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换
            doc.save(os, SaveFormat.PDF);
            os.close();
            long now = System.currentTimeMillis();
            //转化用时
            System.out.println("共耗时:" + ((now - old) / 1000.0) + "秒");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static class UserInfo {
        public UserInfo(String userNumber, String userName, String depaName, String desc, Date pubDate) {
            this.userNumber = userNumber;
            this.userName = userName;
            this.depaName = depaName;
            this.desc = desc;
            this.pubDate = pubDate;
        }

        private String userNumber;
        private String userName;
        private String depaName;
        private String desc;
        private Date pubDate;

        public String getUserNumber() {
            return userNumber;
        }

        public void setUserNumber(String userNumber) {
            this.userNumber = userNumber;
        }

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getDepaName() {
            return depaName;
        }

        public void setDepaName(String depaName) {
            this.depaName = depaName;
        }

        public String getDesc() {
            return desc;
        }

        public void setDesc(String desc) {
            this.desc = desc;
        }

        public Date getPubDate() {
            return pubDate;
        }

        public void setPubDate(Date pubDate) {
            this.pubDate = pubDate;
        }
    }

    public static void main(String[] args) throws Exception {
        ArrayList<UserInfo> userInfoList = getUserInfoList();
        //模板word
        String template = "C:/Users/ping/Desktop/temp/aspose/test/forTest.docx";

        Document firstDocument = null;
        for (UserInfo userInfo : userInfoList) {
            //目标word
            Document document = new Document(template);
            Range range = document.getRange();
            range.replace("ygbh", userInfo.getUserNumber(), new FindReplaceOptions());
            range.replace("ygxm", userInfo.getUserName(), new FindReplaceOptions());
            range.replace("szbm", userInfo.getDepaName(), new FindReplaceOptions());
            range.replace("zygx", userInfo.getDesc(), new FindReplaceOptions());
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            LocalDateTime localDateTime = LocalDateTime.ofInstant(userInfo.getPubDate().toInstant(), ZoneId.systemDefault());
            range.replace("bfrq", formatter.format(localDateTime), new FindReplaceOptions());
            if (firstDocument == null) {
                firstDocument = document;
            } else {
                //添加文档
                firstDocument.appendDocument(document, ImportFormatMode.KEEP_DIFFERENT_STYLES);
            }
        }
        doc2pdf(firstDocument, "C:/Users/ping/Desktop/temp/aspose/test/descForTest.pdf");
    }

    private static ArrayList<UserInfo> getUserInfoList() {
        ArrayList<UserInfo> userInfoList = new ArrayList<UserInfo>();
        userInfoList.add(new UserInfo("666001", "海洋哥",
                "滑水部-酱油项目组-基础架构组",
                "研究如何滑水及如何深入打酱油,对如何平滑滑水取得了重要贡献", new Date()));
        userInfoList.add(new UserInfo("666002", "帅哥",
                "核心项目事业部-超级潜水部-默默潜水研究组-项目三组",
                "对如何潜水取得了重要贡献", new Date()));
        return userInfoList;
    }
}

整体来看代码还是很少的,写完后直接运行跑一下

输出结果

输出文件
输出文件
生成的pdf文件预览
生成的pdf文件预览
效果还是可以的,文字该换行显示的还是换行显示了,效果不错代码执行效率也很快,非常简单。

以后再遇到要求项目中实现动态生成PDF简直就是分分钟的事呀!nice~

linux下导出pdf乱码解决办法

如果在windows下导出pdf一切正常,但在linux下导出pdf中文乱码了,则需要通过添加字体文件并在aspose的代码中指定所在的字体文件

//指定字体目录
FontSettings.getDefaultInstance().setFontsFolder("/opt/user/otherfonts/", false);
//执行保存
doc.save(pdfPath);

具体办法是先找到字体对应的字体文件,在windows不报错的话就从windows上先找下, windows的字体文件都在这个目录下

C:\Windows\Fonts\

方式一,直接部署方式

找到对应的字体然后拷贝到linux某个目中即可

方式二,docker部署方式

但是如果是docker的话,则可以通过volume映射字体目录或通过Dockerfile直接将字体打在docker镜像中,如果打在镜像中的话会导致镜像文件很大

方式三,通过k8s方式

如果是通过kubernetes来部署的项目可以通过pvc来搞定,但如果没得对应的文件服务器,则可以用过init-containers配合emptyDir类型的volumes来解决,参考地址:https://kubernetes.io/docs/concepts/workloads/pods/init-containers/

{
    "template": {
        "spec": {
            "containers": [
                {
                    "image": "xxxxxxxxxxxxxxxxxxxx",
                    "volumeMounts": [
                        {
                            "mountPath": "/opt/user/otherfonts",
                            "name": "font-depend"
                        }
                    ]
                }
            ],
            "initContainers": [
                {
                    "command": [
                        "sh",
                        "-c",
                        "set -ex;mkdir -p /vmfontsdepend/fonts;cp -r /otherfonts/* /vmfontsdepend/fonts;"
                    ],
                    "image": "ccr.ccs.tencentyun.com/haiyang/font-depend-image:1.0.0",
                    "imagePullPolicy": "IfNotPresent",
                    "name": "init-font-depends",
                    "volumeMounts": [
                        {
                            "mountPath": "/vmfontsdepend/fonts",
                            "name": "font-depend"
                        }
                    ]
                }
            ],
            "volumes": [
                {
                    "emptyDir": {},
                    "name": "font-depend"
                }
            ]
        }
    }
}

其中ccr.ccs.tencentyun.com/haiyang/font-depend-image:1.0.0的Dockerfile为:

FROM alpine:3.8
LABEL maintainer="puhaiyang"
ADD fonts/* /otherfonts/

fonts目录下为需要导入的字体文件


参考资料

查找并替换docx的中文字(模板生成)

https://github.com/aspose-words/Aspose.Words-for-Java/blob/master/Examples/src/main/java/com/aspose/words/examples/quickstart/FindAndReplace.java

多个docx文档合并为一个(文档合并)

https://github.com/aspose-words/Aspose.Words-for-Java/blob/master/Examples/src/main/java/com/aspose/words/examples/quickstart/AppendDocuments.java

docx转为pdf

https://github.com/aspose-words/Aspose.Words-for-Java/blob/master/Examples/src/main/java/com/aspose/words/examples/loading_saving/ConvertToPDF.java

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水中加点糖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值