package com.product.file.util;
|
|
import java.awt.image.BufferedImage;
|
import java.io.BufferedReader;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.FileReader;
|
import java.io.IOException;
|
import java.io.InputStreamReader;
|
import java.nio.charset.StandardCharsets;
|
|
import javax.imageio.ImageIO;
|
|
import org.apache.fontbox.ttf.TrueTypeCollection;
|
import org.apache.pdfbox.pdmodel.PDDocument;
|
import org.apache.pdfbox.pdmodel.PDPage;
|
import org.apache.pdfbox.pdmodel.PDPageContentStream;
|
import org.apache.pdfbox.pdmodel.common.PDRectangle;
|
import org.apache.pdfbox.pdmodel.font.PDType0Font;
|
import org.apache.pdfbox.rendering.PDFRenderer;
|
import org.apache.pdfbox.text.PDFTextStripper;
|
|
public class PDFOperateUtil {
|
|
|
public static void createPdfWithCode(String javaFilePath, String outputPath) throws IOException {
|
String javaCode = readJavaFileWithComments(javaFilePath);
|
|
try (PDDocument document = new PDDocument()) {
|
PDPage page = new PDPage(PDRectangle.A4);
|
document.addPage(page);
|
|
// 加载字体(如 SimSun)
|
File fontFile = getSystemFontFile();
|
if (!fontFile.exists()) {
|
throw new IOException("未找到中文字体文件");
|
}
|
TrueTypeCollection ttc = new TrueTypeCollection(fontFile);
|
// 通过字体名称获取(如 "SimSun")
|
PDType0Font font = PDType0Font.load(document, ttc.getFontByName("SimSun"), true);
|
// PDType0Font font = PDType0Font.load(document, fontFile);
|
float fontSize = 10;
|
float margin = 50;
|
float leading = 1.2f * fontSize; // 行距
|
|
// 可用宽度 = 页面宽度 - 左右边距
|
PDRectangle mediaBox = page.getMediaBox();
|
float availableWidth = mediaBox.getWidth() - 2 * margin;
|
|
// 初始 Y 位置(从顶部开始)
|
float yPosition = mediaBox.getHeight() - margin;
|
float startX = margin;
|
|
// 分割代码为行
|
String[] lines = javaCode.split("\n");
|
|
PDPageContentStream contentStream = null;
|
try {
|
contentStream = new PDPageContentStream(document, page);
|
contentStream.setFont(font, fontSize);
|
|
for (String line : lines) {
|
// 如果当前行超出页面底部,换页
|
if (yPosition < margin) {
|
contentStream.close();
|
page = new PDPage(PDRectangle.A4);
|
document.addPage(page);
|
yPosition = mediaBox.getHeight() - margin;
|
contentStream = new PDPageContentStream(document, page);
|
contentStream.setFont(font, fontSize);
|
}
|
|
// 处理超宽行:按字符拆分并换行
|
while (!line.isEmpty()) {
|
// 计算当前行能容纳的最大字符数
|
int maxChars = calculateMaxChars(font, fontSize, line, availableWidth);
|
String subLine = line.substring(0, Math.min(maxChars, line.length()));
|
line = line.substring(subLine.length());
|
|
// 写入当前子行
|
writeTextLine(contentStream, subLine, startX, yPosition);
|
yPosition -= leading;
|
|
// 如果剩余内容仍超出页面底部,换页
|
if (yPosition < margin && !line.isEmpty()) {
|
contentStream.close();
|
page = new PDPage(PDRectangle.A4);
|
document.addPage(page);
|
yPosition = mediaBox.getHeight() - margin;
|
contentStream = new PDPageContentStream(document, page);
|
contentStream.setFont(font, fontSize);
|
}
|
}
|
}
|
} finally {
|
if (contentStream != null) {
|
contentStream.close();
|
}
|
}
|
|
document.save(outputPath);
|
}
|
}
|
|
// 计算一行能容纳的最大字符数
|
private static int calculateMaxChars(PDType0Font font, float fontSize, String text, float availableWidth) throws IOException {
|
float width = 0;
|
int i = 0;
|
for (; i < text.length(); i++) {
|
width += font.getWidth(text.charAt(i)) / 1000 * fontSize;
|
if (width > availableWidth) {
|
break;
|
}
|
}
|
return i;
|
}
|
|
// 写入单行文本(左对齐)
|
private static void writeTextLine(PDPageContentStream contentStream, String text, float x, float y) throws IOException {
|
contentStream.beginText();
|
contentStream.newLineAtOffset(x, y);
|
contentStream.showText(text);
|
contentStream.endText();
|
}
|
|
/**
|
* 读取 Java 文件内容(保留注释和空行)
|
*/
|
private static String readJavaFileWithComments(String filePath) throws IOException {
|
StringBuilder codeBuilder = new StringBuilder();
|
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
|
String line;
|
while ((line = reader.readLine()) != null) {
|
line = line.replace("\t", " ");
|
// 保留所有行(包括注释和空行)
|
codeBuilder.append(line).append("\n");
|
}
|
}
|
return codeBuilder.toString();
|
}
|
|
/**
|
* 计算字体高度(基于字体的 Ascent 和 Descent)
|
*/
|
private static float calculateFontHeight(PDType0Font font, float fontSize) {
|
float ascent = font.getFontDescriptor().getAscent();
|
float descent = font.getFontDescriptor().getDescent();
|
return (ascent - descent) / 1000 * fontSize;
|
}
|
|
/**
|
* 根据文本文件创建 PDF(使用 PDFBox)
|
*
|
* @param textFilePath 输入文本文件路径
|
* @param pdfFilePath 输出 PDF 文件路径
|
* @throws IOException
|
*/
|
public static void createPdfFromTextFile(String textFilePath, String pdfFilePath) throws IOException {
|
String content = readTextFile(textFilePath);
|
content = content.replace("\t", " ");
|
try (PDDocument document = new PDDocument()) {
|
PDPage page = new PDPage(PDRectangle.A4);
|
document.addPage(page);
|
|
// 获取字体文件(仅支持 Windows/Linux)
|
File fontFile = getSystemFontFile();
|
if (!fontFile.exists()) {
|
throw new IOException("未找到支持的中文字体文件(Windows/Linux)");
|
}
|
TrueTypeCollection ttc = new TrueTypeCollection(fontFile);
|
|
// 通过字体名称获取(如 "SimSun")
|
PDType0Font font = PDType0Font.load(document, ttc.getFontByName("SimSun"), true);
|
|
// 加载字体
|
// PDType0Font font;
|
// try (FileInputStream fontStream = new FileInputStream(fontFile)) {
|
// font = PDType0Font.load(document, fontStream);
|
// }
|
|
// 写入 PDF 内容
|
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
|
contentStream.beginText();
|
contentStream.setFont(font, 12);
|
contentStream.newLineAtOffset(50, 700);
|
|
String[] lines = content.split("\n");
|
for (String line : lines) {
|
contentStream.showText(line);
|
contentStream.newLineAtOffset(0, -15);
|
}
|
contentStream.endText();
|
}
|
|
document.save(pdfFilePath);
|
}
|
}
|
|
/**
|
* 读取 PDF 内容(使用 PDFBox)
|
*
|
* @param pdfFilePath PDF 文件路径
|
* @return 提取的文本内容
|
* @throws IOException
|
*/
|
public static String readPdfContent(String pdfFilePath) throws IOException {
|
try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {
|
PDFTextStripper stripper = new PDFTextStripper();
|
return stripper.getText(document);
|
}
|
}
|
|
/**
|
* 将 PDF 转换为图片(使用 PDFBox)
|
*
|
* @param pdfFilePath PDF 文件路径
|
* @param outputFolder 输出图片文件夹
|
* @param imageName 输出图片文件名
|
* @param imageFormat 图片格式(如 "png", "jpg")
|
* @throws IOException
|
*/
|
public static void convertPdfToImages(String pdfFilePath, String outputFolder, String imageName, String imageFormat) throws IOException {
|
try (PDDocument document = PDDocument.load(new File(pdfFilePath))) {
|
PDFRenderer renderer = new PDFRenderer(document);
|
|
// 确保输出文件夹存在
|
new File(outputFolder).mkdirs();
|
|
// 逐页渲染为图片(300 DPI)
|
for (int page = 0; page < document.getNumberOfPages(); page++) {
|
BufferedImage image = renderer.renderImageWithDPI(page, 300);
|
String outputFileName = String.format("%s/%s%d.%s", outputFolder, imageName, page + 1, imageFormat);
|
ImageIO.write(image, imageFormat, new File(outputFileName));
|
}
|
}
|
}
|
|
/**
|
* 辅助方法:读取文本文件内容
|
*
|
* @param filePath 文本文件路径
|
* @return 文件内容
|
* @throws IOException
|
*/
|
private static String readTextFile(String filePath) throws IOException {
|
StringBuilder content = new StringBuilder();
|
try (BufferedReader reader = new BufferedReader(
|
new InputStreamReader(new FileInputStream(filePath), StandardCharsets.UTF_8))) {
|
String line;
|
while ((line = reader.readLine()) != null) {
|
content.append(line).append("\n");
|
}
|
}
|
return content.toString();
|
}
|
|
/**
|
* 获取系统字体文件(仅 Windows/Linux)
|
*/
|
private static File getSystemFontFile() {
|
String os = System.getProperty("os.name").toLowerCase();
|
String[] possiblePaths;
|
|
if (os.contains("win")) {
|
// Windows 字体目录(优先检查常见中文字体)
|
possiblePaths = new String[]{
|
// System.getenv("WINDIR") + "\\Fonts\\arial.ttf", //
|
// System.getenv("WINDIR") + "\\Fonts\\simkai.ttf", // 楷体
|
// System.getenv("WINDIR") + "\\Fonts\\simfang.ttf", // 宋体
|
System.getenv("WINDIR") + "\\Fonts\\simsun.ttc", // 宋体
|
System.getenv("WINDIR") + "\\Fonts\\simhei.ttf", // 黑体
|
System.getenv("WINDIR") + "\\Fonts\\msyh.ttc", // 微软雅黑
|
System.getenv("WINDIR") + "\\Fonts\\mingliu.ttc" // 明体(备用)
|
};
|
} else {
|
// Linux 字体目录(优先检查开源中文字体)
|
possiblePaths = new String[]{
|
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", // 文泉驿微米黑
|
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", // 思源黑体
|
"/usr/share/fonts/truetype/arphic/ukai.ttc" // 文鼎字体(备用)
|
};
|
}
|
|
// 检查所有可能的路径
|
for (String path : possiblePaths) {
|
File fontFile = new File(path);
|
if (fontFile.exists()) {
|
return fontFile;
|
}
|
}
|
|
// 如果系统字体未找到,尝试从项目资源目录加载(需手动放入字体文件)
|
File embeddedFont = new File("fonts/simhei.ttf"); // 假设字体文件放在项目根目录的 fonts/ 文件夹下
|
return embeddedFont.exists() ? embeddedFont : null;
|
}
|
|
// 示例用法
|
public static void main(String[] args) {
|
try {
|
// 1. 从文本文件创建 PDF
|
// createPdfFromTextFile("E://新建1.txt", "E://新建1.pdf");
|
createPdfWithCode("E:\\PROJECT\\Product\\后端\\product\\product-server\\product-server-file\\src\\main\\java\\com\\product\\file\\util\\FileUtil.java", "E://新建1.pdf");
|
|
// 2. 读取 PDF 内容
|
String content = readPdfContent("E://新建1.pdf");
|
System.out.println("PDF 内容:\n" + content);
|
|
// 3. 将 PDF 转换为图片
|
convertPdfToImages("E://新建1.pdf", "E://", "新建1", "png");
|
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
}
|