当前位置: 首页 > news >正文

如何截取PDF内容为图片

说明:本文介绍 Java 中,如何去截取 PDF 中的内容,转为一张图片

场景

如下,该 PDF 结构分两部分,一部分个人信息,一部分内容信息,我希望截取其中的内容信息,截取成一张图片。

在这里插入图片描述

实现

首先,在生成该 PDF 的模板文件中,需要截取的部分(内容信息)前后,加入截取点,字体设置为白色,这样截取点内容看不出来

在这里插入图片描述

编写代码,找到 PDF 中截取点文本内容的位置,获取坐标,并计算

import com.hezy.pojo.TextPositionWithDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.text.PDFTextStripper;
import org.apache.pdfbox.text.TextPosition;
import org.springframework.stereotype.Component;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.List;/*** PDF裁剪图片处理器*/
@Component
@Slf4j
public class PDFCutToImageExtractor {/*** 开始截取点*/private static final String START_POINT = "cut-start";/*** 结束截取点*/private static final String END_POINT = "cut-end";/*** 外边距*/private static final int MARGIN = 20;/*** 提取PDF文件中范围的图片** @param file PDF文件* @return 图片字节数组* @throws IOException*/public byte[] extractImage(File file) throws IOException {try (PDDocument document = PDDocument.load(file)) {// 1.获取文本位置PDFRenderer renderer = new PDFRenderer(document);// 开始和结束位置TextPositionWithDTO startPos = findTextPosition(document, START_POINT);TextPositionWithDTO endPos = findTextPosition(document, END_POINT);// 查询判断(截取点是手动放到模板中的,不可能找不到,但还是判断一下)if (startPos == null) {return null;}if (endPos == null) {return null;}// 获取截取点坐标float startY = startPos.getTextPosition().getY();float endY = endPos.getTextPosition().getY();log.info("截取点坐标:startY={}, endY={}", startY, endY);// 2.渲染图像,计算截取位置BufferedImage pageImage = renderer.renderImageWithDPI(startPos.getPageIndex(), 144);// 定义缩放,这个是按照上一行代码中设置的dpi来计算的,144/72=2int scale = 2;// 左上角坐标 = 起始截取点的x、y坐标float startX = startPos.getTextPosition().getX();int imgStartX = Math.round(startX * scale) - MARGIN;int imgStartY = Math.round(startY * scale);// 图片宽度 = 页宽 - 起始点x坐标int width = pageImage.getWidth() - imgStartX;// 图片高度 = 两截取点高度差int height = Math.round(Math.abs(startY - endY) * scale) - MARGIN;// 判断是否计算有误if (width <= 0 || height <= 0 || imgStartX < 0 || imgStartY < 0) {return null;}// 3.裁剪图像,将截取后的图像文件写入到新的文件流中,返回字节数组log.info("imgStartX: {}, imgStartY: {}, width: {}, height: {}", imgStartX, imgStartY, width, height);BufferedImage croppedImage = pageImage.getSubimage(imgStartX, imgStartY, width, height);ByteArrayOutputStream bos = new ByteArrayOutputStream();try {ImageIO.write(croppedImage, "png", bos);return bos.toByteArray();} catch (IOException e) {log.warn("写入失败: {}", e.getMessage());return null;}}}/*** 查询文本位置* 作用:该方法的作用是根据传入的文本关键字查询文本在文档中所在的位置** @param document   PDF文档* @param searchText 要查询的文本* @return 文本位置DTO* @throws IOException*/private TextPositionWithDTO findTextPosition(PDDocument document, String searchText) throws IOException {/*** 内部类:继承PDFTextStripper,提取PDF文档中的文本内容*/class MyTextStripper extends PDFTextStripper {/*** 找到的文本位置*/private TextPosition foundPosition = null;public MyTextStripper() throws IOException {super();}@Overrideprotected void writeString(String text, List<TextPosition> textPositions) {// 文本位置,不为空,说明已经找到了,直接返回if (foundPosition != null) {return;}// 拿到PDF文档中的文本内容StringBuilder stringBuilder = new StringBuilder();for (TextPosition pos : textPositions) {String unicode = pos.getUnicode();if (unicode != null) {stringBuilder.append(unicode);}}String segmentText = stringBuilder.toString();// 用传入的文本与PDF文档中的文本来匹配,indexOf()方法是精髓int index = segmentText.indexOf(searchText);// 大于等于0,说明文档中有匹配到的文本if (index >= 0) {int charCount = 0;for (TextPosition pos : textPositions) {String unicode = pos.getUnicode();if (unicode == null) {continue;}if (charCount == index) {foundPosition = pos;return;}charCount++;}}}public TextPosition getResult() {return foundPosition;}}// 遍历每一页int totalPages = document.getNumberOfPages();for (int pageIndex = 0; pageIndex < totalPages; pageIndex++) {MyTextStripper stripper = new MyTextStripper();stripper.setStartPage(pageIndex + 1);stripper.setEndPage(pageIndex + 1);// 处理当前页stripper.writeText(document, new StringWriter());TextPosition result = stripper.getResult();if (result != null) {return new TextPositionWithDTO(result, pageIndex);}}return null;}
}

注意以下两点:

