You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何在上传的PDF文档中高亮匹配的清洗后引用字符串?

如何在上传的PDF文档中高亮匹配的清洗后引用字符串?

看起来你已经搞定了脏引用数据的清洗和拆分工作,接下来要做的就是在上传的PDF里定位这些干净的引用并高亮它们对吧?我来给你梳理下在Next.js项目里实现这个功能的具体步骤,结合你现有的代码逻辑来落地:


第一步:选对PDF处理工具,适配Next.js环境

前端处理PDF最常用的就是pdfjs-dist库,不过因为Next.js有服务端渲染的特性,咱们得把PDF相关的逻辑放在客户端组件里(用'use client'指令标记),避免服务端报错。先安装依赖:

npm install pdfjs-dist

第二步:加载PDF并提取带位置信息的文本

要实现高亮,光拿PDF的纯文本不够,得知道每个文本片段在PDF页面上的坐标(x、y、宽度、高度),这样才能准确叠加高亮层。咱们可以用pdfjs-distgetTextContent()方法,它会返回每个文本块的详细位置信息:

'use client';
import { useEffect, useRef, useState } from 'react';
import * as pdfjsLib from 'pdfjs-dist';
import 'pdfjs-dist/build/pdf.worker.entry';

// 配置pdfjs的worker路径
pdfjsLib.GlobalWorkerOptions.workerSrc = 'pdf.worker.entry.js';

// 直接复用你已有的清洗函数!保证PDF文本和引用的清洗规则一致,大幅提升匹配精度
const cleanCitationText = (text: string): string => {
  return text
    .replace(/\\n|\\r|\\t|\n|\r|\t/g, " ")
    .replace(/\\\\/g, " ")
    .replace(/\\/g, " ")
    .replace(/:-/g, "")
    .replace(/\s+/g, " ")
    .trim();
};

export default function CitationHighlighter({ citations }: { citations: string[] }) {
  const pdfContainerRef = useRef<HTMLDivElement>(null);
  const [pdfPages, setPdfPages] = useState<Array<{ canvas: HTMLCanvasElement; highlights: Array<{x: number, y: number, width: number, height: number}> }>>([]);

  // 处理用户上传的PDF文件
  const handlePdfUpload = async (file: File) => {
    if (!pdfContainerRef.current) return;
    
    const arrayBuffer = await file.arrayBuffer();
    const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
    const pagesData: typeof pdfPages = [];

    // 遍历PDF的每一页
    for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
      const page = await pdf.getPage(pageNum);
      const viewport = page.getViewport({ scale: 1.5 }); // 缩放比例,兼顾清晰度和渲染速度
      
      // 创建canvas用于渲染PDF页面
      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');
      if (!context) continue;
      canvas.width = viewport.width;
      canvas.height = viewport.height;
      await page.render({ canvasContext: context, viewport }).promise;

      // 提取当前页面的所有文本块(带位置信息)
      const textContent = await page.getTextContent();
      const pageHighlights: Array<{x: number, y: number, width: number, height: number}> = [];

      // 把PDF文本块按你的规则清洗,同时保留原位置信息
      const pageTextBlocks = textContent.items.map(item => ({
        cleanedText: cleanCitationText(item.str),
        originalText: item.str,
        x: item.transform[4],
        y: viewport.height - item.transform[5], // 转换PDF坐标系(PDF默认y轴向上,和网页坐标系相反)
        width: item.width * viewport.scale,
        height: item.height * viewport.scale
      }));

      // 匹配咱们的清洗后引用
      citations.forEach(targetCitation => {
        const cleanedTarget = cleanCitationText(targetCitation);
        // 这里用包含匹配+忽略大小写,你也可以根据需求改成正则精准匹配
        pageTextBlocks.forEach(block => {
          if (block.cleanedText.toLowerCase().includes(cleanedTarget.toLowerCase())) {
            pageHighlights.push({
              x: block.x,
              y: block.y,
              width: block.width,
              height: block.height
            });
          }
          // 进阶优化:如果引用跨多个文本块,需要把相邻的匹配块合并成一个完整的高亮区域
        });
      });

      pagesData.push({ canvas, highlights: pageHighlights });
    }

    setPdfPages(pagesData);
  };

  return (
    <div>
      <input type="file" accept=".pdf" onChange={(e) => e.target.files && handlePdfUpload(e.target.files[0])} />
      <div ref={pdfContainerRef} className="pdf-container">
        {pdfPages.map((page, idx) => (
          <div key={idx} style={{ position: 'relative', marginBottom: '20px' }}>
            <canvas 
              style={{ border: '1px solid #eee' }} 
              ref={el => {
                if (el) {
                  el.width = page.canvas.width;
                  el.height = page.canvas.height;
                  el.getContext('2d')?.drawImage(page.canvas, 0, 0);
                }
              }} 
            />
            {/* 叠加高亮层,用半透明黄色标记匹配区域 */}
            {page.highlights.map((highlight, hIdx) => (
              <div
                key={hIdx}
                style={{
                  position: 'absolute',
                  left: `${highlight.x}px`,
                  top: `${highlight.y}px`,
                  width: `${highlight.width}px`,
                  height: `${highlight.height}px`,
                  backgroundColor: 'rgba(255, 255, 0, 0.3)',
                  pointerEvents: 'none' // 不干扰PDF的点击、缩放等交互
                }}
              />
            ))}
          </div>
        ))}
      </div>
    </div>
  );
}

第三步:优化匹配逻辑,提升准确率

实际场景中可能遇到一些小问题,咱们可以针对性优化:

  1. 跨文本块的引用:如果一个完整的引用被PDF拆成了多个零散文本块,可以先把页面的文本块按顺序拼接成段落,匹配后再反向定位对应的文本块位置。
  2. 模糊匹配容错:比如用正则忽略多余空格、大小写差异:new RegExp(cleanedTarget.replace(/\s+/g, '\\s+'), 'i')
  3. 过滤无效匹配:复用你parseCitationArray里的过滤规则,避免匹配到短文本或纯数字内容。

第四步:处理边界情况

  • 大文件PDF加载慢:记得加loading状态提示用户。
  • 跨页引用:在高亮时标注对应的页码,方便用户定位。
  • 匹配精度问题:如果PDF里的引用和清洗后的文本差异较大,可以在匹配前对双方做更彻底的归一化(比如去掉所有标点、统一全半角)。

内容来源于stack exchange

火山引擎 最新活动