跳到内容
  • 福昕首页
  • 开发中心
  • SDK文档资料
  • 插件商店
  • 福昕首页
  • 开发中心
  • SDK文档资料
  • 插件商店
申请试用
  • 企业自动化
    • Compressor
  • 福昕CloudAPI
  • 福昕PDF SDK 软件开发工具包
    • 福昕PDF SDK(ActiveX)
    • 福昕PDF SDK(桌面/服务器)
    • 福昕PDF SDK(Plug-in)
    • 福昕 PDF SDK(安卓)
    • 福昕PDF SDK(iOS)
    • 福昕PDF SDK(Web)
  • 福昕管理控制台
    • 公有云
    • 私有云
    • 通用情况
  • 福昕阅读器
    • RMS插件
  • 福昕高级编辑器
    • AI助手
    • Mac版本
      • 常规问题
    • Windows版本
      • 表单
      • ECM集成
      • 互联PDF
      • 企业管理指南
      • 保护
      • 内容编辑
      • 创建PDF
      • 压缩
      • 图章
      • 安装与卸载
      • 常见问题
      • 打印
      • 注释/评论
      • 福昕插件
      • 翻译助手
      • 翻译助手教程
      • 试用与激活
      • 转换
      • 页面管理
    • 教育用户
      • 论文查重
      • 操作指南
    • 网页版
      • Foxit eSign
      • 电子签章
    • 订阅
    • 资源
  • 福昕高级编辑器Linux版本
  • 福船图纸管理系统
  • 福昕PDF SDK 软件开发工具包 > 福昕PDF SDK(Web)
  • 标签:
  • GraphicsObject,PDFPage,图形对象,渲染性能

如何统计 PDF 每页的图形对象数量(含按类型细分)

  • 福昕知识库
  • 2026-11-06

适用产品:福昕 PDF SDK for Web(UIExtension / PDFViewCtrl)| 接口起始版本:7.4.0,本文接口在 11.1 验证

PDF 页面里的图形对象(GraphicsObject)分为文本、路径、图像、渐变着色、Form XObject 五类。在做内容审核、图纸解析、渲染耗时预估、按页分片处理这类需求时,通常需要先知道每一页到底有多少个图形对象。本文给出可直接使用的接口调用方式、按类型细分的两条实现路线,以及在不同文档规模下该怎么选。

一、接口一览

统计相关的方法都在 PDFPage 上,GraphicsObject 提供类型信息。调用前请先确认版本,getGraphicsObjectsCount 自 7.4.0 起提供。

接口 参数 返回值 起始版本 用途
PDFDoc.getPageCount() 无 number(同步) — 取总页数
PDFDoc.getPageByIndex(index) index: number Promise<PDFPage> — 取指定页
PDFPage.getGraphicsObjectsCount() 无 Promise<number> 7.4.0 取本页图形对象总数
PDFPage.getGraphicsObjectByIndex(index) index: number Promise<GraphicsObject> 7.3.0 取第 index 个对象
GraphicsObject.getType() 无 number(Graphics_ObjectType) 7.4.0 取对象类型
PDFPage.getGraphicsObjectsByRect(options) options 见下 Promise<GraphicsObject[]> 8.5.0 按矩形与类型筛选

Graphics_ObjectType 枚举定义在 PDFViewCtrl.PDF.constant.Graphics_ObjectType:

成员 值 含义 按矩形查询支持
All 0 全部类型,仅作过滤用 是
Text 1 文本对象 是
Path 2 路径/矢量图形对象 是
Image 3 图像对象 是
Shading 4 渐变着色对象 是
FormXObject 5 Form XObject 对象 不支持

有一个容易踩的点:getPageCount() 是同步返回值,不需要也不应该 await;getPageByIndex() 才是 Promise。混用容易得到 undefined。

二、统计每页对象总数

只需要数量时,一次 getGraphicsObjectsCount() 就能拿到本页结果,不要通过循环 getGraphicsObjectByIndex() 来计数——后者的调用次数等于该页对象数,在图纸类文档上会慢几个数量级。

最简形式如下,可直接在已初始化 pdfui 的页面控制台执行:

