您好,登錄后才能下訂單哦!
今天小編給大家分享一下Vue怎么使用pdf-lib實現為文件流添加水印并預覽的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
首先預覽pdf就很簡單了,我們只需要通過window.URL.createObjectURL(new Blob(file))轉為一個路徑fileSrc后,再通過window.open(fileSrc)就可以了,window.open方法第二個參數默認就是打開一個新頁簽,這樣就可以直接預覽了,很方便!就是下面這樣子:
并且右上角自動給我們提供了下載、打印等功能。
但是要加上水印的話,可能會稍微復雜一點點,我也百度找了好多,發現好多都是在項目里直接預覽的,也就是在當前頁面或者一個div有個容器用來專門預覽pdf的,然后水印的話也是appendChild到容器div中進行的。這不是我想要的,并且也跟我現在預覽的方式不一樣,所以我的思路就是如何給文件的那個二進制blob流上加上水印,這樣預覽的時候也是用這個文件流,以后不想預覽了、直接下載也要水印也是很方便的。找來找去找到了pdf-lib庫,然后就去https://www.npmjs.com/package/pdf-lib這里去看了下使用示例,看了兩個例子,發現好像這個很合適哦,終于一波操作拿下了,這就是我想要的。
我這里添加水印共三種方式,第一種就是可以直接傳入文本,將文本添加進去作為水印 ;第二種是將圖片的ArrayBuffer傳遞進去,將圖片作為水印;因為第一種方式直接傳文本只能傳英文,我傳入漢字就報錯了,npm官網好像也有寫,這是不可避免的,所以才有了第三種方式,就是也是傳入文本,不過我們通過canvas畫出來,然后通過toDataURL轉為base64路徑,然后再通過XHR去加載該圖片拿到圖片的Blob,再調用Blob的arrayBuffer方法拿到buffer傳遞進去作為水印,其實第三種和第二種都是圖片的形式,第三種會更靈活一些。下面上代碼
npm i pdf-lib
//我的需求里只用到這么多就夠了,其他的按需引入 import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib';
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib'; // This should be a Uint8Array or ArrayBuffer // This data can be obtained in a number of different ways // If your running in a Node environment, you could use fs.readFile() // In the browser, you could make a fetch() call and use res.arrayBuffer() const existingPdfBytes = ... // Load a PDFDocument from the existing PDF bytes const pdfDoc = await PDFDocument.load(existingPdfBytes) // Embed the Helvetica font const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica) // Get the first page of the document const pages = pdfDoc.getPages() const firstPage = pages[0] // Get the width and height of the first page const { width, height } = firstPage.getSize() // Draw a string of text diagonally across the first page firstPage.drawText('This text was added with JavaScript!', { x: 5, y: height / 2 + 300, size: 50, font: helveticaFont, color: rgb(0.95, 0.1, 0.1), rotate: degrees(-45), }) // Serialize the PDFDocument to bytes (a Uint8Array) const pdfBytes = await pdfDoc.save() // For example, `pdfBytes` can be: // ? Written to a file in Node // ? Downloaded from the browser // ? Rendered in an <iframe>
import { PDFDocument } from 'pdf-lib' // These should be Uint8Arrays or ArrayBuffers // This data can be obtained in a number of different ways // If your running in a Node environment, you could use fs.readFile() // In the browser, you could make a fetch() call and use res.arrayBuffer() const jpgImageBytes = ... const pngImageBytes = ... // Create a new PDFDocument const pdfDoc = await PDFDocument.create() // Embed the JPG image bytes and PNG image bytes const jpgImage = await pdfDoc.embedJpg(jpgImageBytes) const pngImage = await pdfDoc.embedPng(pngImageBytes) // Get the width/height of the JPG image scaled down to 25% of its original size const jpgDims = jpgImage.scale(0.25) // Get the width/height of the PNG image scaled down to 50% of its original size const pngDims = pngImage.scale(0.5) // Add a blank page to the document const page = pdfDoc.addPage() // Draw the JPG image in the center of the page page.drawImage(jpgImage, { x: page.getWidth() / 2 - jpgDims.width / 2, y: page.getHeight() / 2 - jpgDims.height / 2, width: jpgDims.width, height: jpgDims.height, }) // Draw the PNG image near the lower right corner of the JPG image page.drawImage(pngImage, { x: page.getWidth() / 2 - pngDims.width / 2 + 75, y: page.getHeight() / 2 - pngDims.height, width: pngDims.width, height: pngDims.height, }) // Serialize the PDFDocument to bytes (a Uint8Array) const pdfBytes = await pdfDoc.save() // For example, `pdfBytes` can be: // ? Written to a file in Node // ? Downloaded from the browser // ? Rendered in an <iframe>
canvas那個也是用的這個這個通過圖片添加水印
上面這些都是官網上給的一些示例,我當時看到上面這兩個例子,靈感瞬間就來了,然后測試,測試成功沒問題,就開始整理代碼,封裝。結合自己的業務需求和可以復用通用的思想進行封裝。下面貼一下最終的成功
import { degrees, PDFDocument, rgb, StandardFonts } from 'pdf-lib'; /** * 瀏覽器打開新頁簽預覽pdf * blob(必選): pdf文件信息(Blob對象)【Blob】 * docTitle(可選): 瀏覽器打開新頁簽的title 【String】 * isAddWatermark(可選,默認為false): 是否需要添加水印 【Boolean】 * watermark(必選):水印信息 【Object: { type: string, text: string, image:{ bytes: ArrayBuffer, imageType: string } }】 * watermark.type(可選):類型 可選值:text、image、canvas * watermark.text(watermark.type為image時不填,否則必填):水印文本。注意:如果watermark.type值為text,text取值僅支持拉丁字母中的218個字符。詳見:https://www.npmjs.com/package/pdf-lib * watermark.image(watermark.type為image時必填,否則不填):水印圖片 * watermark.image.bytes:圖片ArrayBuffer * watermark.image.imageType:圖片類型。可選值:png、jpg * Edit By WFT */ export default class PreviewPdf { constructor({ blob, docTitle, isAddWatermark = false, watermark: { type = 'text', text = 'WFT', image } }) { const _self = this if(!blob) { return console.error('[PDF Blob Is a required parameter]') } if(!isAddWatermark) { // 不添加水印 _self.preView(blob, docTitle) } else { let bytes,imageType if(type == 'image') { if(!image) { return console.error('["image" Is a required parameter]') } bytes = image.bytes imageType = image.imageType } const map = { 'text': _self.addTextWatermark.bind(_self), 'image': _self.addImageWatermark.bind(_self), 'canvas': _self.addCanvasWatermark.bind(_self) } blob.arrayBuffer().then(async buffer => { const existingPdfBytes = buffer const pdfDoc = await PDFDocument.load(existingPdfBytes) let params if(type == 'text') params = { pdfDoc, text, docTitle } if(type == 'image') params = { pdfDoc, bytes, imageType, docTitle } if(type == 'canvas') params = { pdfDoc, text, docTitle } map[type](params) }).catch(e => console.error('[Preview Pdf Error]:', e)) } } // 添加 Text 水印 async addTextWatermark({ pdfDoc, text, docTitle }) { // console.log(StandardFonts, 'StandardFonts-->>') // 字體 const helveticaFont = await pdfDoc.embedFont(StandardFonts.Helvetica) const pages = pdfDoc.getPages() for(let i = 0; i < pages.length; i++) { let page = pages[i] let { width, height } = page.getSize() for(let i = 0; i < 6; i++) { for(let j = 0; j < 6; j++) { page.drawText(text, { x: j * 100, y: height / 5 + i * 100, size: 30, font: helveticaFont, color: rgb(0.95, 0.1, 0.1), opacity: 0.2, rotate: degrees(-35), }) } } } // 序列化為字節 const pdfBytes = await pdfDoc.save() this.preView(pdfBytes, docTitle) } // 添加 image 水印 async addImageWatermark({ pdfDoc, bytes, imageType, docTitle }) { // 嵌入JPG圖像字節和PNG圖像字節 let image const maps = { 'jpg': pdfDoc.embedJpg.bind(pdfDoc), 'png': pdfDoc.embedPng.bind(pdfDoc) } image = await maps[imageType](bytes) // 將JPG圖像的寬度/高度縮小到原始大小的50% const dims = image.scale(0.5) const pages = pdfDoc.getPages() for(let i = 0; i < pages.length; i++) { let page = pages[i] let { width, height } = page.getSize() for(let i = 0; i < 6; i++) { for(let j = 0; j < 6; j++) { page.drawImage(image, { x: width / 5 - dims.width / 2 + j * 100, y: height / 5 - dims.height / 2 + i * 100, width: dims.width, height: dims.height, rotate: degrees(-35) }) } } } // 序列化為字節 const pdfBytes = await pdfDoc.save() this.preView(pdfBytes, docTitle) } // 添加 canvas 水印 addCanvasWatermark({ pdfDoc, text, docTitle }) { // 旋轉角度大小 const rotateAngle = Math.PI / 6; // labels是要顯示的水印文字,垂直排列 let labels = new Array(); labels.push(text); const pages = pdfDoc.getPages() const size = pages[0].getSize() let pageWidth = size.width let pageHeight = size.height let canvas = document.createElement('canvas'); let canvasWidth = canvas.width = pageWidth; let canvasHeight = canvas.height = pageHeight; const context = canvas.getContext('2d'); context.font = "15px Arial"; // 先平移到畫布中心 context.translate(pageWidth / 2, pageHeight / 2 - 250); // 在繞畫布逆方向旋轉30度 context.rotate(-rotateAngle); // 在還原畫布的坐標中心 context.translate(-pageWidth / 2, -pageHeight / 2); // 獲取文本的最大長度 let textWidth = Math.max(...labels.map(item => context.measureText(item).width)); let lineHeight = 15, fontHeight = 12, positionY, i i = 0, positionY = 0 while (positionY <= pageHeight) { positionY = positionY + lineHeight * 5 i++ } canvasWidth += Math.sin(rotateAngle) * (positionY + i * fontHeight) // 給canvas加上畫布向左偏移的最大距離 canvasHeight = 2 * canvasHeight for (positionY = 0, i = 0; positionY <= canvasHeight; positionY = positionY + lineHeight * 5) { // 進行畫布偏移是為了讓畫布旋轉之后水印能夠左對齊; context.translate(-(Math.sin(rotateAngle) * (positionY + i * fontHeight)), 0); for (let positionX = 0; positionX < canvasWidth; positionX += 2 * textWidth) { let spacing = 0; labels.forEach(item => { context.fillText(item, positionX, positionY + spacing); context.fillStyle = 'rgba(187, 187, 187, .8)'; // 字體顏色 spacing = spacing + lineHeight; }) } context.translate(Math.sin(rotateAngle) * (positionY + i * fontHeight), 0); context.restore(); i++ } // 圖片的base64編碼路徑 let dataUrl = canvas.toDataURL('image/png'); // 使用Xhr請求獲取圖片Blob let xhr = new XMLHttpRequest(); xhr.open("get", dataUrl, true); xhr.responseType = "blob"; xhr.onload = res => { const imgBlob = res.target.response // 獲取Blob圖片Buffer imgBlob.arrayBuffer().then(async buffer => { const pngImage = await pdfDoc.embedPng(buffer) for(let i = 0; i < pages.length; i++) { pages[i].drawImage(pngImage) } // 序列化為字節 const pdfBytes = await pdfDoc.save() this.preView(pdfBytes, docTitle) }) } xhr.send(); } // 預覽 preView(stream, docTitle) { const URL = window.URL || window.webkitURL; const href = URL.createObjectURL(new Blob([stream], { type: 'application/pdf;charset=utf-8' })) const wo = window.open(href) // 設置新打開的頁簽 document title let timer = setInterval(() => { if(wo.closed) { clearInterval(timer) } else { wo.document.title = docTitle } }, 500) } }
我這里將上面文件放在src/utils下
3.4.1 預覽(添加文本水印)
代碼:
// 引入 import PreviewPdf from '@/utils/previewPdf' // script // 實例化進行添加水印 并預覽 // file.raw 是要預覽的pdf文件流 Blob new PreviewPdf({ blob: file.raw, docTitle: 'window.open docTitle', isAddWatermark: true, // 是否需要添加水印 watermark: { // watermark必填 里面可以不填 type: 'text', text: 'WFT' } })
效果:
3.4.2 預覽(添加圖片水印)
代碼:
// 引入 import PreviewPdf from '@/utils/previewPdf' // script const watermarkImage = require('@/assets/img/watermark.png') // 水印圖片 let xhr = new XMLHttpRequest(); xhr.open("get", watermarkImage, true); xhr.responseType = "blob"; xhr.onload = function (res) { const imgBlob = res.target.response // 水印圖片的Blob流 imgBlob.arrayBuffer().then(buffer => { //get arraybuffer // 添加水印 預覽 new PreviewPdf({ blob: file.raw, docTitle: file.name, isAddWatermark: true, watermark: { type: 'image', image: { bytes: buffer, imageType: 'png' } } }) }) } xhr.send();
效果:
3.4.3 預覽(添加文本canvas繪制水印)
代碼:
// 引入 import PreviewPdf from '@/utils/previewPdf' // script new PreviewPdf({ blob: file.raw, docTitle: file.name, isAddWatermark: true, watermark: { type: 'canvas', text: 'WFT-CANVAS' } })
效果:
因為有些樣式調的不太好,就我目前寫的我更偏向使用canvas這個,當然都是可以使用的,樣式都是可以調整的。
注意:里面的屬性 isAddWatermark 設置為false或者不傳該字段將不添加水印,還有watermark這個字段是必須的,穿個空對象也行像watermark:{}這樣,因為我上面類中構造方法將參數結構了,可以按需調整。
整體的封裝使用就是上面這樣子了, 希望可以幫到有需要的伙伴~~~
再給大家一個直接往某個dom元素里面添加水印的方法
不傳參數默認為整個body添加水印
function waterMark(text = 'WFT', dom = document.body) { if (document.getElementById('waterMark')) return // 旋轉角度大小 var rotateAngle = Math.PI / 6; // labels是要顯示的水印文字,垂直排列 var labels = new Array(); labels.push(text); let pageWidth = dom.clientWidth let pageHeight = dom.clientHeight let canvas = document.createElement('canvas'); let canvasWidth = canvas.width = pageWidth; let canvasHeight = canvas.height = pageHeight; var context = canvas.getContext('2d'); context.font = "15px Arial"; // 先平移到畫布中心 context.translate(pageWidth / 2, pageHeight / 2 - 250); // 在繞畫布逆方向旋轉30度 context.rotate(-rotateAngle); // 在還原畫布的坐標中心 context.translate(-pageWidth / 2, -pageHeight / 2); // 獲取文本的最大長度 let textWidth = Math.max(...labels.map(item => context.measureText(item).width)); let lineHeight = 15, fontHeight = 12, positionY, i i = 0, positionY = 0 while (positionY <= pageHeight) { positionY = positionY + lineHeight * 5 i++ } canvasWidth += Math.sin(rotateAngle) * (positionY + i * fontHeight) // 給canvas加上畫布向左偏移的最大距離 canvasHeight = 2 * canvasHeight for (positionY = 0, i = 0; positionY <= canvasHeight; positionY = positionY + lineHeight * 5) { // 進行畫布偏移是為了讓畫布旋轉之后水印能夠左對齊; context.translate(-(Math.sin(rotateAngle) * (positionY + i * fontHeight)), 0); for (let positionX = 0; positionX < canvasWidth; positionX += 2 * textWidth) { let spacing = 0; labels.forEach(item => { context.fillText(item, positionX, positionY + spacing); spacing = spacing + lineHeight; }) } context.translate(Math.sin(rotateAngle) * (positionY + i * fontHeight), 0); context.restore(); i++ } let dataUrl = canvas.toDataURL('image/png'); let waterMarkPage = document.createElement('div'); waterMarkPage.id = "waterMark" let style = waterMarkPage.style; style.position = 'fixed'; style.overflow = "hidden"; style.left = 0; style.top = 0; style.opacity = '0.4'; style.background = "url(" + dataUrl + ")"; style.zIndex = 999; style.pointerEvents = "none"; style.width = '100%'; style.height = '100vh'; dom.appendChild(waterMarkPage); }
以上就是“Vue怎么使用pdf-lib實現為文件流添加水印并預覽”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。