学习笔记(Java篇):Java操作文件之PDF(1)

1、前言

        开发过程中遇到的问题:将一个PDF文件进行拆分,例如该文件有20页,需要将其拆分成20个文件单独存放每一页(感觉很离谱的需求),亦或是将指定某些页单独取出来。又或者说是需要将PDF文件进行转储图片文件,因为前端读取图片要比读取文件展示的效率高一些。所以本篇将介绍Java操作PDF将其进行拆分或者转储图片。

2、简述思路

        1、首先将传入的文件转换成流,转换成流之后,可以使用apache的pdfbox中的PDDocument对象加载这个流从而使其变成一个可作为PDF操作的文件对象。

        2、PDDocument对象中提供了很多个可供使用的API,我们可以直接调用其中的API进行操作PDF文件。

        3、然后PDDocument还可以被转换为另一个对象PDFRenderer,此类可以将PDF文件渲染成图片的格式。

3、PDF拆分 

        在操作PDF之前需要得到对应的PDF对象,无论我们得到的是文件连接还是File对象,都必须先将其转换为流的形式。

//通过文件连接转化成流
InputStream inputStream = OkHttpUtil.downloadFile(pdfUrl);
//通过File对象转化成为流
File file = new File(filePath);//文件路径
InputStream inputStream = new FileInputStream(file);
//将文件流转化成可操作的PDDocument对象
PDDocument document = PDDocument.load(inputStream);

然后就可以直接操作这个PDDocument对象对其进行拆分。在PDDocument对象提供的API中,可以直接获取到PDF文件的页码信息。

下面简单列举一些此处用到的方法,其余未用到的暂时不列举,可参考源码:

1、document.getPage(int pageIndex);获取第N页,返回一个PDPage对象。

2、document.getPages();获取所有页,返回一个PDPageTree包含所有页数据的对象。

3、document.getNumberOfPages();底层还是document.getPages().getCount();获取该文件总共有多少页。

4、document.addPage(PDPage page);为当前PDF添加一页(尾部追加)。

5、document.save(OutputStream os);将该PDF作为输出流进行保存。初次之外还支持传入File对象与文件名的字符串类型,这些是用作保存为文件类型。

6、document.close();在操作完毕之后执行关闭Document对象(下面再介绍为何必须执行)。

上代码:

