在TinyMCE编辑器上传pdf,并且解析回显到编辑器中

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);

          
      
           
   
           
           
             
            };

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值