1041 lines
38 KiB
Java
1041 lines
38 KiB
Java
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|