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

2023-11-19

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。具体原因未知,但是我经过多次测试,结果是准确的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • JavaMail Gmail 问题。 “准备启动 TLS”然后失败

    mailServerProperties System getProperties mailServerProperties put mail smtp port 587 mailServerProperties put mail smtp
  • 如何在 JFace 的 TableViewer 中创建复选框?

    我创建了一个包含两列的 tableViewer 我想将其中一列设为复选框 为此 我创建了一个 CheckBoxCellEditor 但我不知道为什么它不起作用 名为 tableName 的列显示其值正常 色谱柱规格如下 String COL
  • ElasticBeanstalk Java,Spring 活动配置文件

    我正在尝试通过 AWS ElasticBeanstalk 启动 spring boot jar 一切正常 配置文件为 默认 有谁知道如何为 java ElasticBeanstalk 应用程序 不是 tomcat 设置活动配置文件 spri
  • 线程自动利用多个CPU核心?

    假设我的应用程序运行 2 个线程 例如渲染线程和游戏更新线程 如果它在具有多核 CPU 当今典型 的移动设备上运行 我是否可以期望线程在可能的情况下自动分配给不同的核心 我知道底层操作系统内核 Android linux内核 决定调度 我的
  • 支持 >65k 行的 Excel VBA SQL 驱动程序

    在 Excel 2010 中通过 VBA 查询 Excel 数据时 我遇到一个有趣的问题 我正在使用这些驱动程序连接到 xls 或 xls x m 文件 Sub OpenCon ByRef theConn As Connection ByV
  • 解决错误:日志已在具有多个实例的atomikos中使用

    我仅在使用atomikos的实时服务器上遇到问题 在我的本地服务器上它工作得很好 我在服务器上面临的问题是 init 中出错 日志已在使用中 完整的异常堆栈跟踪 java lang RuntimeException Log already
  • 在浏览器中点击应用程序时播放框架挂起

    我正在 Play 中运行一个应用程序activator run 也许 5 次中有 3 次 它会挂起 当我去http localhost 9000 它就永远坐在那里旋转 我看到很多promise timed out错误也 我应该去哪里寻找这个
  • Java 页面爬行和解析之 Crawler4j 与 Jsoup

    我想获取页面的内容并提取其中的特定部分 据我所知 此类任务至少有两种解决方案 爬虫4j https github com yasserg crawler4j and Jsoup http jsoup org 它们都能够检索页面的内容并提取其
  • 使用替换字符串中多个单词的最有效方法[重复]

    这个问题在这里已经有答案了 此刻我正在做 Example line replaceAll replaceAll cat dog replaceAll football rugby 我觉得那很丑 不确定有更好的方法吗 也许循环遍历哈希图 ED
  • Prim 的迷宫生成算法:获取相邻单元格

    我基于 Prim 算法编写了一个迷宫生成器程序 该算法是 Prim 算法的随机版本 从充满墙壁的网格开始 选择一个单元格 将其标记为迷宫的一部分 将单元格的墙壁添加到墙壁列表中 While there are walls in the li
  • 如何将文件透明地传输到浏览器?

    受控环境 IE8 IIS 7 ColdFusion 当从 IE 发出指向媒体文件 例如 mp3 mpeg 等 的 GET 请求时 浏览器将启动关联的应用程序 Window Media Player 我猜测 IIS 提供文件的方式允许应用程序
  • 检查 protobuf 消息 - 如何按名称获取字段值?

    我似乎无法找到一种方法来验证 protobuf 消息中字段的值 而无需显式调用其 getter 我看到周围的例子使用Descriptors FieldDescriptor实例到达消息映射内部 但它们要么基于迭代器 要么由字段号驱动 一旦我有
  • Java中未绑定通配符泛型的用途和要点是什么?

    我不明白未绑定通配符泛型有什么用 具有上限的绑定通配符泛型 stuff for Object item stuff System out println item Since PrintStream println 可以处理所有引用类型 通
  • 使用 SAX 进行 XML 解析 |如何处理特殊字符?

    我们有一个 JAVA 应用程序 可以从 SAP 系统中提取数据 解析数据并呈现给用户 使用 SAP JCo 连接器提取数据 最近我们抛出了一个异常 org xml sax SAXParseException 字符引用 是无效的 XML 字符
  • Windows 上的 Nifi 命令

    在我当前的项目中 我一直在Windows操作系统上使用apache nifi 我已经提取了nifi 0 7 0 bin zip文件输入C 现在 当我跑步时 bin run nifi bat as 管理员我在命令行上看到以下消息 但无法运行
  • 将2-3-4树转换为红黑树

    我正在尝试将 2 3 4 树转换为 java 中的红黑树 但我无法弄清楚它 我将这两个基本类编写如下 以使问题简单明了 但不知道从这里到哪里去 public class TwoThreeFour
  • 如何修复“sessionFactory”或“hibernateTemplate”是必需的问题

    我正在使用 Spring Boot JPA WEB 和 MYSQL 创建我的 Web 应用程序 它总是说 sessionFactory or hibernateTemplate是必需的 我该如何修复它 我已经尝试过的东西 删除了本地 Mav
  • java迭代器内部是如何工作的? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我有一个员工列表 List
  • KeyPressed 和 KeyTyped 混淆[重复]

    这个问题在这里已经有答案了 我搜索过之间的区别KeyPressedand KeyTyped事件 但我仍然不清楚 我发现的一件事是 Keypressed 比 KeyTyped 首先被触发 请澄清一下这些事件何时被准确触发 哪个适合用于哪个目的
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个

