java基于word模板动态生成word及转pdf实践 比眉伴天荒 2022-12-13 11:28 531阅读 0赞 *在项目中很容易会遇到需要动态生成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] 在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; } } 整体来看代码还是很少的,写完后直接运行跑一下 ### 输出结果 ### 输出文件 ![输出文件][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3B1aGFpeWFuZw_size_16_color_FFFFFF_t_70_pic_center] 生成的pdf文件预览 ![生成的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/][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 [word]: /images/20221123/7f1b7b6293df45c1a34033832d7c5c1f.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3B1aGFpeWFuZw_size_16_color_FFFFFF_t_70_pic_center]: /images/20221123/86a1a2d4445c44c8b9062f80c348f886.png [pdf]: /images/20221123/9b389d845d8b45d39dbaa7307cd0669f.png [https_kubernetes.io_docs_concepts_workloads_pods_init-containers]: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
还没有评论,来说两句吧...