乐于分享
好东西不私藏

深度解析:如何使用Java工具从PDF文档中提取和操作文本

深度解析:如何使用Java工具从PDF文档中提取和操作文本

01
技术介绍
Apache pdfbox
官网介绍:https://pdfbox.apache.org/
Apache PDFBox 是一个用于处理 PDF 文档的开源 Java 工具。该项目允许创建新的 PDF 文档、操作现有文档以及 从文档中提取内容。 Apache PDFBox 还包括几个命令行实用程序。 Apache PDFBox 是根据 Apache 许可证 v2.0 发布的。
Maven 依赖
<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.6</version></dependency>
Spire.PDF
官网介绍:https://www.e-iceblue.cn/spirepdfjava/spire-pdf-for-java-program-guide-content.html
Spire.PDF for Java 是一款专门对 PDF 文档进行操作的 Java 类库。该类库的主要功能在于帮助开发人员在 Java 应 用程序( J2SE 和 J2EE ) 中生成 PDF 文档和操作现有 PDF 文档,并且运行环境无需安装 Adobe Acrobat 。同时兼 容大部分国产操作系统,能够在中标麒麟和中科方德等国产操作系统中正常运行。
Spire.PDF for Java 支持的功能非常丰富,例如 PDF 文档安全性设置,提取文本和图片,合并和拆分 PDF 文档, 绘制文本、图片、形状、条形码到 PDF ,打印 PDF 文档,创建、读取和填充表单域,导入和导出 PDF 表单数据,添加和删除层,重叠 PDF 页面,添加文本和图片水印,添加、更新、删除 PDF 中的书签,添加表格,添加注释和动作,压缩 PDF 文档,获取 PDF 页数等。此外, Spire.PDF for Java 还支持将 PDF 文档高质量地转换为XPS 、图片、 Excel 、 SVG 、 Word 、 HTML 、 PowerPoint 演示文稿、 OFD 和 PDF/A 格式,以及将 SVG 、 XPS 、 TXT 、图片和 HTML 文档高质量地转换为 PDF 格式。
Maven 依赖
<repositories><repository><id>com.e-iceblue</id><name>e-iceblue</name><url>https://repo.e-iceblue.cn/repository/maven-public/</url></repository></repositories><dependencies><dependency><groupId>e-iceblue</groupId><artifactId>spire.pdf</artifactId><version>10.11.2</version></dependency></dependencies>
iText
官网介绍:https://itextpdf.com/
iText 是著名的开源项目,用于生成 PDF 文档的一个 java 类库。通过 iText 不仅可以生成 PDF 或 rtf 的文档,而且可 以将 XML 、 Html 文件转化为 PDF 文件等。
Maven 依赖
<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.4</version></dependency>
Tesseract
官网介绍:https://tesseract.patagames.com/
Tesseract 是一个开源的 OCR 引擎,可以用于识别图像中的文字。它支持多种语言,包括英语、中文等。
Maven 依赖
<dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess4j</artifactId><version>5.13.0</version></dependency>
Spire.OCR
官网介绍:https://www.e-iceblue.cn/spire_ocr_java/how-to-scan-and-recognize-text-from-images-in-java-projects.html
Spire.OCR for Java 能够帮助开发者在 Java 项目中快速批量识别并提取图片上的文字,实现高效的文字提取功 能。
Maven 依赖
<repositories><repository><id>com.e-iceblue</id><name>e-iceblue</name><url>https://repo.e-iceblue.cn/repository/maven-public/</url></repository></repositories><dependencies><dependency><groupId>e-iceblue</groupId><artifactId>spire.ocr</artifactId><version>1.9.19</version></dependency></dependencies>
02
代码示例
解析没有水印的 pdf 文本
  • Spire.PDF 实现