  • PDF 文本坐标(TextPosition),是以文件左下角为原点的,越靠右x越大,越靠上y越大;

  • pageImage.getSubimage()方法,四个参数定义截取的矩形范围,前两个参数定义矩形左上角坐标,后两个参数定义矩形的宽和高

(源码说明)

在这里插入图片描述

TextPositionWithDTO 对象

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.pdfbox.text.TextPosition;import java.io.Serializable;/*** 文本位置DTO*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TextPositionWithDTO implements Serializable {/*** 文本位置*/private TextPosition textPosition;/*** 文本位置所在的页码*/private int pageIndex;
}

controller,写一个接口,先获取 PDF 文件,再截取其中的图片

    @PostMapping("/pdf2")public byte[] pdf2() throws IOException {// 1.获取PDFbyte[] pdf = pdfService.pdf();// 2.将PDF写入到本地临时文件夹中File pdfFile = FileUtil.createTempFile("demo", ".pdf", null, true);FileUtil.writeBytes(pdf, pdfFile);// 3.构建响应String fileName = "截取图片.png";String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setContentDispositionFormData("attachment", encodedFileName);// 4.截取图片,获取图片的字节数组byte[] image = pdfCutToImageExtractor.extractImage(pdfFile);// 5.删除临时存储的PDF文件FileUtil.del(pdfFile);// 6.返回return ResponseEntity.ok().headers(headers).body(image).getBody();}

以上代码引入的 pom.xml 文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.12</version><relativePath/></parent><groupId>com.hezy</groupId><artifactId>pdf_demo</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- web依赖,用调用接口的方式来测试 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 生成pdf依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>com.github.jhonnymertz</groupId><artifactId>java-wkhtmltopdf-wrapper</artifactId><version>1.3.1-RELEASE</version></dependency><!-- lombok依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!-- PDF截取依赖 --><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>2.0.27</version></dependency><!-- 工具类 --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.6</version></dependency></dependencies><!-- 编译插件,定义编译语言,后面用于构建PDF文件byte[],返回给前端 --><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>17</source><target>17</target></configuration></plugin></plugins></build>
</project>

关于如何生成 PDF 文件,参看前文:

  • Java如何将数据写入到PDF文件

启动,测试,先来看看生成的 PDF 文件,没有影响,看不出“内容信息”前后的截取点文本

在这里插入图片描述

但是,复制空白处,还是可以粘贴出来的

在这里插入图片描述

看看截取图片效果,几乎完美,把“内容信息”部分的文本内容完全截取出来了。

在这里插入图片描述

总结

以上是我自己思考的一种将 PDF 文件中某部分内容截取成图片的方案。

这种方法是可以根据填充的内容多少动态适应,但如果你截取的内容位置是固定的,就更好办了,直接在下面这个截取方法里写死范围

BufferedImage croppedImage = pageImage.getSubimage(imgStartX, imgStartY, width, height);

另外,还需要考虑截取内容跨页的情况,涉及跨页,以上代码可能需要调整。

http://www.dtcms.com/a/532383.html

相关文章:

  • 智慧景区导览小程序
  • 58同城企业网站怎么做的wordpress 评论设计
  • 珠海建站模板怎么做多语言网站
  • 致敬哈耶克,让灯火照亮个人前行的道路
  • 【LeetCode100】--- 96.只出现一次的数字【思维导图+复习回顾】
  • 网络编程Socket套接字
  • 算法基础篇(9)倍增与离散化
  • 搓了一个Deepin15的兼容环境(也支持Deepin20/23)
  • C++ - C++11拓展
  • php 装修网站柳州搜索引擎营销平台
  • Householder变换:线性代数中的镜像反射器
  • 【AI智能体】Coze 打造励志图文智能体应用实战操作详解
  • 网站添加友情链接网络推广营销工具
  • 做网站怎么添加关键词网站开发公司报价单模板
  • 山西太原做企业网站建设的公司松原建设局网站
  • HTTP的get请求和post请求的联系和区别
  • 004-Spring AI Alibaba Chat Memory 功能完整案例
  • 用模板网站做h5宣传页多少钱鄂尔多斯seo
  • 人工智能数学
  • Jenkins 从0基础到有点基础——如何安装
  • 我在高职教STM32(新06)——蜂鸣器鸣响实验
  • STM32实现低功耗管理使用配置知识梳理笔记
  • 8-SpringCloud-服务网关 Gateway-高级特性Predicate
  • 从语法糖到引擎实现——JavaScript Class 完整指南(2025 版)
  • 建设网站导航做电商网站必需知道qc
  • STM32 系统定时器(精准延迟)
  • 千问图像编辑Qwen-image-Edit及Qwen-image-Edit-2509(编辑文字/外观编辑/语义编辑)
  • 静态代理模式
  • 探索有效文章生成的技巧与方法
  • 库的认识和制作