Angular 项目 PDF 批注插件库在线版 API 示例教程

发布于:2025-04-04 ⋅ 阅读:(24) ⋅ 点赞:(0)

本文章介绍 Angular 项目中 PDF 批注插件库 ElasticPDF 在线版 API 示例教程,API 包含 ① 导出批注后PDF数据;② 导出纯批注 json 数据;③ 加载旧批注;④ 切换文档;⑤ 切换用户;⑥ 清空批注 等数据处理功能,可满足通常业务需求。本教程可用于月付许可和在线测试版,欢迎 联系我们 咨询和获取接入 key。

封面.png

0 ElasticPDF 产品介绍

ElasticPDF基于开源pdf.js,增加了多种开箱即用的 PDF 批注功能。代码包延续了 pdf.js-dist 独立且完全离线的结构风格,仅增加了用于支持批注的离线 Javascript 代码,可以快速完美集成到任何可以运行Javascript, HTML, CSS 的项目环境中,在公网及内网环境都可以完美的运行。

项目结构-中文.png

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

① 批注合成版: https://demos.libertynlp.com/#/pdfjs-annotation
② 专业批注版: https://www.elasticpdf.com/demo

1 导入页面 HTML 及初始化

首先将以下代码导入到目标 HTML页面,此处为 app.component.html

<div *ngIf='language==="zh-cn"' class='project-title'>
	<img src="https://elasticpdf.com/elasticpdf-image/logo-no-back.png" alt="" />
	<h2>Angular 项目在线 API 示例教程</h2>
	<a style="cursor: pointer;text-decoration: none;" href='https://www.elasticpdf.com/contact-us.html'
		target="_blank">
		<div title='联系我们获取测试 key' class='message-div info-message'>
			<i class="fa fa-info-circle" aria-hidden="true"></i>
			<span>获取测试 key</span>
		</div>
	</a>
	<button style="margin-left: 20px;" class='theme-btn btn-outline-warning'
		(click)="getPDFData()">获取PDF数据</button>
	<button class='theme-btn btn-outline-help' (click)="outputAnnotation()">导出批注</button>
	<button class='theme-btn btn-outline-success' (click)="changeFile()">切换文档</button>
	<button class='theme-btn btn-outline-warning' (click)="setMember('test_id')">切换用户</button>
	<button class='theme-btn btn-outline-danger' (click)="clearAnnotation()">清空批注</button>
	<button class='theme-btn btn-outline-info' (click)="reloadOldAnnotationData()">加载旧批注</button>
</div>
<div style="width: 100%;height: auto;">
	<iframe src="https://pdfmaster.libertynlp.com/web/viewer.html?file=tutorial.pdf" id='elasticpdf-iframe' 
	(load)='initialPDFEditor()' width="100%" height="680px" frameborder="0"></iframe>
</div>

再将以下逻辑代码导入到 app.component.html 对应的 ts 页面中,此处为 app.component.ts,其中包含了初始化代码 initialPDFEditor() 和接收所有回报信息的函数,所有导出的 PDF数据,批注数据 都在函数 @HostListener 下,可以与后续的业务融合。

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterOutlet } from '@angular/router';
import { HostListener } from '@angular/core';

@Component({
	selector: 'app-root',
	standalone: true,
	imports: [CommonModule, RouterOutlet],
	templateUrl: './app.component.html',
	styleUrls: ['./app.component.css']
})

export class AppComponent {
	language:any = 'en';
	
	elasticpdf_viewer:any = null;
	elasticpdf_iframe:any = null;
	
