wiki.xsx.core.pdf.doc.XEasyPdfDocumentReplacer Maven / Gradle / Ivy
package wiki.xsx.core.pdf.doc;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.pdfbox.cos.*;
import org.apache.pdfbox.pdfparser.PDFStreamParser;
import org.apache.pdfbox.pdfwriter.ContentStreamWriter;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.common.PDStream;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotation;
import org.apache.pdfbox.pdmodel.interactive.annotation.PDAnnotationText;
import wiki.xsx.core.pdf.component.image.XEasyPdfImageType;
import wiki.xsx.core.pdf.util.XEasyPdfFileUtil;
import wiki.xsx.core.pdf.util.XEasyPdfFontUtil;
import wiki.xsx.core.pdf.util.XEasyPdfImageUtil;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* pdf文档替换器
*
* @author xsx
* @date 2022/1/11
* @since 1.8
*
* Copyright (c) 2020-2023 xsx All Rights Reserved.
* x-easypdf is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*
*/
public class XEasyPdfDocumentReplacer implements Serializable {
private static final long serialVersionUID = 5248577569799461988L;
/**
* 日志
*/
private final Log log = LogFactory.getLog(XEasyPdfDocumentReplacer.class);
/**
* pdfbox文档
*/
private PDDocument document;
/**
* pdf文档
*/
private wiki.xsx.core.pdf.doc.XEasyPdfDocument pdfDocument;
/**
* 字体路径
*/
private String fontPath;
/**
* 是否允许替换评论
*/
boolean isAllowReplaceComments = Boolean.FALSE;
/**
* 有参构造
*
* @param pdfDocument pdf文档
*/
XEasyPdfDocumentReplacer(XEasyPdfDocument pdfDocument) {
this.pdfDocument = pdfDocument;
this.document = this.pdfDocument.build(true);
}
/**
* 有参构造
*
* @param pdfDocument pdf文档
* @param target pdfbox文档
*/
XEasyPdfDocumentReplacer(XEasyPdfDocument pdfDocument, PDDocument target) {
this.pdfDocument = pdfDocument;
this.document = target;
}
/**
* 设置是否允许替换引用
*
* @param b
* @return
*/
public XEasyPdfDocumentReplacer enableReplaceComments(boolean b) {
this.isAllowReplaceComments = Boolean.TRUE;
return this;
}
/**
* 设置字体路径
*
* @param fontPath 字体路径
* @return 返回pdf文档替换器
*/
public XEasyPdfDocumentReplacer setFontPath(String fontPath) {
this.fontPath = fontPath;
return this;
}
/**
* 设置默认字体样式
*
* @param style 默认字体样式
* @return 返回pdf文档替换器
*/
public XEasyPdfDocumentReplacer setDefaultFontStyle(wiki.xsx.core.pdf.doc.XEasyPdfDefaultFontStyle style) {
if (style != null) {
this.fontPath = style.getPath();
}
return this;
}
/**
* 替换文本
*
* @param replaceMap 替换字典(key可为正则)
* @return 返回pdf文档替换器
*/
@SneakyThrows
public XEasyPdfDocumentReplacer replaceText(Map replaceMap) {
return this.replaceText(replaceMap, (int[]) null);
}
/**
* 替换文本
*
* @param replaceMap 替换字典(key可为正则)
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
@SneakyThrows
public XEasyPdfDocumentReplacer replaceText(Map replaceMap, int... pageIndex) {
return this.replaceText(1, replaceMap, pageIndex);
}
/**
* 替换文本
*
* @param count 替换次数
* @param replaceMap 替换字典(key可为正则)
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
@SneakyThrows
public XEasyPdfDocumentReplacer replaceText(int count, Map replaceMap, int... pageIndex) {
// 替换字典不为空且替换次数大于0,则替换文本
if (replaceMap != null && !replaceMap.isEmpty() && count > 0) {
// 如果页面索引为空,则替换全部页面
if (pageIndex == null || pageIndex.length == 0) {
// 获取页面树
PDPageTree pages = this.document.getPages();
// 遍历页面树
for (PDPage page : pages) {
// 遍历替换次数
for (int i = 0; i < count; i++) {
// 替换文本
this.replaceText(page, replaceMap);
}
}
}
// 否则替换给定页面索引
else {
// 遍历页面索引
for (int index : pageIndex) {
// 如果页面索引大于等于0,则替换文本
if (index >= 0) {
// 遍历替换次数
for (int i = 0; i < count; i++) {
// 替换文本
this.replaceText(this.document.getPage(index), replaceMap);
}
}
}
}
}
return this;
}
/**
* 替换评论
*
* @param replaceMap 替换字典(key可为正则)
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
@SneakyThrows
public XEasyPdfDocumentReplacer replaceComment(Map replaceMap, int... pageIndex) {
// 替换字典不为空且替换次数大于0,则替换文本
if (replaceMap != null && !replaceMap.isEmpty()) {
// 如果页面索引为空,则替换全部页面
if (pageIndex == null || pageIndex.length == 0) {
// 获取页面树
PDPageTree pages = this.document.getPages();
// 遍历页面树
for (PDPage page : pages) {
this.replaceComment(page, replaceMap);
}
}
// 否则替换给定页面索引
else {
// 遍历页面索引
for (int index : pageIndex) {
// 如果页面索引大于等于0,则替换文本
if (index >= 0) {
// 替换文本
this.replaceComment(this.document.getPage(index), replaceMap);
}
}
}
}
return this;
}
/**
* 替换图像
*
* @param image 待替换图像
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
public XEasyPdfDocumentReplacer replaceImage(BufferedImage image, int... pageIndex) {
return this.replaceImage(image, XEasyPdfImageType.PNG, pageIndex);
}
/**
* 替换图像
*
* @param image 待替换图像
* @param imageType 待替换图像类型
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
public XEasyPdfDocumentReplacer replaceImage(BufferedImage image, XEasyPdfImageType imageType, int... pageIndex) {
return this.replaceImage(image, imageType, null, pageIndex);
}
/**
* 替换图像
*
* @param image 待替换图像
* @param replaceIndexList 待替换图像索引列表
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
@SneakyThrows
public XEasyPdfDocumentReplacer replaceImage(BufferedImage image, List replaceIndexList, int... pageIndex) {
return this.replaceImage(image, XEasyPdfImageType.PNG, replaceIndexList, pageIndex);
}
/**
* 替换图像
*
* @param image 待替换图像
* @param imageType 待替换图像类型
* @param replaceIndexList 待替换图像索引列表
* @param pageIndex 页面索引
* @return 返回pdf文档替换器
*/
@SneakyThrows
public XEasyPdfDocumentReplacer replaceImage(BufferedImage image, XEasyPdfImageType imageType, List replaceIndexList, int... pageIndex) {
// 定义pdf图像
PDImageXObject imageObject = null;
// 如果待替换图像不为空,则重置pdf图像
if (image != null) {
// 重置pdf图像
imageObject = PDImageXObject.createFromByteArray(
this.document,
XEasyPdfImageUtil.toBytes(image, imageType.name()), imageType.name()
);
}
return this.replaceImage(imageObject, replaceIndexList, pageIndex);
}
/**
* 替换属性
*
* @param map 替换字典(key可为正则)
* @return 返回pdf文档替换器
*/
public XEasyPdfDocumentReplacer replaceAttributes(Map map) {
PDDocumentInformation information = this.pdfDocument.getParam().getSource().getDocumentInformation();
this.replaceAttributes(this.pdfDocument.information(), information, map);
return this;
}
/**
* 文档签名器
*
* @return 返回pdf文档签名器
*/
public XEasyPdfDocumentSigner signer() {
return new XEasyPdfDocumentSigner(this.pdfDocument);
}
/**
* 完成操作
*
* @param outputPath 文件输出路径
*/
@SneakyThrows
public void finish(String outputPath) {
try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(XEasyPdfFileUtil.createDirectories(Paths.get(outputPath))))) {
this.finish(outputStream);
}
}
/**
* 完成操作
*
* @param outputStream 文件输出流
*/
@SneakyThrows
public void finish(OutputStream outputStream) {
// 替换总页码占位符
this.pdfDocument.replaceTotalPagePlaceholder(this.document, false);
// 设置基础信息(文档信息、保护策略、版本、xmp信息及书签)
this.pdfDocument.setBasicInfo(this.document);
// 保存文档
this.document.save(outputStream);
// 关闭文档
this.pdfDocument.close();
}
/**
* 完成操作
*/
void finish() {
this.pdfDocument = null;
this.document = null;
}
/**
* 替换文本
*
* @param page pdfbox页面
* @param replaceMap 替换字典(key可为正则)
*/
@SneakyThrows
void replaceText(PDPage page, Map replaceMap) {
// 获取pdfbox字体
PDFont font = this.initFont();
// 获取页面资源
PDResources resources = page.getResources();
// 获取pdf解析器
PDFStreamParser parser = new PDFStreamParser(page);
// 解析页面
parser.parse();
// 获取标记列表
List