| | |
| | | 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; |
| | |
| | | |
| | | 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) |
| | | * |
| | |
| | | */ |
| | | 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); |
| | |
| | | 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); |
| | | } |
| | | // PDType0Font font; |
| | | // try (FileInputStream fontStream = new FileInputStream(fontFile)) { |
| | | // font = PDType0Font.load(document, fontStream); |
| | | // } |
| | | |
| | | // 写入 PDF 内容 |
| | | try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) { |
| | |
| | | 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" // 明体(备用) |
| | |
| | | public static void main(String[] args) { |
| | | try { |
| | | // 1. 从文本文件创建 PDF |
| | | createPdfFromTextFile("E://新建1.txt", "E://新建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"); |