很久没写技术文章了,前段时间在研究word转pdf,以及加盖印章这部分,已经实现了,当然网络上也有很多文章,但是有的要么依赖导入不成功,要么逻辑有问题,总的来说,可能还是文章太老了,有些东西都不适用了,我这里正好也分享一下,希望能帮得上大家。

代码可直接测试运行,我这里JDK17,Maven3.5,IDEA2023

业务需求

我们现有业务是通过poi来实现内容转word,以及各类电子签名和图片,平时相关人员是将word下载下来,然后盖章扫描,再传回平台。
现在需求是在特殊情况下,需要特殊处理,不走这个流程了,因为太麻烦,所以想再平台上直接生成盖章后的pdf,直接使用。

其实需求很简单,就是要最终盖好章的pdf,发给客户,好,那我们就开始吧。

选用技术

我大概了解了下,常用的主要就是下面几种方式

  • 使用Aspose.Words库:Aspose.Words是一个功能强大的库,支持将Word文档转换为PDF格式。你需要将Aspose.Words的许可证字符串设置到你的程序中,然后使用相应的API进行转换。
  • 使用documents4j库:对于Windows环境,documents4j库可以在有Microsoft Office服务的情况下将Word文档转换为PDF。这需要安装Microsoft Office,并且可能需要在程序中配置Office的路径。
  • 使用LibreOffice服务:在Linux环境下,可以利用LibreOffice服务将Word文档转换为PDF。这需要在服务器上安装LibreOffice,并且可能需要在程序中配置LibreOffice的路径。
  • 使用Apache POI和iText库:这种方法适用于处理.doc和.docx格式的Word文档。首先使用Apache POI读取Word文档的内容,然后使用iText库将内容转换为PDF格式。这种方法可能需要处理Word文档中的复杂格式和布局。
    使用其他第三方库:例如Spire.Doc库,它提供了一个简单的API来将Word文档转换为PDF格式。你可以通过下载jar包或使用Maven仓库安装导入这个库。
  • 使用OpenOffice服务:如果你的环境中安装了OpenOffice,你可以使用Java调用OpenOffice的服务来将Word文档转换为PDF。这需要在Java程序中配置OpenOffice的路径和服务设置。

权衡之下,我选择了Aspose.Words,其实选择OpenOffice效果最好,以后等我有空了,会选择这种方式实现,到时候再分享。

实现逻辑

导入依赖,由于Aspose.Words在私服仓库,所以直接pom是无法导入的,需要将jar包手动传到maven仓库,然后在pom引入

去官网下载jar包记得选择21.6的jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>21.6</version>
<classifier>jdk17</classifier>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.5.13</version>
</dependency>

建议使用21.6版本,这个版本我去了水印(正版需要购买证书),相当于破解了吧,其它版本暂未尝试。

其实就是两个文件,WordPdfUtil.java和ImageUtil

WordPdfUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.manxin.ltd.datacenter.util;

import com.aspose.words.Document;
import com.aspose.words.SaveFormat;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Modifier;

public class WordToPdfUtils {

public static void main(String[] args)throws Exception {
Document doc = null;

//去水印
Class<?> aClass = Class.forName("com.aspose.words.zzXyu");
java.lang.reflect.Field zzZXG = aClass.getDeclaredField("zzZXG");
zzZXG.setAccessible(true);
java.lang.reflect.Field modifiersField = zzZXG.getClass().getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(zzZXG, zzZXG.getModifiers() & ~Modifier.FINAL);
zzZXG.set(null, new byte[]{76, 73, 67, 69, 78, 83, 69, 68});

try {
String resultPath = "E:\\download\\test\\test" + ".pdf";
FileOutputStream os = new FileOutputStream(resultPath);
doc = new Document(new FileInputStream("E:\\download\\test\\test.docx"));
doc.save(os, SaveFormat.PDF);

//添加印章
FileInputStream pdfIs = new FileInputStream("E:\\download\\test\\test.pdf");
// 1.1 读取模板文件
PdfReader reader = new PdfReader(pdfIs);
// 1.2 创建文件输出流
FileOutputStream out = new FileOutputStream("E:\\download\\test\\testnew.pdf");
// 2、创建PdfStamper对象
PdfStamper stamper = new PdfStamper(reader, out);
// 4、读取公章
String path = "E:\\download\\test\\baogao.png";
BufferedImage bufferedImage = ImageIO.read(new FileInputStream(path));
BufferedImage[] imgs = ImageUtil.splitImage(bufferedImage, 1, 2);
// 5、读公章图片
Image image = Image.getInstance(ImageUtil.imageToBytes(bufferedImage));
// 公章大小,x轴
int chunkWidth = 150;
// 公章大小,y轴
int chunkHeight = 150;
// pdf页面页码
int pdfPages = reader.getNumberOfPages();

// 6.3 最后一页盖公章
image.scaleToFit(chunkWidth, chunkHeight);
//得到的位置有些许偏差,自行调节
image.setAbsolutePosition(380, 310);
stamper.getOverContent(pdfPages).addImage(image);
stamper.close();
out.close();
reader.close();
pdfIs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

ImageUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.whohs.smartmsp.opscenter.util;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class ImageUtil {

/**
* @Description:分割图片
* @Date: 2021/12/9 8:34
* @Param image: 图片BufferedImage流
* @Param rows: 分割行
* @Param cols: 分割列
* @return: java.awt.image.BufferedImage[] 返回分割后的图片流
**/
public static BufferedImage[] splitImage(BufferedImage image, int rows, int cols) {
// 分割成4*4(16)个小图
int chunks = rows * cols;
// 计算每个小图的宽度和高度
int chunkWidth = image.getWidth() / cols + 3;// 向右移动3
int chunkHeight = image.getHeight() / rows;
int count = 0;
BufferedImage[] imgs = new BufferedImage[chunks];
for (int x = 0; x < rows; x++) {
for (int y = 0; y < cols; y++) {
//设置小图的大小和类型
imgs[count] = new BufferedImage(chunkWidth, chunkHeight, BufferedImage.TYPE_INT_RGB);
//写入图像内容
Graphics2D gr = imgs[count].createGraphics();
// 增加下面代码使得背景透明
imgs[count] = gr.getDeviceConfiguration().createCompatibleImage(chunkWidth, chunkHeight, Transparency.TRANSLUCENT);
gr.dispose();
gr = imgs[count].createGraphics();
gr.drawImage(image, 0, 0,
chunkWidth, chunkHeight,
chunkWidth * y, chunkHeight * x,
chunkWidth * y + chunkWidth,
chunkHeight * x + chunkHeight, null);
gr.dispose();
count++;
}
}
return imgs;
}


/**
* @Description: 将BufferedImage转换成字节数组
* @Date: 2021/12/6 17:31
* @Param bufferedImage:
* @return: byte[]
**/
public static byte[] imageToBytes(BufferedImage bufferedImage) throws IOException {

ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write( bufferedImage, "png", baos );
baos.flush();
byte[] imageInByte = baos.toByteArray();
baos.close();

return imageInByte;

}
}

写在最后

代码就不解释了,几乎都有注释,大家一看就清楚了,有此类需求的可以做为参考。