vue 下载网页为pdf格式

该文章展示了在Vue项目中如何利用html2canvas将HTML内容转化为canvas,然后用jspdf将canvas转换为PDF文件进行下载。用户点击‘邀请函管理’后,弹出对话框显示邀请函内容,点击‘下载’按钮即可下载为PDF。
在vue的项目中,先安装需要用到的两个依赖分别是html2canvas和jspdf

①npm install --save html2canvas
 
②npm install jspdf --save


下载功能
<template>
  <div class="policy">
    <a @click="getInviteLetter" style="color: #199aba;margin-left: 10px;cursor: pointer;">邀请函管理</a>
    <el-dialog  :visible.sync="dialogLetterVisible" width="50%" :before-close="handleClose" :title="'邀请函'">
      <el-form class="demo-ruleForm" >
        <el-form-item label="" prop="title" id="iframeId">
          <div class="yqhback">
            <img src="/static/img/yqhback.png" width="100%" alt="">
            <div class="yqhcontent">
              <div class="yqhtitle">
                <img src="/static/img/yqh.png"/>
              </div>
              <div class="yqhtext">
                <div style="text-indent: 2rem">{{yqhInfo.content}}</div>
                <div class="timeaddress">
                  <p>活动时间:{{yqhInfo.hdtime}}</p>
                  <p>活动地点:{{yqhInfo.address}}</p>
                </div>
              </div>
              <div>
                <div class="yqhjc">
                  <img src="/static/img/jc.png">
                </div>
                <div class="activity_time" v-for="item in yqhInfo.active_timearr">
                  <p>{{item.time}}</p>
                  <ul >
                    <li v-for="item1 in item.arr">{{item1.start}}-{{item1.end}}&nbsp &nbsp &nbsp {{item1.content}}</li>
                  </ul>
                </div>
              </div>
            </div>
          </div>
        </el-form-item>
        <div class="text-right">
          <el-button @click="downLoad('ruleForm1')">下载</el-button>
          <el-button type="primary"  @click="cancle('ruleForm1')">取消</el-button>
        </div>
      </el-form>
    </el-dialog>
  </div>
</template>

<script>
import { downloadPDF } from '@/utils/pdf.js'
export default {
  computed: {
  },
  data () {
    return {
      dialogLetterVisible:false,
      yqhInfo:{
        address:'',
        content:'',
        lk_main:'',
        lk_time:'',
        yq_title:'',
        active_timearr:[],
        hdtime:'',
      },
    }
  },
  created: function () {
  },
  methods: {
    back(){
      this.$router.push({ path: '/science/index?aside=33'+'&detailId='+this.detailId })
    },
    // 点击下载PDF
    downLoad() {
      downloadPDF(document.getElementById('iframeId'))
    },
    cancle(){
      this.dialogLetterVisible = false
    },
    getInviteLetter(){
      this.dialogLetterVisible = true
    },
  },
  mounted () {
  },

}
</script>

pdf.js

import html2canvas from "html2canvas";
import jsPDF from "jspdf";

export const downloadPDF = page => {
  html2canvas(page).then(function(canvas) {
    canvas2PDF(canvas);
  });
};

const canvas2PDF = canvas => {
  let contentWidth = canvas.width;
  let contentHeight = canvas.height;

  let pageHeight = contentWidth / 592.28 * 841.89
  let leftHeight = contentHeight
  let position = 0
  let imgWidth = 595.28
  let imgHeight = 592.28 / contentWidth * contentHeight

  let pageData = canvas.toDataURL('image/jpeg', 1.0)
  // eslint-disable-next-line new-cap
  let pdf = new jsPDF('', 'pt', 'a4')
  if (leftHeight < pageHeight) {
    pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
  } else {
    while (leftHeight > 0) {
      pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
      leftHeight -= pageHeight
      position -= 841.89
      if (leftHeight > 0) {
        pdf.addPage()
      }
    }
  }

  //导出文件的名字,可以自定义
  pdf.save("邀请函.pdf");
};

