dom-to-vector-pdf 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +116 -1
- package/README.zh-CN.md +117 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.esm.js +349 -0
- package/dist/index.js +354 -0
- package/dist/index.umd.js +356 -0
- package/dist/types.d.ts +46 -0
- package/package.json +79 -12
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -1,2 +1,117 @@
|
|
|
1
1
|
# dom-to-vector-pdf
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
A tool for converting DOM elements to vector PDFs using jsPDF, dom-to-svg and svg2pdf.js.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install dom-to-vector-pdf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Configuration Options
|
|
12
|
+
|
|
13
|
+
### Export Options
|
|
14
|
+
|
|
15
|
+
| Option | Type | Default | Description |
|
|
16
|
+
|--------|------|---------|-------------|
|
|
17
|
+
| selector | string | required | CSS selector for DOM element to export |
|
|
18
|
+
| filename | string | required | Exported PDF file name |
|
|
19
|
+
| orientation | 'portrait' \| 'landscape' | 'portrait' | PDF orientation |
|
|
20
|
+
| unit | 'px' | Unit for measurements(only px) |
|
|
21
|
+
| beforeSvgConvert | (svgElement: SVGElement) => void | - | Custom hook for processing SVG elements |
|
|
22
|
+
| beforePdfSave | (pdf: jsPDF) => void | - | Custom hook for processing PDF document |
|
|
23
|
+
|
|
24
|
+
### Font Options
|
|
25
|
+
|
|
26
|
+
| Option | Type | Default | Description |
|
|
27
|
+
|--------|------|---------|-------------|
|
|
28
|
+
| font | string | required | Font file path or URL |
|
|
29
|
+
| fontId | string | required | Font ID for identifying the font |
|
|
30
|
+
| fontStyle | 'normal' \| 'italic' | 'normal' | Font style |
|
|
31
|
+
| fontWeight | string \| number | - | Font weight (100-900) |
|
|
32
|
+
|
|
33
|
+
### Lifecycle Hooks
|
|
34
|
+
|
|
35
|
+
| Hook | Type | Description |
|
|
36
|
+
|------|------|-------------|
|
|
37
|
+
| afterDomClone | (clonedElement: HTMLElement) => void | Triggered after DOM clone |
|
|
38
|
+
| beforeSvgConvert | (svgElement: SVGElement) => void | Triggered before SVG conversion |
|
|
39
|
+
| beforePdfGenerate | (pdf: jsPDF) => void | Triggered before PDF generation |
|
|
40
|
+
| beforePdfSave | (pdf: jsPDF) => void | Triggered before PDF save |
|
|
41
|
+
|
|
42
|
+
## Basic Usage
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import vectorInstance from "dom-to-vector-pdf";
|
|
46
|
+
|
|
47
|
+
export const ExportToPDF = (selector, title) => {
|
|
48
|
+
vectorInstance.registerFont([
|
|
49
|
+
{
|
|
50
|
+
font: PingFangRegular,
|
|
51
|
+
fontId: "PingFang",
|
|
52
|
+
fontWeight: "400",
|
|
53
|
+
fontStyle: "normal",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
font: PingFangHeavy,
|
|
57
|
+
fontId: "PingFang",
|
|
58
|
+
fontWeight: "700",
|
|
59
|
+
fontStyle: "normal",
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
vectorInstance.exportPDF({
|
|
63
|
+
selector,
|
|
64
|
+
filename: title,
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
- Converts DOM elements to vector PDFs
|
|
72
|
+
- Preserves vector graphics and text
|
|
73
|
+
- Supports SVG elements
|
|
74
|
+
- Maintains font styles and weights
|
|
75
|
+
- Handles complex layouts
|
|
76
|
+
|
|
77
|
+
## Todo List
|
|
78
|
+
|
|
79
|
+
### DOM Cloning
|
|
80
|
+
- [ ] Inline style handling
|
|
81
|
+
- [ ] Style priority management
|
|
82
|
+
- [ ] Shadow DOM support
|
|
83
|
+
- [ ] iframe support
|
|
84
|
+
|
|
85
|
+
### Icon Fonts
|
|
86
|
+
- [ ] Current implementation uses 16px as base font size for scaling
|
|
87
|
+
- [ ] Need to improve icon font size handling
|
|
88
|
+
|
|
89
|
+
### SVG Support
|
|
90
|
+
- [ ] Currently only supports inline styles where property names match element attributes
|
|
91
|
+
- [ ] Need to enhance SVG style handling
|
|
92
|
+
|
|
93
|
+
### Text Alignment
|
|
94
|
+
- [ ] Text appears slightly lower than background
|
|
95
|
+
- Current workaround: Shift all text up by 3 pixels
|
|
96
|
+
|
|
97
|
+
### Unsupported Features
|
|
98
|
+
- [ ✅ ] Image background export
|
|
99
|
+
- [ ] Canvas export
|
|
100
|
+
- [ ] other unit
|
|
101
|
+
|
|
102
|
+
### Font Support
|
|
103
|
+
- [ ] Currently limited to single font family
|
|
104
|
+
- Font ID must be consistent during registration
|
|
105
|
+
- [ ] Need to add support for multiple fonts
|
|
106
|
+
- [ ] Consider WOFF2 format compatibility
|
|
107
|
+
|
|
108
|
+
### Image Export
|
|
109
|
+
- [ ] Image export quality needs improvement
|
|
110
|
+
|
|
111
|
+
## Contributing
|
|
112
|
+
|
|
113
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
MIT
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# dom-to-vector-pdf
|
|
2
|
+
|
|
3
|
+
一个使用 jsPDF、dom-to-svg 和 svg2pdf.js 将 DOM 元素转换为矢量 PDF 的工具。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install dom-to-vector-pdf
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 配置选项
|
|
12
|
+
|
|
13
|
+
### 导出选项
|
|
14
|
+
|
|
15
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
16
|
+
|------|------|--------|------|
|
|
17
|
+
| selector | string | 必填 | 要导出的DOM元素的CSS选择器 |
|
|
18
|
+
| filename | string | 必填 | 导出的PDF文件名 |
|
|
19
|
+
| orientation | 'portrait' \| 'landscape' | 'portrait' | PDF方向 |
|
|
20
|
+
| unit | 'px' | 测量单位(只支持px) |
|
|
21
|
+
| beforeSvgConvert | (svgElement: SVGElement) => void | - | SVG元素处理钩子 |
|
|
22
|
+
| beforePdfSave | (pdf: jsPDF) => void | - | PDF文档处理钩子 |
|
|
23
|
+
|
|
24
|
+
### 字体选项
|
|
25
|
+
|
|
26
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
27
|
+
|------|------|--------|------|
|
|
28
|
+
| font | string | 必填 | 字体文件路径或URL |
|
|
29
|
+
| fontId | string | 必填 | 字体ID |
|
|
30
|
+
| fontStyle | 'normal' \| 'italic' | 'normal' | 字体样式 |
|
|
31
|
+
| fontWeight | string \| number | - | 字体粗细(100-900) |
|
|
32
|
+
|
|
33
|
+
### 生命周期钩子
|
|
34
|
+
|
|
35
|
+
| 钩子 | 类型 | 说明 |
|
|
36
|
+
|------|------|------|
|
|
37
|
+
| afterDomClone | (clonedElement: HTMLElement) => void | DOM克隆后触发 |
|
|
38
|
+
| beforeSvgConvert | (svgElement: SVGElement) => void | SVG转换前触发 |
|
|
39
|
+
| beforePdfGenerate | (pdf: jsPDF) => void | PDF生成前触发 |
|
|
40
|
+
| beforePdfSave | (pdf: jsPDF) => void | PDF保存前触发 |
|
|
41
|
+
|
|
42
|
+
## 基本用法
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
import vectorInstance from "dom-to-vector-pdf";
|
|
46
|
+
|
|
47
|
+
export const ExportToPDF = (selector, title) => {
|
|
48
|
+
vectorInstance.registerFont([
|
|
49
|
+
{
|
|
50
|
+
font: PingFangRegular,
|
|
51
|
+
fontId: "PingFang",
|
|
52
|
+
fontWeight: "400",
|
|
53
|
+
fontStyle: "normal",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
font: PingFangHeavy,
|
|
57
|
+
fontId: "PingFang",
|
|
58
|
+
fontWeight: "700",
|
|
59
|
+
fontStyle: "normal",
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
vectorInstance.exportPDF({
|
|
63
|
+
selector,
|
|
64
|
+
filename: title,
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## 特性
|
|
70
|
+
|
|
71
|
+
- 将DOM元素转换为矢量PDF
|
|
72
|
+
- 保持矢量图形和文本
|
|
73
|
+
- 支持SVG元素
|
|
74
|
+
- 保持字体样式和粗细
|
|
75
|
+
- 处理复杂布局
|
|
76
|
+
|
|
77
|
+
## 待办事项
|
|
78
|
+
|
|
79
|
+
### DOM克隆
|
|
80
|
+
- [ ] 内联样式处理
|
|
81
|
+
- [ ] 样式优先级管理
|
|
82
|
+
- [ ] Shadow DOM支持
|
|
83
|
+
- [ ] iframe支持
|
|
84
|
+
|
|
85
|
+
### 图标字体
|
|
86
|
+
- [ ] 当前实现使用16px作为基础字体大小进行缩放
|
|
87
|
+
- [ ] 需要改进图标字体大小处理
|
|
88
|
+
|
|
89
|
+
### SVG支持
|
|
90
|
+
- [ ] 目前仅支持属性名与元素属性匹配的内联样式
|
|
91
|
+
- [ ] 需要增强SVG样式处理
|
|
92
|
+
|
|
93
|
+
### 文本对齐
|
|
94
|
+
- [ ] 文本位置略低于背景
|
|
95
|
+
- 当前解决方案:将所有文本向上偏移3像素
|
|
96
|
+
|
|
97
|
+
### 不支持的功能
|
|
98
|
+
- [ ✅ ] 图片背景导出
|
|
99
|
+
- [ ] Canvas导出
|
|
100
|
+
- [ ] 其他单位支持
|
|
101
|
+
|
|
102
|
+
### 字体支持
|
|
103
|
+
- [ ] 目前仅限于单个字体系列
|
|
104
|
+
- 注册时字体ID必须保持一致
|
|
105
|
+
- [ ] 需要添加多字体支持
|
|
106
|
+
- [ ] 考虑WOFF2格式兼容性
|
|
107
|
+
|
|
108
|
+
### 图片导出
|
|
109
|
+
- [ ] 图片导出质量需要改进
|
|
110
|
+
|
|
111
|
+
## 贡献
|
|
112
|
+
|
|
113
|
+
欢迎贡献!请随时提交Pull Request。
|
|
114
|
+
|
|
115
|
+
## 许可证
|
|
116
|
+
|
|
117
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ExportPdfOptions, FontRegisterOptions, LifecycleHooks } from './types';
|
|
2
|
+
export type { ExportPdfOptions, FontRegisterOptions, LifecycleHooks } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* DOM to PDF tool instance
|
|
5
|
+
*/
|
|
6
|
+
declare class DOMToPDF {
|
|
7
|
+
private converter;
|
|
8
|
+
private fontManager;
|
|
9
|
+
constructor();
|
|
10
|
+
/**
|
|
11
|
+
* Export PDF
|
|
12
|
+
* @param options Export configuration
|
|
13
|
+
* @param hooks Lifecycle hooks
|
|
14
|
+
*/
|
|
15
|
+
exportPDF(options: ExportPdfOptions, hooks?: LifecycleHooks): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Register font
|
|
18
|
+
* @param options Font registration options
|
|
19
|
+
*/
|
|
20
|
+
registerFont(options: FontRegisterOptions | FontRegisterOptions[]): void;
|
|
21
|
+
}
|
|
22
|
+
export declare const instance: DOMToPDF;
|
|
23
|
+
export default instance;
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { jsPDF } from 'jspdf';
|
|
2
|
+
import { elementToSVG } from 'dom-to-svg';
|
|
3
|
+
import { svg2pdf } from 'svg2pdf.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert font weight
|
|
7
|
+
* @param weight Font weight
|
|
8
|
+
* @returns Normalized font weight
|
|
9
|
+
*/
|
|
10
|
+
function normalizeFontWeight(weight) {
|
|
11
|
+
const weightMap = {
|
|
12
|
+
normal: '400',
|
|
13
|
+
bold: '700',
|
|
14
|
+
};
|
|
15
|
+
return weightMap[weight?.toString() || 'normal'] || weight?.toString() || '400';
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Calculate SVG symbol scale ratio
|
|
19
|
+
*/
|
|
20
|
+
function calculateSymbolScale(symbol) {
|
|
21
|
+
const viewBox = symbol.getAttribute('viewBox');
|
|
22
|
+
if (!viewBox) {
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
const [, , width] = viewBox.split(' ').map(Number);
|
|
26
|
+
// 1em 通常计算的像素值
|
|
27
|
+
const expectedSize = 16;
|
|
28
|
+
return expectedSize / width;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Symbol element in inline SVG
|
|
32
|
+
*/
|
|
33
|
+
function inlineSvgSymbols(element) {
|
|
34
|
+
const uses = element.querySelectorAll('use');
|
|
35
|
+
uses.forEach((use) => {
|
|
36
|
+
const href = use.getAttribute('xlink:href') || use.getAttribute('href');
|
|
37
|
+
if (!href) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const symbol = document.querySelector(href);
|
|
41
|
+
if (!symbol) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Create <g> container preserving all attributes
|
|
45
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
46
|
+
// Copy all attributes except href
|
|
47
|
+
Array.from(use.attributes).forEach((attr) => {
|
|
48
|
+
if (attr.name !== 'xlink:href' && attr.name !== 'href') {
|
|
49
|
+
g.setAttribute(attr.name, attr.value);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
// Insert scaled path
|
|
53
|
+
g.innerHTML = `
|
|
54
|
+
<g transform="scale(${calculateSymbolScale(symbol)})">
|
|
55
|
+
${symbol.innerHTML}
|
|
56
|
+
</g>
|
|
57
|
+
`;
|
|
58
|
+
// Replace and preserve parent SVG dimensions
|
|
59
|
+
use.replaceWith(g);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Recursively process SVG element font attributes
|
|
64
|
+
*/
|
|
65
|
+
function processSvgFonts(element, fontManager) {
|
|
66
|
+
if (element.classList.contains('no-print')) {
|
|
67
|
+
element.remove();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (element.tagName === 'text' || element.tagName === 'tspan') {
|
|
71
|
+
// Parse style string
|
|
72
|
+
const style = element.getAttribute('style');
|
|
73
|
+
if (style) {
|
|
74
|
+
style.split(';').forEach((css) => {
|
|
75
|
+
const [key, value] = css.split(':');
|
|
76
|
+
if (!key)
|
|
77
|
+
return;
|
|
78
|
+
element.setAttribute(key.trim(), value?.trim());
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
element.removeAttribute('style');
|
|
82
|
+
const fontFamily = element.getAttribute('font-family');
|
|
83
|
+
const fontWeight = element.getAttribute('font-weight');
|
|
84
|
+
// TODO
|
|
85
|
+
if (fontFamily) {
|
|
86
|
+
element.setAttribute('font-family', fontManager.getFontId());
|
|
87
|
+
element.setAttribute('font-weight', normalizeFontWeight(fontWeight));
|
|
88
|
+
}
|
|
89
|
+
// Adjust y coordinate
|
|
90
|
+
const y = element.getAttribute('y');
|
|
91
|
+
if (y) {
|
|
92
|
+
element.setAttribute('y', String(Number(y) - 3));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Recursively process child elements
|
|
96
|
+
Array.from(element.children).forEach((child) => processSvgFonts(child, fontManager));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Font manager
|
|
101
|
+
*/
|
|
102
|
+
class FontManager {
|
|
103
|
+
constructor() {
|
|
104
|
+
this.registeredFonts = new Map();
|
|
105
|
+
this.callbackList = [];
|
|
106
|
+
this.fontId = 'PingFang';
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get font ID
|
|
110
|
+
*/
|
|
111
|
+
getFontId() {
|
|
112
|
+
return this.fontId;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get font manager singleton
|
|
116
|
+
*/
|
|
117
|
+
static getInstance() {
|
|
118
|
+
if (!FontManager.instance) {
|
|
119
|
+
FontManager.instance = new FontManager();
|
|
120
|
+
}
|
|
121
|
+
return FontManager.instance;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Set PDF instance
|
|
125
|
+
*/
|
|
126
|
+
setPdfInstance(pdf) {
|
|
127
|
+
this.pdfInstance = pdf;
|
|
128
|
+
this.callbackList.forEach((callback) => callback());
|
|
129
|
+
this.callbackList = [];
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Register font
|
|
133
|
+
*/
|
|
134
|
+
registerFont(options) {
|
|
135
|
+
this.fontId = options.fontId;
|
|
136
|
+
this.addFontToPdf(options);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Batch register fonts
|
|
140
|
+
*/
|
|
141
|
+
registerFonts(options) {
|
|
142
|
+
options.map((font) => this.registerFont(font));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Add font to PDF instance
|
|
146
|
+
*/
|
|
147
|
+
addFontToPdf(options) {
|
|
148
|
+
if (!this.pdfInstance) {
|
|
149
|
+
this.callbackList.push(() => this.addFontToPdf(options));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
this.pdfInstance.addFont(options.font, options.fontId, options.fontStyle || 'normal', normalizeFontWeight(options.fontWeight));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const isNullOrUndefined = (value) => {
|
|
157
|
+
return value === null || value === undefined;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* DOM to PDF Converter
|
|
162
|
+
*/
|
|
163
|
+
class DomToPdfConverter {
|
|
164
|
+
constructor() {
|
|
165
|
+
this.resourceQueue = [];
|
|
166
|
+
this.exportOptions = null;
|
|
167
|
+
this.fontManager = FontManager.getInstance();
|
|
168
|
+
}
|
|
169
|
+
setExportOptions(options) {
|
|
170
|
+
this.exportOptions = options;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Export PDF
|
|
174
|
+
*/
|
|
175
|
+
async exportPdf(options, hooks) {
|
|
176
|
+
try {
|
|
177
|
+
this.setExportOptions(options);
|
|
178
|
+
// 1. Get and clone DOM element
|
|
179
|
+
const { element, parentElement } = this.prepareDomElement(options.selector);
|
|
180
|
+
console.log(1);
|
|
181
|
+
// Call lifecycle hook
|
|
182
|
+
hooks?.afterDomClone?.(element);
|
|
183
|
+
console.log(2);
|
|
184
|
+
// 2. Process SVG symbols
|
|
185
|
+
inlineSvgSymbols(element);
|
|
186
|
+
console.log(3);
|
|
187
|
+
// 3. Load resource
|
|
188
|
+
await this.loadResource(element);
|
|
189
|
+
console.log(4);
|
|
190
|
+
// 4. Convert to SVG
|
|
191
|
+
const svgDocument = elementToSVG(element);
|
|
192
|
+
parentElement?.removeChild(element);
|
|
193
|
+
console.log(5);
|
|
194
|
+
const svgElement = svgDocument.documentElement;
|
|
195
|
+
document.body.appendChild(svgElement);
|
|
196
|
+
this.prepareSvgElement(svgElement);
|
|
197
|
+
console.log(6);
|
|
198
|
+
// 5. Process SVG fonts
|
|
199
|
+
processSvgFonts(svgElement, this.fontManager);
|
|
200
|
+
console.log(7);
|
|
201
|
+
// Call lifecycle hook
|
|
202
|
+
hooks?.beforeSvgConvert?.(svgElement);
|
|
203
|
+
console.log(8);
|
|
204
|
+
// 6. Create PDF document
|
|
205
|
+
const pdf = this.createPdfDocument(svgElement);
|
|
206
|
+
this.fontManager.setPdfInstance(pdf);
|
|
207
|
+
console.log(9);
|
|
208
|
+
// 7. Draw SVG content to PDF
|
|
209
|
+
await this.renderSvgToPdf(svgElement, pdf);
|
|
210
|
+
console.log(10);
|
|
211
|
+
// Call lifecycle hook
|
|
212
|
+
hooks?.beforePdfGenerate?.(pdf);
|
|
213
|
+
hooks?.beforePdfSave?.(pdf);
|
|
214
|
+
console.log(11);
|
|
215
|
+
// 8. Save PDF
|
|
216
|
+
pdf.save(`${options.filename}.pdf`);
|
|
217
|
+
console.log(12);
|
|
218
|
+
// 9. Clean up temporary elements
|
|
219
|
+
svgElement.remove();
|
|
220
|
+
this.fontManager.setPdfInstance(null);
|
|
221
|
+
console.log(13);
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
console.error('生成PDF失败:', error);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Prepare DOM element
|
|
230
|
+
*/
|
|
231
|
+
prepareDomElement(selector) {
|
|
232
|
+
const originElement = document.querySelector(selector);
|
|
233
|
+
if (!originElement) {
|
|
234
|
+
throw new Error(`Element with selector "${selector}" not found`);
|
|
235
|
+
}
|
|
236
|
+
const parentElement = originElement.parentElement;
|
|
237
|
+
const element = originElement.cloneNode(true);
|
|
238
|
+
// Set cloned element styles
|
|
239
|
+
element.style.cssText = `
|
|
240
|
+
z-index: -999999;
|
|
241
|
+
position: absolute;
|
|
242
|
+
top: 0;
|
|
243
|
+
left: 0;
|
|
244
|
+
`;
|
|
245
|
+
parentElement?.appendChild(element);
|
|
246
|
+
return { element, parentElement };
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Prepare SVG element
|
|
250
|
+
*/
|
|
251
|
+
prepareSvgElement(svgElement) {
|
|
252
|
+
svgElement.style.cssText = `
|
|
253
|
+
all: unset;
|
|
254
|
+
width: 100%;
|
|
255
|
+
position: absolute;
|
|
256
|
+
top: 0;
|
|
257
|
+
left: 0;
|
|
258
|
+
z-index: -999999;
|
|
259
|
+
`;
|
|
260
|
+
// Add XML declaration
|
|
261
|
+
const utf8Declaration = document.createTextNode('<?xml version="1.0" encoding="utf-8"?>');
|
|
262
|
+
svgElement.insertBefore(utf8Declaration, svgElement.firstChild);
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Create PDF document
|
|
266
|
+
*/
|
|
267
|
+
createPdfDocument(svgElement) {
|
|
268
|
+
const { width, height } = svgElement.getBoundingClientRect();
|
|
269
|
+
return new jsPDF({
|
|
270
|
+
orientation: 'portrait',
|
|
271
|
+
unit: 'px',
|
|
272
|
+
format: [width, height],
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Render SVG to PDF
|
|
277
|
+
*/
|
|
278
|
+
async renderSvgToPdf(svgElement, pdf) {
|
|
279
|
+
await svg2pdf(svgElement, pdf, {
|
|
280
|
+
x: 0,
|
|
281
|
+
y: 0,
|
|
282
|
+
width: pdf.internal.pageSize.getWidth(),
|
|
283
|
+
height: pdf.internal.pageSize.getHeight(),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Load resource
|
|
288
|
+
*/
|
|
289
|
+
async loadResource(element) {
|
|
290
|
+
this.resourceQueue = [];
|
|
291
|
+
const resources = element.querySelectorAll('img');
|
|
292
|
+
resources.forEach((resource) => {
|
|
293
|
+
this.resourceQueue.push(new Promise((resolve) => {
|
|
294
|
+
let done = false;
|
|
295
|
+
resource.onload = () => {
|
|
296
|
+
done = true;
|
|
297
|
+
resolve(resource.src);
|
|
298
|
+
};
|
|
299
|
+
resource.onerror = () => {
|
|
300
|
+
done = true;
|
|
301
|
+
resolve();
|
|
302
|
+
};
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
if (!done) {
|
|
305
|
+
resolve(void 0);
|
|
306
|
+
}
|
|
307
|
+
}, isNullOrUndefined(this.exportOptions?.resourceTimeout)
|
|
308
|
+
? 5000
|
|
309
|
+
: this.exportOptions?.resourceTimeout);
|
|
310
|
+
}));
|
|
311
|
+
});
|
|
312
|
+
console.log(this.resourceQueue, 'this.resourceQueue');
|
|
313
|
+
return Promise.allSettled(this.resourceQueue);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* DOM to PDF tool instance
|
|
319
|
+
*/
|
|
320
|
+
class DOMToPDF {
|
|
321
|
+
constructor() {
|
|
322
|
+
this.converter = new DomToPdfConverter();
|
|
323
|
+
this.fontManager = FontManager.getInstance();
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Export PDF
|
|
327
|
+
* @param options Export configuration
|
|
328
|
+
* @param hooks Lifecycle hooks
|
|
329
|
+
*/
|
|
330
|
+
async exportPDF(options, hooks) {
|
|
331
|
+
await this.converter.exportPdf(options, hooks);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Register font
|
|
335
|
+
* @param options Font registration options
|
|
336
|
+
*/
|
|
337
|
+
registerFont(options) {
|
|
338
|
+
if (Array.isArray(options)) {
|
|
339
|
+
this.fontManager.registerFonts(options);
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
this.fontManager.registerFont(options);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
// Export singleton instance
|
|
347
|
+
const instance = new DOMToPDF();
|
|
348
|
+
|
|
349
|
+
export { instance as default, instance };
|