You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pdf-lib与pdf-dist解析PDF遇文本提取错误及分页偏移问题

PDF解析问题与解决思路

问题描述

  • 分页定位偏差:FireFox、Edge等商业PDF阅读器中,某表格两行内容显示在第23页底部,但使用pdf-lib读取时,这两行被定位到第24页顶部;
  • 文本内容错误:商业阅读器中某表格单元格显示文本为“5,1”,但使用pdfjs读取对应区域时得到的是“20,1”。

解析流程

  1. 解析PDF的绘制动作,获取水平和垂直线条;
  2. 将线条组合成表格,划分页面为逻辑单元格;
  3. 读取并组合单元格内的文本元素,生成单元格字符串。

该逻辑已验证基本正确,第一个问题因不影响数据组装可接受,但第二个问题无法容忍。

相关代码

加载PDF

import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.mjs'; 
const ...
export default async function loadPDF() {
    const data = new Uint8Array(fs.readFileSync(pdfPath));
    const pdf = await pdfjsLib.getDocument({
                data,
                cMapUrl: mapPath,
                cMapPacked: true
    }).promise;
    return pdf;
}

获取文本元素

export async function getTextElements(page, debug = false) {
    let { items } = await page.getTextContent();
    let textElements = items
        .filter(t => !isWatermark(t))  // 通过transform判断水印
        .map(({ str, width, height, transform }) => ({
            str, width, height,
            left: transform[4],
            right: transform[4] + width,
            bottom: transform[5],
            top: transform[5] + height
         }));
    if (debug) {
       fs.writeFileSync("debug.json",JSON.stringify(items)); 
    }
    return removePageNumer(textElements); // 通过位置移除页码
}

排查情况

添加调试代码导出原始文本到debug.json后,发现对应区域的原始内容就是“20,1”,怀疑getTextContent返回了错误的元素。


解决思路

针对文本内容错误的排查方向

  1. 检查PDF文本层级覆盖逻辑
    PDF通过绘制顺序控制显示优先级,后绘制的内容会覆盖先绘制的内容。如果“5,1”是后绘制的遮罩层覆盖在“20,1”之上,getTextContent会返回所有文本(包括被覆盖的),但阅读器只显示上层内容:

    • 遍历getTextContent返回的items,查看元素顺序(对应PDF绘制顺序),判断是否存在后绘制的短文本覆盖长文本的情况;
    • 检查文本元素的opacity属性,确认是否有透明或隐藏的文本。
  2. 尝试PDF.js的底层文本提取方式

    • 使用page.getOperatorList()获取原始绘制指令,手动解析文本操作(如Tj、TJ指令),直接获取所有原始文本及绘制参数:
      const opList = await page.getOperatorList();
      const textItems = [];
      let currentText = '';
      for (const op of opList.ops) {
        if (op.fn === pdfjsLib.OPS.showText) {
          currentText += pdfjsLib.util.decodeText(op.args[0]);
        } else if (op.fn === pdfjsLib.OPS.showTextWithSpace) {
          currentText += ' ' + pdfjsLib.util.decodeText(op.args[0]);
        } else if (op.fn === pdfjsLib.OPS.beginText) {
          currentText = '';
        } else if (op.fn === pdfjsLib.OPS.endText) {
          if (currentText) textItems.push(currentText);
        }
      }
      
    • 在getDocument时添加disableCombineTextItems: true配置,禁止合并相邻文本元素,获取更细粒度的文本片段:
      const pdf = await pdfjsLib.getDocument({
        data,
        cMapUrl: mapPath,
        cMapPacked: true,
        disableCombineTextItems: true
      }).promise;
      
  3. 检查字体与编码映射
    即使配置了cMap,特殊字体或自定义编码仍可能导致解析错误:

    • 在getTextContent时添加includeMarkedContent: true选项,查看标记内容是否影响文本解析;
    • 检查对应文本元素的fontName,确认是否存在字体映射错误导致字符解析偏差。

针对分页偏差的补充说明

该问题多因PDF的“可见页面边界”与“逻辑页面边界”不一致导致,部分PDF会将超出可见区域的内容放在下一页逻辑区域,但阅读器会自动调整显示。若不影响数据组装,可通过调整单元格区域的分页判断逻辑(如允许跨小范围边界的文本归为同一页)优化。

内容的提问来源于stack exchange,提问作者Willi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:13:18