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(); } } }