public static void main(String[] args) {
        String inputFilePath = "path/to/input.pdf";
        String outputDirectory = "path/to/output/directory";

        try {
            //加载文件使之成为流
            InputStream inputStream = OkHttpUtil.downloadFile(pdfUrl);
            //转化为PDDocument对象
            PDDocument document = PDDocument.load(inputStream);
            //获取总页数
            int pageCount = document.getNumberOfPages();
            //循环分别将每一页输出到指定路径
            for (int i = 0; i < pageCount; i++) {
                
                PDPage page = document.getPage(i);

                PDDocument newDocument = new PDDocument();
                newDocument.addPage(page);
                //拼出文件名(包含本地路径)
                String outputFilePath = outputDirectory + File.separator + "page_" + (i + 1) + ".pdf";
                newDocument.save(outputFilePath);
                newDocument.close();
            }
            document.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这样就完成了文件拆分输出,如若想要按照指定条件拆分,例如1-10页作为一个文件输出,11-20合并输出等,只需要改变循环条件,并按照需要对newDocument进行addPage();即可。

4、转储图片

        PDF文件可能在前端加载起来需要较长的时间,但是图片的效率会更高,所以有时候会需要将PDF文件转化为图片格式进行输出。

        这就牵涉到另一个对象:PDFRenderer.这个类支持将PDF渲染成图片的形式。简单介绍一下用到的方法:

1、renderer.renderImage(int pageIndex, float scale);此方法将执行页码的PDF渲染成图片,scale指的是渲染图片的缩放比例,可以不传,不传默认为1是原始大小,1=72DPI

2、renderer.renderImageWithDPI(int pageIndex, float dpi, ImageType type);功能同上,可指定DPI。ImageType用于指定图片渲染方式,不传默认为RGB(意为red,green,blue红绿蓝三原色渲染)。

PDFRenderer renderer = new PDFRenderer(document);
BufferedImage image = renderer.renderImage(page, scale);
//将图片写到指定路径
ImageIO.write(image, "jpg", new File(path));

像这样将其渲染成指定图片之后,就可以对其进行保存等操作。

5、图片拼接

        关于BufferedImage,在此做一下简单的介绍:这是一个Image的实现类,可以将图片加载到内存,通过此类,可以对图像进行一些简单的操作。

        创建对象的时候,我们可以直接指定图像的宽高以及渲染方式,例如:

BufferedImage resultImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);

然后此篇主要介绍将图片拼接到一起的方式,这需要用到另外一个类Graphics2D,它提供对几何图形、坐标转换、颜色管理和文本布局的更复杂的控制。这是在Java(tm)平台上渲染二维形状、文本和图像的基本类。

先上代码:

/**
     * 拼接图片(垂直拼接,image在上,bufferedImage在下)
     * @param image
     * @param bufferedImage
     * @return
     */
    public static BufferedImage concatenateImagesVertically(BufferedImage image, BufferedImage bufferedImage) {
        //为适应图片,需要将拼接的两张图片的最大宽高取出
        //垂直拼接,方式为高度取二者之和,宽取二者中最大值
        //水平拼接,方式为宽度取二者之和,高取二者中最大值
        //以下为垂直拼接
        int totalHeight = image.getHeight() + bufferedImage.getHeight();
        int maxWidth = Math.max(image.getWidth(), bufferedImage.getWidth());
        //首先创建一个指定大小的空对象
        BufferedImage resultImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
        //然后将该对象转换为可被操作的Graphics2D对象
        Graphics2D g2d = resultImage.createGraphics();
        //向该对象中拼入第一张图片
        g2d.drawImage(image, 0, 0, null);
        //向该对象拼入第二章图片
        g2d.drawImage(bufferedImage, 0, image.getHeight(), null);
        //最后处理该图像,此操作会释放占用的资源,之后将无法继续操作Graphics2D对象
        g2d.dispose();

        return resultImage;
    }

以上方法实现了将两个BufferedImage对象拼合到一起的过程,最后返回一个经过拼接的BufferedImage对象。

6、写在最后

        本文主要介绍了将一个PDF文件进行拆分并转储图片的流程,可以在一定程度上解决页面加载文件效率的问题。以上所用内容基本都在Java提供的API里面,详细研究可以参考Java源码。

附流程源码(传入文件下载连接,输出图片连接):

public String pdfDecompose(String pdfUrl) {
        try {
            InputStream inputStream = OkHttpUtil.downloadFile(pdfUrl);
            PDDocument document = PDDocument.load(inputStream);
            int pageCount = document.getNumberOfPages();
            BufferedImage bufferedImage = new BufferedImage(0,0, 1);
            for (int i = 0; i < pageCount; i++) {
                PDDocument newDocument = new PDDocument();
                PDPage page = document.getPage(i);
                newDocument.addPage(page);
                //pdf转储图片文件
                PDFRenderer renderer = new PDFRenderer(newDocument);
                int numberOfPages = newDocument.getNumberOfPages();
                BufferedImage image = renderer.renderImage(numberOfPages, 1);
                concatenateImagesVertically(bufferedImage,image);
                newDocument.close();
            }
            document.close();
            //TODO 上传返回
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 拼接图片(垂直拼接,image在上,bufferedImage在下)
     * @param image
     * @param bufferedImage
     * @return
     */
    public static BufferedImage concatenateImagesVertically(BufferedImage image, BufferedImage bufferedImage) {
        int totalHeight = image.getHeight() + bufferedImage.getHeight();
        int maxWidth = Math.max(image.getWidth(), bufferedImage.getWidth());

        BufferedImage resultImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2d = resultImage.createGraphics();

        g2d.drawImage(image, 0, 0, null);
        g2d.drawImage(bufferedImage, 0, image.getHeight(), null);

        g2d.dispose();

        return resultImage;
    }

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值