vue使用pdf-dist实现pdf预览以及水印
vue使用pdf-dist实现pdf预览以及水印
一.使用pdf-dist插件将PDF文件转换为一张张canvas图片
npm install pdf-dist
二.页面引入插件
const pdfJS = require("pdfjs-dist");
pdfJS.GlobalWorkerOptions.workerSrc = require("pdfjs-dist/build/pdf.worker.entry");
三.渲染PDF
// 根据页码渲染相应的PDF
renderPage(num) {this.renderingPage = true;this.pdfData.promise.then((pdf) => {this.pdfPageNumber = pdf.numPages;pdf.getPage(num).then((page) => {// 获取DOM中为预览PDF准备好的canvasDOM对象let canvas = this.$refs.myCanvas;let ctx = canvas.getContext("2d");// 获取页面比率let ratio = this._getRatio(ctx);// 根据页面宽度和视口宽度的比率就是内容区的放大比率let dialogWidth = this.$refs["canvasCont"].offsetWidth;let pageWidth = page.view[2] * ratio;let scale = dialogWidth / pageWidth;let viewport = page.getViewport({ scale });// 记录内容区宽高,后期添加水印时需要this.width = viewport.width * ratio;this.height = viewport.height * ratio;canvas.width = this.width;canvas.height = this.height;// 缩放比率ctx.setTransform(ratio, 0, 0, ratio, 0, 0);let renderContext = {canvasContext: ctx,viewport: viewport,};page.render(renderContext).promise.then(() => {this.renderingPage = false;this.pageNo = num;// 添加水印this._renderWatermark();});});});
},
// 计算角度
_getRatio(ctx) {let dpr = window.devicePixelRatio || 1;let bsr =ctx.webkitBackingStorePixelRatio ||ctx.mozBackingStorePixelRatio ||ctx.msBackingStorePixelRatio ||ctx.oBackingStorePixelRatio ||ctx.backingStorePixelRatio ||1;return dpr / bsr;
},
四.添加水印
// 生成水印图片
_initWatermark() {let canvas = document.createElement("canvas");canvas.width = 200;canvas.height = 200;let ctx = canvas.getContext("2d");ctx.rotate((-18 * Math.PI) / 180);ctx.font = "10px Vedana";ctx.fillStyle = "rgba(0, 0, 0, 0.8)";ctx.textAlign = "left";ctx.textBaseline = "middle";ctx.fillText(this.watermark, 10, 100);return canvas;
},
五.完整代码(带翻页)
<template><div class="main-container"><input type="file" ref="fielinput" @change="uploadFile" /><div ref="canvasCont" class="canvas-container"><canvas ref="myCanvas" class="pdf-container"></canvas></div><div class="pagination-wrapper"><button @click="clickPre">上一页</button><span>第{{ pageNo }} / {{ pdfPageNumber }}页</span><button @click="clickNext">下一页</button></div></div>
</template><script>
const pdfJS = require("pdfjs-dist");pdfJS.GlobalWorkerOptions.workerSrc = require("pdfjs-dist/build/pdf.worker.entry");
export default {props: {watermark: {type: String,default: "水印文字水印文字水印文字",},},mounted() {},data() {return {pageNo: null,pdfPageNumber: null,renderingPage: false,pdfData: null, // PDF的base64scale: 1, // 缩放值width: "",height: "",};},methods: {uploadFile() {let inputDom = this.$refs.fielinput;let file = inputDom.files[0];let reader = new FileReader();reader.readAsDataURL(file);reader.onload = () => {let data = atob(reader.result.substring(reader.result.indexOf(",") + 1));this.loadPdfData(data);};},loadPdfData(data) {// 引入pdf.js的字体let CMAP_URL = "https://unpkg.com/pdfjs-dist@2.0.943/cmaps/";//读取base64的pdf流文件this.pdfData = pdfJS.getDocument({data: data, // PDF base64编码cMapUrl: CMAP_URL,cMapPacked: true,});this.renderPage(1);},// 根据页码渲染相应的PDFrenderPage(num) {this.renderingPage = true;this.pdfData.promise.then((pdf) => {this.pdfPageNumber = pdf.numPages;pdf.getPage(num).then((page) => {// 获取DOM中为预览PDF准备好的canvasDOM对象let canvas = this.$refs.myCanvas;let ctx = canvas.getContext("2d");// 获取页面比率let ratio = this._getRatio(ctx);// 根据页面宽度和视口宽度的比率就是内容区的放大比率let dialogWidth = this.$refs["canvasCont"].offsetWidth;let pageWidth = page.view[2] * ratio;let scale = dialogWidth / pageWidth;let viewport = page.getViewport({ scale });// 记录内容区宽高,后期添加水印时需要this.width = viewport.width * ratio;this.height = viewport.height * ratio;canvas.width = this.width;canvas.height = this.height;// 缩放比率ctx.setTransform(ratio, 0, 0, ratio, 0, 0);let renderContext = {canvasContext: ctx,viewport: viewport,};page.render(renderContext).promise.then(() => {this.renderingPage = false;this.pageNo = num;// 添加水印this._renderWatermark();});});});},// 计算角度_getRatio(ctx) {let dpr = window.devicePixelRatio || 1;let bsr =ctx.webkitBackingStorePixelRatio ||ctx.mozBackingStorePixelRatio ||ctx.msBackingStorePixelRatio ||ctx.oBackingStorePixelRatio ||ctx.backingStorePixelRatio ||1;return dpr / bsr;},// 在画布上渲染水印_renderWatermark() {let canvas = this.$refs.myCanvas;let ctx = canvas.getContext("2d");// 平铺水印let pattern = ctx.createPattern(this._initWatermark(), "repeat");ctx.rect(0, 0, this.width, this.height);ctx.fillStyle = pattern;ctx.fill();},// 生成水印图片_initWatermark() {let canvas = document.createElement("canvas");canvas.width = 200;canvas.height = 200;let ctx = canvas.getContext("2d");ctx.rotate((-18 * Math.PI) / 180);ctx.font = "10px Vedana";ctx.fillStyle = "rgba(0, 0, 0, 0.8)";ctx.textAlign = "left";ctx.textBaseline = "middle";ctx.fillText(this.watermark, 10, 100);return canvas;},clickPre() {if (!this.renderingPage && this.pageNo && this.pageNo > 1) {this.renderPage(this.pageNo - 1);}},clickNext() {if (!this.renderingPage &&this.pdfPageNumber &&this.pageNo &&this.pageNo < this.pdfPageNumber) {this.renderPage(this.pageNo + 1);}},},
};
</script><style scoped>
.main-container {display: flex;flex-direction: column;align-items: center;
}
.canvas-container {width: 100%;height: 100%;border: 1px dashed black;position: relative;display: flex;justify-content: center;
}
.pdf-container {width: 100%;height: 100%;
}.pagination-wrapper {display: flex;justify-content: center;align-items: center;
}
</style>
六.完整代码(滑动)
<template><div class="main-container"><input type="file" ref="fielinput" @change="uploadFile" /><div ref="canvasCont" class="canvas-container"><canvas v-for="pageIndex in pdfPageNumber" :ref="`myCanvas${pageIndex}`" :key="pageIndex" class="pdf-container"></canvas></div></div>
</template><script>
const pdfJS = require("pdfjs-dist");pdfJS.GlobalWorkerOptions.workerSrc = require("pdfjs-dist/build/pdf.worker.entry");
export default {props: {watermark: {type: String,default: "水印文字水印文字水印文字",},},mounted() {},data() {return {pageNo: null,pdfPageNumber: null,renderingPage: false,pdfData: null, // PDF的base64scale: 1, // 缩放值width: "",height: "",};},methods: {uploadFile() {let inputDom = this.$refs.fielinput;let file = inputDom.files[0];let reader = new FileReader();reader.readAsDataURL(file);reader.onload = () => {let data = atob(reader.result.substring(reader.result.indexOf(",") + 1));this.loadPdfData(data);};},loadPdfData(data) {// 引入pdf.js的字体let CMAP_URL = "https://unpkg.com/pdfjs-dist@2.0.943/cmaps/";//读取base64的pdf流文件this.pdfData = pdfJS.getDocument({data: data, // PDF base64编码cMapUrl: CMAP_URL,cMapPacked: true,});this.renderPage(1);},// 根据页码渲染相应的PDFrenderPage(num) {this.renderingPage = true;this.pdfData.promise.then((pdf) => {this.pdfPageNumber = pdf.numPages;pdf.getPage(num).then((page) => {// 获取DOM中为预览PDF准备好的canvasDOM对象let canvas = this.$refs[`myCanvas${num}`][0];let ctx = canvas.getContext("2d");// 获取页面比率let ratio = this._getRatio(ctx);// 根据页面宽度和视口宽度的比率就是内容区的放大比率let dialogWidth = this.$refs["canvasCont"].offsetWidth;let pageWidth = page.view[2] * ratio;let scale = dialogWidth / pageWidth;let viewport = page.getViewport({ scale });// 记录内容区宽高,后期添加水印时需要this.width = viewport.width * ratio;this.height = viewport.height * ratio;canvas.width = this.width;canvas.height = this.height;// 缩放比率ctx.setTransform(ratio, 0, 0, ratio, 0, 0);let renderContext = {canvasContext: ctx,viewport: viewport,};page.render(renderContext).promise.then(() => {this.renderingPage = false;this.pageNo = num;// 添加水印this._renderWatermark(num);if(num < this.pdfPageNumber){this.renderPage(num+1)}});});});},// 计算角度_getRatio(ctx) {let dpr = window.devicePixelRatio || 1;let bsr =ctx.webkitBackingStorePixelRatio ||ctx.mozBackingStorePixelRatio ||ctx.msBackingStorePixelRatio ||ctx.oBackingStorePixelRatio ||ctx.backingStorePixelRatio ||1;return dpr / bsr;},// 在画布上渲染水印_renderWatermark(num) {let canvas = this.$refs[`myCanvas${num}`][0];let ctx = canvas.getContext("2d");// 平铺水印let pattern = ctx.createPattern(this._initWatermark(), "repeat");ctx.rect(0, 0, this.width, this.height);ctx.fillStyle = pattern;ctx.fill();},// 生成水印图片_initWatermark() {let canvas = document.createElement("canvas");canvas.width = 200;canvas.height = 200;let ctx = canvas.getContext("2d");ctx.rotate((-18 * Math.PI) / 180);ctx.font = "10px Vedana";ctx.fillStyle = "rgba(0, 0, 0, 0.8)";ctx.textAlign = "left";ctx.textBaseline = "middle";ctx.fillText(this.watermark, 10, 100);return canvas;},},
};
</script><style scoped>
.main-container {display: flex;flex-direction: column;align-items: center;
}
.canvas-container {width: 100%;height: 100%;border: 1px dashed black;position: relative;/* display: flex; *//* justify-content: center; */
}
.pdf-container {width: 100%;height: 100%;
}
</style>
七.下载源码地址
下载地址
八.效果展示

相关文章:
vue使用pdf-dist实现pdf预览以及水印
vue使用pdf-dist实现pdf预览以及水印 一.使用pdf-dist插件将PDF文件转换为一张张canvas图片 npm install pdf-dist二.页面引入插件 const pdfJS require("pdfjs-dist"); pdfJS.GlobalWorkerOptions.workerSrc require("pdfjs-dist/build/pdf.worker.entry&…...
[Python进阶] 操纵键盘:Pynput
6.7 操纵键盘:Pynput 6.7.1 press、release 按下或释放某个按键。 from pynput.keyboard import Controller, Keykeyboard Controller() # 按下并释放f keyboard.press(f) keyboard.release(f) # 按下组合按键:alt tab keyboard.press(Key.alt) key…...
购药不烦恼:线上购药小程序的快捷方式
在这个数字化时代,线上购药小程序的快捷方式正在改变着我们购药的方式。本文将介绍如何通过使用Python和Flask框架创建一个简单的线上购药小程序的原型,为用户提供购药的便利和快捷体验。 安装和设置 首先,确保你已经安装了Python和Flask。…...
10.17课上(七段显示器,递归异或与电路)
异或的递归与数电实现 用二选一选择器实现异或函数 在异或当中,如果有一项为0,就可以把那一项消掉;如果有一项为1,就是把剩下的所有项运算完的结果取反 (由此在算法当中可以采用递归解决) 当w1为0时&…...
maven-plugin-shade 详解
一、介绍 [1] This plugin provides the capability to package the artifact in an uber-jar, including its dependencies and to shade - i.e. rename - the packages of some of the dependencies. maven-plugin-shade 插件提供了两个能力: 把整个项目…...
cocosCreator 之 3.x使用NodePool对象池和封装
版本: cocosCreator 3.4.0 语言: TypeScript 环境: Mac NodePool 在项目中频繁的使用instantiate和node.destory对性能有很大的耗费,比如飞机射击中的子弹使用和销毁。 因此官方提供了NodePool,它被作为管理节点对象…...
三、RestClient操作索引库与文档
文章目录 三、RestClient操作索引库与文档3.1 操作索引库3.2 操作文档结束语 三、RestClient操作索引库与文档 ES官方提供了各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。 官方文档地址: https://www.ela…...
Hadoop3教程(五):NameNode和SecondaryNameNode
文章目录 (59)NN和2NN的工作机制(60)FsImage镜像文件(61)Edits编辑日志(62)Checkpoint时间设置参考文献 (59)NN和2NN的工作机制 NameNode的数据是存储在磁盘…...
腾讯云我的世界mc服务器多少钱一年?
腾讯云我的世界mc服务器多少钱?95元一年2核2G3M轻量应用服务器、2核4G5M带宽优惠价218元一年、4核8G12M带宽轻量服务器446元一年,云服务器CVM标准型S5实例2核2G优惠价280元一年、2核4G配置服务器748元一年,腾讯云百科txybk.com分享腾讯云我的…...
modelsim实现二选一以及D触发器并仿真
#实验一 二选一 module two_1(in1,in2,cho,out); input[3:0] in1,in2; output[3:0] out; reg[3:0] out; input cho; always* begin if(cho0) outin1; else outin2; end endmodule module two1_test(); …...
直线导轨在喷涂行业中的应用场景
直线导轨因其具有精度高、速度快、刚性强、使用寿命长等特点被广泛应用在各行各种中,其中,在喷涂行业中的应用最为广泛,以下是直线导轨在喷涂行业中的应用场景: 1、平面喷涂:直线导轨可以应用在各种机械加工的平面&…...
纯css+js自制下拉框
前提 因为html的select标签,下拉框自定义程度非常的低,为了贴合而项目ui显示,所以打算自制下拉框 代码 html <div class"pos-rel"><div id"select" class"select get-select"><span class&…...
uniapp在App端如何动态修改原生导航栏?
uniapp在App端如何动态修改原生导航栏? 文章目录 uniapp在App端如何动态修改原生导航栏?page.json配置修改 buttons 文字修改按钮上的角标设置 searchInput的 focus设置 searchInput的 text 在App端可以通过得到 webview 对象,通过当前 webvi…...
Linux:CPUPower管理器 --- cpufreq解析
一、cpufreq是什么? cpufreq是Linux内核下的一种功率管理框架,它负责改变CPU的频率,以降低功耗并延长电池寿命。该框架的主要机制是动态调整CPU频率,该频率受限于CPU的负载和功耗。cpufreq能够动态地将频率降低到最低值或最高值&a…...
【嵌入式开发问答】不是普通的嵌入式八股
1. 进程、线程、堆栈、溢出 【问:】 进程的堆栈的物理内存是什么时候分配的? 堆栈的大小限制是多大?这个限制可以调整吗? 当堆栈发生溢出后应用程序会发生什么? 【答:】...
面试题-springboot篇-SpringBoot的注解
SpringBootApplication是SpringBoot的最核心的注解。 SpringBootApplication注解在SpringBoot的主类上,标识是SpringBoot应用,用来开启SpringBoot的各项能力。由SpringBootConfiguration、EnableAutoConfiguration、ComponentScan三个注解组成。这三个注…...
BaiChuan2保姆级微调范例
前方干货预警:这可能是你能够找到的,最容易理解,最容易跑通的,适用于各种开源LLM模型的,同时支持多轮和单轮对话数据集的大模型高效微调范例。 我们构造了一个修改大模型自我认知的3轮对话的玩具数据集,使用…...
postgresql参数优化
一 相关参数介绍 1.1 内存参数-shared_buffers shared_buffers:共享缓存区的大小,相当于oracle数据库中的SGA. 一般推荐为内存的四分之一,不超过总内存的二分之一。 该值默认是128M。 1.2 cpu并行参数-max_parallel_workers max_parall…...
【极速发表】2-4区SCI (含CCF),平均录用周期仅2个月,最快11天见刊!
一、计算机科学类SCI (11.30截稿) 【期刊概况】IF:4.0-5.0, JCR2区,中科院3区; 【检索情况】SCI在检,正刊; 【国人占比】10.58%; 【自引率】7.50%; 【年发文量】100篇以下; 【预警情况】无…...
Git 提交规范
遇到的问题 在项目中采用 git 管理代码版本时,突然不能进行提交(git commit)。 报错信息如下: ERROR invalid commit message format. Proper commit message format is required for automated changelog generation. Git 规范…...
智能抢票新纪元:MaxBot如何突破票务平台限制?2025革新攻略
智能抢票新纪元:MaxBot如何突破票务平台限制?2025革新攻略 【免费下载链接】tix_bot Max搶票機器人(maxbot) help you quickly buy your tickets 项目地址: https://gitcode.com/gh_mirrors/ti/tix_bot 在数字票务时代,热门活动门票往…...
BetterNCM安装器终极指南:3分钟搞定网易云音乐插件管理
BetterNCM安装器终极指南:3分钟搞定网易云音乐插件管理 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer BetterNCM安装器是一款专为PC版网易云音乐用户设计的强大插件管理器…...
八大网盘直链提取终极指南:突破客户端限制的JavaScript神器
八大网盘直链提取终极指南:突破客户端限制的JavaScript神器 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘 /…...
后端消息投递可靠性:基于 RabbitMQ 的“双重防线-幂等闭环”模式
后端消息投递可靠性:基于 RabbitMQ 的“双重防线-幂等闭环”模式 一、 发送核心:rabbitTemplate.convertAndSend 重载全览 在 RabbitMQ 的“中控-工人”模式中,中控(生产者)发送指令的形式决定了任务的可靠性深度。 1.…...
丝印层—PCB封装的信息标识系统
如果说焊盘是 PCB 封装的 “硬件骨架”,那么丝印层(Silkscreen) 就是封装的 “信息标识系统”,是 PCB 表面最直观的 “说明书”。一、丝印层的基础定义与特性丝印层,又称 “文字层”“标识层”,是 PCB 表…...
Tree-sitter解析代码
Tree-sitter 语法树 到底是什么、长什么样、包含什么东西。一、通俗解释Tree-sitter 会把一行行代码,解析成一棵结构化的「语法树」(像家族树一样有层级、有分类),让机器能看懂代码的结构(哪个是方法、哪个是方法名、哪…...
3大核心功能解锁QtScrcpy:实现跨平台Android设备高效控制
3大核心功能解锁QtScrcpy:实现跨平台Android设备高效控制 【免费下载链接】QtScrcpy Android real-time display control software 项目地址: https://gitcode.com/GitHub_Trending/qt/QtScrcpy QtScrcpy是一款开源的跨平台Android实时显示与控制工具&#x…...
性能测试中的“假阳性”:如何识别与避免?
在软件性能测试领域,“假阳性”是一个令测试团队既头疼又难以回避的挑战。它指的是测试报告或监控工具错误地发出性能警报,声称系统存在性能瓶颈或缺陷,但经过深入分析或在实际环境中验证,发现系统运行状态良好,并不存…...
tealdeer与其他tldr客户端的终极对比分析:哪个更适合你的命令行工作流?
tealdeer与其他tldr客户端的终极对比分析:哪个更适合你的命令行工作流? 【免费下载链接】tealdeer A very fast implementation of tldr in Rust. 项目地址: https://gitcode.com/gh_mirrors/te/tealdeer tealdeer是一款用Rust开发的tldr客户端&a…...
解决数字阅读焦虑:用fanqienovel-downloader构建个人离线图书馆的创新方法
解决数字阅读焦虑:用fanqienovel-downloader构建个人离线图书馆的创新方法 【免费下载链接】fanqienovel-downloader 下载番茄小说 项目地址: https://gitcode.com/gh_mirrors/fa/fanqienovel-downloader 在数字阅读日益普及的今天,你是否曾遭遇过…...
