使用 Adobe PDF Embed API 掌控 PDF

Avatar of Raymond Camden
Raymond Camden

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

根据我们最近的估计,世界上现有的 PDF 数量已经超过了宇宙中的原子数量(未经外部来源验证),因此很有可能,您时不时会遇到一个或两个 PDF 文档。浏览器在处理 PDF 方面做得相当不错。通常情况下,点击指向 PDF 的链接会在浏览器中打开一个新标签页,并使用自定义 UI 和渲染效果,具体取决于浏览器。以下是在 Edge、Chrome、Firefox 和 Safari 中打开的同一个 PDF 的示例:

正如预期的那样,每个浏览器都对 PDF 文档进行了一些处理,但有一点是始终如一的——所有浏览器都接管了整个视窗来渲染 PDF。虽然这对于为读者提供尽可能多的空间来阅读 PDF 非常有用,但在某些情况下,我们可能希望对 PDF 体验有更多控制权。这就是 Adobe PDF Embed API 的作用。PDF Embed API 是一个免费的 JavaScript 库,它允许您将 PDF 文档与其他内容内联显示,并为您提供对工具 UI、支持注释和事件等的控制权。让我们通过一些示例来了解如何使用该库。

获取密钥

在我们开始之前,您需要注册一个密钥。如果您访问我们的 入门 页面,您会看到一个创建新凭据的链接。

如果您还没有 Adobe 帐户,则需要创建一个。系统会提示您为凭据命名并指定应用程序域。名称并不十分重要,但应用程序域很重要。您获得的密钥将限制在特定域。您只能在此处输入一个域,因此,您可以使用 localhost 作为域来开始,或者如果您想在 CodePen 上尝试,可以使用 cdpn.io 作为域。如果您想在本地和生产环境中使用 API,则可以在控制台中创建多个项目或使用 HOSTS 文件配置。(能够为凭据指定多个域是我们的目标。)

点击可爱的蓝色“创建凭据”按钮,您将获得您的密钥。

如果您好奇并想立即了解 Embed API 的功能,请点击“获取代码示例”,这将带您进入一个交互式在线演示。但由于我们是坚定的程序员,在开始工作之前会先构建自己的编辑器,因此让我们直接深入一个简单的示例。

构建演示

首先,让我们构建一个承载 PDF 的 HTML 页面。我已经是一名 Web 开发人员 20 年了,现在是设计精美 HTML 页面的专家。以下是我设计出来的页面:

<html>
  <head></head>
  <body>
    <h1>Cats are Everything</h1>
    <p>
      Cats are so incredibly awesome that I feel like
      we should talk about them more. Here's a PDF
      that talks about how awesome cats are.
    </p>
		
    <!-- PDF here! -->

    <p>
      Did you like that? Was it awesome? I think it was awesome! 
    </p>
  </body>
</html>

当然,我添加了一些 CSS 代码:

A heading one that says Cats are Everything, followed by two short paragraphs about cats. The text is white against a green background.

说实话,我不知道 Adobe 为什么聘请我担任开发者布道师,因为我显然应该在设计团队工作。无论如何,我们如何将 PDF 放入其中呢?第一步是添加我们的库 SDK:

<script src="https://documentcloud.adobe.com/view-sdk/main.js"></script>

现在我们需要一些 JavaScript 代码。当我们的库加载时,它会触发一个名为 adobe_dc_view_sdk.ready 的事件。根据您加载脚本的方式和您选择的框架,该事件可能会在您有机会检查它之前就触发。

我们也可以检查 window.AdobeDC 是否存在。我们可以通过将它们链接到一个函数来处理这两种情况,该函数将设置我们的 PDF。

if (window.AdobeDC) displayPDF();
else {
  document.addEventListener("adobe_dc_view_sdk.ready", () => displayPDF());
}

function displayPDF() {
  console.log('Lets do some AWESOME PDF stuff!');
}

好了,我们如何显示 PDF 呢?为了接受所有默认设置,我们可以使用以下代码片段:

let adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "mypdf" });
adobeDCView.previewFile({
  content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
  metaData:{fileName: "cat.pdf"}
});

让我们来分析一下。首先,我们创建一个新的 AdobeDC.View 对象。clientId 值是之前获得的密钥。divId 是 DOM 中一个 <div> 的 ID,PDF 将在该 <div> 中渲染。我删除了之前添加的 HTML 注释,并在其中插入了一个带有该 ID 的空 <div>。我还使用了一些 CSS 代码为其指定了宽度和高度:

#mypdf {
  width: 100%;
  height: 500px;
}

previewFile 方法接受两个主要参数。第一个是 PDF URL。PDF Embed API 同时支持 URL 和文件 Promise。对于 URL,我们希望确保已正确设置了 CORS。第二个值是关于 PDF 的元数据,在本例中是文件名。以下是结果:

以下是完整示例的 CodePen 代码,您可以克隆它、修改它并继续使用该密钥。

您会注意到 UI 包含您在任何 PDF 查看器中期望看到的相同工具,以及添加笔记和注释等功能。

