65.9K
CodeProject 正在变化。 阅读更多。
Home

面向程序员的 PDF 操作

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2021 年 11 月 12 日

CPOL

6分钟阅读

viewsIcon

14939

本教程将解释 PDF 操作、其重要性以及用例。

PDF 操作,最简单的形式是创建、读取和编辑 PDF 文件。由于这些文件广泛用于多个行业的许多目的,因此您能够在软件或应用程序中操作 PDF 文件非常重要。

本教程将解释 PDF 操作、其重要性以及用例。它还将演示开发人员在创建 PDF 操作工具时面临的一些挑战,以及如何使用 Foxit 的 SDK 库轻松解决这些挑战。

访问 Foxit PDF SDK Web 演示,通过探索配置和功能来亲眼看看.

为什么要使用 PDF 操作

将文档和文件存储为 PDF 已变得至关重要,因为 PDF 是包含文本、图形或其他数据的文件的易于共享的副本。PDF 文件使企业和政府能够存储、阅读和共享文档,同时确保文件符合其数据保留策略。

随着对 PDF 文件的依赖日益增长,对创建、编辑和读取这些文件的需求也随之而来。企业可能还需要合并文件或从中删除页面。这些以及其他类型的更改,例如添加签名、注释或添加评论的能力,都属于 PDF 操作的范畴。

随着每个用例所需的功能和特性不断扩展,PDF 操作成为程序员的一项越来越重要的技能。

PDF 操作的挑战

PDF 文件旨在易于查看和共享,因此对其进行操作可能很困难。这会使构建 PDF 操作工具变得更具挑战性。

以下是涉及的一些挑战以及如何使用 Foxit 的 SDK 解决这些挑战。

您可以在 这个 GitHub 存储库 中找到这些示例的代码。

解码和读取 PDF 文件

任何 PDF 操作工具的一个基本部分是允许用户查看他们的 PDF 文件。即使是最流行的库,实现起来也可能很困难。

不使用 Foxit

假设我们要构建一个允许用户打开和查看 PDF 文件的 PDF 查看器。使用流行的 JavaScript 库 PDF.js,代码将类似于此

<input type="file" name="pdf" id="pdf_input" />
<canvas id="pdf" style="display: block;"></canvas>
<script src=""></script>
<script>
  const fileInput = document.getElementById("pdf_input");
  const pdfElement = document.getElementById("pdf");
  pdfjsLib.workerSrc = '';

  //bind change event to file input
  fileInput.addEventListener('change', decodePDF)

  function decodePDF() {
      const fileReader = new FileReader();
      fileReader.readAsDataURL(fileInput.files[0]);
      fileReader.onloadend = function (event) {
          convertToBinary(event.target.result);
      }
  }

  const BASE64_MARKER = ';base64,';

  function convertToBinary(dataURI) {
      const base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
      const base64 = dataURI.substring(base64Index);
      const raw = window.atob(base64);
      const rawLength = raw.length;
      const array = new Uint8Array(new ArrayBuffer(rawLength));

      for (let i = 0; i < rawLength; i++) {
          array[i] = raw.charCodeAt(i);
      }
      getPDF(array);
  }

  function getPageText(pageNum, PDFDocumentInstance) {
      // Return a Promise that is solved once the text of the page is retrieved return new Promise(function (resolve, reject) {
          PDFDocumentInstance.getPage(pageNum).then(function (pdfPage) {
              // The main trick to obtain the text of the PDF page, use the getTextContent method
              pdfPage.getTextContent().then(function (textContent) {
                  const textItems = textContent.items;
                  let finalString = "";

                  // Concatenate the string of the item to the final string for (let i = 0; i < textItems.length; i++) {
                      const item = textItems[i];

                      finalString += item.str + " ";
                  }

                  resolve(finalString);
              });
          });
      });
  }

  function getPDF(pdfAsArray) {

      pdfjsLib.getDocument(pdfAsArray).promise.then(function (pdf) {

        for(let i = 1; i <= pdf._pdfInfo.numPages; i++) {
          pdf.getPage(i).then(function (page) {
            const scale = 1.5;
            const viewport = page.getViewport({ scale: scale, });

            const canvas = document.getElementById('pdf');
            const context = canvas.getContext('2d');
            canvas.height = viewport.height;
            canvas.width = viewport.width;

            const renderContext = {
              canvasContext: context,
              viewport: viewport
            };
            page.render(renderContext);
          })
        }

      }).catch(console.error);
  }
</script>

此代码允许用户通过文件输入选择 PDF 文件。该文件以 base64 字符串的形式检索,然后转换为字节数组并传递给 PDF.js 的 getDocument 方法。这允许您遍历 PDF 的页面并将文件渲染到画布元素中。

正如您所见,当用户选择文件时,它会正确显示 PDF。

Viewer in PDF.js

使用 Foxit 的 SDK

Foxit 的 SDK 使查看过程更轻松。这是创建阅读器的代码

