百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

细品pdf.js实践解决含水印、电子签章问题「Vue篇」

ztj100 2024-12-03 06:51 11 浏览 0 评论


作者:jerryWu

转发连接:https://juejin.im/post/5ec48f84e51d45788266753a

pdf.js实战,含水印、电子签章解决方案

项目涉及到移动端查看电子合同的问题,前前后后试了三种方案,真是一步一个坑,三种方案各有各的优点,不水,直接上代码,按照自己的需求选择。

一、vue-pdf

直接使用vue-pdf插件,核心的代码是pdf.js,只不过就是自己封装了一下,优点是方便快捷,缺点是无法加载电子签章。

github地址: https://github.com/FranckFreiburger/vue-pdf

1、npm install pdf-vue --save

2、template代码

<template>
  <div class="pdf" v-show="fileType === 'pdf'">
    <p class="arrow">
    // 上一页
    <span @click="changePdfPage(0)" class="turn" :class="{grey: currentPage==1}">Preview</span>
    {{currentPage}} / {{pageCount}}
    // 下一页
    <span @click="changePdfPage(1)" class="turn" :class="{grey: currentPage==pageCount}">Next</span>
    </p>
    // 自己引入就可以使用,这里我的需求是做了分页功能,如果不需要分页功能,只要src就可以了
    <pdf
      :src="src" // src需要展示的PDF地址
      :page="currentPage" // 当前展示的PDF页码
      @num-pages="pageCount=$event" // PDF文件总页码
      @page-loaded="currentPage=$event" // 一开始加载的页面
      @loaded="loadPdfHandler"> // 加载事件
    </pdf>
  </div>
</template>
复制代码

3、js代码

import pdf from 'vue-pdf'
export default {
    components: {pdf},
    data () {
      return {
        currentPage: 0, // pdf文件页码
        pageCount: 0, // pdf文件总页数
        fileType: 'pdf', // 文件类型
    ????src: '', // pdf文件地址
      }
    },
??created: {
????// 有时PDF文件地址会出现跨域的情况,这里最好处理一下
????this.src = pdf.createLoadingTask(this.src)
??}
    method: {
      // 改变PDF页码,val传过来区分上一页下一页的值,0上一页,1下一页
      changePdfPage (val) {
        // console.log(val)
        if (val === 0 && this.currentPage > 1) {
          this.currentPage--
          // console.log(this.currentPage)
        }
        if (val === 1 && this.currentPage < this.pageCount) {
          this.currentPage++
          // console.log(this.currentPage)
        }
      },
    
      // pdf加载时
      loadPdfHandler (e) {
        this.currentPage = 1 // 加载的时候先加载第一页
      }
    
    }
}
复制代码

使用非常方便,尤其是只需要翻页,或者不需要翻页的,强烈推荐。

二、pdf-dist

pdf-dist也是基于pdf.js的一个组件,只不过没有封装,需要自己配置,优点是可配置,可实现特殊的需求,缺点是需要自己封装,水印可加载,网上说可以加载电子签章,我的加载不出来,所以还是没采用。

1、npm install pdf-dist --save

2、封装一个pdf.vue

<template>
  <div class="cpdf" id="cpdf">
    <div class="center">
      <canvas class="canvasstyle" id="the-canvas"></canvas>
      <div class="contor">
        <button @click="prev" style="margin-right: 10px">上一页</button>
        <span>Page: <span v-text="page_num"></span> / <span v-text="page_count"></span></span>
        <button @click="next" style="margin-left: 10px">下一页</button>
      </div>
    </div>
  </div>
</template>
<script>
import PDFJS from 'pdfjs-dist'

