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

如何利用pdf.js获取PDF中特定单词/字符的边界?(Dart互操作场景)

如何通过pdf.js获取PDF行内单词/字符的边界值

首先要明确:PdfJsPage.getTextContent() 返回的 TextContent 对象里,items数组的每个元素其实是单个字形(字符)的详细信息,而非整行——你当前获取的“行偏移、宽高”应该是对这些字符项按行聚合后的结果。要拿到字符/单词的边界,直接从这些字符项入手即可。


1. 提取单个字符的边界框

每个 TextItem(items数组的元素)包含以下关键属性:

  • transform: 6元素数组,格式为 [scaleX, skewX, skewY, scaleY, translateX, translateY],其中 translateX/translateY 是字符左下角的坐标,scaleX/scaleY 对应缩放系数。
  • width: 字符的实际宽度(已考虑缩放)。
  • height: 字符的实际高度(已考虑缩放)。

通过这些属性可以直接计算字符的边界矩形(x1, y1 为左下角坐标,x2, y2 为右上角坐标):

  • x1 = translateX
  • y1 = translateY
  • x2 = translateX + width
  • y2 = translateY + height

Dart互操作示例代码

import 'dart:js';
import 'dart:js_util' show promiseToFuture;

Future<void> getCharBounds(dynamic page) async {
  final textContent = await promiseToFuture<JsObject>(page.callMethod('getTextContent'));
  final items = textContent['items'] as JsArray;

  for (final item in items) {
    final transform = item['transform'] as JsArray;
    final translateX = transform[4] as double;
    final translateY = transform[5] as double;
    final charWidth = item['width'] as double;
    final charHeight = item['height'] as double;

    final x1 = translateX;
    final y1 = translateY;
    final x2 = translateX + charWidth;
    final y2 = translateY + charHeight;

    print('字符 "${item['str']}" 的边界: ($x1, $y1) -> ($x2, $y2)');
  }
}

2. 合并字符为单词并计算单词边界

如果需要单词级别的边界,可通过以下规则判断字符归属:

  • 同一行:相邻字符的transform[5](y坐标)差值在1像素以内,视为同一行。
  • 间距阈值:相邻字符的间距(下一个字符的x1减去上一个字符的x2)小于上一个字符宽度的1/4,视为同一单词。

单词边界计算示例

import 'dart:js';
import 'dart:js_util' show promiseToFuture;

Future<List<Map<String, dynamic>>> getWordBounds(dynamic page) async {
  final textContent = await promiseToFuture<JsObject>(page.callMethod('getTextContent'));
  final items = textContent['items'] as JsArray;
  final words = <Map<String, dynamic>>[];
  if (items.isEmpty) return words;

  // 初始化第一个单词
  var currentWord = {
    'text': items[0]['str'] as String,
    'x1': items[0]['transform'][4] as double,
    'y1': items[0]['transform'][5] as double,
    'x2': (items[0]['transform'][4] as double) + (items[0]['width'] as double),
    'y2': (items[0]['transform'][5] as double) + (items[0]['height'] as double),
  };

  for (var i = 1; i < items.length; i++) {
    final currentItem = items[i];
    final prevItem = items[i-1];
    final prevX2 = (prevItem['transform'][4] as double) + (prevItem['width'] as double);
    final currentX1 = currentItem['transform'][4] as double;
    final sameLine = (currentItem['transform'][5] as double).abs() - (prevItem['transform'][5] as double).abs() < 1;
    final gap = currentX1 - prevX2;
    final isSameWord = sameLine && gap < (prevItem['width'] as double) * 0.25;

    if (isSameWord) {
      // 合并到当前单词
      currentWord['text'] += currentItem['str'] as String;
      currentWord['x2'] = currentX1 + (currentItem['width'] as double);
      currentWord['y1'] = (currentWord['y1'] as double).min(currentItem['transform'][5] as double);
      currentWord['y2'] = (currentWord['y2'] as double).max((currentItem['transform'][5] as double) + (currentItem['height'] as double));
    } else {
      // 保存当前单词,开始新单词
      words.add(currentWord);
      currentWord = {
        'text': currentItem['str'] as String,
        'x1': currentX1,
        'y1': currentItem['transform'][5] as double,
        'x2': currentX1 + (currentItem['width'] as double),
        'y2': (currentItem['transform'][5] as double) + (currentItem['height'] as double),
      };
    }
  }
  // 添加最后一个单词
  words.add(currentWord);
  return words;
}

注意事项

  • PDF坐标系统原点通常在页面左下角,y值越大位置越靠上。
  • 如果PDF页面存在旋转,需先通过page.getRotation()获取旋转参数,调整边界坐标。
  • 部分PDF包含连字(如fi、fl),这类字符会作为单个TextItem存在,可根据业务需求决定是否拆分。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 22:48:10