当前位置: 首页 > news >正文

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 操纵键盘&#xff1a;Pynput 6.7.1 press、release 按下或释放某个按键。 from pynput.keyboard import Controller, Keykeyboard Controller() # 按下并释放f keyboard.press(f) keyboard.release(f) # 按下组合按键&#xff1a;alt tab keyboard.press(Key.alt) key…...

购药不烦恼:线上购药小程序的快捷方式

在这个数字化时代&#xff0c;线上购药小程序的快捷方式正在改变着我们购药的方式。本文将介绍如何通过使用Python和Flask框架创建一个简单的线上购药小程序的原型&#xff0c;为用户提供购药的便利和快捷体验。 安装和设置 首先&#xff0c;确保你已经安装了Python和Flask。…...

10.17课上(七段显示器,递归异或与电路)

异或的递归与数电实现 用二选一选择器实现异或函数 在异或当中&#xff0c;如果有一项为0&#xff0c;就可以把那一项消掉&#xff1b;如果有一项为1&#xff0c;就是把剩下的所有项运算完的结果取反 &#xff08;由此在算法当中可以采用递归解决&#xff09; 当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 插件提供了两个能力&#xff1a; 把整个项目&#xf…...

cocosCreator 之 3.x使用NodePool对象池和封装

版本&#xff1a; cocosCreator 3.4.0 语言&#xff1a; TypeScript 环境&#xff1a; Mac NodePool 在项目中频繁的使用instantiate和node.destory对性能有很大的耗费&#xff0c;比如飞机射击中的子弹使用和销毁。 因此官方提供了NodePool&#xff0c;它被作为管理节点对象…...

三、RestClient操作索引库与文档

文章目录 三、RestClient操作索引库与文档3.1 操作索引库3.2 操作文档结束语 三、RestClient操作索引库与文档 ES官方提供了各种不同语言的客户端&#xff0c;用来操作ES。这些客户端的本质就是组装DSL语句&#xff0c;通过http请求发送给ES。 官方文档地址: https://www.ela…...

Hadoop3教程(五):NameNode和SecondaryNameNode

文章目录 &#xff08;59&#xff09;NN和2NN的工作机制&#xff08;60&#xff09;FsImage镜像文件&#xff08;61&#xff09;Edits编辑日志&#xff08;62&#xff09;Checkpoint时间设置参考文献 &#xff08;59&#xff09;NN和2NN的工作机制 NameNode的数据是存储在磁盘…...

腾讯云我的世界mc服务器多少钱一年?

腾讯云我的世界mc服务器多少钱&#xff1f;95元一年2核2G3M轻量应用服务器、2核4G5M带宽优惠价218元一年、4核8G12M带宽轻量服务器446元一年&#xff0c;云服务器CVM标准型S5实例2核2G优惠价280元一年、2核4G配置服务器748元一年&#xff0c;腾讯云百科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(); …...

直线导轨在喷涂行业中的应用场景

直线导轨因其具有精度高、速度快、刚性强、使用寿命长等特点被广泛应用在各行各种中&#xff0c;其中&#xff0c;在喷涂行业中的应用最为广泛&#xff0c;以下是直线导轨在喷涂行业中的应用场景&#xff1a; 1、平面喷涂&#xff1a;直线导轨可以应用在各种机械加工的平面&…...

纯css+js自制下拉框

前提 因为html的select标签&#xff0c;下拉框自定义程度非常的低&#xff0c;为了贴合而项目ui显示&#xff0c;所以打算自制下拉框 代码 html <div class"pos-rel"><div id"select" class"select get-select"><span class&…...

uniapp在App端如何动态修改原生导航栏?

uniapp在App端如何动态修改原生导航栏&#xff1f; 文章目录 uniapp在App端如何动态修改原生导航栏&#xff1f;page.json配置修改 buttons 文字修改按钮上的角标设置 searchInput的 focus设置 searchInput的 text 在App端可以通过得到 webview 对象&#xff0c;通过当前 webvi…...

Linux:CPUPower管理器 --- cpufreq解析

一、cpufreq是什么&#xff1f; cpufreq是Linux内核下的一种功率管理框架&#xff0c;它负责改变CPU的频率&#xff0c;以降低功耗并延长电池寿命。该框架的主要机制是动态调整CPU频率&#xff0c;该频率受限于CPU的负载和功耗。cpufreq能够动态地将频率降低到最低值或最高值&a…...

【嵌入式开发问答】不是普通的嵌入式八股

1. 进程、线程、堆栈、溢出 【问&#xff1a;】 进程的堆栈的物理内存是什么时候分配的&#xff1f; 堆栈的大小限制是多大&#xff1f;这个限制可以调整吗&#xff1f; 当堆栈发生溢出后应用程序会发生什么&#xff1f; 【答&#xff1a;】...

面试题-springboot篇-SpringBoot的注解

SpringBootApplication是SpringBoot的最核心的注解。 SpringBootApplication注解在SpringBoot的主类上&#xff0c;标识是SpringBoot应用&#xff0c;用来开启SpringBoot的各项能力。由SpringBootConfiguration、EnableAutoConfiguration、ComponentScan三个注解组成。这三个注…...

BaiChuan2保姆级微调范例

前方干货预警&#xff1a;这可能是你能够找到的&#xff0c;最容易理解&#xff0c;最容易跑通的&#xff0c;适用于各种开源LLM模型的&#xff0c;同时支持多轮和单轮对话数据集的大模型高效微调范例。 我们构造了一个修改大模型自我认知的3轮对话的玩具数据集&#xff0c;使用…...

postgresql参数优化

一 相关参数介绍 1.1 内存参数-shared_buffers shared_buffers&#xff1a;共享缓存区的大小&#xff0c;相当于oracle数据库中的SGA. 一般推荐为内存的四分之一&#xff0c;不超过总内存的二分之一。 该值默认是128M。 1.2 cpu并行参数-max_parallel_workers max_parall…...

【极速发表】2-4区SCI (含CCF),平均录用周期仅2个月,最快11天见刊!

一、计算机科学类SCI (11.30截稿) 【期刊概况】IF:4.0-5.0, JCR2区&#xff0c;中科院3区&#xff1b; 【检索情况】SCI在检&#xff0c;正刊&#xff1b; 【国人占比】10.58%&#xff1b; 【自引率】7.50%&#xff1b; 【年发文量】100篇以下&#xff1b; 【预警情况】无…...

Git 提交规范

遇到的问题 在项目中采用 git 管理代码版本时&#xff0c;突然不能进行提交&#xff08;git commit&#xff09;。 报错信息如下&#xff1a; ERROR invalid commit message format. Proper commit message format is required for automated changelog generation. Git 规范…...

1.3 VSCode安装与环境配置

进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件&#xff0c;然后打开终端&#xff0c;进入下载文件夹&#xff0c;键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist

现象&#xff1a; android studio报错&#xff1a; [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决&#xff1a; 不要动CMakeLists.…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...

路由基础-路由表

本篇将会向读者介绍路由的基本概念。 前言 在一个典型的数据通信网络中&#xff0c;往往存在多个不同的IP网段&#xff0c;数据在不同的IP网段之间交互是需要借助三层设备的&#xff0c;这些设备具备路由能力&#xff0c;能够实现数据的跨网段转发。 路由是数据通信网络中最基…...