mcpField
R
react-pdf

react-pdf

github.com/trailofbits文档处理52385 次安装15 周前更新
代码仓库

关于 react-pdf

使用 React-PDF 生成 PDF 文档的技能,适用于报告、表单及采用 Flexbox、图形或自定义字体的版式,并提供渲染、预览和故障排查指导。

主要功能

  1. 01创建多页 PDF 文档
  2. 02使用 Flexbox 组件组合版式
  3. 03嵌入图像、链接和 SVG 图形
  4. 04注册本地字体和表情资源
  5. 05预览渲染后的 PDF 页面

适用场景

  1. 01生成报告、发票或简历
  2. 02制作含网格、卡片或分栏的 PDF
  3. 03为 PDF 添加图表、图标或自定义字体
  4. 04通过图像预览检查页面布局

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trailofbits/skills-curated --skill react-pdf

安装后在 Claude Code 中输入/react-pdf确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
example-template.tsx
references/
components.md
google-fonts.txt

详细介绍

使用 React-PDF 生成 PDF

适用场景

  • 生成 PDF 文档(报告、发票、简历、表单、证书)
  • 创建需要复杂布局的 PDF(多栏、网格、卡片)
  • 构建包含内嵌 SVG 图形、图表或图标的 PDF
  • 生成使用自定义 Google Fonts 或表情符号的文档
  • 任何使用 flexbox 布局比通过绝对坐标计算更简单的 PDF 任务

不适用场景

  • 读取或提取现有 PDF 中的文本——请改用 pdfplumber 或 pypdf
  • 填写现有 PDF 表单——请使用 Python pypdf 或 pdftk
  • 将 HTML 转换为 PDF——请改用 Playwright 或 WeasyPrint
  • 不需要布局的简单一次性文本 PDF——使用 Python 脚本可能更简单

重要要求

  1. 字体必须是本地文件——远程字体 URL(http/https)无法使用。使用字体前,请务必先将其下载到本地文件。
  2. 将异步代码封装在 IIFE 中——顶层 await 会导致错误。请始终使用 (async () => { ... })() 模式。
  3. 使用自定义字体时禁用断字——自定义字体缺少断字词典,可能导致程序崩溃或错误地拆分单词。注册自定义字体后,务必调用 Font.registerHyphenationCallback((word) => [word]);。

文件

  • {baseDir}/skills/react-pdf/references/google-fonts.txt——约 65 种常用 Google Fonts 的元数据及 TrueType URL。每行是一个以制表符分隔的字体变体,格式为:font name、style、category、weight、url。
  • {baseDir}/skills/react-pdf/references/components.md——完整的组件 API 参考和受支持的 CSS 属性
  • {baseDir}/skills/react-pdf/assets/example-template.tsx——最简可运行示例,演示固定页脚、页码和不可拆分的内容。开始前请先阅读,以了解基本模式。注意:此处未展示所有 API——请始终参阅文档和 {baseDir}/skills/react-pdf/references/components.md,了解完整 API。

前提条件

npm install react @react-pdf/renderer
npm install -D tsx @types/react

tsx 可通过 Node 直接运行 TypeScript + JSX 文件,无需配置,也不需要 tsconfig.json。它底层使用 esbuild,并会自动处理 JSX 转换。

核心组件

  • Document:根组件(元数据、设置)
  • Page:单个页面(A4、Letter 或自定义尺寸)
  • View:容器组件(类似 div)
  • Text:文本内容,支持嵌套以设置行内样式
  • Image:嵌入图像(JPG、PNG、base64)
  • Link:可点击的超链接(外部或内部)
  • Note:注释
  • Canvas:使用 pdfkit 方法进行自由绘图
  • Svg:矢量图形(Circle、Rect、Path、Line、Polygon 等)
  • StyleSheet:创建可复用样式

完整的组件属性和 CSS 属性请参阅 references/components.md。

基本示例

import React from "react";
import { Document, Page, Text, View, StyleSheet, renderToFile } from "@react-pdf/renderer";

const styles = StyleSheet.create({
  page: { flexDirection: "column", backgroundColor: "#ffffff", padding: 40 },
  title: { fontSize: 24, marginBottom: 20, fontWeight: "bold" },
  text: { fontSize: 12, lineHeight: 1.5 },
});

const MyDocument = () => (
  <Document>
    <Page size="A4" style={styles.page}>
      <View style={{ margin: 10, padding: 20 }}>
        <Text style={styles.title}>Document Title</Text>
        <Text style={styles.text}>Your content here</Text>
      </View>
    </Page>
  </Document>
);

