@akira1ce/html2pdf
v1.1.0
Published
html2pdf
Downloads
31
Readme
html2pdf
HTML -> layout -> image -> PDF。
主要的问题在于,分页的切割拆分。
适用于:自定义渲染出来的报告页面,可手动标记某些节点,以便于分页。
安装
npm install @akira1ce/html2pdf
# or
yarn add @akira1ce/html2pdfType
/**
* PDF generation options
*/
export interface PdfOptions {
/** CSS selector for the units to be split across pages */
unitSelector: string;
/** Class name for page wrapper elements */
wrapperClassName?: string;
/** Vertical padding for each page */
py?: number;
/** Output PDF filename */
filename?: string;
/** Image quality (0-1) */
quality?: number;
/** Progress callback function */
onProgress?: (current: number, total: number) => void;
}
/**
* Convert HTML to PDF
*/
export declare const transfer2Pdf: (
ele: HTMLElement | string,
options: PdfOptions
) => Promise<void>;基础使用
import { transfer2Pdf } from '@akira1ce/html2pdf';
export default function App() {
const handleDownload = async () => {
try {
await transfer2Pdf('.container', {
unitSelector: '.min-unit',
wrapperClassName: 'pdf-page',
});
} catch (error) {
console.error('PDF generation failed:', error);
}
};
return (
<>
<button onClick={handleDownload}>Download PDF</button>
{/* 容器 - 需要定宽 */}
<div className="container" style={{ width: 794 }}>
{/* 单元节点 */}
<div className="min-unit">Hello, world!</div>
<div className="min-unit">
Lorem ipsum dolor sit amet, consectetur adipiscing elit...
</div>
<div className="min-unit">
Duis aute irure dolor in reprehenderit...
</div>
</div>
</>
);
}重要注意事项
- 必须标记单元节点:使用
unitSelector标记需要分页的最小单元 - 容器需要定宽:容器元素必须有固定宽度(建议 794px 对应 A4)
- 单元节点不可跨页:每个单元节点会作为整体放在一页中,不会被切割
- 避免过大的单元:单个单元节点高度不应超过一页高度
- 容器 padding 会被保留:容器的 padding 样式会自动应用到 PDF 的每一页中
关于 Container Padding
容器的 padding 会被自动识别并应用到生成的 PDF 中:
<div className="container" style={{ width: 794, padding: '40px' }}>
<div className="min_unit">内容 1</div>
<div className="min_unit">内容 2</div>
</div>生成的 PDF 中,每一页都会保持 40px 的内边距。支持:
- 统一 padding:
padding: 40px - 分别设置:
padding: 30px 50px 40px 60px(top right bottom left) - 单独设置:
padding-top: 30px; padding-left: 50px;
核心原理
核心在于布局的调整:
- 收集单元节点:根据
unitSelector收集所有需要分页的单元 - 计算 A4 尺寸:根据容器宽度计算对应的 A4 页面高度
- 自上而下布局:遍历所有单元,判断当前页是否能放下
- 生成页面包装器:将放不下的节点移至下一页
- 转换为图片:使用
dom-to-image将每页转换为高清图片 - 生成 PDF:使用
jsPDF将图片添加到 PDF 中
布局算法示意
┌─────────────────────┐
│ Page 1 │
│ ┌───────────────┐ │
│ │ padding (py) │ │
│ ├───────────────┤ │
│ │ Unit 1 │ │
│ ├───────────────┤ │
│ │ Unit 2 │ │
│ └───────────────┘ │
│ ┌───────────────┐ │
│ │ blank space │ │ <- 不足放下 Unit 3,填充空白
│ └───────────────┘ │
└─────────────────────┘
┌─────────────────────┐
│ Page 2 │
│ ┌───────────────┐ │
│ │ padding (py) │ │
│ ├───────────────┤ │
│ │ Unit 3 │ │ <- 移到下一页
│ ├───────────────┤ │
│ │ Unit 4 │ │
│ └───────────────┘ │
└─────────────────────┘核心依赖
- dom-to-image - 将 DOM 转换为图片
- jspdf - 生成 PDF 文档
高级工具函数
库还导出了一些工具函数供高级使用:
import { utils } from '@akira1ce/html2pdf';
// 根据宽度计算 A4 高度
const height = utils.getA4Height(794);
// 获取元素的盒模型高度(包含 margin)
const boxHeight = utils.getBoxHeight(element);
// 深度克隆节点(包括 canvas 元素)
const clonedNode = utils.deepCloneNode(node, true);License
MIT