export default {
  name: 'c-pdf',
  // 接收父组件传来的参数   
  props: ['pdfurl'],
  components: { },
  data () {
    return {
      pdfDoc: null, // pdfjs 生成的对象
      pageNum: 1, //
      pageRendering: false,
      pageNumPending: null,
      scale: 1, // 放大倍数
      page_num: 0, // 当前页数
      page_count: 0, // 总页数
      maxscale: 2, // 最大放大倍数
      minscale: 0.8// 最小放大倍数
    }
  },
  methods: {
    renderPage (num) { // 渲染pdf
      let vm = this
      this.pageRendering = true
      let canvas = document.getElementById('the-canvas')
      let ctx = canvas.getContext('2d')
      let bsr =
                ctx.webkitBackingStorePixelRatio ||
                ctx.mozBackingStorePixelRatio ||
                ctx.msBackingStorePixelRatio ||
                ctx.oBackingStorePixelRatio ||
                ctx.backingStorePixelRatio ||
                1
      let dpr = window.devicePixelRatio || 1
      let ratio = dpr / bsr
      // Using promise to fetch the page
      this.pdfDoc.getPage(num).then(function (page) {
        var viewport = page.getViewport(screen.availWidth / page.getViewport(1).width)
        // alert(vm.canvas.height)
        canvas.height = ratio * viewport.width
        canvas.width = ratio * viewport.height
        canvas.style.width = 1.5 * viewport.width + 'px'
        canvas.style.height = 1 * viewport.height + 'px'
        ctx.setTransform(ratio, 0, 0, ratio, 0, 0)
        // Render PDF page into canvas context
        var renderContext = {
          canvasContext: ctx,
          viewport: viewport
        }
        var renderTask = page.render(renderContext)

        // Wait for rendering to finish
        renderTask.promise.then(function () {
          vm.pageRendering = false
          if (vm.pageNumPending !== null) {
            // New page rendering is pending
            vm.renderPage(vm.pageNumPending)
            vm.pageNumPending = null
          }
        })
      })
      vm.page_num = vm.pageNum
    },
    addscale () { // 放大
      if (this.scale >= this.maxscale) {
        return
      }
      this.scale += 0.1
      this.queueRenderPage(this.pageNum)
    },
    minus () { // 缩小
      if (this.scale <= this.minscale) {
        return
      }
      this.scale -= 0.1
      this.queueRenderPage(this.pageNum)
    },
    prev () { // 上一页
      let vm = this
      if (vm.pageNum <= 1) {
        return
      }
      vm.pageNum--
      vm.queueRenderPage(vm.pageNum)
    },
    next () { // 下一页
      let vm = this
      if (vm.pageNum >= vm.page_count) {
        return
      }
      vm.pageNum++
      vm.queueRenderPage(vm.pageNum)
    },
    closepdf () { // 关闭PDF
      this.$emit('closepdf')
    },
    queueRenderPage (num) {
      if (this.pageRendering) {
        this.pageNumPending = num
      } else {
        this.renderPage(num)
      }
    }
  },
  computed: {
    ctx () {
      let id = document.getElementById('the-canvas')
      return id.getContext('2d')
    }
  },
  mounted () {
    let vm = this
    PDFJS.getDocument(vm.pdfurl).then(function (pdfDoc_) { // 初始化pdf
      vm.pdfDoc = pdfDoc_
      vm.page_count = vm.pdfDoc.numPages
      vm.renderPage(vm.pageNum)
    })
  }
}

</script>
<style  lang="stylus" scoped>
  .cpdf {
    display: flex;
    justify-content: center;
    align-items: center;
    .center {
      text-align: center;
      height: 100%;
      overflow: hidden;
      padding-top: 20px;
      .contor {
        position: fixed;
        bottom: 30px;
        left: 0;
        width: 100%;
        z-index: 99999;
        font-size 30px
        margin-top 20px
        margin-bottom: 10px;
      }
    }
  }
</style>
复制代码

3、直接当成组件,引用就可以了

import cdpdf from '../../../components/pdf.vue'

<cdpdf :pdfurl="pdfurl"></cdpdf>
复制代码

一开始项目使用的是pdf-dist,因为后来电子签章显示不出来:

Warning: Unimplemented widget field type "Sig", falling back to base field type.
复制代码

从网上搜了很多方法,说是需要修改pdf.work.js的源码,全局搜索AnnotationFlag.HIDDEN:

if(data.fieldType==='Sig') {
    warn('unimplemented annotation type: Widget signature');
    // 注释下面这行代码
    this.setFlags(AnnotationFlag.HIDDEN);
 }
复制代码

可能是移动端使用微信浏览器的原因,注释掉代码还是不好使,只能再想其他办法了

三、pdf.js

最后用了最笨的办法,直接从GitHub拉下来pdf.js的demo,用iframe标签包住demo里的HTML文件,直接套着用,完美解决电子签章的问题:

1、从GitHub拉一下源码,或者从这个地址直接下载

https://github.com/mozilla/pdf.js

下载下来以后放在public文件下(3.x脚手架)

2、iframe标签直接粗暴的设置src

<iframe :src="pdfUrl" :style="{height: Height}" style="width: 100%"></iframe>

this.pdfUrl = '../pdf/web/viewer.html?file=' + this.pdfurl, +'PDF'

pdfUrl是iframe标签的URL,pdfurl是需要查看的PDF文件的url
复制代码

