Files
server/src/main/java/com/point/strategy/common/PdfFileHelper.java
2025-11-22 21:03:27 +08:00

1041 lines
38 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.point.strategy.common;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifDirectoryBase;
import com.itextpdf.text.*;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfReader;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.FileImageInputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.List;
@Slf4j
public class PdfFileHelper {
/*
水印间隔
* */
private static int interval = -100;
/**
* 获取字体文件优先从文件系统读取然后从classpath读取
* 兼容JAR包环境和Docker环境
* @return FontFile对象包含字体文件路径和临时文件
*/
private static class FontFile {
public File file;
public boolean isTempFile = false;
public FontFile(File file, boolean isTempFile) {
this.file = file;
this.isTempFile = isTempFile;
}
}
/**
* 获取SIMYOU字体文件兼容JAR包和文件系统
* @return FontFile对象
* @throws IOException 字体文件获取失败
*/
private static FontFile getSimYouFontFile() throws IOException {
// 1. 优先从Docker环境的标准字体目录查找
String[] dockerFontPaths = {
"/usr/share/fonts/SIMYOU.TTF",
"/usr/local/share/fonts/SIMYOU.TTF",
"/app/fonts/SIMYOU.TTF",
"/app/data/fonts/SIMYOU.TTF"
};
for (String fontPath : dockerFontPaths) {
File fontFile = new File(fontPath);
if (fontFile.exists() && fontFile.canRead()) {
log.info("从文件系统找到字体文件: {}", fontPath);
return new FontFile(fontFile, false);
}
}
// 2. 从classpath中读取字体文件适用于JAR包环境
try {
InputStream fontStream = PdfFileHelper.class.getClassLoader().getResourceAsStream("SIMYOU.TTF");
if (fontStream != null) {
// 创建临时文件
File tempFontFile = File.createTempFile("SIMYOU", ".TTF");
tempFontFile.deleteOnExit(); // JVM退出时删除临时文件
// 将资源流写入临时文件
try (InputStream input = fontStream;
FileOutputStream output = new FileOutputStream(tempFontFile)) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
log.info("从classpath创建临时字体文件: {}", tempFontFile.getAbsolutePath());
return new FontFile(tempFontFile, true);
}
} catch (Exception e) {
log.warn("从classpath读取字体文件失败: {}", e.getMessage());
}
// 3. 尝试使用ResourceUtils开发环境
try {
File fontFile = ResourceUtils.getFile("classpath:SIMYOU.TTF");
if (fontFile.exists() && fontFile.canRead()) {
log.info("使用ResourceUtils读取字体文件: {}", fontFile.getAbsolutePath());
return new FontFile(fontFile, false);
}
} catch (Exception e) {
log.warn("使用ResourceUtils读取字体文件失败: {}", e.getMessage());
}
throw new IOException("无法找到SIMYOU.TTF字体文件请确保字体文件存在于文件系统或classpath中");
}
/**
* 获取BaseFont对象自动处理字体文件路径
* @return BaseFont对象
* @throws Exception 字体创建失败
*/
private static BaseFont getSimYouBaseFont() throws Exception {
FontFile fontFile = getSimYouFontFile();
try {
return BaseFont.createFont(fontFile.file.getAbsolutePath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
} finally {
// 如果是临时文件,清理资源
if (fontFile.isTempFile && fontFile.file.exists()) {
fontFile.file.delete();
log.debug("清理临时字体文件: {}", fontFile.file.getAbsolutePath());
}
}
}
/**
* 归档章 单元格宽度
*/
public static int cell_width = 45;
/**
* 归档章 单元格高度
*/
public static int cell_height = 24;
/**
* 归档章 顶部距离(最小值38)
*/
public static int grid_top = 40;
/**
* 归档章 字体大小
*/
public static int font_size = 9;
/**
* 页号字体大小
*/
public static int pageno_font_size = 35;
/**
* PDF文件加盖归档章
*
* @param srcFile 源文件名
* @param tarFile 目标文件名称
* @param pageIndex 需要盖章的页序号 默认传1
* @param textContent 归档章内容
* @param direction 归档章位置1表示右边, 2表示中间 3表示左边
* @param scale 归档章放大系数,默认为1
*/
public static boolean Seal(String srcFile, String tarFile, int pageIndex, String[][] textContent,
String direction, double scale) {
//目标文件存在,则先删除
File _tarFile = new File(tarFile);
if (_tarFile.exists()) {
_tarFile.delete();
}
try {
PdfReader pdfReader = new PdfReader(srcFile);
PdfReader.unethicalreading = true;
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(tarFile));
int numberOfPages = pdfReader.getNumberOfPages();
if (numberOfPages < 1)
return false;
Rectangle rectangle = pdfReader.getPageSizeWithRotation(pageIndex);
PdfContentByte content = pdfStamper.getOverContent(pageIndex);
//表格默认宽度
int grid_col_width = (int) Math.ceil(cell_width * scale * 1.0);
//表格默认高度
int grid_col_height = (int) Math.ceil(cell_height * scale * 1.0);
//表格行合计
int grid_rows = 2;
//表格列合计
int grid_cols = 3;
//起点位置(居中)
float grid_left = (rectangle.getWidth() - grid_col_width * grid_cols) / 2;
float text_left = (rectangle.getWidth() - grid_col_width * grid_cols) / 2;
if ("1".equals(direction)) {
grid_left = rectangle.getWidth() - grid_col_width * grid_cols - 70;
text_left = rectangle.getWidth() - grid_col_width * grid_cols - 70;
} else if ("3".equals(direction)) {
grid_left = 10;
text_left = 10;
}
BaseFont font = getSimYouBaseFont();
//BaseFont font = BaseFont.createFont("C:\\Users\\MI\\Desktop\\13\\SIMYOU.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
float width = rectangle.getWidth();
float height = rectangle.getHeight();
//全宗号,年度,件号
//机构或者问题,保管期限,总页数
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.6f);
content.setGState(gs);
content.setGrayFill(0.3f);
//画网格
for (int r = 0; r < grid_rows; r++) {
for (int j = 0; j < grid_cols; j++) {
//画网格矩形
content.setColorStroke(BaseColor.BLACK);
float x = grid_left + grid_col_width * j;
float y = height - grid_top - grid_col_height * (r + 1);
content.rectangle(x, y, grid_col_width, grid_col_height);
content.setLineWidth(1f);
content.stroke();
//计算文本显示的矩形大小
float xxxx = font.getWidthPointKerned(textContent[r][j], (int) Math.ceil(font_size * scale * 1.0));
float yyyy = font.getAscentPoint(textContent[r][j], (int) Math.ceil(font_size * scale * 1.0));
int x1 = (int) ((grid_col_width - xxxx) / 2);
int y1 = (int) ((grid_col_height - yyyy) / 2);
//写入文本
content.beginText();
content.setColorFill(BaseColor.BLACK);
content.setFontAndSize(font, (int) Math.ceil(font_size * scale * 1.0));
content.setTextMatrix(0, 0);
//content.ShowTextAligned(Element.ALIGN_LEFT, textContent[r, j], text_left + grid_col_width*j +
// 2, height - grid_top - grid_col_height*r + 5, 0);
content.showTextAligned(Element.ALIGN_LEFT, textContent[r][j], x + x1, y + y1, 0);
content.endText();
}
}
pdfStamper.close();
pdfReader.close();
return true;
} catch (Exception ex) {
System.out.println(ex.getMessage());
return false;
}
}
/**
* 当前号码
*/
public static int CurrPageNo = 1;
/**
* 新绘制页码
*
* @param srcFile
* @param tarFile
* @return
*/
public static boolean pageNo(String srcFile, String tarFile) {
int currPageNo2 = 1;
//目标文件存在,则先删除
File _tarFile = new File(tarFile);
if (_tarFile.exists()) {
_tarFile.delete();
}
try {
PdfReader pdfReader = new PdfReader(srcFile);
PdfReader.unethicalreading = true;
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(tarFile));
int numberOfPages = pdfReader.getNumberOfPages();
BaseFont font = getSimYouBaseFont();
// BaseFont font = BaseFont.createFont("C:\\Users\\MI\\Desktop\\13\\SIMYOU.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.6f);
for (int i = 1; i <= numberOfPages; i++) {
Rectangle rectangle = pdfReader.getPageSizeWithRotation(i);
float pageWidth = rectangle.getWidth();
float pageHeight = rectangle.getHeight();
PdfContentByte content = pdfStamper.getOverContent(i);
content.setGState(gs);
content.setGrayFill(0.6f);
content.beginText();
content.setColorFill(BaseColor.BLACK);
content.setFontAndSize(font, pageno_font_size);
content.setTextMatrix(0, 0);
if (currPageNo2 % 2 == 1) {
content.showTextAligned(Element.ALIGN_LEFT, (currPageNo2++) + "", pageWidth - 40, pageHeight - 40,
0);
} else {
content.showTextAligned(Element.ALIGN_LEFT, (currPageNo2++) + "", 40, pageHeight - 40, 0);
}
content.endText();
}
pdfStamper.close();
pdfReader.close();
return true;
} catch (Exception ex) {
System.out.println(ex.getMessage());
return false;
}
}
/**
* 获取pdf文件总页数
* PdfReader
*
* @param f
* @return
*/
public static int getPdfPageCoun(String f) {
try {
PdfReader pdfReader = new PdfReader(f);
PdfReader.unethicalreading = true;
int numberOfPages = pdfReader.getNumberOfPages();
pdfReader.close();
return numberOfPages;
} catch (Exception ex) {
return 0;
}
}
/**
* 获取pdf文件总页数
* PdfReader
*
* @param f
* @return
*/
public static int getPdfPageCounOrOther(String f) {
try {
PdfReader pdfReader = new PdfReader(f);
PdfReader.unethicalreading = true;
int numberOfPages = pdfReader.getNumberOfPages();
pdfReader.close();
return numberOfPages;
} catch (Exception ex) {
return 1;
}
}
/**
* Pdf文件加水印文字--居中
*
* @param srcFile
* @param markStr
* @return
*/
public static boolean setWaterMark(String srcFile,
String tarFile,
String markStr,
int fontSize,
String color,
int globalOblique) {
//目标文件存在,则先删除
File _tarFile = new File(tarFile);
if (_tarFile.exists()) {
_tarFile.delete();
}
try {
PdfReader pdfReader = new PdfReader(srcFile);
PdfReader.unethicalreading = true;
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(tarFile));
int numberOfPages = pdfReader.getNumberOfPages();
BaseFont font = getSimYouBaseFont();
// BaseFont font = BaseFont.createFont("C:\\Users\\MI\\Desktop\\13\\SIMYOU.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.6f);
for (int page = 1; page <= numberOfPages; page++) {
Rectangle rectangle = pdfReader.getPageSizeWithRotation(page);
float pageWidth = rectangle.getWidth();
float pageHeight = rectangle.getHeight();
PdfContentByte content = pdfStamper.getOverContent(page);
content.setGState(gs);
content.setGrayFill(0.6f);
//计算该文本某字体的矩形
float xxxx = font.getWidthPointKerned(markStr, fontSize);
float yyyy = font.getAscentPoint(markStr, fontSize);
content.beginText();
switch (color) {
case "WHITE":
content.setColorFill(BaseColor.WHITE);
break;
case "LIGHT_GRAY":
content.setColorFill(BaseColor.LIGHT_GRAY);
break;
case "GRAY":
content.setColorFill(BaseColor.GRAY);
break;
case "DARK_GRAY":
content.setColorFill(BaseColor.DARK_GRAY);
break;
case "BLACK":
content.setColorFill(BaseColor.BLACK);
break;
case "RED":
content.setColorFill(BaseColor.RED);
break;
case "PINK":
content.setColorFill(BaseColor.PINK);
break;
case "ORANGE":
content.setColorFill(BaseColor.ORANGE);
break;
case "YELLOW":
content.setColorFill(BaseColor.YELLOW);
break;
case "GREEN":
content.setColorFill(BaseColor.GREEN);
break;
case "MAGENTA":
content.setColorFill(BaseColor.MAGENTA);
break;
case "CYAN":
content.setColorFill(BaseColor.CYAN);
break;
case "BLUE":
content.setColorFill(BaseColor.BLUE);
break;
default:
content.setColorFill(BaseColor.GREEN);
}
content.setFontAndSize(font, fontSize);
content.setTextMatrix(0, 0);
//角度
content.showTextAligned(Element.ALIGN_LEFT, markStr, (pageWidth - xxxx) / 2, (pageHeight - yyyy) / 2,
globalOblique);
content.endText();
}
pdfStamper.close();
pdfReader.close();
return true;
} catch (Exception ex) {
System.out.println("水印添加失败:" + ex.getMessage());
return false;
}
}
/**
* 合并Pdf
*
* @param fileArray pdf图片数组[]{"d:/a/1.pdf","d:/a/2.pdf"}
* @return
*/
public static boolean mergePdf(String[] fileArray, String targetFile) {
if (fileArray == null || fileArray.length == 0) {
log.warn("输入的PDF文件数组为空无需合并。");
return true; // 或根据业务需求返回false
}
File target = new File(targetFile);
if (target.exists()) {
if (!target.delete()) {
log.error("无法删除已存在的目标文件: {}", targetFile);
return false;
}
}
// 按文件名中的数字片段正序排序(如 xxx.001.pdf, xxx.002.pdf
try {
log.info("mergePdf 接收到 {} 个PDF按文件名数字片段正序合并。", (fileArray == null ? 0 : fileArray.length));
if (fileArray != null) {
Arrays.sort(fileArray, new FileNameComparator());
for (int idx = 0; idx < fileArray.length; idx++) {
log.info("排序后合并顺序 {} -> {}", idx + 1, fileArray[idx]);
}
}
} catch (Exception ignore) {
}
Document document = null;
PdfCopy pdfCopy = null;
try {
document = new Document();
pdfCopy = new PdfCopy(document, new FileOutputStream(targetFile));
document.open();
for (String filePath : fileArray) {
if (filePath == null || filePath.trim().isEmpty()) {
log.warn("发现一个空的PDF文件路径已跳过。");
continue;
}
PdfReader pdfReader = null;
try {
pdfReader = new PdfReader(filePath);
pdfCopy.addDocument(pdfReader);
} catch(Exception e) {
log.error("读取或合并文件 {} 时出错,已跳过此文件。", filePath, e);
} finally {
if (pdfReader != null) {
pdfReader.close();
}
}
}
return true;
} catch (IOException | DocumentException e) {
log.error("合并PDF过程中发生严重错误。", e);
return false;
} finally {
// 5. 在最外层的 finally 块中关闭 Document 和 PdfCopy
// Document.close() 会自动调用 PdfCopy.close() 和底层的流。
// 检查 document 是否为 null 并且已打开,以防在初始化时就发生异常。
if (document != null && document.isOpen()) {
document.close();
}
}
}
/*
* @Description 图片转pdf
* @Date 13:33 2019/6/10
* @Param []
* @return boolean
**/
public static boolean image2Pdf(String source, String target) {
try {
BufferedImage img = ImageIO.read(new File(source));
PdfReader.unethicalreading = true;
Image png1 = Image.getInstance(source); //通过文件路径获取image
// float heigth = png1.getHeight();
// float width = png1.getWidth();
// 新增读取图片的EXIF方向信息
int orientation = getExifOrientation(source);
//new一个pdf文档
Document doc = new Document(null, 0, 0, 0, 0);
if (img == null) {
doc.setPageSize(new Rectangle(png1.getWidth(), png1.getHeight()));
} else {
doc.setPageSize(new Rectangle(img.getWidth(), img.getHeight()));
}
PdfWriter.getInstance(doc, new FileOutputStream(target)); //pdf写入
doc.open();//打开文档
// doc.newPage(); //在pdf创建一页
// int percent = getPercent2(heigth, width);
// png1.setAlignment(Image.MIDDLE);
// png1.scalePercent(percent+3);// 表示是原来图像的比例;
// 新增根据EXIF方向调整图片
adjustImageOrientation(png1, orientation);
doc.add(png1);
doc.close();
File mOutputPdfFile = new File(target); //输出流
if (!mOutputPdfFile.exists()) {
mOutputPdfFile.deleteOnExit();
}
mOutputPdfFile.createNewFile();
return true;
} catch (Exception ex) {
System.out.println("图片转pdf失败" + ex.getMessage());
return false;
}
}
// 新增获取图片的EXIF方向信息
private static int getExifOrientation(String imagePath) {
try {
File imageFile = new File(imagePath);
Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
// 遍历元数据目录,查找 EXIF 方向标签
for (Directory directory : metadata.getDirectories()) {
// 使用 ExifDirectoryBase 的常量
if (directory.containsTag(ExifDirectoryBase.TAG_ORIENTATION)) {
return directory.getInt(ExifDirectoryBase.TAG_ORIENTATION);
}
}
} catch (Exception e) {
System.err.println("读取EXIF方向失败: " + e.getMessage());
}
return 1; // 默认方向:正常
}
// 新增根据EXIF方向调整图片
private static void adjustImageOrientation(Image image, int orientation) throws DocumentException {
switch (orientation) {
case 1: // 正常
break;
case 3: // 旋转180度
image.setRotationDegrees(180);
break;
case 6: // 顺时针旋转90度
image.setRotationDegrees(90);
break;
case 8: // 逆时针旋转90度
image.setRotationDegrees(270);
break;
// 其他情况可以根据需要添加
}
}
/****
* 给PDF文件某些页增加马赛克
* @param srcFile 源文件
* @param tarFile 目标文件
* @param tarPages 需要增加马赛克的页号数组
* @return
*/
public static boolean PdfBlock(String srcFile, String tarFile, int[] tarPages) {
//目标文件存在,则先删除
File _tarFile = new File(tarFile);
if (_tarFile.exists()) {
_tarFile.delete();
}
try {
PdfReader pdfReader = new PdfReader(srcFile);
PdfReader.unethicalreading = true;
PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileOutputStream(tarFile));
//获取文件总页数
int numberOfPages = pdfReader.getNumberOfPages();
for (int page : tarPages) {
if (page > numberOfPages) {
break;
}
Rectangle rectangle = pdfReader.getPageSizeWithRotation(page);
float pageWidth = rectangle.getWidth();
float pageHeight = rectangle.getHeight();
PdfContentByte content = pdfStamper.getOverContent(page);
//保存状态
content.saveState();
content.setColorFill(BaseColor.BLACK);
content.rectangle(0, 0, pageWidth, pageHeight);
content.fill();
//还原状态
content.restoreState();
}
pdfStamper.close();
pdfReader.close();
return true;
} catch (Exception ex) {
System.out.println(ex.getMessage());
return false;
}
}
public static int getPercent(float h, float w) {
int p = 0;
float p2 = 0.0f;
if (h > w) {
p2 = 297 / h * 100;
} else {
p2 = 210 / w * 100;
}
p = Math.round(p2);
return p;
}
public static int getPercent2(float h, float w) {
int p = 0;
float p2 = 0.0f;
p2 = 530 / w * 100;
p = Math.round(p2);
return p;
}
/*
pdf加水印平铺
* */
public static void waterMark(String inputFile,
String outputFile, String waterMarkName) {
try {
PdfReader reader = new PdfReader(inputFile);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(
outputFile));
BaseFont base = getSimYouBaseFont();
// BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
Rectangle pageRect = null;
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.2f);
gs.setStrokeOpacity(0.2f);
int total = reader.getNumberOfPages() + 1;
JLabel label = new JLabel();
FontMetrics metrics;
int textH = 0;
int textW = 0;
label.setText(waterMarkName);
metrics = label.getFontMetrics(label.getFont());
textH = metrics.getHeight();
textW = metrics.stringWidth(label.getText());
PdfContentByte under;
for (int i = 1; i < total; i++) {
pageRect = reader.getPageSizeWithRotation(i);
under = stamper.getOverContent(i);
under.saveState();
under.setGState(gs);
under.beginText();
under.setFontAndSize(base, 30);
// 水印文字成30度角倾斜
//你可以随心所欲的改你自己想要的角度
for (int height = interval + textH; height < pageRect.getHeight();
height = height + textH*3) {
for (int width = interval + textW; width < pageRect.getWidth() + textW;
width = width + textW*2) {
under.showTextAligned(Element.ALIGN_LEFT
, waterMarkName, width - textW,
height - textH, 30);
}
}
// 添加水印文字
under.endText();
}
//说三遍
//一定不要忘记关闭流
//一定不要忘记关闭流
//一定不要忘记关闭流
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/*
pdf加水印平铺
*/
public static ByteArrayOutputStream waterMark(InputStream inputStream, String waterMarkName) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
PdfReader reader = new PdfReader(inputStream);
PdfStamper stamper = new PdfStamper(reader, outputStream);
BaseFont base = getSimYouBaseFont();
Rectangle pageRect = null;
PdfGState gs = new PdfGState();
gs.setFillOpacity(0.2f);
gs.setStrokeOpacity(0.2f);
int total = reader.getNumberOfPages() + 1;
JLabel label = new JLabel();
FontMetrics metrics;
int textH = 0;
int textW = 0;
label.setText(waterMarkName);
metrics = label.getFontMetrics(label.getFont());
textH = metrics.getHeight();
textW = metrics.stringWidth(label.getText());
PdfContentByte under;
for (int i = 1; i < total; i++) {
pageRect = reader.getPageSizeWithRotation(i);
under = stamper.getOverContent(i);
under.saveState();
under.setGState(gs);
under.beginText();
under.setFontAndSize(base, 30);
// 水印文字成30度角倾斜
//你可以随心所欲的改你自己想要的角度
for (int height = interval + textH; height < pageRect.getHeight();
height = height + textH*10) {
for (int width = interval + textW; width < pageRect.getWidth() + textW;
width = width + textW*9) {
under.showTextAligned(Element.ALIGN_LEFT
, waterMarkName, width - textW,
height - textH, 30);
}
}
// 添加水印文字
under.endText();
}
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return outputStream;
}
public static int getTifPageCount(String filePath) {
int pageCount = 0;
try {
File file = new File(filePath);
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("TIFF");
ImageReader reader = readers.next();
reader.setInput(new FileImageInputStream(file));
pageCount = reader.getNumImages(true);
reader.dispose();
} catch (IOException e) {
e.printStackTrace();
}
return pageCount;
}
public static void main1(String[] args) {
String source = "C:\\Users\\MI\\Desktop\\13\\pdf\\b.pdf";
String target = "C:\\Users\\MI\\Desktop\\13\\pdf\\b-" + DateUtil.date2String(new Date(), 3) + ".pdf";
// String source = "D:\\test\\222.pdf";
// String target = "D:\\test\\111.pdf";
String[][] aaa = {{"11", "12", "13"}, {"21", "22", "23"}};
Seal(source, target, 1, aaa, "2", 2);
// test(source, target);
// source = "d:/2.pdf";
// target = "d:/3.pdf";
// int[] pages = {1, 3};
//
// boolean r = PdfBlock(source, target, pages);
System.out.print("1");
}
public static void main2(String[] args) {
String source = "C:\\Users\\MI\\Desktop\\13\\img\\2.jpg";
String target = "C:\\Users\\MI\\Desktop\\13\\img\\\\2-110.pdf";
image2Pdf(source, target);
}
public static void main3(String[] args) {
String source = "C:\\Users\\MI\\Desktop\\13\\img\\1.jpg";
String target = "C:\\Users\\MI\\Desktop\\13\\img\\1-" + DateUtil.date2String(new Date(), 3) + ".pdf";
image2Pdf(source, target);
}
public static void main4(String[] args) {
String srcFile = "C:\\Users\\MI\\Desktop\\13\\pdf\\b.pdf";
String tarFile = "C:\\Users\\MI\\Desktop\\13\\pdf\\setWaterMark-" + DateUtil.date2String(new Date(), 3) + ".pdf";
String markStr = "管理员";
int fontSize = 50;
String color = "GREEN";
int globalOblique = 0;
setWaterMark(srcFile, tarFile, markStr, fontSize, color, globalOblique);
}
public static void main(String[] args) {
String tarFile = "/Users/ab/Desktop/pdf/test.pdf";
String[] fileArray = {"/Users/ab/Desktop/pdf/1.pdf", "/Users/ab/Desktop/pdf/2.pdf"};
mergePdf(fileArray, tarFile);
}
public static boolean mergeOfdAndPdf(String[] sourceFiles, String targetFile, String tempDir) {
if (sourceFiles == null || sourceFiles.length == 0) {
log.warn("输入文件数组为空");
return true;
}
Path tempPath = Paths.get(tempDir);
try {
if (!Files.exists(tempPath)) {
Files.createDirectories(tempPath);
}
} catch (Exception e) {
log.error("创建临时目录失败: {}", tempDir, e);
return false;
}
List<String> pdfFiles = new ArrayList<>();
List<String> tempFilesToDelete = new ArrayList<>();
try {
// 不在此处改变顺序,保持调用方传入顺序(调用方如需排序,请在外部完成)
try {
log.info("mergeOfdAndPdf 接收到 {} 个源文件(保持传入顺序)", (sourceFiles == null ? 0 : sourceFiles.length));
if (sourceFiles != null) {
for (int i = 0; i < sourceFiles.length; i++) {
log.info("顺序 {} -> {}", i + 1, sourceFiles[i]);
}
}
} catch (Exception ignore) {}
for (String filePath : sourceFiles) {
if (filePath == null || filePath.trim().isEmpty()) {
continue;
}
String lowerPath = filePath.toLowerCase();
if (lowerPath.endsWith(".pdf")) {
pdfFiles.add(filePath);
} else if (lowerPath.endsWith(".ofd")) {
// 将 OFD 转换后的 PDF 命名为原始文件名.pdf而非 UUID保证排序与可读性
String originalName = new File(filePath).getName();
String baseName = originalName.endsWith(".ofd") ? originalName.substring(0, originalName.length() - 4) : originalName;
// 简单清洗,防止异常字符
// 仅保留字母、数字、下划线、点和连字符,其他替换为下划线
String sanitized = baseName.replaceAll("[^\\w.-]+", "_");
Path outPath = Paths.get(tempDir, sanitized + ".pdf");
int suffix = 1;
while (Files.exists(outPath)) {
outPath = Paths.get(tempDir, sanitized + "(" + (suffix++) + ").pdf");
}
String tempPdfPath = outPath.toString();
try {
OfdToPdfUtil.ofdToPdf(filePath, tempPdfPath);
pdfFiles.add(tempPdfPath);
tempFilesToDelete.add(tempPdfPath);
log.info("OFD转换成功: {} -> {}", filePath, tempPdfPath);
} catch (Exception e) {
log.error("OFD转换失败: {}", filePath, e);
}
} else {
log.warn("不支持的文件格式: {}", filePath);
}
}
if (pdfFiles.isEmpty()) {
log.warn("没有可合并的PDF文件");
return false;
}
try {
log.info("最终参与合并的PDF顺序已按源文件名排序共 {} 个:", pdfFiles.size());
for (int i = 0; i < pdfFiles.size(); i++) {
log.info("合并顺序 {} -> {}", i + 1, pdfFiles.get(i));
}
} catch (Exception ignore) {}
boolean mergeSuccess = mergePdfFiles(
pdfFiles.toArray(new String[0]),
targetFile
);
return mergeSuccess;
} finally {
cleanupTempFiles(tempFilesToDelete);
}
}
/**
* 合并PDF文件(改进版)
*/
private static boolean mergePdfFiles(String[] fileArray, String targetFile) {
// 删除已存在的目标文件
File target = new File(targetFile);
if (target.exists() && !target.delete()) {
log.error("无法删除已存在的目标文件: {}", targetFile);
return false;
}
Document document = null;
PdfCopy pdfCopy = null;
try {
document = new Document();
pdfCopy = new PdfCopy(document, new FileOutputStream(targetFile));
document.open();
int successCount = 0;
int totalPages = 0;
try {
log.info("mergePdfFiles 将合并 {} 个PDF按如下顺序", (fileArray == null ? 0 : fileArray.length));
if (fileArray != null) {
for (int i = 0; i < fileArray.length; i++) {
log.info("合并顺序 {} -> {}", i + 1, fileArray[i]);
}
}
} catch (Exception ignore) {}
for (String filePath : fileArray) {
PdfReader reader = null;
try {
reader = new PdfReader(filePath);
int pages = reader.getNumberOfPages();
// 逐页添加
for (int i = 1; i <= pages; i++) {
pdfCopy.addPage(pdfCopy.getImportedPage(reader, i));
}
totalPages += pages;
successCount++;
log.info("已合并文件: {} ({}页)", filePath, pages);
} catch (Exception e) {
log.error("处理文件失败: {}", filePath, e);
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception e) {
log.warn("关闭reader失败", e);
}
}
}
}
log.info("合并完成: {}/{} 个文件, 共{}页",
successCount, fileArray.length, totalPages);
return successCount > 0;
} catch (Exception e) {
log.error("合并PDF失败", e);
return false;
} finally {
if (document != null && document.isOpen()) {
document.close();
}
}
}
/**
* 清理临时文件
*/
private static void cleanupTempFiles(List<String> tempFiles) {
for (String tempFile : tempFiles) {
try {
Files.deleteIfExists(Paths.get(tempFile));
log.debug("删除临时文件: {}", tempFile);
} catch (Exception e) {
log.warn("删除临时文件失败: {}", tempFile, e);
}
}
}
}