前端使用 react-pdf 生成 pdf,不再依赖后端
react-pdf
是一个可以在 React 应用中生成、预览、下载 PDF 文件的库。
安装
npm install @react-pdf/renderer --save
创建 pdf
创建单页 pdf
以创建一个表格视图为例,工具不支持表格组件,需要自己写表格
import React from 'react';
import { Page, Text, View, Document, StyleSheet, Image } from '@react-pdf/renderer';
// 创建样式
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
backgroundColor: '#E4E4E4',
padding: 20
},
section: {
margin: 10,
padding: 10,
flexGrow: 1,
backgroundColor: 'white',
borderRadius: 5
},
title: {
fontSize: 18,
marginBottom: 10,
textAlign: 'center'
},
image: {
width: '100%',
height: 150,
marginBottom: 10,
borderRadius: 5
},
table: {
width: '100%',
marginBottom: 10
},
tableRow: {
flexDirection: 'row',
borderBottom: '1 solid black',
padding: 5
},
tableCell: {
flex: 1,
padding: 5
}
});
// 创建pdf文档
const MyDocument = () => (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text style={styles.title}>PDF Example</Text>
<Image src="https://www.example.com/logo.png" style={styles.image} />
<Text>Section #1</Text>
</View>
<View style={styles.section}>
<Text style={styles.title}>Table Example</Text>
<View style={styles.table}>
<View style={styles.tableRow}>
<View style={styles.tableCell}>
<Text>Name</Text>
</View>
<View style={styles.tableCell}>
<Text>Age</Text>
</View>
</View>
<View style={styles.tableRow}>
<View style={styles.tableCell}>
<Text>John Doe</Text>
</View>
<View style={styles.tableCell}>
<Text>30</Text>
</View>
</View>
<View style={styles.tableRow}>
<View style={styles.tableCell}>
<Text>Jane Smith</Text>
</View>
<View style={styles.tableCell}>
<Text>25</Text>
</View>
</View>
</View>
</View>
</Page>
</Document>
);
export default MyDocument;
多页 pdf
const MyPdfDocument = (props) => {
return (
<Document>
<Page size={{ width: 708, height: 708 / 0.7 }} style={styles.page}>
<View>
<Image src="https://www.example.com/bg.png" />
</View>
</Page>
<Page size={{ width: 708, height: 708 / 0.7 }} style={styles.page}>
<View>
<Image src="https://www.example.com/bg.png" />
</View>
</Page>
</Document>
);
};
内联样式
除了使用 StyleSheet.create
创建样式外,还支持使用多个样式与内联样式
- 使用多个样式对象:
const pageStyle = {
flexDirection: 'row',
backgroundColor: '#E4E4E4',
};
const sectionStyle = {
margin: 10,
padding: 10,
flexGrow: 1,
};
const textStyle = {
fontSize: 12,
color: 'black',
};
// 使用多个样式对象
<View style={[pageStyle, sectionStyle]}>
<Text style={textStyle}>Hello, React-PDF!</Text>
</View>
- 使用内联样式:
// 使用内联样式
<View style={{ flexDirection: 'row', backgroundColor: '#E4E4E4', margin: 10, padding: 10, flexGrow: 1 }}>
<Text style={{ fontSize: 12, color: 'black' }}>Hello, React-PDF!</Text>
</View>
自定义文档尺寸
- A4: 默认的页面尺寸,类似标准的 A4 纸张尺寸,宽度为 210mm,高度为 297mm。
<Page size="A4">
{/* 页面内容 */}
</Page>
- Letter: 类似标准的 Letter 纸张尺寸,宽度为 8.5in(英寸),高度为 11in。
<Page size="Letter">
{/* 页面内容 */}
</Page>
- Legal: 类似标准的 Legal 纸张尺寸,宽度为 8.5in,高度为 14in。
<Page size="Legal">
{/* 页面内容 */}
</Page>
- Custom: 自定义页面尺寸,可以设置特定的宽度和高度。
<Page size={{ width: '210mm', height: '150mm' }}>
{/* 页面内容 */}
</Page>
你还可以指定厘米 cm
- Landscape: 横向页面,将宽度和高度进行调换,适合横向排列的内容。
jsxCopy code
<Page size="A4" orientation="landscape">
{/* 页面内容 */}
</Page>
当你在 size
属性中设置一个数字值而没有指定单位时,默认的单位是“点”(points),这是印刷行业常用的度量单位,1 英寸约等于 72 点。
添加字体
如需使用中文字体需要自行下载字体文件,并注册
import chineseFontBold from '../../assets/苹方粗体字体.ttf';
import chineseFont from '../../assets/PingFang SC Regular.ttf';
// 注册字体
Font.register({ family: 'ChineseFont', src: chineseFontBold });
Font.register({ family: 'ChineseFontNormal', src: chineseFont });
// 超出宽度自动换行
Font.registerHyphenationCallback((word: string) => {
// 1. 如果单词只有一个字符,直接返回单个字符。
// 1. 如果单词长度大于 1,将单词拆分为单个字符的数组,并在每个字符之间插入一个空字符串,以表示单词的断开。
if (word.length === 1) {
return [word];
}
return Array.from(word)
.map((char) => [char, ''])
.reduce((arr, current) => {
arr.push(...current);
return arr;
}, []);
});
react-pdf对中文换行处理很不友好,是根据段落中的空格去换行.
Font.registerHyphenationCallback
注册了一个自定义的断词回调函数,该函数接受一个单词字符串作为参数,并返回一个数组,其中包含了断开单词后的多个部分,断词后工具可以支持文本自动换行。
可用组件
- Document: 该组件代表PDF文档本身。它必须是树元素结构的根,并且在任何情况下都不应该用作另一个Reaction-pdf组件的子级。此外,它应该只有类型的子项。
- Page: 表示 PDF 文档中的单个页面,< Document/> 可以包含任意数量的页面,但是确保不会在除 Document 之外的任何组件中呈现页面。
- View: 类似于 HTML 中的
<div>
,用于创建一个容器来包裹其他组件。 - Image: 用于在 PDF 中插入图像,可以设置图像的路径、宽度、高度等属性。
- Link: 用于创建一个超链接,可以链接到其他页面或 URL。
- Note: 用于添加注释或备注,可以在 PDF 中显示一段文本作为注释。
- Canvas: 用于在 PDF 中绘制图形和图像,可以自定义绘制内容。
- PDFViewer: 用于在应用中显示 PDF 查看器,允许用户浏览和交互 PDF 内容。
- Text: 用于添加文本内容,可以设置文本的样式、大小等属性。
- BlobProvider: 用于提供一个生成 PDF 的容器,通常与 PDFDownloadLink 配合使用。
- PDFDownloadLink: 用于创建一个下载链接,用户点击后可以下载生成的 PDF 文件。
更多详细信息可参考 react-pdf 组件
预览 pdf
import React from 'react';
import { PDFViewer } from '@react-pdf/renderer';
import MyPdfDocument from './MyPdfDocument';
const App = () => (
<PDFViewer>
<MyPdfDocument />
</PDFViewer>
);
export default App;
注意 : PDFViewer 是 iframe 容器,react的各种provider对其内部组件都是无效的,例如 redux 的Provider,如果要与其内部组件通讯,可以通过props传值方式或纯 js 共享数据,绕过DOM。
下载 pdf
import React from 'react';
import { PDFDownloadLink } from '@react-pdf/renderer';
import MyPdfDocument from './MyPdfDocument';
const App = () => (
<div>
<PDFDownloadLink document={<MyPdfDocument />} fileName="my-document.pdf">
{({ blob, url, loading, error }) => (loading ? 'Loading ...' : '下载 PDF')}
</PDFDownloadLink>
</div>
);
export default App;
总结
整体来说该工具还是非常好用的,推荐使用。