通过itextpdf操作PDF,向PDF文件最后一页添加图片(缩放图片并判断最后一页是否能放下图片)
本人第一篇博客,哈哈!第一次接触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;
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;
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);
}
public void addImageToPdf() {
try {
document = new Document(reader.getPageSize(pages));
PdfReaderContentParser contentParser = new PdfReaderContentParser(reader);
PdfRenderListener pdfRenderListener = new PdfRenderListener();
pdfRenderListener = contentParser.processContent(pages, pdfRenderListener);
RectangeInfo rectangeInfo = pdfRenderListener.getRectangeInfo();
float bottomY = rectangeInfo.getRectangeY();
Image oldImage = Image.getInstance(imagePath);
float imgWidth = oldImage.getWidth();
float width = document.getPageSize().getWidth();
float scale = scaleImageByWidth(imagePath, targetImagePath, width, imgWidth);
float scaleHeight = scale * oldImage.getHeight();
if (bottomY - WORD_IMAGE_SPACE < scaleHeight) {
float height = document.getPageSize().getHeight();
addImgToNewPage(filePath, targetPath, targetImagePath, width, height);
} else {
float imageY = bottomY - WORD_IMAGE_SPACE - scaleHeight;
addImageToBottom(filePath, targetImagePath, width, imageY);
}
} catch (BadElementException | IOException e) {
e.printStackTrace();
}
}
public float scaleImageByWidth(String imagePath, String targetImage, float width, float imgWidth) {
float div = 1;
if (width < imgWidth) {
div = (float)NumberUtil.div(width - 72, imgWidth, 4);
}
ImgUtil.scale(FileUtil.file(imagePath), FileUtil.file(targetImage), div);
return div;
}
public void addImgToNewPage(String filePath, String targetPath, String imagePath, float width, float height) {
try {
stamper = new PdfStamper(reader, fos);
stamper.insertPage(reader.getNumberOfPages() + 1, reader.getPageSizeWithRotation(1));
PdfContentByte under = stamper.getOverContent(reader.getNumberOfPages());
Image image = Image.getInstance(imagePath);
float imageX = (width - image.getWidth()) / 2;
float imageY = height - image.getHeight() - 36;
image.setAbsolutePosition(imageX, imageY);
image.setAlignment(Image.ALIGN_MIDDLE);
under.addImage(image);
} catch (DocumentException | IOException e) {
e.printStackTrace();
}
}
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);
img.setAlignment(Image.ALIGN_MIDDLE);
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;
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
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;
@Data
@ToString
@Builder
public class RectangeInfo {
private String content;
private float rectangeX;
private float rectangeY;
}