UNPKG

c-htmlpdf

Version:

A tool script for converting HTML elements to PDF with TypeScript support, modified based on k-htmlpdf

441 lines (340 loc) 11.6 kB
# c-htmlpdf TypeScript 支持 本项目现已支持 TypeScript,提供完整的类型定义。 ## 安装和配置 1. 安装包: ```bash npm install c-htmlpdf ``` 2. 确保您的项目支持 TypeScript 和 DOM 类型: ```bash npm install --save-dev typescript @types/node ``` ## TypeScript 使用示例 ### 基础用法 #### 🆕 推荐:对象配置方式 ```typescript import PdfLoader from 'c-htmlpdf'; // 获取 DOM 元素 const element = document.querySelector('#pdf-content') as HTMLElement; if (element) { // 使用对象配置方式创建 PdfLoader 实例(推荐) const pdfLoader = new PdfLoader(element, { pdfFileName: '我的PDF文件', width: 595, height: 842, orientation: 'portrait' }); // 生成并下载 PDF pdfLoader.outPutPdfFn().then(() => { console.log('PDF 生成完成'); }).catch((error) => { console.error('PDF 生成失败:', error); }); } ``` #### 传统参数方式(向后兼容) ```typescript import PdfLoader from 'c-htmlpdf'; const element = document.querySelector('#pdf-content') as HTMLElement; if (element) { // 传统方式(仍然支持) const pdfLoader = new PdfLoader(element, '我的PDF文件', 595, 842, 'p'); pdfLoader.outPutPdfFn().then(() => { console.log('PDF 生成完成'); }); } ``` ### 高级配置 #### 🆕 对象配置方式(清晰易读) ```typescript import PdfLoader from 'c-htmlpdf'; const element = document.querySelector('#content') as HTMLElement; if (element) { const pdfLoader = new PdfLoader(element, { pdfFileName: '高级PDF', width: 595, // PDF 宽度 height: 842, // PDF 高度 orientation: 'portrait', // PDF 方向:'p'|'l'|'portrait'|'landscape' splitClassName: 'no-break-item', // 防止截断的类名 breakClassName: 'page-break', // 分页符类名 processChildScrollable: true // 是否处理子元素滚动 }); // 预览 PDF await pdfLoader.previewPdf(); } ``` #### 仅指定部分配置 ```typescript import PdfLoader from 'c-htmlpdf'; const element = document.querySelector('#content') as HTMLElement; if (element) { // 只配置需要的参数,其他使用默认值 const pdfLoader = new PdfLoader(element, { pdfFileName: '简单配置', orientation: 'landscape', // 只改变方向为横向 processChildScrollable: false // 禁用子元素滚动处理 }); await pdfLoader.outPutPdfFn(); } ``` #### 传统参数方式(向后兼容) ```typescript import PdfLoader from 'c-htmlpdf'; const element = document.querySelector('#content') as HTMLElement; if (element) { const pdfLoader = new PdfLoader( element, // DOM 元素 '高级PDF', // 文件名 595, // 宽度 (可选,默认 595) 842, // 高度 (可选,默认 842) 'portrait', // 方向 (可选,默认 'p') 'no-break-item', // 防止截断的类名 (可选,默认 'itemClass') 'page-break', // 分页符类名 (可选,默认 'break_page') true // 是否处理子元素滚动 (可选,默认 true) ); // 预览 PDF await pdfLoader.previewPdf(); } ``` ### 获取 PDF 实例进行自定义操作 ```typescript import PdfLoader from 'c-htmlpdf'; async function customPdfGeneration() { const element = document.querySelector('#content') as HTMLElement; if (!element) return; const pdfLoader = new PdfLoader(element, '自定义PDF'); try { // 获取 jsPDF 实例 const pdfInstance = await pdfLoader.genPDf(); // 进行自定义操作 // pdfInstance.addImage(...); // pdfInstance.addPage(); // 手动保存 pdfInstance.save('自定义文件名.pdf'); } catch (error) { console.error('生成失败:', error); } } ``` ### 表格数据处理 #### 🆕 对象配置方式 ```typescript import PdfLoader from 'c-htmlpdf'; function createTablePdf() { const tableElement = document.querySelector('#data-table') as HTMLElement; if (!tableElement) return; // 为表格行添加防截断类名 const rows = tableElement.querySelectorAll('tr'); rows.forEach(row => { row.classList.add('table-row-item'); }); const pdfLoader = new PdfLoader(tableElement, { pdfFileName: '数据表格', splitClassName: 'table-row-item', // 防止表格行被截断 orientation: 'portrait', processChildScrollable: true // 处理表格内的滚动内容 }); return pdfLoader.outPutPdfFn('数据报表'); } ``` ### 横向布局 #### 🆕 对象配置方式 ```typescript import PdfLoader from 'c-htmlpdf'; function createLandscapePdf() { const element = document.querySelector('#landscape-content') as HTMLElement; if (!element) return; const pdfLoader = new PdfLoader(element, { pdfFileName: '横向PDF', width: 842, // 横向时宽度更大 height: 595, // 横向时高度较小 orientation: 'landscape' // 横向布局 }); return pdfLoader.outPutPdfFn(); } ``` ### 处理子元素滚动 (新功能) #### 🆕 对象配置方式 ```typescript import PdfLoader from 'c-htmlpdf'; function handleChildScrollableElements() { // 场景:父容器不滚动,但内部有多个可滚动的子元素 const parentContainer = document.querySelector('#parent-container') as HTMLElement; if (!parentContainer) return; // 启用子元素滚动处理(默认已启用) const pdfLoader = new PdfLoader(parentContainer, { pdfFileName: '包含滚动子元素的PDF', width: 595, height: 842, orientation: 'portrait', splitClassName: 'itemClass', breakClassName: 'break_page', processChildScrollable: true // 启用子元素滚动处理 }); return pdfLoader.outPutPdfFn(); } // 如果您不希望处理子元素滚动,可以禁用此功能 function disableChildScrollProcessing() { const element = document.querySelector('#simple-content') as HTMLElement; if (!element) return; const pdfLoader = new PdfLoader(element, { pdfFileName: '简单PDF', processChildScrollable: false // 禁用子元素滚动处理,仅处理根元素 // 其他参数使用默认值 }); return pdfLoader.outPutPdfFn(); } ``` ### 复杂滚动场景示例 ```typescript import PdfLoader from 'c-htmlpdf'; // 处理包含多层嵌套滚动元素的复杂布局 function handleComplexScrollableLayout() { const complexLayout = document.querySelector('#complex-layout') as HTMLElement; if (!complexLayout) return; /* * 假设 HTML 结构如下: * <div id="complex-layout"> * <div class="header">标题</div> * <div class="scrollable-sidebar" style="overflow-y: auto; height: 300px;"> * <ul>很多列表项...</ul> * </div> * <div class="main-content"> * <div class="scrollable-table" style="overflow: auto; max-height: 400px;"> * <table>大量数据行...</table> * </div> * </div> * </div> */ const pdfLoader = new PdfLoader( complexLayout, '复杂布局PDF', 595, 842, 'p', 'table-row', // 防止表格行被截断 'page-break', true // 自动处理所有子元素滚动 ); // 新版本会自动: // 1. 发现 .scrollable-sidebar 有垂直滚动 // 2. 发现 .scrollable-table 有滚动 // 3. 临时展开所有滚动区域 // 4. 捕获完整内容 // 5. 恢复原始滚动状态 return pdfLoader.outPutPdfFn('完整复杂布局'); } ``` ### 配置方式对比 ```typescript // ❌ 传统方式:参数位置容易搞错,可读性差 const pdf1 = new PdfLoader(element, 'file', 595, 842, 'p', 'itemClass', 'break_page', true); // ✅ 对象方式:清晰明了,不容易出错 const pdf2 = new PdfLoader(element, { pdfFileName: 'file', width: 595, height: 842, orientation: 'portrait', splitClassName: 'itemClass', breakClassName: 'break_page', processChildScrollable: true }); // ✅ 只配置必要参数,其他使用默认值 const pdf3 = new PdfLoader(element, { pdfFileName: 'simple', orientation: 'landscape' }); ``` ## 类型定义 ### PdfOrientation 类型 ```typescript type PdfOrientation = 'p' | 'l' | 'portrait' | 'landscape'; ``` ### PdfLoaderOptions 接口 ```typescript interface PdfLoaderOptions { /** PDF 文件名,默认 'document' */ pdfFileName?: string; /** PDF 宽度,默认 595 */ width?: number; /** PDF 高度,默认 842 */ height?: number; /** PDF 方向,默认 'p' */ orientation?: PdfOrientation; /** 避免分段截断的类名,默认 'itemClass' */ splitClassName?: string; /** 自定义分页符类名,默认 'break_page' */ breakClassName?: string; /** 是否处理子元素滚动,默认 true */ processChildScrollable?: boolean; } ``` ### PdfLoader 类 ```typescript class PdfLoader { // 🆕 推荐:对象配置方式 constructor(element: HTMLElement, options?: PdfLoaderOptions); // 传统方式(向后兼容) constructor( element: HTMLElement, pdfFileName: string, width?: number, height?: number, orientation?: PdfOrientation, splitClassName?: string, breakClassName?: string, processChildScrollable?: boolean ); // 生成 PDF 实例 genPDf(): Promise<jsPDF>; // 下载 PDF 文件 getPDF(): Promise<void>; // 预览 PDF previewPdf(): Promise<void>; // 输出 PDF 文件 outPutPdfFn(pdfFileName?: string): Promise<void>; } ``` ## 滚动元素处理机制 ### 自动检测逻辑 新版本会递归检查所有子元素,自动识别可滚动元素: 1. **内容溢出检测**: `scrollHeight > clientHeight` 或 `scrollWidth > clientWidth` 2. **样式检测**: `overflow`、`overflowY`、`overflowX` 为 `auto` 或 `scroll` 3. **递归处理**: 遍历所有子元素,包括深层嵌套的滚动容器 ### 处理流程 ```typescript // 处理流程示意 const processFlow = { 1: "扫描元素树,找出所有可滚动元素", 2: "保存每个滚动元素的原始状态(位置、样式)", 3: "临时修改样式:overflow: visible, height: auto", 4: "重置滚动位置到顶部", 5: "执行 html2canvas 渲染", 6: "恢复所有元素的原始状态" }; ``` ### 支持的滚动类型 - ✅ 垂直滚动 (`overflow-y: auto/scroll`) - ✅ 水平滚动 (`overflow-x: auto/scroll`) - ✅ 双向滚动 (`overflow: auto/scroll`) - ✅ 嵌套滚动容器 - ✅ 动态高度限制 (`max-height` + `overflow`) - ✅ 固定尺寸滚动区域 ## 注意事项 1. **元素引用**: 确保传入的 `ele` 参数是有效的 HTMLElement,而不是选择器字符串 2. **防截断功能**: 如果使用防截断功能,需要为可能被截断的元素添加指定的类名 3. **分页符功能**: 通过添加 `breakClassName` 类名实现自定义分页 4. **滚动处理**: - 默认启用子元素滚动处理,适用于大多数场景 - 如果性能敏感或布局简单,可以设置 `processChildScrollable: false` 5. **错误处理**: 所有异步方法都返回 Promise,建议使用 try-catch 或 .catch() 处理错误 6. **样式恢复**: 无论成功还是失败,都会自动恢复所有元素的原始样式和滚动状态 ## 兼容性 - 支持现代浏览器环境 - 需要 DOM API 支持 - 依赖 jsPDF 和 html2canvas 库 - 支持 TypeScript 3.0+ ## 性能考虑 - 递归扫描会增加少量初始化时间 - 大量滚动元素时建议分批处理 - 复杂布局建议使用防截断类名优化分页效果