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 规范…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
(二)原型模式
原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
Netty从入门到进阶(二)
二、Netty入门 1. 概述 1.1 Netty是什么 Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients. Netty是一个异步的、基于事件驱动的网络应用框架,用于…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
