业务背景
需求是前端打印出一排2个、一排5个、圆标签等,各种样式的标签,并包含二维码、中英文、数字, 如下图:
对于打印机来讲,一排就是一页,想要打印出这样的标签,步骤如下:
- 确认前端打印功能
- 计算每个标签在一页中的位置
- 每个标签中打印二维码
- 打印英文和数字
- 打印中文
方案确认
首先确定jspdf作为前端打印插件,jspdf的文档很细,功能强大,是一个优秀的框架,但是默认不支持中文,需要额外引入字体,最开始想通过html2canvas的方法把页面的中文通过dom生成图片再使用jspdf打印出来的方案,后来发现打印出来的文字很不清晰,清晰度的问题难以解决,所以就只能换成直接使用jspdf直接生成文字的方式处理,就是必须要引入字体文件。
实现方法
一、引入jspdf和qrcode
// 下载包, 本文使用jspdf@2.3.1
npm install jspdf qrcode
// 引入
import jsPDF from 'jspdf'
var QRcode = require('qrcode')
二、确定标签尺寸和纸张尺寸, 配置jspdf
以一排5个为例,使用的纸张尺寸是87 * 33, 标签尺寸是 15 * 30
const pdf = new jsPDF('l', 'mm', [870, 330])
// 声明一套配置
const printConfig = {
pageWidth: 870, // 一页宽度
pageHeight: 330, // 一页的高度
left: 35, // 标签左边距
offsetLeft: 174, // 每个标签的偏移量
top: 25, // 标签上间距
picWidth: 100, // 二维码宽度
picHeight: 100, // 二维码高度
linePicNum: 5, // 一排显示个数
rotateDeg: 270, // 角度
fontSize: 58 // 字号
}
三、生成二维码并在指定位置打印
const imageList = []
// 声明一个imageList用了存放二维码base64
list.map((item, index) => {
QRCode.toDataURL(item.code)
.then(url => {
imageList.push(url)
})
.catch(err => {
console.error(err)
})
})
// 打印二维码图片
let left = printConfig.left
let top = printConfig.top
imageList.map(base64 => {
left = printConfig.left + printConfig.offsetLeft * (index % printConfig.linePicNum)
// 添加图片
pdf.addImage(base64, 'JPEG', left, top, picWidth, picHeight, '', 'FAST')
// 超过限制个数就翻页
if (index !== 0 && (index + 1) % printConfig.linePicNum === 0 && index < imageList.length - 1 ) {
pdf.addPage([width, height], 'l')
}
})
这样可以在指定位置打印出二维码了
三、打印英文和数字
jspdf直接可以打印英文和数字,所以直接打印即可, left和top是需要增加偏移量的
pdf.text('this is my word', left + item.offsetLeft, top + item.offsetTop, {angle: printConfig.rotateDeg})
四、打印中文
中文的引入会麻烦一些,需要引入语言包,具体生成方法可以参考官网,或参考 www.cnblogs.com/ww01/p/1149…
先找到ttf文件后convert成一个js,将生成的js放到文件中
function addfont(pdf) {
var font = 'AA****' // 中的就是ttf转化成的编码
pdf.addFileToVFS('bolds', font)
return true;
}
// 在pdf生成后,添加并设置字体
addfont(pdf)
pdf.addFont('bolds', 'customFont', 'normal')
pdf.setFont('customFont')
pdf.setFontSize(58)
//打印
pdf.text('我是中文', left + item.offsetLeft, top + item.offsetTop, {angle: printConfig.rotateDeg})
完整代码
以下是我封装的打印方法, 外部直接调用generatePrint即可
import jsPDF from 'jspdf'
var QRCode = require('qrcode')
let width, height;
let pdf;
let timer
class PrintTag {
constructor(data) {
this.imageList = []
this.list = data
this.printConfig = {
pageWidth: 870, // 一页宽度
pageHeight: 330, // 一页的高度
left: 35, // 标签左边距
offsetLeft: 174, // 每个标签的偏移量
top: 25, // 标签上间距
picWidth: 100, // 二维码宽度
picHeight: 100, // 二维码高度
linePicNum: 5, // 一排显示个数
rotateDeg: 270, // 角度
fontSize: 58 // 字号
}
this.setPdfConfig()
}
start() {
this.buildQRcode()
}
// 设置pdf
setPdfConfig() {
width = this.printConfig.pageWidth
height = this.printConfig.pageHeight
pdf = new jsPDF('l', 'mm', [width, height])
//添加并设置字体
addfont(pdf)
pdf.addFont('bolds', 'customFont', 'normal')
pdf.setFont('customFont')
pdf.setFontSize(58)
}
// 生成二维码
buildQRcode() {
this.list.map((item, index) => {
QRCode.toDataURL(item.sampleIdLims)
.then(url => {
this.imageList.push(url)
})
.catch(err => {
console.error(err)
})
})
this.checkImageLoaded()
}
// qrcode是异步,所以延迟生成pdf
checkImageLoaded() {
setTimeout(() => {
if (this.imageList.length === this.list.length) {
this.generate(this.imageList)
}
}, 500)
}
// 生成pdf
generate(arr) {
console.time('print')
const picWidth = this.printConfig.picWidth
const picHeight = this.printConfig.picWidth
let left = 0
let top = this.printConfig.top
const textOpt = {angle: this.printConfig.rotateDeg}
arr.map((base64, index) => {
left = this.printConfig.left + this.printConfig.offsetLeft * (index % this.printConfig.linePicNum)
pdf.addImage(base64, 'JPEG', left, top, picWidth, picHeight, '', 'FAST')
pdf.text('hello 12345', left + item.offsetLeft, top + item.offsetTop, textOpt)
pdf.text('我说中文', left + item.offsetLeft, top + item.offsetTop + 25, textOpt)
if (index !== 0 && (index + 1) % this.printConfig.linePicNum === 0 && index < arr.length - 1 ) {
pdf.addPage([width, height], 'l')
}
})
window.open(pdf.output('bloburl'), '_blank');
console.timeEnd('print')
}
}
export function generatePrint(data, step, lableType) {
console.log(data, step)
return new PrintTag(data, step, lableType).start()
}