React 项目使用 pdf.js 及 Elasticpdf 教程

news2025/4/8 20:38:17

摘要:本文章介绍如何在 React 中使用 pdf.js 及基于 pdf.js 的批注开发包 Elasticpdf。简单 5 步可完成集成部署,包括数据的云端同步,示例代码完善且简单,文末有集成代码分享。
快照.png

1. 工具库介绍与 Demo

1.1 代码包结构

ElasticPDF基于开源 pdf.js (Demo地址:https://mozilla.github.io/pdf.js/web/viewer.html),增加了多种开箱即用的 PDF 批注功能。代码包延续了 pdf.js-dist 独立且完全离线的结构风格,仅增加了用于支持批注的离线 Javascript 代码,与 pdf.js-dist 一样可以快速完美集成到任何可以运行Javascript, HTML, CSS 的项目环境中,在公网及内网环境下运行。
项目结构-中文.png

1.2 Elasticpdf 在线 Demo

根据不同的功能及预算需求,有两个版本的产品可供选择,两者仅在最终的批注保存阶段有区别,产品 Demo 地址如下:

① 批注合成版: https://demos.libertynlp.com/#/pdfjs-annotation

② 专业批注版: https://www.elasticpdf.com/demo

2. 移动至 React 项目

移动 pdf.js 或 Elasticpdf 代码包到 React 项目的 public 文件夹下。
package-position.png

pdf.js 成功导入 React 快照
pdfjs-快照.png

3. 导入 viewer.html

① 通过 <iframe> 导入 elasticpdf 或 pdf.js 代码包中的 viewer.html 文件,注意路径不要写错。

<!-- elasticpdf 示例 -->
<iframe id='elasticpdf-iframe' onLoad={initialPDFEditor} 
  src='elasticpdf/web/viewer.html' style={{width:"100%",border:0,height:"700px"}}></iframe>

<!-- pdf.js 示例 -->
<iframe id='elasticpdf-iframe' onLoad={initialPDFEditor} 
  src='pdfjs-3.2/web/viewer.html' style={{width:"100%",border:0,height:"700px"}}></iframe>

② 将 web 文件夹下 viewer.jsdefaultUrl 默认值置空,否则在第 ① 步中导入 viewer.html 时会默认加载 compressed.tracemonkey-pldi-09.pdf 文件,影响自定义加载文件的流程。Elasticpdf 代码包中的 viewer.js 已默认修改完成。

// 原 defaultUrl 默认值
defaultOptions.defaultUrl = {
  value: "compressed.tracemonkey-pldi-09.pdf",
  kind: OptionKind.VIEWER
};

// 置空后默认值
defaultOptions.defaultUrl = {
  value: "",
  kind: OptionKind.VIEWER
};

③ 在 React 页面中的 <iframe> onLoad() 函数下调用 initialApp() 函数,由于 pdf.js 和 elasticpdf 中的函数都是在 iframe 的作用域下,因此必须在 iframe load结束后再初始化,否则无法获取 iframe 的contentWindow,也就无法调用其中的函数。

var elasticpdf_viewer = null;
function initialPDFEditor() {
	listenPDFEditorMessage();
	elasticpdf_viewer = document.getElementById('elasticpdf-iframe').contentWindow;
	console.log('elasticpdf_viewer', elasticpdf_viewer);
	var pdf_url="compressed.tracemonkey-pldi-09.pdf";
	elasticpdf_viewer.initialApp({
		'language': 'zh-cn', // 交互语言
		'pdf_url': pdf_url,
		'member_info': { //用户信息
			'id': 'elasticpdf_id',
			'name': 'elasticpdf',
		},
	});
}
	
// 监听 pdf 编辑等各种信息的回调
function listenPDFEditorMessage() {
	window.addEventListener('message', (e) => {
		if (e.data.source !== 'elasticpdf') {
			return;
		}

		// pdf 加载结束的回调,可以在此处导入服务器上储存的批注文件
		if (e.data.function_name === 'pdfLoaded') {
			console.log('PDF加载成功');
			reloadData();
		}
	});
}

④ pdf.js 初始化函数如下,主要内容为调用 PDFViewerApplication.open() 打开传入的文档链接,并使用 loadPdf() 函数监听文档是否初始化结束,最后通过 postMessage 广播加载状态至 React 页面。

<script type='text/javascript'>
	//初始化函数
	function initialApp(paras) {
		var oriUrl=paras['pdf_url'];
		PDFViewerApplication.open(oriUrl);
		interval = setInterval('loadPdf()', 1000);
	}
	
	//监听文档是否初始化完成
	var interval = null;
	function loadPdf() {
		if (PDFViewerApplication.pdfDocument == null) {
			console.info('Loading...');
		} else {
			//文档初始化完成
			console.log('PDF Load successfully');
			clearInterval(interval);
			//广播信息
			postPDFData("pdfLoaded", '');
		}
	}
	
	//广播 pdf.js 操作状态信息
	function postPDFData(function_name,new_content){
		window.parent.postMessage({"type":0,"source":"elasticpdf",'function_name':function_name,"content":new_content},'*');
		window.postMessage({"type":0,"source":"elasticpdf",'function_name':function_name,"content":new_content},'*');
	}
</script>

⑤ 需要注意的是 pdf.js 端和存放 pdf 文件的都要支持跨域,否则会报 CORS 跨域错误。具体来说如果服务器是通过 Java 或者 Python 等程序提供文档,则需要在程序中允许跨域;而如果是 nginx 服务器,则在配置中可以如下设置。

location / {
	add_header Access-Control-Allow-Origin *;
	add_header Access-Control-Allow-Headers *;
	add_header Access-Control-Expose-Headers  Accept-Ranges,Content-Range;
	add_header Accept-Ranges bytes;
}

对于 pdf.js 端的跨域,需要在 elasticpdf 或 pdf.js 的 viewer.js 中搜索 HOSTED_VIEWER_ORIGINS 并加入域名。

const HOSTED_VIEWER_ORIGINS = ["null", "http://mozilla.github.io", "https://mozilla.github.io"];

4. 导出 pdf 及批注数据

Elasticpdf 所生成批注数据的保存有两种方式,我们推荐方式二。pdf.js 默认将批注写入文档,无法分离保存。

4.1 方式一:批注写入PDF

将批注写入到 pdf 中然后下载整个文档,一般用户可以通过Ctrl+S快捷键和 UI 按钮来完成,这种方式完全不需要后端服务的支持。

在需要保存批注后 pdf 至服务器的场景中,可以通过如下代码实现。

// 绑定该函数至 dom 用于触发 pdf 保存
function getPDFData() {
	elasticpdf_viewer.getPDFData();
}

// 接收pdf数据并且上传至服务器
window.addEventListener('message', (e) => {
	if (e.data.source != 'elasticpdf') {
		return;
	}

	// 接收pdf数据
	if (e.data.function_name == 'downloadPDF') {
		let file_name = e.data.content['file_name'];
		let pdf_blob = e.data.content['pdf_blob'];
		let pdf_base64 = e.data.content['pdf_base64'];
		
		// 接收到 pdf 数据,其中 pdf_base64 字符串数据可以快捷上传到服务器
		postService('upload-pdf-data', {
			'file_name':file_name,
			'file_id':'123ddasfsdffads',
			'file_data':pdf_base64,
		});
	}
});

4.1 方式二:批注单独保存

针对云端同步批注的需求,单独将批注文件导出为JSON文件,传输并保存于服务器,之后加载回显后可继续编辑批注。

这样的方式仅需一个在线PDF原文件,只传输很小体积的批注(通常不到 1M 大小),可以节约很多的存储和宽带费用。

// 在 pdf 批注编辑后的回调函数中可以读取所有批注文件并且上传至服务器
window.addEventListener('message', (e) => {
	if (e.data.source != 'elasticpdf') {
		return;
	}

	// pdf 批注编辑回调,可以在此处导出批注并传输到服务器
	if (e.data.function_name == 'annotationsModified') {
		// 仅获取 pdf 批注文件,不写入到 pdf 中
		let this_data = elasticpdf_viewer.pdfAnnotation.outputAnnotations();
		let annotation_content = JSON.stringify(this_data['file_annotation']);
		let file_name = this_data['file_name'];
		postService('upload-annotation-data', {
			'file_name':file_name,
			'file_id':'123ddasfsdffads',
			'file_annotation':annotation_content,
		});
	}
});

5. 重载 pdf 及批注数据

单独将 pdf 批注保存至服务器后,可以在加载 pdf 文件后再次从服务器中下载批注并且重载回显到 pdf 上继续编辑。

// 在 pdf 加载完成后的回调中可以从服务器请求相应的批注并重载于 pdf 上。
window.addEventListener('message', (e) => {
	if (e.data.source != 'elasticpdf') {
		return;
	}

	// pdf 加载完成的回调,可以在此处导入服务器上储存的批注文件
	if (e.data.function_name == 'pdfLoaded') {
		let file_name = 'tutorial.pdf'
		let annotation_content =await postService('get-annotation-data', {
			'file_name':'tutorial.pdf',
			'file_id':'123ddasfsdffads',
		});
		// 批注重载回显于当前文件
		elasticpdf_viewer.setPureFileAnnotation({
			'file_annotation': annotation_content
		});
	}
});

以上的所有与服务器的交互需要前后端协同,后端服务器需要响应程序来接收和保存数据,对于 Elasticpdf 的用户我们有简单的 PHP、Python 及 Java 代码示例供参考。

前端发起请求的示例函数 postService() 代码如下。


// 与后端服务器进行网络通信的函数
async function postService(url, data) {
	var new_data = new URLSearchParams();
	var encrpte_data = data;
	new_data.append('data', encrpte_data);
	
	var base_url = "your-server-url";
	var posturl = base_url + url;
	const response = await fetch(posturl, {
		method: 'POST',
		headers: {},
		body: new_data, 
	});

	
	const resp = await response.json();
	resp['data'] = JSON.parse(resp['data']);
	
	return resp;
}

总结

至此,pdf.js 及 elasticpdf 集成于 React 项目的代码完毕,带有 pdf.js 代码包的 React 示例项目包内容已上传至 Github(网址:https://github.com/ElasticPDF/React-use-pdf.js-elasticpdf),可以直接下载。Elasticpdf 客户如有其他应用场景需求欢迎联系我们,我们将为您提供示例代码。

温馨提示:本文首发于 https://www.elasticpdf.com ,转载请注明出处:https://www.elasticpdf.com/blog/react-use-pdfjs-and-elasticpdf-tutorial-zh.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2329466.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

性能测试之jmeter的基本使用

简介 Jmeter是Apache的开源项目&#xff0c;基于Java开发&#xff0c;主要用于进行压力测试。 优点&#xff1a;开源免费、支持多协议、轻量级、功能强大 官网&#xff1a;https://jmeter.apache.org/index.html 安装 安装步骤&#xff1a; 下载&#xff1a;进入jmeter的…

CAD插件实现:所有文字显示到列表、缩放、编辑——CAD-c#二次开发

当图中有大量文字&#xff0c;需要全部显示到一个列表时并缩放到需要的文字时&#xff0c;可采用插件实现&#xff0c;效果如下&#xff1a; 附部分代码如下&#xff1a; private void BtnSelectText_Click(object sender, EventArgs e){var doc Application.DocumentManager.…

Oracle数据库数据编程SQL<8 文本编辑器Notepad++和UltraEdit(UE)对比>

首先&#xff0c;用户界面方面。Notepad是开源的&#xff0c;界面看起来比较简洁&#xff0c;可能更适合喜欢轻量级工具的用户。而UltraEdit作为商业软件&#xff0c;界面可能更现代化&#xff0c;功能布局更复杂一些。不过&#xff0c;UltraEdit支持更多的主题和自定义选项&am…

Linux驱动开发练习案例

1 开发目标 1.1 架构图 操作系统&#xff1a;基于Linux5.10.10源码和STM32MP157开发板&#xff0c;完成tf-a(FSBL)、u-boot(SSBL)、uImage、dtbs的裁剪&#xff1b; 驱动层&#xff1a;为每个外设配置DTS并且单独封装外设驱动模块。其中电压ADC测试&#xff0c;采用linux内核…

Apache httpclient okhttp(1)

学习链接 Apache httpclient & okhttp&#xff08;1&#xff09; Apache httpclient & okhttp&#xff08;2&#xff09; httpcomponents-client github apache httpclient文档 apache httpclient文档详细使用 log4j日志官方文档 【Java基础】- HttpURLConnection…

微信小程序—路由

关于 app.json 中的配置 app.json 主要是对整个小程序进行一个全局的配置。 pages&#xff1a;在这个配置项目中&#xff0c;就可以配置小程序里面的页面&#xff0c;小程序默认显示 pages 数组中的第一个页面windows&#xff1a;主要配置和导航栏相关的 当然&#xff0c;在…

人工智能驱动的数据仓库优化:现状、挑战与未来趋势

1. 引言&#xff1a;数据仓库的演进与人工智能驱动优化的兴起 现代数据仓库的复杂性和规模正以前所未有的速度增长&#xff0c;这主要是由于数据量、种类和产生速度的急剧增加所致。传统的数据仓库技术在应对这些现代数据需求方面显得力不从心&#xff0c;这催生了对更先进解决…

LVS高可用负载均衡

一、项目图 二、主机规划 主机系统安装应用网络IPclientredhat 9.5无NAT192.168.72.115/24lvs-masterredhat 9.5ipvsadm&#xff0c;keepalivedNAT192.168.72.116/24 VIP 192.168.72.100/32lvs-backupredhat 9.5ipvsadm&#xff0c;keepalivedNAT192.168.72.117/24 VIP 192.168…

脑影像分析软件推荐 | JuSpace

目录 1. 软件界面 2.工具包功能简介 3.软件安装注意事项 参考文献&#xff1a; Dukart J, Holiga S, Rullmann M, Lanzenberger R, Hawkins PCT, Mehta MA, Hesse S, Barthel H, Sabri O, Jech R, Eickhoff SB. JuSpace: A tool for spatial correlation analyses of magne…

逛好公园的好处

逛公园和软件开发看似是两个不同的活动&#xff0c;但它们之间存在一些有趣的关联和相互促进的关系&#xff1a; 激发创造力&#xff1a;公园中的自然景观、多样的人群以及各种活动能为开发者带来新的灵感和创意。软件开发过程中&#xff0c;从公园中获得的创意可以帮助开发者设…

【网络安全】 防火墙技术

防火墙是网络安全防御的重要组成部分&#xff0c;它的主要任务是阻止或限制不安全的网络通信。在这篇文章中&#xff0c;我们将详细介绍防火墙的工作原理&#xff0c;类型以及如何配置和使用防火墙。我们将尽可能使用简单的语言和实例&#xff0c;以便于初学者理解。 一、什么…

文档的预解析

1. 预解析的核心目标 浏览器在正式解析&#xff08;Parsing&#xff09;HTML 前&#xff0c;会启动一个轻量级的 预解析器&#xff08;Pre-Parser&#xff09;&#xff0c;快速扫描文档内容&#xff0c;实现&#xff1a; 提前发现并加载关键资源&#xff08;如 CSS、JavaScrip…

记一次表格数据排序优化(一)--排序30000条数据有多卡

目录 需求 第一次尝试 运行环境 思路 存储 排序 触发排序操作 如何实现高效的排序 关键1 关键2 关键3 磨刀不误砍柴工 关键4 代码 效果 卡顿原因分析 原因1 原因2 第二次尝试 需求 1 我的qt程序通过表格显示30000条数据。数据来自udp&#xff0c;udp每隔10秒…

图形渲染中的定点数和浮点数

三种API的NDC区别 NDC全称&#xff0c;Normalized Device Coordinates Metal、Vulkan、OpenGL的区别如下&#xff1a; featureOpenGL NDCMetal NDCVulkan NDC坐标系右手左手右手z值范围[-1,1][0,1][0,1]xy视口范围[-1,1][-1,1][-1,1] GPU渲染的定点数和浮点数 定点数类型&a…

【深度学习】CNN简述

文章目录 一、卷积神经网络&#xff08;CNN&#xff09;二、CNN结构特性1. CNN 典型结构2. 局部连接3. 权重共享4.空间或时间上的次采样 三、理解层面 一、卷积神经网络&#xff08;CNN&#xff09; 卷积神经网络(Convolutional Neural Network&#xff0c;CNN)是一种用于处理…

强化学习课程:stanford_cs234 学习笔记(3)introduction to RL

文章目录 前言7 markov 实践7.1 markov 过程再叙7.2 markov 奖励过程 MRP&#xff08;markov reward process&#xff09;7.3 markov 价值函数与贝尔曼方程7.4 markov 决策过程MDP&#xff08;markov decision process&#xff09;的 状态价值函数7.4.1 状态价值函数7.4.2 状态…

紫檀博物馆一游与软件开发

今天去逛了中国紫檀博物馆&#xff0c;里边很多层展品&#xff0c;也有一些清代的古物&#xff0c;檀木&#xff0c;黄花梨木家具和各种摆件&#xff0c;馆主陈丽华女士也是发心复原、保留和弘扬中国的传统文化&#xff0c;和西游记唐僧扮演者迟成瑞先生一家。 每一件展品都精…

RocketMQ初认识

ProducerCustomerNameServer: Broker的注册服务发现中心BrokerServer:主要负责消息的存储、投递和查询以及服务高可用保证 RocketMQ的集群部署&#xff1a; 单个master的分支多个Master 模式&#xff1a;集群中有多个 Master 节点&#xff0c;彼此之间相互独立。生产者可以将消…

Chrome开发者工具实战:调试三剑客

在前端开发的世界里&#xff0c;Chrome开发者工具就是我们的瑞士军刀&#xff0c;它集成了各种强大的功能&#xff0c;帮助我们快速定位和解决代码中的问题。今天&#xff0c;就让我们一起来看看如何使用Chrome开发者工具中的“调试三剑客”&#xff1a;断点调试、调用栈跟踪和…

C++/Qt 模拟sensornetwork的工作

C/Qt 可视化模拟sensornetwork的工作 C/Qt 模拟sensornetwork的工作 C/Qt 可视化模拟sensornetwork的工作内容简介&#xff08;一&#xff09; 需求和规格说明&#xff08;1&#xff09;问题描述&#xff08;2&#xff09;设计目的&#xff08;3&#xff09;基本要求&#xff0…