PDF 为世界提供了一种在各种设备之间高度兼容的共享文档和媒体的通用格式,但以编程方式生成它们通常很棘手。
我们将探索如何在不同环境中使用 JavaScript 生成 PDF 的一些选项。
生成 PDF 的麻烦之处……
在使用 PDF 时,您通常会像阅读图像一样阅读或查看它们,但如果您曾经尝试复制某些文本、搜索 PDF 或单击链接,您可能已经注意到 PDF 不仅仅是一张静态图像。
许多生成 PDF 的解决方案依赖于将其生成为图像,但缺乏嵌入文本所提供的可访问性和可用性。
但根据您的限制和环境,也许值得权衡。
html2pdf.js
html2pdf.js是一个客户端库,允许您使用 Canvas(特别是html2canvas和jsPDF)从 HTML 呈现 PDF 。
要创建 PDF,您需要指定要从页面呈现的元素,将其传递到 html2pdf 实例中,然后库将生成 PDF 并提示用户下载它。
如何开始?
您可以通过几种不同的方式包含 html2pdf.js,包括指向第三方 CDN 的脚本标签或通过 npm 导入它。
如果使用 npm,首先安装 html2pdf.js:
npm install html2pdf.js
将其作为依赖项导入:
import html2pdf from 'html2pdf.js';
选择一个 HTML 元素并将其传递给 html2pdf:
html2pdf(document.getElementById('my-id'));
这将提示您的用户开始下载文件。
您还可以在支持的地方动态导入依赖项,以避免将其直接与您的应用程序捆绑在一起并仅在需要时加载它。
const html2pdf = await require('html2pdf.js');
html2pdf(document.getElementById('my-id'));
文档中有很多可用的选项。
例如,在我显示发票的页面上,html2pdf.js 将呈现:
它有什么好处?
html2pdf 允许您使用 JavaScript 直接在浏览器中生成 PDF。这意味着您无需向外部服务器发出请求,因此您需要处理的基础设施和网络请求更少。
您还可以使用任何工具以任何您想要的方式管理您的 HTML,因为最终您将传递一个 DOM 节点来呈现。
这也提高了您已经构建的页面的可重用性,因此您不必为 UI 和 PDF 版本分别维护多个页面。
还有什么更好的呢?
总体来说,它运行良好,但在生成目标 HTML 时,渲染可能会有点不一致。CSS 可能不太完美,有时页面的某些部分可能会出现偏移或被切断。
虽然 API 为您提供了一些选项和灵活性,但您仍然受到页面呈现方式的限制。您可以隐藏内容 ( data-html2canvas-ignore
),但您无法提供特定样式,例如,您可以像打印预览那样提供特定样式。
页面移动导致项目被切断的问题似乎可以通过在 html2pdf.js GitHub Issues中找到的一些基本样式来修复。
@layer base { img { display: initial; } }
PDF Kit 和 React PDF
PDF Kit是另一个 JavaScript HTML 到 PDF 渲染工具,其工作方式略有不同。
PDF Kit 本身实际上并不呈现 HTML,但允许您使用它所描述的类似 HTML5 Canvas 的 API 来创建和定位元素。
但是在 React 环境中工作,你会得到更接近 HTML 或 JSX 的东西,其中React PDF使用组件 API 和 PDF Kit 来提供一种更自然的方式来表达内容(就像在 React 中一样)。
在这里我们将重点介绍 React PDF,但如果您想要纯 JavaScript 版本,请查看文档。
如何开始?
首先使用 npm 安装 React PDF:
npm install @react-pdf/renderer
导入一些组件作为依赖项:
import { renderToStream, Page, Text, Document, StyleSheet } from '@react-pdf/renderer';
设置一些样式:
const styles = StyleSheet.create({
page: {
padding: 50
},
title: {
fontSize: 22,
},
});
创建文档组件:
const MyDocument = () => (
<Document>
<Page style={styles.page}>
<Text style={styles.title}>My Text</Text>
</Page>
</Document>
);
如果渲染到流,请使用该renderToStream
方法:
await renderToStream(<MyDocument />)
此时它就像一个 React 组件,因此您可以设置动态值以便更轻松地处理动态数据。
创建动态路由器处理程序
使用它的一个示例是创建一个 Next.js 路由处理程序,它像页面一样查询数据并呈现 PDF,并将其作为流返回。
为此,您可以在 创建路线app/pdf/route.tsx
,在其中,您将创建 PDF 组件、渲染它,并在 Next.js 响应中返回它。
例如:
import { NextResponse } from 'next/server';
const Invoice = (props: InvoiceProps) => (
<Document>{/** PDF Components */}</Document>
);
export async function GET(request: Request, { params }: { params: { invoiceId: string; }}) {
const invoice = await getMyInvoice(params.invoiceId);
const stream = await renderToStream(<Invoice {...invoice} />)
return new NextResponse(stream as unknown as ReadableStream)
}
这可能导致:
它有什么好处?
渲染效果非常好。
您可以选择如何呈现文档,包括在视图中呈现它以及将其呈现到 ReadableStream 中。
如果您想要创建一个可动态生成和传送 PDF 的路由器处理程序,或者想要在服务器上生成 PDF,那么这会非常方便,就像上面的例子一样。
它还允许您嵌入实际字体。打开 PDF 后,文本节点实际上是可选择和可复制的,这出于多种原因非常有用。
还有什么更好的呢?
目前,React PDF 似乎无法在即将推出的 React 19 中使用。GitHub Issues 中有一些关于如何支持(甚至是分叉)的讨论。
虽然您能够以类似 HTML/JSX 的结构创建 PDF,但您仍然必须使用其组件单独维护它,但如果不使用像 html2pdf 这样的直接从 DOM 中抓取它的解决方案,该语法可能比其他一些 API 更好。
Puppeteer
Puppeteer 是一种流行的选项,用于处理 HTML、截屏以及尝试渲染通常涉及浏览器的动态内容。
这是我们可以用来根据现有内容创建 PDF 的另一个工具。
其工作方式是 Puppeteer 帮助自动化 Chromium 浏览器,一旦连接,我们就可以导航到不同的页面,与这些页面进行交互,并且可以想象,截取屏幕截图甚至生成 PDF。
如何开始?
使用 Puppeteer 非常依赖于您所处的环境。例如,在无服务器函数中运行 Puppeteer 很棘手,但幸运的是我有一个教程:使用 Puppeteer 和 Next.js API 路由构建 Web Scraper。
对于这个例子,我假设您能够运行标准 Puppeteer 库。
首先,安装 Puppeteer:
npm install puppeteer
将模块导入到您的项目中:
import puppeteer from 'puppeteer';
然后我们可以自动运行 Puppeteer,例如,如果我们想要生成 PDF,我们可以运行:
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://spacejelly.dev');
const pdf = await page.pdf();
await browser.close();
此时,Uint8Array
我们可以将 PDF 上传到我们选择的位置。
例如:
提示:查看我的YouTube 视频,我将在其中向您展示如何使用 Clerk 创建经过身份验证的 Puppeteer 会话以生成 PDF!
或者,您也可以截取屏幕截图,而不是生成 PDF:
const screenshot = await page.screenshot();
不同之处在于.screenshot
生成图像而不是 PDF 文件,这是一个重要的区别。
Alt:使用 Puppeteer 生成的 PDF
它有什么好处?
Puppeteer 用途非常广泛。你可以通过多种方式来控制页面并与之交互。
设置好页面后,您可以使用.pdf
包含嵌入文本的方法轻松捕获它,这对于高质量 PDF 至关重要。
还有什么更好的呢?
对于这种用例来说,Puppeteer 可能比较慢,但其他方法速度更快,可以带来更好的用户体验。
在环境中进行设置也可能很棘手,假设您有一个可以设置 Puppeteer 的环境。
我们最好的选择是什么?
它们都有各自的优点和缺点,但我认为 React PDF 是我们更好的解决方案。
当然,我们必须创建和维护一个单独的模板,但该模板不需要与 UI 完全相同,对于交互性有不同的标准和期望级别。
与 html2pdf 相比,React PDF 为我们提供了嵌入文本。
与 Puppeteer 相比,React PDF 速度更快,设置也更容易。
该文章在 2024/10/28 16:27:22 编辑过