注意上图中的“保存”图标。下载时,PDF 将包含注释和漂亮的标记绘制。

自定义体验

好的,您已经看到了基本示例,现在让我们来进行一些改进,并自定义体验。我们可能首先要做的就是更改嵌入模式,该模式控制 PDF 的显示方式。库支持四种不同的模式:

  • 大小容器——默认模式,用于在 <div> 容器内渲染 PDF。它一次渲染一个页面。
  • 全窗口——与大小容器类似,它会“填充”其父 <div>,但会将整个 PDF 显示在一个可以滚动的“流”中。
  • 内联——在网页中显示,类似于大小容器,但会将每个页面渲染成一个垂直堆叠。显然,不要对大型的 99 页 PDF 使用这种模式,除非您讨厌您的用户。(但是,如果您已经在用户访问您的网站时显示了“订阅我们的新闻稿”模态窗口,或者您的网站自动播放视频,那么您可以随意使用这种模式。)
  • 灯箱——将 PDF 显示在一个居中的窗口中,同时将其他内容变暗。关闭显示的 UI 会自动包含在内。

要指定不同的视图,可以传递一个包含选项的第二个参数。例如:

function displayPDF() {
  console.log('Lets do some AWESOME PDF stuff!');
  let adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "mypdf" });
  adobeDCView.previewFile({
    content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
    metaData:{fileName: "cat.pdf"}
  }, 
  {
    embedMode: "IN_LINE"
  });	
}

请注意,在内联模式下,为您的 div 指定的高度将被忽略,以便 PDF 可以稍微伸展一下。您可以在这里查看该版本的演示:https://codepen.io/cfjedimaster/pen/OJpJRKr

让我们考虑另一个示例——使用灯箱和按钮,我们可以让用户在需要时加载 PDF。我们可以像这样修改 HTML 代码:

<html>
  <head></head>
  <body>
    <h1>Cats are Everything</h1>
    <p>
      Cats are so incredibly awesome that I feel like
      we should talk about them more. Here's a PDF
      that talks about how awesome cats are.
    </p>
		
    <!-- PDF here! -->
    <button id="showPDF" disabled>Show PDF</button>

    <p>
      Did you like that? Was it awesome? I think it was awesome! 
    </p>
  </body>
</html>

我在 HTML 中添加了一个禁用的按钮,并删除了空的 <div>。我们不需要它,因为灯箱模式将使用模态视图。现在我们修改 JavaScript 代码:

const ADOBE_KEY = 'b9151e8d6a0b4d798e0f8d7950efea91';

if(window.AdobeDC) enablePDF();
else {
  document.addEventListener("adobe_dc_view_sdk.ready", () => enablePDF());
}

function enablePDF() {
  let btn = document.querySelector('#showPDF');
  btn.addEventListener('click', () => displayPDF());
  btn.disabled = false;
}

function displayPDF() {
  console.log('Lets do some AWESOME PDF stuff!');
  let adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY });
  adobeDCView.previewFile({
    content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
    metaData:{fileName: "cat.pdf"}
  }, 
  {
    embedMode: "LIGHT_BOX"
  });	
}

这里有两个主要变化。首先,检查库是否正在加载(或已加载)会运行 enablePDF,该函数会从按钮中删除禁用的属性并添加一个点击事件。这将运行 displayPDF。请注意,初始化程序不再使用 divId。其次,请注意 embedMode 模式更改。您可以通过下面的 Pen 代码亲身体验。

您还有更多自定义选项,包括调整 UI 菜单和图标 以启用和禁用各种功能。

adobeDCView.previewFile({
	content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
	metaData:{fileName: "cat.pdf"}
}, 
{
	showDownloadPDF: false,
	showPrintPDF: false,
	showAnnotationTools: false,
	showLeftHandPanel: false
});	

您可能猜到了这将做什么,但这里有一张带有默认选项的截图:

以下是禁用这些选项后的样子:

顺便说一句,为了明确起见,我们当然知道禁用下载按钮并不会“保护”这里看到的 PDF,因为 URL 仍然可以通过查看源代码看到。

同样,这只是一个简单的示例,因此请务必查看 自定义文档 以获取更多示例。

使用 API 并处理事件

除了自定义 UI,我们还可以对加载后的体验进行细粒度的控制。这是通过一个 API 支持的,该 API 可以返回有关 PDF 的信息,还可以监听事件。

使用 API 使用 previewFile 方法的结果。我们还没有使用它,但它会返回一个 Promise。API 的一个用途是获取元数据。以下是一个示例:

let resultPromise = adobeDCView.previewFile({
  content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
  metaData:{fileName: "cat.pdf"}
}, { embedMode:"SIZED_CONTAINER" });	

resultPromise.then(adobeViewer => {
  adobeViewer.getAPIs().then(apis => {
    apis.getPDFMetadata()
    .then(result => console.log(result))
    .catch(error => console.log(error));
  });
});

这将返回:

{
  'numPages':6,
  'pdfTitle':'Microsoft Word - Document1',
  'fileName':''
}