const viewer = await pdfui.getPDFViewer();
const doc = viewer.getCurrentPDFDoc();      // PDFViewer 上同步返回 PDFDoc | null
if (!doc) throw new Error('未打开文档');

const counts = [];
for (let i = 0; i < doc.getPageCount(); i++) {
    const page = await doc.getPageByIndex(i);
    counts.push(await page.getGraphicsObjectsCount());
}
console.log(counts);   // 例如 [12, 5, 0, 31, ...]

要同时拿到页码和数量、便于输出报表时,用下面这段:

async function countPerPage(pdfui) {
    const viewer = await pdfui.getPDFViewer();
    const doc = viewer.getCurrentPDFDoc();
    if (!doc) throw new Error('未打开文档');

    const list = [];
    for (let i = 0; i < doc.getPageCount(); i++) {
        const page = await doc.getPageByIndex(i);
        list.push({ pageNo: i + 1, count: await page.getGraphicsObjectsCount() });
    }
    console.table(list);
    return list;
}

const rows = await countPerPage(pdfui);

list 的结构是 [{ pageNo, count }, ...],页码从 1 开始。这个循环的调用次数是页数的两倍,200 页文档通常在数百毫秒内完成。

关于 pdfui 与 pdfViewer 的取值差异需要注意:PDFUI 类只继承了 PDFViewer 的方法声明,实际把调用委托给 PDFViewer 实例,所以在 pdfui 上调用 getCurrentPDFDoc() 返回的是 Promise,在 pdfViewer 实例上调用则是同步返回 PDFDoc。为了行为一致,建议统一走 await pdfui.getPDFViewer() 拿到实例后再调用。统计本身不依赖页面渲染,文档加载完成即可执行,服务端环境同样支持。

三、按类型细分:两条路线

需要区分文本、路径、图像各有多少时,有两条路线。它们的差异不在代码风格,而在复杂度量级——一条随对象数线性增长,一条是常数。

路线 A:矩形查询(推荐)

思路是把”统计某类型有几个”变成”查询该类型在整页有哪些”,用返回数组的长度作为数量。整页矩形从 getAllBoxes() 取 cropBox(缺失时退回 mediaBox),然后把 isInRect 设为 false,让查询覆盖与整页相交的所有对象,而不是仅完全位于页内的对象。

async function typeBreakdownFast(page) {
    const c = PDFViewCtrl.PDF.constant.Graphics_ObjectType;

    // 取整页矩形;PDFRect 结构为 { left, bottom, right, top },单位 pt,原点在左下角
    const boxes = await page.getAllBoxes();
    const rect = boxes.cropBox || boxes.mediaBox;

    const result = {};
    const types = ['Text', 'Path', 'Image', 'Shading'];
    for (const name of types) {
        const objs = await page.getGraphicsObjectsByRect({
            rect: rect,
            tolerance: 0,
            type: c[name],
            isInRect: false
        });
        result[name] = objs ? objs.length : 0;
    }

    // 该接口不支持 FormXObject,用总数减其余四类推算
    const total = await page.getGraphicsObjectsCount();
    const known = types.reduce((s, k) => s + result[k], 0);
    result.FormXObject = total - known;

    return result;
}

每页固定 5 次调用(4 次类型查询 + 1 次总数),与页面对象数无关。单页 3000 个对象的图纸,这条路线通常在几十毫秒内返回。

getGraphicsObjectsByRect 的 options 字段:

字段 类型 说明
rect PDFRect 查询矩形,PDF 坐标({left, bottom, right, top})
tolerance number 矩形容差,用整页矩形时传 0 即可
type Graphics_ObjectType 目标类型,不支持 FormXObject
isInRect boolean true 只返回完全在矩形内的对象;false 返回与矩形相交的对象。默认 true

isInRect 的默认值是 true,用整页矩形查询时如果保持默认,跨越页面边界(例如因裁切框与媒体框不一致)的对象会被漏掉,导致数量偏小。这是路线 A 最常见的错误来源。

路线 B:逐对象遍历(精确,但慢)

