1.需求
最近客户提出,希望在使用的富文本编辑器中,增加一个上传pdf还要显示在编辑器中的一个需求,我网上找了下资料发现TinyMCE编辑器没有这个功能,好在这个TinyMCE支持自定义按键,所以准备写一个自定义按钮
2 思路
一开始,我是准备使用原生的iframe插入到编辑器中,后来经过测试,因为我的项目这个富文本的内容html最后是传到后端,让后端通过html解析生成pdf的,经过测试发现iframe标签不能被解析成pdf,所以我的大致思路是准备将pdf解析后,在我这边转成图片标签传给后端,
3 工具
tinymce@6.1.2 pdfjs: 2.16.105
4 开始
首先应该是在编辑器中增加一个按钮,经过我查资料和翻看文档,是在tinymce配置项init中有一个setup函数,在这个函数中通过编辑器的api可以自定义一个按钮,代码如下:
Tinymce.init({
toolbar:
' undo redo | fullscreen | blocks alignleft aligncenter alignright alignjustify | link myCustomToolbarButton | numlist bullist | image media table | fontsize forecolor backcolor | bold italic underline strikethrough | indent outdent | superscript subscript | removeformat | ',
},
setup:(content)=>{
content.ui.registry.addButton("myCustomToolbarButton", {
text: "上传",
// 按钮事件的回调
onAction: function() {
// 先创建一个input用于选择文件
const input = document.createElement('input');
input.setAttribute('type', 'file');
const filetype='.pdf';
// 因为我的项目最需要上传pdf所以,限制了上传格式,可以通过修改filetype增加需要的格式
input.setAttribute('accept', filetype);
input.click();
}
});
})
因为定义了一个自定义的按钮在工具栏所以要在 toolbar增加
setting: {
toolbar:
'...之前的内容 | myCustomToolbarButton ',
},
本来我是想通过文件上传到后端,通过后端返回的链接交给pdfjs解析,经过实测,发现这样做,pdjjs解析链接会有跨域的问题,这个网上有很多案例可以查一下,但我的项目中文件都是本地上传的,而且pdfjs也能通过解析文件里显示pdf,所以准备通过文件流显示
const input = document.createElement('input');
input.setAttribute('type', 'file');
const filetype='.pdf';
input.setAttribute('accept', filetype);
input.click();
// input事件中拿到文件
input.onchange = function() {
const file = input
//通过FileReader 读取文件
const reader = new FileReader(); //读取成功后onload的回调
reader.onload = (e) => {
// 成功的回调
const url: string | null | ArrayBuffer = e.target ? e.target.result : '';
};
//最后运行
reader.readAsDataURL(file);
};
然后就需要写pdjjs将文件流转成图片的代码了,
import * as pdfjsLib from 'pdfjs-dist'
import * as pdfWorkerMin from 'pdfjs-dist/build/pdf.worker.min?url'
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorkerMin.default
export class Pdf2Image {
constructor(pdfDoc) {
this.pdfDoc = pdfDoc
}
static async open(url) {
const pdfDoc = await pdfjsLib.getDocument({ url }).promise
return new Pdf2Image(pdfDoc)
}
// `计算缩放比例`
static calcScale(page, option) {
if (option.scale !== undefined) {
return option.scale
}
if (option.width === undefined || option.height === undefined) {
return 1.0
}
const viewport = page.getViewport({ scale: 1.0 })
return Math.min(option.width / viewport.width, option.height / viewport.height)
}
numPages() {
return this.pdfDoc.numPages
}
// `将第pageNo页的pdf转换为指定格式的图片,option用于添加一些配置`
// `比如设置图片导出格式,设置缩放比例、执行回调函数等`
async getImageDataUrl(pageNo, option) {
const page = await this.pdfDoc.getPage(pageNo)
let scale = 1
if (option) {
scale = Pdf2Image.calcScale(page, option)
}
option = option || {}
if (!option.image) {
// `默认导出图片格式为jpeg`
option.image = 'jpeg'
}
// `指定pdf页面的视口大小`
const viewport = page.getViewport({ scale })
const canvas = document.createElement('canvas')
const canvasContext = canvas.getContext('2d')
// `设置画布的大小`
canvas.height = viewport.height
canvas.width = viewport.width
// `设置画布上下文的大小, 决定了在画布上下文中绘制的图形大小`
canvasContext.height = viewport.height
canvasContext.width = viewport.width
const renderContext = {
canvasContext,
viewport
}
// `将pdf页面渲染为图片并返回promise`
await page.render(renderContext).promise
if (option.callback) option.callback({ canvas })
// `将canvas导出为指定格式的图片`
switch (option.image) {
case 'jpeg':
return canvas.toDataURL('image/jpeg')
case 'webp':
return canvas.toDataURL('image/webp')
default:
return canvas.toDataURL()
}
}
// `将整个pdf转换为指定格式的图片`
async getAllImageDataUrl(option) {
const pages = []
const numPages = this.numPages()
for (let i = 1; i <= numPages; i += 1) {
const img = await this.getImageDataUrl(i, option)
pages.push(img)
}
return pages
}
}
export default Pdf2Image
这里的代码是参考了掘金一位大神写的,这是他的文章链接
:链接,
要注意的是pdfjs的某些版本和vite4+的版本有冲突,引入会报错用不了,可把我折磨坏了,找了好多资料按照上面的引入方式终于能用了。
最后完整代码如下
input.onchange = function() {
const file = input? input.files[0] :null;
const reader = new FileReader(); //
reader.onload = (e) => {
// 成功的回调
const url: string | null | ArrayBuffer = e.target ? e.target.result : '';
Pdf2Image.open(url).then((pdf2ImageInstance) => {
// 配置选项,比如指定图片格式和缩放比例等
const options = {
image: 'jpeg', // 指定输出图片格式为 jpeg
scale: 1.0, // 指定缩放比例,如果需要的话
// 可以添加其他选项,比如宽度和高度限制等
};
// 调用 getAllImageDataUrl 方法将整个 PDF 转换为图片数据 URL 数组
pdf2ImageInstance.getAllImageDataUrl(options).then((imageUrls) => {
// 现在 imageUrls 是一个包含所有页面图片数据 URL 的数组
// 您可以对这些 URL 进行处理,比如显示在页面上,或者发送到服务器
imageUrls.forEach((imageUrl, index) => {
// 这里就拿到了转成图片的url地址了,我这边是直接在我绑定的context里最后插入这个图片标签,传到后端就行了,也可以把图片上传到服务器
dataForm.context=dataForm.context+`<img src="${imageUrl}" >`
});
}).catch((error) => {
// 处理任何可能出现的错误
console.error('Error converting PDF toimages:',error);
});
}).catch((error) => {
// 处理打开 PDF 文档时可能出现的错误
console.error('Error opening PDF:', error);
});
};
reader.readAsDataURL(file);
};