pdfjs实现段落高亮

需求:利用pdfjs插件进行pdf预览,并支持对切片数据进行高亮
问题:现有pdfjs插件只支持对关键词搜索高亮、长文本效果不好

效果

介绍

PDF.js 是一个强大的 JavaScript 库,可以在浏览器中渲染 PDF 文件,无需依赖任何第三方插件。它允许开发者在 web 应用中嵌入 PDF 阅读器,提供了一个完整的 PDF 渲染解决方案。PDF.js 是由 Mozilla 开发的,使用纯 JavaScript 实现,使用了 Web Worker 来处理复杂的解析任务【来源:百度】

PDF.js高亮的原理:循环pdf每一页的的文本信息,进行正则匹配,返回关键词在本页的位置以及匹配长度,进行渲染。

  • 处理文本内容 #extractText
  • 循环匹配文本
  • 匹配 #calculateMatch
    1、将关键词转成正则表达式 #convertToRegExpString
    2、正则匹配文本 #calculateRegExpMatch
    3、获取源坐标点、长度 getOriginalIndex
  • 渲染匹配

本文也只是处理了部分匹配规则,因为切片数据与pdfjs插件解析出来的文本数据并不一定能一一匹配,还有各种各样的异常需要处理
更好的高亮方式我认为是后端返回坐标点,前端添加遮罩的方式。
可以参考文章【PDF.js】2023 最新 PDF.js 在 Vue3 中的使用

使用

PDF.js版本:4.3.136
vue:vue3
vue3中引入pdfjs进行文件预览的方式有多种,这里选用部署在服务器上,远程调用传入url的方式进行预览。具体方式可以参考文章:
PDF.js 前端开发使用指南
pdf.js使用全教程(开发笔记)

<iframe id="pdf-view-iframe" :src="pdfUrl" width="100%" height="100%"></iframe>

pdfUrl的组成:插件地址+pdf文件源地址,例如:

http://localhost:8080/pdfjs/web/viewer.html?file=http.xxxxxxx.test.pdf

高亮-修改源码

1、pdfjs插件接收需要高亮的文本
2、调用搜索方法进行查询

传参方式

接收需要高亮的文本有两种方式,一种是在pdf文件地址上拼接参数。如:
1、url传参

http://localhost:8080/pdfjs/web/viewer.html?file=http.xxxxxxx.test.pdf?mark=你好

这里的mark就是需要高亮的文字,同理这里也可以传入page等
2、postMessage

const iframe = document.getElementById('pdf-view-iframe');
  // 高亮文本
 iframe.contentWindow?.postMessage({ type: 'HIGHT_LINE', value: targetText }, '*');

这里如果仅使用方法1,则相同文件切片数据切换的时候都会重新刷新页面,用户体验感不好;
仅使用方法2,如果要实现首次渲染就高亮选中的功能,需要等待页面渲染完成(使用定时器或者其他方式)
两种方式结合可以实现:

  • 首次加载pdf即可高亮文本
  • 切换不同的切片数据可以页面不刷新跳转到高亮页

具体实现

一、使用Iframe载入pdf页面

<template>
  <div class="container">
    <iframe id="pdf-view-iframe" :src="pdfUrl" width="100%" height="100%"></iframe>
  </div>
</template>

二、监听url以及切片文本、进行渲染\高亮

watch(
  () => [props.url, props.markText],
  (newv, oldv) => {
    if ((oldv && newv && newv[0] !== oldv[0]) || !oldv) {
      render();
    } else {
      setHighLight(props.markText);
    }
  },
  { immediate: true, deep: true },
);

以下的props.markText 都是encodeURIComponent之后的字符串

const render = () => {
  let paramsUrl = '';
  if (props.markText) {
    paramsUrl += `&markText=${props.markText}`;
  }
  if (props.page) {
    paramsUrl += `&page=${props.page}`;
  }
  pdfUrl.value = fileUrl + encodeURIComponent(props.url) + paramsUrl;
  }
};
const setHighLight = (targetText) => {
  const iframe = document.getElementById('pdf-view-iframe');
  // 高亮文本
  iframe.contentWindow?.postMessage({ type: 'HIGHT_LINE', value: targetText }, '*');
};

三、修改源码
1、在web目录下新建messageHandler.js文件处理postMessage消息,并在viewer.html文件引入

window.addEventListener('message', function (message) {
	const { data } = message;
	const { type, value } = data;
	switch (type) {
		// 页面切换
		case 'SET_PAGE':
			window.PDFViewerApplication.page = value;
			break;
		// 高亮文本
		case 'HIGHT_LINE':
			const anchor = decodeURIComponent(value);
			anchor && setHightlight(anchor);
			break;
		// 页面高度定位
		case 'SET_TOP':
			document.getElementById('viewerContainer').scrollTop =
				document.getElementById('viewerContainer').scrollTop +
				c_urlArray['top'] * 1;
		default:
			break;
	}
});

2、调用 PDFViewerApplication 的find方法进行高亮匹配,我这里将大段的高亮文本切割成35个字一段的文本

const setHightlight = anchor => {
	const arr = [];
	for (let i = 0; i < anchor.length; i++) {
		if (i % 35 === 0) {
			arr.push(anchor.substring(i, i + 35));
		}
	}
	const last = arr.length - 1;
	if (arr[last].length < 10) {
		arr[last - 1] = arr[last - 1] + arr[last];
		arr.pop();
	}
	window.PDFViewerApplication.eventBus.dispatch('find', {
		query: arr,
		caseSensitive: false,
		highlightAll: true,
		findPrevious: false,
	});
};

3、viewer.mjs 对url上的参数处理,进行高亮匹配 setInitialView函数
源码位置

// 获取url参数
		function getQueryVariable(variable) {
			var query = window.location.search.substring(1);
			var vars = query.split('&');
			for (var i = 0; i < vars.length; i++) {
				var pair = vars[i].split('=');
				if (pair[0] == variable) {
					return pair[1];
				}
			}
			return false;
		}
		// 页数  跳转
		const page = getQueryVariable('page');
		// 关键词检索跳转
		const markText = getQueryVariable('markText');
		if (page) {
			this.pdfViewer.currentPageNumber = Number(page);
		}
		if (markText) {
			const markTextDecode = decodeURIComponent(markText);
			setHighlightAllMessage(markTextDecode);
		}

在messageHandler.js将setHightlight挂载在window上 供viewer.mjs调用

window.setHighlightAllMessage = setHightlight;

4、修改#convertToRegExpString函数,给每个字后面加上匹配任何数量空格
在这里插入图片描述
数字后面也加上
在这里插入图片描述
到这里就可以完成切片数据高亮了,如果切片数据不会分布在不同页的话,这里已经可以很好地匹配到了。
如果需要匹配换页的文本,可以修改源码将原本的:在单页匹配改为在全文本匹配,我这里不想过多修改源代码,所以另外写了高亮函数,在scrollIntoView函数中添加代码进行匹配
自定义函数setHighlightHtml,也是在messageHandler.js函数中,将函数挂在window上
在这里插入图片描述
messageHandler.js还有自定义的一些函数,这里就不细说了
在这里插入图片描述
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值