@akira1ce/html2pdf
v2.0.0
Published
html2pdf
Readme
@akira1ce/html2pdf
一个智能的 HTML 转 PDF 工具,专注于解决分页切割问题。
特性
- 🎯 智能分页:自动计算并分割内容到多个 A4 页面
- 📦 零配置:开箱即用,只需标记最小单元节点
- 🎨 样式保留:完整保留 CSS 样式和布局
- ⚡ 高性能:支持并发渲染,大文档也能快速生成
- 🔧 灵活配置:支持自定义页边距、图片质量等参数
安装
npm install @akira1ce/html2pdf
# or
yarn add @akira1ce/html2pdf
# or
pnpm add @akira1ce/html2pdf快速开始
import { transfer2Pdf } from '@akira1ce/html2pdf';
export default function App() {
const handleDownload = async () => {
await transfer2Pdf('.container', {
unitSelector: '.item',
filename: 'document.pdf',
});
};
return (
<>
<button onClick={handleDownload}>导出 PDF</button>
<div className="container" style={{ width: 794 }}>
<div className="item">内容块 1</div>
<div className="item">内容块 2</div>
<div className="item">内容块 3</div>
</div>
</>
);
}API
transfer2Pdf(element, options)
将 HTML 元素转换为 PDF 文件。
参数
element:HTMLElement | string- 容器元素或 CSS 选择器options:PdfOptions- 配置选项
PdfOptions
interface PdfOptions {
/** CSS 选择器,用于标记需要分页的最小单元 */
unitSelector: string;
/** 页面包装器的类名(默认:'pdf-page-wrapper') */
wrapperClassName?: string;
/** 每页的垂直内边距(默认:20) */
py?: number;
/** 输出的 PDF 文件名(默认:'document.pdf') */
filename?: string;
/** 图片质量 0-1(默认:0.95) */
quality?: number;
/** 进度回调函数 */
onProgress?: (current: number, total: number) => void;
/** 并发渲染的页面数(默认:3) */
concurrency?: number;
}完整示例
import { transfer2Pdf } from '@akira1ce/html2pdf';
function ReportPage() {
const handleExport = async () => {
await transfer2Pdf('.report-container', {
unitSelector: '.report-section',
filename: 'report.pdf',
py: 20,
quality: 0.95,
concurrency: 3,
onProgress: (current, total) => {
console.log(`生成进度: ${current}/${total}`);
}
});
};
return (
<div>
<button onClick={handleExport}>导出报告</button>
<div className="report-container" style={{ width: 794, padding: '40px' }}>
<div className="report-section">
<h1>第一章</h1>
<p>这是第一章的内容...</p>
</div>
<div className="report-section">
<h2>第二章</h2>
<p>这是第二章的内容...</p>
</div>
<div className="report-section">
<h2>第三章</h2>
<p>这是第三章的内容...</p>
</div>
</div>
</div>
);
}使用指南
1. 容器宽度
容器必须有固定宽度,推荐使用 794px(对应 A4 纸张宽度):
<div className="container" style={{ width: 794 }}>
{/* 内容 */}
</div>2. 标记最小单元
使用 unitSelector 标记不可分割的内容块:
// ✅ 正确:每个章节作为一个单元
<div className="section">章节内容</div>
// ❌ 错误:没有标记单元,无法正确分页
<div>内容 1</div>
<div>内容 2</div>3. 容器内边距
容器的 padding 会自动应用到每一页:
<div style={{ width: 794, padding: '40px 30px' }}>
{/* 每页都会有 40px 上下边距,30px 左右边距 */}
</div>4. 进度监听
通过 onProgress 回调监听生成进度:
await transfer2Pdf('.container', {
unitSelector: '.item',
onProgress: (current, total) => {
const percent = Math.round((current / total) * 100);
console.log(`${percent}%`);
}
});注意事项
- ⚠️ 单个单元节点高度不应超过一页高度
- ⚠️ 单元节点不会被切割,会作为整体放在一页中
- ⚠️ 容器必须有固定宽度
- ⚠️ 生成过程中会临时修改 DOM 结构
工作原理
核心算法
- 收集单元节点 - 根据
unitSelector获取所有需要分页的内容块 - 计算页面尺寸 - 根据容器宽度计算对应的 A4 页面高度
- 智能布局 - 自上而下遍历单元,判断当前页是否能容纳
- 创建页面 - 将内容分配到不同页面,填充空白区域
- 渲染图片 - 使用 dom-to-image 将每页转换为高质量图片
- 生成 PDF - 使用 jsPDF 将图片组装成 PDF 文档
分页示意图
┌─────────────────────┐
│ Page 1 │
│ ┌───────────────┐ │
│ │ py padding │ │
│ ├───────────────┤ │
│ │ Unit 1 │ │
│ ├───────────────┤ │
│ │ Unit 2 │ │
│ ├───────────────┤ │
│ │ blank space │ │ ← Unit 3 放不下,填充空白
│ └───────────────┘ │
└─────────────────────┘
┌─────────────────────┐
│ Page 2 │
│ ┌───────────────┐ │
│ │ py padding │ │
│ ├───────────────┤ │
│ │ Unit 3 │ │ ← 移到新页面
│ ├───────────────┤ │
│ │ Unit 4 │ │
│ └───────────────┘ │
└─────────────────────┘工具函数
库还导出了一些实用工具函数:
import { utils } from '@akira1ce/html2pdf';
// 根据宽度计算 A4 高度
const height = utils.getA4Height(794); // 返回 1123
// 获取元素高度(包含 margin)
const boxHeight = utils.getBoxHeight(element);
// 深度克隆节点(支持 canvas)
const cloned = utils.deepCloneNode(node, true);错误处理
库提供了自定义错误类型:
import { transfer2Pdf, Html2PdfError } from '@akira1ce/html2pdf';
try {
await transfer2Pdf('.container', {
unitSelector: '.item'
});
} catch (error) {
if (error instanceof Html2PdfError) {
console.error(`错误代码: ${error.code}`);
console.error(`错误信息: ${error.message}`);
}
}错误代码
NO_CONTAINER- 找不到容器元素NO_UNITS- 没有找到匹配的单元节点NO_PAGES- 布局计算后没有生成页面GENERATION_FAILED- PDF 生成失败
技术栈
- jsPDF - PDF 文档生成
- dom-to-image - DOM 转图片
License
MIT © akiraice
