Java准确获取Word/Excel/PPT/PDF的页数(附Word页数读不准的处理办法)

1.需求背景

前两天接了个小需求,就是用Java准确地判断出Word(.doc和.docx)、Excel(.xls和.xlsx)、PPT(.ppt和.pptx)还有PDF的页数。开始觉得很简单,想都没想就用了Apache的POI,但后来发现个大问题:
Apache POI对于.doc(Office Word 1997- 2003的版本)后缀的word文件,页数读取不准!
度娘了一下,发现大家普遍都有这个问题,解决办法也是五花八门,有用POI的,有用JACOB将Word转PDF的,还有用第三方工具比如PageOffice/OpenOffice的,但是感觉都不是特别简洁。所以结合这些博客的经验及我自己的摸索,我终于找到了较为简单准确的解决方式,下面完整地分享给大家。

2.环境准备工作

2.1 JACOB介绍及安装

我的思路还是使用JACOB来操作Word,因为对于Office Word 97-03这种远古版本,除了软件自身提供的宏,似乎没有什么能对它进行直接的操作,而在Windows平台为了解决这种软件缺乏通用API的问题,推出了COM的解决方案。
而想通过Java来操作DOM,我们就需要一个JACOB(Java-COM Bridge)这样的桥梁。JACOB 开源项目提供的是一个 JVM 独立的自动化服务器实现,其核心是基于 JNI 技术实现的 Variant, Dispatch 等接口,从而调用Windows的COM(Component Object Model组对象模型)。
JACOB下载地址为:https://sourceforge.net/projects/jacob-project/
如下图:
在这里插入图片描述
点击Download,然后解压,将其中的jacob-1.19-x64.dll复制到System32文件夹下,如下图:
在这里插入图片描述

2.2 Microsoft Office Word的设置

因为是调用的Word Application自身,所以环境里必须得安装有Office Word,这个就不赘述。
Word里需要做如下两项设置,如下图:
在这里插入图片描述
在这里插入图片描述
原因是如果使用高版本的Office Word打开.doc文件,默认是预览视图,而不是编辑视图,这个预览视图会影响后面要介绍的方法的判断准确性,如下图:
在这里插入图片描述

3.代码

3.1 代码示例

3.1.1 pom.xml

<dependencies>
        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna-platform</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.8</version>
        </dependency>
        <!--用于操作Office系列-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.8</version>
        </dependency>
        <!--用于操作Word-->
        <!-- https://mvnrepository.com/artifact/net.sf.jacob-project/jacob -->
        <dependency>
            <groupId>net.sf.jacob-project</groupId>
            <artifactId>jacob</artifactId>
            <version>1.14.3</version>
        </dependency>
        <!--用于操作PDF-->
        <!-- https://mvnrepository.com/artifact/com.lowagie/itext -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.0.6</version>
        </dependency>
    </dependencies>

3.1.2 主要功能实现

package com.docreader;

import com.itextpdf.text.pdf.PdfReader;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.hslf.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.SlideShow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xslf.usermodel.XMLSlideShow;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import java.io.FileInputStream;
import java.io.IOException;


public class reader {

    /**
     * description: 静态方法,用于判断文件类型,并返回页数
     * @param filePath 文件完整路径
     */
    public static int getFilePageNum(String filePath) throws IOException {
        int pageNum = 0;
        String lowerFilePath = filePath.toLowerCase();
        if (lowerFilePath.endsWith(".xls")) {
            HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(lowerFilePath));
            Integer sheetNums = workbook.getNumberOfSheets();
            if (sheetNums > 0) {
                pageNum = workbook.getSheetAt(0).getRowBreaks().length + 1;
            }
        } else if (lowerFilePath.endsWith(".xlsx")) {
            XSSFWorkbook xwb = new XSSFWorkbook(lowerFilePath);
            Integer sheetNums = xwb.getNumberOfSheets();
            if (sheetNums > 0) {
                pageNum = xwb.getSheetAt(0).getRowBreaks().length + 1;
            }
        } else if (lowerFilePath.endsWith(".docx")) {
            XWPFDocument docx = new XWPFDocument(POIXMLDocument.openPackage(lowerFilePath));
            pageNum = docx.getProperties().getExtendedProperties().getUnderlyingProperties().getPages();
        } else if (lowerFilePath.endsWith(".doc")) {
            //下方的方法不好使,经常只统计出一页
//            HWPFDocument wordDoc = new HWPFDocument(new FileInputStream(lowerFilePath));
//            pageNum = wordDoc.getSummaryInformation().getPageCount();
            //采用如下方法
            pageNum = getDocPageNum(lowerFilePath);
        } else if (lowerFilePath.endsWith(".ppt")) {
            HSLFSlideShow document = new HSLFSlideShow(new FileInputStream(lowerFilePath));
            SlideShow slideShow = new SlideShow(document);
            pageNum = slideShow.getSlides().length;
        } else if (lowerFilePath.endsWith(".pptx")) {
            XMLSlideShow xslideShow = new XMLSlideShow(new FileInputStream(lowerFilePath));
            pageNum = xslideShow.getSlides().length + 1;
        } else if (lowerFilePath.endsWith(".pdf")){
            PdfReader reader = new PdfReader(filePath);
            pageNum = reader.getNumberOfPages();
        }
        return pageNum;
    }


    /**
     * description: 静态方法,专门用于判断Office 2003版本之前的Word(格式为.doc)的页数
     * @param filePath 文件完整路径
     */
    private static int getDocPageNum(String filePath) {
        int pageNum = 0;
        try{
            // 建立ActiveX部件
            ActiveXComponent wordCom = new ActiveXComponent("Word.Application");
            //word应用程序不可见
            wordCom.setProperty("Visible", false);
            // 返回wrdCom.Documents的Dispatch
            Dispatch wrdDocs = wordCom.getProperty("Documents").toDispatch();//Documents表示word的所有文档窗口(word是多文档应用程序)

            // 调用wrdCom.Documents.Open方法打开指定的word文档,返回wordDoc
            Dispatch wordDoc = Dispatch.call(wrdDocs, "Open", filePath, false, true, false).toDispatch();
            Dispatch selection = Dispatch.get(wordCom, "Selection").toDispatch();
            pageNum = Integer.parseInt(Dispatch.call(selection,"information",4).toString());//总页数 //显示修订内容的最终状态

            //关闭文档且不保存
            Dispatch.call(wordDoc, "Close", new Variant(false));
            //退出进程对象
            wordCom.invoke("Quit", new Variant[] {});
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pageNum;
    }
}

