如何从 PDF 中提取页面并使用 JavaScript 渲染它们(下)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

如何在 JavaScript 中读取本地 PDF 文件

在对我们的 PDF 文档进行任何操作之前,我们必须从用户那里获取文档。在浏览器中读取任何文件都可以通过FileReaderWeb API 处理。

首先,我们将制作文件输入按钮,然后使用FileReaderWeb API 处理上传的文件。

<input type="file" id="file-selector" accept=".pdf" onChange={onFileSelected} />
复制代码

由于 Filereader API 与回调一起使用,我发现 async/await 更加简洁且易于使用。因此,让我们创建一个辅助函数来将 Filereader 回调修改为 async/await。

function readFileAsync(file) {
    return new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.onload = () => {
        resolve(reader.result);
      };
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    });
  }
复制代码

现在,当用户使用之前的文件输入上传文件时,我们会监听文件输入事件,然后使用此readFileAsync函数读取文件。

此逻辑的实现在代码中如下所示:

const onFileSelected = async (e) => {
    const fileList = e.target.files;
    if (fileList?.length > 0) {
      const pdfArrayBuffer = await readFileAsync(fileList[0]);
    }
  };
复制代码

如何提取 PDF 页面

至此,我们的 PDF 已上传并转换为 JavaScript ArrayBuffer。当我们从 PDF 中提取一系列页面时,我们需要一个包含 PDF 的页码的数组。

在 JavaScript 中生成自然数数组并不难。所以我们创建了一个函数range()来生成我们想要的所有索引。

我们必须提供起始页码和结束页码,然后此range()函数可以生成具有适当页码的数组。

function range(start, end) {
	let length = end - start + 1;
	return Array.from({ length }, (_, i) => start + i - 1);
}
复制代码

在这里,我们在末尾添加 -1。你知道原因吗?是的——在编程中,索引从 0 开始,而不是 1。所以我们必须从每个页码中减去 -1 以获得我们想要的行为。

现在让我们开始本文的主要部分:提取。在进行任何工作之前,请导入 pdf-lib 库。

import { PDFDocument } from "pdf-lib";
复制代码

首先,我们加载ArrayBuffer从上一个onFileSelected函数中获得的 PDF。然后我们将 加载ArrayBufferPDFDocument.load(arraybuffer)函数中。这是我们用户提供的 PDF。为方便起见,我们将其称为pdfSrcDoc

现在我们将创建一个新的 PDF。从用户提供的文档中提取的所有 PDF 页面都合并到新文档中。我们使用该PDFDocument.create()功能来做到这一点。为了使用方便,我们称之为pdfNewDoc

之后,我们使用该功能将所需的页面从其中复制pdfSrcDoc到。然后我们将复制的页面添加到.pdfNewDoc``copyPages()``pdfNewDoc

要保存更改,请运行pdfNewDoc.save(). 让我们创建一个调用extractPdfPage()来重用逻辑的函数。函数内部的代码如下所示:

async function extractPdfPage(arrayBuff) {
    const pdfSrcDoc = await PDFDocument.load(arrayBuff);
    const pdfNewDoc = await PDFDocument.create();
    const pages = await pdfNewDoc.copyPages(pdfSrcDoc,range(2,3));
    pages.forEach(page=>pdfNewDoc.addPage(page));
    const newpdf= await pdfNewDoc.save();
    return newpdf;
  }
复制代码

我们Uint8ArrayextractPdfPage()函数中返回一个。

如何在浏览器中渲染 PDF

到目前为止,我们有Uint8Array一个修改后的 PDF。要在浏览器中呈现它,我们必须将其转换为 Blob。

然后我们将用它制作一个 URL 并在 iframe 中呈现它。

如上所述,你还可以使用 pdfjs 库制作自定义 PDF 查看器。但是,如果你不需要这样的品牌和自定义,浏览器的默认 PDF 查看器就可以用于此目的。

function renderPdf(uint8array) {
    const tempblob = new Blob([uint8array], {
      type: "application/pdf",
    });
    const docUrl = URL.createObjectURL(tempblob);
    setPdfFileData(docUrl);
  }
复制代码

现在,你可以轻松地renderPdf()iframe.

完整的代码示例

我在本教程中使用 Next.js。如果你使用其他框架或原生 JavaScript,结果将是相似的。这是这个项目的所有代码:

import { useState } from "react";
import { PDFDocument } from "pdf-lib";

export default function Home() {
  const [pdfFileData, setPdfFileData] = useState();

  function readFileAsync(file) {
    return new Promise((resolve, reject) => {
      let reader = new FileReader();
      reader.onload = () => {
        resolve(reader.result);
      };
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    });
  }

  function renderPdf(uint8array) {
    const tempblob = new Blob([uint8array], {
      type: "application/pdf",
    });
    const docUrl = URL.createObjectURL(tempblob);
    setPdfFileData(docUrl);
  }

  function range(start, end) {
    let length = end - start + 1;
    return Array.from({ length }, (_, i) => start + i - 1);
  }

  async function extractPdfPage(arrayBuff) {
    const pdfSrcDoc = await PDFDocument.load(arrayBuff);
    const pdfNewDoc = await PDFDocument.create();
    const pages = await pdfNewDoc.copyPages(pdfSrcDoc, range(2, 3));
    pages.forEach((page) => pdfNewDoc.addPage(page));
    const newpdf = await pdfNewDoc.save();
    return newpdf;
  }

  // Execute when user select a file
  const onFileSelected = async (e) => {
    const fileList = e.target.files;
    if (fileList?.length > 0) {
      const pdfArrayBuffer = await readFileAsync(fileList[0]);
      const newPdfDoc = await extractPdfPage(pdfArrayBuffer);
      renderPdf(newPdfDoc);
    }
  };

  return (
    <>
      <h1>Hello world</h1>
      <input
        type="file"
        id="file-selector"
        accept=".pdf"
        onChange={onFileSelected}
      />
      <iframe
        style={{ display: "block", width: "100vw", height: "90vh" }}
        title="PdfFrame"
        src={pdfFileData}
        frameborder="0"
        type="application/pdf"
      ></iframe>
    </>
  );
}
复制代码

你现在可以使用 PDF 查看器上的下载按钮保存生成的 PDF。

结论

感谢你的阅读

猜你喜欢

转载自juejin.im/post/7110585808993648653