逐个取出对象、读 getType() 再累加。这条路线的结果最精确,FormXObject 也能直接得到真实值而非推算。

async function typeBreakdownSlow(page, total) {
    const c = PDFViewCtrl.PDF.constant.Graphics_ObjectType;
    const nameOf = {};
    for (const k in c) {
        if (typeof c[k] === 'number') nameOf[c[k]] = k;
    }

    const result = { Text: 0, Path: 0, Image: 0, Shading: 0, FormXObject: 0 };
    for (let i = 0; i < total; i++) {
        const obj = await page.getGraphicsObjectByIndex(i);
        if (!obj) continue;
        const name = nameOf[obj.getType()] || ('Unknown(' + obj.getType() + ')');
        result[name] = (result[name] || 0) + 1;
    }
    return result;
}

单个对象的取值是一次跨线程的异步调用,total 为多少就要等多少次。单页 3000 个对象时,这条路线的耗时通常在分钟级。它适合页数不多、或只对少数几页要求精确值的场景。

两条路线的取舍

对比项 路线 A(矩形查询) 路线 B(逐对象)
每页调用次数 固定 5 次 等于该页对象数
单页 3000 对象的量级 数十毫秒 分钟级
Shading 支持 支持
FormXObject 不支持,只能按差值推算 支持,结果为真实值
结果精度 四类精确,FormXObject 为推算值 全部精确
适用场景 大文档全量统计、报表、抽样 少量页面、需精确 FormXObject

一个实用做法是先用路线 A 跑全文档,同时挑对象最少的一页用路线 B 交叉校验,确认两者的四类结果一致后再采信路线 A 的全量数据。如果某页的 FormXObject 推算值为负,说明路线 A 的其余四类查询有遗漏,通常是 isInRect 用了默认值或 rect 取错了框。

四、性能与正确性注意事项

串行遍历,不要一次性并发。 按页串行访问符合 SDK 文档模型的使用方式。把几十页的 getPageByIndex 全部塞进 Promise.all 并不会更快,内部仍要排队,反而更容易触发资源竞争。需要并发时建议控制在 3 到 5 页以内,并实测确认。

统计不需要先渲染页面。 getPageByIndex 配合 getGraphicsObjectsCount 走的是文档模型,与视图无关。不必等页面渲染完成,也不必跳转到目标页。

统计范围要分清。 这里统计的是 PDF 内容流中的图形对象,包含上述五类。它不等于 PDF 编辑器的”内容”面板所展示的条目数——后者按内容流操作符层级组织,两者计算范围不同,不能直接对比。表单域(Widget)属于注释体系,不包含在图形对象里,需要另外用 getAnnotCountAsync() 统计;从 11.1.0 起注释采用懒加载,取总数请用异步版本而非 getAnnotCount()。

长任务要提供进度反馈。 用路线 B 处理多页文档时,务必把进度刷新放在最内层循环内部,并配一个独立于业务逻辑的定时器更新已用时间。只在”整页处理完”后刷新是不够的:某一页对象特别多时,内层长时间不返回,界面会停在上一句提示上,看起来与卡死无异。同时建议在控制台打印每页耗时,便于定位是哪一页拖慢了整体。

getBitmap 不要在统计循环里调用。 它真正触发渲染,开销远高于取类型。

五、常见问题

1. 统计结果和 PDF 编辑器”内容”面板的条目数不一致

两者计算范围不同。本文统计的是内容流中的图形对象,按 Text、Path、Image、Shading、FormXObject 五类划分;编辑器的内容面板展示的是内容流操作符层级。Form XObject 内部的对象是否展开为独立对象,也取决于该 Form 是否被作为独立对象返回。做数量比对时请用同一套接口,不要跨工具比较。

2. 按矩形查询得到的数量小于全页总数

先检查 isInRect 是否用了默认值。默认 true 只返回完全位于矩形内的对象,跨越边界的对象会被排除。整页统计应显式传 isInRect: false。其次确认 rect 取自 cropBox 而非其他框——getAllBoxes() 返回 mediaBox、cropBox、trimBox、artBox、bleedBox、calcBox 六个矩形及最小宽高,页面可见区域对应的是 cropBox。