3.1.3 Main方法

import com.docreader.reader;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;


public class Main {

    public static void main(String[] args) {

        try{
            int xlsNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\5 pages.xls");
            System.out.println("Office2003之前版本的Excel:5 pages.xls 的页数为:" + xlsNum);

            int xlsxNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\8 pages.xlsx");
            System.out.println("Office2003之后版本的Excel:8 pages.xlsx 的页数为:" + xlsxNum);

            int docNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\30 pages.doc");
            System.out.println("Office2003之前版本的Word:30 pages.doc 的页数为:" + docNum);

            int docxNum =  reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\133 pages.docx");
            System.out.println("Office2003之后版本的Word:133 pages.docx 的页数为:" + docxNum);

            int pptNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\35 pages.ppt");
            System.out.println("Office2003之前版本的PPT:35 pages.ppt 的页数为:" + pptNum);

            int pptxNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\95 pages.pptx");
            System.out.println("Office2003之后版本的PPT:95 pages.pptx 的页数为:" + pptxNum);

            int pdfNum = reader.getFilePageNum("E:\\workspace\\DocumentReader\\src\\main\\resources\\30 pages.pdf");
            System.out.println("PDF:30 pages.pdf 的页数为:" + pdfNum);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.1.4 运行结果

在这里插入图片描述
在这里插入图片描述

3.2 注意事项

3.2.1 获取Excel页数的注意事项

if (lowerFilePath.endsWith(".xls")) {
            HSSFWorkbook workbook = new HSSFWorkbook(new FileInputStream(lowerFilePath));
            Integer sheetNums = workbook.getNumberOfSheets();
            if (sheetNums > 0) {
                pageNum = workbook.getSheetAt(0).getRowBreaks().length + 1;
            }
        } else if (lowerFilePath.endsWith(".xlsx")) {
            XSSFWorkbook xwb = new XSSFWorkbook(lowerFilePath);
            Integer sheetNums = xwb.getNumberOfSheets();
            if (sheetNums > 0) {
                pageNum = xwb.getSheetAt(0).getRowBreaks().length + 1;
            }
        }

仔细看代码,这里的:

getRowBreaks().length + 1

指的是Excel中的分页符的数量,如下图:
在这里插入图片描述
如果Excel中插入了7个分页符,那么总共可打印的页数就是7+1 = 8页,如下图:
在这里插入图片描述
注意,我看有的教程里,页数 = workbook.getNumberOfSheets(),这就错得太离谱了,这是sheet的数量,根本不是页数。

3.2.2 获取Word页数的注意事项

/**
     * description: 静态方法,专门用于判断Office 2003版本之前的Word(格式为.doc)的页数
     * @param filePath 文件完整路径
     */
    private static int getDocPageNum(String filePath) {
        int pageNum = 0;
        try{
            // 建立ActiveX部件
            ActiveXComponent wordCom = new ActiveXComponent("Word.Application");
            //word应用程序不可见
            wordCom.setProperty("Visible", false);
            // 返回wrdCom.Documents的Dispatch
            Dispatch wrdDocs = wordCom.getProperty("Documents").toDispatch();//Documents表示word的所有文档窗口(word是多文档应用程序)

            // 调用wrdCom.Documents.Open方法打开指定的word文档,返回wordDoc
            Dispatch wordDoc = Dispatch.call(wrdDocs, "Open", filePath, false, true, false).toDispatch();
            Dispatch selection = Dispatch.get(wordCom, "Selection").toDispatch();
            pageNum = Integer.parseInt(Dispatch.call(selection,"information",4).toString());//总页数 //显示修订内容的最终状态

            //关闭文档且不保存
            Dispatch.call(wordDoc, "Close", new Variant(false));
            //退出进程对象
            wordCom.invoke("Quit", new Variant[] {});
        } catch (Exception e) {
            e.printStackTrace();
        }
        return pageNum;
    }

这里的:

//word应用程序不可见
wordCom.setProperty("Visible", false);

如果设置成true,则会打开可见的Word应用,并打开指定的Word文档,对于调试时会有很大帮助。

3.2.3 获取PPT页数的注意事项

else if (lowerFilePath.endsWith(".pptx")) {
            XMLSlideShow xslideShow = new XMLSlideShow(new FileInputStream(lowerFilePath));
            pageNum = xslideShow.getSlides().length + 1;
        }

仔细看代码,这里需要+1。具体原因未知,但是我经过多次测试,结果是准确的。

发布了42 篇原创文章 · 获赞 15 · 访问量 9803

猜你喜欢

转载自blog.csdn.net/weixin_40326608/article/details/100115014