@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.
Files changed (107) hide show
  1. package/README.md +180 -43
  2. package/dist/{chunk-33YRFXZZ.js → chunk-46FMDOWZ.js} +260 -14
  3. package/dist/chunk-46FMDOWZ.js.map +1 -0
  4. package/dist/{chunk-ERZ627GR.js → chunk-5LSSWZPU.js} +12 -27
  5. package/dist/chunk-5LSSWZPU.js.map +1 -0
  6. package/dist/chunk-6M7Z25LA.js +46 -0
  7. package/dist/chunk-6M7Z25LA.js.map +1 -0
  8. package/dist/{chunk-S3Y7H2BK.js → chunk-D7IFPWNZ.js} +95 -50
  9. package/dist/chunk-D7IFPWNZ.js.map +1 -0
  10. package/dist/chunk-DQAZR57U.js +33 -0
  11. package/dist/chunk-DQAZR57U.js.map +1 -0
  12. package/dist/chunk-EHLGMSTE.js +273 -0
  13. package/dist/chunk-EHLGMSTE.js.map +1 -0
  14. package/dist/{chunk-A3FHLTY5.js → chunk-MJGRI6XR.js} +6 -19
  15. package/dist/chunk-MJGRI6XR.js.map +1 -0
  16. package/dist/chunk-PN52A5AA.js +32 -0
  17. package/dist/chunk-PN52A5AA.js.map +1 -0
  18. package/dist/chunk-RFAPOKHJ.js +123 -0
  19. package/dist/chunk-RFAPOKHJ.js.map +1 -0
  20. package/dist/chunk-RTS5XBZ7.js +526 -0
  21. package/dist/chunk-RTS5XBZ7.js.map +1 -0
  22. package/dist/{chunk-7DEUGIOO.js → chunk-SHLPSADL.js} +120 -2
  23. package/dist/chunk-SHLPSADL.js.map +1 -0
  24. package/dist/chunk-SSUPBUF5.js +15 -0
  25. package/dist/chunk-SSUPBUF5.js.map +1 -0
  26. package/dist/{chunk-UDS45KUJ.js → chunk-WWBIKRNP.js} +40 -80
  27. package/dist/chunk-WWBIKRNP.js.map +1 -0
  28. package/dist/{chunk-VN2KEOYB.js → chunk-YESS7VY3.js} +379 -87
  29. package/dist/chunk-YESS7VY3.js.map +1 -0
  30. package/dist/csv/index.d.ts +50 -0
  31. package/dist/csv/index.js +13 -0
  32. package/dist/csv/index.js.map +1 -0
  33. package/dist/docx/index.js +6 -3
  34. package/dist/epub/index.js +4 -2
  35. package/dist/html/index.d.ts +42 -2
  36. package/dist/html/index.js +9 -2
  37. package/dist/index.d.ts +3 -1
  38. package/dist/index.js +47 -14
  39. package/dist/ooxml/index.d.ts +5 -1
  40. package/dist/ooxml/index.js +13 -9
  41. package/dist/pdf/index.js +2 -1
  42. package/dist/pptx/index.d.ts +36 -19
  43. package/dist/pptx/index.js +9 -2
  44. package/dist/registry/index.d.ts +167 -0
  45. package/dist/registry/index.js +17 -0
  46. package/dist/registry/index.js.map +1 -0
  47. package/dist/xlsx/index.d.ts +60 -29
  48. package/dist/xlsx/index.js +4 -1
  49. package/package.json +12 -2
  50. package/src/__tests__/convert.test.ts +186 -0
  51. package/src/__tests__/csvImport.test.ts +84 -0
  52. package/src/__tests__/exportThemeReconciliation.test.ts +87 -0
  53. package/src/__tests__/formatRegistry.test.ts +174 -0
  54. package/src/__tests__/htmlImport.test.ts +57 -0
  55. package/src/__tests__/lossyWarnings.test.ts +105 -0
  56. package/src/__tests__/pdfImport.test.ts +95 -1
  57. package/src/__tests__/plainHtml.test.ts +31 -0
  58. package/src/__tests__/pptxExport.test.ts +138 -0
  59. package/src/__tests__/pptxImport.test.ts +115 -0
  60. package/src/__tests__/roundTripAssets.test.ts +50 -0
  61. package/src/__tests__/roundTripMatrix.fixtures.ts +86 -0
  62. package/src/__tests__/roundTripMatrix.helpers.ts +154 -0
  63. package/src/__tests__/roundTripMatrix.test.ts +142 -0
  64. package/src/__tests__/sharedContainer.test.ts +41 -0
  65. package/src/__tests__/sharedImages.test.ts +61 -0
  66. package/src/__tests__/xlsxExport.test.ts +164 -0
  67. package/src/__tests__/xlsxImport.test.ts +80 -0
  68. package/src/csv/index.ts +188 -0
  69. package/src/docx/export.ts +27 -44
  70. package/src/docx/import.ts +7 -39
  71. package/src/epub/export.ts +10 -27
  72. package/src/html/imageUtils.ts +5 -19
  73. package/src/html/import.ts +297 -0
  74. package/src/html/index.ts +4 -0
  75. package/src/html/plainHtml.ts +66 -16
  76. package/src/index.ts +36 -5
  77. package/src/ooxml/index.ts +3 -0
  78. package/src/ooxml/namespaces.ts +13 -0
  79. package/src/pdf/export.ts +10 -5
  80. package/src/pdf/import.ts +146 -51
  81. package/src/pptx/export.ts +225 -85
  82. package/src/pptx/import.ts +273 -0
  83. package/src/pptx/index.ts +10 -28
  84. package/src/registry/convert.ts +318 -0
  85. package/src/registry/defaultFormats.ts +323 -0
  86. package/src/registry/errors.ts +46 -0
  87. package/src/registry/index.ts +38 -0
  88. package/src/registry/registry.ts +48 -0
  89. package/src/registry/types.ts +107 -0
  90. package/src/shared/container.ts +28 -0
  91. package/src/shared/images.ts +44 -0
  92. package/src/shared/inlineRuns.ts +99 -0
  93. package/src/shared/text.ts +41 -0
  94. package/src/xlsx/export.ts +253 -0
  95. package/src/xlsx/import.ts +159 -0
  96. package/src/xlsx/index.ts +18 -61
  97. package/dist/chunk-33YRFXZZ.js.map +0 -1
  98. package/dist/chunk-67KIJHV2.js +0 -21
  99. package/dist/chunk-67KIJHV2.js.map +0 -1
  100. package/dist/chunk-7DEUGIOO.js.map +0 -1
  101. package/dist/chunk-A3FHLTY5.js.map +0 -1
  102. package/dist/chunk-ERZ627GR.js.map +0 -1
  103. package/dist/chunk-S3Y7H2BK.js.map +0 -1
  104. package/dist/chunk-UDS45KUJ.js.map +0 -1
  105. package/dist/chunk-VN2KEOYB.js.map +0 -1
  106. package/dist/chunk-YN5HFCEW.js +0 -120
  107. 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
- } from "./chunk-7DEUGIOO.js";
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 { resolveTheme, resolveFontFamily } from "@bendyline/squisq/schemas";
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 = resolveTheme(themeId);
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
- return xmlDeclaration() + `<p:sld xmlns:a="${NS_DRAWINGML}" xmlns:r="${NS_R}" xmlns:p="${NS_PML}"><p:cSld>` + bgXml + `<p:spTree>` + shapes.join("") + `</p:spTree></p:cSld></p:sld>`;
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
- const parts = [];
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
- switch (node.type) {
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
- function inferMimeType(ext) {
606
- const mimeTypes = {
607
- jpg: "image/jpeg",
608
- jpeg: "image/jpeg",
609
- png: "image/png",
610
- gif: "image/gif",
611
- webp: "image/webp",
612
- svg: "image/svg+xml",
613
- bmp: "image/bmp",
614
- avif: "image/avif"
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
- return mimeTypes[ext] || "image/png";
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 pptxToMarkdownDoc(_data, _options) {
621
- throw new Error("PPTX import is not yet implemented");
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-VN2KEOYB.js.map
925
+ //# sourceMappingURL=chunk-YESS7VY3.js.map