@bendyline/squisq-formats 1.3.0 → 1.4.0
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/dist/chunk-6M7Z25LA.js +46 -0
- package/dist/chunk-6M7Z25LA.js.map +1 -0
- package/dist/{chunk-S3Y7H2BK.js → chunk-APSIAEXL.js} +9 -8
- package/dist/chunk-APSIAEXL.js.map +1 -0
- package/dist/{chunk-UDS45KUJ.js → chunk-BJQIJ4S4.js} +36 -52
- package/dist/chunk-BJQIJ4S4.js.map +1 -0
- package/dist/{chunk-33YRFXZZ.js → chunk-CRTC6DEX.js} +259 -13
- package/dist/chunk-CRTC6DEX.js.map +1 -0
- package/dist/chunk-FMOIGA5T.js +201 -0
- package/dist/chunk-FMOIGA5T.js.map +1 -0
- package/dist/{chunk-VN2KEOYB.js → chunk-I2GFTEOV.js} +321 -91
- package/dist/chunk-I2GFTEOV.js.map +1 -0
- package/dist/chunk-JBNVE2GV.js +116 -0
- package/dist/chunk-JBNVE2GV.js.map +1 -0
- package/dist/chunk-L53YIGYS.js +141 -0
- package/dist/chunk-L53YIGYS.js.map +1 -0
- package/dist/{chunk-ERZ627GR.js → chunk-ON232HJR.js} +11 -26
- package/dist/chunk-ON232HJR.js.map +1 -0
- package/dist/chunk-PN52A5AA.js +32 -0
- package/dist/chunk-PN52A5AA.js.map +1 -0
- package/dist/{chunk-7DEUGIOO.js → chunk-QSE7EWE7.js} +12 -87
- package/dist/chunk-QSE7EWE7.js.map +1 -0
- package/dist/container/index.d.ts +7 -5
- package/dist/container/index.js +39 -70
- package/dist/container/index.js.map +1 -1
- package/dist/csv/index.d.ts +37 -0
- package/dist/csv/index.js +13 -0
- package/dist/csv/index.js.map +1 -0
- package/dist/docx/index.d.ts +128 -15
- package/dist/docx/index.js +19 -22
- package/dist/docx/index.js.map +1 -1
- package/dist/epub/index.d.ts +66 -11
- package/dist/epub/index.js +11 -18
- package/dist/epub/index.js.map +1 -1
- package/dist/html/index.d.ts +315 -15
- package/dist/html/index.js +32 -165
- package/dist/html/index.js.map +1 -1
- package/dist/index.d.ts +10 -38
- package/dist/index.js +80 -37
- package/dist/index.js.map +1 -1
- package/dist/ooxml/index.d.ts +353 -14
- package/dist/ooxml/index.js +116 -22
- package/dist/ooxml/index.js.map +1 -1
- package/dist/pdf/index.d.ts +143 -17
- package/dist/pdf/index.js +16 -25
- package/dist/pdf/index.js.map +1 -1
- package/dist/pptx/index.d.ts +98 -21
- package/dist/pptx/index.js +18 -34
- package/dist/pptx/index.js.map +1 -1
- package/dist/xlsx/index.d.ts +29 -25
- package/dist/xlsx/index.js +13 -44
- package/dist/xlsx/index.js.map +1 -1
- package/package.json +7 -2
- package/src/__tests__/csvImport.test.ts +51 -0
- package/src/__tests__/exportThemeReconciliation.test.ts +87 -0
- package/src/__tests__/htmlImport.test.ts +57 -0
- package/src/__tests__/plainHtml.test.ts +31 -0
- package/src/__tests__/pptxExport.test.ts +138 -0
- package/src/__tests__/pptxImport.test.ts +70 -0
- package/src/__tests__/roundTripMatrix.fixtures.ts +86 -0
- package/src/__tests__/roundTripMatrix.helpers.ts +154 -0
- package/src/__tests__/roundTripMatrix.test.ts +136 -0
- package/src/__tests__/xlsxImport.test.ts +80 -0
- package/src/csv/index.ts +165 -0
- package/src/docx/export.ts +27 -44
- package/src/docx/import.ts +1 -2
- package/src/epub/export.ts +10 -27
- 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 +10 -3
- package/src/ooxml/namespaces.ts +3 -0
- package/src/pdf/export.ts +10 -5
- package/src/pdf/import.ts +1 -1
- package/src/pptx/export.ts +225 -85
- package/src/pptx/import.ts +156 -0
- package/src/pptx/index.ts +10 -28
- package/src/shared/inlineRuns.ts +99 -0
- package/src/shared/text.ts +41 -0
- package/src/xlsx/import.ts +159 -0
- package/src/xlsx/index.ts +11 -28
- package/dist/__tests__/container.test.d.ts +0 -8
- package/dist/__tests__/container.test.d.ts.map +0 -1
- package/dist/__tests__/container.test.js +0 -147
- package/dist/__tests__/container.test.js.map +0 -1
- package/dist/__tests__/docxExport.test.d.ts +0 -8
- package/dist/__tests__/docxExport.test.d.ts.map +0 -1
- package/dist/__tests__/docxExport.test.js +0 -383
- package/dist/__tests__/docxExport.test.js.map +0 -1
- package/dist/__tests__/docxImport.test.d.ts +0 -8
- package/dist/__tests__/docxImport.test.d.ts.map +0 -1
- package/dist/__tests__/docxImport.test.js +0 -309
- package/dist/__tests__/docxImport.test.js.map +0 -1
- package/dist/__tests__/epub.test.d.ts +0 -8
- package/dist/__tests__/epub.test.d.ts.map +0 -1
- package/dist/__tests__/epub.test.js +0 -472
- package/dist/__tests__/epub.test.js.map +0 -1
- package/dist/__tests__/html.test.d.ts +0 -8
- package/dist/__tests__/html.test.d.ts.map +0 -1
- package/dist/__tests__/html.test.js +0 -317
- package/dist/__tests__/html.test.js.map +0 -1
- package/dist/__tests__/ooxml.test.d.ts +0 -5
- package/dist/__tests__/ooxml.test.d.ts.map +0 -1
- package/dist/__tests__/ooxml.test.js +0 -152
- package/dist/__tests__/ooxml.test.js.map +0 -1
- package/dist/__tests__/pdfExport.test.d.ts +0 -8
- package/dist/__tests__/pdfExport.test.d.ts.map +0 -1
- package/dist/__tests__/pdfExport.test.js +0 -275
- package/dist/__tests__/pdfExport.test.js.map +0 -1
- package/dist/__tests__/pdfImport.test.d.ts +0 -11
- package/dist/__tests__/pdfImport.test.d.ts.map +0 -1
- package/dist/__tests__/pdfImport.test.js +0 -254
- package/dist/__tests__/pdfImport.test.js.map +0 -1
- package/dist/__tests__/plainHtml.test.d.ts +0 -5
- package/dist/__tests__/plainHtml.test.d.ts.map +0 -1
- package/dist/__tests__/plainHtml.test.js +0 -327
- package/dist/__tests__/plainHtml.test.js.map +0 -1
- package/dist/__tests__/plainHtmlBundle.test.d.ts +0 -11
- package/dist/__tests__/plainHtmlBundle.test.d.ts.map +0 -1
- package/dist/__tests__/plainHtmlBundle.test.js +0 -210
- package/dist/__tests__/plainHtmlBundle.test.js.map +0 -1
- package/dist/__tests__/roundTrip.test.d.ts +0 -7
- package/dist/__tests__/roundTrip.test.d.ts.map +0 -1
- package/dist/__tests__/roundTrip.test.js +0 -173
- package/dist/__tests__/roundTrip.test.js.map +0 -1
- 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-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
- package/dist/container/index.d.ts.map +0 -1
- package/dist/docx/export.d.ts +0 -69
- package/dist/docx/export.d.ts.map +0 -1
- package/dist/docx/export.js +0 -985
- package/dist/docx/export.js.map +0 -1
- package/dist/docx/import.d.ts +0 -65
- package/dist/docx/import.d.ts.map +0 -1
- package/dist/docx/import.js +0 -779
- package/dist/docx/import.js.map +0 -1
- package/dist/docx/index.d.ts.map +0 -1
- package/dist/docx/styles.d.ts +0 -68
- package/dist/docx/styles.d.ts.map +0 -1
- package/dist/docx/styles.js +0 -125
- package/dist/docx/styles.js.map +0 -1
- package/dist/epub/export.d.ts +0 -72
- package/dist/epub/export.d.ts.map +0 -1
- package/dist/epub/export.js +0 -698
- package/dist/epub/export.js.map +0 -1
- package/dist/epub/index.d.ts.map +0 -1
- package/dist/html/docsHtmlBundle.d.ts +0 -53
- package/dist/html/docsHtmlBundle.d.ts.map +0 -1
- package/dist/html/docsHtmlBundle.js +0 -299
- package/dist/html/docsHtmlBundle.js.map +0 -1
- package/dist/html/htmlTemplate.d.ts +0 -70
- package/dist/html/htmlTemplate.d.ts.map +0 -1
- package/dist/html/htmlTemplate.js +0 -282
- package/dist/html/htmlTemplate.js.map +0 -1
- package/dist/html/imageUtils.d.ts +0 -29
- package/dist/html/imageUtils.d.ts.map +0 -1
- package/dist/html/imageUtils.js +0 -63
- package/dist/html/imageUtils.js.map +0 -1
- package/dist/html/index.d.ts.map +0 -1
- package/dist/html/plainHtml.d.ts +0 -79
- package/dist/html/plainHtml.d.ts.map +0 -1
- package/dist/html/plainHtml.js +0 -614
- package/dist/html/plainHtml.js.map +0 -1
- package/dist/html/plainHtmlBundle.d.ts +0 -93
- package/dist/html/plainHtmlBundle.d.ts.map +0 -1
- package/dist/html/plainHtmlBundle.js +0 -357
- package/dist/html/plainHtmlBundle.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/ooxml/index.d.ts.map +0 -1
- package/dist/ooxml/namespaces.d.ts +0 -77
- package/dist/ooxml/namespaces.d.ts.map +0 -1
- package/dist/ooxml/namespaces.js +0 -104
- package/dist/ooxml/namespaces.js.map +0 -1
- package/dist/ooxml/reader.d.ts +0 -52
- package/dist/ooxml/reader.d.ts.map +0 -1
- package/dist/ooxml/reader.js +0 -190
- package/dist/ooxml/reader.js.map +0 -1
- package/dist/ooxml/types.d.ts +0 -82
- package/dist/ooxml/types.d.ts.map +0 -1
- package/dist/ooxml/types.js +0 -9
- package/dist/ooxml/types.js.map +0 -1
- package/dist/ooxml/writer.d.ts +0 -71
- package/dist/ooxml/writer.d.ts.map +0 -1
- package/dist/ooxml/writer.js +0 -200
- package/dist/ooxml/writer.js.map +0 -1
- package/dist/ooxml/xmlUtils.d.ts +0 -76
- package/dist/ooxml/xmlUtils.d.ts.map +0 -1
- package/dist/ooxml/xmlUtils.js +0 -108
- package/dist/ooxml/xmlUtils.js.map +0 -1
- package/dist/pdf/export.d.ts +0 -56
- package/dist/pdf/export.d.ts.map +0 -1
- package/dist/pdf/export.js +0 -768
- package/dist/pdf/export.js.map +0 -1
- package/dist/pdf/import.d.ts +0 -94
- package/dist/pdf/import.d.ts.map +0 -1
- package/dist/pdf/import.js +0 -796
- package/dist/pdf/import.js.map +0 -1
- package/dist/pdf/index.d.ts.map +0 -1
- package/dist/pdf/styles.d.ts +0 -142
- package/dist/pdf/styles.d.ts.map +0 -1
- package/dist/pdf/styles.js +0 -145
- package/dist/pdf/styles.js.map +0 -1
- package/dist/pptx/export.d.ts +0 -75
- package/dist/pptx/export.d.ts.map +0 -1
- package/dist/pptx/export.js +0 -724
- package/dist/pptx/export.js.map +0 -1
- package/dist/pptx/index.d.ts.map +0 -1
- package/dist/pptx/styles.d.ts +0 -54
- package/dist/pptx/styles.d.ts.map +0 -1
- package/dist/pptx/styles.js +0 -73
- package/dist/pptx/styles.js.map +0 -1
- package/dist/pptx/templates.d.ts +0 -24
- package/dist/pptx/templates.d.ts.map +0 -1
- package/dist/pptx/templates.js +0 -158
- package/dist/pptx/templates.js.map +0 -1
- package/dist/xlsx/index.d.ts.map +0 -1
package/src/pptx/export.ts
CHANGED
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
* ```
|
|
28
28
|
*/
|
|
29
29
|
|
|
30
|
-
import type { Doc } from '@bendyline/squisq/schemas';
|
|
31
|
-
import {
|
|
30
|
+
import type { Doc, Transition, TransitionDirection } from '@bendyline/squisq/schemas';
|
|
31
|
+
import { resolveFontFamily } from '@bendyline/squisq/schemas';
|
|
32
32
|
import type { Theme } from '@bendyline/squisq/schemas';
|
|
33
|
-
import { docToMarkdown } from '@bendyline/squisq/doc';
|
|
33
|
+
import { docToMarkdown, resolveThemeForDoc } from '@bendyline/squisq/doc';
|
|
34
34
|
import type {
|
|
35
35
|
MarkdownDocument,
|
|
36
36
|
MarkdownBlockNode,
|
|
@@ -52,9 +52,18 @@ import { readFrontmatterThemeId } from '@bendyline/squisq/markdown';
|
|
|
52
52
|
|
|
53
53
|
import { createPackage } from '../ooxml/writer.js';
|
|
54
54
|
import { xmlDeclaration, escapeXml } from '../ooxml/xmlUtils.js';
|
|
55
|
+
import { inferMimeType } from '../html/imageUtils.js';
|
|
56
|
+
import { stripHtmlTags, extractPlainText } from '../shared/text.js';
|
|
57
|
+
import {
|
|
58
|
+
inlineNodesToRuns,
|
|
59
|
+
inlineNodeToRuns,
|
|
60
|
+
type InlineRunHandlers,
|
|
61
|
+
} from '../shared/inlineRuns.js';
|
|
55
62
|
import {
|
|
56
63
|
NS_PML,
|
|
64
|
+
NS_PML_2010,
|
|
57
65
|
NS_DRAWINGML,
|
|
66
|
+
NS_MC,
|
|
58
67
|
NS_R,
|
|
59
68
|
REL_OFFICE_DOCUMENT,
|
|
60
69
|
REL_SLIDE,
|
|
@@ -143,7 +152,7 @@ export async function markdownDocToPptx(
|
|
|
143
152
|
// shorter legacy `themeId` / `theme` aliases — see
|
|
144
153
|
// `readFrontmatterThemeId` for the precedence.
|
|
145
154
|
const themeId = options.themeId ?? readFrontmatterThemeId(doc.frontmatter);
|
|
146
|
-
const style = resolveSlideStyle(themeId, options);
|
|
155
|
+
const style = resolveSlideStyle(themeId, options, doc);
|
|
147
156
|
|
|
148
157
|
const slides = segmentIntoSlides(doc.children, options.slideBreak ?? 'h2');
|
|
149
158
|
|
|
@@ -196,7 +205,11 @@ interface SlideStyle {
|
|
|
196
205
|
hasTheme: boolean;
|
|
197
206
|
}
|
|
198
207
|
|
|
199
|
-
function resolveSlideStyle(
|
|
208
|
+
function resolveSlideStyle(
|
|
209
|
+
themeId: string | undefined,
|
|
210
|
+
options: PptxExportOptions,
|
|
211
|
+
doc: MarkdownDocument,
|
|
212
|
+
): SlideStyle {
|
|
200
213
|
if (!themeId) {
|
|
201
214
|
return {
|
|
202
215
|
background: 'FFFFFF',
|
|
@@ -211,7 +224,7 @@ function resolveSlideStyle(themeId: string | undefined, options: PptxExportOptio
|
|
|
211
224
|
};
|
|
212
225
|
}
|
|
213
226
|
|
|
214
|
-
const theme: Theme =
|
|
227
|
+
const theme: Theme = resolveThemeForDoc(doc, themeId);
|
|
215
228
|
const c = theme.colors;
|
|
216
229
|
|
|
217
230
|
return {
|
|
@@ -238,6 +251,7 @@ function stripHash(color: string): string {
|
|
|
238
251
|
interface SlideData {
|
|
239
252
|
title?: string;
|
|
240
253
|
titleDepth?: number;
|
|
254
|
+
transition?: Transition;
|
|
241
255
|
bodyNodes: MarkdownBlockNode[];
|
|
242
256
|
}
|
|
243
257
|
|
|
@@ -255,6 +269,7 @@ function segmentIntoSlides(
|
|
|
255
269
|
current = {
|
|
256
270
|
title: extractPlainText(node.children),
|
|
257
271
|
titleDepth: node.depth,
|
|
272
|
+
transition: node.attributes?.blockMeta?.transition,
|
|
258
273
|
bodyNodes: [],
|
|
259
274
|
};
|
|
260
275
|
} else {
|
|
@@ -269,37 +284,6 @@ function segmentIntoSlides(
|
|
|
269
284
|
return slides;
|
|
270
285
|
}
|
|
271
286
|
|
|
272
|
-
function extractPlainText(nodes: MarkdownInlineNode[]): string {
|
|
273
|
-
const parts: string[] = [];
|
|
274
|
-
for (const node of nodes) {
|
|
275
|
-
switch (node.type) {
|
|
276
|
-
case 'text':
|
|
277
|
-
parts.push(node.value);
|
|
278
|
-
break;
|
|
279
|
-
case 'inlineCode':
|
|
280
|
-
parts.push(node.value);
|
|
281
|
-
break;
|
|
282
|
-
case 'strong':
|
|
283
|
-
case 'emphasis':
|
|
284
|
-
case 'delete':
|
|
285
|
-
parts.push(extractPlainText(node.children));
|
|
286
|
-
break;
|
|
287
|
-
case 'link':
|
|
288
|
-
parts.push(extractPlainText(node.children));
|
|
289
|
-
break;
|
|
290
|
-
case 'image':
|
|
291
|
-
parts.push(node.alt ?? '');
|
|
292
|
-
break;
|
|
293
|
-
case 'inlineMath':
|
|
294
|
-
parts.push(node.value);
|
|
295
|
-
break;
|
|
296
|
-
default:
|
|
297
|
-
break;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return parts.join('');
|
|
301
|
-
}
|
|
302
|
-
|
|
303
287
|
// ============================================
|
|
304
288
|
// Slide Context
|
|
305
289
|
// ============================================
|
|
@@ -450,20 +434,208 @@ function buildSlideXml(slide: SlideData, ctx: SlideContext): string {
|
|
|
450
434
|
const bgXml = ctx.style.hasTheme
|
|
451
435
|
? `<p:bg><p:bgPr><a:solidFill><a:srgbClr val="${ctx.style.background}"/></a:solidFill><a:effectLst/></p:bgPr></p:bg>`
|
|
452
436
|
: '';
|
|
437
|
+
const transitionXml = buildTransitionXml(slide.transition);
|
|
438
|
+
const extensionAttrs = transitionXml.includes('p14:')
|
|
439
|
+
? ` xmlns:mc="${NS_MC}" xmlns:p14="${NS_PML_2010}" mc:Ignorable="p14"`
|
|
440
|
+
: '';
|
|
453
441
|
|
|
454
442
|
return (
|
|
455
443
|
xmlDeclaration() +
|
|
456
|
-
`<p:sld xmlns:a="${NS_DRAWINGML}" xmlns:r="${NS_R}" xmlns:p="${NS_PML}">` +
|
|
444
|
+
`<p:sld xmlns:a="${NS_DRAWINGML}" xmlns:r="${NS_R}" xmlns:p="${NS_PML}"${extensionAttrs}>` +
|
|
457
445
|
`<p:cSld>` +
|
|
458
446
|
bgXml +
|
|
459
447
|
`<p:spTree>` +
|
|
460
448
|
shapes.join('') +
|
|
461
449
|
`</p:spTree>` +
|
|
462
450
|
`</p:cSld>` +
|
|
451
|
+
transitionXml +
|
|
463
452
|
`</p:sld>`
|
|
464
453
|
);
|
|
465
454
|
}
|
|
466
455
|
|
|
456
|
+
interface TransitionXml {
|
|
457
|
+
xml: string;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function buildTransitionXml(transition: Transition | undefined): string {
|
|
461
|
+
if (!transition || transition.type === 'cut') return '';
|
|
462
|
+
|
|
463
|
+
const child = buildTransitionChildXml(transition);
|
|
464
|
+
const spd = pptxSpeedForDuration(transition.duration);
|
|
465
|
+
return `<p:transition spd="${spd}">${child.xml}</p:transition>`;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function buildTransitionChildXml(transition: Transition): TransitionXml {
|
|
469
|
+
const type = transition.type;
|
|
470
|
+
switch (type) {
|
|
471
|
+
case 'fade':
|
|
472
|
+
case 'morph':
|
|
473
|
+
return p('fade');
|
|
474
|
+
case 'dissolve':
|
|
475
|
+
return p('dissolve');
|
|
476
|
+
case 'push':
|
|
477
|
+
return p('push', ` dir="${sideDir(transition.direction, 'left')}"`);
|
|
478
|
+
case 'slideLeft':
|
|
479
|
+
return p('push', ` dir="l"`);
|
|
480
|
+
case 'slideRight':
|
|
481
|
+
return p('push', ` dir="r"`);
|
|
482
|
+
case 'slideUp':
|
|
483
|
+
return p('push', ` dir="u"`);
|
|
484
|
+
case 'slideDown':
|
|
485
|
+
return p('push', ` dir="d"`);
|
|
486
|
+
case 'wipe':
|
|
487
|
+
return p('wipe', ` dir="${sideDir(transition.direction, 'left')}"`);
|
|
488
|
+
case 'split':
|
|
489
|
+
return p('split', ` orient="${orientation(transition.direction)}" dir="out"`);
|
|
490
|
+
case 'randomBars':
|
|
491
|
+
case 'randomBar':
|
|
492
|
+
return p('randomBar', ` dir="${orientation(transition.direction)}"`);
|
|
493
|
+
case 'shape':
|
|
494
|
+
case 'circle':
|
|
495
|
+
return p('circle');
|
|
496
|
+
case 'diamond':
|
|
497
|
+
return p('diamond');
|
|
498
|
+
case 'plus':
|
|
499
|
+
return p('plus');
|
|
500
|
+
case 'uncover':
|
|
501
|
+
case 'pull':
|
|
502
|
+
return p('pull', ` dir="${sideDir(transition.direction, 'left')}"`);
|
|
503
|
+
case 'cover':
|
|
504
|
+
return p('cover', ` dir="${sideDir(transition.direction, 'left')}"`);
|
|
505
|
+
case 'checkerboard':
|
|
506
|
+
case 'checker':
|
|
507
|
+
return p('checker', ` dir="${orientation(transition.direction)}"`);
|
|
508
|
+
case 'blinds':
|
|
509
|
+
return p('blinds', ` dir="${orientation(transition.direction)}"`);
|
|
510
|
+
case 'comb':
|
|
511
|
+
return p('comb', ` dir="${orientation(transition.direction)}"`);
|
|
512
|
+
case 'clock':
|
|
513
|
+
case 'wheel':
|
|
514
|
+
return p('wheel', ` spokes="4"`);
|
|
515
|
+
case 'wheelReverse':
|
|
516
|
+
return p14('wheelReverse');
|
|
517
|
+
case 'newsflash':
|
|
518
|
+
return p('newsflash');
|
|
519
|
+
case 'random':
|
|
520
|
+
return p('random');
|
|
521
|
+
case 'strips':
|
|
522
|
+
return p('strips', ` dir="${cornerDir(transition.direction)}"`);
|
|
523
|
+
case 'wedge':
|
|
524
|
+
return p('wedge');
|
|
525
|
+
case 'zoom':
|
|
526
|
+
case 'box':
|
|
527
|
+
return p('zoom', ` dir="${inOutDir(transition.direction, 'in')}"`);
|
|
528
|
+
case 'flash':
|
|
529
|
+
return p14('flash');
|
|
530
|
+
case 'vortex':
|
|
531
|
+
case 'orbit':
|
|
532
|
+
case 'rotate':
|
|
533
|
+
return p14('vortex');
|
|
534
|
+
case 'switch':
|
|
535
|
+
case 'cube':
|
|
536
|
+
return p14('switch');
|
|
537
|
+
case 'flip':
|
|
538
|
+
case 'fallOver':
|
|
539
|
+
return p14('flip');
|
|
540
|
+
case 'ripple':
|
|
541
|
+
return p14('ripple');
|
|
542
|
+
case 'glitter':
|
|
543
|
+
case 'prestige':
|
|
544
|
+
return p14('glitter');
|
|
545
|
+
case 'honeycomb':
|
|
546
|
+
return p14('honeycomb');
|
|
547
|
+
case 'prism':
|
|
548
|
+
return p14('prism');
|
|
549
|
+
case 'doors':
|
|
550
|
+
case 'drape':
|
|
551
|
+
case 'curtains':
|
|
552
|
+
return p14('doors');
|
|
553
|
+
case 'window':
|
|
554
|
+
return p14('window');
|
|
555
|
+
case 'shred':
|
|
556
|
+
case 'fracture':
|
|
557
|
+
case 'crush':
|
|
558
|
+
return p14('shred');
|
|
559
|
+
case 'ferris':
|
|
560
|
+
case 'ferrisWheel':
|
|
561
|
+
return p14('ferris');
|
|
562
|
+
case 'flythrough':
|
|
563
|
+
case 'flyThrough':
|
|
564
|
+
case 'airplane':
|
|
565
|
+
case 'origami':
|
|
566
|
+
return p14('flythrough');
|
|
567
|
+
case 'warp':
|
|
568
|
+
case 'pageCurl':
|
|
569
|
+
case 'pageCurlDouble':
|
|
570
|
+
case 'pageCurlSingle':
|
|
571
|
+
case 'peelOff':
|
|
572
|
+
return p14('warp');
|
|
573
|
+
case 'gallery':
|
|
574
|
+
return p14('gallery');
|
|
575
|
+
case 'conveyor':
|
|
576
|
+
return p14('conveyor');
|
|
577
|
+
case 'pan':
|
|
578
|
+
case 'wind':
|
|
579
|
+
return p14('pan');
|
|
580
|
+
case 'reveal':
|
|
581
|
+
return p14('reveal');
|
|
582
|
+
default:
|
|
583
|
+
return p('fade');
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function p(tag: string, attrs: string = ''): TransitionXml {
|
|
588
|
+
return { xml: `<p:${tag}${attrs}/>` };
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function p14(tag: string, attrs: string = ''): TransitionXml {
|
|
592
|
+
return { xml: `<p14:${tag}${attrs}/>` };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function pptxSpeedForDuration(duration: number | undefined): 'fast' | 'med' | 'slow' {
|
|
596
|
+
if (duration != null && duration <= 0.5) return 'fast';
|
|
597
|
+
if (duration != null && duration >= 1.2) return 'slow';
|
|
598
|
+
return 'med';
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
function sideDir(
|
|
602
|
+
direction: TransitionDirection | undefined,
|
|
603
|
+
fallback: TransitionDirection,
|
|
604
|
+
): string {
|
|
605
|
+
switch (direction ?? fallback) {
|
|
606
|
+
case 'right':
|
|
607
|
+
return 'r';
|
|
608
|
+
case 'up':
|
|
609
|
+
return 'u';
|
|
610
|
+
case 'down':
|
|
611
|
+
return 'd';
|
|
612
|
+
case 'left':
|
|
613
|
+
default:
|
|
614
|
+
return 'l';
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function cornerDir(direction: TransitionDirection | undefined): string {
|
|
619
|
+
switch (direction) {
|
|
620
|
+
case 'right':
|
|
621
|
+
case 'down':
|
|
622
|
+
return 'rd';
|
|
623
|
+
case 'up':
|
|
624
|
+
return 'lu';
|
|
625
|
+
case 'left':
|
|
626
|
+
default:
|
|
627
|
+
return 'ld';
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function orientation(direction: TransitionDirection | undefined): string {
|
|
632
|
+
return direction === 'vertical' || direction === 'up' || direction === 'down' ? 'vert' : 'horz';
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function inOutDir(direction: TransitionDirection | undefined, fallback: 'in' | 'out'): string {
|
|
636
|
+
return direction === 'out' ? 'out' : direction === 'in' ? 'in' : fallback;
|
|
637
|
+
}
|
|
638
|
+
|
|
467
639
|
function buildTitleShape(title: string, style: SlideStyle): string {
|
|
468
640
|
return (
|
|
469
641
|
`<p:sp>` +
|
|
@@ -768,43 +940,29 @@ interface InlineFormat {
|
|
|
768
940
|
code?: boolean;
|
|
769
941
|
}
|
|
770
942
|
|
|
943
|
+
/**
|
|
944
|
+
* PPTX leaf handlers for the shared run-based inline walker. The traversal
|
|
945
|
+
* (format threading) lives in `shared/inlineRuns.ts`; these emit DrawingML.
|
|
946
|
+
*/
|
|
947
|
+
function pptxRunHandlers(ctx: SlideContext): InlineRunHandlers {
|
|
948
|
+
return {
|
|
949
|
+
run: (text, format) => makeRun(text, format, ctx.style),
|
|
950
|
+
link: (node, format) => convertLink(node, ctx, format),
|
|
951
|
+
image: (node, format) => convertImage(node, format, ctx),
|
|
952
|
+
lineBreak: () => `<a:br/>`,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
|
|
771
956
|
function convertInlines(
|
|
772
957
|
nodes: MarkdownInlineNode[],
|
|
773
958
|
ctx: SlideContext,
|
|
774
959
|
format: InlineFormat = {},
|
|
775
960
|
): string {
|
|
776
|
-
|
|
777
|
-
for (const node of nodes) {
|
|
778
|
-
parts.push(convertInline(node, ctx, format));
|
|
779
|
-
}
|
|
780
|
-
return parts.join('');
|
|
961
|
+
return inlineNodesToRuns(nodes, pptxRunHandlers(ctx), format);
|
|
781
962
|
}
|
|
782
963
|
|
|
783
964
|
function convertInline(node: MarkdownInlineNode, ctx: SlideContext, format: InlineFormat): string {
|
|
784
|
-
|
|
785
|
-
case 'text':
|
|
786
|
-
return makeRun(node.value, format, ctx.style);
|
|
787
|
-
case 'strong':
|
|
788
|
-
return convertInlines(node.children, ctx, { ...format, bold: true });
|
|
789
|
-
case 'emphasis':
|
|
790
|
-
return convertInlines(node.children, ctx, { ...format, italic: true });
|
|
791
|
-
case 'delete':
|
|
792
|
-
return convertInlines(node.children, ctx, { ...format, strike: true });
|
|
793
|
-
case 'inlineCode':
|
|
794
|
-
return makeRun(node.value, { ...format, code: true }, ctx.style);
|
|
795
|
-
case 'link':
|
|
796
|
-
return convertLink(node, ctx, format);
|
|
797
|
-
case 'image':
|
|
798
|
-
return convertImage(node, format, ctx);
|
|
799
|
-
case 'break':
|
|
800
|
-
return `<a:br/>`;
|
|
801
|
-
case 'htmlInline':
|
|
802
|
-
return makeRun(stripHtmlTags(node.rawHtml), format, ctx.style);
|
|
803
|
-
case 'inlineMath':
|
|
804
|
-
return makeRun(node.value, { ...format, code: true }, ctx.style);
|
|
805
|
-
default:
|
|
806
|
-
return '';
|
|
807
|
-
}
|
|
965
|
+
return inlineNodeToRuns(node, pptxRunHandlers(ctx), format);
|
|
808
966
|
}
|
|
809
967
|
|
|
810
968
|
function makeRun(text: string, format: InlineFormat, style: SlideStyle): string {
|
|
@@ -1013,10 +1171,6 @@ async function buildPptxPackage(
|
|
|
1013
1171
|
// Helpers
|
|
1014
1172
|
// ============================================
|
|
1015
1173
|
|
|
1016
|
-
function stripHtmlTags(html: string): string {
|
|
1017
|
-
return html.replace(/<[^>]*>/g, '');
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
1174
|
function inferExtension(path: string): string {
|
|
1021
1175
|
const dot = path.lastIndexOf('.');
|
|
1022
1176
|
if (dot === -1) return 'png';
|
|
@@ -1026,17 +1180,3 @@ function inferExtension(path: string): string {
|
|
|
1026
1180
|
.split('?')[0];
|
|
1027
1181
|
return ext || 'png';
|
|
1028
1182
|
}
|
|
1029
|
-
|
|
1030
|
-
function inferMimeType(ext: string): string {
|
|
1031
|
-
const mimeTypes: Record<string, string> = {
|
|
1032
|
-
jpg: 'image/jpeg',
|
|
1033
|
-
jpeg: 'image/jpeg',
|
|
1034
|
-
png: 'image/png',
|
|
1035
|
-
gif: 'image/gif',
|
|
1036
|
-
webp: 'image/webp',
|
|
1037
|
-
svg: 'image/svg+xml',
|
|
1038
|
-
bmp: 'image/bmp',
|
|
1039
|
-
avif: 'image/avif',
|
|
1040
|
-
};
|
|
1041
|
-
return mimeTypes[ext] || 'image/png';
|
|
1042
|
-
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PPTX import — PresentationML (.pptx) → MarkdownDocument.
|
|
3
|
+
*
|
|
4
|
+
* Reuses the shared ooxml/ reader. Reads slide order from
|
|
5
|
+
* `ppt/presentation.xml` (`<p:sldIdLst>`), resolves each slide part via
|
|
6
|
+
* relationships, and converts each slide to: an H2 of the title placeholder
|
|
7
|
+
* (or "Slide N"), the remaining text as a bullet list, and any slide tables
|
|
8
|
+
* (`<a:tbl>`) as markdown tables. Text lives in the DrawingML namespace
|
|
9
|
+
* (`a:p` / `a:r` / `a:t`) inside PresentationML shapes (`p:sp`).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
MarkdownBlockNode,
|
|
14
|
+
MarkdownDocument,
|
|
15
|
+
MarkdownListItem,
|
|
16
|
+
MarkdownTable,
|
|
17
|
+
MarkdownTableCell,
|
|
18
|
+
MarkdownTableRow,
|
|
19
|
+
} from '@bendyline/squisq/markdown';
|
|
20
|
+
import { getPartRelationships, getPartXml, openPackage } from '../ooxml/reader.js';
|
|
21
|
+
import type { OoxmlPackage } from '../ooxml/types.js';
|
|
22
|
+
import { NS_DRAWINGML, NS_PML, NS_R } from '../ooxml/namespaces.js';
|
|
23
|
+
|
|
24
|
+
export interface PptxImportOptions {
|
|
25
|
+
/** Whether to extract embedded images as data URIs (not yet used). */
|
|
26
|
+
extractImages?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function attrNS(el: Element, ns: string, local: string, fallback: string): string | null {
|
|
30
|
+
return el.getAttributeNS(ns, local) ?? el.getAttribute(fallback);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function resolveTarget(baseDir: string, target: string): string {
|
|
34
|
+
if (target.startsWith('/')) return target.replace(/^\//, '');
|
|
35
|
+
const stack = baseDir ? baseDir.split('/') : [];
|
|
36
|
+
for (const seg of target.split('/')) {
|
|
37
|
+
if (seg === '..') stack.pop();
|
|
38
|
+
else if (seg !== '.') stack.push(seg);
|
|
39
|
+
}
|
|
40
|
+
return stack.join('/');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function orderedSlidePaths(pkg: OoxmlPackage): Promise<string[]> {
|
|
44
|
+
const pres = await getPartXml(pkg, 'ppt/presentation.xml');
|
|
45
|
+
if (!pres) return [];
|
|
46
|
+
const rels = await getPartRelationships(pkg, 'ppt/presentation.xml');
|
|
47
|
+
const relById = new Map(rels.map((r) => [r.id, r.target]));
|
|
48
|
+
const out: string[] = [];
|
|
49
|
+
const ids = pres.getElementsByTagNameNS(NS_PML, 'sldId');
|
|
50
|
+
for (let i = 0; i < ids.length; i++) {
|
|
51
|
+
const rid = attrNS(ids[i]!, NS_R, 'id', 'r:id');
|
|
52
|
+
const target = rid ? relById.get(rid) : undefined;
|
|
53
|
+
if (target) out.push(resolveTarget('ppt', target));
|
|
54
|
+
}
|
|
55
|
+
return out;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Concatenate the DrawingML text runs (`a:t`) inside a paragraph element. */
|
|
59
|
+
function paragraphText(para: Element): string {
|
|
60
|
+
const ts = para.getElementsByTagNameNS(NS_DRAWINGML, 't');
|
|
61
|
+
let s = '';
|
|
62
|
+
for (let i = 0; i < ts.length; i++) s += ts[i]!.textContent ?? '';
|
|
63
|
+
return s.trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function isTitleShape(sp: Element): boolean {
|
|
67
|
+
const ph = sp.getElementsByTagNameNS(NS_PML, 'ph');
|
|
68
|
+
if (!ph.length) return false;
|
|
69
|
+
const type = ph[0]!.getAttribute('type');
|
|
70
|
+
return type === 'title' || type === 'ctrTitle';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function tableToMarkdown(tbl: Element): MarkdownTable {
|
|
74
|
+
const rows: MarkdownTableRow[] = [];
|
|
75
|
+
const trs = tbl.getElementsByTagNameNS(NS_DRAWINGML, 'tr');
|
|
76
|
+
for (let r = 0; r < trs.length; r++) {
|
|
77
|
+
const tcs = trs[r]!.getElementsByTagNameNS(NS_DRAWINGML, 'tc');
|
|
78
|
+
const cells: MarkdownTableCell[] = [];
|
|
79
|
+
for (let c = 0; c < tcs.length; c++) {
|
|
80
|
+
const paras = tcs[c]!.getElementsByTagNameNS(NS_DRAWINGML, 'p');
|
|
81
|
+
const text = Array.from({ length: paras.length }, (_, i) => paragraphText(paras[i]!))
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
.join(' ');
|
|
84
|
+
cells.push({
|
|
85
|
+
type: 'tableCell',
|
|
86
|
+
...(r === 0 ? { isHeader: true } : {}),
|
|
87
|
+
children: text ? [{ type: 'text', value: text }] : [],
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
rows.push({ type: 'tableRow', children: cells });
|
|
91
|
+
}
|
|
92
|
+
return { type: 'table', children: rows };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function convertSlide(
|
|
96
|
+
pkg: OoxmlPackage,
|
|
97
|
+
path: string,
|
|
98
|
+
index: number,
|
|
99
|
+
): Promise<MarkdownBlockNode[]> {
|
|
100
|
+
const doc = await getPartXml(pkg, path);
|
|
101
|
+
if (!doc) return [];
|
|
102
|
+
const out: MarkdownBlockNode[] = [];
|
|
103
|
+
|
|
104
|
+
let title = '';
|
|
105
|
+
const bullets: string[] = [];
|
|
106
|
+
const shapes = doc.getElementsByTagNameNS(NS_PML, 'sp');
|
|
107
|
+
for (let s = 0; s < shapes.length; s++) {
|
|
108
|
+
const sp = shapes[s]!;
|
|
109
|
+
const txBody = sp.getElementsByTagNameNS(NS_PML, 'txBody');
|
|
110
|
+
if (!txBody.length) continue;
|
|
111
|
+
const paras = txBody[0]!.getElementsByTagNameNS(NS_DRAWINGML, 'p');
|
|
112
|
+
const texts: string[] = [];
|
|
113
|
+
for (let p = 0; p < paras.length; p++) {
|
|
114
|
+
const t = paragraphText(paras[p]!);
|
|
115
|
+
if (t) texts.push(t);
|
|
116
|
+
}
|
|
117
|
+
if (texts.length === 0) continue;
|
|
118
|
+
if (isTitleShape(sp) && !title) {
|
|
119
|
+
title = texts.join(' ');
|
|
120
|
+
} else {
|
|
121
|
+
bullets.push(...texts);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
out.push({
|
|
126
|
+
type: 'heading',
|
|
127
|
+
depth: 2,
|
|
128
|
+
children: [{ type: 'text', value: title || `Slide ${index + 1}` }],
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
if (bullets.length > 0) {
|
|
132
|
+
const items: MarkdownListItem[] = bullets.map((text) => ({
|
|
133
|
+
type: 'listItem',
|
|
134
|
+
children: [{ type: 'paragraph', children: [{ type: 'text', value: text }] }],
|
|
135
|
+
}));
|
|
136
|
+
out.push({ type: 'list', ordered: false, children: items });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const tbls = doc.getElementsByTagNameNS(NS_DRAWINGML, 'tbl');
|
|
140
|
+
for (let t = 0; t < tbls.length; t++) out.push(tableToMarkdown(tbls[t]!));
|
|
141
|
+
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function pptxToMarkdownDoc(
|
|
146
|
+
data: ArrayBuffer | Blob,
|
|
147
|
+
_options: PptxImportOptions = {},
|
|
148
|
+
): Promise<MarkdownDocument> {
|
|
149
|
+
const pkg = await openPackage(data);
|
|
150
|
+
const paths = await orderedSlidePaths(pkg);
|
|
151
|
+
const children: MarkdownBlockNode[] = [];
|
|
152
|
+
for (let i = 0; i < paths.length; i++) {
|
|
153
|
+
children.push(...(await convertSlide(pkg, paths[i]!, i)));
|
|
154
|
+
}
|
|
155
|
+
return { type: 'document', children };
|
|
156
|
+
}
|
package/src/pptx/index.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Slide segmentation: each H1/H2 heading starts a new slide by default.
|
|
8
8
|
* Inline formatting (bold, italic, code, links) is preserved as DrawingML runs.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Includes both export and import paths.
|
|
11
11
|
*
|
|
12
12
|
* @example
|
|
13
13
|
* ```ts
|
|
@@ -19,38 +19,20 @@
|
|
|
19
19
|
export { markdownDocToPptx, docToPptx } from './export.js';
|
|
20
20
|
export type { PptxExportOptions } from './export.js';
|
|
21
21
|
|
|
22
|
-
// Import
|
|
23
|
-
import
|
|
22
|
+
// Import
|
|
23
|
+
import { markdownToDoc } from '@bendyline/squisq/doc';
|
|
24
24
|
import type { Doc } from '@bendyline/squisq/schemas';
|
|
25
|
+
import { type PptxImportOptions, pptxToMarkdownDoc } from './import.js';
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*/
|
|
29
|
-
export interface PptxImportOptions {
|
|
30
|
-
/** Whether to extract embedded images as data URIs */
|
|
31
|
-
extractImages?: boolean;
|
|
32
|
-
}
|
|
27
|
+
export type { PptxImportOptions } from './import.js';
|
|
28
|
+
export { pptxToMarkdownDoc } from './import.js';
|
|
33
29
|
|
|
34
30
|
/**
|
|
35
|
-
* Convert a .pptx file to a
|
|
36
|
-
*
|
|
37
|
-
* @throws Error — PPTX import is not yet implemented
|
|
38
|
-
*/
|
|
39
|
-
export async function pptxToMarkdownDoc(
|
|
40
|
-
_data: ArrayBuffer | Blob,
|
|
41
|
-
_options?: PptxImportOptions,
|
|
42
|
-
): Promise<MarkdownDocument> {
|
|
43
|
-
throw new Error('PPTX import is not yet implemented');
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Convert a .pptx file to a squisq Doc.
|
|
48
|
-
*
|
|
49
|
-
* @throws Error — PPTX import is not yet implemented
|
|
31
|
+
* Convert a .pptx file to a squisq Doc (via the markdown model).
|
|
50
32
|
*/
|
|
51
33
|
export async function pptxToDoc(
|
|
52
|
-
|
|
53
|
-
|
|
34
|
+
data: ArrayBuffer | Blob,
|
|
35
|
+
options?: PptxImportOptions,
|
|
54
36
|
): Promise<Doc> {
|
|
55
|
-
|
|
37
|
+
return markdownToDoc(await pptxToMarkdownDoc(data, options));
|
|
56
38
|
}
|