<think>我们面临的需求是在Vue2项目中实现PDF标注功能,并支持下载标注后的PDF文件。 根据引用[1]和引用[2],我们可以使用vue-pdf-embed(注意引用[1]是Vue3的示例,但vue-pdf-embed也支持Vue2)或vue-pdf(引用[2])来显示PDF。 但是,标注功能需要更高级的操作,可能需要一个能够支持标注的库。另外,下载标注后的PDF需要将标注内容合并到原始PDF中并生成新的PDF文件。 考虑到标注功能,我们可以使用一些专门的PDF标注库。在Vue2中,一个常见的选择是使用pdf.js(Mozilla的开源库)并结合一些注解库,比如annotator.pdf(但注意annotator.pdf可能已经过时)或者使用pdf-lib来操作PDF。 然而,引用[3]提到了使用JSPDF下载PDF,但JSPDF主要用于从HTML生成PDF,并不适合直接处理已有的PDF文档的标注。 因此,我们可以考虑以下方案: 1. 使用pdf.js(通过vue-pdfvue-pdf-embed)来渲染PDF,并添加一个标注层(例如使用fabric.js或者直接使用HTML元素来绘制标注)。 2. 使用pdf-lib(一个强大的PDF操作库)来将标注(如矩形、文本等)写入原始PDF,然后生成新的PDF文件供下载。 但是,标注和保存是一个复杂的过程。我们需要: - 在PDF页面上叠加一个画布(canvas)用于绘制标注。 - 记录标注的位置和内容。 - 使用pdf-lib将标注绘制到原始PDF的对应位置上。 由于整个过程较为复杂,这里我们提供一个基本思路和关键步骤。 步骤: 1.Vue2项目中安装必要的依赖: - vue-pdf-embed(或vue-pdf,但vue-pdf-embed功能更全面,支持注解层)注意:vue-pdf-embed在Vue2中也可以使用,但需要安装兼容版本。 - pdf-lib(用于操作PDF文档) - 可能还需要fabric.js用于绘制标注(或者使用简单的HTML5 canvas) 2. 在组件中引入vue-pdf-embed并显示PDF。 3.PDF渲染区域上方叠加一个canvas,用于绘制标注。注意需要根据PDF页面的大小和位置调整canvas。 4. 实现标注的绘制(例如,选择矩形、箭头、文本等)。 5. 记录每个标注的位置(相对于PDF页面的坐标)和样式。 6. 当用户点击下载时,使用pdf-lib加载原始PDF,然后根据记录的标注信息,使用pdf-lib的API在PDF的每一页上绘制相应的图形和文本。 7. 将修改后的PDF保存为文件,并提供下载。 由于实现完整的标注功能非常复杂,这里我们只能给出一个简化的示例,重点在如何将标注信息添加到PDF下载。 安装依赖: ```bash npm install vue-pdf-embed@1.3.0 # 注意:vue-pdf-embed的1.x版本支持Vue2,2.x支持Vue3 npm install pdf-lib npm install fabric # 可选,如果使用fabric.js作为绘图工具 ``` 组件示例(简化版): ```vue <template> <div> <div style="position: relative;"> <vue-pdf-embed :source="pdfSource" @rendered="handlePdfRender" /> <canvas ref="annotationCanvas" style="position: absolute; top: 0; left: 0; pointer-events: none;" ></canvas> </div> <button @click="downloadAnnotatedPdf">下载标注后的PDF</button> </div> </template> <script> import VuePdfEmbed from 'vue-pdf-embed' import { PDFDocument, rgb } from 'pdf-lib' import { fabric } from 'fabric' // 如果使用fabric export default { components: { VuePdfEmbed }, data() { return { pdfSource: 'example.pdf', // PDF文件路径 canvas: null, fabricCanvas: null, // fabric的canvas实例 annotations: [] // 存储标注信息 } }, mounted() { // 初始化fabric canvas,但注意需要在PDF渲染完成后才能确定大小 }, methods: { handlePdfRender(rendering) { // 当PDF渲染完成后,我们可以获取到页面的大小和位置 const page = rendering.page const { width, height } = page.viewport // 获取我们自己的canvas元素 const canvas = this.$refs.annotationCanvas canvas.width = width canvas.height = height // 初始化fabric canvas this.fabricCanvas = new fabric.Canvas(canvas, { isDrawingMode: false, // 根据需要设置 selectable: true, selection: false }) // 监听fabric的事件来记录标注 this.fabricCanvas.on('object:added', (e) => { // 记录标注 this.annotations.push(e.target) }) }, async downloadAnnotatedPdf() { // 加载原始PDF const response = await fetch(this.pdfSource) const pdfBytes = await response.arrayBuffer() // 使用pdf-lib加载PDF const pdfDoc = await PDFDocument.load(pdfBytes) const pages = pdfDoc.getPages() // 假设我们只有一个页面,且标注也在第一页 // 实际中需要根据多页处理 const page = pages[0] const { width, height } = page.getSize() // 获取fabric canvas上的所有对象 const objects = this.fabricCanvas.getObjects() // 遍历每个对象,将其绘制到PDF页面上 objects.forEach(obj => { if (obj.type === 'rect') { // 注意:fabric的坐标原点在左上角,而pdf-lib的坐标原点在左下角 // 因此需要换y坐标:pdfHeight - (obj.top + obj.height) page.drawRectangle({ x: obj.left, y: height - (obj.top + obj.height), // 翻y坐标 width: obj.width, height: obj.height, borderWidth: 1, borderColor: rgb(1, 0, 0), color: rgb(0, 0, 0, 0) // 透明填充 }) } // 可以添加其他类型的绘制,如文本、箭头等 }) // 保存修改后的PDF const modifiedPdfBytes = await pdfDoc.save() // 下载 const blob = new Blob([modifiedPdfBytes], { type: 'application/pdf' }) const link = document.createElement('a') link.href = URL.createObjectURL(blob) link.download = 'annotated.pdf' link.click() } } } </script> ``` 注意: 1. 上述示例仅处理了一个页面的矩形标注,且坐标换可能不准确(需要根据实际渲染的尺寸调整)。 2. 实际应用中,PDF的渲染尺寸和pdf-lib中的页面尺寸可能不同,需要根据比例进行换。 3. 标注的坐标换是一个复杂的问题,因为PDF渲染时可能有缩放,而pdf-lib操作的是原始PDF尺寸。 4.页面处理需要更复杂的逻辑。 因此,完整的标注功能实现起来非常复杂,可能需要一个成熟的库。如果项目要求高,可以考虑使用现成的商业库,如PSPDFKit、PDFTron等。 相关问题: 1. 如何在Vue2中调整PDF的渲染尺寸? 2. 如何实现多种标注类型(如高亮、下划线、箭头等)? 3. 如何保存和加载标注数据(以便下次打开PDF时显示)? 4. 如何支持多页PDF的标注? 5. 如何解决坐标换问题,确保标注在PDF中的位置准确?</think>在 Vue2 中实现 PDF 标注功能并支持下载标注后的文件,可以通过以下方案实现: ### 完整解决方案 #### 1. 安装依赖 ```bash npm install vue-pdf-embed@1.3.0 # Vue2兼容版本 npm install pdf-lib # 用于PDF操作 npm install file-saver # 文件下载 ``` #### 2. 组件实现 ```vue <template> <div> <!-- PDF显示区域 --> <div class="pdf-container" ref="pdfContainer"> <vue-pdf-embed :source="pdfSource" ref="pdfViewer" @rendered="onPageRender" /> </div> <!-- 标注工具栏 --> <div class="toolbar"> <button @click="addTextAnnotation">添加文字</button> <button @click="addRectangle">添加矩形</button> <button @click="downloadAnnotatedPdf">下载标注文件</button> </div> </div> </template> <script> import VuePdfEmbed from 'vue-pdf-embed' import { PDFDocument, rgb } from 'pdf-lib' import { saveAs } from 'file-saver' export default { components: { VuePdfEmbed }, data() { return { pdfSource: '/sample.pdf', // PDF文件路径 annotations: [], // 存储标注数据 currentPage: 1 } }, mounted() { this.initCanvas() }, methods: { // 初始化标注画布 initCanvas() { const container = this.$refs.pdfContainer this.canvas = document.createElement('canvas') container.appendChild(this.canvas) this.ctx = this.canvas.getContext('2d') // 监听鼠标事件 this.canvas.addEventListener('mousedown', this.startDrawing) this.canvas.addEventListener('mousemove', this.draw) this.canvas.addEventListener('mouseup', this.stopDrawing) }, // 当PDF页面渲染时更新画布尺寸 onPageRender() { const page = this.$refs.pdfViewer.page const { width, height } = page.viewport this.canvas.width = width this.canvas.height = height this.canvas.style.width = `${width}px` this.canvas.style.height = `${height}px` this.redrawAnnotations() }, // 添加文字标注 addTextAnnotation() { this.annotations.push({ type: 'text', content: '新标注', x: 100, y: 150, color: '#FF0000' }) this.redrawAnnotations() }, // 添加矩形标注 addRectangle() { this.annotations.push({ type: 'rect', x: 50, y: 50, width: 200, height: 100, color: '#0000FF' }) this.redrawAnnotations() }, // 重绘所有标注 redrawAnnotations() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) this.annotations.forEach(anno => { this.ctx.fillStyle = anno.color if (anno.type === 'text') { this.ctx.font = '16px Arial' this.ctx.fillText(anno.content, anno.x, anno.y) } else if (anno.type === 'rect') { this.ctx.strokeStyle = anno.color this.ctx.lineWidth = 2 this.ctx.strokeRect(anno.x, anno.y, anno.width, anno.height) } }) }, // 下载标注后的PDF async downloadAnnotatedPdf() { try { // 1. 加载原始PDF const response = await fetch(this.pdfSource) const pdfBytes = await response.arrayBuffer() // 2. 使用pdf-lib修改PDF const pdfDoc = await PDFDocument.load(pdfBytes) const pages = pdfDoc.getPages() const firstPage = pages[0] // 3. 添加标注到PDF this.annotations.forEach(anno => { if (anno.type === 'text') { firstPage.drawText(anno.content, { x: anno.x, y: firstPage.getHeight() - anno.y, // 坐标换 size: 16, color: rgb(1, 0, 0) // 红色文本 }) } else if (anno.type === 'rect') { firstPage.drawRectangle({ x: anno.x, y: firstPage.getHeight() - anno.y - anno.height, width: anno.width, height: anno.height, borderWidth: 2, borderColor: rgb(0, 0, 1) // 蓝色边框 }) } }) // 4. 生成并下载PDF const modifiedPdf = await pdfDoc.save() const blob = new Blob([modifiedPdf], { type: 'application/pdf' }) saveAs(blob, 'annotated-document.pdf') } catch (error) { console.error('下载失败:', error) } }, // 绘图相关方法 startDrawing(e) { /* 实现绘图逻辑 */ }, draw(e) { /* 实现绘图逻辑 */ }, stopDrawing() { /* 实现绘图逻辑 */ } } } </script> <style> .pdf-container { position: relative; } canvas { position: absolute; top: 0; left: 0; pointer-events: auto; /* 允许交互 */ } .toolbar { margin-top: 20px; } </style> ``` ### 关键实现说明 1. **PDF显示**: - 使用 `vue-pdf-embed` 显示 PDF 文件[^1] - 监听 `@rendered` 事件获取页面尺寸信息 2. **标注系统**: - 在 PDF 上方叠加透明 Canvas 层 - 实现绘图工具(文本、矩形等) - 存储标注数据在 `annotations` 数组中 3. **PDF处理**: - 使用 `pdf-lib` 操作 PDF 内容[^3] - 将标注换为 PDF 原生元素(文本、形状) - 注意坐标系统换(Canvas y轴向下,PDF y轴向上) 4. **文件下载**: - 使用 `file-saver` 生成下载文件 - 生成包含标注的新 PDF 文件 ### 注意事项 1. **坐标换**: - PDF 坐标系原点在左下角,Canvas 在左上角 - 需要换 y 坐标:$y_{pdf} = pageHeight - y_{canvas} - height$ 2. **多页支持**: - 需要为每页维护独立的标注数据 - 实现分页渲染逻辑 3. **性能优化**: - 对于大型 PDF 使用按需渲染 - 限制历史记录数量 4. **标注持久化**: - 可将 `annotations` 保存到 IndexedDB - 或通过 API 发送到服务器存储
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值