Skip to content

pdf文档操作

🕒 Published at:

pdf-lib

支持以下功能

  • 从头开始创建 PDF 文档,或修改现有的 PDF 文档。绘制文本、图像和矢量图形。嵌入您自己的字体。甚至可以嵌入和绘制其他 PDF 中的页面。

  • 添加、插入和删除页面。将单个 PDF 拆分为单独的 PDF。或者将多个 PDF 合并到一个文档中。

  • 创建新表单或填写和读取现有字段。复选框、按钮、单选按钮组、下拉列表、选项列表和文本字段均受支持。

官方案例地址

具体api文档

pdfjs

版本 "pdfjs-dist": "^5.1.91",

可以读取pdf的图片和文档

js
import * as pdfjsLib from 'pdfjs-dist'
import workerSrc from 'pdfjs-dist/build/pdf.worker?worker&url'

// import pdfjsWorker from 'pdfjs-dist/build/pdf.worker.mjs'
// const pdfjsWorker = await import('pdfjs-dist/build/pdf.worker.mjs')
// 确保 PDF.js 使用正确的 worker 路径
pdfjsLib.GlobalWorkerOptions.workerSrc = workerSrc

/**
 * 提取 PDF 中的图片和文字
 * @param {File} file - PDF 文件
 * @returns {Promise<{ images: any, texts: string[] }>} - 提取的图片和文字
 */
export async function extractPDFContent(file: File): Promise<{ images: any; texts: string[] }> {
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader()
    fileReader.onload = async (e: any) => {
      try {
        const typedarray = new Uint8Array(e.target.result)
        const loadingTask = pdfjsLib.getDocument(typedarray)
        const { images, texts } = await processPDF(loadingTask)
        resolve({ images, texts })
      } catch (error) {
        reject(error)
      }
    }

    fileReader.onerror = (error) => {
      reject(error)
    }

    fileReader.readAsArrayBuffer(file)
  })
}

/**
 * 处理 PDF 文件
 * @param {Object} loadingTask - PDF 加载任务
 * @returns {Promise<{ images: any, texts: string[] }>} - 提取的图片和文字
 */
async function processPDF(loadingTask: any) {
  const pdf = await loadingTask.promise
  const numPages = pdf.numPages

  const images = new Map() // 存储图片的 URL 和对应的 Blob 数据
  const texts = [] // 存储每页的文字内容

  for (let curPage = 1; curPage <= numPages; curPage++) {
    const page = await pdf.getPage(curPage)
    // 提取图片
    const scale = 1.5
    const viewport = page.getViewport({ scale })
    const outputScale = window.devicePixelRatio || 1
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')

    const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null

    const renderContext = {
      canvasContext: context,
      transform,
      viewport
    }
    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) {
      page.objs.get(imageName, (image: any) => {
        ;(async () => {
          const bmp = image.bitmap
          const resizeScale = 1 / 4
          const width = bmp.width * resizeScale
          const height = bmp.height * resizeScale
          const canvas = new OffscreenCanvas(width, height)
          const ctx: any = canvas.getContext('bitmaprenderer')
          ctx.transferFromImageBitmap(bmp)
          const blob = await canvas.convertToBlob()
          const imageUrl = URL.createObjectURL(blob)
          images.set(imageUrl, blob) // 将图片 URL 和对应的 Blob 数据存储到对象中
        })()
      })
    }

    // 提取文字
    const textContent = await page.getTextContent()
    const textItems = textContent.items.map((item) => item.str).join('\n')
    if (textItems) {
      texts.push(textItems)
    }
  }

  return { images, texts }
}