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



<dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.6</version></dependency>

<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>

<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.4</version></dependency>

<dependency><groupId>net.sourceforge.tess4j</groupId><artifactId>tess4j</artifactId><version>5.13.0</version></dependency>

<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>


-
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(); }
-
使用 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[]{}; }
-
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[]{}; }

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

往期推荐
夜雨聆风
