【记录版】JAVA压缩PDF中的图片

JAVA + Itextpdf + thumbnailator + 图片压缩

背景: 项目开发中,如果需要处理图片数据,那么压缩步骤必不可少。一方面压缩后存储空间降低,IO、内存等资源消耗也同步减少。在Java开发中,使用thumbnailator压缩图片是一个常规用法,本文仅记录PDF中图片数据压缩成字节数组的场景。

博客内容精选:
1、Servlet请求体重复读&修改新姿势
2、根据请求获取后端接口详情
3、封装Springboot项目的starter-sdk新方式
4、Springboot全局处理完整版
5、itextpdf读取文本时上下行位置错乱
6、JAVA读取PDF表格内容
7、JAVA读取PDF出现内容错乱

具体步骤:
1、通过Itextpdf工具读取PDF,获取图片对象

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.3</version>
</dependency>
// 首先自定义读取监听器类,将图片数据保存下来
public class PdfContentReader implements RenderListener {
    private final List<ImageRenderInfo> renderImages = new ArrayList<>();

    @Override
    public void beginTextBlock() {
    }

    @Override
    public void renderText(TextRenderInfo textRenderInfo) {
    }

    @Override
    public void endTextBlock() {

    }

	//注意:PDF图片对象为ImageRenderInfo,不是BufferedImage,但可进一步转换
    @Override
    public void renderImage(ImageRenderInfo imageRenderInfo) {
        renderImages.add(imageRenderInfo);
    }
}
// 入口类,通过PdfReader对象和监听器对象,将图片数据抽取出来
PdfReader reader = new PdfReader(localFile.getAbsolutePath());
int pageNum = reader.getNumberOfPages();
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
PdfContentReader listener = new PdfContentReader();
for (int page = 1; page <= pageNum; page++) {
    parser.processContent(page, listener);
}
reader.close();

2、逐个压缩ImageRenderInfo图片数据

	 /**
     * @param requestId 请求ID
     * @param pageNum 页序号
     * @param renderImage PDF图片对象
     * @return 压缩后的字节数组
     * @throws ExtractException 异常
     */
    public static byte[] compressionRenderImage(String requestId, int pageNum, ImageRenderInfo renderImage) throws ExtractException {
        PdfImageObject imageObject = null;
        try {
        	// 此处返回图片对象,特殊PDF可能为null
            imageObject = renderImage.getImage();
            byte[] initData = imageObject.getImageAsBytes();
            // 仅考虑2M以上图片压缩,根据自身业务场景设置
            return initData.length > 2097152 ? compressionBufferImage(requestId, pageNum, initData.length, imageObject.getBufferedImage()) : initData;
        } catch (Throwable e) {
            log.warn("Request[{}] compress render image failed, type: {}, reason: {}", requestId, e.getClass().getSimpleName(), e.getMessage());
            if (imageObject == null) {
                throw new ExtractException("PDF图片获取图片对象为空");
            }
            throw new ExtractException("PDF图片压缩失败");
        }
    }

    /**
     * @param requestId 请求ID
     * @param pageNum 页序号
     * @param imageSize 图片大小
     * @param bufferedImage 图片对象
     * @return 压缩后的字节数组
     * @throws ExtractException 异常
     */
    public static byte[] compressionBufferImage(String requestId, int pageNum, int imageSize, BufferedImage bufferedImage) throws ExtractException {
        int compressCounter = 0;
        long startTime = System.currentTimeMillis();
        // 使用内存字节数据中转,压缩次数尽量少点
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            int width = bufferedImage.getWidth() / 2;
            int height = bufferedImage.getHeight() / 2;
            do {
            	// 重复使用记得reset下流对象
                out.reset();
                // 此处outputFormat设置格式为jpg,如果设置png,字节数明显变大
                Thumbnails.of(bufferedImage).scalingMode(ScalingMode.BICUBIC).size(width, height).outputQuality(QUALITY).outputFormat("jpg").toOutputStream(out);
                width *= QUALITY;
                height *= QUALITY;
                compressCounter++;
            } while (out.size() > 2097152);
            float durationTime = (System.currentTimeMillis() - startTime) / 1000f;
            // 涉及到重要操作,日志记录要详细点
            log.warn("Request[{}] compress {}th pdf image from {}KB to {}KB after {} times and total cost {}s", requestId, pageNum, imageSize / 1024, out.size() / 1024, compressCounter, durationTime);
            return out.toByteArray();
        } catch (IOException e) {
            throw new ExtractException("BufferedImage字节数据获取失败");
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                log.warn("close compression image byte stream failed");
            }
        }
    }
}

结语:以上内容就是pdf中图片数据的压缩过程了,仅个人项目需求,不具有代表性。虽然简单,其实里面也有几个问题需要注意,如果处理不好,内存、异常问题都不会少。

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用 iText 库来实现 Java PDF 图片压缩。具体实现步骤如下: 1. 引入 iText 库到项目。 2. 读取 PDF 文件图片并获取图片对象。 ``` PdfReader reader = new PdfReader("path/to/pdf"); PdfDictionary page = reader.getPageN(1); PdfDictionary resources = page.getAsDict(PdfName.RESOURCES); PdfDictionary xobjects = resources.getAsDict(PdfName.XOBJECT); PdfName imgName = xobjects.getKeys().iterator().next(); PdfStream imgStream = (PdfStream) xobjects.getAsStream(imgName); byte[] imgBytes = PdfReader.getStreamBytesRaw((PRStream) imgStream); Image img = Image.getInstance(imgBytes); ``` 3. 设置图片压缩质量,并将图片重新写入 PDF 文件。 ``` ByteArrayOutputStream out = new ByteArrayOutputStream(); BufferedImage bi = ImageIO.read(new ByteArrayInputStream(imgBytes)); Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); ImageWriter writer = writers.next(); ImageWriteParam param = writer.getDefaultWriteParam(); param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(0.5f); // 设置图片压缩质量,值越小压缩比越高 writer.setOutput(new MemoryCacheImageOutputStream(out)); writer.write(null, new IIOImage(bi, null, null), param); writer.dispose(); imgBytes = out.toByteArray(); img = Image.getInstance(imgBytes); PdfStamper stamper = new PdfStamper(reader, new FileOutputStream("path/to/output/pdf")); PdfContentByte contentByte = stamper.getOverContent(1); contentByte.addImage(img); stamper.close(); reader.close(); ``` 以上代码只是一个简单的示例,实际使用时可能需要根据具体情况进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值