import com.jhlabs.image.GaussianFilter; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.ImageType; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.pdfbox.text.TextPosition; import javax.imageio.IIOImage; import javax.imageio.ImageIO; import javax.imageio.ImageWriteParam; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import java.awt.*; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class bjbdtest { private static final Pattern BARCODE_PATTERN = Pattern.compile("(条形码|条\\s*码)[::]?\\s*(\\d{10,20})"); // 测试示例 public static void main(String[] args) { String path = "D:\\集中打印\\pdf"; // 替换为实际路径,例如 "C:/Users/YourName/Documents" try { List<File> pdfFiles = findPDFFiles(path); System.out.println("找到 " + pdfFiles.size() + " 个 PDF 文件:"); for (File pdf : pdfFiles) { System.out.println(pdf.getAbsolutePath()); } // 2. 处理每个PDF文件 // for (File pdfFile : pdfFiles) { // System.out.println("\n处理文件: " + pdfFile.getName()); // System.out.println("文件路径: " + pdfFile.getAbsolutePath()); // // // 3. 提取文本内容 // String content = extractTextFromPDF(pdfFile); // // // 4. 输出前200个字符作为预览 // int previewLength = Math.min(200, content.length()); // String preview = content.substring(0, previewLength).replaceAll("\\s+", " "); // System.out.println("内容预览: " + preview + (content.length() > 200 ? "..." : "")); // // // 5. 可选:保存完整内容到文件 // // saveContentToFile(pdfFile, content); // } System.out.println("\n处理文件: " + pdfFiles.get(0).getName()); System.out.println("文件路径: " + pdfFiles.get(0).getAbsolutePath()); // 3. 提取文本内容 String content = extractTextFromPDF(pdfFiles.get(0)); String barcode = extractBarcode(content)+"03"; //pdf 转 jpg File jpgFile = pdfToHighQualityImage( pdfFiles.get(0), "D:\\集中打印\\pdf\\" + barcode ); // 4. 输出前200个字符作为预览 System.out.println(barcode); int previewLength = Math.min(30, content.length()); String preview = content.substring(0, previewLength).replaceAll("\\s+", " "); System.out.println("内容预览: " + preview + (content.length() > 30 ? "..." : "")); } catch (IllegalArgumentException e) { System.err.println("错误: " + e.getMessage()); } catch (Exception e) { throw new RuntimeException(e); } } public static File pdfToHighQualityImage(File pdfFile, String outputPath) throws IOException { // 系统级字体渲染优化 System.setProperty("sun.java2d.cmm", "sun.java2d.cmm.kcms.KcmsServiceProvider"); System.setProperty("org.apache.pdfbox.rendering.UsePureJavaCMYK", "true"); File outputFile = new File(outputPath + ".jpg"); try (PDDocument document = PDDocument.load(pdfFile)) { PDFRenderer renderer = new OptimizedPDFRenderer(document); // 仅处理第一页(如需多页可修改循环) BufferedImage image = renderer.renderImageWithDPI(0, 350, ImageType.RGB); // 高级图像后处理 BufferedImage optimizedImage = applyAdvancedProcessing(image); // 保存为高质量JPEG saveAsJpeg(optimizedImage, outputFile, 0.92f); // 92%质量 } return outputFile; } // 自定义渲染器优化字体处理 private static class OptimizedPDFRenderer extends PDFRenderer { public OptimizedPDFRenderer(PDDocument document) { super(document); setSubsamplingAllowed(true); } } // 高级图像处理流程 private static BufferedImage applyAdvancedProcessing(BufferedImage source) { BufferedImage result = new BufferedImage( source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g2d = result.createGraphics(); // 精细化的抗锯齿设置 g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_LCD_HRGB); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); // 轻微减轻字体权重 g2d.setComposite(AlphaComposite.SrcOver.derive(0.98f)); g2d.drawImage(source, 0, 0, null); // 边缘锐化处理 result = applyUnsharpMask(result, 0.7f); g2d.dispose(); return result; } // 高质量JPEG保存 private static void saveAsJpeg(BufferedImage image, File output, float quality) throws IOException { Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer = writers.next(); try (ImageOutputStream ios = ImageIO.createImageOutputStream(output)) { writer.setOutput(ios); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); writer.write(null, new IIOImage(image, null, null), param); } writer.dispose(); } // 反锐化掩模算法(减轻加粗效果) private static BufferedImage applyUnsharpMask(BufferedImage image, float amount) { BufferedImage blurred = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); // 创建轻微模糊版本 Graphics2D g = blurred.createGraphics(); g.drawImage(image, 0, 0, null); g.dispose(); blurred = new GaussianFilter(1.2f).filter(blurred, null); // 应用反锐化掩模 BufferedImage result = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB); for (int y = 0; y < image.getHeight(); y++) { for (int x = 0; x < image.getWidth(); x++) { int original = image.getRGB(x, y); int blur = blurred.getRGB(x, y); // 计算差值并混合 int deltaR = ((original >> 16) & 0xFF) - ((blur >> 16) & 0xFF); int deltaG = ((original >> 8) & 0xFF) - ((blur >> 8) & 0xFF); int deltaB = (original & 0xFF) - (blur & 0xFF); int newR = clamp((int)(((original >> 16) & 0xFF) + amount * deltaR)); int newG = clamp((int)(((original >> 8) & 0xFF) + amount * deltaG)); int newB = clamp((int)((original & 0xFF) + amount * deltaB)); result.setRGB(x, y, (newR << 16) | (newG << 8) | newB); } } return result; } private static int clamp(int value) { return Math.max(0, Math.min(255, value)); } // 同上文的extractBarcode方法 public static String extractBarcode(String text) { Pattern pattern = Pattern.compile(String.valueOf(BARCODE_PATTERN)); Matcher matcher = pattern.matcher(text); return matcher.find() ? matcher.group(2) : null; } /** * 提取PDF文件中的文本内容 * @param pdfFile PDF文件 * @return 提取的文本内容 */ public static String extractTextFromPDF(File pdfFile) { try (PDDocument document = PDDocument.load(pdfFile)) { // 1. 配置文本提取器 PDFTextStripper stripper = new PDFTextStripper() { @Override protected void processTextPosition(TextPosition text) { // 直接提取Unicode字符,避免字体问题 try { String unicode = text.getUnicode(); if (!unicode.isEmpty() && !unicode.matches("\\s")) { writeString(unicode); } } catch (IOException e) { throw new RuntimeException("文本提取失败", e); } } }; // 2. 设置提取参数 stripper.setSortByPosition(true); // 按物理位置排序 stripper.setShouldSeparateByBeads(false); stripper.setAddMoreFormatting(false); // 禁用自动格式化 // 3. 提取并清理结果 String rawText = stripper.getText(document); return cleanExtractedText(rawText); } catch (Exception e) { System.err.println("PDF处理错误: " + e.getMessage()); return ""; } } /** * 清理提取的文本(合并空格、移除无效字符) */ private static String cleanExtractedText(String text) { return text.replaceAll("[\\r\\n]+", "\n") // 合并换行 .replaceAll("[\\s]{2,}", " ") // 合并空格 .replaceAll("[\\x00-\\x1F]", ""); // 移除控制字符 } /** * 获取指定目录下所有 PDF 文件(包括子文件夹) * @param directoryPath 文件夹路径(例如:"C:/Documents") * @return PDF 文件列表 * @throws IllegalArgumentException 如果路径无效 */ public static List<File> findPDFFiles(String directoryPath) { File rootDir = new File(directoryPath); // 检查路径有效性 if (!rootDir.exists()) { throw new IllegalArgumentException("路径不存在: " + directoryPath); } if (!rootDir.isDirectory()) { throw new IllegalArgumentException("路径不是文件夹: " + directoryPath); } List<File> pdfFiles = new ArrayList<>(); LinkedList<File> queue = new LinkedList<>(); queue.add(rootDir); // 广度优先遍历文件夹(参考引用[2]方法) while (!queue.isEmpty()) { File currentDir = queue.removeFirst(); File[] files = currentDir.listFiles(); if (files != null) { for (File file : files) { if (file.isDirectory()) { queue.add(file); // 添加子文件夹到队列 } else if (file.isFile() && isPDFFile(file)) { pdfFiles.add(file); // 收集 PDF 文件 } } } } return pdfFiles; } // 检查文件是否为 PDF(基于扩展名) private static boolean isPDFFile(File file) { String fileName = file.getName().toLowerCase(); return fileName.endsWith(".pdf"); } } 转换成图片后没有加粗的字体变的加粗了 字体有可能改变了

时间: 2025-08-25 17:59:15 AIGC 浏览: 27 评论: 5
<think>我们正在解决Java中将PDF转换为图片后文字变粗的问题。这通常是由于PDF渲染时的抗锯齿设置或图像处理中的插值算法导致的。 可能的原因和解决方案: 1. 调整抗锯齿设置:在渲染PDF时,使用合适的抗锯齿模式。 2. 调整图像类型:使用BufferedImage.TYPE_INT_RGB而不是默认的TYPE_INT_ARGB,因为透明通道可能导致边缘变深。 3. 调整分辨率:提高分辨率可能减少字体变粗的现象。 4. 使用合适的图像缩放插值算法:在缩放图像时,使用双线性插值或双三次插值(如RenderingHints.VALUE_INTERPOLATION_BILINEAR)可能比最近邻插值更好。 我们将以Apache PDFBox为例,因为它是常用的Java库。在PDFBox中,我们可以设置渲染参数来优化输出。 示例代码调整: 使用PDFBox将PDF页面转换为图像时,可以尝试以下设置: ```java import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.rendering.ImageType; import java.awt.image.BufferedImage; import java.io.File; public class PdfToImageConverter { public static void main(String[] args) { try (PDDocument document = PDDocument.load(new File("input.pdf"))) { PDFRenderer renderer = new PDFRenderer(document); // 设置抗锯齿 renderer.setSubsamplingAllowed(true); // 允许子采样,可以提高渲染速度并可能减少变粗 renderer.setImageType(ImageType.RGB); // 使用RGB图像类型,避免透明通道 // 设置DPI(分辨率)为300,高DPI可以减少字体变粗 BufferedImage image = renderer.renderImage(0, 300); // 第一页,300 DPI // 保存图像 ImageIO.write(image, "jpg", new File("output.jpg")); } catch (Exception e) { e.printStackTrace(); } } } ``` 另外,如果生成的图片仍然有变粗现象,可以尝试在保存之前对图像进行后处理,例如使用锐化滤镜或调整对比度。但通常调整渲染参数即可。 如果使用其他库(如iText),则需要调整相应的渲染设置。 相关问题: 1. 如何在使用PDFBox时进一步提高转换图像的质量? 2. 除了PDFBox,还有哪些Java库可以用于PDF转图片? 3. 如何调整图像的后处理以减少字体变粗? 4. 为什么提高分辨率可以减少字体变粗的现象?</think>### Java PDF转图片后文字变粗问题解决方案 在Java中将PDF转换为图片时出现文字变粗的问题,通常是由于渲染过程中的抗锯齿处理、分辨率设置或图像格式问题导致的。以下是系统性的解决方案: #### 1. **调整渲染分辨率**(核心方案) 提高DPI设置可显著改善文字渲染效果: ```java PDFRenderer renderer = new PDFRenderer(document); renderer.setDPI(300); // 关键设置:提高到300-400 DPI BufferedImage image = renderer.renderImageWithDPI(pageIndex, dpi); ``` **原理**:低DPI(如96)会导致字体轮廓模糊,高DPI提供更多像素精确渲染字形[^1]。 #### 2. **优化抗锯齿设置** ```java Graphics2D graphics = image.createGraphics(); graphics.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON ); graphics.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON ); ``` #### 3. **使用RGB图像模式** 避免透明通道干扰: ```java BufferedImage image = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB // 替代默认的TYPE_INT_ARGB ); ``` #### 4. **PDFBox高级配置**(推荐) ```java PDFRenderer renderer = new PDFRenderer(document); renderer.setSubsamplingAllowed(true); // 启用子采样 renderer.setImageType(ImageType.RGB); // RGB模式 renderer.setAnnotationsRendering(true); // 正确渲染注释 ``` #### 5. **后处理锐化**(备选方案) ```java Kernel kernel = new Kernel(3, 3, new float[] { 0, -1, 0, -1, 5, -1, 0, -1, 0 }); BufferedImageOp op = new ConvolveOp(kernel); image = op.filter(image, null); ``` #### 完整工作流示例 ```java try (PDDocument doc = PDDocument.load(pdfFile)) { PDFRenderer renderer = new PDFRenderer(doc); renderer.setDPI(350); renderer.setImageType(ImageType.RGB); for (int i = 0; i < doc.getNumberOfPages(); i++) { BufferedImage image = renderer.renderImage(i); ImageIO.write(image, "PNG", new File("output_"+i+".png")); // PNG优于JPG } } ``` **关键参数对比**: | 参数 | 推荐值 | 问题值 | |------|--------|--------| | DPI | 300-400 | <150 | | 图像类型 | RGB | ARGB | | 输出格式 | PNG | JPG | | 抗锯齿 | 开启 | 关闭 | **根本原因**:字体渲染时亚像素定位不准确导致笔画重叠,在高分辨率下该效应减弱[^2]。 --- ### 相关问题 1. 如何平衡PDF转图片的质量与性能? 2. 除PDFBox外,哪些Java库能更好地处理PDF渲染? 3. 为什么PNG格式比JPG更适合保存文本图像? 4. 如何批量处理PDF转图片时的内存优化? 5. 在不同操作系统上渲染效果不一致如何解决? [^1]: 高DPI设置通过增加像素密度提升轮廓精度 [^^2]: 亚像素渲染误差导致笔画宽度视觉增加
阅读全文

相关推荐

package com.zzyl.common.utils; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; import java.io.File; import java.io.IOException; import java.io.InputStream; public class PDFUtil { public static String pdfToString(InputStream inputStream) { PDDocument document = null; try { // 加载PDF文档 document = PDDocument.load(inputStream); // 创建一个PDFTextStripper实例来提取文本 PDFTextStripper pdfStripper = new PDFTextStripper(); // 从PDF文档中提取文本 String text = pdfStripper.getText(document); return text; } catch (IOException e) { e.printStackTrace(); } finally { // 关闭PDF文档 if (document != null) { try { document.close(); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } } java.io.EOFException: null at org.apache.fontbox.ttf.TTFDataStream.readUnsignedInt(TTFDataStream.java:151) at org.apache.fontbox.ttf.TTFParser.readTableDirectory(TTFParser.java:312) at org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:139) at org.apache.fontbox.ttf.TTFParser.parse(TTFParser.java:87) at org.apache.pdfbox.pdmodel.font.FileSystemFontProvider.addTrueTypeFont(FileSystemFontProvider.java:657) at org.apache.pdfbox.pdmodel.font.FileSystemFontProvider.scanFonts(FileSystemFontProvider.java:379) at org.apache.pdfbox.pdmodel.font.FileSystemFontProvider.<init>(FileSystemFontProvider.java:358) at org.apache.pdfbox.pdmodel.font.FontMapperImpl$DefaultFontProvider.<clinit>(FontMapperImpl.java:140) at org.apache.pdfbox.pdmodel.font.FontMapperImpl.getProvider(FontMapperImpl.java:159) at org.apache.pdfbox.pdmodel.font.FontMapperImpl.findFont(FontMapperImpl.java:423) at org.apache.pdfbox.pdmodel.font.FontMapperImpl.getTrueTypeFont(FontMapperImpl.java:331) at org.apache.pdfbox.pdmodel.font.PDTrueTypeFont.<init>(PDTrueTypeFont.java:215) at org.apache.pdfbox.pdmodel.font.PDFontFactory.createFont(PDFontFactory.java:89) at org.apache.pdfbox.pdmodel.PDResources.getFont(PDResources.java:146) at org.apache.pdfbox.contentstream.operator.text.SetFontAndSize.process(SetFontAndSize.java:66) at org.apache.pdfbox.contentstream.PDFStreamEngine.processOperator(PDFStreamEngine.java:933) at org.apache.pdfbox.contentstream.PDFStreamEngine.processStreamOperators(PDFStreamEngine.java:514) at org.apache.pdfbox.contentstream.PDFStreamEngine.processStream(PDFStreamEngine.java:492) at org.apache.pdfbox.contentstream.PDFStreamEngine.processPage(PDFStreamEngine.java:155) at org.apache.pdfbox.text.LegacyPDFStreamEngine.processPage(LegacyPDFStreamEngine.java:144) at org.apache.pdfbox.text.PDFTextStripper.processPage(PDFTextStripper.java:394) at org.apache.pdfbox.text.PDFTextStripper.processPages(PDFTextStripper.java:322) at org.apache.pdfbox.text.PDFTextStripper.writeText(PDFTextStripper.java:269) at org.apache.pdfbox.text.PDFTextStripper.getText(PDFTextStripper.java:233) at com.zzyl.common.utils.PDFUtil.pdfToString(PDFUtil.java:25) at com.zzyl.nursing.controller.HealthAssessmentController.uploadFile(HealthAssessmentController.java:134)

package org.jeecg.modules.manage.util; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDOutlineItem; import org.apache.pdfbox.rendering.PDFRenderer; import org.ofdrw.core.basicStructure.doc.CT_DocInfo; import org.ofdrw.core.basicStructure.ofd.DocBody; import org.ofdrw.core.basicStructure.ofd.OFD; import org.ofdrw.core.basicStructure.pageTree.Page; import org.ofdrw.core.basicType.ST_Box; import org.ofdrw.core.basicType.ST_ID; import org.ofdrw.core.basicType.ST_RefID; import org.ofdrw.core.pageDescription.CT_PageArea; import org.ofdrw.core.pageDescription.drawParam.PageArea; import org.ofdrw.layout.OFDDoc; import org.ofdrw.layout.element.Img; import org.ofdrw.reader.OFDReader; import java.awt.image.BufferedImage; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class PdfToOfdConverter { public static void convertPdfToOfd(String pdfPath, String ofdPath) throws IOException { try (PDDocument pdfDoc = PDDocument.load(Paths.get(pdfPath).toFile())) { // 创建OFD文档 try (OFDDoc ofdDoc = new OFDDoc(Paths.get(ofdPath))) { // 存储页码到OFD页面ID的映射 Map<Integer, ST_ID> pageIdMap = new HashMap<>(); PDFRenderer pdfRenderer = new PDFRenderer(pdfDoc); // 转换每一页PDF for (int i = 0; i < pdfDoc.getNumberOfPages(); i++) { PDPage pdfPage = pdfDoc.getPage(i); // 获取PDF页面尺寸并转换为毫米 double[] size = getPageSize(pdfPage); double width = size[0]; double height = size[1]; // 创建OFD页面并设置尺寸 Page ofdPage = createOfdPage(width, height); // 添加页面并获取页面ID ST_ID pageId = ofdDoc.addPage(ofdPage); pageIdMap.put(i, pageId); // 渲染PDF页面为图片 BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300); // 将图片添加到OFD页面 Img img = new Img(image); img.setPosition(0, 0); img.setSize(width, height); ofdDoc.add(img); } // 提取PDF书签并转换为OFD书签 List<Bookmark> bookmarks = extractBookmarks(pdfDoc, pageIdMap); // 将书签添加到文档元数据 addBookmarksToMetadata(ofdDoc, bookmarks); } } } // 创建OFD页面并正确设置尺寸 private static Page createOfdPage(double width, double height) { // 创建页面区域对象 PageArea physicalArea = new PageArea(); physicalArea.setPhysicalBox(new ST_Box(0, 0, width, height)); // 创建页面描述区域 CT_PageArea pageArea = new CT_PageArea(); pageArea.setPhysicalBox(physicalArea); // 创建OFD页面 Page ofdPage = new Page(); ofdPage.setArea(pageArea); return ofdPage; } // 获取PDF页面尺寸并转换为毫米 private static double[] getPageSize(PDPage page) { // PDF尺寸单位是点 (1点 = 1/72英寸) float pdfWidth = page.getMediaBox().getWidth(); float pdfHeight = page.getMediaBox().getHeight(); // 转换为毫米 (1英寸 = 25.4毫米) double width = pdfWidth / 72 * 25.4; double height = pdfHeight / 72 * 25.4; return new double[]{width, height}; } // 自定义书签类 static class Bookmark { String name; ST_RefID pageRef; double x; double y; List<Bookmark> children = new ArrayList<>(); public Bookmark(String name, ST_RefID pageRef, double x, double y) { this.name = name; this.pageRef = pageRef; this.x = x; this.y = y; } } // 提取PDF书签结构 private static List<Bookmark> extractBookmarks(PDDocument pdfDoc, Map<Integer, ST_ID> pageIdMap) throws IOException { List<Bookmark> bookmarks = new ArrayList<>(); PDOutlineItem root = pdfDoc.getDocumentCatalog().getDocumentOutline(); if (root != null) { extractBookmarkItems(root, bookmarks, pdfDoc, pageIdMap); } return bookmarks; } // 递归提取书签项 private static void extractBookmarkItems(PDOutlineItem item, List<Bookmark> bookmarks, PDDocument pdfDoc, Map<Integer, ST_ID> pageIdMap) throws IOException { if (item == null) return; // 获取书签目标页码 int targetPageNum = item.findDestinationPage(pdfDoc); ST_RefID pageRef = null; double x = 0; double y = 0; if (targetPageNum >= 0 && pageIdMap.containsKey(targetPageNum)) { pageRef = new ST_RefID(pageIdMap.get(targetPageNum)); // 获取书签位置(PDF单位:点) float[] position = item.getDestination().getPosition(); if (position != null && position.length >= 2) { // 转换为毫米 x = position[0] / 72 * 25.4; y = position[1] / 72 * 25.4; } } // 创建书签 Bookmark bookmark = new Bookmark(item.getTitle(), pageRef, x, y); bookmarks.add(bookmark); // 递归处理子书签 PDOutlineItem child = item.getFirstChild(); while (child != null) { extractBookmarkItems(child, bookmark.children, pdfDoc, pageIdMap); child = child.getNextSibling(); } } // 将书签添加到OFD文档元数据 private static void addBookmarksToMetadata(OFDDoc ofdDoc, List<Bookmark> bookmarks) { // 使用OFD文档的元数据存储书签信息 StringBuilder bookmarkXml = new StringBuilder("<Bookmarks>"); for (Bookmark bm : bookmarks) { bookmarkXml.append(bookmarkToXml(bm)); } bookmarkXml.append("</Bookmarks>"); // 添加到自定义元数据 CT_DocInfo docInfo = ofdDoc.getOFD().getDocBody().getDocInfo(); docInfo.addCustomData("Bookmarks", bookmarkXml.toString()); } // 单个书签转换为XML private static String bookmarkToXml(Bookmark bm) { StringBuilder xml = new StringBuilder("<Bookmark>"); xml.append("<Name>").append(escapeXml(bm.name)).append("</Name>"); if (bm.pageRef != null) { xml.append("").append(bm.pageRef.toString()).append(""); xml.append("<X>").append(bm.x).append("</X>"); xml.append("<Y>").append(bm.y).append("</Y>"); } if (!bm.children.isEmpty()) { xml.append("<Children>"); for (Bookmark child : bm.children) { xml.append(bookmarkToXml(child)); } xml.append("</Children>"); } xml.append("</Bookmark>"); return xml.toString(); } // XML转义处理 private static String escapeXml(String input) { return input.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace("\"", """) .replace("'", "'"); } // 书签读取工具 public static List<Bookmark> readBookmarks(String ofdPath) throws Exception { try (OFDReader reader = new OFDReader(Path.of(ofdPath))) { OFD ofd = reader.getOFD(); DocBody docBody = ofd.getDocBody(); CT_DocInfo docInfo = docBody.getDocInfo(); // 获取书签XML String bookmarkXml = docInfo.getCustomDatas().stream() .filter(data -> "Bookmarks".equals(data.getName())) .findFirst() .map(data -> new String(data.getContent())) .orElse("<Bookmarks/>"); // 解析XML(简化版,实际应使用XML解析器) return parseBookmarkXml(bookmarkXml); } } // 解析书签XML(简化实现) private static List<Bookmark> parseBookmarkXml(String xml) { // 实际应用中应使用DOM或SAX解析器 // 这里简化为返回空列表 return new ArrayList<>(); } public static void main(String[] args) { try { convertPdfToOfd("input.pdf", "output.ofd"); System.out.println("PDF转换OFD成功!"); // 读取书签验证 List<Bookmark> bookmarks = readBookmarks("output.ofd"); System.out.println("读取到书签数量: " + bookmarks.size()); } catch (Exception e) { System.err.println("转换失败: " + e.getMessage()); e.printStackTrace(); } } } import org.ofdrw.core.basicStructure.doc.CT_DocInfo; import org.ofdrw.core.pageDescription.CT_PageArea; import org.ofdrw.core.pageDescription.drawParam.PageArea; 这三个无法解析 使用的是 <dependency> <groupId>org.ofdrw</groupId> <artifactId>ofdrw-full</artifactId> <version>2.3.6</version> </dependency>

<groupId>org.jeecgframework.boot</groupId> <artifactId>jeecg-module-system</artifactId> <version>3.4.0</version> <modelVersion>4.0.0</modelVersion> <artifactId>jeecg-system-biz</artifactId> <dependencies> <dependency> <groupId>org.jeecgframework.boot</groupId> <artifactId>jeecg-system-local-api</artifactId> <exclusions> <exclusion> <artifactId>checker-qual</artifactId> <groupId>org.checkerframework</groupId> </exclusion> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> <exclusion> <artifactId>mybatis-plus-extension</artifactId> <groupId>com.baomidou</groupId> </exclusion> <exclusion> <artifactId>swagger-annotations</artifactId> <groupId>io.swagger</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> </dependency> <dependency> <groupId>org.jeecgframework.boot</groupId> <artifactId>hibernate-re</artifactId> </dependency> <dependency> <groupId>org.jeecgframework</groupId> <artifactId>jeewx-api</artifactId> <exclusions> <exclusion> <artifactId>commons-collections</artifactId> <groupId>commons-collections</groupId> </exclusion> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.jeecgframework.jimureport</groupId> <artifactId>jimureport-spring-boot-starter</artifactId> <exclusions> <exclusion> <artifactId>commons-collections</artifactId> <groupId>commons-collections</groupId> </exclusion> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.5.1</version> <exclusions> <exclusion> <artifactId>jsqlparser</artifactId> <groupId>com.github.jsqlparser</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.swagger</groupId> <artifactId>swagger-annotations</artifactId> <version>1.5.22</version> </dependency> <dependency> <groupId>net.sf.dozer</groupId> <artifactId>dozer</artifactId> <version>5.5.1</version> <exclusions> <exclusion> <artifactId>commons-beanutils</artifactId> <groupId>commons-beanutils</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.baidu.aip</groupId> <artifactId>java-sdk</artifactId> <version>4.16.2</version> <exclusions> <exclusion> <artifactId>guava</artifactId> <groupId>com.google.guava</groupId> </exclusion> <exclusion> <artifactId>jsr305</artifactId> <groupId>com.google.code.findbugs</groupId> </exclusion> <exclusion> <artifactId>slf4j-simple</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>net.sourceforge.tess4j</groupId> <artifactId>tess4j</artifactId> <version>3.4.0</version> <exclusions> <exclusion> <artifactId>commons-beanutils</artifactId> <groupId>commons-beanutils</groupId> </exclusion> <exclusion> <artifactId>commons-io</artifactId> <groupId>commons-io</groupId> </exclusion> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.2</version> </dependency> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>3.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>2.1.7</version> </dependency> <dependency> <groupId>org.opencv</groupId> <artifactId>opencv</artifactId> <version>4.6.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/lib/opencv-460.jar</systemPath> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.9</version> </dependency> <dependency> <groupId>com.belerweb</groupId> <artifactId>pinyin4j</artifactId> <version>2.5.1</version> </dependency> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.24</version> </dependency> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>7.1.15</version> <type>pom</type> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.4.3</version> </dependency> <dependency> <groupId>javax.media</groupId> <artifactId>jai-core</artifactId> <version>1.1.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.2.1</version> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>4.0.4</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>2.7.9</version> <optional>true</optional> </dependency> <dependency> <groupId>com.drewnoakes</groupId> <artifactId>metadata-extractor</artifactId> <version>2.16.0</version> </dependency> <dependency> <groupId>org.ofdrw</groupId> <artifactId>ofdrw-full</artifactId> <version>2.3.6</version> </dependency> </dependencies> 这是我的pom.xml,使用这个引用编写一份把PDF转换成ofd,包括PDF的标签, OFD ofd = ofdDoc.getOfd();这个方法,找不到方法调用 ofdDoc.getOfd() 的候选者 import org.ofdrw.core.basicStructure.doc.Bookmark; import org.ofdrw.core.basicStructure.doc.Bookmarks; 这两个标签无法引用 Page ofdPage = new Page(); ofdPage.setSize(getPageSize(pdfDoc.getPage(i))); ST_ID pageId = ofdDoc.addPage(ofdPage); 找不到方法调用 ofdDoc.addPage(ofdPage) ,ofdDoc.addPage(ofdPage);的候选者。 无法解析符号 'Bookmarks'

评论
用户头像
吉利吉利
2025.08.07
调整抗锯齿和图像类型有助于改善字体在转换过程中的加粗现象。
用户头像
白小俗
2025.07.13
使用PDFBox高级设置,如启用子采样和正确的图像类型,可减少字体变粗。
用户头像
shashashalalala
2025.06.02
PNG格式比JPG更适合保存文本图像,可以保持字体清晰度。
用户头像
南小鹏
2025.05.11
解决了Java中PDF转图片导致字体变粗的问题,通过优化渲染参数和分辨率设置。
用户头像
zh222333
2025.03.31
在PDF转图片的后处理阶段,锐化或调整对比度可进一步优化字体质量。

最新推荐

recommend-type

基于LSTM-Attention与GRU-Attention的语音情感识别系统设计与实现

<项目说明> 本项目为个人在学习阶段完成的课程实践内容,代码经过验证可正常执行,所有功能均通过测试后才进行发布,整体评估成绩为94.5分,具有较高可靠性,可放心获取与使用。该项目适用于计算机类专业学生、教师及从业人员进行技术学习与研究,同时也适合初学者作为入门练习,或作为毕业设计、课程作业及项目开发的参考案例。若具备一定基础,可在此基础上进行功能扩展或优化,适用于多种教学与实践场景。获取后建议优先查阅项目中的说明文档(如README.md),仅供研究与学习用途,禁止用于商业目的。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
recommend-type

C++调用CTP接口获取行情数据

C++调用CTP接口
recommend-type

用C语言掌握网络编程:套接字与安全代码编写指南

《使用C进行动手网络编程》是一本由Lewis Van Winkle编写的书籍,由Packt出版,专注于教授读者如何使用C语言编写网络程序。在这本书中,作者不仅向读者介绍了C语言中套接字编程的基础知识,还深入探讨了如何开发安全且优化的网络代码。以下是从书籍标题、描述和标签中提取出的关键知识点: 1. C语言网络编程基础 - 套接字编程是网络通信的核心技术,它允许计算机之间通过网络传输数据。 - 在C语言中使用套接字API编写网络程序是一项高级技能,需要对网络协议和操作系统API有深入的理解。 - 学习套接字编程可以帮助开发者构建客户端和服务器端的网络应用。 2. 跨平台套接字编程API - 跨平台编程是软件开发中的重要概念,意味着编写的应用能够在多种操作系统上运行。 - 套接字API在不同的操作系统中存在差异,但也有共通之处,作者可能会介绍如何编写适应多个操作系统的网络代码。 3. 支持IPv4和IPv6技术的实现 - IPv4和IPv6是互联网上使用的两种主要网络层协议。 - 随着IPv6的推广,网络程序需要能够同时支持这两种协议,实现无缝通信。 4. TCP和UDP连接的工作原理 - 传输控制协议(TCP)和用户数据报协议(UDP)是两种常用的传输层协议。 - TCP提供可靠的、面向连接的通信服务,而UDP提供不可靠的、无连接的数据传输服务。 - 本书可能涉及如何在C语言中使用TCP和UDP实现网络应用。 5. 主机名解析和DNS工作机制 - 域名系统(DNS)用于将域名解析为IP地址,这是互联网通信的关键部分。 - 主机名解析是网络程序中常见需求,了解DNS的工作原理对于网络开发来说至关重要。 6. 使用HTTP和HTTPS与Web API进行接口 - 超文本传输协议(HTTP)和安全超文本传输协议(HTTPS)是互联网上应用最广泛的协议之一。 - 学习如何使用HTTP和HTTPS可以让开发者与Web API进行交互,开发出能够访问网络资源的应用程序。 7. 通过SMTP进行电子邮件协议的实践 - 简单邮件传输协议(SMTP)用于发送电子邮件。 - 掌握SMTP协议能够使开发者实现发送邮件的功能,这对于许多网络应用来说是一个有用的特性。 8. 物联网(IoT)的新方法 - 物联网指的是将各种日常物品通过网络连接起来的设备或系统。 - C语言是物联网开发中常用的编程语言之一,因其性能高效且对资源的要求低。 - 探索物联网的新方法可能包括对嵌入式系统编程的介绍,以及如何在受限设备上实现网络通信。 总结来说,这本书是一本针对有志于深入学习C语言网络编程的开发者或学生编写的实用性教材。通过阅读本书,读者不仅可以学习到网络编程的基础知识,还能够掌握如何开发出稳定、高效的网络应用,并了解网络技术的最新发展,特别是物联网方面的应用。书中内容的组织结构和实例代码可以帮助读者将理论知识转化为实践经验,对于希望扩展自己网络编程技能的初学者和专业人士来说,是一本宝贵的参考资料。
recommend-type

阻塞 vs 非阻塞任务提交:接口设计背后的性能权衡与场景选择建议

# 摘要 本文系统探讨了阻塞与非阻塞任务提交机制在并发编程中的核心作用,从基本概念出发,剖析同步与异步、阻塞与非阻塞的本质区别及其在线程行为和执行模型中的体现。文章深入研究任务调度的关键性能指标及并发模型的支持机制,结合线程池、Future/Promise、Reactor与Actor等技术,分析阻塞与非阻塞在Java线程池、Spring异步注解和Netty框架中的具体实现。通过对比不同任
recommend-type

zsh安装

### 安装 Zsh Shell Zsh(Z Shell)是一个功能强大的 Unix shell,相比传统的 Bash,它提供了更丰富的功能和更好的交互体验。以下是针对 Linux 和 macOS 系统安装 Zsh 的详细步骤。 #### 在 Linux 上安装 Zsh Linux 系统通常可以通过包管理器安装 Zsh。常见的发行版如 CentOS、Ubuntu、Debian 等均支持通过以下方式安装: - **CentOS / RHEL 系统**: 使用 `yum` 安装 Zsh: ```bash sudo yum install zsh ``` 如果使用的是较新
recommend-type

Python包装器urlscan-py:简化urlscan.io API使用

标题中提到的“urlscan-py”是一个Python语言编写的包装器程序,专为urlscan.io的API服务。这表明它是一个开发工具,使得在Python中调用urlscan.io的API变得更加容易,从而实现对URL的扫描功能。 描述部分详细介绍了如何使用urlscan-py。首先,提供了通过Docker使用urlscan-py的方法,即使用“docker pull heywoodlh/urlscan-py”命令来下载Docker镜像。接着,提到可以通过PyPI(Python Package Index)安装urlscan-py,使用“pip3 install --user urlscan-py”命令进行安装。这样,Python开发者就可以在本地环境中使用urlscan-py。 安装后,用户需要保存API密钥。这一步是与urlscan.io服务交互所必需的,API密钥类似于一个访问令牌,用于在调用API时验证用户身份和授权。API密钥应保存在默认的数据库中,该数据库还会记录所有启动的扫描结果。在Linux系统中,默认数据库文件的位置通常为“~/.urlscan/urlscan.db”,在Windows系统中位置可能有所不同。 如果API密钥输入错误,或者在使用过程中发生其他错误导致数据库中的API密钥值不正确,用户可以通过执行“urlscan init --api xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”命令来重新初始化API密钥并保存到本地数据库。这个命令中的“--api”参数后面应该跟随实际的API密钥。如果需要修改或覆盖已经存在的错误密钥,可以重复执行上述命令。 在描述中还暗示了urlscan-py的一些潜在功能,例如启动URL扫描和记录结果。尽管没有详细说明,但通常此类包装器会提供诸如启动扫描、获取扫描状态、查看扫描结果等接口或命令,用户可以通过这些接口或命令与urlscan.io的API进行交互。 关于“【标签】: Python”,这指的是urlscan-py程序使用Python语言编写。Python是一种广泛使用的高级编程语言,以其简洁易读的语法、强大的标准库以及在科学计算、网络开发、数据科学等领域的广泛应用而知名。由于Python的易用性和灵活性,它常常被用来快速开发各种工具和应用程序。 最后,“【压缩包子文件的文件名称列表】: urlscan-py-master”提示了该文件所在的具体位置或版本库的名称。在软件开发中,文件名称列表通常用来标识包含特定代码或资源的文件夹或压缩包。此处的“urlscan-py-master”可能指的是包含urlscan-py源代码的主分支(master branch),或者是包含该程序所有资源的压缩包名称。用户可能需要通过下载或克隆这个名称所指向的资源来进行安装或者开发工作。 综上所述,urlscan-py是一个为urlscan.io API提供Python语言接口的工具,它可以简化对特定URL的扫描工作。开发者可通过Docker或PyPI的方式安装urlscan-py,并通过命令行操作来初始化和管理API密钥。此外,urlscan-py的源代码可能位于名为“urlscan-py-master”的资源库中。
recommend-type

深入pthread_mutex_t与pthread_cond_t:彻底避免死锁和虚假唤醒的6种策略

# 摘要 线程同步是多线程编程中保障数据一致性和程序正确性的核心机制,而pthread_mutex_t与pthread_cond_t是POSIX线程库中实现同步的关键工具。本文系统阐述了线程同步的基础概念与典型问题,深入剖析互斥锁的类型、机制及其正确使用模式,并揭示了因加锁顺序不当或异常路径导致的死锁成因。结合条件变量的工作原理,重点探讨虚假唤醒的本质及循环验证谓词的必要性。进一步提出锁层级排序、
recommend-type

ububtu 更新火狐浏览器

在 Ubuntu 系统上更新 Firefox 浏览器可以通过多种方式进行,包括使用系统自带的软件更新工具、通过官方 PPA 添加最新版本,或者手动下载并安装 Firefox 的 tar.gz 包。以下是几种常见的更新方法: ### 使用 APT 更新 Firefox Ubuntu 默认的软件包管理工具 APT 可以用于更新 Firefox。首先确保系统软件包索引是最新的,然后执行升级命令: ```bash sudo apt update sudo apt upgrade firefox ``` 这种方法适用于大多数用户,能够确保 Firefox 更新到官方仓库提供的最新版本[^1]。 ##
recommend-type

Aurora Engine在NEAR上部署EVM:Rust实现的前沿探索

标题《Aurora Engine在NEAR协议上实现以太坊虚拟机(EVM)-Rust开发》所涉及的知识点主要集中在区块链技术领域,特别是与智能合约开发、多链互操作性、以及Rust编程语言的相关技术细节。以下是对标题和描述中提到的内容进行详细解释。 ### 区块链互操作性与Aurora Engine Aurora Engine是一种重要的区块链技术,它的出现解决了不同区块链协议之间的互操作性问题。互操作性是区块链技术发展中的关键挑战之一,因为它能够允许跨不同区块链的资产、数据和功能进行交互。在本例中,Aurora Engine被用来在NEAR协议上实现以太坊虚拟机(EVM),这意味着NEAR协议能够运行以太坊智能合约,这对于以太坊的开发者和用户来说是一个巨大的便利。 ### NEAR协议与以太坊虚拟机(EVM) NEAR协议是一个开源的云计算平台,支持智能合约的运行,并且着重于高性能、高可扩展性和易用性。NEAR支持的智能合约是用Rust语言编写的,提供了安全、高效的方式来处理交易和状态的变更。通过实现EVM,NEAR协议能够提供一个与以太坊兼容的环境,这样原本为以太坊开发的智能合约和去中心化应用(dApp)就可以不需要做大量的修改直接移植到NEAR协议上。 ### 部署网络与链ID状态 描述中提到了部署网络和链ID状态,这通常指的是在不同环境(如主网、测试网、本地开发网等)中智能合约部署的具体配置。在区块链领域,主网(MainNet)是指正式上线并用于生产环境的网络,而测试网(如BetaNet或TestNet)是为了测试目的而存在的网络,本地开发网(Local)则是开发者在本地机器上搭建的,用于本地开发和测试的网络。链ID是一个独特的标识符,用于区分不同的区块链网络。 ### WebAssembly工具链 WebAssembly(Wasm)是一种执行字节码的轻量级虚拟机,它在区块链领域的智能合约开发中扮演着重要角色。WebAssembly支持多语言编程,特别是Rust语言,因此它被广泛用于区块链智能合约的开发中。GNU Make是一个构建自动化工具,用于在编程中自动化编译过程。描述中提到的“每晚构建”可能是指在开发过程中定期自动执行构建过程,以便进行持续集成和测试。 ### Rust开发环境的构建 Rust是一种系统编程语言,它专注于速度、内存安全和并发性。描述中提及了部署Aurora Engine时必须满足的Rust开发环境配置,这包括安装Rust的nightly版本(即开发版),并添加wasm32-unknown-unknown目标,这个目标支持将Rust编译为WebAssembly格式。rustup是一个用于管理Rust版本的工具,它可以安装不同版本的Rust编译器并更新工具链。 ### 标签:Rust与加密货币 标签中的“Rust”指出了这个项目与Rust编程语言的紧密关联。由于Rust的设计目标与区块链的需求高度契合,它已经成为区块链领域中非常流行的编程语言。标签中的“Cryptocurrencies”表明Aurora Engine与加密货币和区块链技术直接相关,特别是它在兼容EVM方面的作用。 ### 压缩包子文件的文件名称列表 文件名称列表“aurora-engine-master”表示当前讨论的项目可能是一个开源项目,它包含一个名为“master”的主分支,通常是指项目的主要代码分支。在这种情况下,开发者可以获取该代码库,并在本地环境中进行测试、修改和部署。通常这类代码库中会包含编译脚本、合约源代码、智能合约的接口定义等。 总结而言,这个文件中提到的知识点涵盖了区块链智能合约开发的多个方面,特别是关于跨链互操作性和Rust编程语言在区块链生态中的应用。这不仅对于区块链开发者来说是一个重要的参考,同时也为对区块链技术感兴趣的读者提供了一个深入理解EVM兼容性和智能合约开发的窗口。
recommend-type

函数指针+void*参数传递精髓:实现通用回调接口的3大陷阱与避坑指南

# 摘要 本文系统探讨了基于函数指针与void*参数的通用回调机制的核心原理及其在工业级软件中的应用与风险。文章首先解析函数指针的调用约定与void*的类型擦除特性,进而提出通用回调接口的抽象建模方法,并深入剖析类型不匹配、生命周期失控及线程安全等三大经典陷阱。结合防御式编程理念,本文提出了封装结构体、编译期类型检查与运行时校验相结合的防护策略,并通过构建高可靠事件注册系统展示实践