通过itextpdf操作PDF,向PDF文件最后一页添加图片(缩放图片并判断最后一页是否能放下图片)
- 本人第一篇博客,哈哈!第一次接触itextpdf,想实现将图片向PDF尾部追加(判断原页面使用情况,图片缩放后是否可以放的下,放不下时新增页面插入图片),网上博客也看了很多,没有符合我的预期的(可能我没有发现吧,哈哈,百度也是个技术活),就研究了两天,写了这个,菜鸡一枚,希望多多交流哈!有代码优化及重构思路也希望可以指点一二!
- pom.xml
- ITextTest 测试类
- PdfHandler类
- 1.获取到最后一页(可以是任意页)document,拿到对应PDF页面宽度和高度;
- 2.通过pdfRenderListener 处理最后一页所有文本内容,记录文本对应坐标,获取页面内容尾部高度;
- 3.压缩图片:根据页面宽度和图片宽度进行图片缩放,过程中页边距按默认36进行计算,如需精确计算页边距可以通过PdfRenderListener记录的文本横坐标进行判定(不清楚是否itext有对应的方法可以直接调用获取);
- 4.通过文本尾部高度-文本与插入图片保持的上下间距(自定义为10)-压缩后图片高度>0判断是否图片可以插入(没有考虑下页边距);
- 5.当前页插入图片或新开一页插入图片
- PdfRenderListener(实现RenderListener,重写renderText方法)
- RectangeInfo
本人第一篇博客,哈哈!第一次接触itextpdf,想实现将图片向PDF尾部追加(判断原页面使用情况,图片缩放后是否可以放的下,放不下时新增页面插入图片),网上博客也看了很多,没有符合我的预期的(可能我没有发现吧,哈哈,百度也是个技术活),就研究了两天,写了这个,菜鸡一枚,希望多多交流哈!有代码优化及重构思路也希望可以指点一二!
pom.xml
<!-- 核心依赖包 -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<!-- 辅助工具包 -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
ITextTest 测试类
package com.dyx.springbootbase;
import com.dyx.springbootbase.pdf.itext.PdfHandler;
import java.io.IOException;
import org.junit.Test;
/**
* @author duyunxi
* @sign 世人莫羡长安景,风雨楼外多风雨
* @date 2021-10-17 10:07
*/
public class ITextTest {
private static String filePath = "C:\\Users\\TR\\Desktop\\java\\java电子书\\深入理解Java虚拟机:JVM高级特性与最佳实践(最新第三版).pdf";
private static String filePath1 = "C:\\Users\\TR\\Desktop\\java\\java电子书\\阿里巴巴Java开发手册泰山版.pdf";
private static String imageName = "C:\\Users\\TR\\Pictures\\1.jpg";
@Test
public void insertToPdf() {
try {
PdfHandler pdfHandler = new PdfHandler(filePath, imageName);
pdfHandler.addImageToPdf();
// 关流,删除临时文件
pdfHandler.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
PdfHandler类
1.获取到最后一页(可以是任意页)document,拿到对应PDF页面宽度和高度;
2.通过pdfRenderListener 处理最后一页所有文本内容,记录文本对应坐标,获取页面内容尾部高度;
3.压缩图片:根据页面宽度和图片宽度进行图片缩放,过程中页边距按默认36进行计算,如需精确计算页边距可以通过PdfRenderListener记录的文本横坐标进行判定(不清楚是否itext有对应的方法可以直接调用获取);
4.通过文本尾部高度-文本与插入图片保持的上下间距(自定义为10)-压缩后图片高度>0判断是否图片可以插入(没有考虑下页边距);
5.当前页插入图片或新开一页插入图片
package com.dyx.springbootbase.pdf.itext;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Objects;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
/**
* @author duyunxi
* @sign 世人莫羡长安景,风雨楼外多风雨
* @date 2021-10-16 22:21
*/
public class PdfHandler {
private static final int WORD_IMAGE_SPACE = 10;
private String targetPath = "D:\\target";
private String targetImagePath = "D:\\target";
private String filePath;
private String imagePath;
private int pages;
private PdfReader reader;
private Document document;
private PdfStamper stamper;
private FileOutputStream fos;
public PdfHandler(String filePath, String imagePath) throws IOException {
this.filePath = filePath;
this.imagePath = imagePath;
this.targetPath = targetPath + File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".pdf";
this.targetImagePath =
targetImagePath + File.separator + DateUtil.format(new Date(), "yyyyMMddHHmmss") + ".jpg";
this.reader = new PdfReader(filePath);
this.pages = reader.getNumberOfPages();
this.fos = new FileOutputStream(targetPath);
}
/**
* 在pdf最后一页添加图片
*/
public void addImageToPdf() {
try {
// 获取最后一页文档对象进行判断
document = new Document(reader.getPageSize(pages));
// Create a stamper that will copy the document to a new file
PdfReaderContentParser contentParser = new PdfReaderContentParser(reader);
PdfRenderListener pdfRenderListener = new PdfRenderListener();
// 处理最后一页(pages)
pdfRenderListener = contentParser.processContent(pages, pdfRenderListener);
RectangeInfo rectangeInfo = pdfRenderListener.getRectangeInfo();
// 最后一页实际文字内容到达的纵坐标位置
float bottomY = rectangeInfo.getRectangeY();
// 如果图片大小超过PDF宽度,对图片按宽度进行等比压缩(一般图片宽度大于高度)
Image oldImage = Image.getInstance(imagePath);
// 图片宽度
float imgWidth = oldImage.getWidth();
// pdf宽度
float width = document.getPageSize().getWidth();
float scale = scaleImageByWidth(imagePath, targetImagePath, width, imgWidth);
// 压缩后图片高度
float scaleHeight = scale * oldImage.getHeight();
// Can't fit the picture on the current page
if (bottomY - WORD_IMAGE_SPACE < scaleHeight) {
float height = document.getPageSize().getHeight();
// add image to the new page
addImgToNewPage(filePath, targetPath, targetImagePath, width, height);
} else {
// add image on the current page
// 文字到达的纵坐标位置-文字与图片间距10-压缩后图片高度
float imageY = bottomY - WORD_IMAGE_SPACE - scaleHeight;
addImageToBottom(filePath, targetImagePath, width, imageY);
}
} catch (BadElementException | IOException e) {
e.printStackTrace();
}
}
/**
* when the imgWidth more than the width,scale image, return the scale
*
* @param imagePath
* image path
* @param targetImage
* target image
* @param width
* pdf width
* @param imgWidth
* img width
* @return scale
*/
public float scaleImageByWidth(String imagePath, String targetImage, float width, float imgWidth) {
// 缩放比例
float div = 1;
// 图片宽度过长时进行压缩处理
if (width < imgWidth) {
// 一般PDF页边距为36,减去两边的页边距72
div = (float)NumberUtil.div(width - 72, imgWidth, 4);
}
ImgUtil.scale(FileUtil.file(imagePath), FileUtil.file(targetImage), div);
return div;
}
/**
* 添加图片到新页面
*
* @param filePath
* 文件路径
* @param targetPath
* 修改后pdf路径
* @param imagePath
* 压缩后图片路径
* @param width
* pdf宽度
* @param height
* pdf高度
*/
public void addImgToNewPage(String filePath, String targetPath, String imagePath, float width, float height) {
try {
stamper = new PdfStamper(reader, fos);
// insert new page
stamper.insertPage(reader.getNumberOfPages() + 1, reader.getPageSizeWithRotation(1));
// get new page
PdfContentByte under = stamper.getOverContent(reader.getNumberOfPages());
Image image = Image.getInstance(imagePath);
float imageX = (width - image.getWidth()) / 2;
// PDF高度-图片高度-页面上边距
float imageY = height - image.getHeight() - 36;
// 设置图片坐标位置
image.setAbsolutePosition(imageX, imageY);
// 图片居中
image.setAlignment(Image.ALIGN_MIDDLE);
under.addImage(image);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
}
/**
* 添加图片到当前页面尾部
*
* @param filePath
* 文件路径
* @param imagePath
* 图片路径
* @param width
* pdf宽度
* @param imageY
* 图片放置位置纵坐标
*/
private void addImageToBottom(String filePath, String imagePath, float width, float imageY) {
try {
stamper = new PdfStamper(reader, fos);
Image img = Image.getInstance(imagePath);
float imageX = (width - img.getWidth()) / 2;
// 图片放置位置
img.setAbsolutePosition(imageX, imageY);
// set image in center
img.setAlignment(Image.ALIGN_MIDDLE);
// Text over the existing page
PdfContentByte over = stamper.getOverContent(pages);
over.addImage(img);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
}
public void close() {
try {
if (!Objects.isNull(stamper)) {
stamper.close();
}
if (!Objects.isNull(document)) {
document.close();
}
if (!Objects.isNull(fos)) {
fos.close();
}
if (!Objects.isNull(reader)) {
reader.close();
}
// 删除压缩产生的临时图片
FileUtil.del(targetImagePath);
} catch (DocumentException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
PdfRenderListener(实现RenderListener,重写renderText方法)
package com.dyx.springbootbase.pdf.itext;
import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import java.util.ArrayList;
import java.util.List;
/**
* @author duyunxi
* @sign 世人莫羡长安景,风雨楼外多风雨
* @date 2021-10-16 21:10
*/
public class PdfRenderListener implements RenderListener {
// 用于记录页面文字内容对应的文字横纵坐标及文字宽高
private List<RectangeInfo> rectangeInfos = new ArrayList<>();
//
private RectangeInfo rectangeInfo;
public List<RectangeInfo> getRectangeInfos() {
return rectangeInfos;
}
public void setRectangeInfos(List<RectangeInfo> rectangeInfos) {
this.rectangeInfos = rectangeInfos;
}
public RectangeInfo getRectangeInfo() {
return rectangeInfo;
}
@Override
// 开始读取文字内容时
public void beginTextBlock() {}
@Override
// 读取文字内容,经测试,读取文字顺序是按页眉、页脚、正文顺序读取
//即使页面包含图片,读取结束的text对应的也是整个页面所有内容的终止坐标点
// 本文只关注纵坐标Y的位置 ,如需精确页面页边距可通过集合记录横坐标X确定左右页边距大小
public void renderText(TextRenderInfo renderInfo) {
String text = renderInfo.getText();
Rectangle2D.Float boundingRectange =renderInfo.getBaseline().getBoundingRectange();
RectangeInfo rectangeInfo = RectangeInfo.builder().rectangeX(boundingRectange.x).rectangeY(boundingRectange.y).build();
rectangeInfo.setContent(text);
rectangeInfos.add(rectangeInfo);
}
@Override
public void endTextBlock() {
rectangeInfo = rectangeInfos.get(rectangeInfos.size() - 1);
}
@Override
// 对页面中图片进行操作
public void renderImage(ImageRenderInfo renderInfo) {}
}
RectangeInfo
package com.dyx.springbootbase.pdf.itext;
import lombok.Builder;
import lombok.Data;
import lombok.ToString;
/**
* 记录文本内容坐标信息
*
* @author duyunxi
* @sign 世人莫羡长安景,风雨楼外多风雨
* @date 2021-10-16 21:14
*/
@Data
@ToString
@Builder
public class RectangeInfo {
private String content;
private float rectangeX;
private float rectangeY;
}
更多推荐
通过itextpdf操作PDF,动态向PDF文件最后一页添加图片
发布评论