admin管理员组

文章数量:1605619

背景

业务需求,对系统中任务信息需要做下载打印,之前用是``poi-tl``实现的下载word(实现方式可参考根据模板导出word文档),现在需求变为下载pdf。补充信息:客户侧的服务器是离线服务器,尽量不在服务器安装第三方服务。

方案调研

关于word转pdf网上找到的可行方案大概5种,分别是:aspose-words、docx4j、openoffice、poi、spire.doc,综合网上信息得出的结论如下:aspose-words、spire.doc都是商业jar包,且费用较高,但是两个jar的生成效果确实是最好的,开源jar中openoffice是效果最好的,但是需要安装openoffice服务。

我的场景相对简单,需要转pdf的word只是一张表格,且字体只有“宋体”、“黑体”,所以还是自己测试对比了aspose-words、docx4j、poi的效果,因spire.doc同为商业软件且性能不如aspose-words,所以未作测试,openoffice依赖服务器安装openoffice服务,不便于迁移,所以也放弃了。测试效果与上面的结论基本一致,aspose-words最佳,docx4j把一页word变成了两页的pdf,第二页为空白页。poi因引入jar与项目中已有poi有冲突所以放弃。

参考自:Java开发中Word转PDF文件5种方案横向评测

方案确定

    根据以上方案调研结果最终选择了aspose-words,具体实现如下:

1. 引入jar

<dependency>
    <groupId>com.aspose</groupId>
    <artifactId>aspose-words</artifactId>
    <version>21.1</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/aspose-words-21.1-jdk17-cracked.jar</systemPath>
</dependency>

2. 代码实现

   可以先将前面的word写入临时文件,Document根据文件路径读临时文件,也可以将word模板文件输出到流中,如下代码实现Document读流。

@SneakyThrows
public static void exportPdfFromInputStream(InputStream input, String fileName, HttpServletResponse response) {
    Document doc = new Document(input);
    OsInfo osInfo = SystemUtil.getOsInfo();
    if(osInfo.isLinux()){
        log.info("os is linux,加载字体文件...");
        FontSettings.getDefaultInstance().setFontsFolder("/fonts/path",false);
    }
    response.setContentType("application/pdf;charset=UTF-8");
    fileName = URLEncoder.encode(fileName,"UTF-8");
    response.setHeader("Content-Disposition", "attachment;filename="+fileName+";"+"filename*=utf-8''"+fileName);
    try (ServletOutputStream os = response.getOutputStream()) {
        doc.save(os, SaveFormat.PDF);
    }
}

3. 注意事项

   springboot默认打包不会把scope=system的文件打入项目中,修改pom文件

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>2.3.5.RELEASE</version>
    <!-- 打包时会将本地jar一起打包 -->
    <configuration>
        <includeSystemScope>true</includeSystemScope>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

4. jar下载地址

   网上有很多aspose-words去水印版的jar下载,推荐两个地址:

一个是鱼

码云地址https://gitee/liuzy1988/aspose-words-crack

一个是渔

吾爱破解论坛https://www.52pojie/thread-1824449-1-1.html

方案优化

       以上所有方案在服务器部署均会遇到中文字体乱码的问题,通用解决方法有两种,将windows字体库C:\Windows\Fonts复制到linux字体库/usr/share/fonts,

方法1是服务器安装字体,https://segmentfault/a/1190000040275198

方法2是将linux中的字体路径指给程序,如上代码即如此实现FontSettings.getDefaultInstance().setFontsFolder("/fonts/path",false)。本项目中涉及字体较少,可以采用第三种方案,将字体文件打包到项目jar中。

1. 代码实现

   在工具类初始化时,加载jar中的字体文件

OsInfo osInfo = SystemUtil.getOsInfo();
if(osInfo.isLinux()){
    log.info("os is linux,加载字体文件...");
    InputStream sunStream = AsposeUtil.class.getClassLoader().getResourceAsStream("fonts/simsun.ttc");
    byte[] fontData = streamToByteArray(sunStream);
    MemoryFontSource heiSource = new MemoryFontSource(fontData);
    FontSettings.getDefaultInstance().setFontsSources(new FontSourceBase[]{heiSource});
}

2. 注意点

   InputStream中读取数据不全的问题

InputStream读字节数组时,直接使用inputStream.read(byte[inputStream.available]),会出现字节流读取不全的问题,可以参考如下实现,或者使用commons-io包中的IOUtils.toByteArray(inputStream)

public static byte[] streamToByteArray(InputStream in) throws IOException {
    ByteArrayOutputStream output = new ByteArrayOutputStream();
    byte[] buffer = new byte[4096];
    int n;
    while (-1 != (n = in.read(buffer))) {
        output.write(buffer, 0, n);
    }
    byte[] res = output.toByteArray();
    in.close();
    output.close();
    return res;
}

本文标签: JavawordPDF