VUE3使用pdfjs转换pdf文件为图片

背景:vue3展示给定pdf的url,把第一页转换成图片,无法运行各种报错。根据网上搜索到的文章和文心一言问了半天处理方法都不行,最后翻看PDF.JS官方文档得以解决,现记录。

PDF.js - Examples

1.版本问题,网络上搜索到的文章基本上都是很久之前的方法,新版本做了大幅度更新导致各种不兼容,建议使用2.2.228版本。

2.报错​​​​​​Error: No “GlobalWorkerOptions.workerSrc“ specified.

pdfjs工作的时候必须要先指定GlobalWorkerOptions.workerSrc

3.报错TypeError: Cannot set properties of undefined (setting 'workerSrc')

一开始搜到的办法都是

import * as pdfjsLib from 'pdfjs-dist';
import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.entry';
pdfjsLib.GlobalWorkerOptions.workerSrc = pdfjsWorker;

或者是

import * as pdfjsLib from 'pdfjs-dist';
const pdfJS = require('pdfjs-dist')
pdfJS.GlobalWorkerOptions.workerSrc = require('pdfjs-dist/build/pdf.worker.entry')

又或者是

import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc =  '../../../node_modules/pdfjs-dist/build/pdf.worker.mjs';

报错TypeError: Cannot set properties of undefined (setting 'workerSrc')

以上方法全都用不了,读取js文件的时候都会失败。因为某些原因无法直接从node_modules文件夹里直接读取js文件,所以必须拿出来放到项目文件里调用。正确的代码如下

import * as pdfjsLib from 'pdfjs-dist';
pdfjsLib.GlobalWorkerOptions.workerSrc = '/js/pdf.worker.min.js';

指定一个项目内的路径比如public之类的文件夹,把下载到的node_modules\pdfjs-dist\build里的pdf.worker.js或者pdf.worker.min.js都行(min就是压缩版本)拿到项目的public/js文件夹里用

不要使用pdf.worker.entry,读不到

4.报错TypeError: Promise.withResolvers is not a function

因为在读取promise的时候还没加载出来,要用异步函数嵌套保证读取的时候promise已经加载了

网上搜到的代码一般都是

const loadingTask = pdfjsLib.getDocument(this.pdfUrl);
const pdf = await loadingTask.promise;
const page = await pdf.getPage(1); // Get the first page

 这样不行,promise还是没加载到。

改成如下就不会报错了

loadingTask.promise.then(function(pdf) {
          renderPdfPage(pdf, pageNumber); // 现在可以安全地传递 pdf 对象
        });

5.page和viewport读空体报错

同理,因为读viewport的时候page没加载出来

pdf.getPage(1).then(async function (page) {
        const scale = 1.5;
        const viewport = page.getViewport({scale: scale,});

这样写就保证加载了

6.完整的代码如下

<template>
  <div>
    <div ref="pdfContainer" class="pdf-container" v-if="!imageDataUrl"></div>
    <div v-if="imageDataUrl">
      <img :src="imageDataUrl" alt="PDF Page as Image" />
    </div>
  </div>
</template>

<script>
import { ref, onMounted, defineProps } from 'vue';
import * as pdfjsLib from 'pdfjs-dist';

export default {
  name: 'PdfToImage',
  props: {
    pdfUrl: {
      type: String,
      required: true,
    },
    pageNumber: {
      type: Number,
      default: 1,
    },
  },
  setup(props) {
    const pdfContainer = ref(null);
    const imageDataUrl = ref(null);

    const renderPdfPage = async (pdf, pageNumber) => {
      const canvas = document.createElement('canvas');
      const context = canvas.getContext('2d');
      pdf.getPage(1).then(async function (page) {
        const scale = 1.5;
        const viewport = page.getViewport({scale: scale,});
        canvas.width = viewport.width;
        canvas.height = viewport.height;
        canvas.style.width = Math.floor(viewport.width*0.62) + "px";
        canvas.style.height =  Math.floor(viewport.height*0.62) + "px";
       
        const renderContext = {
          canvasContext: context,
          viewport: viewport,
        };

        await page.render(renderContext).promise;

        pdfContainer.value.appendChild(canvas);
      });
    };

    const loadPdf = async (url, pageNumber) => {
      const loadingTask = pdfjsLib.getDocument(url);
      try {
        loadingTask.promise.then(function(pdf) {
          renderPdfPage(pdf, pageNumber); // 现在可以安全地传递 pdf 对象
        });
      } catch (error) {
        console.error('Error loading PDF:', error);
      }
    };


    onMounted(() => {
      pdfjsLib.GlobalWorkerOptions.workerSrc = '/js/pdf.worker.min.js';
      loadPdf(props.pdfUrl, props.pageNumber);
    });

    // Expose necessary reactive variables to the template
    return {
      pdfContainer,
      imageDataUrl,
    };
  },
};
</script>

<style scoped>
.pdf-container {

}
</style>

默认读取pdf的第一页,需要改的话把getPage里的数值改成传入的。需要读取多页就改成for循环

在需要用的地方这样调用这个组件就行,把pdf的url传进去

<PdfToImage :pdfUrl="pdfUrl" :pageNumber="1" class=""/>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值