总结

只要能实现需求的代码就是好代码,我的项目是移动端查看PDF文件,因为文件上有电子签章,所以尝试了好几种方案,个人还是推荐第二种方案,如果没有电子签章的情况下。

推荐Vue学习资料文章:

基于vue + element的后台管理系统解决方案

Vue仿蘑菇街商城项目(vue+koa+mongodb)

基于 electron-vue 开发的音乐播放器「实践」

「实践」Vue项目中标配编辑器插件Vue-Quill-Editor

消息队列助你成为高薪 Node.js 工程师

Node.js 中的 stream 模块详解

「干货」Deno TCP Echo Server 是怎么运行的?

「干货」了不起的 Deno 实战教程

「干货」通俗易懂的Deno 入门教程

Deno 正式发布,彻底弄明白和 node 的区别

「实践」基于Apify+node+react/vue搭建一个有点意思的爬虫平台

「实践」深入对比 Vue 3.0 Composition API 和 React Hooks

前端网红框架的插件机制全梳理(axios、koa、redux、vuex)

深入Vue 必学高阶组件 HOC「进阶篇」

深入学习Vue的data、computed、watch来实现最精简响应式系统

10个实例小练习,快速入门熟练 Vue3 核心新特性(一)

10个实例小练习,快速入门熟练 Vue3 核心新特性(二)

教你部署搭建一个Vue-cli4+Webpack移动端框架「实践」

2020前端就业Vue框架篇「实践」

详解Vue3中 router 带来了哪些变化?

Vue项目部署及性能优化指导篇「实践」

Vue高性能渲染大数据Tree组件「实践」

尤大大细品VuePress搭建技术网站与个人博客「实践」

10个Vue开发技巧「实践」

是什么导致尤大大选择放弃Webpack?【vite 原理解析】

带你了解 vue-next(Vue 3.0)之 小试牛刀【实践】

带你了解 vue-next(Vue 3.0)之 初入茅庐【实践】

实践Vue 3.0做JSX(TSX)风格的组件开发

一篇文章教你并列比较React.js和Vue.js的语法【实践】

手拉手带你开启Vue3世界的鬼斧神工【实践】

深入浅出通过vue-cli3构建一个SSR应用程序【实践】

怎样为你的 Vue.js 单页应用提速

聊聊昨晚尤雨溪现场针对Vue3.0 Beta版本新特性知识点汇总

【新消息】Vue 3.0 Beta 版本发布,你还学的动么?

Vue真是太好了 壹万多字的Vue知识点 超详细!

Vue + Koa从零打造一个H5页面可视化编辑器——Quark-h5

深入浅出Vue3 跟着尤雨溪学 TypeScript 之 Ref 【实践】

手把手教你深入浅出vue-cli3升级vue-cli4的方法

Vue 3.0 Beta 和React 开发者分别杠上了

手把手教你用vue drag chart 实现一个可以拖动 / 缩放的图表组件

Vue3 尝鲜

总结Vue组件的通信

手把手让你成为更好的Vue.js开发人员的12个技巧和窍门【实践】

Vue 开源项目 TOP45

2020 年,Vue 受欢迎程度是否会超过 React?

尤雨溪:Vue 3.0的设计原则

使用vue实现HTML页面生成图片

实现全栈收银系统(Node+Vue)(上)

实现全栈收银系统(Node+Vue)(下)

vue引入原生高德地图

Vue合理配置WebSocket并实现群聊

多年vue项目实战经验汇总

vue之将echart封装为组件

基于 Vue 的两层吸顶踩坑总结

Vue插件总结【前端开发必备】

Vue 开发必须知道的 36 个技巧【近1W字】

构建大型 Vue.js 项目的10条建议

深入理解vue中的slot与slot-scope

手把手教你Vue解析pdf(base64)转图片【实践】

使用vue+node搭建前端异常监控系统

推荐 8 个漂亮的 vue.js 进度条组件

基于Vue实现拖拽升级(九宫格拖拽)

手摸手,带你用vue撸后台 系列二(登录权限篇)

手摸手,带你用vue撸后台 系列三(实战篇)

前端框架用vue还是react?清晰对比两者差异

Vue组件间通信几种方式,你用哪种?【实践】

浅析 React / Vue 跨端渲染原理与实现

10个Vue开发技巧助力成为更好的工程师

手把手教你Vue之父子组件间通信实践讲解【props、$ref 、$emit】

1W字长文+多图,带你了解vue的双向数据绑定源码实现