除了 API 调用,我们还拥有深入的分析集成。虽然 文档 对此进行了详细说明(并讨论了与 Adobe Analytics 的集成),但您可以以任何对您有意义的方式处理 PDF 查看和交互事件。

例如,由于我们知道 PDF 中有多少页,并且可以监听查看页面的事件,因此我们可以注意到用户何时已查看所有页面。为了构建这个功能,我修改了 JavaScript 代码,如下所示:

const ADOBE_KEY = 'b9151e8d6a0b4d798e0f8d7950efea91';

//used to track what we've read
const pagesRead = new Set([1]);
let totalPages, adobeDCView, shownAlert=false;

if(window.AdobeDC) displayPDF();
else {
  document.addEventListener("adobe_dc_view_sdk.ready", () => displayPDF());
}

function displayPDF() {
  console.log('Lets do some AWESOME PDF stuff!');
  adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "mypdf" });
	
  let resultPromise = adobeDCView.previewFile({
    content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
    metaData:{fileName: "cat.pdf"}
  }, { embedMode:"SIZED_CONTAINER" });	

  resultPromise.then(adobeViewer => {
    adobeViewer.getAPIs().then(apis => {
      apis.getPDFMetadata()
      .then(result => {
        totalPages = result.numPages;
        console.log('totalPages', totalPages);
        listenForReads();
      })
      .catch(error => console.log(error));
    });
  });
	
}

function listenForReads() {
	
  const eventOptions = {
    enablePDFAnalytics: true
  }

  adobeDCView.registerCallback(
  AdobeDC.View.Enum.CallbackType.EVENT_LISTENER,
  function(event) {
    let page = event.data.pageNumber;
    pagesRead.add(page);
    console.log(`view page ${page}`);
    if(pagesRead.size === totalPages && !shownAlert) {
      alert('You read it all!');
      shownAlert = true;
    }
  }, eventOptions
);

}

请注意,在我获取有关页数的信息后,我运行了一个函数,该函数开始监听页面查看事件。我使用一个 Set 来记录每个唯一的页面,当总数等于 PDF 中的页数时,我将发出一个 alert 消息。(当然,我们不知道读者是否真正阅读了文本。)虽然这有点无聊,但您可以在以下位置自己尝试一下:

const ADOBE_KEY = 'b9151e8d6a0b4d798e0f8d7950efea91';

//used to track what we've read
const pagesRead = new Set([1]);
let totalPages, adobeDCView, shownAlert=false;

if(window.AdobeDC) displayPDF();
else {
  document.addEventListener("adobe_dc_view_sdk.ready", () => displayPDF());
}

function displayPDF() {
  console.log('Lets do some AWESOME PDF stuff!');
  adobeDCView = new AdobeDC.View({clientId: ADOBE_KEY, divId: "mypdf" });
	
  let resultPromise = adobeDCView.previewFile({
    content:{location: {url: "https://static.raymondcamden.com/enclosures/cat.pdf"}},
    metaData:{fileName: "cat.pdf"}
  }, { embedMode:"SIZED_CONTAINER" });	

  resultPromise.then(adobeViewer => {
    adobeViewer.getAPIs().then(apis => {
      apis.getPDFMetadata()
      .then(result => {
        totalPages = result.numPages;
        console.log('totalPages', totalPages);
        listenForReads();
      })
      .catch(error => console.log(error));
    });
  });
	
}

function listenForReads() {
	
  const eventOptions = {
    listenOn: [ AdobeDC.View.Enum.PDFAnalyticsEvents.PAGE_VIEW ],
    enablePDFAnalytics: true
  }

  adobeDCView.registerCallback(
    AdobeDC.View.Enum.CallbackType.EVENT_LISTENER,
    function(event) {
      /*
       console.log("Type " + event.type);
       console.log("Data " + JSON.stringify(event.data));
      */
      let page = event.data.pageNumber;
      pagesRead.add(page);
      console.log(`view page ${page}`);
      if(pagesRead.size === totalPages && !shownAlert) {
        alert('You read it all!');
        shownAlert = true;
      }
    }, eventOptions
  );

}

如何了解更多

我希望本篇关于嵌入 API 的介绍对你有所帮助。以下是一些资源,可以帮助你更深入地了解它。

  • 首先,你可以浏览一下 文档,它详细介绍了所有内容。
  • 我们还有一个 实时演示,让你可以直观地看到所有功能,甚至可以为你生成代码。
  • 如果你有任何问题或需要支持,我们有一个 论坛 供你提问,你也可以在 StackOverflow 上使用 adobe-embed-api 标签。
  • 如果你需要在服务器级别处理 PDF,我们还有 Adobe PDF 工具 API 以及一个非常酷的 Adobe 文档生成 工具。这些工具不像 PDF 嵌入 API 那样免费,但你可以试用六个月,并通过 注册 试用。

最后,我们非常欢迎你对这方面的反馈。如果你有任何建议、想法、问题或其他任何事项,请随时联系我们!