mcpField
R
react-pdf

react-pdf

github.com/molefrog文档处理169730 次安装37 周前更新
代码仓库

关于 react-pdf

使用 React-PDF 以 TypeScript 和 JSX 生成 PDF 的技能指南,涵盖文档组件、布局、字体、渲染与预览,并说明确保输出可靠的关键限制。

主要功能

  1. 01使用 React-PDF 组件组合文档
  2. 02应用可复用样式和布局
  3. 03注册本地字体
  4. 04将 PDF 渲染为文件或缓冲区
  5. 05添加页码和固定页眉
  6. 06将页面转换为图像预览

适用场景

  1. 01从 JSX 生成分页文档
  2. 02使用自定义本地字体制作 PDF
  3. 03检查分页和渲染结果

安装

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

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

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

包含的文件

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

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

详细介绍

使用 React-PDF 生成 PDF

关键要求

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

文件

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

前置条件

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

npx tsx 无需全局安装 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 文件不能用作 Image 源。**请读取 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

使用 references/google-fonts.txt 查找字体 URL,然后下载到本地:

# Find the font URL
grep "^Roboto" 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 来源,Emoji 将无法在 PDF 中呈现。安装 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