随机推荐

  • obsidian上手使用

    前言 我之前一直使用的是typora typora付费后就没有使用过了 后面就一直找markdown编辑器 在一次网课中我发现了老师用的网页富文本编辑器非常不错 这样我认识了语雀 语雀的文本编辑体验确实非常的不错 还可以在笔记里添加思维导图
  • 使用 SpringBoot 访问 MySQL 数据库

    一 目标 创建一个 MySQL 数据库 构建一个 Spring 应用程序 并将其连接到新创建的数据库 二 准备工作 1 最喜欢的文本编辑器或 IDE 2 Java 17或更高版本 3 Gradle 7 5 或Maven 3 5 三 初始化项
  • 使用Python制作酷炫的二维码

    参考 https www cnblogs com zhuwjwh p 11401312 html 制作动图二维码只需要原始图像是动图就可以哦 制作动图可以参考我之前的博客
  • 量化编程环境python库安装包

    必装工具包 1 ccxt conda没有 2 pandas 3 pathos 并发计算 conda没有 4 websocket 获取实时数据 conda没有 5 ntplib 用于时间校对 conda没有 6 cryptography 加密
  • Qt中 gui 模块和 widgets 模块的区别

    作者 billy 版权声明 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 简述 在Qt5下 QWidget系列从QtGui中被剥离出去 成为单独的QtWidget模块 随着Qt Quick2的引入 QtDeclarat
  • 【JavaScript】js继承

    es6 extends继承 原型式继承 构造函数继承 组合式继承 一 es6 extends继承 scala复制代码 class Child extends Parent constructor super this name 张三 let
  • Java课程设计之学习成绩管理系统

    package System import java awt import java awt event import java io import javax swing import javax swing table Abstract
  • JavaScript的slice()方法最详解

    JavaScript slice 方法怎么用 最详尽最易懂的讲解 1 JavaScript slice 方法的定义 slice 方法可提取字符串的某个部分 并以新的字符串返回被提取的部分 2 1 JavaScript slice 方法的语法
  • 基于LLMs的多模态大模型(Flamingo, BLIP-2,KOSMOS-1,ScienceQA)

    前一篇博客已经整理了不训练视觉模型的文章们 基于LLMs的多模态大模型 Visual ChatGPT PICa MM REACT MAGIC 本篇文章将介绍一些需要训练视觉编码器来适配多模态大模型的工作们 这也是目前最为流行的研究思路 其实
  • 2022 如何在 GitHub 上搭建个人网站(github.io)

    前言 目前 想要搭建自己的项目演示 又想要免费的服务 这时就可以使用 GitHub 提供的免费Pages服务 github io 前置 有GitHub账号 熟练使用git版本管理 成果展示 步骤 第一步 新建仓库 在GitHub上 创建一个
  • 下一步准备给自研引擎换皮

    抄完osg osgearth 鬼火引擎最低版本 以及ogre的大部分后 看到消息处理都通过窗口 突然顿悟 引擎都是类似的 下一步给自研引擎换皮 全换成ogre的皮 渲染流程类似osg的 多线程渲染 无非就是在哪个线程wglMakeCurre
  • 云原生热门话题|什么是可观测性-Observability

    code杂坛 关注一线大厂 互联网时讯 各技术栈 产品 开源社区 等最新讯息 1 可观测性引入 可观测性 术语源于几十年前的控制理论 在许多实际问题中 控制系统的状态变量不是由直接测量得到的 而是通过某种观测方法得到的 由某种观测系统所得到
  • 深大自考本科所需课程

    自考本科需要通过课程 网络经济与企业管理 数据结构导论 运筹学基础 信息资源管理 中国近代史纲要 计算机原理 马克思主义基本原理概论 数据库系统原理 管理经济学 软件开发工具 C 程序设计 英语2 操作系统概论 管理信息系统 计算机网络原理
  • 【OpenCV学习笔记】【编程实例】五 (霍夫圆检测)

    GetCircle cpp 定义控制台应用程序的入口点 圆形检测代码demo 载入数张包含各种形状的图片 检测出其中的圆形 include cv h include highgui h include
  • 排队论(Queuing Theory)

    目录 简介 一 基本概念 1 1 排队过程的一般表示 1 2 排队系统的组成和特征 1 2 1 输入过程 1 2 2 排队规则 1 2 3 服务过程 1 3 排队模型的符号表示 1 4 排队系统的运行指标 二 输入过程与服务时间的分布 2
  • 【MySQL】—— 在windows下的MySQL安装与配置

    更新日志 2020 11 13 文章发布 说明 本文地址 MySQL 在windows下的MySQL安装与配置 https blog csdn net maixiaochai article details 109676520 关于 Mai
  • 2FSK调制解调实验

    一 2FSK原理 频移键控是利用载波的频率变化来传递数字信息 数字频率调制是数据通信中使用较 早的一种通信方式 由于这种调制解调方式容易实现 抗噪声和抗衰减性能较强 因此在 中低速数字通信系统中得到了较为广泛的应用 在2FSK中 载波的频率
  • 树与二叉树(二叉树的表示,性质,遍历,还原)

    1 基本术语 A 或B 是I的祖先 I是A 或B 的子孙 D是I的双亲 I是D的孩子 节点的孩子个数称为节点的度 树中节点的最大度数称为树的度 度大于0的节点称为分支节点 度等于0的节点称为叶节点 定义树根为第一层 则 树的深度 高度 为5
  • 数据库开发三:JDBC数据库开发入门三(PrepareStatement的使用及预处理原理)

    目录 一PrepareStatement使用 二预处理原理 文章相关视频出处 https developer aliyun com lesson 1694 13598 spm 5176 10731542 0 0 4a023fdbjxoV5w
  • Java准确获取Word/Excel/PPT/PDF的页数(附Word页数读不准的处理办法)

    Java准确获取Word Excel PPT PDF的页数 附Word页数读不准的处理办法 1 需求背景 2 环境准备工作 2 1 JACOB介绍及安装 2 2 Microsoft Office Word的设置 3 代码 3 1 代码示例