前端導出PDF(適配ios Safari瀏覽器)

目前市面上常用的前端導出PDF庫組合一般為:

1. html2canvas + js-pdf

2. html2canvas+pdf-lib

3. domtoimage+pdf-lib

因本人項目中導出pdf需求為導出30頁及以上的多頁pdf,考慮性能問題,選擇了?html2canvas+pdf-lib 及domtoimage+pdf-lib兩種方式嘗試實現

html2canvas+pdf-lib(個人推薦,因為適配ios Safari瀏覽器)

? ? ?本人是先嘗試使用的domtoimage+pdf-lib方案,但實測中發現H5在ios Safari瀏覽器端倒不出來故有個html2canvas+pdf-lib方案,經實戰測試該方案能夠適配ios Safari瀏覽器導出!!!

代碼如下:

首先引入必要插件:

yarn add pdf-lib
yarn add html2canvas
yarn add file-saver 

file-saver 插件很重要,使用a.click方案導出的pdf在Safari中不是直接下載,而是打開一個類似預覽頁的頁面查看pdf,需用戶分享導出,比較麻煩。

async downloadPDF() {// 創建一個新的 PDF 文檔const pdfDoc = await PDFDocument.create();// 處理需轉pdf的dom的id數組const pdfDoms = await this.handlePDFPageDom();this.allNum = pdfDoms.length;for (let i = 0; i < pdfDoms.length; i++) {this.loadText = `文件生成中${i + 1}/${this.allNum}`;const doc = document.querySelector("#" + pdfDoms[i]);const canvas = await html2canvas(doc, {scale: 2, // 提高清晰度,控制內存useCORS: true,});const imgDataUrl = canvas.toDataURL("image/jpeg", 0.95); // 壓縮圖像const imgBytes = await fetch(imgDataUrl).then((res) =>res.arrayBuffer());const img = await pdfDoc.embedJpg(imgBytes);// const { width, height } = img.scaleToFit(595.28, 841.89);const A4_WIDTH = 595.28; // A4 寬度const A4_HEIGHT = 841.89; // A4 高度const scale = Math.min(A4_WIDTH / img.width, A4_HEIGHT / img.height);const scaledWidth = img.width * scale;const scaledHeight = img.height * scale;const xOffset = (A4_WIDTH - scaledWidth) / 2;const yOffset = (A4_HEIGHT - scaledHeight) / 2;const page = pdfDoc.addPage([595.28, 841.89]);page.drawImage(img, {x: xOffset,y: yOffset,width: scaledWidth,height: scaledHeight,});canvas.remove();await new Promise((resolve) => setTimeout(resolve, 100)); // 防止卡死}const pdfBytes = await pdfDoc.save();const blob = new Blob([pdfBytes], {type: "application/octet-stream",});FileSaver.saveAs(blob, `導出的PDF.pdf`);uni.hideLoading();this.loadText = "文件生成成功!";},

domtoimage+pdf-lib

async downloadPDF() {this.loadText = "文件生成中...";// 創建一個新的 PDF 文檔const pdfDoc = await PDFDocument.create();// 處理需轉pdf的dom idconst pdfDoms = await this.handlePDFPageDom();let pdfPage = [];let base64Arr = [];for (let i = 0; i < pdfDoms.length; i++) {const element = document.getElementById(pdfDoms[i]);const url = await domtoimage.toPng(element, {quality: 0.95,skipFonts: true,});base64Arr.push({ base64: url });}await base64Arr.map((item, index) => {pdfDoc.addPage([595.28, 841.89]);pdfPage.push(this.handleReportView(item.base64, index, pdfDoc));});await Promise.all(pdfPage).then(async (res) => {// 將 PDF 文檔保存為 Uint8Arrayconst pdfBytes = await pdfDoc.save();// 生成下載鏈接并自動下載 PDFconst blob = new Blob([pdfBytes], { type: "application/pdf" });const link = document.createElement("a");link.href = URL.createObjectURL(blob);link.download = `${this.studentName}.pdf`;link.click();URL.revokeObjectURL(link.href);uni.hideLoading();this.loadText = "文件生成成功!";setTimeout(() => {window.parent.postMessage({cmd: "success",});}, 1000);}).catch((err) => {// PDF = null;console.log("生成失敗", err);});},
async handleReportView(imgBase64, index, pdfDoc) {const A4_WIDTH = 595.28; // A4 寬度const A4_HEIGHT = 841.89; // A4 高度// 獲取所有頁面const pages = pdfDoc.getPages();// 修改第index頁(索引從0開始)const pageNow = pages[index];return await new Promise(async (resolve, reject) => {const pageData = imgBase64;// setTimeout(() => {let img = new Image();img.crossOrigin = "Anonymous";img.onload = async () => {const imgBytes = await fetch(pageData).then((res) =>res.arrayBuffer());// 嵌入 PNG 圖片const pngImage = await pdfDoc.embedPng(imgBytes);const { width: imgWidth, height: imgHeight } = img;// 計算縮放比例,確保圖片適應 A4 頁面并保持寬高比const scale = Math.min(A4_WIDTH / imgWidth, A4_HEIGHT / imgHeight);const scaledWidth = imgWidth * scale;const scaledHeight = imgHeight * scale;img.width = scaledWidth;img.height = scaledHeight;// 計算圖片的偏移量,使其居中顯示在頁面上const xOffset = (A4_WIDTH - scaledWidth) / 2;const yOffset = (A4_HEIGHT - scaledHeight) / 2;// 將內容設置到第幾頁await pageNow.drawImage(pngImage, {x: xOffset,y: yOffset,width: scaledWidth,height: scaledHeight,});resolve();};img.onerror = () => {alert("資源加載失敗");resolve();};img.src = pageData;// }, 500);}).catch((err) => {return Promise.resolve();});},

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/86832.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/86832.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/86832.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

physicsnemo開源程序是開源深度學習框架,用于使用最先進的 Physics-ML 方法構建、訓練和微調深度學習模型

?一、軟件介紹 文末提供程序和源碼下載 NVIDIA PhysicsNeMo 是一個開源深度學習框架&#xff0c;用于使用最先進的 SciML 方法構建、訓練、微調和推理物理 AI 模型&#xff0c;以實現 AI4 科學和工程。PhysicsNeMo 提供 python 模塊來構建可擴展和優化的訓練和推理管道&#…

JDBC接口開發指南

1.簡介 JDBC&#xff08;Java Data Base Connectivity,java數據庫連接&#xff09;是一種用于執行SQL語句的Java API&#xff0c;可以為多種關系數據庫提供統一訪問&#xff0c;它由一組用Java語言編寫的類和接口組成。JDBC提供了一種基準&#xff0c;據此可以構建更高級的工具…

Shell 腳本:系統管理與任務自動化的利器

在開發者忙碌的日常工作中&#xff0c;效率就是生命線。當面對大量重復、繁瑣的系統管理任務與開發流程時&#xff0c;一款得力的編程工具能讓工作事半功倍。Shell 腳本&#xff0c;這把在 Linux 和 Unix 系統環境下閃耀著光芒的利器&#xff0c;憑借其強大的自動化能力&#x…

關于mybatis插入大批量數據效率問題

一、即便分批次用mybatis插入數據&#xff0c;效率依舊不高&#xff0c;原因&#xff1a; MyBatis一次性批量插入幾千條數據&#xff0c;為什么性能很差&#xff1f;-騰訊云開發者社區-騰訊云 文中提出&#xff1a; 默認執行器類型為Simple&#xff0c;會為每個語句創建一個新…

在 JavaScript中編寫 Appium 測試(入門)

1.編寫一個測試 (JS) 要在 JavaScript&#xff08;Node.js&#xff09;中編寫 Appium 測試&#xff0c;我們需要選擇一個與 Appium 兼容的客戶端 庫。維護最好的庫和 Appium 團隊推薦使用的庫是 WebdriverIO, 所有就讓我們使用它吧。既然我們已經安裝了 Appium&#xff0c;我們…

【android bluetooth 框架分析 04】【bt-framework 層詳解 6】【Properties介紹】

DeviceProperties、AdapterProperties、StorageModule、以及 bt_config.conf 是 AOSP Bluetooth 棧中 設備屬性管理與持久化系統 的核心組成部分&#xff0c;它們之間關系緊密&#xff0c;但職責各有不同。 下面我將依次講解它們的區別與聯系. 注意: 在代碼里面 還有 Blueto…

@Resource vs @Autowired 在Spring中的使用和區別

Resource vs Autowired 在Spring中的使用和區別 在Spring開發中&#xff0c;我們常會接觸兩個用于實現引用模塊注入的注解&#xff1a;Resource 和 Autowired。它們在使用上有些相似之處&#xff0c;但本質上來看&#xff0c;有所區別。本文將給出兩者的詳細介紹和對比&#x…

Mac M4 芯片運行大模型指南,包括模型微調與推理

Mac M4 芯片運行大模型指南&#xff0c;模型微調與推理 背景模型推理 Ollama&#x1f50d; 舉例說明&#xff1a;踩坑 模型微調 unsloth 背景 在國補、教育優惠、京東會員500優惠券等眾多優惠之下。 我拿下了Macmini M4 16G 內存萬兆網卡。在機器到手的第一時間&#xff0c;馬…

微信小程序中安裝vant

以下是微信小程序中安裝 Vant 的詳細步驟&#xff1a; 1. 初始化項目 在微信小程序項目目錄下&#xff0c;打開終端&#xff0c;執行以下命令進行項目初始化&#xff1a; npm init -y該命令會快速生成一個默認的package.json文件&#xff0c;-y參數表示直接使用默認配置&…

今天做的力扣SQL

我本地markdown的東西直接復制出來了。 多說一嘴&#xff0c;今天早上六點醒了&#xff0c;然后被外面吵&#xff0c;心里也擔心找實習就一直睡不著了。索性直接來實驗室&#xff0c;這一上午感覺好快啊。幸運的是&#xff0c;自己也沒有浪費時間&#xff0c;還行吧。SQL欠的賬…

【開發常用命令】:docker常用命令

docker常用命令 基礎命令 # 啟動docker systemctl start docker # 關閉docker systemctl stop docker # 重啟docker systemctl restart docker # 設置開機自啟動 systemctl enable docker # 查看docker運行狀態 systemctl status docker # 查看docker版本號信息 docker versi…

安裝配置以太鏈錢包工具

安裝go語言環境 1、官網下載go安裝包并上傳到指定機器 https://golang.google.cn/dl/ 2、解壓縮至指定位置&#xff1a; tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz 3、將 /usr/local/go/bin 目錄添加至 PATH 環境變量&#xff1a; export PATH$PATH:/usr/local/g…

論文閱讀:speculative decoding

Fast Inference from Transformers via Speculative Decoding 論文地址&#xff1a;https://arxiv.org/pdf/2211.17192 speculative sampling 為了從分布 p ( x ) p(x) p(x) 中采樣&#xff0c;我們實際上是從分布 q ( x ) q(x) q(x) 中采樣 x x x&#xff0c;如果 q ( …

java操作word里的表格

依賴&#xff1a; <dependency><groupId>com.techCoLtd</groupId><artifactId>aspose-words-16.4.0-jdk16</artifactId><classifier>jdk16</classifier> </dependency>/*** 刪除表格及表格的行* throws Exception*/ private s…

單鏈表經典算法題之分割鏈表

給定一個頭結點和一個值x&#xff0c;是鏈表中所有小于x的值都在x前面 typedef struct ListNode ListNode; struct ListNode* partition(struct ListNode* head, int x) { //思路一&#xff1a;在原鏈表上進行修改 //思路二&#xff1a;創建新鏈表&#xff0c;使用哨兵位&…

Modbus TCP轉DeviceNet網關連接ABB變頻器配置案例

某工廠需要將支持Modbus TCP協議的上位機控制系統&#xff08;如PLC或SCADA&#xff09;與支持DeviceNet協議的變頻器&#xff08;如ABB ACS880、施耐德ATV320等&#xff09;進行通信。為實現協議轉換&#xff0c;采用開疆智能Modbus TCP轉DeviceNet網關KJ-DVCZ-MTCPS作為中間設…

【力扣 簡單 C++】206. 反轉鏈表

目錄 題目 解法一&#xff1a;迭代 解法二&#xff1a;遞歸 題目 待添加 解法一&#xff1a;迭代 class Solution { private:ListNode* reverse(ListNode* head){ListNode* newHead {};while (head){ListNode* nextNode {head->next};head->next newHead;newHead …

計算機視覺之三維重建(深入淺出SfM與SLAM核心算法)—— 1. 攝像機幾何

文章目錄 1. 針孔相機1.1. 針孔成像1.2. 光圈對成像的影響 2. 透視投影相機2.1. 透鏡成像2.2. 失焦2.3. 徑向畸變2.4. 透視投影的性質 3. 世界坐標系到像素坐標系的變換4. 其它相機模型4.1. 弱透視投影攝像機4.2. 正交投影攝像機4.3. 各種攝像機模型的應用場合 課程視頻鏈接&am…

第十三節:第七部分:Stream流的中間方法、Stream流的終結方法

Stream流常見的中間方法 Stream流常見的終結方法 代碼 學生類&#xff08;代碼一與代碼二共涉及到的類&#xff09; package com.itheima.day28_Stream;import java.util.Objects;public class Student implements Comparable<Student> {private String name;private i…

深入理解 Go 中的字節序(Endianness)檢測代碼

深入理解 Go 中的字節序&#xff08;大小端&#xff09;檢測代碼 在計算機系統中&#xff0c;字節序&#xff08;Endianness&#xff09; 是指多字節數據類型&#xff08;如 int16、int32 等&#xff09;在內存中的存儲順序。Go 語言標準庫提供了對大端&#xff08;Big-endian&…