	initialPDFEditor() {
		let languages = navigator.languages as string[];		
		if (languages[0].toLowerCase().includes('zh')) {
		  this.language = 'zh-cn';
		  // alert(languages);
		}
		
		this.elasticpdf_iframe = document.getElementById('elasticpdf-iframe') as HTMLIFrameElement;
		
		// The online version only supports opening online documents
		// 在线版只支持打开在线文档
		let pdf_url = "tutorial.pdf";
		if (this.elasticpdf_iframe && this.elasticpdf_iframe.contentWindow) {
			this.elasticpdf_viewer = this.elasticpdf_iframe?.contentWindow as any;
			this.elasticpdf_viewer.postMessage({
				"source": "test-elasticpdf",
				"function_name": "initialApp",
				"content": {
					'language': this.language, // GUI language 交互语言
					'pdf_url': pdf_url,
					'member_info': { //Member Info  用户信息
						'id': 'elasticpdf_id',
						'name': 'elasticpdf',
					},
				}
			}, '*');
		}
	}
	
	
	// Listen for callbacks of various information about PDF editing
	// 监听 pdf 编辑各种信息的回调
	@HostListener('window:message', ['$event'])
	onMessage(event : Event) : void {
		const e : any = event;
		if (e.data.source != 'elasticpdf') {
			return;
		}
		
		// PDF loading completed callback, you can import the annotation file stored on the server here
		// pdf 加载结束的回调,可以在此处导入服务器上储存的批注文件
		if (e.data.function_name == 'pdfLoaded') {
			// console.log is invalid, please use alert to print content
			// console.log 无效,请使用 alert 打印内容
			// alert('PDF loaded successfully PDF加载成功');
			this.reloadData();
		}
		
		// PDF annotation export callback, where annotations can be exported and transferred to the server
		// pdf 批注导出回调,可以在此处导出批注并传输到服务器
		if (e.data.function_name == 'outputAnnotation') {
			// Only get the PDF annotation data, do not write it into the PDF
			// 仅获取 pdf 批注文件,不写入到 pdf 中
			let this_data = e.data.content;
			let annotation_content = JSON.stringify(this_data['file_annotation']);
			let file_name = this_data['file_name'];
			// console.log is invalid, please use alert to print content
			// console.log 无效,请使用 alert 打印内容
			alert('Annotation data 批注数据\n'+annotation_content);
		}
		
		// PDF annotation editing callback, where annotations can be exported and transferred to the server
		// pdf 批注编辑回调,可以在此处导出批注并传输到服务器
		if (e.data.function_name == 'annotationsModified') {
			// Only get the PDF annotation data, do not write it into the PDF
			// 仅获取 pdf 批注文件,不写入到 pdf 中
			let this_data = e.data.content;
			let annotation_content = JSON.stringify(this_data['file_annotation']);
			let file_name = this_data['file_name'];
			
			// console.log is invalid, please use alert to print content
			// alert('annotation modified 批注被修改');
			this.postService('upload-annotation-data', {
				'file_name': file_name,
				'file_id': '123ddasfsdffads',
				'file_annotation': annotation_content,
			});
		}
		
		
		// Receive the edited PDF data, and the annotations are written into the PDF
		// 接收编辑后的pdf数据,批注被写入 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'];
			// If the document has not been edited, pdf_base64 is still the file name or file url
			// Receive pdf data, where pdf_base64 can be quickly uploaded to the server
			// 如果文档没有被编辑过,则 pdf_base64 仍然是文件名或文件链接
			// 接收到 pdf 数据,其中 pdf_base64 可以快捷上传到服务器
			this.postService('upload-pdf-data', {
				'file_name': file_name,
				'file_id': '123ddasfsdffads',
				'file_data': pdf_base64,
			});
			alert('Get the pdf base64 data. Please go to postService function to add the subsequent function.\n\n获取到 pdf base64 数据,如有需要请到postService中增加业务函数');
		}
	}
}

2 调用 API

① 导出批注数据

导出 pdf 批注的 json 数据,可以用于后续的筛选、合并、入库保存等业务流程,非常适用于在线批注流程,因为只需要保存一个原 pdf 文档,然后从数据库中仅加载和回显批注,可以节省很多的服务器性能、流量和带宽费用。

// export annotations data 导出可保存的批注对象
outputAnnotation() {
	this.elasticpdf_viewer.postMessage({
		"source": "test-elasticpdf",
		"function_name": "outputAnnotation",
		"content": ""
	}, '*');
}

② 导入旧批注

从服务器中依据文件 ID 或 PDF 链接加载 ① 中导出的批注数据并回显至文档上,支持再次操作编辑,以此来实现批注数据的云端同步。