深入浅出Vue3 的响应式和以前的区别到底在哪里?【实践】

干货满满!如何优雅简洁地实现时钟翻牌器(支持JS/Vue/React)

基于Vue/VueRouter/Vuex/Axios登录路由和接口级拦截原理与实现

手把手教你D3.js 实现数据可视化极速上手到Vue应用

吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【上】

吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【中】

吃透 Vue 项目开发实践|16个方面深入前端工程化开发技巧【下】

Vue3.0权限管理实现流程【实践】

后台管理系统,前端Vue根据角色动态设置菜单栏和路由

作者:jerryWu

转发连接:https://juejin.im/post/5ec48f84e51d45788266753a

相关推荐

电脑装系统用GHOST好,还是原装版本好?老司机都是这么装的

Hello大家好,我是兼容机之家的咖啡。安装Windows系统是原版ISO好还是ghost好呢?针对这个的问题,我们先来科普一下什么是ghost系统,和原版ISO镜像两者之间有哪些优缺点。如果是很了解...

苹果 iOS 14.5.1/iPadOS 14.5.1 正式版发布

IT之家5月4日消息今日凌晨,苹果发布了iOS14.5.1与iPadOS14.5.1正式版更新。这一更新距iOS14.5正式版发布过去了一周时间。IT之家了解到,苹果表示,...

iOS 13.1.3 正式版发布 包含错误修复和改进

苹果今天发布了iOS13.1.3和iPadOS13.1.3,这是iOS13发布之后第四个升级补丁。iOS13.1.2两周前发布。iOS13.1.3主要包括针对iPad和...

还不理解 Error 和 Exception 吗,看这篇就够了

在Java中的基本理念是结构不佳的代码不能运行,发现错误的理想时期是在编译期间,因为你不用运行程序,只是凭借着对Java基本理念的理解就能发现问题。但是编译期并不能找出所有的问题,有一些N...

Linux 开发人员发现了导致 MacBook“无法启动”的 macOS 错误

“多个严重”错误影响配备ProMotion显示屏的MacBookPro。...

启动系统时无法正常启动提示\windows\system32\winload.efi

启动系统时无法正常启动提示\windows\system32\winload.efi。该怎么解决?  最近有用户遇到了开机遇到的问题,是Windows未能启动。原因可能是最近更改了硬件或软件。虽然提...

离线部署之两种构建Ragflow镜像的方式,dify同理

在实际项目交付过程中,经常遇到要离线部署的问题,生产服务器无法连接外网,这时就需要先构建好ragflow镜像,然后再拷到U盘或刻盘,下面介绍两种构建ragflow镜像的方式。性能测试(网络情况好的情况...

Go语言 error 类型详解(go语言 异常)

Go语言的error类型是用于处理程序运行中错误情况的核心机制。它通过显式的返回值(而非异常抛出)来管理错误,强调代码的可控性和清晰性。以下是详细说明及示例:一、error类型的基本概念内置接口...

Mac上“闪烁的问号”错误提示如何修复?

现在Mac电脑的用户越来越多,Mac电脑在使用过程中也会出现系统故障。当苹果电脑无法找到系统软件时,Mac会给出一个“闪烁的问号”的标志。很多用户受到过闪烁问号这一常见的错误提示的影响,如何解决这个问...

python散装笔记——177 sys 模块(python sys模块详解)

sys模块提供了访问程序运行时环境的函数和值,例如命令行参数...

30天自制操作系统:第一天(30天自制操作系统电子书)

因为咱们的目的是为了研究操作系统的组成,所以直接从系统启动的第二阶段的主引导记录开始。前提是将编译工具放在该文件目录的同级目录下,该工具为日本人川合秀实自制的编译程序,优化过的nasm编译工具。...

五大原因建议您现在不要升级iOS 13或iPadOS

今天苹果放出了iPadOS和iOS13的公测版本,任何对新版功能感兴趣的用户都可以下载安装参与测试。除非你想要率先体验Dark模式,以及使用AppleID来登陆Facebook等服务,那么外媒CN...

Python安装包总报错?这篇解决指南让你告别pip烦恼!

在Python开发中,...

苹果提供了在M1 Mac上修复macOS重装错误的方案

#AppleM1芯片#在苹果新的M1Mac推出后不久,我们看到有报道称,在这些机器上恢复和重新安装macOS,可能会导致安装错误,使你的Mac无法使用。具体来说,错误信息如下:"An...

黑苹果卡代码篇三:常见卡代码问题,满满的干货

前言...

取消回复欢迎 发表评论: