js生成PDF的幾種方法

前言

最近由於要求前端生成pdf,就此研究了一陣子,發現就 jspdf 稍微好用一些,爲了能導出pdf我試了好幾種辦法,下面給你們分享一下css

不管使用哪種辦法,圖片都必須轉成base64,不然顯示不出來html

方法一:html2canvas + jsPDF 生成pdf

這個方法就是把html內容轉成canvas,而後生成圖片,把圖片添加到jspdf裏,保存導出pdf前端

  • 優勢:生成的pdf清晰度良好,且支持中文字體
  • 缺點:生成的pdf內容大小不可控與模板樣式有關,而且html模板必需要展現,display:none或者visibility:hidden, 甚至設置定位,top:-1000%,也不行,會打印出空白的 下面貼出代碼
// 批量導出多個頁面數據在一張pdf裏
// html生成Canvas圖片,添加到pdf裏
downloadPDF () {
    // 判斷全部圖片是否都已經轉成base64了,若是沒有繼續等待
    // 我在模板的全部圖片的onload裏在轉base64,每轉成一個就調取下載方法,
    // 下載方法就判斷是否和要生成的圖片總數一致
	let imgload = this.judgeimg()
	if (!imgload) {
		return false
	}
	const pdf = new window.jsPDF('', 'pt', 'a4')
	let pdfList = document.getElementsByClassName('pdfItem')
	for (let i = 0, len = pdfList.length; i < len; i++) {
            let target = pdfList[i]
            target.style.background = '#FFFFFF'
	    window.html2canvas(target, {
                dpi: 144, // 設置dpi,會使圖片高清一些
		onrendered: function (canvas) {
			const contentWidth = canvas.width
			const contentHeight = canvas.height

			//一頁pdf顯示html頁面生成的canvas高度;
			const pageHeight = (contentWidth / 595.28) * 841.89
			//未生成pdf的html頁面高度
			let leftHeight = contentHeight
			//頁面偏移
			let position = 0
			//a4紙的尺寸[595.28,841.89],html頁面生成的canvas在pdf中圖片的寬高
			const imgWidth = 595.28
			const imgHeight = (592.28 / contentWidth) * contentHeight

			const pageData = canvas.toDataURL('image/jpeg', 1.0)
			if (leftHeight < pageHeight) {
			    pdf.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)
			    // 爲下一條數據添加空白頁
			    if (i < len - 1) {
			        pdf.addPage()
				}
			} else {
			    while (leftHeight > 0) {
				    pdf.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)
				    leftHeight -= pageHeight
				    position -= 841.89
				    //避免添加空白頁
				    if (leftHeight > 0) {
				        pdf.addPage()
				    }
			    }
			}
			if (i === len - 1) {
			    pdf.save('PDF存檔.pdf')
			}
		    }   
	})
    }
}
複製代碼

方法二:jsPDF的addHTML生成pdf

jsPDF有直接提供html生成PDF的方法,也是對生成的html進行截圖生成圖片git

  • 優勢:代碼簡單,且支持中文字體
  • 缺點:一樣html模板必需要展現,display:none或者visibility:hidden, 甚至設置定位,top:-1000%,也不行,會打印出空白的(或者整塊黑色的);清晰度通常,不是很高,要求不高的可使用這個方法

模板頁面的樣式必定要設置background:#ffffff; ,不然會打印出的背景默認黑色github

下面貼出代碼canvas

// html直接生成pdf截圖
downloadPDF () {
    let imgload = this.judgeimg()
    if (!imgload) {
        return false
    }
    const pdf = new window.jsPDF('', 'pt', 'a4')
    let orderList = document.getElementsByClassName('pdfOrder')
    for (let i = 0, len = orderList.length; i < len; i++) {
        let target = orderList[i]
        target.style.background = '#FFFFFF'
        // 經調試,55%的時候內容顯示效果比較好,與模板內容樣式也有關係,本身調節一下比較好
        target.style.width = '55%'
        pdf.addHTML(target, function () {
            if (i < len - 1) {
                pdf.addPage()
            }
            if (i === len - 1) {
                pdf.save('PDF存檔.pdf')
            }
        })
    }
}
複製代碼

方法三:jsPDF手寫內容生成pdf

按照jspdf的api逐行編寫排版api

  • 優勢:生成的pdf清晰度高,不須要html模板,不須要顯示在瀏覽器內,可靜默導出
  • 缺點:默認字體不支持中文字體,本身排版比較麻煩

如何支持中文字體呢?

既然寫出來第三種辦法就說明確定是能夠解決中文字體不支持狀況的, 我查看了jspdf官方網站,沒找到api文檔。可是搜到了一些文章,而後開始了本身的測試。promise

首先支持設置中文字體的版本,目前找到的方法裏,只有1.4.0版本
<script src="https://cdn.bootcss.com/jspdf/1.4.0/jspdf.debug.js"></script> 而後在網上下載一套中文字體,ttf格式的就好。 感謝大佬的生成字體的演示 生成字體代碼瀏覽器

// addFileToVFS方法添加字體文件
doc.addFileToVFS(fileName, Base64content);
// 添加字體
doc.addFont(fileName, fontName, fontStyle);
// 使用字體
doc.setFont(fontName)
複製代碼

這裏有有個坑,我當時設置了字體樣式,而後就亂碼了,說明在字體樣式上的支持還不是很友好,可是字體大小仍是ok的bash

<button @click="download2PDF" >導出pdf</button>
複製代碼
// 手寫pdf內容
download2PDF () {
    this.pdfloading = true
    const pdf = new window.jsPDF()
    //添加並設置字體
    pdf.addFont('華文仿宋.ttf', 'custom', 'normal');
    pdf.setFont('custom');
    console.log(this.pdfData.length)
    for (let i = 0, len = this.pdfData.length; i < len; i++) {
		const item = this.pdfData[i]
		try {
		const promises = [item.imgurl1, item.imgurl2].map(imgurl => {
		    return imgGetAction(imgurl)
		})
		    Promise.all(promises).then(values => {
		        console.log(values)
		        item.pFrontImg = 'data:image/jpeg;base64,' + btoa(String.fromCharCode(...new Uint8Array(values[0])))
		        item.pBackImg = 'data:image/jpeg;base64,' + btoa(String.fromCharCode(...new Uint8Array(values[1])))
		        this.getPdfTemplate(pdf, item)
		        if (i < len - 1) {
		            pdf.addPage()
		        }
		        if (i === len - 1) {
		            pdf.save('PDF存檔.pdf')
		            this.pdfloading = false
		        }
		    })
		} catch (error) {
			console.log(error)
			this.$message.error('PDF導出失敗')
			this.pdfloading = false
			return false
		}
	}
},
// pdf模板信息
getPdfTemplate (pdf, data) {
    let linePos = 14
	pdf.text(60, linePos, '訂單號 ' + data.id)
	pdf.setFontType('normal')
	
	pdf.setFontSize(10.5)
	linePos += 8
	//// 信息
	pdf.text(15, linePos, '信息')
	linePos += 5
	pdf.text(15, linePos, '姓名:' + data.name)
	pdf.text(75, linePos, '申請日期:' + data.applyDate)
	pdf.text(135.5, linePos, '審覈日期:' + data.auditDate)

	linePos += 5
	// 第四行 40 照片
	pdf.text(15, linePos, '正面照片:')
	pdf.text(75, linePos, '反面照片:')
	linePos += 3
	pdf.addImage(pFrontImg, 'JPEG', 15, linePos, 50, 60)
	pdf.addImage(pBackImg, 'JPEG', 75, linePos, 50, 60)
	//// 收取信息 215
	linePos += 6
	pdf.text(15, linePos, '收取信息')
	// 第一行 220
	linePos += 5
	pdf.text(15, linePos, '收取方式:' + this.wayName[data.receiveWayId])
	pdf.text(75, linePos, '收取人姓名:' + data.destName)
	pdf.text(135.5, linePos, '收取人聯繫方式:' + data.destPhone)
	// 第二行 225
	linePos += 5
	let addrStr = (data.destProvince + data.destCity + data.destDistrict + data.destAddress) || '--'
	pdf.text(15, linePos, '收貨地址:' + addrStr)
	//// 結尾footer 230
	pdf.text(15, 240, '打印時間:' + formatDate(new Date().valueOf(), 'yyyy-MM-dd hh:mm:ss'))
	pdf.text(135.5, 240, '經辦人簽字:')
}
	
複製代碼

參考內容

相關文章
相關標籤/搜索