<input type="file" name="pdf" id="pdf_input" />
<div id="pdf"></div>
<script src="/license-key.js"></script>
<script src="/lib/PDFViewCtrl.full.js"></script> <script> const fileInput = document.getElementById("pdf_input");

  const pdfViewer = new PDFViewCtrl.PDFViewer({
      libPath: '/lib',
      jr: {
          licenseSN: licenseSN,
          licenseKey: licenseKey,
          tileSize: 300,
      },
  });
  pdfViewer.init('#pdf');


  function decodePDF (e) {
    pdfViewer.openPDFByFile(fileInput.files[0]);
  }

  fileInput.addEventListener('change', decodePDF);
</script>

请务必加载您在下载 SDK 时收到的 license-key.js/lib/PDFViewCtrl.full.js 脚本。然后初始化 PDF 查看器并指定应在其上渲染 PDF 的 HTML 元素。使用 openPDFByFile 方法查看用户选择的文件。

运行代码时,请注意它不仅成功渲染了 PDF,而且质量也大大提高了。Foxit 的渲染引擎之所以能够取得更好的效果,是因为它经过开发,即使是大型、复杂的文档,也能快速提供最佳质量的图像。

With Foxit’s SDK

添加和查看注释

共享或协作处理 PDF 文件的一部分是能够对文件进行注释或添加评论。

如果您要在 PDF 操作工具功能中添加注释,您需要将注释绘制到 PDF 中,而不是将其添加为文本。要添加注释,您需要在一个特定坐标(通常由用户选择)处添加一个矩形,然后将文本插入该矩形内。

这是因为 PDF 文件旨在用于查看而不是操作,而简单地将文本添加到文档中将不起作用。此格式最重要的是输出数据的视觉效果。因此,添加注释可能具有挑战性,尤其是因为用户需要在导出文件后在任何支持的 PDF 查看器上看到这些注释。

使用 Foxit 的 SDK

Foxit 的 SDK 包含一个完整的编辑器,该编辑器提供开箱即用的支持,用于在 PDF 文件中添加、查看和正确导出注释。要使用注释和评论,请包含从下载的 SDK 中所需的所有脚本,并添加一个用于渲染编辑器的 HTML 元素

<head>
        <link rel="stylesheet" href="/lib/UIExtension.css">
        <script src="/lib/adaptive.js"></script>
</head>
<body>
<div id="pdf"></div>
<script src="/license-key.js"></script> <script src="/lib/UIExtension.full.js"></script> <script src="/lib/preload-jr-worker.js"></script>

然后添加一个首先初始化 PDF UI 的脚本

const readyWorker = preloadJrWorker({
        workerPath: '/lib/',
        enginePath: '/lib/jr-engine/gsdk',
        licenseSN: licenseSN,
        licenseKey: licenseKey
    });

    const pdfui = new UIExtension.PDFUI({
        viewerOptions: {
            libPath: '/lib',
            jr: {
                readyWorker: readyWorker
            }
        },
        renderTo: '#pdf',
        appearance: UIExtension.appearances.adaptive,
        addons: UIExtension.PDFViewCtrl.DeviceInfo.isMobile ?
            '/lib/uix-addons/allInOne.mobile.js':
            '/lib/uix-addons/allInOne.js'
    });

    //open a ready file
    pdfui.openPDFByHttpRangeRequest({
        range: {
            url: '/Sample.pdf',
        }
    }, { fileName: 'Sample.pdf' });

这将使用 Foxit SDK 提供的 UI 初始化 PDF 查看器,其中添加了包括评论和注释在内的功能。

PDF UI

您可以在 PDF 查看器中添加和查看评论。

Manage comments

当您下载 PDF 文件时,您将能够在其他 PDF 查看器中查看注释。

Using another PDF viewer

在浏览器中尝试我们的 SDK Web 演示,无需下载或登录.

数字签名

数字签名在某些企业中经常使用,但确保其安全性和有效性可能很复杂。

签名文档需要几个步骤

  1. 打开文档并添加 UI 以允许用户签名文档。
  2. 将签名转换为文件流。
  3. 计算带有签名的文档的消息摘要。
  4. 使用签名者的数字私钥(如 p12 文件)以及您的证书或密钥加密消息和文档。
  5. 将加密的签名数据写入文件流。

然后,您需要执行以下步骤在服务器上验证签名数据

  1. 获取原始 PDF 文档的内容、签名范围、签名数据和签名者信息。
  2. 计算带有签名范围的内容的消息摘要。
  3. 将计算出的摘要与签名数据进行验证。

如您所见,此过程很复杂,从头开始构建它可能会很麻烦。

使用 Foxit 的 SDK

Foxit 的 SDK 提供了现成的 API,可以执行整个过程。

要在 PDF 操作工具中添加数字签名功能,首先要初始化 Foxit 的 PDF UI。(上一节已包含此代码。)

除其他功能外,SDK 还提供了一个用于数字签名的签名字段。

Adding signature form

