feat: jszip 后处理注入 type=custom + 画布高度限制140cm

- 用 jszip 原地修改 PPTX 的 sldSz,加 type=custom(WPS 识别自定义尺寸)
- 画布最大高度限制 140cm(WPS 上限)
- zip 命令行打包会破坏 PPTX 结构,改用 jszip 保证完整性
This commit is contained in:
李进
2026-07-27 12:20:22 +08:00
parent f3d60360ac
commit a10969b562
+27 -2
View File
@@ -932,16 +932,18 @@ async function convert(input) {
// 第三步:组装最终 Schema // 第三步:组装最终 Schema
// ---------------------------------------------------------- // ----------------------------------------------------------
// 画布尺寸:多 slide(PPT设计稿)用容器尺寸,单 slide(网页)用 canvas // 画布尺寸:多 slide(PPT设计稿)用容器尺寸,单 slide(网页)用 canvas
// 限制最大高度 140cmWPS 上限约140cm
const MAX_H_IN = 55.12; // 140cm
let layout = 'LAYOUT_16x9'; let layout = 'LAYOUT_16x9';
var sizeSource; var sizeSource;
if (slideGroups.length > 1) { if (slideGroups.length > 1) {
sizeSource = slideGroups[0].slideRect; sizeSource = slideGroups[0].slideRect;
} else { } else {
sizeSource = { w: canvas.width || 1920, h: canvas.height || 1080 }; sizeSource = { w: canvas.width || 1920, h: Math.min(canvas.height || 1080, MAX_H_IN * 72) };
} }
if (sizeSource.w && sizeSource.h) { if (sizeSource.w && sizeSource.h) {
const sw = sizeSource.w / 72; const sw = sizeSource.w / 72;
const sh = sizeSource.h / 72; const sh = Math.min(sizeSource.h / 72, MAX_H_IN);
layout = 'CUSTOM'; layout = 'CUSTOM';
return { return {
presentation: { layout: 'CUSTOM', slideWidth: sw, slideHeight: sh }, presentation: { layout: 'CUSTOM', slideWidth: sw, slideHeight: sh },
@@ -1046,7 +1048,30 @@ if (require.main === module) {
const pptxPath = inputPath.replace(/\.json$/, '.pptx'); const pptxPath = inputPath.replace(/\.json$/, '.pptx');
pres.writeFile({ fileName: pptxPath }).then(() => { pres.writeFile({ fileName: pptxPath }).then(() => {
// 后处理:给 sldSz 加 type="custom",确保 WPS 等应用识别自定义尺寸
try {
const JSZip = require('jszip');
const zipData = fs.readFileSync(pptxPath);
JSZip.loadAsync(zipData).then(function(zip) {
return zip.file('ppt/presentation.xml').async('string');
}).then(function(presXml) {
if (presXml.includes('sldSz') && !presXml.includes('type="custom"')) {
var fixed = presXml.replace(/sldSz cx="([^"]*)" cy="([^"]*)"/, 'sldSz cx="$1" cy="$2" type="custom"');
return JSZip.loadAsync(zipData).then(function(zip) {
zip.file('ppt/presentation.xml', fixed);
return zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' });
}).then(function(buf) {
fs.writeFileSync(pptxPath, buf);
});
}
}).then(function() {
console.log('✅ PPTX: ' + pptxPath); console.log('✅ PPTX: ' + pptxPath);
}).catch(function(e) {
console.log('✅ PPTX: ' + pptxPath + ' (后处理跳过)');
});
} catch (e) {
console.log('✅ PPTX: ' + pptxPath + ' (后处理跳过)');
}
}).catch(e => console.error('❌ PPTX 渲染失败:', e.message)); }).catch(e => console.error('❌ PPTX 渲染失败:', e.message));
fs.writeFileSync(outputPath, JSON.stringify(result, null, 2), 'utf8'); fs.writeFileSync(outputPath, JSON.stringify(result, null, 2), 'utf8');