@microlee666/dom-to-pptx 1.1.4
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/.prettierrc +6 -0
- package/CHANGELOG.md +96 -0
- package/CONTRIBUTING.md +72 -0
- package/LICENSE +21 -0
- package/Readme.md +310 -0
- package/SUPPORTED.md +50 -0
- package/USAGE_CN.md +324 -0
- package/cli/.claude/settings.local.json +8 -0
- package/cli/.dockerignore +8 -0
- package/cli/Dockerfile +54 -0
- package/cli/Dockerfile.cn +22 -0
- package/cli/HTML_PPT_SPEC.md +360 -0
- package/cli/README.md +196 -0
- package/cli/docker-compose.yml +20 -0
- package/cli/dom-to-pptx.bundle.js +64731 -0
- package/cli/html2pptx.js +214 -0
- package/cli/output.pptx +1 -0
- package/cli/package-lock.json +1171 -0
- package/cli/package.json +20 -0
- package/cli/server.js +808 -0
- package/dist/dom-to-pptx.bundle.js +64917 -0
- package/dist/dom-to-pptx.cjs +2735 -0
- package/dist/dom-to-pptx.cjs.map +1 -0
- package/dist/dom-to-pptx.mjs +2705 -0
- package/dist/dom-to-pptx.mjs.map +1 -0
- package/eslint.config.js +17 -0
- package/package.json +86 -0
- package/rollup.config.js +96 -0
- package/src/font-embedder.js +163 -0
- package/src/font-utils.js +32 -0
- package/src/image-processor.js +118 -0
- package/src/index.js +1376 -0
- package/src/utils.js +1032 -0
package/cli/html2pptx.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* dom-to-pptx CLI - 在服务器上将 HTML 转换为 PPTX
|
|
5
|
+
*
|
|
6
|
+
* 使用方法:
|
|
7
|
+
* node html2pptx.js input.html output.pptx
|
|
8
|
+
* node html2pptx.js input.html # 输出到 input.pptx
|
|
9
|
+
* node html2pptx.js https://example.com/page.html # 支持 URL
|
|
10
|
+
*
|
|
11
|
+
* 依赖安装:
|
|
12
|
+
* npm install playwright
|
|
13
|
+
* npx playwright install chromium
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const { chromium } = require('playwright');
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
// 获取命令行参数
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
|
|
23
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
24
|
+
console.log(`
|
|
25
|
+
dom-to-pptx CLI - HTML 转 PowerPoint 命令行工具
|
|
26
|
+
|
|
27
|
+
使用方法:
|
|
28
|
+
node html2pptx.js <input> [output]
|
|
29
|
+
|
|
30
|
+
参数:
|
|
31
|
+
input 输入的 HTML 文件路径或 URL
|
|
32
|
+
output 输出的 PPTX 文件路径(可选,默认与输入同名)
|
|
33
|
+
|
|
34
|
+
选项:
|
|
35
|
+
--selector, -s 指定幻灯片元素选择器(默认: .slide)
|
|
36
|
+
选择器匹配多个元素时,将导出为多页
|
|
37
|
+
--width, -w 视口宽度(默认: 1920)
|
|
38
|
+
--height, -h 视口高度(默认: 1080)
|
|
39
|
+
--help 显示帮助信息
|
|
40
|
+
|
|
41
|
+
示例:
|
|
42
|
+
node html2pptx.js slide.html
|
|
43
|
+
node html2pptx.js slide.html output.pptx
|
|
44
|
+
node html2pptx.js slide.html -s "#my-slide"
|
|
45
|
+
node html2pptx.js https://example.com/slide.html
|
|
46
|
+
`);
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 解析参数
|
|
51
|
+
let input = null;
|
|
52
|
+
let output = null;
|
|
53
|
+
let selector = '.slide';
|
|
54
|
+
let viewportWidth = 1920;
|
|
55
|
+
let viewportHeight = 1080;
|
|
56
|
+
|
|
57
|
+
for (let i = 0; i < args.length; i++) {
|
|
58
|
+
const arg = args[i];
|
|
59
|
+
|
|
60
|
+
if (arg === '--selector' || arg === '-s') {
|
|
61
|
+
selector = args[++i];
|
|
62
|
+
} else if (arg === '--width' || arg === '-w') {
|
|
63
|
+
viewportWidth = parseInt(args[++i]);
|
|
64
|
+
} else if (arg === '--height') {
|
|
65
|
+
viewportHeight = parseInt(args[++i]);
|
|
66
|
+
} else if (!input) {
|
|
67
|
+
input = arg;
|
|
68
|
+
} else if (!output) {
|
|
69
|
+
output = arg;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!input) {
|
|
74
|
+
console.error('错误: 请指定输入文件');
|
|
75
|
+
process.exit(1);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 确定输出文件名
|
|
79
|
+
if (!output) {
|
|
80
|
+
const inputBase = path.basename(input).replace(/\.(html?|url)$/i, '');
|
|
81
|
+
output = inputBase + '.pptx';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 判断是 URL 还是文件路径
|
|
85
|
+
const isUrl = /^https?:\/\//i.test(input);
|
|
86
|
+
|
|
87
|
+
async function convert() {
|
|
88
|
+
console.log(`📄 输入: ${input}`);
|
|
89
|
+
console.log(`📦 输出: ${output}`);
|
|
90
|
+
console.log(`🎯 选择器: ${selector}`);
|
|
91
|
+
console.log('');
|
|
92
|
+
|
|
93
|
+
let browser;
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
// 启动浏览器
|
|
97
|
+
console.log('🚀 启动浏览器...');
|
|
98
|
+
browser = await chromium.launch({
|
|
99
|
+
headless: true,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const context = await browser.newContext({
|
|
103
|
+
viewport: { width: viewportWidth, height: viewportHeight },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const page = await context.newPage();
|
|
107
|
+
|
|
108
|
+
// 加载页面
|
|
109
|
+
console.log('📖 加载页面...');
|
|
110
|
+
if (isUrl) {
|
|
111
|
+
await page.goto(input, { waitUntil: 'networkidle', timeout: 30000 });
|
|
112
|
+
} else {
|
|
113
|
+
// 本地文件
|
|
114
|
+
const htmlPath = path.resolve(input);
|
|
115
|
+
if (!fs.existsSync(htmlPath)) {
|
|
116
|
+
throw new Error(`文件不存在: ${htmlPath}`);
|
|
117
|
+
}
|
|
118
|
+
const htmlContent = fs.readFileSync(htmlPath, 'utf8');
|
|
119
|
+
await page.setContent(htmlContent, { waitUntil: 'networkidle' });
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 等待页面完全加载
|
|
123
|
+
await page.waitForTimeout(1000);
|
|
124
|
+
|
|
125
|
+
// 注入 dom-to-pptx 库
|
|
126
|
+
console.log('💉 注入 dom-to-pptx...');
|
|
127
|
+
await page.addScriptTag({
|
|
128
|
+
url: 'https://cdn.jsdelivr.net/npm/dom-to-pptx@latest/dist/dom-to-pptx.bundle.js',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// 等待库加载
|
|
132
|
+
await page.waitForFunction(() => typeof window.domToPptx !== 'undefined', {
|
|
133
|
+
timeout: 10000,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// 检查选择器是否存在(至少一个元素)
|
|
137
|
+
const initialCount = await page.evaluate((sel) => {
|
|
138
|
+
return document.querySelectorAll(sel).length;
|
|
139
|
+
}, selector);
|
|
140
|
+
|
|
141
|
+
if (initialCount === 0) {
|
|
142
|
+
// 尝试常见的选择器
|
|
143
|
+
const fallbackSelectors = ['.slide', '#slide', '[class*="slide"]', 'body > div:first-child'];
|
|
144
|
+
let found = false;
|
|
145
|
+
|
|
146
|
+
for (const fallback of fallbackSelectors) {
|
|
147
|
+
const count = await page.evaluate((sel) => document.querySelectorAll(sel).length, fallback);
|
|
148
|
+
if (count > 0) {
|
|
149
|
+
console.log(`⚠️ 未找到 "${selector}",使用 "${fallback}"`);
|
|
150
|
+
selector = fallback;
|
|
151
|
+
found = true;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!found) {
|
|
157
|
+
throw new Error(`未找到幻灯片元素,尝试的选择器: ${selector}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const slideCount = await page.evaluate((sel) => {
|
|
162
|
+
return document.querySelectorAll(sel).length;
|
|
163
|
+
}, selector);
|
|
164
|
+
|
|
165
|
+
console.log(`🧩 幻灯片数量: ${slideCount}`);
|
|
166
|
+
|
|
167
|
+
// 执行导出
|
|
168
|
+
console.log('⚙️ 正在转换...');
|
|
169
|
+
const pptxBase64 = await page.evaluate(async (sel) => {
|
|
170
|
+
const elements = Array.from(document.querySelectorAll(sel));
|
|
171
|
+
if (elements.length === 0) {
|
|
172
|
+
throw new Error('Element not found');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const target = elements.length === 1 ? elements[0] : elements;
|
|
176
|
+
const blob = await window.domToPptx.exportToPptx(target, {
|
|
177
|
+
skipDownload: true,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// 转换为 Base64
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
const reader = new FileReader();
|
|
183
|
+
reader.onloadend = () => {
|
|
184
|
+
const base64 = reader.result.split(',')[1];
|
|
185
|
+
resolve(base64);
|
|
186
|
+
};
|
|
187
|
+
reader.onerror = reject;
|
|
188
|
+
reader.readAsDataURL(blob);
|
|
189
|
+
});
|
|
190
|
+
}, selector);
|
|
191
|
+
|
|
192
|
+
// 保存文件
|
|
193
|
+
console.log('💾 保存文件...');
|
|
194
|
+
const buffer = Buffer.from(pptxBase64, 'base64');
|
|
195
|
+
fs.writeFileSync(output, buffer);
|
|
196
|
+
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log(`✅ 转换完成: ${output}`);
|
|
199
|
+
console.log(` 文件大小: ${(buffer.length / 1024).toFixed(2)} KB`);
|
|
200
|
+
|
|
201
|
+
await context.close();
|
|
202
|
+
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error('');
|
|
205
|
+
console.error('❌ 转换失败:', error.message);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
} finally {
|
|
208
|
+
if (browser) {
|
|
209
|
+
await browser.close();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
convert();
|
package/cli/output.pptx
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"error":"Missing html or url parameter"}
|