@mcpcn/markdown-converter 1.0.10
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/README.md +210 -0
- package/dist/config/word.d.ts +67 -0
- package/dist/config/word.d.ts.map +1 -0
- package/dist/config/word.js +86 -0
- package/dist/config/word.js.map +1 -0
- package/dist/core/BaseConverter.d.ts +104 -0
- package/dist/core/BaseConverter.d.ts.map +1 -0
- package/dist/core/BaseConverter.js +203 -0
- package/dist/core/BaseConverter.js.map +1 -0
- package/dist/core/BrowserPool.d.ts +25 -0
- package/dist/core/BrowserPool.d.ts.map +1 -0
- package/dist/core/BrowserPool.js +152 -0
- package/dist/core/BrowserPool.js.map +1 -0
- package/dist/core/CacheManager.d.ts +26 -0
- package/dist/core/CacheManager.d.ts.map +1 -0
- package/dist/core/CacheManager.js +74 -0
- package/dist/core/CacheManager.js.map +1 -0
- package/dist/core/MarkdownRenderer.d.ts +19 -0
- package/dist/core/MarkdownRenderer.d.ts.map +1 -0
- package/dist/core/MarkdownRenderer.js +160 -0
- package/dist/core/MarkdownRenderer.js.map +1 -0
- package/dist/core/PDFConverter.d.ts +81 -0
- package/dist/core/PDFConverter.d.ts.map +1 -0
- package/dist/core/PDFConverter.js +284 -0
- package/dist/core/PDFConverter.js.map +1 -0
- package/dist/core/PDFGenerator.d.ts +32 -0
- package/dist/core/PDFGenerator.d.ts.map +1 -0
- package/dist/core/PDFGenerator.js +352 -0
- package/dist/core/PDFGenerator.js.map +1 -0
- package/dist/core/TemplateManager.d.ts +16 -0
- package/dist/core/TemplateManager.d.ts.map +1 -0
- package/dist/core/TemplateManager.js +321 -0
- package/dist/core/TemplateManager.js.map +1 -0
- package/dist/core/WordConverter.d.ts +44 -0
- package/dist/core/WordConverter.d.ts.map +1 -0
- package/dist/core/WordConverter.js +409 -0
- package/dist/core/WordConverter.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +507 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +69 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF转换器
|
|
3
|
+
* 基于原EnhancedConverter重构,继承BaseConverter
|
|
4
|
+
*/
|
|
5
|
+
import pLimit from 'p-limit';
|
|
6
|
+
import { BaseConverter } from './BaseConverter.js';
|
|
7
|
+
import { PuppeteerBrowserPool } from './BrowserPool.js';
|
|
8
|
+
import { TemplateManager } from './TemplateManager.js';
|
|
9
|
+
import { MarkdownRenderer } from './MarkdownRenderer.js';
|
|
10
|
+
import { PDFGenerator } from './PDFGenerator.js';
|
|
11
|
+
import { MemoryCacheManager } from './CacheManager.js';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { writeFile, readFile } from 'fs/promises';
|
|
14
|
+
import { createRequire } from 'module';
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
export class PDFConverter extends BaseConverter {
|
|
17
|
+
browserPool;
|
|
18
|
+
templateManager;
|
|
19
|
+
markdownRenderer;
|
|
20
|
+
pdfGenerator;
|
|
21
|
+
memoryCacheManager;
|
|
22
|
+
concurrencyLimit;
|
|
23
|
+
constructor(options = {}) {
|
|
24
|
+
super({
|
|
25
|
+
cacheEnabled: options.cacheEnabled,
|
|
26
|
+
cacheDir: options.cacheDir
|
|
27
|
+
});
|
|
28
|
+
const { maxBrowsers = 3, maxConcurrent = 2, templateDir, cacheEnabled = true } = options;
|
|
29
|
+
// 初始化核心组件
|
|
30
|
+
this.browserPool = new PuppeteerBrowserPool(maxBrowsers);
|
|
31
|
+
this.templateManager = new TemplateManager(templateDir);
|
|
32
|
+
this.markdownRenderer = new MarkdownRenderer();
|
|
33
|
+
this.pdfGenerator = new PDFGenerator(this.browserPool, this.templateManager);
|
|
34
|
+
this.memoryCacheManager = cacheEnabled ? new MemoryCacheManager() : null;
|
|
35
|
+
this.concurrencyLimit = pLimit(maxConcurrent);
|
|
36
|
+
// 优雅关闭处理
|
|
37
|
+
process.on('SIGINT', () => this.cleanup());
|
|
38
|
+
process.on('SIGTERM', () => this.cleanup());
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 实现基类的抽象方法
|
|
42
|
+
*/
|
|
43
|
+
async convert(markdownContent, outputPath, options = {}) {
|
|
44
|
+
const startTime = Date.now();
|
|
45
|
+
try {
|
|
46
|
+
// 解析内容并获取统计信息
|
|
47
|
+
const { content, stats: parseStats } = this.parseMarkdown(markdownContent);
|
|
48
|
+
this.reportProgress({
|
|
49
|
+
stage: 'parsing',
|
|
50
|
+
progress: 10,
|
|
51
|
+
message: '开始转换PDF...'
|
|
52
|
+
}, options.progress);
|
|
53
|
+
// 检查本地文件系统缓存
|
|
54
|
+
const cacheKey = this.generateCacheKey(content, options);
|
|
55
|
+
let cachedResult = null;
|
|
56
|
+
if (this.cacheEnabled) {
|
|
57
|
+
cachedResult = await this.loadFromCache(cacheKey, 'pdf');
|
|
58
|
+
if (cachedResult) {
|
|
59
|
+
await this.ensureOutputDir(outputPath);
|
|
60
|
+
await writeFile(outputPath, cachedResult);
|
|
61
|
+
const fileSize = await this.getFileSize(outputPath);
|
|
62
|
+
this.reportProgress({
|
|
63
|
+
stage: 'complete',
|
|
64
|
+
progress: 100,
|
|
65
|
+
message: '使用缓存结果'
|
|
66
|
+
}, options.progress);
|
|
67
|
+
return {
|
|
68
|
+
success: true,
|
|
69
|
+
filePath: outputPath,
|
|
70
|
+
fileSize,
|
|
71
|
+
stats: {
|
|
72
|
+
processingTime: Date.now() - startTime,
|
|
73
|
+
headings: parseStats.headings || 0,
|
|
74
|
+
codeBlocks: parseStats.codeBlocks || 0,
|
|
75
|
+
paragraphs: parseStats.paragraphs || 0,
|
|
76
|
+
tables: parseStats.tables || 0,
|
|
77
|
+
fileSize
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// 内存缓存HTML处理
|
|
83
|
+
const memoryCacheKey = this.memoryCacheManager
|
|
84
|
+
? MemoryCacheManager.generateKey('html', {
|
|
85
|
+
markdown: content,
|
|
86
|
+
template: options.template,
|
|
87
|
+
markdownConfig: options.markdownConfig,
|
|
88
|
+
watermark: options.watermark
|
|
89
|
+
})
|
|
90
|
+
: null;
|
|
91
|
+
let html;
|
|
92
|
+
if (memoryCacheKey && this.memoryCacheManager) {
|
|
93
|
+
const cached = await this.memoryCacheManager.get(memoryCacheKey);
|
|
94
|
+
if (cached) {
|
|
95
|
+
this.reportProgress({
|
|
96
|
+
stage: 'parsing',
|
|
97
|
+
progress: 50,
|
|
98
|
+
message: '从内存缓存加载HTML'
|
|
99
|
+
}, options.progress);
|
|
100
|
+
html = cached;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// 渲染HTML并缓存到内存
|
|
104
|
+
this.markdownRenderer.updateConfig(options.markdownConfig || {});
|
|
105
|
+
html = await this.markdownRenderer.render(content, options.progress);
|
|
106
|
+
await this.memoryCacheManager.set(memoryCacheKey, html, 600000); // 10分钟TTL
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
// 直接渲染HTML
|
|
111
|
+
this.markdownRenderer.updateConfig(options.markdownConfig || {});
|
|
112
|
+
html = await this.markdownRenderer.render(content, options.progress);
|
|
113
|
+
}
|
|
114
|
+
this.reportProgress({
|
|
115
|
+
stage: 'converting',
|
|
116
|
+
progress: 70,
|
|
117
|
+
message: '生成PDF文件'
|
|
118
|
+
}, options.progress);
|
|
119
|
+
// 生成PDF
|
|
120
|
+
const result = await this.pdfGenerator.generatePDF(html, outputPath, {
|
|
121
|
+
template: options.template || 'default',
|
|
122
|
+
pdfConfig: options.pdfConfig,
|
|
123
|
+
watermark: options.watermark,
|
|
124
|
+
progressCallback: options.progress
|
|
125
|
+
});
|
|
126
|
+
// 缓存PDF结果
|
|
127
|
+
if (this.cacheEnabled) {
|
|
128
|
+
const pdfBuffer = await readFile(result);
|
|
129
|
+
await this.saveToCache(cacheKey, 'pdf', pdfBuffer);
|
|
130
|
+
}
|
|
131
|
+
const fileSize = await this.getFileSize(result);
|
|
132
|
+
const processingTime = Date.now() - startTime;
|
|
133
|
+
this.reportProgress({
|
|
134
|
+
stage: 'complete',
|
|
135
|
+
progress: 100,
|
|
136
|
+
message: `PDF转换完成: ${this.formatFileSize(fileSize)}`
|
|
137
|
+
}, options.progress);
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
filePath: result,
|
|
141
|
+
fileSize,
|
|
142
|
+
stats: {
|
|
143
|
+
processingTime,
|
|
144
|
+
headings: parseStats.headings || 0,
|
|
145
|
+
codeBlocks: parseStats.codeBlocks || 0,
|
|
146
|
+
paragraphs: parseStats.paragraphs || 0,
|
|
147
|
+
tables: parseStats.tables || 0,
|
|
148
|
+
fileSize
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
console.error('PDF转换失败:', error);
|
|
154
|
+
this.reportProgress({
|
|
155
|
+
stage: 'parsing',
|
|
156
|
+
progress: 0,
|
|
157
|
+
message: '转换失败',
|
|
158
|
+
error: error
|
|
159
|
+
}, options.progress);
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
error: error instanceof Error ? error.message : String(error)
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 批量转换
|
|
168
|
+
*/
|
|
169
|
+
async convertBatch(inputs, globalProgress) {
|
|
170
|
+
const results = [];
|
|
171
|
+
const errors = [];
|
|
172
|
+
let completed = 0;
|
|
173
|
+
const tasks = inputs.map((input, index) => this.concurrencyLimit(async () => {
|
|
174
|
+
try {
|
|
175
|
+
const progressCallback = input.options?.progress || ((progress) => {
|
|
176
|
+
globalProgress?.({
|
|
177
|
+
completed,
|
|
178
|
+
total: inputs.length,
|
|
179
|
+
current: `${index + 1}/${inputs.length}: ${progress.message}`,
|
|
180
|
+
errors
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
const result = await this.convert(input.markdown, input.outputPath, {
|
|
184
|
+
...input.options,
|
|
185
|
+
progress: progressCallback
|
|
186
|
+
});
|
|
187
|
+
if (result.success && result.filePath) {
|
|
188
|
+
results[index] = result.filePath;
|
|
189
|
+
completed++;
|
|
190
|
+
globalProgress?.({
|
|
191
|
+
completed,
|
|
192
|
+
total: inputs.length,
|
|
193
|
+
current: `完成: ${path.basename(result.filePath)}`,
|
|
194
|
+
errors
|
|
195
|
+
});
|
|
196
|
+
return result.filePath;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
throw new Error(result.error || '转换失败');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
errors.push(error);
|
|
204
|
+
completed++;
|
|
205
|
+
globalProgress?.({
|
|
206
|
+
completed,
|
|
207
|
+
total: inputs.length,
|
|
208
|
+
current: `失败: ${input.outputPath}`,
|
|
209
|
+
errors
|
|
210
|
+
});
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}));
|
|
214
|
+
// 等待所有任务完成
|
|
215
|
+
const settledResults = await Promise.allSettled(tasks);
|
|
216
|
+
// 收集成功的结果
|
|
217
|
+
const successfulResults = [];
|
|
218
|
+
settledResults.forEach((result, index) => {
|
|
219
|
+
if (result.status === 'fulfilled' && result.value) {
|
|
220
|
+
successfulResults.push(result.value);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
if (errors.length > 0 && successfulResults.length === 0) {
|
|
224
|
+
throw new Error(`批量转换失败: ${errors.map(e => e.message).join(', ')}`);
|
|
225
|
+
}
|
|
226
|
+
return successfulResults;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 从文件转换
|
|
230
|
+
*/
|
|
231
|
+
async convertFromFile(markdownPath, outputPath, options = {}) {
|
|
232
|
+
// 验证输入文件
|
|
233
|
+
await this.validateInputFile(markdownPath);
|
|
234
|
+
const fs = await import('fs/promises');
|
|
235
|
+
const markdown = await fs.readFile(markdownPath, 'utf-8');
|
|
236
|
+
const finalOutputPath = outputPath ||
|
|
237
|
+
markdownPath.replace(/\.md$/i, '.pdf');
|
|
238
|
+
return this.convert(markdown, finalOutputPath, options);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 获取系统状态
|
|
242
|
+
*/
|
|
243
|
+
getStats() {
|
|
244
|
+
const memUsage = process.memoryUsage();
|
|
245
|
+
return {
|
|
246
|
+
browserPool: this.browserPool.getStats(),
|
|
247
|
+
cache: this.memoryCacheManager?.getStats(),
|
|
248
|
+
system: {
|
|
249
|
+
memory: {
|
|
250
|
+
used: memUsage.heapUsed,
|
|
251
|
+
total: memUsage.heapTotal
|
|
252
|
+
},
|
|
253
|
+
cpu: process.cpuUsage().user / 1000000 // 转换为秒
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* 清理资源 - 重写基类方法
|
|
259
|
+
*/
|
|
260
|
+
async cleanup() {
|
|
261
|
+
try {
|
|
262
|
+
await this.browserPool.cleanup();
|
|
263
|
+
if (this.memoryCacheManager) {
|
|
264
|
+
await this.memoryCacheManager.clear();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
console.error('清理PDF转换器资源时出错:', error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* 获取可用模板
|
|
273
|
+
*/
|
|
274
|
+
getAvailableTemplates() {
|
|
275
|
+
return this.templateManager.getTemplateNames();
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* 获取模板详情
|
|
279
|
+
*/
|
|
280
|
+
async getTemplateInfo(name) {
|
|
281
|
+
return await this.templateManager.getTemplate(name);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=PDFConverter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PDFConverter.js","sourceRoot":"","sources":["../../src/core/PDFConverter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { PDFConfig, ConversionProgress } from '../types/index.js';
|
|
2
|
+
import { PuppeteerBrowserPool } from './BrowserPool.js';
|
|
3
|
+
import { TemplateManager } from './TemplateManager.js';
|
|
4
|
+
/**
|
|
5
|
+
* 高性能PDF生成器
|
|
6
|
+
* 支持并行生成、模板系统、水印等功能
|
|
7
|
+
*/
|
|
8
|
+
export declare class PDFGenerator {
|
|
9
|
+
private browserPool;
|
|
10
|
+
private templateManager;
|
|
11
|
+
constructor(browserPool: PuppeteerBrowserPool, templateManager: TemplateManager);
|
|
12
|
+
generatePDF(html: string, outputPath: string, options?: {
|
|
13
|
+
template?: string;
|
|
14
|
+
pdfConfig?: Partial<PDFConfig>;
|
|
15
|
+
watermark?: {
|
|
16
|
+
text: string;
|
|
17
|
+
opacity?: number;
|
|
18
|
+
fontSize?: number;
|
|
19
|
+
color?: string;
|
|
20
|
+
rotation?: number;
|
|
21
|
+
};
|
|
22
|
+
progressCallback?: (progress: ConversionProgress) => void;
|
|
23
|
+
}): Promise<string>;
|
|
24
|
+
private buildFullHtml;
|
|
25
|
+
private buildWatermarkHtml;
|
|
26
|
+
private waitForMermaidRender;
|
|
27
|
+
private buildPDFConfig;
|
|
28
|
+
private createTempHtml;
|
|
29
|
+
private getUniqueFilePath;
|
|
30
|
+
private delay;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=PDFGenerator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PDFGenerator.d.ts","sourceRoot":"","sources":["../../src/core/PDFGenerator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAY,MAAM,mBAAmB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAMvD;;;GAGG;AACH,qBAAa,YAAY;IAErB,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,eAAe;gBADf,WAAW,EAAE,oBAAoB,EACjC,eAAe,EAAE,eAAe;IAGpC,WAAW,CACf,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;QACP,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/B,SAAS,CAAC,EAAE;YACV,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,CAAC;KACtD,GACL,OAAO,CAAC,MAAM,CAAC;YA6HJ,aAAa;IA4L3B,OAAO,CAAC,kBAAkB;YAUZ,oBAAoB;IAgBlC,OAAO,CAAC,cAAc;YAuBR,cAAc;YAYd,iBAAiB;YAgBjB,KAAK;CAGpB"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
/**
|
|
6
|
+
* 高性能PDF生成器
|
|
7
|
+
* 支持并行生成、模板系统、水印等功能
|
|
8
|
+
*/
|
|
9
|
+
export class PDFGenerator {
|
|
10
|
+
browserPool;
|
|
11
|
+
templateManager;
|
|
12
|
+
constructor(browserPool, templateManager) {
|
|
13
|
+
this.browserPool = browserPool;
|
|
14
|
+
this.templateManager = templateManager;
|
|
15
|
+
}
|
|
16
|
+
async generatePDF(html, outputPath, options = {}) {
|
|
17
|
+
const { template = 'default', pdfConfig = {}, watermark, progressCallback } = options;
|
|
18
|
+
let browser = null;
|
|
19
|
+
let page = null;
|
|
20
|
+
try {
|
|
21
|
+
progressCallback?.({
|
|
22
|
+
stage: 'rendering',
|
|
23
|
+
progress: 0,
|
|
24
|
+
message: 'Acquiring browser instance...'
|
|
25
|
+
});
|
|
26
|
+
// 获取浏览器实例
|
|
27
|
+
browser = await this.browserPool.getBrowser();
|
|
28
|
+
page = await browser.newPage();
|
|
29
|
+
progressCallback?.({
|
|
30
|
+
stage: 'rendering',
|
|
31
|
+
progress: 10,
|
|
32
|
+
message: 'Loading template...'
|
|
33
|
+
});
|
|
34
|
+
// 加载模板
|
|
35
|
+
const templateObj = await this.templateManager.getTemplate(template);
|
|
36
|
+
if (!templateObj) {
|
|
37
|
+
throw new Error(`Template not found: ${template}`);
|
|
38
|
+
}
|
|
39
|
+
progressCallback?.({
|
|
40
|
+
stage: 'rendering',
|
|
41
|
+
progress: 20,
|
|
42
|
+
message: 'Building full HTML...'
|
|
43
|
+
});
|
|
44
|
+
// 构建完整HTML
|
|
45
|
+
const fullHtml = await this.buildFullHtml(html, templateObj, watermark);
|
|
46
|
+
// 创建临时HTML文件
|
|
47
|
+
const tempHtmlPath = await this.createTempHtml(fullHtml);
|
|
48
|
+
progressCallback?.({
|
|
49
|
+
stage: 'rendering',
|
|
50
|
+
progress: 40,
|
|
51
|
+
message: 'Configuring page...'
|
|
52
|
+
});
|
|
53
|
+
// 设置视口
|
|
54
|
+
await page.setViewport({
|
|
55
|
+
width: 1200,
|
|
56
|
+
height: 1600,
|
|
57
|
+
deviceScaleFactor: 2 // 提高清晰度
|
|
58
|
+
});
|
|
59
|
+
// 加载页面
|
|
60
|
+
await page.goto(`file://${tempHtmlPath}`, {
|
|
61
|
+
waitUntil: 'networkidle0',
|
|
62
|
+
timeout: 60000
|
|
63
|
+
});
|
|
64
|
+
progressCallback?.({
|
|
65
|
+
stage: 'rendering',
|
|
66
|
+
progress: 60,
|
|
67
|
+
message: 'Waiting for content rendering...'
|
|
68
|
+
});
|
|
69
|
+
// 等待Mermaid图表渲染
|
|
70
|
+
await this.waitForMermaidRender(page);
|
|
71
|
+
progressCallback?.({
|
|
72
|
+
stage: 'generating',
|
|
73
|
+
progress: 80,
|
|
74
|
+
message: 'Generating PDF...'
|
|
75
|
+
});
|
|
76
|
+
// 确保输出目录存在
|
|
77
|
+
await fs.mkdir(path.dirname(outputPath), { recursive: true });
|
|
78
|
+
// 获取最终输出路径(处理重名)
|
|
79
|
+
const finalOutputPath = await this.getUniqueFilePath(outputPath);
|
|
80
|
+
// 生成PDF
|
|
81
|
+
const finalPdfConfig = this.buildPDFConfig(pdfConfig, templateObj);
|
|
82
|
+
await page.pdf({
|
|
83
|
+
path: finalOutputPath,
|
|
84
|
+
...finalPdfConfig
|
|
85
|
+
});
|
|
86
|
+
// 清理临时文件
|
|
87
|
+
await fs.unlink(tempHtmlPath).catch(() => { });
|
|
88
|
+
progressCallback?.({
|
|
89
|
+
stage: 'complete',
|
|
90
|
+
progress: 100,
|
|
91
|
+
message: 'PDF generation completed',
|
|
92
|
+
filename: finalOutputPath
|
|
93
|
+
});
|
|
94
|
+
return finalOutputPath;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
progressCallback?.({
|
|
98
|
+
stage: 'error',
|
|
99
|
+
progress: 0,
|
|
100
|
+
message: 'Error generating PDF',
|
|
101
|
+
error: error
|
|
102
|
+
});
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
finally {
|
|
106
|
+
// 释放浏览器
|
|
107
|
+
if (browser) {
|
|
108
|
+
this.browserPool.releaseBrowser(browser);
|
|
109
|
+
}
|
|
110
|
+
if (page && !page.isClosed()) {
|
|
111
|
+
await page.close().catch(() => { });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async buildFullHtml(content, template, watermark) {
|
|
116
|
+
const wm = watermark
|
|
117
|
+
? { opacity: 0.1, fontSize: 60, color: '#cccccc', rotation: -45, ...watermark }
|
|
118
|
+
: undefined;
|
|
119
|
+
const watermarkHtml = wm ? this.buildWatermarkHtml(wm) : '';
|
|
120
|
+
return `<!DOCTYPE html>
|
|
121
|
+
<html lang="zh-CN">
|
|
122
|
+
<head>
|
|
123
|
+
<meta charset="UTF-8">
|
|
124
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
125
|
+
<title>Generated PDF</title>
|
|
126
|
+
|
|
127
|
+
<!-- Mermaid -->
|
|
128
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
129
|
+
|
|
130
|
+
<!-- Highlight.js CSS -->
|
|
131
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
|
|
132
|
+
|
|
133
|
+
<!-- 模板样式 -->
|
|
134
|
+
<style>
|
|
135
|
+
${template.css}
|
|
136
|
+
|
|
137
|
+
/* 水印样式 */
|
|
138
|
+
${wm ? `
|
|
139
|
+
.watermark {
|
|
140
|
+
position: fixed;
|
|
141
|
+
top: 50%;
|
|
142
|
+
left: 50%;
|
|
143
|
+
transform: translate(-50%, -50%) rotate(${wm.rotation}deg);
|
|
144
|
+
font-size: ${wm.fontSize}px;
|
|
145
|
+
color: ${wm.color};
|
|
146
|
+
opacity: ${wm.opacity};
|
|
147
|
+
pointer-events: none;
|
|
148
|
+
user-select: none;
|
|
149
|
+
z-index: 1000;
|
|
150
|
+
white-space: nowrap;
|
|
151
|
+
font-weight: bold;
|
|
152
|
+
}
|
|
153
|
+
` : ''}
|
|
154
|
+
|
|
155
|
+
/* 任务列表样式 */
|
|
156
|
+
.task-list-item {
|
|
157
|
+
list-style: none;
|
|
158
|
+
margin-left: -20px;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.task-list-item input[type="checkbox"] {
|
|
162
|
+
margin-right: 8px;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* 警告框样式 */
|
|
166
|
+
.alert {
|
|
167
|
+
padding: 12px 16px;
|
|
168
|
+
margin: 16px 0;
|
|
169
|
+
border-radius: 4px;
|
|
170
|
+
border-left: 4px solid;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.alert-note { border-left-color: #0969da; background-color: #f6f8fa; }
|
|
174
|
+
.alert-tip { border-left-color: #1a7f37; background-color: #f6f8fa; }
|
|
175
|
+
.alert-important { border-left-color: #8250df; background-color: #f6f8fa; }
|
|
176
|
+
.alert-warning { border-left-color: #d1242f; background-color: #fff8f8; }
|
|
177
|
+
.alert-caution { border-left-color: #bf8700; background-color: #fff8c5; }
|
|
178
|
+
|
|
179
|
+
/* 代码块增强 */
|
|
180
|
+
.code-block-container {
|
|
181
|
+
position: relative;
|
|
182
|
+
margin: 16px 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.code-language {
|
|
186
|
+
position: absolute;
|
|
187
|
+
top: 8px;
|
|
188
|
+
right: 8px;
|
|
189
|
+
background: rgba(0, 0, 0, 0.1);
|
|
190
|
+
padding: 2px 8px;
|
|
191
|
+
border-radius: 3px;
|
|
192
|
+
font-size: 12px;
|
|
193
|
+
opacity: 0.7;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* 表格容器 */
|
|
197
|
+
.table-container {
|
|
198
|
+
overflow-x: auto;
|
|
199
|
+
margin: 16px 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.enhanced-table {
|
|
203
|
+
border-collapse: collapse;
|
|
204
|
+
width: 100%;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Mermaid容器 */
|
|
208
|
+
.mermaid-container {
|
|
209
|
+
text-align: center;
|
|
210
|
+
margin: 20px 0;
|
|
211
|
+
page-break-inside: avoid;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* 脚注样式 */
|
|
215
|
+
.footnote {
|
|
216
|
+
font-size: 12px;
|
|
217
|
+
margin-top: 20px;
|
|
218
|
+
padding: 8px;
|
|
219
|
+
background-color: #f8f9fa;
|
|
220
|
+
border-left: 3px solid #dee2e6;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.footnote-ref {
|
|
224
|
+
text-decoration: none;
|
|
225
|
+
font-weight: bold;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* 打印优化 */
|
|
229
|
+
@media print {
|
|
230
|
+
.watermark {
|
|
231
|
+
position: fixed !important;
|
|
232
|
+
top: 50% !important;
|
|
233
|
+
left: 50% !important;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.mermaid-container,
|
|
237
|
+
.code-block-container,
|
|
238
|
+
.table-container {
|
|
239
|
+
page-break-inside: avoid;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
h1, h2, h3, h4, h5, h6 {
|
|
243
|
+
page-break-after: avoid;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
</style>
|
|
247
|
+
</head>
|
|
248
|
+
<body>
|
|
249
|
+
${watermarkHtml}
|
|
250
|
+
<div class="content">
|
|
251
|
+
${content}
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<script>
|
|
255
|
+
// 初始化Mermaid
|
|
256
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
257
|
+
mermaid.initialize({
|
|
258
|
+
startOnLoad: false,
|
|
259
|
+
theme: 'default',
|
|
260
|
+
themeVariables: {
|
|
261
|
+
fontFamily: 'inherit'
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// 渲染所有Mermaid图表
|
|
266
|
+
const mermaidElements = document.querySelectorAll('.mermaid[data-processed="false"]');
|
|
267
|
+
|
|
268
|
+
if (mermaidElements.length > 0) {
|
|
269
|
+
mermaid.run({
|
|
270
|
+
nodes: mermaidElements
|
|
271
|
+
}).then(() => {
|
|
272
|
+
// 标记为已处理
|
|
273
|
+
mermaidElements.forEach(el => el.setAttribute('data-processed', 'true'));
|
|
274
|
+
}).catch(error => {
|
|
275
|
+
console.error('Mermaid渲染失败:', error);
|
|
276
|
+
// 显示错误信息
|
|
277
|
+
mermaidElements.forEach(el => {
|
|
278
|
+
el.innerHTML = \`<div style="color: red; padding: 10px; border: 1px solid red;">
|
|
279
|
+
<strong>Mermaid渲染失败:</strong><br>
|
|
280
|
+
\${error.message}
|
|
281
|
+
</div>\`;
|
|
282
|
+
el.setAttribute('data-processed', 'error');
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
</script>
|
|
288
|
+
</body>
|
|
289
|
+
</html>`;
|
|
290
|
+
}
|
|
291
|
+
buildWatermarkHtml(watermark) {
|
|
292
|
+
return `<div class="watermark">${watermark.text}</div>`;
|
|
293
|
+
}
|
|
294
|
+
async waitForMermaidRender(page) {
|
|
295
|
+
// 等待Mermaid渲染完成
|
|
296
|
+
await page.waitForFunction(() => {
|
|
297
|
+
const mermaidElements = document.querySelectorAll('.mermaid');
|
|
298
|
+
return Array.from(mermaidElements).every(el => el.getAttribute('data-processed') === 'true' ||
|
|
299
|
+
el.getAttribute('data-processed') === 'error');
|
|
300
|
+
}, { timeout: 30000 }).catch(() => {
|
|
301
|
+
console.warn('Mermaid渲染超时,继续生成PDF');
|
|
302
|
+
});
|
|
303
|
+
// 额外等待确保渲染稳定
|
|
304
|
+
await this.delay(2000);
|
|
305
|
+
}
|
|
306
|
+
buildPDFConfig(config, template) {
|
|
307
|
+
const defaultConfig = {
|
|
308
|
+
format: 'a4',
|
|
309
|
+
orientation: 'portrait',
|
|
310
|
+
margin: {
|
|
311
|
+
top: '20mm',
|
|
312
|
+
right: '15mm',
|
|
313
|
+
bottom: '20mm',
|
|
314
|
+
left: '15mm'
|
|
315
|
+
},
|
|
316
|
+
printBackground: true,
|
|
317
|
+
displayHeaderFooter: false,
|
|
318
|
+
preferCSSPageSize: true,
|
|
319
|
+
scale: 1
|
|
320
|
+
};
|
|
321
|
+
return {
|
|
322
|
+
...defaultConfig,
|
|
323
|
+
...template.config,
|
|
324
|
+
...config
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
async createTempHtml(html) {
|
|
328
|
+
// 使用系统临时目录而不是当前工作目录
|
|
329
|
+
const tempDir = path.join(os.tmpdir(), 'enhanced-markdown2pdf');
|
|
330
|
+
await fs.mkdir(tempDir, { recursive: true });
|
|
331
|
+
const hash = crypto.createHash('md5').update(html).digest('hex');
|
|
332
|
+
const tempPath = path.join(tempDir, `temp_${hash}.html`);
|
|
333
|
+
await fs.writeFile(tempPath, html, 'utf-8');
|
|
334
|
+
return tempPath;
|
|
335
|
+
}
|
|
336
|
+
async getUniqueFilePath(basePath) {
|
|
337
|
+
const dir = path.dirname(basePath);
|
|
338
|
+
const ext = path.extname(basePath);
|
|
339
|
+
const name = path.basename(basePath, ext);
|
|
340
|
+
let counter = 1;
|
|
341
|
+
let uniquePath = basePath;
|
|
342
|
+
while (await fs.access(uniquePath).then(() => true).catch(() => false)) {
|
|
343
|
+
uniquePath = path.join(dir, `${name}-${counter}${ext}`);
|
|
344
|
+
counter++;
|
|
345
|
+
}
|
|
346
|
+
return uniquePath;
|
|
347
|
+
}
|
|
348
|
+
async delay(ms) {
|
|
349
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=PDFGenerator.js.map
|