// reload old annotation data
// 加载旧批注
reloadOldAnnotationData() {
	var old_annotation = this.getOldAnnotation();
	this.elasticpdf_viewer.postMessage({
		"source": "test-elasticpdf",
		"function_name": "setFileAnnotation",
		"content": old_annotation
	}, '*');
}

// Generate simulated old annotation data
// 生成模拟旧批注数据
getOldAnnotation() {
	var old_annotation = {
		"annos-for-page-1": {
			"page_id": "annos-for-page-1",
			"page_canvas_container": {},
			"page_annotations": [],
			"page_canvas": {
				"fabric_canvas": {
					"version": "5.2.0",
					"objects": [{
						"type": "rect",
						"version": "5.2.0",
						"left": 64.38,
						"top": 159.99,
						"width": 608.27,
						"height": 290.3,
						"fill": "rgba(255,237,0,0.3)",
						"stroke": "rgba(17,153,158,1)",
						"erasable": true
					}],
					"background": "rgba(255, 255, 255, 0)"
				},
				"width": 994,
				"height": 1407,
				"fabric_canvas_json": {
					"version": "5.2.0",
					"objects": [{
						"type": "rect",
						"version": "5.2.0",
						"left": 64.38,
						"top": 159.99,
						"width": 608.27,
						"height": 290.3,
						"fill": "rgba(255,237,0,0.3)",
						"stroke": "rgba(17,153,158,1)",
						"erasable": true,
						"id": "1742436474916_1",
						"hasControls": true,
						"hasBorders": true,
						"selectable": true,
						"lockMovementX": false,
						"lockMovementY": false,
						"member_id": "elasticpdf_id",
						"member_name": "elasticpdf",
						"my_type": "rectangle",
						"comment": "添加批注",
						"backup_opacity": 1,
						"lockRotation": false
					}],
					"background": "rgba(255, 255, 255, 0)"
				}
			}
		}
	}
	return JSON.stringify(old_annotation);
}

③ 导出 PDF 文件

将批注合并到批注文件并导出批注后 PDF 文档 base64 数据,可以直接入库保存。

// export edited pdf data
// 导出批注编辑后pdf数据
getPDFData() {
	this.elasticpdf_viewer.postMessage({
		"source": "test-elasticpdf",
		"function_name": "getPDFData",
		"content": ""
	}, '*');
}

④ 切换和打开文档

打开在线文档,其中文件服务器或站点需要允许 CORS 跨域,否则加载文档会失败。

// You can change test_pdf with any online pdf url
// The file server needs to be configured to allow cross-domain
// 切换打开的文档,可以把 test_pdf 换成任意在线pdf链接
// 文件服务器需要配置允许跨域
changeFile() {
	var test_pdf = 'https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf';
	this.elasticpdf_viewer.postMessage({
		"source": "test-elasticpdf",
		"function_name": "openFile",
		"content": test_pdf
	}, '*');
}

⑤ 设置用户信息

设置插件内当前操作用户的 ID 和用户名,这些信息会被记录到每个批注中,后续可以用于做权限差异设置,例如是否允许当前用户操作他人批注。

// set member info including id and name
// 设置用户的 id 和 name
setMember(id:string) {
	var this_member = {
		'id': 'test-id',
		'name': 'test-name',
	};
	this.elasticpdf_viewer.postMessage({
		"source": "test-elasticpdf",
		"function_name": "setMember",
		"content": this_member
	}, '*');
}

⑥ 清空批注数据

将当前文档对应的操作批注完全清空。

// clear all annotations
// 清空批注
clearAnnotation() {
	this.elasticpdf_viewer.postMessage({
		"source": "test-elasticpdf",
		"function_name": "clearFileAnnotation",
		"content": ""
	}, '*');
}

总结

至此,elasticpdf 在线测试版集成于 Angular 项目并调用数据业务 API 的代码介绍完毕,测试代码文件已上传至 Github(网址:https://github.com/ElasticPDF/Angular-use-pdf.js-elasticpdf),欢迎联系我们咨询和获取 Key。

温馨提示:本文首发于 https://www.elasticpdf.com ,转载请注明出处:https://www.elasticpdf.com/blog/angular-pdf-annotation-plugin-library-online-api-examples-zh.html