如何在PDF中查找指定字符串的绝对坐标(PHP+TCPDF/FPDI)
如何用TCPDF/FPDI定位PDF中唯一字符串的坐标
核心思路
由于TCPDF/FPDI没有直接提供文本坐标查询API,我们可以通过解析PDF页面的底层指令,拦截包含目标字符串的文本渲染操作,提取其相对于页面左上角的坐标。同时利用你允许替换字符串的条件,辅助验证坐标的准确性。
实现步骤
1. 自定义FPDI子类,拦截PDF指令
创建一个继承自FPDI的子类,重写页面内容解析方法,追踪文本操作和当前坐标:
class FPDICoordTracker extends FPDI { private $targetString; private $foundCoords = []; private $currentX = 0; private $currentY = 0; public function setTargetString(string $str) { $this->targetString = $str; } public function getFoundCoords() { return $this->foundCoords; } protected function _parsePageContent($content) { parent::_parsePageContent($content); $tokens = preg_split('/\s+/', trim($content)); $i = 0; $tokenCount = count($tokens); while ($i < $tokenCount) { $token = $tokens[$i]; // 追踪文本位置指令:Td(直接设置位置) if ($token === 'Td') { $x = (float)$tokens[$i-2]; $y = (float)$tokens[$i-1]; // 转换PDF默认的左下角原点为左上角原点 $this->currentX = $x; $this->currentY = $this->h - $y; $i += 1; } // 追踪文本矩阵指令:Tm(包含平移的矩阵) elseif ($token === 'Tm') { $e = (float)$tokens[$i-2]; $f = (float)$tokens[$i-1]; $this->currentX = $e; $this->currentY = $this->h - $f; $i += 1; } // 处理单个字符串显示指令:Tj elseif ($token === 'Tj') { $str = trim($tokens[$i-1], '()'); $str = preg_replace('/\\\(.)/', '$1', $str); if ($str === $this->targetString) { $this->foundCoords[] = [ 'x' => $this->currentX, 'y' => $this->currentY, 'page' => $this->page ]; } $i += 1; } // 处理字符串数组显示指令:TJ elseif ($token === 'TJ') { $j = $i - 1; $tjContent = ''; while ($j >= 0 && $tokens[$j] !== '[') { $part = trim($tokens[$j], '()'); if (is_numeric($part)) { $j--; continue; } $tjContent = preg_replace('/\\\(.)/', '$1', $part) . $tjContent; $j--; } if (strpos($tjContent, $this->targetString) !== false) { $this->foundCoords[] = [ 'x' => $this->currentX, 'y' => $this->currentY, 'page' => $this->page ]; } $i += 1; } else { $i += 1; } } } }
2. 使用子类加载PDF并获取坐标
// 初始化自定义追踪类 $pdf = new FPDICoordTracker(); $pdf->setTargetString('你的唯一目标字符串'); // 加载目标PDF文件 $pageCount = $pdf->setSourceFile('target.pdf'); // 遍历所有页面解析内容 for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { $tplIdx = $pdf->importPage($pageNo); $pdf->AddPage(); $pdf->useTemplate($tplIdx); } // 获取定位结果 $coords = $pdf->getFoundCoords(); // 输出结果 if (!empty($coords)) { foreach ($coords as $coord) { echo "页面{$coord['page']},坐标:X={$coord['x']}pt,Y={$coord['y']}pt\n"; } } else { echo "未找到目标字符串\n"; }
3. 替换字符串验证坐标(可选)
如果需要确认坐标准确性,可以在对应位置绘制替换后的字符串:
// 遍历坐标替换字符串 foreach ($coords as $coord) { $pdf->setPage($coord['page']); // 用白色矩形覆盖原字符串(可选) $pdf->SetFillColor(255, 255, 255); $pdf->Rect($coord['x'], $coord['y'] - 5, $pdf->GetStringWidth('替换后的字符串'), 10, 'F'); // 绘制新字符串 $pdf->SetFont('helvetica', '', 12); $pdf->SetTextColor(255, 0, 0); $pdf->Text($coord['x'], $coord['y'], '替换后的字符串'); } // 输出修改后的PDF $pdf->Output('modified.pdf', 'F');
注意事项
- PDF默认以左下角为原点,代码中通过
$this->h - $y转换为左上角为原点的Y坐标,$this->h为当前页面高度(单位:pt)。 - 部分复杂PDF可能使用特殊文本矩阵或字符间距调整,若遇到匹配问题,可微调指令解析逻辑。
- 确保目标字符串唯一,避免返回多组坐标。
内容的提问来源于stack exchange,提问作者jeremysawesome
相关产品推荐
相关产品推荐