然后添加一个签名处理程序。当用户签署签名表单字段时,处理程序会将与签名和签名者相关的数据与 PDF 转换为文件流,然后将 Blob 发送到服务器。服务器会计算签名的消息摘要并返回签名文档。

pdfui.registerSignHandler({
    filter: 'Adobe.PPKLite',
    subfilter: 'adbe.pkcs7.sha1',
    flag: 0x100,
    distinguishName: 'e=support@yourcompany.com',
    location: 'FZ',
    reason: 'Test',
    signer: 'web sdk',
    showTime: true,
    sign: function(setting, buffer) {

        const formData = new FormData();
        formData.append('plain', new Blob([buffer]));


        return fetch('https://webviewer-demo.foxitsoftware.com/signature/digest_and_sign', {
          method: 'POST',
          body: formData
        }).then((response) => response.arrayBuffer());
    }
});

Signing signature form field

上面的示例使用了 Foxit 的 测试服务器。使用 Foxit 提供的示例,您的服务器的 digest_and_sign 代码可能如下所示

router.post('/digest_and_sign', koabody({ multipart: true }), async (ctx) => {
    fs.copyFileSync(ctx.request.files.plain.path, '.\\temp\\plain');
    let { filter, subfilter, signer, md } = ctx.request.body;
    if (!md) md = 'sha1';
    if (!subfilter) subfilter = 'adbe.pkcs7.detached';
    if (subfilter == 'adbe.pkcs7.sha1') {
        process.execSync(
            '.\\bin\\pkcs7.exe digest .\\temp\\plain .\\temp\\sha1'
        );
        process.execSync(
            '.\\bin\\pkcs7.exe sign .\\bin\\foxit_all.pfx 123456 .\\temp\\sha1 .\\temp\\signedData'
        );
    } else if ((subfilter = 'adbe.pkcs7.detached')) {
        switch (md) {
            case 'sha1':
                md = '0';
                break;
            case 'sha256':
                md = '1';
                break;
            case 'sha384':
                md = '2';
                break;
        }
        process.execSync(
            '.\\bin\\pkcs7.exe sign .\\bin\\foxit_all.pfx 123456 .\\temp\\plain .\\temp\\signedData Yes ' +
                md
        );
    }
    ctx.body = fs.createReadStream('.\\temp\\signedData');
    return;
});

请注意,此代码要求您使用 PKCS7 和 PFX 密钥。

接下来是验证步骤。要验证 PDF 的签名,请将消息摘要以及与签名和签名者相关的所有数据发送到服务器

pdfui.setVerifyHandler(function (signatureField, plainBuffer, signedData){
  const formData = new FormData();
  formData.append('filter', signatureField.getFilter());
  formData.append('subfilter', signatureField.getSubfilter());
  formData.append('signer', signatureField.getSigner());
  formData.append('plainContent', new Blob([plainBuffer]));
  formData.append('signedData', new Blob([signedData]));

  return fetch('https://webviewer-demo.foxitsoftware.com/signature/verify', {
          method: 'POST',
          body: formData
        }).then((response) => response.text());
});

当 Foxit SDK UI 中的签名签名字段被点击时,将执行此处理程序

Verification response

同样,这使用的是 Foxit 的测试服务器来验证签名。如果您想在服务器上验证签名,代码将如下所示

router.post('/verify', koabody({ multipart: true }), async (ctx) => {
    let { filter, subfilter, signer } = ctx.request.body;


    fs.copyFileSync(
        ctx.request.files.plainContent.path,
        '.\\temp\\plainBuffer'
    );
    fs.copyFileSync(ctx.request.files.signedData.path, '.\\temp\\signedData');


    if (subfilter == 'adbe.pkcs7.sha1') {
        process.execSync(
            '.\\bin\\pkcs7.exe digest .\\temp\\plainBuffer .\\temp\\digest'
        );
        process.execSync(
            '.\\bin\\pkcs7.exe verify .\\temp\\signedData .\\temp\\digest .\\temp\\output'
        );
    } else if ((subfilter = 'adbe.pkcs7.detached')) {
        process.execSync(
            '.\\bin\\pkcs7.exe verify .\\temp\\signedData .\\temp\\plainBuffer .\\temp\\output'
        );
    }


    ctx.body = fs.createReadStream('.\\temp\\output');
});

结论

随着对使用、共享和协作处理 PDF 文件的需求增加,将 PDF 操作工具集成到您的系统或应用程序中是必不可少的。然而,PDF 的设计初衷是为了让多个用户轻松查看,而不一定是为了编辑。

因此,使用 Foxit 等 PDF 操作 SDK 对于您的项目至关重要。Foxit 为所有平台和项目类型提供 SDK 和 API,以便您可以轻松地操作 PDF 文件。

在您选择的平台(Web、Windows、Android、iOS、Linux、UWP 或 Mac)上试用 Foxit PDF SDK 的先进技术。立即注册 免费三十天试用

© . All rights reserved.