如何利用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
相关产品推荐
相关产品推荐