(async () => {
  await renderToFile(<MyDocument />, "./output.pdf");
  console.log("PDF saved!");
})();

运行脚本

PDF 生成脚本使用 JSX,Node 无法直接运行。请使用 tsx 执行:

npx tsx my-document.tsx

无需全局安装 tsx,npx tsx 会按需下载。如果将 tsx 安装为开发依赖(npm install -D tsx),它会立即运行,无需通过 npx 下载。

始终将渲染操作封装在异步 IIFE 中:

// Good
(async () => {
  await renderToFile(<MyDocument />, "./output.pdf");
})();

// Bad - top-level await may fail
await renderToFile(<MyDocument />, "./output.pdf");

预览 PDF

若要直观检查生成的 PDF,请将页面转换为图像。先尝试 pdftoppm(通常已预装);如果不可用,再使用 Python 的 PyMuPDF。

选项 1:pdftoppm(poppler-utils)——首选,许多环境中无需安装:

pdftoppm -png -r 200 document.pdf preview
# → preview-1.png, preview-2.png, ...

选项 2:PyMuPDF(Python)——如果 pdftoppm 不可用,则使用此备用方案:

pip install pymupdf
import fitz

doc = fitz.open("document.pdf")
for i, page in enumerate(doc):
    pix = page.get_pixmap(dpi=200)
    pix.save(f"page-{i+1}.png")

渲染方法

import { renderToFile, renderToBuffer } from "@react-pdf/renderer";

// To file
(async () => {
  await renderToFile(<MyDocument />, "./document.pdf");
})();

// To buffer
(async () => {
  const buffer = await renderToBuffer(<MyDocument />);
})();

样式

有三种方式:StyleSheet.create()、行内对象或混合数组。

const styles = StyleSheet.create({ container: { padding: 20 } });

<View style={styles.container} />
<View style={{ padding: 20 }} />
<View style={[styles.container, { marginTop: 10 }]} />
支持的单位

pt(默认值,72 DPI)、in、mm、cm、%、vw、vh

常见样式属性
{
  // Flexbox
  flexDirection: "row", justifyContent: "space-between", alignItems: "center",
  flexWrap: "wrap", gap: 10,

  // Box model
  margin: 10, padding: 20, width: "100%", height: 200,

  // Borders
  borderWidth: 1, borderColor: "#333", borderRadius: 5, borderStyle: "solid",

  // Colors
  backgroundColor: "#f0f0f0", color: "#000", opacity: 0.8,

  // Typography
  fontSize: 12, fontWeight: "bold", fontFamily: "Helvetica", fontStyle: "italic",
  lineHeight: 1.5, textAlign: "center", textDecoration: "underline",
  textTransform: "uppercase", letterSpacing: 1,

  // Position
  position: "absolute", top: 0, left: 0, right: 0, bottom: 0, zIndex: 10,

  // Transforms
  transform: "rotate(45deg)", transformOrigin: "center",
}

图片

本地文件最可靠。远程 URL 可能因网络或 CORS 问题而无法使用。

import { Image } from '@react-pdf/renderer';

<Image src="./images/photo.jpg" style={{ width: 200, height: 150 }} />
<Image src={{ data: buffer, format: 'png' }} />

不能将 SVG 文件用作图片源。 请读取 SVG 源码,并使用 react-pdf Svg 组件重新创建。

SVG 图形

import { Svg, Circle, Rect, Path, Line, G, Defs, LinearGradient, Stop } from "@react-pdf/renderer";

<Svg width="200" height="200" viewBox="0 0 200 200">
  <Defs>
    <LinearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
      <Stop offset="0%" stopColor="#3498db" />
      <Stop offset="100%" stopColor="#9b59b6" />
    </LinearGradient>
  </Defs>
  <Circle cx="100" cy="100" r="50" fill="url(#grad1)" />
  <Rect x="10" y="10" width="50" height="50" fill="#e74c3c" />
  <Path d="M10,50 Q50,10 90,50" stroke="#2ecc71" strokeWidth="2" fill="none" />
</Svg>;

使用图标

读取图标库中的 SVG 源码,并将其转换为 react-pdf Svg 组件:

npm install lucide-static
import { Svg, Path, Rect } from "@react-pdf/renderer";

// Converted from lucide-static/icons/mail.svg
const MailIcon = ({ size = 12, color = "#888" }) => (
  <Svg width={size} height={size} viewBox="0 0 24 24">
    <Path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" stroke={color} strokeWidth={2} fill="none" />
    <Rect x="2" y="4" width="20" height="16" rx="2" stroke={color} strokeWidth={2} fill="none" />
  </Svg>
);

链接和导航

