@bendyline/squisq-formats 1.3.1 → 1.4.1
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 +180 -43
- package/dist/{chunk-33YRFXZZ.js → chunk-46FMDOWZ.js} +260 -14
- package/dist/chunk-46FMDOWZ.js.map +1 -0
- package/dist/{chunk-ERZ627GR.js → chunk-5LSSWZPU.js} +12 -27
- package/dist/chunk-5LSSWZPU.js.map +1 -0
- package/dist/chunk-6M7Z25LA.js +46 -0
- package/dist/chunk-6M7Z25LA.js.map +1 -0
- package/dist/{chunk-S3Y7H2BK.js → chunk-D7IFPWNZ.js} +95 -50
- package/dist/chunk-D7IFPWNZ.js.map +1 -0
- package/dist/chunk-DQAZR57U.js +33 -0
- package/dist/chunk-DQAZR57U.js.map +1 -0
- package/dist/chunk-EHLGMSTE.js +273 -0
- package/dist/chunk-EHLGMSTE.js.map +1 -0
- package/dist/{chunk-A3FHLTY5.js → chunk-MJGRI6XR.js} +6 -19
- package/dist/chunk-MJGRI6XR.js.map +1 -0
- package/dist/chunk-PN52A5AA.js +32 -0
- package/dist/chunk-PN52A5AA.js.map +1 -0
- package/dist/chunk-RFAPOKHJ.js +123 -0
- package/dist/chunk-RFAPOKHJ.js.map +1 -0
- package/dist/chunk-RTS5XBZ7.js +526 -0
- package/dist/chunk-RTS5XBZ7.js.map +1 -0
- package/dist/{chunk-7DEUGIOO.js → chunk-SHLPSADL.js} +120 -2
- package/dist/chunk-SHLPSADL.js.map +1 -0
- package/dist/chunk-SSUPBUF5.js +15 -0
- package/dist/chunk-SSUPBUF5.js.map +1 -0
- package/dist/{chunk-UDS45KUJ.js → chunk-WWBIKRNP.js} +40 -80
- package/dist/chunk-WWBIKRNP.js.map +1 -0
- package/dist/{chunk-VN2KEOYB.js → chunk-YESS7VY3.js} +379 -87
- package/dist/chunk-YESS7VY3.js.map +1 -0
- package/dist/csv/index.d.ts +50 -0
- package/dist/csv/index.js +13 -0
- package/dist/csv/index.js.map +1 -0
- package/dist/docx/index.js +6 -3
- package/dist/epub/index.js +4 -2
- package/dist/html/index.d.ts +42 -2
- package/dist/html/index.js +9 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +47 -14
- package/dist/ooxml/index.d.ts +5 -1
- package/dist/ooxml/index.js +13 -9
- package/dist/pdf/index.js +2 -1
- package/dist/pptx/index.d.ts +36 -19
- package/dist/pptx/index.js +9 -2
- package/dist/registry/index.d.ts +167 -0
- package/dist/registry/index.js +17 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/xlsx/index.d.ts +60 -29
- package/dist/xlsx/index.js +4 -1
- package/package.json +12 -2
- package/src/__tests__/convert.test.ts +186 -0
- package/src/__tests__/csvImport.test.ts +84 -0
- package/src/__tests__/exportThemeReconciliation.test.ts +87 -0
- package/src/__tests__/formatRegistry.test.ts +174 -0
- package/src/__tests__/htmlImport.test.ts +57 -0
- package/src/__tests__/lossyWarnings.test.ts +105 -0
- package/src/__tests__/pdfImport.test.ts +95 -1
- package/src/__tests__/plainHtml.test.ts +31 -0
- package/src/__tests__/pptxExport.test.ts +138 -0
- package/src/__tests__/pptxImport.test.ts +115 -0
- package/src/__tests__/roundTripAssets.test.ts +50 -0
- package/src/__tests__/roundTripMatrix.fixtures.ts +86 -0
- package/src/__tests__/roundTripMatrix.helpers.ts +154 -0
- package/src/__tests__/roundTripMatrix.test.ts +142 -0
- package/src/__tests__/sharedContainer.test.ts +41 -0
- package/src/__tests__/sharedImages.test.ts +61 -0
- package/src/__tests__/xlsxExport.test.ts +164 -0
- package/src/__tests__/xlsxImport.test.ts +80 -0
- package/src/csv/index.ts +188 -0
- package/src/docx/export.ts +27 -44
- package/src/docx/import.ts +7 -39
- package/src/epub/export.ts +10 -27
- package/src/html/imageUtils.ts +5 -19
- package/src/html/import.ts +297 -0
- package/src/html/index.ts +4 -0
- package/src/html/plainHtml.ts +66 -16
- package/src/index.ts +36 -5
- package/src/ooxml/index.ts +3 -0
- package/src/ooxml/namespaces.ts +13 -0
- package/src/pdf/export.ts +10 -5
- package/src/pdf/import.ts +146 -51
- package/src/pptx/export.ts +225 -85
- package/src/pptx/import.ts +273 -0
- package/src/pptx/index.ts +10 -28
- package/src/registry/convert.ts +318 -0
- package/src/registry/defaultFormats.ts +323 -0
- package/src/registry/errors.ts +46 -0
- package/src/registry/index.ts +38 -0
- package/src/registry/registry.ts +48 -0
- package/src/registry/types.ts +107 -0
- package/src/shared/container.ts +28 -0
- package/src/shared/images.ts +44 -0
- package/src/shared/inlineRuns.ts +99 -0
- package/src/shared/text.ts +41 -0
- package/src/xlsx/export.ts +253 -0
- package/src/xlsx/import.ts +159 -0
- package/src/xlsx/index.ts +18 -61
- package/dist/chunk-33YRFXZZ.js.map +0 -1
- package/dist/chunk-67KIJHV2.js +0 -21
- package/dist/chunk-67KIJHV2.js.map +0 -1
- package/dist/chunk-7DEUGIOO.js.map +0 -1
- package/dist/chunk-A3FHLTY5.js.map +0 -1
- package/dist/chunk-ERZ627GR.js.map +0 -1
- package/dist/chunk-S3Y7H2BK.js.map +0 -1
- package/dist/chunk-UDS45KUJ.js.map +0 -1
- package/dist/chunk-VN2KEOYB.js.map +0 -1
- package/dist/chunk-YN5HFCEW.js +0 -120
- package/dist/chunk-YN5HFCEW.js.map +0 -1
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
inlineNodeToRuns,
|
|
3
|
+
inlineNodesToRuns
|
|
4
|
+
} from "./chunk-6M7Z25LA.js";
|
|
5
|
+
import {
|
|
6
|
+
extractPlainText,
|
|
7
|
+
stripHtmlTags
|
|
8
|
+
} from "./chunk-PN52A5AA.js";
|
|
1
9
|
import {
|
|
2
10
|
CONTENT_TYPE_PPTX_PRESENTATION,
|
|
3
11
|
CONTENT_TYPE_PPTX_SLIDE,
|
|
@@ -5,7 +13,9 @@ import {
|
|
|
5
13
|
CONTENT_TYPE_PPTX_SLIDE_MASTER,
|
|
6
14
|
CONTENT_TYPE_PPTX_THEME,
|
|
7
15
|
NS_DRAWINGML,
|
|
16
|
+
NS_MC,
|
|
8
17
|
NS_PML,
|
|
18
|
+
NS_PML_2010,
|
|
9
19
|
NS_R,
|
|
10
20
|
REL_HYPERLINK,
|
|
11
21
|
REL_IMAGE,
|
|
@@ -14,16 +24,29 @@ import {
|
|
|
14
24
|
REL_SLIDE_LAYOUT,
|
|
15
25
|
REL_SLIDE_MASTER,
|
|
16
26
|
REL_THEME,
|
|
17
|
-
createPackage
|
|
18
|
-
|
|
27
|
+
createPackage,
|
|
28
|
+
getPartBinary,
|
|
29
|
+
getPartRelationships,
|
|
30
|
+
getPartXml,
|
|
31
|
+
openPackage
|
|
32
|
+
} from "./chunk-SHLPSADL.js";
|
|
19
33
|
import {
|
|
20
34
|
escapeXml,
|
|
21
35
|
xmlDeclaration
|
|
22
36
|
} from "./chunk-U4MRIFKL.js";
|
|
37
|
+
import {
|
|
38
|
+
buildContainer
|
|
39
|
+
} from "./chunk-SSUPBUF5.js";
|
|
40
|
+
import {
|
|
41
|
+
inferMimeType
|
|
42
|
+
} from "./chunk-MJGRI6XR.js";
|
|
43
|
+
import {
|
|
44
|
+
extToMime
|
|
45
|
+
} from "./chunk-DQAZR57U.js";
|
|
23
46
|
|
|
24
47
|
// src/pptx/export.ts
|
|
25
|
-
import {
|
|
26
|
-
import { docToMarkdown } from "@bendyline/squisq/doc";
|
|
48
|
+
import { resolveFontFamily } from "@bendyline/squisq/schemas";
|
|
49
|
+
import { docToMarkdown, resolveThemeForDoc } from "@bendyline/squisq/doc";
|
|
27
50
|
import { readFrontmatterThemeId } from "@bendyline/squisq/markdown";
|
|
28
51
|
|
|
29
52
|
// src/pptx/styles.ts
|
|
@@ -65,7 +88,7 @@ function buildThemeXml() {
|
|
|
65
88
|
// src/pptx/export.ts
|
|
66
89
|
async function markdownDocToPptx(doc, options = {}) {
|
|
67
90
|
const themeId = options.themeId ?? readFrontmatterThemeId(doc.frontmatter);
|
|
68
|
-
const style = resolveSlideStyle(themeId, options);
|
|
91
|
+
const style = resolveSlideStyle(themeId, options, doc);
|
|
69
92
|
const slides = segmentIntoSlides(doc.children, options.slideBreak ?? "h2");
|
|
70
93
|
if (slides.length === 0) {
|
|
71
94
|
slides.push({ bodyNodes: [] });
|
|
@@ -87,7 +110,7 @@ async function docToPptx(doc, options = {}) {
|
|
|
87
110
|
}
|
|
88
111
|
return markdownDocToPptx(markdownDoc, options);
|
|
89
112
|
}
|
|
90
|
-
function resolveSlideStyle(themeId, options) {
|
|
113
|
+
function resolveSlideStyle(themeId, options, doc) {
|
|
91
114
|
if (!themeId) {
|
|
92
115
|
return {
|
|
93
116
|
background: "FFFFFF",
|
|
@@ -101,7 +124,7 @@ function resolveSlideStyle(themeId, options) {
|
|
|
101
124
|
hasTheme: false
|
|
102
125
|
};
|
|
103
126
|
}
|
|
104
|
-
const theme =
|
|
127
|
+
const theme = resolveThemeForDoc(doc, themeId);
|
|
105
128
|
const c = theme.colors;
|
|
106
129
|
return {
|
|
107
130
|
background: stripHash(c.background),
|
|
@@ -128,6 +151,7 @@ function segmentIntoSlides(children, slideBreak) {
|
|
|
128
151
|
current = {
|
|
129
152
|
title: extractPlainText(node.children),
|
|
130
153
|
titleDepth: node.depth,
|
|
154
|
+
transition: node.attributes?.blockMeta?.transition,
|
|
131
155
|
bodyNodes: []
|
|
132
156
|
};
|
|
133
157
|
} else {
|
|
@@ -140,36 +164,6 @@ function segmentIntoSlides(children, slideBreak) {
|
|
|
140
164
|
if (current) slides.push(current);
|
|
141
165
|
return slides;
|
|
142
166
|
}
|
|
143
|
-
function extractPlainText(nodes) {
|
|
144
|
-
const parts = [];
|
|
145
|
-
for (const node of nodes) {
|
|
146
|
-
switch (node.type) {
|
|
147
|
-
case "text":
|
|
148
|
-
parts.push(node.value);
|
|
149
|
-
break;
|
|
150
|
-
case "inlineCode":
|
|
151
|
-
parts.push(node.value);
|
|
152
|
-
break;
|
|
153
|
-
case "strong":
|
|
154
|
-
case "emphasis":
|
|
155
|
-
case "delete":
|
|
156
|
-
parts.push(extractPlainText(node.children));
|
|
157
|
-
break;
|
|
158
|
-
case "link":
|
|
159
|
-
parts.push(extractPlainText(node.children));
|
|
160
|
-
break;
|
|
161
|
-
case "image":
|
|
162
|
-
parts.push(node.alt ?? "");
|
|
163
|
-
break;
|
|
164
|
-
case "inlineMath":
|
|
165
|
-
parts.push(node.value);
|
|
166
|
-
break;
|
|
167
|
-
default:
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return parts.join("");
|
|
172
|
-
}
|
|
173
167
|
var SlideContext = class {
|
|
174
168
|
// 1=group, 2=title, 3=body
|
|
175
169
|
constructor(style, images, slideIndex) {
|
|
@@ -273,7 +267,175 @@ function buildSlideXml(slide, ctx) {
|
|
|
273
267
|
);
|
|
274
268
|
}
|
|
275
269
|
const bgXml = ctx.style.hasTheme ? `<p:bg><p:bgPr><a:solidFill><a:srgbClr val="${ctx.style.background}"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>` : "";
|
|
276
|
-
|
|
270
|
+
const transitionXml = buildTransitionXml(slide.transition);
|
|
271
|
+
const extensionAttrs = transitionXml.includes("p14:") ? ` xmlns:mc="${NS_MC}" xmlns:p14="${NS_PML_2010}" mc:Ignorable="p14"` : "";
|
|
272
|
+
return xmlDeclaration() + `<p:sld xmlns:a="${NS_DRAWINGML}" xmlns:r="${NS_R}" xmlns:p="${NS_PML}"${extensionAttrs}><p:cSld>` + bgXml + `<p:spTree>` + shapes.join("") + `</p:spTree></p:cSld>` + transitionXml + `</p:sld>`;
|
|
273
|
+
}
|
|
274
|
+
function buildTransitionXml(transition) {
|
|
275
|
+
if (!transition || transition.type === "cut") return "";
|
|
276
|
+
const child = buildTransitionChildXml(transition);
|
|
277
|
+
const spd = pptxSpeedForDuration(transition.duration);
|
|
278
|
+
return `<p:transition spd="${spd}">${child.xml}</p:transition>`;
|
|
279
|
+
}
|
|
280
|
+
function buildTransitionChildXml(transition) {
|
|
281
|
+
const type = transition.type;
|
|
282
|
+
switch (type) {
|
|
283
|
+
case "fade":
|
|
284
|
+
case "morph":
|
|
285
|
+
return p("fade");
|
|
286
|
+
case "dissolve":
|
|
287
|
+
return p("dissolve");
|
|
288
|
+
case "push":
|
|
289
|
+
return p("push", ` dir="${sideDir(transition.direction, "left")}"`);
|
|
290
|
+
case "slideLeft":
|
|
291
|
+
return p("push", ` dir="l"`);
|
|
292
|
+
case "slideRight":
|
|
293
|
+
return p("push", ` dir="r"`);
|
|
294
|
+
case "slideUp":
|
|
295
|
+
return p("push", ` dir="u"`);
|
|
296
|
+
case "slideDown":
|
|
297
|
+
return p("push", ` dir="d"`);
|
|
298
|
+
case "wipe":
|
|
299
|
+
return p("wipe", ` dir="${sideDir(transition.direction, "left")}"`);
|
|
300
|
+
case "split":
|
|
301
|
+
return p("split", ` orient="${orientation(transition.direction)}" dir="out"`);
|
|
302
|
+
case "randomBars":
|
|
303
|
+
case "randomBar":
|
|
304
|
+
return p("randomBar", ` dir="${orientation(transition.direction)}"`);
|
|
305
|
+
case "shape":
|
|
306
|
+
case "circle":
|
|
307
|
+
return p("circle");
|
|
308
|
+
case "diamond":
|
|
309
|
+
return p("diamond");
|
|
310
|
+
case "plus":
|
|
311
|
+
return p("plus");
|
|
312
|
+
case "uncover":
|
|
313
|
+
case "pull":
|
|
314
|
+
return p("pull", ` dir="${sideDir(transition.direction, "left")}"`);
|
|
315
|
+
case "cover":
|
|
316
|
+
return p("cover", ` dir="${sideDir(transition.direction, "left")}"`);
|
|
317
|
+
case "checkerboard":
|
|
318
|
+
case "checker":
|
|
319
|
+
return p("checker", ` dir="${orientation(transition.direction)}"`);
|
|
320
|
+
case "blinds":
|
|
321
|
+
return p("blinds", ` dir="${orientation(transition.direction)}"`);
|
|
322
|
+
case "comb":
|
|
323
|
+
return p("comb", ` dir="${orientation(transition.direction)}"`);
|
|
324
|
+
case "clock":
|
|
325
|
+
case "wheel":
|
|
326
|
+
return p("wheel", ` spokes="4"`);
|
|
327
|
+
case "wheelReverse":
|
|
328
|
+
return p14("wheelReverse");
|
|
329
|
+
case "newsflash":
|
|
330
|
+
return p("newsflash");
|
|
331
|
+
case "random":
|
|
332
|
+
return p("random");
|
|
333
|
+
case "strips":
|
|
334
|
+
return p("strips", ` dir="${cornerDir(transition.direction)}"`);
|
|
335
|
+
case "wedge":
|
|
336
|
+
return p("wedge");
|
|
337
|
+
case "zoom":
|
|
338
|
+
case "box":
|
|
339
|
+
return p("zoom", ` dir="${inOutDir(transition.direction, "in")}"`);
|
|
340
|
+
case "flash":
|
|
341
|
+
return p14("flash");
|
|
342
|
+
case "vortex":
|
|
343
|
+
case "orbit":
|
|
344
|
+
case "rotate":
|
|
345
|
+
return p14("vortex");
|
|
346
|
+
case "switch":
|
|
347
|
+
case "cube":
|
|
348
|
+
return p14("switch");
|
|
349
|
+
case "flip":
|
|
350
|
+
case "fallOver":
|
|
351
|
+
return p14("flip");
|
|
352
|
+
case "ripple":
|
|
353
|
+
return p14("ripple");
|
|
354
|
+
case "glitter":
|
|
355
|
+
case "prestige":
|
|
356
|
+
return p14("glitter");
|
|
357
|
+
case "honeycomb":
|
|
358
|
+
return p14("honeycomb");
|
|
359
|
+
case "prism":
|
|
360
|
+
return p14("prism");
|
|
361
|
+
case "doors":
|
|
362
|
+
case "drape":
|
|
363
|
+
case "curtains":
|
|
364
|
+
return p14("doors");
|
|
365
|
+
case "window":
|
|
366
|
+
return p14("window");
|
|
367
|
+
case "shred":
|
|
368
|
+
case "fracture":
|
|
369
|
+
case "crush":
|
|
370
|
+
return p14("shred");
|
|
371
|
+
case "ferris":
|
|
372
|
+
case "ferrisWheel":
|
|
373
|
+
return p14("ferris");
|
|
374
|
+
case "flythrough":
|
|
375
|
+
case "flyThrough":
|
|
376
|
+
case "airplane":
|
|
377
|
+
case "origami":
|
|
378
|
+
return p14("flythrough");
|
|
379
|
+
case "warp":
|
|
380
|
+
case "pageCurl":
|
|
381
|
+
case "pageCurlDouble":
|
|
382
|
+
case "pageCurlSingle":
|
|
383
|
+
case "peelOff":
|
|
384
|
+
return p14("warp");
|
|
385
|
+
case "gallery":
|
|
386
|
+
return p14("gallery");
|
|
387
|
+
case "conveyor":
|
|
388
|
+
return p14("conveyor");
|
|
389
|
+
case "pan":
|
|
390
|
+
case "wind":
|
|
391
|
+
return p14("pan");
|
|
392
|
+
case "reveal":
|
|
393
|
+
return p14("reveal");
|
|
394
|
+
default:
|
|
395
|
+
return p("fade");
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
function p(tag, attrs = "") {
|
|
399
|
+
return { xml: `<p:${tag}${attrs}/>` };
|
|
400
|
+
}
|
|
401
|
+
function p14(tag, attrs = "") {
|
|
402
|
+
return { xml: `<p14:${tag}${attrs}/>` };
|
|
403
|
+
}
|
|
404
|
+
function pptxSpeedForDuration(duration) {
|
|
405
|
+
if (duration != null && duration <= 0.5) return "fast";
|
|
406
|
+
if (duration != null && duration >= 1.2) return "slow";
|
|
407
|
+
return "med";
|
|
408
|
+
}
|
|
409
|
+
function sideDir(direction, fallback) {
|
|
410
|
+
switch (direction ?? fallback) {
|
|
411
|
+
case "right":
|
|
412
|
+
return "r";
|
|
413
|
+
case "up":
|
|
414
|
+
return "u";
|
|
415
|
+
case "down":
|
|
416
|
+
return "d";
|
|
417
|
+
case "left":
|
|
418
|
+
default:
|
|
419
|
+
return "l";
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function cornerDir(direction) {
|
|
423
|
+
switch (direction) {
|
|
424
|
+
case "right":
|
|
425
|
+
case "down":
|
|
426
|
+
return "rd";
|
|
427
|
+
case "up":
|
|
428
|
+
return "lu";
|
|
429
|
+
case "left":
|
|
430
|
+
default:
|
|
431
|
+
return "ld";
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function orientation(direction) {
|
|
435
|
+
return direction === "vertical" || direction === "up" || direction === "down" ? "vert" : "horz";
|
|
436
|
+
}
|
|
437
|
+
function inOutDir(direction, fallback) {
|
|
438
|
+
return direction === "out" ? "out" : direction === "in" ? "in" : fallback;
|
|
277
439
|
}
|
|
278
440
|
function buildTitleShape(title, style) {
|
|
279
441
|
return `<p:sp><p:nvSpPr><p:cNvPr id="2" name="Title"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph type="title"/></p:nvPr></p:nvSpPr><p:spPr><a:xfrm><a:off x="${TITLE_LEFT}" y="${TITLE_TOP}"/><a:ext cx="${TITLE_WIDTH}" cy="${TITLE_HEIGHT}"/></a:xfrm></p:spPr><p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="en-US" sz="${DEFAULT_TITLE_SIZE}" dirty="0"><a:solidFill><a:srgbClr val="${style.titleColor}"/></a:solidFill><a:latin typeface="${escapeXml(style.titleFont)}"/></a:rPr><a:t>${escapeXml(title)}</a:t></a:r></a:p></p:txBody></p:sp>`;
|
|
@@ -420,38 +582,19 @@ function convertHtmlBlock(node, ctx) {
|
|
|
420
582
|
function convertMathBlock(node) {
|
|
421
583
|
return `<a:p><a:pPr algn="ctr"/><a:r><a:rPr lang="en-US" i="1"><a:latin typeface="Cambria Math"/></a:rPr><a:t>${escapeXml(node.value)}</a:t></a:r></a:p>`;
|
|
422
584
|
}
|
|
585
|
+
function pptxRunHandlers(ctx) {
|
|
586
|
+
return {
|
|
587
|
+
run: (text, format) => makeRun(text, format, ctx.style),
|
|
588
|
+
link: (node, format) => convertLink(node, ctx, format),
|
|
589
|
+
image: (node, format) => convertImage(node, format, ctx),
|
|
590
|
+
lineBreak: () => `<a:br/>`
|
|
591
|
+
};
|
|
592
|
+
}
|
|
423
593
|
function convertInlines(nodes, ctx, format = {}) {
|
|
424
|
-
|
|
425
|
-
for (const node of nodes) {
|
|
426
|
-
parts.push(convertInline(node, ctx, format));
|
|
427
|
-
}
|
|
428
|
-
return parts.join("");
|
|
594
|
+
return inlineNodesToRuns(nodes, pptxRunHandlers(ctx), format);
|
|
429
595
|
}
|
|
430
596
|
function convertInline(node, ctx, format) {
|
|
431
|
-
|
|
432
|
-
case "text":
|
|
433
|
-
return makeRun(node.value, format, ctx.style);
|
|
434
|
-
case "strong":
|
|
435
|
-
return convertInlines(node.children, ctx, { ...format, bold: true });
|
|
436
|
-
case "emphasis":
|
|
437
|
-
return convertInlines(node.children, ctx, { ...format, italic: true });
|
|
438
|
-
case "delete":
|
|
439
|
-
return convertInlines(node.children, ctx, { ...format, strike: true });
|
|
440
|
-
case "inlineCode":
|
|
441
|
-
return makeRun(node.value, { ...format, code: true }, ctx.style);
|
|
442
|
-
case "link":
|
|
443
|
-
return convertLink(node, ctx, format);
|
|
444
|
-
case "image":
|
|
445
|
-
return convertImage(node, format, ctx);
|
|
446
|
-
case "break":
|
|
447
|
-
return `<a:br/>`;
|
|
448
|
-
case "htmlInline":
|
|
449
|
-
return makeRun(stripHtmlTags(node.rawHtml), format, ctx.style);
|
|
450
|
-
case "inlineMath":
|
|
451
|
-
return makeRun(node.value, { ...format, code: true }, ctx.style);
|
|
452
|
-
default:
|
|
453
|
-
return "";
|
|
454
|
-
}
|
|
597
|
+
return inlineNodeToRuns(node, pptxRunHandlers(ctx), format);
|
|
455
598
|
}
|
|
456
599
|
function makeRun(text, format, style) {
|
|
457
600
|
if (!text) return "";
|
|
@@ -593,41 +736,190 @@ async function buildPptxPackage(slideXmls, slideContexts, options) {
|
|
|
593
736
|
}
|
|
594
737
|
return pkg.toArrayBuffer();
|
|
595
738
|
}
|
|
596
|
-
function stripHtmlTags(html) {
|
|
597
|
-
return html.replace(/<[^>]*>/g, "");
|
|
598
|
-
}
|
|
599
739
|
function inferExtension(path) {
|
|
600
740
|
const dot = path.lastIndexOf(".");
|
|
601
741
|
if (dot === -1) return "png";
|
|
602
742
|
const ext = path.substring(dot + 1).toLowerCase().split("?")[0];
|
|
603
743
|
return ext || "png";
|
|
604
744
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
745
|
+
|
|
746
|
+
// src/pptx/index.ts
|
|
747
|
+
import { markdownToDoc } from "@bendyline/squisq/doc";
|
|
748
|
+
|
|
749
|
+
// src/pptx/import.ts
|
|
750
|
+
import { stringifyMarkdown } from "@bendyline/squisq/markdown";
|
|
751
|
+
function attrNS(el, ns, local, fallback) {
|
|
752
|
+
return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
|
|
753
|
+
}
|
|
754
|
+
function resolveTarget(baseDir, target) {
|
|
755
|
+
if (target.startsWith("/")) return target.replace(/^\//, "");
|
|
756
|
+
const stack = baseDir ? baseDir.split("/") : [];
|
|
757
|
+
for (const seg of target.split("/")) {
|
|
758
|
+
if (seg === "..") stack.pop();
|
|
759
|
+
else if (seg !== ".") stack.push(seg);
|
|
760
|
+
}
|
|
761
|
+
return stack.join("/");
|
|
762
|
+
}
|
|
763
|
+
async function orderedSlidePaths(pkg) {
|
|
764
|
+
const pres = await getPartXml(pkg, "ppt/presentation.xml");
|
|
765
|
+
if (!pres) return [];
|
|
766
|
+
const rels = await getPartRelationships(pkg, "ppt/presentation.xml");
|
|
767
|
+
const relById = new Map(rels.map((r) => [r.id, r.target]));
|
|
768
|
+
const out = [];
|
|
769
|
+
const ids = pres.getElementsByTagNameNS(NS_PML, "sldId");
|
|
770
|
+
for (let i = 0; i < ids.length; i++) {
|
|
771
|
+
const rid = attrNS(ids[i], NS_R, "id", "r:id");
|
|
772
|
+
const target = rid ? relById.get(rid) : void 0;
|
|
773
|
+
if (target) out.push(resolveTarget("ppt", target));
|
|
774
|
+
}
|
|
775
|
+
return out;
|
|
776
|
+
}
|
|
777
|
+
function paragraphText(para) {
|
|
778
|
+
const ts = para.getElementsByTagNameNS(NS_DRAWINGML, "t");
|
|
779
|
+
let s = "";
|
|
780
|
+
for (let i = 0; i < ts.length; i++) s += ts[i].textContent ?? "";
|
|
781
|
+
return s.trim();
|
|
782
|
+
}
|
|
783
|
+
function isTitleShape(sp) {
|
|
784
|
+
const ph = sp.getElementsByTagNameNS(NS_PML, "ph");
|
|
785
|
+
if (!ph.length) return false;
|
|
786
|
+
const type = ph[0].getAttribute("type");
|
|
787
|
+
return type === "title" || type === "ctrTitle";
|
|
788
|
+
}
|
|
789
|
+
function tableToMarkdown(tbl) {
|
|
790
|
+
const rows = [];
|
|
791
|
+
const trs = tbl.getElementsByTagNameNS(NS_DRAWINGML, "tr");
|
|
792
|
+
for (let r = 0; r < trs.length; r++) {
|
|
793
|
+
const tcs = trs[r].getElementsByTagNameNS(NS_DRAWINGML, "tc");
|
|
794
|
+
const cells = [];
|
|
795
|
+
for (let c = 0; c < tcs.length; c++) {
|
|
796
|
+
const paras = tcs[c].getElementsByTagNameNS(NS_DRAWINGML, "p");
|
|
797
|
+
const text = Array.from({ length: paras.length }, (_, i) => paragraphText(paras[i])).filter(Boolean).join(" ");
|
|
798
|
+
cells.push({
|
|
799
|
+
type: "tableCell",
|
|
800
|
+
...r === 0 ? { isHeader: true } : {},
|
|
801
|
+
children: text ? [{ type: "text", value: text }] : []
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
rows.push({ type: "tableRow", children: cells });
|
|
805
|
+
}
|
|
806
|
+
return { type: "table", children: rows };
|
|
807
|
+
}
|
|
808
|
+
function baseDirOf(path) {
|
|
809
|
+
const slash = path.lastIndexOf("/");
|
|
810
|
+
return slash === -1 ? "" : path.slice(0, slash);
|
|
811
|
+
}
|
|
812
|
+
async function extractSlideImages(doc, slidePath, ctx) {
|
|
813
|
+
const rels = await getPartRelationships(ctx.pkg, slidePath);
|
|
814
|
+
const relById = new Map(rels.map((r) => [r.id, r.target]));
|
|
815
|
+
const baseDir = baseDirOf(slidePath);
|
|
816
|
+
const images = [];
|
|
817
|
+
const pics = doc.getElementsByTagNameNS(NS_PML, "pic");
|
|
818
|
+
for (let i = 0; i < pics.length; i++) {
|
|
819
|
+
const pic = pics[i];
|
|
820
|
+
const blips = pic.getElementsByTagNameNS(NS_DRAWINGML, "blip");
|
|
821
|
+
if (!blips.length) continue;
|
|
822
|
+
const embed = attrNS(blips[0], NS_R, "embed", "r:embed");
|
|
823
|
+
if (!embed) continue;
|
|
824
|
+
const target = relById.get(embed);
|
|
825
|
+
if (!target) continue;
|
|
826
|
+
const mediaPath = resolveTarget(baseDir, target);
|
|
827
|
+
const data = await getPartBinary(ctx.pkg, mediaPath);
|
|
828
|
+
if (!data) continue;
|
|
829
|
+
const dot = mediaPath.lastIndexOf(".");
|
|
830
|
+
const ext = dot !== -1 ? mediaPath.slice(dot).toLowerCase() : ".png";
|
|
831
|
+
const mimeType = extToMime(ext);
|
|
832
|
+
ctx.imageCounter++;
|
|
833
|
+
const imagePath = `images/image${ctx.imageCounter}${ext}`;
|
|
834
|
+
ctx.extractedImages.set(imagePath, { data, mimeType });
|
|
835
|
+
const cNvPrs = pic.getElementsByTagNameNS(NS_PML, "cNvPr");
|
|
836
|
+
const alt = (cNvPrs.length ? cNvPrs[0].getAttribute("descr") || cNvPrs[0].getAttribute("name") : "") || "Image";
|
|
837
|
+
images.push({ type: "image", url: imagePath, alt });
|
|
838
|
+
}
|
|
839
|
+
return images;
|
|
840
|
+
}
|
|
841
|
+
async function convertSlide(path, index, ctx) {
|
|
842
|
+
const pkg = ctx.pkg;
|
|
843
|
+
const doc = await getPartXml(pkg, path);
|
|
844
|
+
if (!doc) return [];
|
|
845
|
+
const out = [];
|
|
846
|
+
let title = "";
|
|
847
|
+
const bullets = [];
|
|
848
|
+
const shapes = doc.getElementsByTagNameNS(NS_PML, "sp");
|
|
849
|
+
for (let s = 0; s < shapes.length; s++) {
|
|
850
|
+
const sp = shapes[s];
|
|
851
|
+
const txBody = sp.getElementsByTagNameNS(NS_PML, "txBody");
|
|
852
|
+
if (!txBody.length) continue;
|
|
853
|
+
const paras = txBody[0].getElementsByTagNameNS(NS_DRAWINGML, "p");
|
|
854
|
+
const texts = [];
|
|
855
|
+
for (let p2 = 0; p2 < paras.length; p2++) {
|
|
856
|
+
const t = paragraphText(paras[p2]);
|
|
857
|
+
if (t) texts.push(t);
|
|
858
|
+
}
|
|
859
|
+
if (texts.length === 0) continue;
|
|
860
|
+
if (isTitleShape(sp) && !title) {
|
|
861
|
+
title = texts.join(" ");
|
|
862
|
+
} else {
|
|
863
|
+
bullets.push(...texts);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
out.push({
|
|
867
|
+
type: "heading",
|
|
868
|
+
depth: 2,
|
|
869
|
+
children: [{ type: "text", value: title || `Slide ${index + 1}` }]
|
|
870
|
+
});
|
|
871
|
+
if (bullets.length > 0) {
|
|
872
|
+
const items = bullets.map((text) => ({
|
|
873
|
+
type: "listItem",
|
|
874
|
+
children: [{ type: "paragraph", children: [{ type: "text", value: text }] }]
|
|
875
|
+
}));
|
|
876
|
+
out.push({ type: "list", ordered: false, children: items });
|
|
877
|
+
}
|
|
878
|
+
if (ctx.extractImages) {
|
|
879
|
+
const images = await extractSlideImages(doc, path, ctx);
|
|
880
|
+
for (const image of images) {
|
|
881
|
+
out.push({ type: "paragraph", children: [image] });
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
const tbls = doc.getElementsByTagNameNS(NS_DRAWINGML, "tbl");
|
|
885
|
+
for (let t = 0; t < tbls.length; t++) out.push(tableToMarkdown(tbls[t]));
|
|
886
|
+
return out;
|
|
887
|
+
}
|
|
888
|
+
async function importDocument(pkg, options) {
|
|
889
|
+
const ctx = {
|
|
890
|
+
pkg,
|
|
891
|
+
extractImages: options.extractImages ?? false,
|
|
892
|
+
extractedImages: /* @__PURE__ */ new Map(),
|
|
893
|
+
imageCounter: 0
|
|
615
894
|
};
|
|
616
|
-
|
|
895
|
+
const paths = await orderedSlidePaths(pkg);
|
|
896
|
+
const children = [];
|
|
897
|
+
for (let i = 0; i < paths.length; i++) {
|
|
898
|
+
children.push(...await convertSlide(paths[i], i, ctx));
|
|
899
|
+
}
|
|
900
|
+
return { doc: { type: "document", children }, ctx };
|
|
901
|
+
}
|
|
902
|
+
async function pptxToMarkdownDoc(data, options = {}) {
|
|
903
|
+
const pkg = await openPackage(data);
|
|
904
|
+
const { doc } = await importDocument(pkg, options);
|
|
905
|
+
return doc;
|
|
906
|
+
}
|
|
907
|
+
async function pptxToContainer(data, options = {}) {
|
|
908
|
+
const pkg = await openPackage(data);
|
|
909
|
+
const { doc, ctx } = await importDocument(pkg, { ...options, extractImages: true });
|
|
910
|
+
return buildContainer(stringifyMarkdown(doc), ctx.extractedImages);
|
|
617
911
|
}
|
|
618
912
|
|
|
619
913
|
// src/pptx/index.ts
|
|
620
|
-
async function
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
async function pptxToDoc(_data, _options) {
|
|
624
|
-
throw new Error("PPTX import is not yet implemented");
|
|
914
|
+
async function pptxToDoc(data, options) {
|
|
915
|
+
return markdownToDoc(await pptxToMarkdownDoc(data, options));
|
|
625
916
|
}
|
|
626
917
|
|
|
627
918
|
export {
|
|
628
919
|
markdownDocToPptx,
|
|
629
920
|
docToPptx,
|
|
630
921
|
pptxToMarkdownDoc,
|
|
922
|
+
pptxToContainer,
|
|
631
923
|
pptxToDoc
|
|
632
924
|
};
|
|
633
|
-
//# sourceMappingURL=chunk-
|
|
925
|
+
//# sourceMappingURL=chunk-YESS7VY3.js.map
|