javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript提取PDF图片

JavaScript提取PDF图片的方法详解

作者:NeoYu

这篇文章主要为大家详细介绍了如何使用JavaScript提取 PDF 图片,即把每一页里的多张图片都提取出来,不是把一整页都转换为一张图片,感兴趣的可以了解下

提取 PDF 图片,是指把每一页里的多张图片都提取出来,不是把一整页都转换为一张图片 ^_^ 。

效果 

技术实现详解 

使用 PDF.js 解析和渲染 PDFs,在 github 上有 43.9kb Star,非常成熟。

一、 引入 pdf.js

需要同时引入 pdf.js 和 WebWorker,其中 WebWorker 在浏览器子线程解析图片等资源,不阻塞页面UI和交互。引入代码如下:

<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.10.111/pdf.js"></script>

<script>
// pdf.js 从性能考虑,使用了 WebWorker, 在浏览器子线程解析图片等资源,不阻塞页面UI和交互。
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdn.jsdelivr.net/npm/pdfjs-dist@3.10.111/build/pdf.worker.js';
</script>

二、上传 PDF 文件并监听

上传文件时,使用 FileReader 异步文件读取对象,读取之后转换为 Uint8Array,传入 pdfjsLib,得到 PDF 加载任务。

代码如下:

<input type="file" id="fileInput" name="avatar" title ="选择 pdf 文件"/>

<script>
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener(
  "change",
  () => {
    const file = fileInput.files[0];
    console.log('fileInput file: ', file);
    // 上传文件时,使用 FileReader 对象读取
    var fileReader = new FileReader();
    fileReader.onload = function() {
          // 将文件对象转化为 Uint8Array 
          var typedarray = new Uint8Array(this.result);
          // 返回 PDF 加载任务 PDFDocumentLoadingTask
          const loadingTask = pdfjsLib.getDocument(typedarray);
          // 开始加载 PDF,这里封装了一个函数
          loadPDFFile(loadingTask);
    };

    fileReader.readAsArrayBuffer(file);
  },
  false
);
</script>

三、获取和遍历 PDF 页数

async function loadPDFFile(loadingTask) {
  // PDF 加载任务
  const pdf = await loadingTask.promise;
  // 获取 PDF 页数
  const numPages = pdf.numPages;
  for (let curPage = 1; curPage <= numPages; curPage++) {
    // 返回当前页
    console.log('loadingServerFile curPage: ', curPage);
    const page = await pdf.getPage(curPage);

    const scale = 1.5;
    // 获取渲染视角尺寸
    const viewport = page.getViewport({ scale });
    // Support HiDPI-screens.
    const outputScale = window.devicePixelRatio || 1;
    // 传入离屏 Canvas
    const canvas = new OffscreenCanvas(200, 200);
    // 获取 Canvas 上下文
    const context = canvas.getContext("2d");
    // 转换尺寸
    const transform = outputScale !== 1 
      ? [outputScale, 0, 0, outputScale, 0, 0] 
      : null;

    const renderContext = {
      canvasContext: context,
      transform,
      viewport,
    };
    // 调用 page.render 触发渲染
    const renderTask = page.render(renderContext);
    // 等待 renderTask 渲染任务
    // await delay(500); // 如果渲染过快,可能导致 CPU 飙升、电脑可用内存不够,可以加延迟减慢速度
    await renderTask.promise;
  }
}

代码里使用 OffscreenCanvas,提供了一个可以脱离屏幕渲染的 canvas 对象,在主线程和子线程 (web worker) 都可以使用,是为了性能优化,提取 PDF 图片不用渲染到页面,但也要渲染任务

如果渲染过快,可能导致 CPU 飙升、电脑可用内存不够,可以加延迟 setTimeout 减慢速度

四、提取每页 PDF 的图片

在这一步,我们获取了 pdfjs 提取的 PDF 图片,是 ImageBitmap 格式,它是对 Canvas 上位图数据的引用,存储在 GPU 中,可以在 web worker 进行生成,从而避免影响主线程绘制 UI,也避免阻塞响应用户交互。

代码如下:

async function loadPDFFile(loadingTask) {
    // ...接上一步
    const renderTask = page.render(renderContext);
    // 等待当前页渲染任务执行
    await renderTask.promise;
    // 获取操作列表
    const ops = await page.getOperatorList();

    // 提取图片
    const imageNames = ops.fnArray.reduce((acc, curr, i) => {
      if ([pdfjsLib.OPS.paintImageXObject, pdfjsLib.OPS.paintJpegXObject].includes(curr)) {
        acc.push(ops.argsArray[i][0]);
      }
      return acc;
    }, []);
    for (const imageName of imageNames) {
      console.log('imageName: ', imageName);
      page.objs.get(imageName, (image) => {
        // console.log('image: ', image);
        (async function() {          
          const bmp = image.bitmap;
          // create a canvas
          console.log('bmp: ', bmp);
        })();      
      });
    }
}

五、ImageBitmap 转化为 Img

再坚持一下,最后一步啦!─=≡Σ(((つ•̀ω•́)つ

在上一步中,我们获取了在 Canvas 绘图的引用 ImageBitmap,我们需要转换为浏览器下的 Img 图片。

具体过程:先将 ImageBitmap 渲染到 Canvas,调用 canvas.convertToBlob 就能得到 Blob 对象。

async function loadPDFFile(loadingTask) {
    // ...接上一步,拿到了每页 PDF 的 image.bitmap 对象
    for (const imageName of imageNames) {
      console.log('imageName: ', imageName);
      page.objs.get(imageName, (image) => {
        // console.log('image: ', image);
        (async function() {          
            const bmp = image.bitmap;
            console.log('bmp: ', bmp);
            // OffscreenCanvas
            const resizeScale = 1/4; // 这个可以控制转换后的图片大小
            const width = bmp.width * resizeScale;
            const height = bmp.height * resizeScale;
            const canvas = new OffscreenCanvas(width, height);
            // 获取 canvas bitmaprenderer 上下文
            const ctx = canvas.getContext('bitmaprenderer');
            // 把 ImageBitmap 渲染到 OffscreenCanvas
            ctx.transferFromImageBitmap(bmp);
            // 把 canvas 画布转化为 Blob 对象
            // https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas/convertToBlob
            const blob = await canvas.convertToBlob();
            console.log('blob: ', blob); // blob
            // 最后使用 Blob 作为 URL.createObjectURL 的参数,渲染出 img 图片
            // 如果不需要渲染,则可以讲 Blob 数据上传到云存储
            const img = document.body.appendChild(new Image());
            img.width = width;
            img.height = height;
            img.src = URL.createObjectURL(blob);
        })();      
      });
    }
}

OffscreenCanvas 可以放在 Web Worker 中创建,从而不阻塞主线程的UI绘制和交互响应。

bitmaprenderer.transferFromImageBitmap 可以实现不拷贝数据,只传递地址的方式,把 ImageBitmap 传递到 Canvas 上。

背景(缘起)

前段时间遇到 从 PDF 中提取图片用于 评论、审核 的功能。

由于项目时间赶、任务重,由后端实现了一页 PDF 转换为一张图片。

后端提取优劣:

前端提取优劣:

以上就是JavaScript提取PDF图片的方法详解的详细内容,更多关于JavaScript提取PDF图片的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文