1、前言
开发过程遇到的问题:将一些文字、图片甚至excel表格中的信息整合到一起,然后按照排版生成对应的PDF文件。本篇介绍将图片、文字、excel表格信息整合成PDF的Java实现方法。
2、 思路简述
由于Java提供了一些操作PDF的API,pdfbox,其中提供了可供操作PDF的一些方法。使用这些方法就可以实现上述功能。只需要在编辑拼接的时候,设置对应的样式例如字体大小,图片位置等,就可以拼接到PDF文件中,最后在输出保存即可。
注:其中涉及到的流一定要全部关闭,否则会报错!
3、实现逻辑
写在前面:实现功能之前,先将创建文件的逻辑写出来,创建一个PDDocument对象来存储想要创建的PDF文件信息,此后的操作均基于该对象进行实现。然后创建PDPage对象来表示PDF文件中的某一页,将该对象加入到PDDocument中,就已经创建好了拥有一页空白内容的PDF文件。然后将文件对象和页面对象放在一起初始化一个PDPageContentStream对象,它提供在一个页面以流的方式写入内容,追加方式选择overwrite(在已有页面继续绘制内容,另外两种绘制方式分别为:append-写在所有已存在的页面流之后,prepend-写在所有已存在的页面流之前),最后一个是否压缩默认传true
PDDocument document = null;
try {
document = new PDDocument();
PDPage page = new PDPage();
document.addPage(page);
PDPageContentStream contentStream = new PDPageContentStream(pdDocument, coverPage, PDPageContentStream.AppendMode.OVERWRITE, true);
//实现逻辑。。。
document.save("C:\\Users\\难看哭了\\Desktop\\test.pdf");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
document.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
3.1整合文字信息
整合文字信息比较简单,只需要知道文字的大小和样式,以及绘制的位置即可。
public static void montageText(PDPageContentStream contentStream,float textSize, float positionX, float positionY, PDFont font, String text) throws IOException{
contentStream.beginText();
contentStream.setFont(font, textSize);
contentStream.newLineAtOffset(positionX, positionY);
contentStream.showText(text);
contentStream.endText();
}
注意:这里的文字样式可以采用PDType1Font提供的样式,但是此种样式不适用于中文,建议使用PDType0Font.load();去下载已有的字体(微软提供的都可以),传入pdf文件PDDocument以及字体文件即可,字体文件需要使用.ttf格式的,亲测.ttc格式的文件会报错。
3.2整合图片信息
整合图片信息跟文字其实异曲同工,图片大小,位置,等信息拿到之后直接就可以绘制。
public static void montagePicture(PDDocument document,
PDPageContentStream contentStream,
float positionX, float positionY,
float width, float height,
String picture) throws IOException{
InputStream inputStream = OkHttpUtil.downloadFile(picture);
PDImageXObject image = LosslessFactory.createFromImage(document, ImageIO.read(inputStream));
contentStream.drawImage(image, positionX, positionY, width, height);
}
需要注意的是,多张图片绘制到一页上的时候,会按照绘制的顺序依次覆盖,需要合理分配位置,或者提供背景透明的图片。
3.3 将excel表格信息绘制到PDF中
将excel表格中的内容逐行绘制,其实就是读取到excel内容之后,将文字逐一绘制进去(还没尝试excel中有图片等其他信息如何处理,等待后续更新)。需要注意的点就是调整好行间距和页面高度实现一页满了之后再创建新的一页继续直到结束。
public static void montageExcel(PDDocument pdDocument, String pdFontUrl, String excelUrl){
PDPageContentStream contentStream = null;
try {
//读取excel文件
InputStream fis = OkHttpUtil.downloadFile(excelUrl);
Workbook workbook = new XSSFWorkbook(fis);
Sheet sheet = workbook.getSheetAt(0);
//为pdf添加一页
//todo 是否需要改为按照剩余文字坐标进行追加,这样需要改入参为坐标
PDPage page = new PDPage();
pdDocument.addPage(page);
contentStream = new PDPageContentStream(pdDocument, page);
float pageWidth = page.getCropBox().getWidth();
float pageHeight = page.getCropBox().getHeight();
float y = pageHeight - 50; // Starting y position
float margin = 50;
boolean newPageNeeded = false;
PDFont font = PDType0Font.load(pdDocument, new File(pdFontUrl));
//遍历行和每个Colum进行书写
for (Row row : sheet) {
//是否需要新建一页进行继续写入
if (newPageNeeded) {
PDPage newPage = new PDPage();
pdDocument.addPage(newPage);
y = pageHeight - 50;
contentStream.close();
contentStream = new PDPageContentStream(pdDocument, newPage);
newPageNeeded = false;
}
float x = margin; // Starting x position
for (Cell cell : row) {
if (y < 50){
newPageNeeded = true;
break;
}
String value = null;
//根据单元格类型设置写入的内容
if(cell.getCellType().equals(CellType.NUMERIC)){
value = String.valueOf(cell.getNumericCellValue());
}else {
value = cell.getStringCellValue();
}
//将excel信息写入pdf
PDFCreateUtil.montageText(contentStream, 12, x, y, font, value);
x += 100; // Adjust to your needs
}
y -= 20; // Adjust to your needs
}
}catch (IOException e){
e.printStackTrace();
}finally {
try {
contentStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
4、尾语
本文主要介绍将指定内容合并到PDF文件中去,我是在代码中将其整合成了工具类的形式,可以在多个地方重复使用。其中的一些遗留问题会在后续更新中补充(目前不影响功能,可以正常使用)