3. FormXObject 的推算值是负数

说明其余四类的查询结果之和超过了总数,即四类中有遗漏或重复。常见原因是 isInRect 用了默认值,或 rect 传入了错误的框(例如误用 trimBox 而文档实际以 cropBox 显示)。修正 rect 与 isInRect 后重算;如仍有异常,对该页改用路线 B 取真实值。

4. 逐对象统计在长文档上运行很久,控制台也没有输出

这通常是路线选择问题,而非代码缺陷。逐对象统计的调用次数等于总对象数,图纸类文档单页可达数千个对象。判断某一页的预期耗时,可以只取该页前 50 个对象计时后按比例推算:

async function probeSlowPage(page, total, sample) {
    const n = Math.min(sample, total);
    const t0 = performance.now();
    for (let i = 0; i < n; i++) {
        await page.getGraphicsObjectByIndex(i);
    }
    const dt = performance.now() - t0;
    return { objects: total, sample: n, sampleMs: dt, estimateSec: (dt / n * total) / 1000 };
}

推算结果显示该页需要数十秒以上时,改用路线 A。

5. 在控制台执行统计脚本后没有任何反应

按以下顺序排查。

现象 原因 处理
只回显函数定义或 undefined,无表格输出 脚本只定义了函数,尚未调用 显式调用该函数并 await
无输出且页面无变化 使用了逐对象统计,长文档耗时过长 先只统计总数,或改用路线 A
报 xxx is not a function 或读到 null 在 pdfui 上直接调了 getCurrentPDFDoc(),拿到的是 Promise 先 await pdfui.getPDFViewer() 再取文档
报 Identifier 'xxx' has already been declared 同一段含 const/let 的代码粘贴了第二次 刷新页面,或用立即执行函数包裹
无表格但无报错 控制台过滤级别设为 Errors 或 Warnings,console.table 属 Info 级 切回全部级别

6. 只想统计某个区域内的对象数量

用 getGraphicsObjectsByRect 指定 rect 为该区域,取返回数组长度。若使用 7.4.0 至 8.4.0 之间提供的 getGraphicsObjectsInRect(rect, tolerance, type),注意该接口同时不支持 Shading 与 FormXObject,覆盖面比 getGraphicsObjectsByRect 更窄。

六、参考

  • 接口签名与返回值:Foxit PDF SDK for Web API Reference 中的 PDFPage、GraphicsObject、Constants 类目,见 https://devdocs.fuxinsoft.cn/resources/pdf-sdk-web/api_reference/html/index.html
  • 矩形结构 PDFRect 的字段定义与坐标系约定,见上述 API Reference 中 PDFRect 的说明

相关内容

扫描件又歪又灰?福昕黑白去底+纠偏矫正教程来啦!

如何在「编辑」菜单工具栏中添加自定义按钮

PDF如何转为PDF/A?

无文件时进入 PDF 操作页:previewFile 必填 content 的三种应对方案

UIExtension 功能按钮隐藏与禁用最佳实践

播放多媒体条目时不使用QuickTime播放器

如何自动检测PDF文档中的URL并创建链接

如何在AI助手中接入自己的模型

如何在AI助手中接入技能

如何通过权限控制有选择的运行JS脚本

推荐内容

如何统计 PDF 每页的图形对象数量(含按类型细分)

扫描件又歪又灰?福昕黑白去底+纠偏矫正教程来啦!

如何在「编辑」菜单工具栏中添加自定义按钮

PDF如何转为PDF/A?

无文件时进入 PDF 操作页:previewFile 必填 content 的三种应对方案

UIExtension 功能按钮隐藏与禁用最佳实践

播放多媒体条目时不使用QuickTime播放器

如何自动检测PDF文档中的URL并创建链接

如何在AI助手中接入自己的模型

如何在AI助手中接入技能

产品
  • 应用行业
  • 白皮书
开发支持
  • 开发中心
  • SDK文档资料

销售咨询:010-50951668

客服电话:0591-38509808

销售咨询
微信公众号

©2026 福建福昕软件开发股份有限公司 版权所有

隐私策略