<Link src="https://example.com"><Text>Visit website</Text></Link>

<View id="section-1"><Text>Target</Text></View>
<Link src="#section-1"><Text>Jump to Section 1</Text></Link>

动态内容和页码

<Text render={({ pageNumber, totalPages }) => `Page ${pageNumber} of ${totalPages}`} />

固定页眉/页脚

<Page size="A4">
  <View fixed style={{ position: "absolute", top: 20, left: 30, right: 30 }}>
    <Text>Header</Text>
  </View>
  <View style={{ marginTop: 60, marginBottom: 60 }}>
    <Text>Content</Text>
  </View>
  <Text
    fixed
    style={{ position: "absolute", bottom: 20, left: 30, right: 30, textAlign: "center" }}
    render={({ pageNumber, totalPages }) => `Page ${pageNumber} of ${totalPages}`}
  />
</Page>

分页和换行

<View break />                              // Force page break
<View wrap={false}><Text>Keep together</Text></View>  // Prevent breaking inside
<Text orphans={2} widows={2}>Long text...</Text>       // Orphan/widow control
<View minPresenceAhead={100}><Text>Content</Text></View>  // Min space before break

自定义字体

重要:所有字体源都必须是本地文件路径。 远程 URL 无法使用。

import { Font } from "@react-pdf/renderer";

Font.register({
  family: "Roboto",
  fonts: [
    { src: "./fonts/Roboto-Regular.ttf", fontWeight: "normal" },
    { src: "./fonts/Roboto-Bold.ttf", fontWeight: "bold" },
    { src: "./fonts/Roboto-Italic.ttf", fontStyle: "italic" },
  ],
});

// Always disable hyphenation when using custom fonts
Font.registerHyphenationCallback((word) => [word]);

内置字体:Courier、Helvetica、Times-Roman(每种都提供 Bold、Italic/Oblique 变体)

字体粗细值:thin(100)、ultralight(200)、light(300)、normal(400)、medium(500)、 semibold(600)、bold(700)、ultrabold(800)、heavy(900)

Google Fonts

使用 {baseDir}/skills/react-pdf/references/google-fonts.txt 查找字体 URL,然后下载到本地:

# Find the font URL
grep "^Roboto" {baseDir}/skills/react-pdf/references/google-fonts.txt | grep "700" | grep "normal"

# Download
mkdir -p fonts
curl -sL "<url-from-grep>" -o fonts/Roboto-Bold.ttf

# Verify - must show "TrueType Font data"
file fonts/Roboto-Bold.ttf

如果 file 显示“HTML document”或“ASCII text”,说明下载失败。请尝试其他 URL,或在 GitHub 上搜索该字体的官方仓库,查找 TTF 文件。

Emoji

除非注册了 Emoji 源,否则 PDF 中无法渲染 Emoji。安装 twemoji-emojis 即可获取本地 Twemoji PNG 资源,无需在渲染时连接互联网。

npm install twemoji-emojis
import { Font } from "@react-pdf/renderer";

Font.registerEmojiSource({
  format: "png",
  url: "node_modules/twemoji-emojis/vendor/72x72/",
});

然后可直接在 Text 中使用 Emoji:<Text>Hello</Text>

其他功能

// Canvas drawing
<Canvas style={{ width: 200, height: 200 }}
  paint={(painter, w, h) => { painter.circle(w/2, h/2, 50).fill("#3498db"); }} />

// Annotation notes
<Note style={{ color: "yellow" }}>Annotation text</Note>

// Hyphenation
Font.registerHyphenationCallback((word) => [word]); // disable

// Debug mode - visualize boundaries
<View debug><Text debug>Debug text</Text></View>

// Document metadata
<Document title="My Doc" author="Author" subject="Report" language="en-US" pdfVersion="1.5" />

最佳实践

  1. 使用 StyleSheet.create()——只定义一次样式并重复使用
  2. 嵌入图片前先压缩;对于远程图片,使用 cache={true}
  3. 测试分页效果——内容的流动方式可能与预期不同
  4. 优先使用 flexbox,而不是绝对定位
  5. 使用 fixed 属性,让页眉/页脚显示在每一页
  6. 使用 debug={true} 可视化元素边界
  7. 使用 try-catch 块包裹渲染过程

常见问题

文本溢出:<Text style={{ width: 200, maxLines: 3, textOverflow: "ellipsis" }}>...</Text>

缺少字体:下载字体到本地,并使用本地文件路径注册。远程 URL 无法使用。

分页异常:使用 wrap={false} 将内容保持在一起,或使用 <View break /> 强制分页。

评论

支持 Markdown