publicstatic String readPdfForSpire(InputStream inputStream)throws Exception {// 创建 PdfDocument 对象@Cleanup        PdfDocument doc = new PdfDocument();// 加载 PDF 文件        doc.loadFromStream(inputStream);// 获取第一页,遍历文档所有页便可提取文档所有文本内容        PdfPageCollection page = doc.getPages();        StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < page.getCount(); i++) {// 创建PdfTextExtractor 对象            PdfTextExtractor textExtractor = new PdfTextExtractor(page.get(i));// 创建PdfTextExtractOptions 对象            PdfTextExtractOptions extractOptions = new PdfTextExtractOptions();// 从页面中提取文本            String text = textExtractor.extract(extractOptions);            stringBuilder.append(text);        }return stringBuilder.toString();    }
  • Apache pdfbox 实现
/**     * 读取pdf文件中的文本内容     * 描述:会在每页的最后才读取水印内容     * https://www.cnblogs.com/whsongblog/p/7906869.html     *     * @param inputStream 上传文件     * @return     */publicstaticString[] commonParse(InputStream inputStream) throws Exception {@Cleanup        PDDocument document = PDDocument.load(inputStream);if (!document.isEncrypted()) {            PDFTextStripper tStripper = new PDFTextStripper();String pdfFileInText = tStripper.getText(document);// split by whitespacereturn pdfFileInText.split("\\r?\\n");        }returnnewString[]{};    }
  • iText 实现
publicstatic String readPdfByPage(InputStream inputStream){        StringBuilder result = new StringBuilder();try {// 新建一个PDF解析器对象            PdfReader reader = new PdfReader(inputStream);            reader.setAppendable(true);// 对PDF文件进行解析,获取PDF文档页码for (int i = 1; i <= reader.getNumberOfPages(); i++) {//一页页读取PDF文本                String pageStr = PdfTextExtractor.getTextFromPage(reader, i);                result.append(pageStr);            }            reader.close();        } catch (Exception e) {            e.printStackTrace();        }return result.toString();    }
解析带水印的 pdf 文本
  • 使用 Apache pdfbox 去除水印后,再读取内容。
publicclassPdfBoxRemoveWaterMarkDemoextendsPDFTextStripper{private Integer rotationAngle;publicPdfBoxRemoveWaterMarkDemo(Integer rotationAngle)throws IOException {super();if (rotationAngle == null) {                rotationAngle = 5;            }this.rotationAngle = rotationAngle;        }@OverrideprotectedvoidwriteString(String text, List<TextPosition> textPositions)throws IOException {            Iterator<TextPosition> iterator = textPositions.iterator();while (iterator.hasNext()) {                TextPosition position = iterator.next();//获取字体倾斜度                Matrix m = position.getTextMatrix().clone();                m.concatenate(position.getFont().getFontMatrix());int angle = (int) Math.round(Math.toDegrees(Math.atan2(m.getShearX(), m.getScaleX())));// 根据旋转角度大小 判定为水印 去除if (Math.abs(angle) > this.rotationAngle) {                    iterator.remove();                }            }            text = textPositions.stream().filter(elm -> !Objects.isNull(elm)).map(TextPosition::getUnicode).collect(Collectors.joining());super.writeString(text, textPositions);        }/**         * 解析文本或表格格式的pdf文件         * 会根据旋转角度判断是否为水印,删除后再读取。         * 既支持带水印,也支持不带水印的文件。         *         * @param inputStream 文件输入流         * @param rotationAngle 旋转角度         * @return         */publicstatic String explainRemoveWatermark(InputStream inputStream, Integer rotationAngle)throws Exception {//加载文件@Cleanup PDDocument pdDocument = PDDocument.load(inputStream);//传入旋转角度            PdfBoxRemoveWaterMarkDemo textStripper = new PdfBoxRemoveWaterMarkDemo(rotationAngle);//解析的pdf内容return textStripper.getText(pdDocument);        }
  • 使用 Apache pdfbox 将水印放在最后读取。
/**         * 读取pdf文件中的文本内容         * 描述:会在每页的最后才读取水印内容         * https://www.cnblogs.com/whsongblog/p/7906869.html         *         * @param inputStream 上传文件         * @return         */publicstaticString[] commonParse(InputStream inputStream) throws Exception {@Cleanup PDDocument document = PDDocument.load(inputStream);if (!document.isEncrypted()) {                PDFTextStripper tStripper = new PDFTextStripper();String pdfFileInText = tStripper.getText(document);// split by whitespacereturn pdfFileInText.split("\\r?\\n");            }returnnewString[]{};        }
解析 pdf 表格
  • Spire.PDF 实现
/**         * 解析不带水印的pdf表格数据         * 该方法仅支持不带水印的表格数据的读取。         *         * @param inputStream 输入流         * @return         */publicstatic String[] explainWithTable(InputStream inputStream) throws Exception {//加载PDF文档@Cleanup PdfDocument pdf = new PdfDocument();            pdf.loadFromStream(inputStream);//创建StringBuilder类的实例            StringBuilder builder = new StringBuilder();//抽取表格            PdfTableExtractor extractor = new PdfTableExtractor(pdf);for (int page = 0; page < pdf.getPages().getCount(); page++) {                PdfTable[] pdfTables = extractor.extractTable(page);if (pdfTables == null || pdfTables.length == 0) {continue;                }for (PdfTable table : pdfTables) {int row = table.getRowCount();int column = table.getColumnCount();for (int i = 0; i < row; i++) {for (int j = 0; j < column; j++) {                            String text = table.getText(i, j);                            builder.append(text).append(" ");                        }                        builder.append("\r\n");                    }                }            }return builder.toString().split("\\r?\\n");        }
  • iText 实现
publicstatic String readPdfByPage(InputStream inputStream){            StringBuilder result = new StringBuilder();try {// 新建一个PDF解析器对象                PdfReader reader = new PdfReader(inputStream);                reader.setAppendable(true);// 对PDF文件进行解析,获取PDF文档页码for (int i = 1; i <= reader.getNumberOfPages(); i++) {//一页页读取PDF文本                    String pageStr = PdfTextExtractor.getTextFromPage(reader, i);                    result.append(pageStr);                }                reader.close();            } catch (Exception e) {                e.printStackTrace();            }return result.toString();        }
  • Apache pdfbox 实现
/**         * 读取pdf文件中的文本内容         * 描述:会在每页的最后才读取水印内容         * https://www.cnblogs.com/whsongblog/p/7906869.html         *         * @param inputStream 上传文件         * @return         */publicstaticString[] explainWithWatermark(InputStream inputStream) throws Exception {@Cleanup            PDDocument document = PDDocument.load(inputStream);if (!document.isEncrypted()) {                PDFTextStripper tStripper = new PDFTextStripper();String pdfFileInText = tStripper.getText(document);// split by whitespacereturn pdfFileInText.split("\\r?\\n");            }returnnewString[]{};        }
根据起始页和结束页解析文件(文本 + 表格 )
  • Apache pdfbox 实现
/**         * 根据起始页和结束页提取部分内容         *         * @param inputStream 文件输入流         * @param startPage 开始页数         * @param endPage 结束页数         */publicstatic String[] parseByPage(InputStream inputStream, int startPage, int endPage) throws Exception {//获取PDDocument文档对象@Cleanup PDDocument pdDocument = PDDocument.load(inputStream);if (!pdDocument.isEncrypted()) {//获取一个PDFTextStripper文本剥离对象                PDFTextStripper stripper = new PDFTextStripper();// 设置起始页                stripper.setStartPage(startPage);// 设置结束页                stripper.setEndPage(endPage);//获取文本内容                String content = stripper.getText(pdDocument);//打印内容return content.split("\\r?\\n");            }returnnew String[]{};        }
解析 pdf 图片
分为两步:先解析出图片,再通过 OCR 识别图片内容。
图像的清晰度对 OCR 识别率有显著影响,如果图像模糊或分辨率低,识别率会大大降低。开源的 OCR 识别技术准确率不高,如果对文字识别有较高要求,请慎用。
  • 使用 Apache pdfbox + Tesseract 实现
publicstaticString explainWithPicture(InputStream inputStream) throws Exception {@Cleanup PDDocument document = PDDocument.load(inputStream);            PDFRenderer renderer = new PDFRenderer(document);// 使用OCR识别图像中的文字            Tesseract tesseract = new Tesseract();// 设置Tesseract的语言数据文件路径:// 设置方法:https://blog.csdn.net/u010833154/article/details/135599860// 安装路径:https://digi.bib.uni-mannheim.de/tesseract/, 已下载到resource目录下。            tesseract.setDatapath("D:\\software\\TesseractOCR\\tessdata");            StringBuilder result = new StringBuilder();// 遍历每一页for (int pageIndex = 0; pageIndex < document.getNumberOfPages(); pageIndex++) {// 处理图片                BufferedImage image = renderer.renderImageWithDPI(pageIndex, 300, ImageType.RGB);String text = tesseract.doOCR(image);                result.append(text);            }return result.toString();        }
  • 使用 Spire.PDF + Spire.OCR 实现
/**         * 解析pdf文件:先把pdf转成图片,再调用spire.ocr识别。         * 图像的清晰度对OCR识别率有显著影响,如果图像模糊或分辨率低,识别率会大大降低。         * 开源的OCR识别技术准确率不高,如果对文字识别有较高要求,请采用闭源的技术(如百度OCR)。         *         * @param inputStream PDF文档的输入流         * @return 提取出的文本         */publicstaticString explainWithPicture(InputStream inputStream, String language, String modelPath) throws Exception {@Cleanup// 打开扫描PDF文档            PdfDocument pdf = new PdfDocument();            pdf.loadFromStream(inputStream);            StringBuilder stringBuilder = new StringBuilder();// 配置并初始化OCR扫描器            OcrScanner scanner = new OcrScanner();            ConfigureOptions configureOptions = new ConfigureOptions();// 设置OCR识别语言,支持语言包括 English, Chinese, Chinesetraditional, French, German, Japanese 和             Korean configureOptions.setLanguage(language);// 设置OCR模型路径            configureOptions.setModelPath(modelPath);// 应用配置            scanner.ConfigureDependencies(configureOptions);// 从扫描PDF文档中识别文字并将结果保存到文本文件for (int pageIndex = 0; pageIndex < pdf.getPages().getCount(); pageIndex++) {                BufferedImage image = pdf.saveAsImage(pageIndex);// 将BufferedImage转换为InputStream@Cleanup ByteArrayOutputStream os = new ByteArrayOutputStream();                ImageIO.write(image, "PNG", os);// 识别文本@Cleanup InputStream imageStream = new ByteArrayInputStream(os.toByteArray());                scanner.Scan(imageStream, OCRImageFormat.Png);// 返回识别出的文本String scannedText = scanner.getText().toString();                stringBuilder.append(scannedText);            }return stringBuilder.toString();        }
END
👇欢迎分享和推荐,好运将与你相伴~👇

往期推荐

程序员副业新选择:10大接单平台助你技能变现

[阿里二面] Redis内存淘汰算法,这样答直接通关

别再裸用CompletableFuture了!从原理到避坑必看硬核指南

MySQL日志系统全景:从redo/undo到binlog

深入Redis核心:探索字符串、哈希、列表、集合和有序集合的命令与实现

一次讲透MySQL存储引擎

ConcurrentHashMap:从JDK1.7到1.8,那些面试必问的底层逻辑

Mysql索引的底层原理是什么

马年春节倒计时,打工人年度的终极期待—年终奖,终于迎来全面曝光。

Java多线程:从入门到实战

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 深度解析:如何使用Java工具从PDF文档中提取和操作文本

评论 抢沙发

7 + 5 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