【CLODOP】用pdfjs和clodop实现pdf打印本文通过使用pdfjs和clodop来实现pdf打印,流程简介:
本文通过使用pdfjs和clodop来实现pdf打印,我的gitee地址:pdf打印,可以直接下载使用
主要流程:
- 下载clodop并注册,将官网的LodopFuncs.js文件放到项目中,并引入要使用的vue文件中
- 下载pdfjs,并引入到要使用的vue文件中
- 在 index.html中添加
<script src='http://localhost:18000/CLodopfuncs.js?name=CLODOPA'></script>
(否则在使用时会出现getCLodop is not defined的报错)- 对pdf文件地址的处理:从后端获得blob类型地址,通过
pdfjsLib.getDocument
方法将blob处理;再通过loadingTask.promise
得到pdf的详细信息对象,对象中包含页数(注意:当pdf文件页数较多时需要分批处理,我这里是分10页一批去处理)- 处理pdf,新建画布,再将画好的画布转换成图片,接着将图片放到colodop的中配置,最后删除画布。(注意这个过程只能一页一页的进行处理,目前只研究出了这样的方法)
当然如果公司资金充足可以直接注册colodp包含pdf打印的商用注册
一、pdfjs引入
方法一
在index.html中cdn引入, 加载远程pdf时会出现 CORS 跨域的问题
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.7.107/pdf.min.js"></script>
方法二
去官网mozilla.github.io/pdf.js/下载插件包,将pdf.js和pdf.worker.js放在项目中,在index.html中引入,会加大项目的体积也不灵活
<script type="text/javascript" src="pdfjs/pdf.js"></script>
<script type="text/javascript" src="pdfjs/pdf.worker.js"></script>
方法三
npm安装 npm i pdfjs-dist
二、clodop下载和使用
去clodop官网下载www.lodop.net/, 现在样例中有许多已经封装好的方法,一部分的方法可以免费使用,另外部分方法可以本地测试使用,商用要商用注册,这里介绍一些我用到的clodop的方法,要了解其他就去clodop的官网查看使用
-
初始化LODOP 对象
LODOP.PRINT_INIT('任务名字')
-
设置纸张大小和打印方向
`LODOP.SET_PRINT_PAGESIZE(pageDirection, pageWidth, pageHeight, pageName)` pageDirection: 纸张方向,1纵向 2横向 pageWidth: 纸张宽度,单位mm pageHeight: 纸张高度,单位mm pageName:设置纸张格式名称,比如A4 举个栗子: LODOP.SET_PRINT_PAGESIZE(1, 2200, 2970, "A4") // 设置纸张大小A4 纵向打印 LODOP.SET_PRINT_PAGESIZE(2, 1480, 2100, "A5") // 设置纸张大小A5 横向打印
-
设置打印数据
`LODOP.ADD_PRINT_IMAGE(top, left, width, height, imageSrc)` top:距离将放置图像的页面顶部的距离 left:距离要放置图像的页面左侧的距离 width:图像的宽度 height:图像的高度 imageSrc:镜像文件的源路径
-
设置打印样式
`LODOP.SET_PRINT_STYLEA(nStyle1, nStyle2, nValue)` nStyle1: 表示要设置的样式类型,可以是字体样式、对齐方式、边框样式等 nStyle2: 表示要设置的具体样式属性,如字体大小、字体颜色、边框宽度等 nValue: 表示具体的属性值,如字体大小为12px,字体颜色为红色等 举个栗子: LODOP.SET_PRINT_STYLEA(0, 1, 12); // 设置字体大小为12px LODOP.SET_PRINT_STYLEA(0, 2, "red") // 设置字体颜色为红色 LODOP.SET_PRINT_STYLEA(0,"Stretch",2) // 设置打印文本的拉伸属性为两倍大小
-
设置打印模式(我这里在分批打印时给单个任务命名)
`LODOP.SET_PRINT_MODE("ModeName", ModeValue)`
-
设置打印份数
`LODOP.SET_PRINT_COPIES('打印份数')`
-
设置打印机
`LODOP.SET_PRINTER_INDEX('打印机名称')`
-
直接打印
LODOP.PRINT()
-
打印预览
LODOP.PREVIEW()
具体使用
pdfjs可以直接根据pdf地址去获取和解析pdf,但是在客户环境中可能会出现跨域问题,当出现跨域问题就改成让后端直接返回文件流
LodopFuncs.js
import { MessageBox } from 'element-ui'
//====判断是否需要安装CLodop云打印服务器:====
export function needCLodop() {
try {
var ua = navigator.userAgent
if (ua.match(/Windows\sPhone/i) != null) return true
if (ua.match(/iPhone|iPod/i) != null) return true
if (ua.match(/Android/i) != null) return true
if (ua.match(/Edge\D?\d+/i) != null) return true
var verTrident = ua.match(/Trident\D?\d+/i)
var verIE = ua.match(/MSIE\D?\d+/i)
var verOPR = ua.match(/OPR\D?\d+/i)
var verFF = ua.match(/Firefox\D?\d+/i)
var x64 = ua.match(/x64/i)
if (verTrident == null && verIE == null && x64 !== null) return true
else if (verFF !== null) {
verFF = verFF[0].match(/\d+/)
if (verFF[0] >= 42 || x64 !== null) return true
} else if (verOPR !== null) {
verOPR = verOPR[0].match(/\d+/)
if (verOPR[0] >= 32) return true
} else if (verTrident == null && verIE == null) {
var verChrome = ua.match(/Chrome\D?\d+/i)
if (verChrome !== null) {
verChrome = verChrome[0].match(/\d+/)
if (verChrome[0] >= 42) return true
}
}
return false
} catch (err) {
return true
}
}
//====页面引用CLodop云打印必须的JS文件:====
if (needCLodop()) {
var head =
document.head ||
document.getElementsByTagName('head')[0] ||
document.documentElement
var oscript = document.createElement('script')
oscript.src = 'http://localhost:8000/CLodopfuncs.js?priority=1'
head.insertBefore(oscript, head.firstChild)
//引用双端口(8000和18000)避免其中某个被占用:
oscript = document.createElement('script')
oscript.src = 'http://localhost:18000/CLodopfuncs.js?priority=0'
head.insertBefore(oscript, head.firstChild)
}
// 下载loadLodop 可以将指定版本的clodop下载到项目的静态资源中
function loadLodop() {
window.open('../../static/Lodop/CLodop_Setup_for_Win32NT.exe')
}
//====获取LODOP对象的主过程:====
export function getLodop() {
var LODOP
try {
LODOP = getCLodop()
if (!LODOP && document.readyState !== 'complete') {
return MessageBox.alert('C-Lodop打印控件还没准备好,请稍后再试!')
}
//===如下空白位置适合调用统一功能(如注册语句、语言选择等):===
//LODOP.SET_LICENSES("北京XXXXX公司","8xxxxxxxxxxxxx5","","");
} catch (err) {
MessageBox({
title: '温馨提示',
type: 'warning',
showCancelButton: true,
confirmButtonText: '下载',
cancelButtonText: '取消',
message: '检测到您还未安装C-LODOP套打控件,请确认启用后再打印。或您可点击下载该套打控件,安装成功后刷新页面再进行打印',
callback: res => {
if (res === 'confirm') {
loadLodop()
}
}
})
}
return LODOP
}
print.js
/*
* @Author: xiaxia
* @Description:
* @Date: 2023-06-08 17:35:19
* @LastEditTime: 2023-12-20 11:22:40
* @FilePath: \pdf-print\src\utils\printjs.js
*/
import axios from 'axios'
import {getLodop} from './LodopFuncs'
const instance = axios.create({
timeout: 1000
})
var LODOP = getLodop(); // 创建一个LODOP对象
// 获取本地打印机
function getLocalPrint() {
let counter = LODOP.GET_PRINTER_COUNT(); // 获取打印机个数
let printerList= []
for (let i = 0; i < counter; i++) {
printerList.push({//将打印机存入printerList数组中
value: LODOP.GET_PRINTER_NAME(i),
label: LODOP.GET_PRINTER_NAME(i)
});
}
return printerList
}
// 打印Base64图片
const lodopImageBase64 = (img64, printer) => {
if (!LODOP) return
LODOP.PRINT_INIT("测试打印");
// LODOP.SET_PRINT_PAGESIZE(2, 0, 0, 'A4') // 设置纸张大小
LODOP.ADD_PRINT_IMAGE(0, 0, "100%", "100%", img64)
LODOP.SET_PRINTER_INDEX(printer)
LODOP.PREVIEW() // 预览
}
// 1. 模拟请求
const getFileBase64 = async pdfUrl => {
let fileData = null
try {
fileData = await instance({
method: 'get',
url: pdfUrl, // 请求地址
responseType: 'blob' // 指明服务器返回的数据类型
})
} catch {
fileData = null
}
if (fileData && fileData.data) {
const result = getObjectURL(fileData.data) // 流转成url
return result
} else {
return false
}
}
// 2.流转成url
const getObjectURL = (file) => {
let url = null
if (window.createObjectURL !== undefined) { // basic
url = window.createObjectURL(file)
} else if (window.webkitURL !== undefined) { // webkit or chrome
try {
url = window.webkitURL.createObjectURL(file)
} catch (error) {
console.log(error)
}
} else if (window.URL !== undefined) { // mozilla(firefox)
try {
url = window.URL.createObjectURL(file)
} catch (error) {
console.log(error)
}
}
return url
}
export { getLocalPrint, lodopImageBase64, getFileBase64, getObjectURL }
Vue文件
<template>
<div clas="app">
<div>
<h2>pdf打印</h2>
<el-form label-width="85px" size="mini">
<el-form-item label="打印机">
<el-select
v-model="dyParam.printer"
placeholder="请选择"
>
<el-option
v-for="item in locaolPrinter"
:key="item.value"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
</el-form-item>
</el-form>
<el-button type="primary" @click="getLocalPrint()">获取本地打印机</el-button>
<el-button type="primary" @click="printPdf(dyParam, true)">直接打印</el-button>
<el-button type="primary" @click="printPdf(dyParam, false)">打印预览</el-button>
</div>
</div>
</template>
<script>
// pdfjs识别解析pdf文件--> Canvas将解析后的数据画出来 --> 转换成image --> 通过clodop打印
import { getFileBase64, getLocalPrint } from './utils/printjs.js'
import {getLodop} from './utils/LodopFuncs' // clodop
import * as pdfjsLib from "pdfjs-dist"
const pdfjsWorker = import("pdfjs-dist/build/pdf.worker.entry")
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker
export default {
name: 'App',
data(){
return{
// dyParam 包含打印的配置信息:发票号码 打印机,打印份数等...
dyParam: {
goldtaxNum: '', // 发票号码
printer: '', // 打印机名称
dyNumber: 1, // 打印份数
},
locaolPrinter: null, // 获取到的本地打印机
}
},
methods:{
async printPdf(dyParam, ispreview) {
let answer = null
let fpNum = dyParam.goldtaxNum// 这里去获取发票号码
let LODOP = getLodop() // 初始化clodop 没有控件会提示用户安装
// 1. pdf地址
// 这里使用pdf地址 可根据自己项目替换
const url = "http://localhost:8000/CLodopDemos/PDFDemo.pdf"
const zhUrl = await getFileBase64(url) // 模拟请求,获得blob类型地址
const loadingTask = pdfjsLib.getDocument(zhUrl)
console.log('pdf地址111111:', zhUrl)
// 2. 文件流
// let strURL = dyParam.row.file.address // pdf地址
// let res = await getPrintFeil({FileAddress: strURL}) // 请求后端 获取文件流
// let bolbFile = new Blob([res.data], { type: 'application/pdf; charset=utf-8' })
// let blobUrl = getObjectURL(bolbFile)
// const loadingTask = pdfjsLib.getDocument(blobUrl)
// console.log( 'pdf地址111111:' , blobUrl);
let pdf = null
try {
pdf= await loadingTask.promise
} catch(e) {
return answer = { success: false,msg: "未查询到pdf地址,请联系管理员" }
}
let numPages = pdf.numPages
// 1. 如果当前打印的页数 <= 10 ------------------------------------------------------------------
if(numPages <= 10) {
LODOP.PRINT_INIT('发票'+fpNum)
LODOP.SET_PRINT_PAGESIZE(1, 2200, 2970, "") // 设置纸张大小A4 纵向打印
// LODOP.SET_PRINT_PAGESIZE(2, 0, 0, "A4") // 设置纸张大小A4 横向打印
for(let i=1; i<=numPages; i++){
await this.CreateOnePage( LODOP, pdf, i)
}
if(ispreview) { // 判断是预览还是直接打印
LODOP.SET_PRINTER_INDEX(dyParam.printer)
LODOP.PRINT() // 打印
} else {
LODOP.PREVIEW() // 预览
}
this.$message.success('发票 '+ dyParam.goldtaxNum +' 解析完成正在发送请求到打印机,请耐心等待......');
return new Promise((resolve) => {
LODOP.On_Return = function (TaskID, Value) {
if(Value){
console.log('打印成功:发送打印请求成功')
answer = { success: true, msg: "发送打印请求成功" }
} else {
console.log('打印失败:发送打印请求失败')
answer = { success: false, msg: "发送打印请求失败" }
}
resolve(answer) // 完成Promise
}
})
}
// 2. 当前页数>10--需要进行分组 ------------------------------------------------------------------
else {
let num1 = Math.ceil(numPages/10) // 向上取整
for (let it = 0; it <= num1-1; it++) {
LODOP.PRINT_INIT('发票'+fpNum)
LODOP.SET_PRINT_PAGESIZE(1, 2200, 2970, ""); // 设置纸张大小
// LODOP.SET_PRINT_PAGESIZE(2, 0, 0, "A4"); // 设置纸张大小A4 横向打印
if(it == 0) {
for (let ind = 1; ind <= 10; ind++) {
await this.CreateOnePage( LODOP, pdf, ind)
}
} else{
for (let ind = 1; ind <= 10; ind++) {
if(ind == 10){
await this.CreateOnePage(LODOP, pdf, parseInt(it+1+''+0))
if(parseInt(it+1+''+0)==numPages) break
} else {
await this.CreateOnePage( LODOP, pdf, parseInt(it+''+ind))
if(parseInt(it+''+ind)==numPages) break
}
}
}
LODOP.SET_PRINT_MODE("CUSTOM_TASK_NAME",'发票'+fpNum+'第'+(it+1)+'份') // 单独任务名
console.log('当前打印的页数>10,打印份数:', this.dyNumber)
LODOP.SET_PRINT_COPIES(this.dyNumber) // 打印份数
if(ispreview) { // 判断是预览还是直接打印
LODOP.SET_PRINTER_INDEX(dyParam.printer)
LODOP.PRINT() // 打印
} else {
LODOP.PREVIEW() // 预览
}
}
this.$message.success(' 解析完成正在发送请求到打印机,请耐心等待......');
return new Promise((resolve) => {
LODOP.On_Return = function (TaskID, Value) {
if(Value){
console.log('打印成功:发送打印请求成功')
answer = { success: true, msg: "发送打印请求成功" }
} else {
console.log('发送打印请求失败')
answer = { success: false, msg: "发送打印请求失败" }
}
resolve(answer); // 完成Promise
}
})
}
},
// pdf解析
async CreateOnePage(LODOP, pdf, i){
LODOP.NewPage()
let pdfId = "pdf" + i
const newCanvas = document.createElement("canvas") // 新建画布
newCanvas.setAttribute("id", pdfId) // 赋id
const page = await pdf.getPage(i)
const context = newCanvas.getContext("2d")
const viewport = page.getViewport({ scale: 7}) // scale值越大越清晰 解析时间越长
newCanvas.height = viewport.height
newCanvas.width = viewport.width
const renderContext = {
canvasContext: context,
viewport: viewport,
};
await page.render(renderContext).promise
console.log("Page rendered")
const img = newCanvas.toDataURL("image/jpeg") // 转换成image地址
LODOP.ADD_PRINT_IMAGE(0, 0, "95%", "95%", img)
LODOP.SET_PRINT_STYLEA(0,"Stretch",2)
newCanvas.remove() // 销毁画布
},
getLocalPrint() {
this.locaolPrinter = getLocalPrint()
console.log(this.locaolPrinter);
}
}
}
</script>
<style>
.app {
text-align: center;
}
canvas {
border: 1px solid #ccc;
}
img {
width: 600px;
height: 500px;
border: 1px solid red;
}
.content {
display: flex;
align-items: center;
justify-content: center;
}
.pdf {
margin-right: 20px;
}
</style>
注意
打印pdf时可能出现报错,中文文字缺失,解决办法参考文档:blog.csdn.net/gentleman_h…
Warning: Error during font loading: The CMap "baseUrl" parameter must be specified, ensure that the "cMapUrl" and "cMapPacked" API parameters are provided
处理前
处理
params.cMapPacked = true
params.cMapUrl = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/'
处理后
转载自:https://juejin.cn/post/7411480128528990262