@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.
Files changed (46) hide show
  1. package/README.md +210 -0
  2. package/dist/config/word.d.ts +67 -0
  3. package/dist/config/word.d.ts.map +1 -0
  4. package/dist/config/word.js +86 -0
  5. package/dist/config/word.js.map +1 -0
  6. package/dist/core/BaseConverter.d.ts +104 -0
  7. package/dist/core/BaseConverter.d.ts.map +1 -0
  8. package/dist/core/BaseConverter.js +203 -0
  9. package/dist/core/BaseConverter.js.map +1 -0
  10. package/dist/core/BrowserPool.d.ts +25 -0
  11. package/dist/core/BrowserPool.d.ts.map +1 -0
  12. package/dist/core/BrowserPool.js +152 -0
  13. package/dist/core/BrowserPool.js.map +1 -0
  14. package/dist/core/CacheManager.d.ts +26 -0
  15. package/dist/core/CacheManager.d.ts.map +1 -0
  16. package/dist/core/CacheManager.js +74 -0
  17. package/dist/core/CacheManager.js.map +1 -0
  18. package/dist/core/MarkdownRenderer.d.ts +19 -0
  19. package/dist/core/MarkdownRenderer.d.ts.map +1 -0
  20. package/dist/core/MarkdownRenderer.js +160 -0
  21. package/dist/core/MarkdownRenderer.js.map +1 -0
  22. package/dist/core/PDFConverter.d.ts +81 -0
  23. package/dist/core/PDFConverter.d.ts.map +1 -0
  24. package/dist/core/PDFConverter.js +284 -0
  25. package/dist/core/PDFConverter.js.map +1 -0
  26. package/dist/core/PDFGenerator.d.ts +32 -0
  27. package/dist/core/PDFGenerator.d.ts.map +1 -0
  28. package/dist/core/PDFGenerator.js +352 -0
  29. package/dist/core/PDFGenerator.js.map +1 -0
  30. package/dist/core/TemplateManager.d.ts +16 -0
  31. package/dist/core/TemplateManager.d.ts.map +1 -0
  32. package/dist/core/TemplateManager.js +321 -0
  33. package/dist/core/TemplateManager.js.map +1 -0
  34. package/dist/core/WordConverter.d.ts +44 -0
  35. package/dist/core/WordConverter.d.ts.map +1 -0
  36. package/dist/core/WordConverter.js +409 -0
  37. package/dist/core/WordConverter.js.map +1 -0
  38. package/dist/index.d.ts +36 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +507 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/types/index.d.ts +69 -0
  43. package/dist/types/index.d.ts.map +1 -0
  44. package/dist/types/index.js +2 -0
  45. package/dist/types/index.js.map +1 -0
  46. 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