pptx-glimpse 3.0.0 → 3.1.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/browser.cjs +3058 -737
- package/dist/browser.d.cts +77 -8
- package/dist/browser.d.ts +77 -8
- package/dist/browser.js +486 -23
- package/dist/{chunk-KMSN55AE.js → chunk-NMAO44T7.js} +2242 -522
- package/dist/{chunk-2AYNMYMC.js → chunk-PGCREQQU.js} +134 -113
- package/dist/{chunk-MWMHSSAD.js → chunk-WAMKNW47.js} +4 -3
- package/dist/chunk-XGCLQP2P.js +83 -0
- package/dist/chunk-ZZKN4U5J.js +141 -0
- package/dist/cli.cjs +11214 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +195 -0
- package/dist/font-collector-BYuaCG-o.d.cts +547 -0
- package/dist/font-collector-BYuaCG-o.d.ts +547 -0
- package/dist/index.cjs +752 -562
- package/dist/index.d.cts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +12 -76
- package/dist/{node-6KTAWUIB.js → node-E6UXPQSQ.js} +2 -2
- package/dist/{node-font-loader-274G445U.js → node-font-loader-OE3OLNDI.js} +2 -2
- package/package.json +9 -6
- package/dist/font-collector-DYooJP6L.d.cts +0 -1345
- package/dist/font-collector-DYooJP6L.d.ts +0 -1345
|
@@ -1,30 +1,111 @@
|
|
|
1
1
|
import {
|
|
2
|
+
DEFAULT_FONT_MAPPING,
|
|
2
3
|
createFontMapping,
|
|
3
4
|
createOpentypeSetupFromBuffers,
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
createWarningLogger,
|
|
6
|
+
getActiveWarningLogger,
|
|
6
7
|
getAscenderRatio,
|
|
7
|
-
|
|
8
|
+
getFontMapping,
|
|
8
9
|
getLineHeightRatio,
|
|
10
|
+
getMappedFont,
|
|
9
11
|
getMetricsFallbackFont,
|
|
10
12
|
getTextPathFontResolver,
|
|
11
|
-
getWarningEntries,
|
|
12
|
-
initWarningLogger,
|
|
13
13
|
measureTextWidth,
|
|
14
|
-
resetFontMapping,
|
|
15
|
-
resetTextPathFontResolver,
|
|
16
|
-
setFontMapping,
|
|
17
|
-
setTextPathFontResolver,
|
|
18
14
|
unsafeBrandAssertion,
|
|
19
15
|
unsafeExternalInteropAssertion,
|
|
20
16
|
warn
|
|
21
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-PGCREQQU.js";
|
|
22
18
|
|
|
23
19
|
// ../renderer/src/utils/constants.ts
|
|
24
20
|
var EMU_PER_INCH = 914400;
|
|
25
21
|
var DEFAULT_DPI = 96;
|
|
26
22
|
var DEFAULT_OUTPUT_WIDTH = 960;
|
|
27
23
|
|
|
24
|
+
// ../renderer/src/font/font-usage-collector.ts
|
|
25
|
+
var FontUsageCollector = class {
|
|
26
|
+
/** key: font name at the beginning of tspan's font-family list (= name declared with @font-face) */
|
|
27
|
+
usages = /* @__PURE__ */ new Map();
|
|
28
|
+
record(fonts, text) {
|
|
29
|
+
const primary = fonts.find((f) => f !== null && f !== void 0);
|
|
30
|
+
if (!primary || text.length === 0) return;
|
|
31
|
+
let usage = this.usages.get(primary);
|
|
32
|
+
if (!usage) {
|
|
33
|
+
usage = { fonts: [...fonts], chars: /* @__PURE__ */ new Set() };
|
|
34
|
+
this.usages.set(primary, usage);
|
|
35
|
+
}
|
|
36
|
+
for (const char of text) {
|
|
37
|
+
usage.chars.add(char);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
getUsages() {
|
|
41
|
+
return this.usages;
|
|
42
|
+
}
|
|
43
|
+
reset() {
|
|
44
|
+
this.usages.clear();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var currentCollector = null;
|
|
48
|
+
function getFontUsageCollector() {
|
|
49
|
+
return currentCollector;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ../renderer/src/font/script-font-context.ts
|
|
53
|
+
var jpanMajorFont = null;
|
|
54
|
+
var jpanMinorFont = null;
|
|
55
|
+
function getScriptFonts() {
|
|
56
|
+
return { majorJpan: jpanMajorFont, minorJpan: jpanMinorFont };
|
|
57
|
+
}
|
|
58
|
+
function getJpanFallbackFont() {
|
|
59
|
+
return jpanMajorFont ?? jpanMinorFont;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ../renderer/src/font/text-measurer.ts
|
|
63
|
+
var DefaultTextMeasurer = class {
|
|
64
|
+
measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa, _context) {
|
|
65
|
+
return measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa);
|
|
66
|
+
}
|
|
67
|
+
getLineHeightRatio(fontFamily, fontFamilyEa, _context) {
|
|
68
|
+
return getLineHeightRatio(fontFamily, fontFamilyEa);
|
|
69
|
+
}
|
|
70
|
+
getAscenderRatio(fontFamily, fontFamilyEa, _context) {
|
|
71
|
+
return getAscenderRatio(fontFamily, fontFamilyEa);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var currentMeasurer = new DefaultTextMeasurer();
|
|
75
|
+
function getTextMeasurer() {
|
|
76
|
+
return currentMeasurer;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ../renderer/src/renderer/render-context.ts
|
|
80
|
+
function createRendererContext(overrides = {}) {
|
|
81
|
+
return {
|
|
82
|
+
textMeasurer: overrides.textMeasurer ?? new DefaultTextMeasurer(),
|
|
83
|
+
textPathFontResolver: overrides.textPathFontResolver ?? null,
|
|
84
|
+
fontMapping: overrides.fontMapping ?? { ...DEFAULT_FONT_MAPPING },
|
|
85
|
+
fontUsageCollector: overrides.fontUsageCollector ?? null,
|
|
86
|
+
scriptFonts: overrides.scriptFonts ?? { majorJpan: null, minorJpan: null },
|
|
87
|
+
warningLogger: overrides.warningLogger ?? createWarningLogger("off"),
|
|
88
|
+
fontWarningCache: overrides.fontWarningCache ?? /* @__PURE__ */ new Set()
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function createLegacyRendererContext() {
|
|
92
|
+
return {
|
|
93
|
+
textMeasurer: getTextMeasurer(),
|
|
94
|
+
textPathFontResolver: getTextPathFontResolver(),
|
|
95
|
+
fontMapping: getFontMapping(),
|
|
96
|
+
fontUsageCollector: getFontUsageCollector(),
|
|
97
|
+
scriptFonts: getScriptFonts(),
|
|
98
|
+
warningLogger: getActiveWarningLogger(),
|
|
99
|
+
fontWarningCache: /* @__PURE__ */ new Set()
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function getJpanFallbackFontFromContext(context) {
|
|
103
|
+
return context.scriptFonts.majorJpan ?? context.scriptFonts.minorJpan;
|
|
104
|
+
}
|
|
105
|
+
function getMappedFontFromContext(fontFamily, context) {
|
|
106
|
+
return getMappedFont(fontFamily, context.fontMapping);
|
|
107
|
+
}
|
|
108
|
+
|
|
28
109
|
// ../renderer/src/utils/base64.ts
|
|
29
110
|
function uint8ArrayToBase64(data) {
|
|
30
111
|
let binary = "";
|
|
@@ -47,7 +128,7 @@ function glyphName(glyph, firstUnicode) {
|
|
|
47
128
|
if (glyph.name) return glyph.name;
|
|
48
129
|
return `uni${firstUnicode.toString(16).toUpperCase().padStart(4, "0")}`;
|
|
49
130
|
}
|
|
50
|
-
async function subsetFont(font, chars, familyName) {
|
|
131
|
+
async function subsetFont(font, chars, familyName, warningLogger) {
|
|
51
132
|
const opentype = await tryLoadOpentypeCtors();
|
|
52
133
|
if (!opentype) return null;
|
|
53
134
|
const source = unsafeExternalInteropAssertion(font);
|
|
@@ -103,44 +184,32 @@ async function subsetFont(font, chars, familyName) {
|
|
|
103
184
|
});
|
|
104
185
|
return new Uint8Array(subset.toArrayBuffer());
|
|
105
186
|
} catch (e) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
187
|
+
const message = `Failed to subset font "${familyName}": ${e instanceof Error ? e.message : String(e)}`;
|
|
188
|
+
if (warningLogger) {
|
|
189
|
+
warningLogger.warn("font.subsetFailed", message);
|
|
190
|
+
} else {
|
|
191
|
+
warn("font.subsetFailed", message);
|
|
192
|
+
}
|
|
110
193
|
return null;
|
|
111
194
|
}
|
|
112
195
|
}
|
|
113
196
|
|
|
114
|
-
// ../renderer/src/font/script-font-context.ts
|
|
115
|
-
var jpanMajorFont = null;
|
|
116
|
-
var jpanMinorFont = null;
|
|
117
|
-
function setScriptFonts(majorJpan, minorJpan) {
|
|
118
|
-
jpanMajorFont = majorJpan;
|
|
119
|
-
jpanMinorFont = minorJpan;
|
|
120
|
-
}
|
|
121
|
-
function resetScriptFonts() {
|
|
122
|
-
jpanMajorFont = null;
|
|
123
|
-
jpanMinorFont = null;
|
|
124
|
-
}
|
|
125
|
-
function getJpanFallbackFont() {
|
|
126
|
-
return jpanMajorFont ?? jpanMinorFont;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
197
|
// ../renderer/src/font/font-embedder.ts
|
|
130
198
|
function escapeCssFamilyName(name) {
|
|
131
199
|
return name.replace(/[\\"]/g, (c) => `\\${c}`).replace(/[<>&]/g, (c) => `\\${c.codePointAt(0).toString(16)} `);
|
|
132
200
|
}
|
|
133
|
-
async function buildFontFaceStyle(usages, fontResolver) {
|
|
201
|
+
async function buildFontFaceStyle(usages, fontResolver, context) {
|
|
134
202
|
const faces = [];
|
|
135
|
-
const jpanFallback = getJpanFallbackFont();
|
|
203
|
+
const jpanFallback = context !== void 0 ? getJpanFallbackFontFromContext(context) : getJpanFallbackFont();
|
|
136
204
|
for (const [familyName, usage] of usages) {
|
|
137
205
|
const font = fontResolver.resolveFont(
|
|
138
206
|
usage.fonts[0],
|
|
139
207
|
usage.fonts[1] ?? null,
|
|
140
|
-
usage.fonts[2] ?? jpanFallback
|
|
208
|
+
usage.fonts[2] ?? jpanFallback,
|
|
209
|
+
context
|
|
141
210
|
);
|
|
142
211
|
if (!font) continue;
|
|
143
|
-
const buffer = await subsetFont(font, usage.chars, familyName);
|
|
212
|
+
const buffer = await subsetFont(font, usage.chars, familyName, context?.warningLogger);
|
|
144
213
|
if (!buffer) continue;
|
|
145
214
|
const base64 = uint8ArrayToBase64(buffer);
|
|
146
215
|
faces.push(
|
|
@@ -151,63 +220,6 @@ async function buildFontFaceStyle(usages, fontResolver) {
|
|
|
151
220
|
return `<style type="text/css">${faces.join("")}</style>`;
|
|
152
221
|
}
|
|
153
222
|
|
|
154
|
-
// ../renderer/src/font/font-usage-collector.ts
|
|
155
|
-
var FontUsageCollector = class {
|
|
156
|
-
/** key: font name at the beginning of tspan's font-family list (= name declared with @font-face) */
|
|
157
|
-
usages = /* @__PURE__ */ new Map();
|
|
158
|
-
record(fonts, text) {
|
|
159
|
-
const primary = fonts.find((f) => f !== null && f !== void 0);
|
|
160
|
-
if (!primary || text.length === 0) return;
|
|
161
|
-
let usage = this.usages.get(primary);
|
|
162
|
-
if (!usage) {
|
|
163
|
-
usage = { fonts: [...fonts], chars: /* @__PURE__ */ new Set() };
|
|
164
|
-
this.usages.set(primary, usage);
|
|
165
|
-
}
|
|
166
|
-
for (const char of text) {
|
|
167
|
-
usage.chars.add(char);
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
getUsages() {
|
|
171
|
-
return this.usages;
|
|
172
|
-
}
|
|
173
|
-
reset() {
|
|
174
|
-
this.usages.clear();
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
var currentCollector = null;
|
|
178
|
-
function setFontUsageCollector(collector) {
|
|
179
|
-
currentCollector = collector;
|
|
180
|
-
}
|
|
181
|
-
function getFontUsageCollector() {
|
|
182
|
-
return currentCollector;
|
|
183
|
-
}
|
|
184
|
-
function resetFontUsageCollector() {
|
|
185
|
-
currentCollector = null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// ../renderer/src/font/text-measurer.ts
|
|
189
|
-
var DefaultTextMeasurer = class {
|
|
190
|
-
measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa) {
|
|
191
|
-
return measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa);
|
|
192
|
-
}
|
|
193
|
-
getLineHeightRatio(fontFamily, fontFamilyEa) {
|
|
194
|
-
return getLineHeightRatio(fontFamily, fontFamilyEa);
|
|
195
|
-
}
|
|
196
|
-
getAscenderRatio(fontFamily, fontFamilyEa) {
|
|
197
|
-
return getAscenderRatio(fontFamily, fontFamilyEa);
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
var currentMeasurer = new DefaultTextMeasurer();
|
|
201
|
-
function setTextMeasurer(measurer) {
|
|
202
|
-
currentMeasurer = measurer;
|
|
203
|
-
}
|
|
204
|
-
function getTextMeasurer() {
|
|
205
|
-
return currentMeasurer;
|
|
206
|
-
}
|
|
207
|
-
function resetTextMeasurer() {
|
|
208
|
-
currentMeasurer = new DefaultTextMeasurer();
|
|
209
|
-
}
|
|
210
|
-
|
|
211
223
|
// ../renderer/src/utils/emu.ts
|
|
212
224
|
function emuToPixels(emu2, dpi = DEFAULT_DPI) {
|
|
213
225
|
return emu2 / EMU_PER_INCH * dpi;
|
|
@@ -350,7 +362,7 @@ var DEFAULT_SERIES_COLORS = [
|
|
|
350
362
|
{ hex: "#70AD47", alpha: 1 }
|
|
351
363
|
];
|
|
352
364
|
var LEGEND_SIDE_WIDTH = 100;
|
|
353
|
-
function renderChart(element) {
|
|
365
|
+
function renderChart(element, context = createLegacyRendererContext()) {
|
|
354
366
|
const { transform, chart } = element;
|
|
355
367
|
const w = emuToPixels(transform.extentWidth);
|
|
356
368
|
const h = emuToPixels(transform.extentHeight);
|
|
@@ -378,37 +390,37 @@ function renderChart(element) {
|
|
|
378
390
|
if (plotW > 0 && plotH > 0) {
|
|
379
391
|
switch (chart.chartType) {
|
|
380
392
|
case "bar":
|
|
381
|
-
parts.push(renderBarChart(chart, plotX, plotY, plotW, plotH));
|
|
393
|
+
parts.push(renderBarChart(chart, plotX, plotY, plotW, plotH, context));
|
|
382
394
|
break;
|
|
383
395
|
case "line":
|
|
384
|
-
parts.push(renderLineChart(chart, plotX, plotY, plotW, plotH));
|
|
396
|
+
parts.push(renderLineChart(chart, plotX, plotY, plotW, plotH, context));
|
|
385
397
|
break;
|
|
386
398
|
case "pie":
|
|
387
|
-
parts.push(renderPieChart(chart, plotX, plotY, plotW, plotH));
|
|
399
|
+
parts.push(renderPieChart(chart, plotX, plotY, plotW, plotH, context));
|
|
388
400
|
break;
|
|
389
401
|
case "doughnut":
|
|
390
|
-
parts.push(renderDoughnutChart(chart, plotX, plotY, plotW, plotH));
|
|
402
|
+
parts.push(renderDoughnutChart(chart, plotX, plotY, plotW, plotH, context));
|
|
391
403
|
break;
|
|
392
404
|
case "scatter":
|
|
393
|
-
parts.push(renderScatterChart(chart, plotX, plotY, plotW, plotH));
|
|
405
|
+
parts.push(renderScatterChart(chart, plotX, plotY, plotW, plotH, context));
|
|
394
406
|
break;
|
|
395
407
|
case "bubble":
|
|
396
|
-
parts.push(renderBubbleChart(chart, plotX, plotY, plotW, plotH));
|
|
408
|
+
parts.push(renderBubbleChart(chart, plotX, plotY, plotW, plotH, context));
|
|
397
409
|
break;
|
|
398
410
|
case "area":
|
|
399
|
-
parts.push(renderAreaChart(chart, plotX, plotY, plotW, plotH));
|
|
411
|
+
parts.push(renderAreaChart(chart, plotX, plotY, plotW, plotH, context));
|
|
400
412
|
break;
|
|
401
413
|
case "radar":
|
|
402
|
-
parts.push(renderRadarChart(chart, plotX, plotY, plotW, plotH));
|
|
414
|
+
parts.push(renderRadarChart(chart, plotX, plotY, plotW, plotH, context));
|
|
403
415
|
break;
|
|
404
416
|
case "stock":
|
|
405
|
-
parts.push(renderStockChart(chart, plotX, plotY, plotW, plotH));
|
|
417
|
+
parts.push(renderStockChart(chart, plotX, plotY, plotW, plotH, context));
|
|
406
418
|
break;
|
|
407
419
|
case "surface":
|
|
408
|
-
parts.push(renderSurfaceChart(chart, plotX, plotY, plotW, plotH));
|
|
420
|
+
parts.push(renderSurfaceChart(chart, plotX, plotY, plotW, plotH, context));
|
|
409
421
|
break;
|
|
410
422
|
case "ofPie":
|
|
411
|
-
parts.push(renderOfPieChart(chart, plotX, plotY, plotW, plotH));
|
|
423
|
+
parts.push(renderOfPieChart(chart, plotX, plotY, plotW, plotH, context));
|
|
412
424
|
break;
|
|
413
425
|
}
|
|
414
426
|
}
|
|
@@ -421,21 +433,24 @@ function renderChart(element) {
|
|
|
421
433
|
function renderChartTitle(title, chartWidth) {
|
|
422
434
|
return `<text x="${round2(chartWidth / 2)}" y="20" text-anchor="middle" font-size="14" font-weight="bold" fill="#404040">${escapeXml(title)}</text>`;
|
|
423
435
|
}
|
|
424
|
-
function
|
|
436
|
+
function debugChart(context, feature, message) {
|
|
437
|
+
context.warningLogger.debug(feature, message);
|
|
438
|
+
}
|
|
439
|
+
function renderBarChart(chart, x, y, w, h, context) {
|
|
425
440
|
const parts = [];
|
|
426
441
|
const { series, categories } = chart;
|
|
427
442
|
if (series.length === 0) {
|
|
428
|
-
|
|
443
|
+
debugChart(context, "chart.bar", "series is empty");
|
|
429
444
|
return "";
|
|
430
445
|
}
|
|
431
446
|
const maxVal = getMaxValue(series);
|
|
432
447
|
if (maxVal === 0) {
|
|
433
|
-
|
|
448
|
+
debugChart(context, "chart.bar", "max value is 0");
|
|
434
449
|
return "";
|
|
435
450
|
}
|
|
436
451
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
437
452
|
if (catCount === 0) {
|
|
438
|
-
|
|
453
|
+
debugChart(context, "chart.bar", "category count is 0");
|
|
439
454
|
return "";
|
|
440
455
|
}
|
|
441
456
|
const isHorizontal = chart.barDirection === "bar";
|
|
@@ -508,21 +523,21 @@ function renderBarChart(chart, x, y, w, h) {
|
|
|
508
523
|
}
|
|
509
524
|
return parts.join("");
|
|
510
525
|
}
|
|
511
|
-
function renderLineChart(chart, x, y, w, h) {
|
|
526
|
+
function renderLineChart(chart, x, y, w, h, context) {
|
|
512
527
|
const parts = [];
|
|
513
528
|
const { series, categories } = chart;
|
|
514
529
|
if (series.length === 0) {
|
|
515
|
-
|
|
530
|
+
debugChart(context, "chart.line", "series is empty");
|
|
516
531
|
return "";
|
|
517
532
|
}
|
|
518
533
|
const maxVal = getMaxValue(series);
|
|
519
534
|
if (maxVal === 0) {
|
|
520
|
-
|
|
535
|
+
debugChart(context, "chart.line", "max value is 0");
|
|
521
536
|
return "";
|
|
522
537
|
}
|
|
523
538
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
524
539
|
if (catCount === 0) {
|
|
525
|
-
|
|
540
|
+
debugChart(context, "chart.line", "category count is 0");
|
|
526
541
|
return "";
|
|
527
542
|
}
|
|
528
543
|
const ticks = computeNiceTicks(0, maxVal);
|
|
@@ -560,21 +575,21 @@ function renderLineChart(chart, x, y, w, h) {
|
|
|
560
575
|
}
|
|
561
576
|
return parts.join("");
|
|
562
577
|
}
|
|
563
|
-
function renderAreaChart(chart, x, y, w, h) {
|
|
578
|
+
function renderAreaChart(chart, x, y, w, h, context) {
|
|
564
579
|
const parts = [];
|
|
565
580
|
const { series, categories } = chart;
|
|
566
581
|
if (series.length === 0) {
|
|
567
|
-
|
|
582
|
+
debugChart(context, "chart.area", "series is empty");
|
|
568
583
|
return "";
|
|
569
584
|
}
|
|
570
585
|
const maxVal = getMaxValue(series);
|
|
571
586
|
if (maxVal === 0) {
|
|
572
|
-
|
|
587
|
+
debugChart(context, "chart.area", "max value is 0");
|
|
573
588
|
return "";
|
|
574
589
|
}
|
|
575
590
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
576
591
|
if (catCount === 0) {
|
|
577
|
-
|
|
592
|
+
debugChart(context, "chart.area", "category count is 0");
|
|
578
593
|
return "";
|
|
579
594
|
}
|
|
580
595
|
const ticks = computeNiceTicks(0, maxVal);
|
|
@@ -613,16 +628,16 @@ function renderAreaChart(chart, x, y, w, h) {
|
|
|
613
628
|
}
|
|
614
629
|
return parts.join("");
|
|
615
630
|
}
|
|
616
|
-
function renderPieChart(chart, x, y, w, h) {
|
|
631
|
+
function renderPieChart(chart, x, y, w, h, context) {
|
|
617
632
|
const parts = [];
|
|
618
633
|
const series = chart.series[0];
|
|
619
634
|
if (!series || series.values.length === 0) {
|
|
620
|
-
|
|
635
|
+
debugChart(context, "chart.pie", "series is empty or has no values");
|
|
621
636
|
return "";
|
|
622
637
|
}
|
|
623
638
|
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
624
639
|
if (total === 0) {
|
|
625
|
-
|
|
640
|
+
debugChart(context, "chart.pie", "total value is 0");
|
|
626
641
|
return "";
|
|
627
642
|
}
|
|
628
643
|
const cx = x + w / 2;
|
|
@@ -651,16 +666,16 @@ function renderPieChart(chart, x, y, w, h) {
|
|
|
651
666
|
}
|
|
652
667
|
return parts.join("");
|
|
653
668
|
}
|
|
654
|
-
function renderDoughnutChart(chart, x, y, w, h) {
|
|
669
|
+
function renderDoughnutChart(chart, x, y, w, h, context) {
|
|
655
670
|
const parts = [];
|
|
656
671
|
const series = chart.series[0];
|
|
657
672
|
if (!series || series.values.length === 0) {
|
|
658
|
-
|
|
673
|
+
debugChart(context, "chart.doughnut", "series is empty or has no values");
|
|
659
674
|
return "";
|
|
660
675
|
}
|
|
661
676
|
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
662
677
|
if (total === 0) {
|
|
663
|
-
|
|
678
|
+
debugChart(context, "chart.doughnut", "total value is 0");
|
|
664
679
|
return "";
|
|
665
680
|
}
|
|
666
681
|
const cx = x + w / 2;
|
|
@@ -698,11 +713,11 @@ function renderDoughnutChart(chart, x, y, w, h) {
|
|
|
698
713
|
}
|
|
699
714
|
return parts.join("");
|
|
700
715
|
}
|
|
701
|
-
function renderScatterChart(chart, x, y, w, h) {
|
|
716
|
+
function renderScatterChart(chart, x, y, w, h, context) {
|
|
702
717
|
const parts = [];
|
|
703
718
|
const { series } = chart;
|
|
704
719
|
if (series.length === 0) {
|
|
705
|
-
|
|
720
|
+
debugChart(context, "chart.scatter", "series is empty");
|
|
706
721
|
return "";
|
|
707
722
|
}
|
|
708
723
|
let maxX = 0;
|
|
@@ -736,11 +751,11 @@ function renderScatterChart(chart, x, y, w, h) {
|
|
|
736
751
|
}
|
|
737
752
|
return parts.join("");
|
|
738
753
|
}
|
|
739
|
-
function renderBubbleChart(chart, x, y, w, h) {
|
|
754
|
+
function renderBubbleChart(chart, x, y, w, h, context) {
|
|
740
755
|
const parts = [];
|
|
741
756
|
const { series } = chart;
|
|
742
757
|
if (series.length === 0) {
|
|
743
|
-
|
|
758
|
+
debugChart(context, "chart.bubble", "series is empty");
|
|
744
759
|
return "";
|
|
745
760
|
}
|
|
746
761
|
let maxX = 0;
|
|
@@ -784,21 +799,21 @@ function renderBubbleChart(chart, x, y, w, h) {
|
|
|
784
799
|
}
|
|
785
800
|
return parts.join("");
|
|
786
801
|
}
|
|
787
|
-
function renderRadarChart(chart, x, y, w, h) {
|
|
802
|
+
function renderRadarChart(chart, x, y, w, h, context) {
|
|
788
803
|
const parts = [];
|
|
789
804
|
const { series, categories } = chart;
|
|
790
805
|
if (series.length === 0) {
|
|
791
|
-
|
|
806
|
+
debugChart(context, "chart.radar", "series is empty");
|
|
792
807
|
return "";
|
|
793
808
|
}
|
|
794
809
|
const maxVal = getMaxValue(series);
|
|
795
810
|
if (maxVal === 0) {
|
|
796
|
-
|
|
811
|
+
debugChart(context, "chart.radar", "max value is 0");
|
|
797
812
|
return "";
|
|
798
813
|
}
|
|
799
814
|
const catCount = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
800
815
|
if (catCount === 0) {
|
|
801
|
-
|
|
816
|
+
debugChart(context, "chart.radar", "category count is 0");
|
|
802
817
|
return "";
|
|
803
818
|
}
|
|
804
819
|
const cx = x + w / 2;
|
|
@@ -861,11 +876,15 @@ function renderRadarChart(chart, x, y, w, h) {
|
|
|
861
876
|
}
|
|
862
877
|
return parts.join("");
|
|
863
878
|
}
|
|
864
|
-
function renderStockChart(chart, x, y, w, h) {
|
|
879
|
+
function renderStockChart(chart, x, y, w, h, context) {
|
|
865
880
|
const parts = [];
|
|
866
881
|
const { series, categories } = chart;
|
|
867
882
|
if (series.length < 3) {
|
|
868
|
-
|
|
883
|
+
debugChart(
|
|
884
|
+
context,
|
|
885
|
+
"chart.stock",
|
|
886
|
+
`insufficient series count: ${series.length} (need at least 3)`
|
|
887
|
+
);
|
|
869
888
|
return "";
|
|
870
889
|
}
|
|
871
890
|
const highSeries = series[0];
|
|
@@ -873,7 +892,7 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
873
892
|
const closeSeries = series[2];
|
|
874
893
|
const catCount = categories.length || highSeries.values.length;
|
|
875
894
|
if (catCount === 0) {
|
|
876
|
-
|
|
895
|
+
debugChart(context, "chart.stock", "category count is 0");
|
|
877
896
|
return "";
|
|
878
897
|
}
|
|
879
898
|
let maxVal = 0;
|
|
@@ -885,7 +904,7 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
885
904
|
}
|
|
886
905
|
}
|
|
887
906
|
if (maxVal === minVal) {
|
|
888
|
-
|
|
907
|
+
debugChart(context, "chart.stock", "max equals min value");
|
|
889
908
|
return "";
|
|
890
909
|
}
|
|
891
910
|
const ticks = computeNiceTicks(minVal, maxVal);
|
|
@@ -924,17 +943,17 @@ function renderStockChart(chart, x, y, w, h) {
|
|
|
924
943
|
}
|
|
925
944
|
return parts.join("");
|
|
926
945
|
}
|
|
927
|
-
function renderSurfaceChart(chart, x, y, w, h) {
|
|
946
|
+
function renderSurfaceChart(chart, x, y, w, h, context) {
|
|
928
947
|
const parts = [];
|
|
929
948
|
const { series, categories } = chart;
|
|
930
949
|
if (series.length === 0) {
|
|
931
|
-
|
|
950
|
+
debugChart(context, "chart.surface", "series is empty");
|
|
932
951
|
return "";
|
|
933
952
|
}
|
|
934
953
|
const rows = series.length;
|
|
935
954
|
const cols = categories.length || Math.max(...series.map((s) => s.values.length));
|
|
936
955
|
if (cols === 0) {
|
|
937
|
-
|
|
956
|
+
debugChart(context, "chart.surface", "column count is 0");
|
|
938
957
|
return "";
|
|
939
958
|
}
|
|
940
959
|
let minVal = Infinity;
|
|
@@ -1006,16 +1025,16 @@ function heatmapColor(t) {
|
|
|
1006
1025
|
}
|
|
1007
1026
|
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
|
1008
1027
|
}
|
|
1009
|
-
function renderOfPieChart(chart, x, y, w, h) {
|
|
1028
|
+
function renderOfPieChart(chart, x, y, w, h, context) {
|
|
1010
1029
|
const parts = [];
|
|
1011
1030
|
const series = chart.series[0];
|
|
1012
1031
|
if (!series || series.values.length === 0) {
|
|
1013
|
-
|
|
1032
|
+
debugChart(context, "chart.ofPie", "series is empty or has no values");
|
|
1014
1033
|
return "";
|
|
1015
1034
|
}
|
|
1016
1035
|
const total = series.values.reduce((sum, v) => sum + v, 0);
|
|
1017
1036
|
if (total === 0) {
|
|
1018
|
-
|
|
1037
|
+
debugChart(context, "chart.ofPie", "total value is 0");
|
|
1019
1038
|
return "";
|
|
1020
1039
|
}
|
|
1021
1040
|
const splitPos = chart.splitPos ?? 2;
|
|
@@ -2543,7 +2562,7 @@ function splitTextIntoFragments(text) {
|
|
|
2543
2562
|
}
|
|
2544
2563
|
return fragments;
|
|
2545
2564
|
}
|
|
2546
|
-
function tokenizeRuns(runs, defaultFontSize, fontScale) {
|
|
2565
|
+
function tokenizeRuns(runs, defaultFontSize, fontScale, textMeasurer, measurementContext) {
|
|
2547
2566
|
const tokens = [];
|
|
2548
2567
|
let isFirst = true;
|
|
2549
2568
|
for (const run of runs) {
|
|
@@ -2569,12 +2588,13 @@ function tokenizeRuns(runs, defaultFontSize, fontScale) {
|
|
|
2569
2588
|
const fontFamilyEa2 = run.properties.fontFamilyEa;
|
|
2570
2589
|
const fragments2 = splitTextIntoFragments(part);
|
|
2571
2590
|
for (const { fragment, breakable } of fragments2) {
|
|
2572
|
-
const width =
|
|
2591
|
+
const width = textMeasurer.measureTextWidth(
|
|
2573
2592
|
fragment,
|
|
2574
2593
|
fontSize2,
|
|
2575
2594
|
bold2,
|
|
2576
2595
|
fontFamily2,
|
|
2577
|
-
fontFamilyEa2
|
|
2596
|
+
fontFamilyEa2,
|
|
2597
|
+
measurementContext
|
|
2578
2598
|
);
|
|
2579
2599
|
tokens.push({
|
|
2580
2600
|
text: fragment,
|
|
@@ -2593,12 +2613,13 @@ function tokenizeRuns(runs, defaultFontSize, fontScale) {
|
|
|
2593
2613
|
const fontFamilyEa = run.properties.fontFamilyEa;
|
|
2594
2614
|
const fragments = splitTextIntoFragments(run.text);
|
|
2595
2615
|
for (const { fragment, breakable } of fragments) {
|
|
2596
|
-
const width =
|
|
2616
|
+
const width = textMeasurer.measureTextWidth(
|
|
2597
2617
|
fragment,
|
|
2598
2618
|
fontSize,
|
|
2599
2619
|
bold,
|
|
2600
2620
|
fontFamily,
|
|
2601
|
-
fontFamilyEa
|
|
2621
|
+
fontFamilyEa,
|
|
2622
|
+
measurementContext
|
|
2602
2623
|
);
|
|
2603
2624
|
tokens.push({
|
|
2604
2625
|
text: fragment,
|
|
@@ -2617,7 +2638,7 @@ function isSpaceOnly(text) {
|
|
|
2617
2638
|
}
|
|
2618
2639
|
return true;
|
|
2619
2640
|
}
|
|
2620
|
-
function splitTokenByChars(token, availableWidth, defaultFontSize, fontScale) {
|
|
2641
|
+
function splitTokenByChars(token, availableWidth, defaultFontSize, fontScale, textMeasurer, measurementContext) {
|
|
2621
2642
|
const lines = [];
|
|
2622
2643
|
let currentLine = [];
|
|
2623
2644
|
let currentWidth = 0;
|
|
@@ -2626,12 +2647,13 @@ function splitTokenByChars(token, availableWidth, defaultFontSize, fontScale) {
|
|
|
2626
2647
|
const fontFamily = token.properties.fontFamily;
|
|
2627
2648
|
const fontFamilyEa = token.properties.fontFamilyEa;
|
|
2628
2649
|
for (const char of token.text) {
|
|
2629
|
-
const charWidth =
|
|
2650
|
+
const charWidth = textMeasurer.measureTextWidth(
|
|
2630
2651
|
char,
|
|
2631
2652
|
fontSize,
|
|
2632
2653
|
bold,
|
|
2633
2654
|
fontFamily,
|
|
2634
|
-
fontFamilyEa
|
|
2655
|
+
fontFamilyEa,
|
|
2656
|
+
measurementContext
|
|
2635
2657
|
);
|
|
2636
2658
|
if (currentWidth + charWidth > availableWidth && currentLine.length > 0) {
|
|
2637
2659
|
lines.push(currentLine);
|
|
@@ -2678,7 +2700,7 @@ function trimTrailingSpaces(segments) {
|
|
|
2678
2700
|
}
|
|
2679
2701
|
return segments;
|
|
2680
2702
|
}
|
|
2681
|
-
function layoutTokensIntoLines(tokens, availableWidth, defaultFontSize, fontScale) {
|
|
2703
|
+
function layoutTokensIntoLines(tokens, availableWidth, defaultFontSize, fontScale, textMeasurer, measurementContext) {
|
|
2682
2704
|
if (tokens.length === 0) return [{ segments: [] }];
|
|
2683
2705
|
const lines = [];
|
|
2684
2706
|
let currentLine = [];
|
|
@@ -2700,7 +2722,14 @@ function layoutTokensIntoLines(tokens, availableWidth, defaultFontSize, fontScal
|
|
|
2700
2722
|
if (isSpaceOnly(token.text)) {
|
|
2701
2723
|
continue;
|
|
2702
2724
|
}
|
|
2703
|
-
const splitLines = splitTokenByChars(
|
|
2725
|
+
const splitLines = splitTokenByChars(
|
|
2726
|
+
token,
|
|
2727
|
+
availableWidth,
|
|
2728
|
+
defaultFontSize,
|
|
2729
|
+
fontScale,
|
|
2730
|
+
textMeasurer,
|
|
2731
|
+
measurementContext
|
|
2732
|
+
);
|
|
2704
2733
|
for (let j = 0; j < splitLines.length; j++) {
|
|
2705
2734
|
if (j < splitLines.length - 1) {
|
|
2706
2735
|
const segments = trimTrailingSpaces(mergeSegments(splitLines[j]));
|
|
@@ -2733,14 +2762,27 @@ function layoutTokensIntoLines(tokens, availableWidth, defaultFontSize, fontScal
|
|
|
2733
2762
|
}
|
|
2734
2763
|
return lines.length > 0 ? lines : [{ segments: [] }];
|
|
2735
2764
|
}
|
|
2736
|
-
function wrapParagraph(paragraph, availableWidth, defaultFontSize = DEFAULT_FONT_SIZE, fontScale = 1) {
|
|
2765
|
+
function wrapParagraph(paragraph, availableWidth, defaultFontSize = DEFAULT_FONT_SIZE, fontScale = 1, textMeasurer = getTextMeasurer(), measurementContext) {
|
|
2737
2766
|
if (paragraph.runs.length === 0 || !paragraph.runs.some((r) => r.text.length > 0)) {
|
|
2738
2767
|
return [{ segments: [] }];
|
|
2739
2768
|
}
|
|
2740
2769
|
const safeWidth = Math.max(availableWidth, 1);
|
|
2741
|
-
const tokens = tokenizeRuns(
|
|
2770
|
+
const tokens = tokenizeRuns(
|
|
2771
|
+
paragraph.runs,
|
|
2772
|
+
defaultFontSize,
|
|
2773
|
+
fontScale,
|
|
2774
|
+
textMeasurer,
|
|
2775
|
+
measurementContext
|
|
2776
|
+
);
|
|
2742
2777
|
if (tokens.length === 0) return [{ segments: [] }];
|
|
2743
|
-
return layoutTokensIntoLines(
|
|
2778
|
+
return layoutTokensIntoLines(
|
|
2779
|
+
tokens,
|
|
2780
|
+
safeWidth,
|
|
2781
|
+
defaultFontSize,
|
|
2782
|
+
fontScale,
|
|
2783
|
+
textMeasurer,
|
|
2784
|
+
measurementContext
|
|
2785
|
+
);
|
|
2744
2786
|
}
|
|
2745
2787
|
|
|
2746
2788
|
// ../renderer/src/utils/unit-types.ts
|
|
@@ -2792,10 +2834,10 @@ function resolveTextDimensions(bodyProperties, originalWidth, originalHeight) {
|
|
|
2792
2834
|
marginBottomPx: emuToPixels(bodyProperties.marginBottom)
|
|
2793
2835
|
};
|
|
2794
2836
|
}
|
|
2795
|
-
function renderTextBody(textBody, transform) {
|
|
2796
|
-
const fontResolver =
|
|
2837
|
+
function renderTextBody(textBody, transform, context = createLegacyRendererContext()) {
|
|
2838
|
+
const fontResolver = context.textPathFontResolver;
|
|
2797
2839
|
if (fontResolver) {
|
|
2798
|
-
return renderTextBodyAsPath(textBody, transform, fontResolver);
|
|
2840
|
+
return renderTextBodyAsPath(textBody, transform, fontResolver, context);
|
|
2799
2841
|
}
|
|
2800
2842
|
const { bodyProperties, paragraphs } = textBody;
|
|
2801
2843
|
const originalWidth = emuToPixels(transform.extentWidth);
|
|
@@ -2818,12 +2860,13 @@ function renderTextBody(textBody, transform) {
|
|
|
2818
2860
|
fontScale,
|
|
2819
2861
|
lnSpcReduction,
|
|
2820
2862
|
textWidth,
|
|
2821
|
-
availableHeight
|
|
2863
|
+
availableHeight,
|
|
2864
|
+
context
|
|
2822
2865
|
);
|
|
2823
2866
|
}
|
|
2824
2867
|
const scaledDefaultFontSizePt = defaultFontSize * fontScale;
|
|
2825
|
-
const defaultLineHeightRatio = getDefaultLineHeightRatio(paragraphs);
|
|
2826
|
-
const defaultAscenderRatio = getDefaultAscenderRatio(paragraphs);
|
|
2868
|
+
const defaultLineHeightRatio = getDefaultLineHeightRatio(paragraphs, context);
|
|
2869
|
+
const defaultAscenderRatio = getDefaultAscenderRatio(paragraphs, context);
|
|
2827
2870
|
const defaultNaturalHeightPt = scaledDefaultFontSizePt * defaultLineHeightRatio;
|
|
2828
2871
|
const tspans = [];
|
|
2829
2872
|
let isFirstLine = true;
|
|
@@ -2863,7 +2906,9 @@ function renderTextBody(textBody, transform) {
|
|
|
2863
2906
|
para,
|
|
2864
2907
|
effectiveTextWidth,
|
|
2865
2908
|
scaledDefaultFontSizePt,
|
|
2866
|
-
fontScale
|
|
2909
|
+
fontScale,
|
|
2910
|
+
context.textMeasurer,
|
|
2911
|
+
context
|
|
2867
2912
|
);
|
|
2868
2913
|
for (let lineIdx = 0; lineIdx < wrappedLines.length; lineIdx++) {
|
|
2869
2914
|
const line = wrappedLines[lineIdx];
|
|
@@ -2883,7 +2928,8 @@ function renderTextBody(textBody, transform) {
|
|
|
2883
2928
|
const lineNaturalHeightPt = computeLineNaturalHeight(
|
|
2884
2929
|
line.segments,
|
|
2885
2930
|
defaultFontSize,
|
|
2886
|
-
fontScale
|
|
2931
|
+
fontScale,
|
|
2932
|
+
context
|
|
2887
2933
|
);
|
|
2888
2934
|
const dy = computeDy(
|
|
2889
2935
|
isFirstLine,
|
|
@@ -2900,16 +2946,17 @@ function renderTextBody(textBody, transform) {
|
|
|
2900
2946
|
para.properties,
|
|
2901
2947
|
lineFontSize,
|
|
2902
2948
|
fontScale,
|
|
2903
|
-
bulletFontChain
|
|
2949
|
+
bulletFontChain,
|
|
2950
|
+
context
|
|
2904
2951
|
);
|
|
2905
|
-
|
|
2952
|
+
context.fontUsageCollector?.record(bulletFontChain, bulletText);
|
|
2906
2953
|
tspans.push(
|
|
2907
2954
|
`<tspan x="${bulletX}" dy="${dy}" text-anchor="start" ${bulletStyles}>${escapeXml2(bulletText)}</tspan>`
|
|
2908
2955
|
);
|
|
2909
2956
|
for (let segIdx = 0; segIdx < line.segments.length; segIdx++) {
|
|
2910
2957
|
const seg = line.segments[segIdx];
|
|
2911
2958
|
const prefix = segIdx === 0 ? `x="${xPos}" text-anchor="${anchorValue}" ` : "";
|
|
2912
|
-
tspans.push(renderSegment(seg.text, seg.properties, fontScale, prefix));
|
|
2959
|
+
tspans.push(renderSegment(seg.text, seg.properties, fontScale, prefix, context));
|
|
2913
2960
|
}
|
|
2914
2961
|
} else {
|
|
2915
2962
|
for (let segIdx = 0; segIdx < line.segments.length; segIdx++) {
|
|
@@ -2918,7 +2965,8 @@ function renderTextBody(textBody, transform) {
|
|
|
2918
2965
|
const lineNaturalHeightPt = computeLineNaturalHeight(
|
|
2919
2966
|
line.segments,
|
|
2920
2967
|
defaultFontSize,
|
|
2921
|
-
fontScale
|
|
2968
|
+
fontScale,
|
|
2969
|
+
context
|
|
2922
2970
|
);
|
|
2923
2971
|
const dy = computeDy(
|
|
2924
2972
|
isFirstLine,
|
|
@@ -2926,9 +2974,9 @@ function renderTextBody(textBody, transform) {
|
|
|
2926
2974
|
lineGapPx
|
|
2927
2975
|
);
|
|
2928
2976
|
const prefix = `x="${xPos}" dy="${dy}" text-anchor="${anchorValue}" `;
|
|
2929
|
-
tspans.push(renderSegment(seg.text, seg.properties, fontScale, prefix));
|
|
2977
|
+
tspans.push(renderSegment(seg.text, seg.properties, fontScale, prefix, context));
|
|
2930
2978
|
} else {
|
|
2931
|
-
tspans.push(renderSegment(seg.text, seg.properties, fontScale, ""));
|
|
2979
|
+
tspans.push(renderSegment(seg.text, seg.properties, fontScale, "", context));
|
|
2932
2980
|
}
|
|
2933
2981
|
}
|
|
2934
2982
|
}
|
|
@@ -2939,7 +2987,12 @@ function renderTextBody(textBody, transform) {
|
|
|
2939
2987
|
if (bulletText) {
|
|
2940
2988
|
const firstRun = para.runs.find((r) => r.text.length > 0);
|
|
2941
2989
|
const fontSize = (firstRun?.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
2942
|
-
const naturalHeightPt = computeLineNaturalHeight(
|
|
2990
|
+
const naturalHeightPt = computeLineNaturalHeight(
|
|
2991
|
+
para.runs,
|
|
2992
|
+
defaultFontSize,
|
|
2993
|
+
fontScale,
|
|
2994
|
+
context
|
|
2995
|
+
);
|
|
2943
2996
|
const dy = computeDy(
|
|
2944
2997
|
isFirstLine,
|
|
2945
2998
|
getLineHeightPx(para, naturalHeightPt, lnSpcReduction),
|
|
@@ -2954,9 +3007,10 @@ function renderTextBody(textBody, transform) {
|
|
|
2954
3007
|
para.properties,
|
|
2955
3008
|
fontSize,
|
|
2956
3009
|
fontScale,
|
|
2957
|
-
bulletFontChain
|
|
3010
|
+
bulletFontChain,
|
|
3011
|
+
context
|
|
2958
3012
|
);
|
|
2959
|
-
|
|
3013
|
+
context.fontUsageCollector?.record(bulletFontChain, bulletText);
|
|
2960
3014
|
tspans.push(
|
|
2961
3015
|
`<tspan x="${bulletX}" dy="${dy}" text-anchor="start" ${bulletStyles}>${escapeXml2(bulletText)}</tspan>`
|
|
2962
3016
|
);
|
|
@@ -2967,20 +3021,25 @@ function renderTextBody(textBody, transform) {
|
|
|
2967
3021
|
if (!firstRunRendered) {
|
|
2968
3022
|
if (bulletText) {
|
|
2969
3023
|
const prefix = `x="${xPos}" text-anchor="${anchorValue}" `;
|
|
2970
|
-
tspans.push(renderSegment(run.text, run.properties, fontScale, prefix));
|
|
3024
|
+
tspans.push(renderSegment(run.text, run.properties, fontScale, prefix, context));
|
|
2971
3025
|
} else {
|
|
2972
|
-
const naturalHeightPt = computeLineNaturalHeight(
|
|
3026
|
+
const naturalHeightPt = computeLineNaturalHeight(
|
|
3027
|
+
para.runs,
|
|
3028
|
+
defaultFontSize,
|
|
3029
|
+
fontScale,
|
|
3030
|
+
context
|
|
3031
|
+
);
|
|
2973
3032
|
const dy = computeDy(
|
|
2974
3033
|
isFirstLine,
|
|
2975
3034
|
getLineHeightPx(para, naturalHeightPt, lnSpcReduction),
|
|
2976
3035
|
paragraphGapPx
|
|
2977
3036
|
);
|
|
2978
3037
|
const prefix = `x="${xPos}" dy="${dy}" text-anchor="${anchorValue}" `;
|
|
2979
|
-
tspans.push(renderSegment(run.text, run.properties, fontScale, prefix));
|
|
3038
|
+
tspans.push(renderSegment(run.text, run.properties, fontScale, prefix, context));
|
|
2980
3039
|
}
|
|
2981
3040
|
firstRunRendered = true;
|
|
2982
3041
|
} else {
|
|
2983
|
-
tspans.push(renderSegment(run.text, run.properties, fontScale, ""));
|
|
3042
|
+
tspans.push(renderSegment(run.text, run.properties, fontScale, "", context));
|
|
2984
3043
|
}
|
|
2985
3044
|
}
|
|
2986
3045
|
isFirstLine = false;
|
|
@@ -2994,7 +3053,8 @@ function renderTextBody(textBody, transform) {
|
|
|
2994
3053
|
shouldWrap,
|
|
2995
3054
|
textWidth,
|
|
2996
3055
|
lnSpcReduction,
|
|
2997
|
-
fontScale
|
|
3056
|
+
fontScale,
|
|
3057
|
+
context
|
|
2998
3058
|
);
|
|
2999
3059
|
if (bodyProperties.anchor === "ctr") {
|
|
3000
3060
|
yStart = Math.max(marginTopPx, (height - totalTextHeight) / 2);
|
|
@@ -3091,13 +3151,13 @@ function toAlpha(num) {
|
|
|
3091
3151
|
function buildBulletFontChain(props, runFontFamily, runFontFamilyEa) {
|
|
3092
3152
|
return [props.bulletFont, runFontFamily ?? null, runFontFamilyEa ?? null];
|
|
3093
3153
|
}
|
|
3094
|
-
function buildBulletStyleAttrs(props, textFontSizePt, fontScale, bulletFontChain) {
|
|
3154
|
+
function buildBulletStyleAttrs(props, textFontSizePt, fontScale, bulletFontChain, context = createLegacyRendererContext()) {
|
|
3095
3155
|
const styles = [];
|
|
3096
3156
|
if (props.bulletSizePct !== null) {
|
|
3097
3157
|
const size = textFontSizePt * (props.bulletSizePct / 1e5);
|
|
3098
3158
|
styles.push(`font-size="${size}pt"`);
|
|
3099
3159
|
}
|
|
3100
|
-
const fontFamilyValue = buildFontFamilyValue(bulletFontChain);
|
|
3160
|
+
const fontFamilyValue = buildFontFamilyValue(bulletFontChain, context);
|
|
3101
3161
|
if (fontFamilyValue) {
|
|
3102
3162
|
styles.push(`font-family="${fontFamilyValue}"`);
|
|
3103
3163
|
}
|
|
@@ -3150,13 +3210,14 @@ function getLineFontSize(segments, defaultFontSize) {
|
|
|
3150
3210
|
}
|
|
3151
3211
|
return defaultFontSize;
|
|
3152
3212
|
}
|
|
3153
|
-
function computeLineNaturalHeight(segments, defaultFontSize, fontScale) {
|
|
3213
|
+
function computeLineNaturalHeight(segments, defaultFontSize, fontScale, context = createLegacyRendererContext()) {
|
|
3154
3214
|
let maxHeight = 0;
|
|
3155
3215
|
for (const seg of segments) {
|
|
3156
3216
|
const fontSize = (seg.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
3157
|
-
const ratio =
|
|
3217
|
+
const ratio = context.textMeasurer.getLineHeightRatio(
|
|
3158
3218
|
seg.properties.fontFamily,
|
|
3159
|
-
seg.properties.fontFamilyEa
|
|
3219
|
+
seg.properties.fontFamilyEa,
|
|
3220
|
+
context
|
|
3160
3221
|
);
|
|
3161
3222
|
maxHeight = Math.max(maxHeight, fontSize * ratio);
|
|
3162
3223
|
}
|
|
@@ -3197,14 +3258,14 @@ function getGenericFamily(fontFamily) {
|
|
|
3197
3258
|
function escapeFontName(name) {
|
|
3198
3259
|
return name.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
3199
3260
|
}
|
|
3200
|
-
function buildFontFamilyValue(fonts) {
|
|
3261
|
+
function buildFontFamilyValue(fonts, context = createLegacyRendererContext()) {
|
|
3201
3262
|
const uniqueFonts = [];
|
|
3202
3263
|
const seen = /* @__PURE__ */ new Set();
|
|
3203
3264
|
for (const font of fonts) {
|
|
3204
3265
|
if (font && !seen.has(font)) {
|
|
3205
3266
|
seen.add(font);
|
|
3206
3267
|
uniqueFonts.push(font);
|
|
3207
|
-
const mapped =
|
|
3268
|
+
const mapped = getMappedFontFromContext(font, context);
|
|
3208
3269
|
if (mapped && !seen.has(mapped)) {
|
|
3209
3270
|
seen.add(mapped);
|
|
3210
3271
|
uniqueFonts.push(mapped);
|
|
@@ -3225,14 +3286,14 @@ function buildFontFamilyValue(fonts) {
|
|
|
3225
3286
|
parts.push(genericFamily);
|
|
3226
3287
|
return parts.join(", ");
|
|
3227
3288
|
}
|
|
3228
|
-
function buildStyleAttrs(props, fontScale = 1, fontFamilies) {
|
|
3289
|
+
function buildStyleAttrs(props, fontScale = 1, fontFamilies, context = createLegacyRendererContext()) {
|
|
3229
3290
|
const styles = [];
|
|
3230
3291
|
if (props.fontSize) {
|
|
3231
3292
|
const scaledSize = props.fontSize * fontScale;
|
|
3232
3293
|
styles.push(`font-size="${scaledSize}pt"`);
|
|
3233
3294
|
}
|
|
3234
3295
|
const fonts = fontFamilies ?? [props.fontFamily, props.fontFamilyEa];
|
|
3235
|
-
const fontFamilyValue = buildFontFamilyValue(fonts);
|
|
3296
|
+
const fontFamilyValue = buildFontFamilyValue(fonts, context);
|
|
3236
3297
|
if (fontFamilyValue) {
|
|
3237
3298
|
styles.push(`font-family="${fontFamilyValue}"`);
|
|
3238
3299
|
}
|
|
@@ -3270,20 +3331,20 @@ function buildStyleAttrs(props, fontScale = 1, fontFamilies) {
|
|
|
3270
3331
|
}
|
|
3271
3332
|
return styles.join(" ");
|
|
3272
3333
|
}
|
|
3273
|
-
function renderSegment(text, props, fontScale, prefix) {
|
|
3334
|
+
function renderSegment(text, props, fontScale, prefix, context) {
|
|
3274
3335
|
let tspanContent;
|
|
3275
3336
|
if (!needsScriptSplit(props)) {
|
|
3276
|
-
const styles = buildStyleAttrs(props, fontScale);
|
|
3277
|
-
|
|
3337
|
+
const styles = buildStyleAttrs(props, fontScale, void 0, context);
|
|
3338
|
+
context.fontUsageCollector?.record([props.fontFamily, props.fontFamilyEa], text);
|
|
3278
3339
|
tspanContent = `<tspan ${prefix}${styles}>${escapeXml2(text)}</tspan>`;
|
|
3279
3340
|
} else {
|
|
3280
3341
|
const parts = splitByScript(text);
|
|
3281
3342
|
const result = [];
|
|
3282
3343
|
for (let i = 0; i < parts.length; i++) {
|
|
3283
3344
|
const part = parts[i];
|
|
3284
|
-
const fonts = part.isEa ? [props.fontFamilyEa,
|
|
3285
|
-
const styles = buildStyleAttrs(props, fontScale, fonts);
|
|
3286
|
-
|
|
3345
|
+
const fonts = part.isEa ? [props.fontFamilyEa, getJpanFallbackFontFromContext(context), props.fontFamily] : [props.fontFamily, props.fontFamilyEa];
|
|
3346
|
+
const styles = buildStyleAttrs(props, fontScale, fonts, context);
|
|
3347
|
+
context.fontUsageCollector?.record(fonts, part.text);
|
|
3287
3348
|
if (i === 0) {
|
|
3288
3349
|
result.push(`<tspan ${prefix}${styles}>${escapeXml2(part.text)}</tspan>`);
|
|
3289
3350
|
} else {
|
|
@@ -3306,33 +3367,35 @@ function getDefaultFontSize(paragraphs) {
|
|
|
3306
3367
|
}
|
|
3307
3368
|
return DEFAULT_FONT_SIZE_PT;
|
|
3308
3369
|
}
|
|
3309
|
-
function getDefaultLineHeightRatio(paragraphs) {
|
|
3370
|
+
function getDefaultLineHeightRatio(paragraphs, context = createLegacyRendererContext()) {
|
|
3310
3371
|
for (const p of paragraphs) {
|
|
3311
3372
|
for (const r of p.runs) {
|
|
3312
3373
|
if (r.properties.fontFamily || r.properties.fontFamilyEa) {
|
|
3313
|
-
return
|
|
3374
|
+
return context.textMeasurer.getLineHeightRatio(
|
|
3314
3375
|
r.properties.fontFamily,
|
|
3315
|
-
r.properties.fontFamilyEa
|
|
3376
|
+
r.properties.fontFamilyEa,
|
|
3377
|
+
context
|
|
3316
3378
|
);
|
|
3317
3379
|
}
|
|
3318
3380
|
}
|
|
3319
3381
|
}
|
|
3320
3382
|
return 1.2;
|
|
3321
3383
|
}
|
|
3322
|
-
function getDefaultAscenderRatio(paragraphs) {
|
|
3384
|
+
function getDefaultAscenderRatio(paragraphs, context = createLegacyRendererContext()) {
|
|
3323
3385
|
for (const p of paragraphs) {
|
|
3324
3386
|
for (const r of p.runs) {
|
|
3325
3387
|
if (r.properties.fontFamily || r.properties.fontFamilyEa) {
|
|
3326
|
-
return
|
|
3388
|
+
return context.textMeasurer.getAscenderRatio(
|
|
3327
3389
|
r.properties.fontFamily,
|
|
3328
|
-
r.properties.fontFamilyEa
|
|
3390
|
+
r.properties.fontFamilyEa,
|
|
3391
|
+
context
|
|
3329
3392
|
);
|
|
3330
3393
|
}
|
|
3331
3394
|
}
|
|
3332
3395
|
}
|
|
3333
3396
|
return 1;
|
|
3334
3397
|
}
|
|
3335
|
-
function computeSpAutofitHeight(textBody, transform) {
|
|
3398
|
+
function computeSpAutofitHeight(textBody, transform, context = createLegacyRendererContext()) {
|
|
3336
3399
|
const { bodyProperties, paragraphs } = textBody;
|
|
3337
3400
|
const hasText = paragraphs.some((p) => p.runs.some((r) => r.text.length > 0));
|
|
3338
3401
|
if (!hasText) return null;
|
|
@@ -3344,13 +3407,21 @@ function computeSpAutofitHeight(textBody, transform) {
|
|
|
3344
3407
|
const textWidth = numCol > 1 ? fullTextWidth / numCol : fullTextWidth;
|
|
3345
3408
|
const defaultFontSize = getDefaultFontSize(paragraphs);
|
|
3346
3409
|
const shouldWrap = bodyProperties.wrap !== "none";
|
|
3347
|
-
const textHeight = estimateTextHeight(
|
|
3410
|
+
const textHeight = estimateTextHeight(
|
|
3411
|
+
paragraphs,
|
|
3412
|
+
defaultFontSize,
|
|
3413
|
+
shouldWrap,
|
|
3414
|
+
textWidth,
|
|
3415
|
+
void 0,
|
|
3416
|
+
void 0,
|
|
3417
|
+
context
|
|
3418
|
+
);
|
|
3348
3419
|
const requiredHeightPx = textHeight + marginTopPx + marginBottomPx;
|
|
3349
3420
|
if (requiredHeightPx <= height) return null;
|
|
3350
3421
|
const DEFAULT_DPI2 = 96;
|
|
3351
3422
|
return asEmu(requiredHeightPx / DEFAULT_DPI2 * EMU_PER_INCH);
|
|
3352
3423
|
}
|
|
3353
|
-
function computeShrinkToFitScale(paragraphs, defaultFontSize, fontScale, lnSpcReduction, textWidth, availableHeight) {
|
|
3424
|
+
function computeShrinkToFitScale(paragraphs, defaultFontSize, fontScale, lnSpcReduction, textWidth, availableHeight, context = createLegacyRendererContext()) {
|
|
3354
3425
|
if (availableHeight <= 0) return fontScale;
|
|
3355
3426
|
const minScale = fontScale * 0.1;
|
|
3356
3427
|
let scale = fontScale;
|
|
@@ -3362,7 +3433,8 @@ function computeShrinkToFitScale(paragraphs, defaultFontSize, fontScale, lnSpcRe
|
|
|
3362
3433
|
true,
|
|
3363
3434
|
textWidth,
|
|
3364
3435
|
lnSpcReduction,
|
|
3365
|
-
scale
|
|
3436
|
+
scale,
|
|
3437
|
+
context
|
|
3366
3438
|
);
|
|
3367
3439
|
if (textHeight <= availableHeight) break;
|
|
3368
3440
|
const newScale = scale * (availableHeight / textHeight);
|
|
@@ -3371,15 +3443,15 @@ function computeShrinkToFitScale(paragraphs, defaultFontSize, fontScale, lnSpcRe
|
|
|
3371
3443
|
}
|
|
3372
3444
|
return scale;
|
|
3373
3445
|
}
|
|
3374
|
-
function estimateTextHeight(paragraphs, defaultFontSize, shouldWrap, textWidth, lnSpcReduction = 0, fontScale = 1) {
|
|
3446
|
+
function estimateTextHeight(paragraphs, defaultFontSize, shouldWrap, textWidth, lnSpcReduction = 0, fontScale = 1, context = createLegacyRendererContext()) {
|
|
3375
3447
|
let totalHeight = 0;
|
|
3376
|
-
const defaultRatio = getDefaultLineHeightRatio(paragraphs);
|
|
3448
|
+
const defaultRatio = getDefaultLineHeightRatio(paragraphs, context);
|
|
3377
3449
|
let prevSpaceAfterPx = 0;
|
|
3378
3450
|
const scaledDefaultForWrap = defaultFontSize * fontScale;
|
|
3379
3451
|
for (let pIdx = 0; pIdx < paragraphs.length; pIdx++) {
|
|
3380
3452
|
const para = paragraphs[pIdx];
|
|
3381
3453
|
const isEmpty = !para.runs.some((r) => r.text.length > 0);
|
|
3382
|
-
const naturalHeightPt = isEmpty && para.endParaRunProperties?.fontSize ? para.endParaRunProperties.fontSize * fontScale * defaultRatio : computeLineNaturalHeight(para.runs, defaultFontSize, fontScale);
|
|
3454
|
+
const naturalHeightPt = isEmpty && para.endParaRunProperties?.fontSize ? para.endParaRunProperties.fontSize * fontScale * defaultRatio : computeLineNaturalHeight(para.runs, defaultFontSize, fontScale, context);
|
|
3383
3455
|
const lineHeight = getLineHeightPx(
|
|
3384
3456
|
para,
|
|
3385
3457
|
naturalHeightPt > 0 ? naturalHeightPt : defaultFontSize * fontScale * defaultRatio,
|
|
@@ -3387,7 +3459,14 @@ function estimateTextHeight(paragraphs, defaultFontSize, shouldWrap, textWidth,
|
|
|
3387
3459
|
);
|
|
3388
3460
|
let lineCount;
|
|
3389
3461
|
if (shouldWrap && para.runs.length > 0 && para.runs.some((r) => r.text.length > 0)) {
|
|
3390
|
-
const wrappedLines = wrapParagraph(
|
|
3462
|
+
const wrappedLines = wrapParagraph(
|
|
3463
|
+
para,
|
|
3464
|
+
textWidth,
|
|
3465
|
+
scaledDefaultForWrap,
|
|
3466
|
+
fontScale,
|
|
3467
|
+
context.textMeasurer,
|
|
3468
|
+
context
|
|
3469
|
+
);
|
|
3391
3470
|
lineCount = wrappedLines.length;
|
|
3392
3471
|
} else {
|
|
3393
3472
|
lineCount = para.runs.some((r) => r.text.length > 0) ? 1 : 1;
|
|
@@ -3408,9 +3487,9 @@ function computePathLineX(alignment, textStartX, effectiveTextWidth, width, marg
|
|
|
3408
3487
|
if (alignment === "r") return width - marginRightPx - lineWidth;
|
|
3409
3488
|
return textStartX;
|
|
3410
3489
|
}
|
|
3411
|
-
function measureLineWidth(segments, defaultFontSize, fontScale, fontResolver) {
|
|
3490
|
+
function measureLineWidth(segments, defaultFontSize, fontScale, fontResolver, context = createLegacyRendererContext()) {
|
|
3412
3491
|
let totalWidth = 0;
|
|
3413
|
-
const jpanFallback = fontResolver ?
|
|
3492
|
+
const jpanFallback = fontResolver ? getJpanFallbackFontFromContext(context) : null;
|
|
3414
3493
|
for (const seg of segments) {
|
|
3415
3494
|
const fontSize = (seg.properties.fontSize ?? defaultFontSize) * fontScale;
|
|
3416
3495
|
if (fontResolver) {
|
|
@@ -3418,19 +3497,21 @@ function measureLineWidth(segments, defaultFontSize, fontScale, fontResolver) {
|
|
|
3418
3497
|
const font = fontResolver.resolveFont(
|
|
3419
3498
|
seg.properties.fontFamily,
|
|
3420
3499
|
seg.properties.fontFamilyEa,
|
|
3421
|
-
jpanFallback
|
|
3500
|
+
jpanFallback,
|
|
3501
|
+
context
|
|
3422
3502
|
);
|
|
3423
3503
|
if (font) {
|
|
3424
3504
|
totalWidth += font.getAdvanceWidth(seg.text, fontSizePx);
|
|
3425
3505
|
continue;
|
|
3426
3506
|
}
|
|
3427
3507
|
}
|
|
3428
|
-
totalWidth +=
|
|
3508
|
+
totalWidth += context.textMeasurer.measureTextWidth(
|
|
3429
3509
|
seg.text,
|
|
3430
3510
|
fontSize,
|
|
3431
3511
|
seg.properties.bold,
|
|
3432
3512
|
seg.properties.fontFamily,
|
|
3433
|
-
seg.properties.fontFamilyEa
|
|
3513
|
+
seg.properties.fontFamilyEa,
|
|
3514
|
+
context
|
|
3434
3515
|
);
|
|
3435
3516
|
}
|
|
3436
3517
|
return totalWidth;
|
|
@@ -3466,7 +3547,7 @@ function renderTextDecorations(x, y, segmentWidth, fontSizePx, props) {
|
|
|
3466
3547
|
}
|
|
3467
3548
|
return lines;
|
|
3468
3549
|
}
|
|
3469
|
-
function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, fontResolver, vert) {
|
|
3550
|
+
function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, fontResolver, context, vert) {
|
|
3470
3551
|
const fontSize = (props.fontSize ?? defaultFontSize) * fontScale;
|
|
3471
3552
|
const fontSizePx = fontSize * PX_PER_PT;
|
|
3472
3553
|
const parts = [];
|
|
@@ -3476,16 +3557,17 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
3476
3557
|
if (props.baseline > 0) yOffset = -fontSizePx * 0.35;
|
|
3477
3558
|
else if (props.baseline < 0) yOffset = fontSizePx * 0.2;
|
|
3478
3559
|
const effectiveY = y + yOffset;
|
|
3479
|
-
const jpanFallback =
|
|
3560
|
+
const jpanFallback = getJpanFallbackFontFromContext(context);
|
|
3480
3561
|
const processSegment = (segText, fontFamily, fontFamilyEa) => {
|
|
3481
3562
|
if (segText.length === 0) return;
|
|
3482
|
-
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback);
|
|
3483
|
-
const segWidth = font ? font.getAdvanceWidth(segText, fontSizePx) :
|
|
3563
|
+
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback, context);
|
|
3564
|
+
const segWidth = font ? font.getAdvanceWidth(segText, fontSizePx) : context.textMeasurer.measureTextWidth(
|
|
3484
3565
|
segText,
|
|
3485
3566
|
fontSize,
|
|
3486
3567
|
props.bold,
|
|
3487
3568
|
props.fontFamily,
|
|
3488
|
-
props.fontFamilyEa
|
|
3569
|
+
props.fontFamilyEa,
|
|
3570
|
+
context
|
|
3489
3571
|
);
|
|
3490
3572
|
if (font) {
|
|
3491
3573
|
const path = font.getPath(segText, x + totalWidth, effectiveY, fontSizePx);
|
|
@@ -3502,15 +3584,16 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
3502
3584
|
};
|
|
3503
3585
|
const processCjkUpright = (segText, fontFamily, fontFamilyEa) => {
|
|
3504
3586
|
if (segText.length === 0) return;
|
|
3505
|
-
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback);
|
|
3587
|
+
const font = fontResolver.resolveFont(fontFamily, fontFamilyEa, jpanFallback, context);
|
|
3506
3588
|
const fillAttrs = buildPathFillAttrs(props);
|
|
3507
3589
|
for (const char of segText) {
|
|
3508
|
-
const charWidth = font ? font.getAdvanceWidth(char, fontSizePx) :
|
|
3590
|
+
const charWidth = font ? font.getAdvanceWidth(char, fontSizePx) : context.textMeasurer.measureTextWidth(
|
|
3509
3591
|
char,
|
|
3510
3592
|
fontSize,
|
|
3511
3593
|
props.bold,
|
|
3512
3594
|
props.fontFamily,
|
|
3513
|
-
props.fontFamilyEa
|
|
3595
|
+
props.fontFamilyEa,
|
|
3596
|
+
context
|
|
3514
3597
|
);
|
|
3515
3598
|
if (font) {
|
|
3516
3599
|
const charX = x + totalWidth;
|
|
@@ -3560,13 +3643,13 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
|
|
|
3560
3643
|
}
|
|
3561
3644
|
return { svg, width: totalWidth };
|
|
3562
3645
|
}
|
|
3563
|
-
function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontScale, fontResolver, runFontFamily, runFontFamilyEa) {
|
|
3646
|
+
function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontScale, fontResolver, context, runFontFamily, runFontFamilyEa) {
|
|
3564
3647
|
let bulletFontSize = textFontSizePt;
|
|
3565
3648
|
if (paraProps.bulletSizePct !== null) {
|
|
3566
3649
|
bulletFontSize = textFontSizePt * (paraProps.bulletSizePct / 1e5);
|
|
3567
3650
|
}
|
|
3568
3651
|
const fontSizePx = bulletFontSize * PX_PER_PT;
|
|
3569
|
-
const font = paraProps.bulletFont ? fontResolver.resolveFont(paraProps.bulletFont, null) : fontResolver.resolveFont(runFontFamily ?? null, runFontFamilyEa ?? null);
|
|
3652
|
+
const font = paraProps.bulletFont ? fontResolver.resolveFont(paraProps.bulletFont, null, void 0, context) : fontResolver.resolveFont(runFontFamily ?? null, runFontFamilyEa ?? null, void 0, context);
|
|
3570
3653
|
if (!font) return [];
|
|
3571
3654
|
const path = font.getPath(bulletText, x, y, fontSizePx);
|
|
3572
3655
|
const pathData = path.toPathData(2);
|
|
@@ -3582,7 +3665,7 @@ function renderBulletAsPath(bulletText, x, y, paraProps, textFontSizePt, fontSca
|
|
|
3582
3665
|
}
|
|
3583
3666
|
return [`<path d="${pathData}" ${attrs.join(" ")}/>`];
|
|
3584
3667
|
}
|
|
3585
|
-
function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
3668
|
+
function renderTextBodyAsPath(textBody, transform, fontResolver, context) {
|
|
3586
3669
|
const { bodyProperties, paragraphs } = textBody;
|
|
3587
3670
|
const originalWidth = emuToPixels(transform.extentWidth);
|
|
3588
3671
|
const originalHeight = emuToPixels(transform.extentHeight);
|
|
@@ -3604,12 +3687,13 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3604
3687
|
fontScale,
|
|
3605
3688
|
lnSpcReduction,
|
|
3606
3689
|
textWidth,
|
|
3607
|
-
availableHeight
|
|
3690
|
+
availableHeight,
|
|
3691
|
+
context
|
|
3608
3692
|
);
|
|
3609
3693
|
}
|
|
3610
3694
|
const scaledDefaultFontSizePt = defaultFontSize * fontScale;
|
|
3611
|
-
const defaultLineHeightRatio = getDefaultLineHeightRatio(paragraphs);
|
|
3612
|
-
const defaultAscenderRatio = getDefaultAscenderRatio(paragraphs);
|
|
3695
|
+
const defaultLineHeightRatio = getDefaultLineHeightRatio(paragraphs, context);
|
|
3696
|
+
const defaultAscenderRatio = getDefaultAscenderRatio(paragraphs, context);
|
|
3613
3697
|
const defaultNaturalHeightPt = scaledDefaultFontSizePt * defaultLineHeightRatio;
|
|
3614
3698
|
let yStart = marginTopPx;
|
|
3615
3699
|
const totalTextHeight = estimateTextHeight(
|
|
@@ -3618,7 +3702,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3618
3702
|
shouldWrap,
|
|
3619
3703
|
textWidth,
|
|
3620
3704
|
lnSpcReduction,
|
|
3621
|
-
fontScale
|
|
3705
|
+
fontScale,
|
|
3706
|
+
context
|
|
3622
3707
|
);
|
|
3623
3708
|
if (bodyProperties.anchor === "ctr") {
|
|
3624
3709
|
yStart = Math.max(marginTopPx, (height - totalTextHeight) / 2);
|
|
@@ -3657,7 +3742,9 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3657
3742
|
para,
|
|
3658
3743
|
effectiveTextWidth,
|
|
3659
3744
|
scaledDefaultFontSizePt,
|
|
3660
|
-
fontScale
|
|
3745
|
+
fontScale,
|
|
3746
|
+
context.textMeasurer,
|
|
3747
|
+
context
|
|
3661
3748
|
);
|
|
3662
3749
|
for (let lineIdx = 0; lineIdx < wrappedLines.length; lineIdx++) {
|
|
3663
3750
|
const line = wrappedLines[lineIdx];
|
|
@@ -3672,12 +3759,19 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3672
3759
|
const lineNaturalHeightPt = computeLineNaturalHeight(
|
|
3673
3760
|
line.segments,
|
|
3674
3761
|
defaultFontSize,
|
|
3675
|
-
fontScale
|
|
3762
|
+
fontScale,
|
|
3763
|
+
context
|
|
3676
3764
|
);
|
|
3677
3765
|
if (!isFirstLine) {
|
|
3678
3766
|
currentY += getLineHeightPx(para, lineNaturalHeightPt, lnSpcReduction) + lineGapPx;
|
|
3679
3767
|
}
|
|
3680
|
-
const lineWidth = measureLineWidth(
|
|
3768
|
+
const lineWidth = measureLineWidth(
|
|
3769
|
+
line.segments,
|
|
3770
|
+
defaultFontSize,
|
|
3771
|
+
fontScale,
|
|
3772
|
+
fontResolver,
|
|
3773
|
+
context
|
|
3774
|
+
);
|
|
3681
3775
|
const lineStartX = computePathLineX(
|
|
3682
3776
|
para.properties.alignment,
|
|
3683
3777
|
textStartX,
|
|
@@ -3699,6 +3793,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3699
3793
|
lineFontSize,
|
|
3700
3794
|
fontScale,
|
|
3701
3795
|
fontResolver,
|
|
3796
|
+
context,
|
|
3702
3797
|
firstSeg?.properties.fontFamily,
|
|
3703
3798
|
firstSeg?.properties.fontFamilyEa
|
|
3704
3799
|
)
|
|
@@ -3713,6 +3808,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3713
3808
|
fontScale,
|
|
3714
3809
|
defaultFontSize,
|
|
3715
3810
|
fontResolver,
|
|
3811
|
+
context,
|
|
3716
3812
|
bodyProperties.vert
|
|
3717
3813
|
);
|
|
3718
3814
|
if (result.svg) elements.push(result.svg);
|
|
@@ -3721,12 +3817,23 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3721
3817
|
isFirstLine = false;
|
|
3722
3818
|
}
|
|
3723
3819
|
} else {
|
|
3724
|
-
const naturalHeightPt = computeLineNaturalHeight(
|
|
3820
|
+
const naturalHeightPt = computeLineNaturalHeight(
|
|
3821
|
+
para.runs,
|
|
3822
|
+
defaultFontSize,
|
|
3823
|
+
fontScale,
|
|
3824
|
+
context
|
|
3825
|
+
);
|
|
3725
3826
|
if (!isFirstLine) {
|
|
3726
3827
|
currentY += getLineHeightPx(para, naturalHeightPt, lnSpcReduction) + paragraphGapPx;
|
|
3727
3828
|
}
|
|
3728
3829
|
const runsAsSegments = para.runs.filter((r) => r.text.length > 0).map((r) => ({ text: r.text, properties: r.properties }));
|
|
3729
|
-
const lineWidth = measureLineWidth(
|
|
3830
|
+
const lineWidth = measureLineWidth(
|
|
3831
|
+
runsAsSegments,
|
|
3832
|
+
defaultFontSize,
|
|
3833
|
+
fontScale,
|
|
3834
|
+
fontResolver,
|
|
3835
|
+
context
|
|
3836
|
+
);
|
|
3730
3837
|
const lineStartX = computePathLineX(
|
|
3731
3838
|
para.properties.alignment,
|
|
3732
3839
|
textStartX,
|
|
@@ -3748,6 +3855,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3748
3855
|
fontSize,
|
|
3749
3856
|
fontScale,
|
|
3750
3857
|
fontResolver,
|
|
3858
|
+
context,
|
|
3751
3859
|
firstRun?.properties.fontFamily,
|
|
3752
3860
|
firstRun?.properties.fontFamilyEa
|
|
3753
3861
|
)
|
|
@@ -3763,6 +3871,7 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
|
|
|
3763
3871
|
fontScale,
|
|
3764
3872
|
defaultFontSize,
|
|
3765
3873
|
fontResolver,
|
|
3874
|
+
context,
|
|
3766
3875
|
bodyProperties.vert
|
|
3767
3876
|
);
|
|
3768
3877
|
if (result.svg) elements.push(result.svg);
|
|
@@ -3788,11 +3897,11 @@ function escapeXml2(str) {
|
|
|
3788
3897
|
}
|
|
3789
3898
|
|
|
3790
3899
|
// ../renderer/src/renderer/shape-renderer.ts
|
|
3791
|
-
function renderShape(shape) {
|
|
3900
|
+
function renderShape(shape, context = createLegacyRendererContext()) {
|
|
3792
3901
|
const { transform, geometry, fill, outline, textBody, effects } = shape;
|
|
3793
3902
|
let effectiveTransform = transform;
|
|
3794
3903
|
if (textBody?.bodyProperties.autoFit === "spAutofit") {
|
|
3795
|
-
const requiredHeightEmu = computeSpAutofitHeight(textBody, transform);
|
|
3904
|
+
const requiredHeightEmu = computeSpAutofitHeight(textBody, transform, context);
|
|
3796
3905
|
if (requiredHeightEmu !== null) {
|
|
3797
3906
|
effectiveTransform = { ...transform, extentHeight: requiredHeightEmu };
|
|
3798
3907
|
}
|
|
@@ -3819,7 +3928,7 @@ function renderShape(shape) {
|
|
|
3819
3928
|
parts.push(styledGeometry);
|
|
3820
3929
|
}
|
|
3821
3930
|
if (textBody) {
|
|
3822
|
-
const textSvg = renderTextBody(textBody, effectiveTransform);
|
|
3931
|
+
const textSvg = renderTextBody(textBody, effectiveTransform, context);
|
|
3823
3932
|
if (textSvg) {
|
|
3824
3933
|
parts.push(textSvg);
|
|
3825
3934
|
}
|
|
@@ -3859,7 +3968,7 @@ function renderConnector(connector) {
|
|
|
3859
3968
|
}
|
|
3860
3969
|
|
|
3861
3970
|
// ../renderer/src/renderer/table-renderer.ts
|
|
3862
|
-
function renderTable(element) {
|
|
3971
|
+
function renderTable(element, context = createLegacyRendererContext()) {
|
|
3863
3972
|
const { transform, table } = element;
|
|
3864
3973
|
const transformAttr = buildTransformAttr(transform);
|
|
3865
3974
|
const colWidths = table.columns.map((col) => emuToPixels(col.width));
|
|
@@ -3922,7 +4031,7 @@ function renderTable(element) {
|
|
|
3922
4031
|
flipH: false,
|
|
3923
4032
|
flipV: false
|
|
3924
4033
|
};
|
|
3925
|
-
const textSvg = renderTextBody(cell.textBody, cellTransform);
|
|
4034
|
+
const textSvg = renderTextBody(cell.textBody, cellTransform, context);
|
|
3926
4035
|
if (textSvg) {
|
|
3927
4036
|
parts.push(`<g transform="translate(${x}, ${y})">${textSvg}</g>`);
|
|
3928
4037
|
}
|
|
@@ -3947,7 +4056,7 @@ function pixelsToEmu(px) {
|
|
|
3947
4056
|
}
|
|
3948
4057
|
|
|
3949
4058
|
// ../renderer/src/renderer/svg-renderer.ts
|
|
3950
|
-
function renderSlideToSvg(slide, slideSize) {
|
|
4059
|
+
function renderSlideToSvg(slide, slideSize, context = createLegacyRendererContext()) {
|
|
3951
4060
|
const width = emuToPixels(slideSize.width);
|
|
3952
4061
|
const height = emuToPixels(slideSize.height);
|
|
3953
4062
|
const parts = [];
|
|
@@ -3968,7 +4077,7 @@ function renderSlideToSvg(slide, slideSize) {
|
|
|
3968
4077
|
parts.push(`<rect width="${width}" height="${height}" fill="#FFFFFF"/>`);
|
|
3969
4078
|
}
|
|
3970
4079
|
for (const element of slide.elements) {
|
|
3971
|
-
const result = renderElement(element);
|
|
4080
|
+
const result = renderElement(element, context);
|
|
3972
4081
|
if (result) {
|
|
3973
4082
|
parts.push(result.content);
|
|
3974
4083
|
defs.push(...result.defs);
|
|
@@ -3980,11 +4089,11 @@ function renderSlideToSvg(slide, slideSize) {
|
|
|
3980
4089
|
parts.push("</svg>");
|
|
3981
4090
|
return parts.join("");
|
|
3982
4091
|
}
|
|
3983
|
-
function renderElement(element) {
|
|
4092
|
+
function renderElement(element, context) {
|
|
3984
4093
|
let result = null;
|
|
3985
4094
|
switch (element.type) {
|
|
3986
4095
|
case "shape":
|
|
3987
|
-
result = renderShape(element);
|
|
4096
|
+
result = renderShape(element, context);
|
|
3988
4097
|
break;
|
|
3989
4098
|
case "image":
|
|
3990
4099
|
result = renderImage(element);
|
|
@@ -3993,13 +4102,13 @@ function renderElement(element) {
|
|
|
3993
4102
|
result = renderConnector(element);
|
|
3994
4103
|
break;
|
|
3995
4104
|
case "group":
|
|
3996
|
-
result = renderGroup(element);
|
|
4105
|
+
result = renderGroup(element, context);
|
|
3997
4106
|
break;
|
|
3998
4107
|
case "chart":
|
|
3999
|
-
result = renderChart(element);
|
|
4108
|
+
result = renderChart(element, context);
|
|
4000
4109
|
break;
|
|
4001
4110
|
case "table":
|
|
4002
|
-
result = renderTable(element);
|
|
4111
|
+
result = renderTable(element, context);
|
|
4003
4112
|
break;
|
|
4004
4113
|
}
|
|
4005
4114
|
if (result && "altText" in element && element.altText) {
|
|
@@ -4015,7 +4124,7 @@ function addAriaLabel(svgFragment, altText) {
|
|
|
4015
4124
|
const escaped = altText.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">");
|
|
4016
4125
|
return svgFragment.replace(/^<(g|image|path)\b/, `<$1 role="img" aria-label="${escaped}"`);
|
|
4017
4126
|
}
|
|
4018
|
-
function renderGroup(group) {
|
|
4127
|
+
function renderGroup(group, context) {
|
|
4019
4128
|
const x = emuToPixels(group.transform.offsetX);
|
|
4020
4129
|
const y = emuToPixels(group.transform.offsetY);
|
|
4021
4130
|
const w = emuToPixels(group.transform.extentWidth);
|
|
@@ -4045,7 +4154,7 @@ function renderGroup(group) {
|
|
|
4045
4154
|
const defs = [];
|
|
4046
4155
|
parts.push(`<g transform="${transformParts.join(" ")}">`);
|
|
4047
4156
|
for (const child of group.children) {
|
|
4048
|
-
const childResult = renderElement(child);
|
|
4157
|
+
const childResult = renderElement(child, context);
|
|
4049
4158
|
if (childResult) {
|
|
4050
4159
|
parts.push(childResult.content);
|
|
4051
4160
|
defs.push(...childResult.defs);
|
|
@@ -4081,6 +4190,84 @@ function asSourceNodeId(value) {
|
|
|
4081
4190
|
function asRawSidecarId(value) {
|
|
4082
4191
|
return unsafeBrandAssertion2(value);
|
|
4083
4192
|
}
|
|
4193
|
+
var RELS_SUFFIX = ".rels";
|
|
4194
|
+
var RELS_MARKER = "_rels/";
|
|
4195
|
+
var ABSOLUTE_URI_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
|
|
4196
|
+
function isRelationshipPart(path) {
|
|
4197
|
+
return path.endsWith(RELS_SUFFIX) && path.includes(RELS_MARKER);
|
|
4198
|
+
}
|
|
4199
|
+
function relationshipsPartPath(sourcePartPath) {
|
|
4200
|
+
if (sourcePartPath === "") return "_rels/.rels";
|
|
4201
|
+
const slash = sourcePartPath.lastIndexOf("/");
|
|
4202
|
+
const dir = slash === -1 ? "" : sourcePartPath.slice(0, slash + 1);
|
|
4203
|
+
const file = slash === -1 ? sourcePartPath : sourcePartPath.slice(slash + 1);
|
|
4204
|
+
return `${dir}_rels/${file}.rels`;
|
|
4205
|
+
}
|
|
4206
|
+
function relationshipsSourcePartPath(relsPath) {
|
|
4207
|
+
const idx = relsPath.lastIndexOf(RELS_MARKER);
|
|
4208
|
+
const dir = relsPath.slice(0, idx);
|
|
4209
|
+
const file = relsPath.slice(idx + RELS_MARKER.length);
|
|
4210
|
+
const base = file.endsWith(RELS_SUFFIX) ? file.slice(0, -RELS_SUFFIX.length) : file;
|
|
4211
|
+
return asPartPath(dir + base);
|
|
4212
|
+
}
|
|
4213
|
+
function resolveRelationshipTarget(sourcePartPath, target) {
|
|
4214
|
+
if (ABSOLUTE_URI_PATTERN.test(target)) return target;
|
|
4215
|
+
const combined = target.startsWith("/") ? target.slice(1) : joinPackageRelativeTarget(sourcePartPath, target);
|
|
4216
|
+
return normalizePackagePath(combined);
|
|
4217
|
+
}
|
|
4218
|
+
function resolveInternalRelationshipTarget(sourcePartPath, relationship) {
|
|
4219
|
+
if (relationship.targetMode === "External") return void 0;
|
|
4220
|
+
return asPartPath(resolveRelationshipTarget(sourcePartPath, relationship.target));
|
|
4221
|
+
}
|
|
4222
|
+
function parseRelationshipTargetMode(value) {
|
|
4223
|
+
if (value === "Internal" || value === "External") return value;
|
|
4224
|
+
return void 0;
|
|
4225
|
+
}
|
|
4226
|
+
function joinPackageRelativeTarget(sourcePartPath, target) {
|
|
4227
|
+
const slash = sourcePartPath.lastIndexOf("/");
|
|
4228
|
+
const baseDir = slash === -1 ? "" : sourcePartPath.slice(0, slash);
|
|
4229
|
+
return baseDir === "" ? target : `${baseDir}/${target}`;
|
|
4230
|
+
}
|
|
4231
|
+
function normalizePackagePath(path) {
|
|
4232
|
+
const segments = [];
|
|
4233
|
+
for (const segment of path.split("/")) {
|
|
4234
|
+
if (segment === "" || segment === ".") continue;
|
|
4235
|
+
if (segment === "..") {
|
|
4236
|
+
segments.pop();
|
|
4237
|
+
continue;
|
|
4238
|
+
}
|
|
4239
|
+
segments.push(segment);
|
|
4240
|
+
}
|
|
4241
|
+
return segments.join("/");
|
|
4242
|
+
}
|
|
4243
|
+
var RELS_CONTENT_TYPE = "application/vnd.openxmlformats-package.relationships+xml";
|
|
4244
|
+
var SLIDE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml";
|
|
4245
|
+
var NOTES_SLIDE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml";
|
|
4246
|
+
var SLIDE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
|
|
4247
|
+
var SLIDE_LAYOUT_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
4248
|
+
var NOTES_SLIDE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
|
|
4249
|
+
var IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
4250
|
+
var EMPTY_SLIDE_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
4251
|
+
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr></p:spTree></p:cSld><p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr></p:sld>`;
|
|
4252
|
+
var EDITABLE_TEXT_RUN_PROPERTIES = [
|
|
4253
|
+
"bold",
|
|
4254
|
+
"italic",
|
|
4255
|
+
"underline",
|
|
4256
|
+
"fontSize",
|
|
4257
|
+
"color",
|
|
4258
|
+
"typeface"
|
|
4259
|
+
];
|
|
4260
|
+
var EDITABLE_TEXT_RUN_PROPERTY_SET = new Set(EDITABLE_TEXT_RUN_PROPERTIES);
|
|
4261
|
+
var CONNECTOR_PRESETS = /* @__PURE__ */ new Set([
|
|
4262
|
+
"straightConnector1",
|
|
4263
|
+
"bentConnector3",
|
|
4264
|
+
"curvedConnector3"
|
|
4265
|
+
]);
|
|
4266
|
+
var ARROW_TYPES = /* @__PURE__ */ new Set(["triangle", "stealth", "diamond", "oval", "arrow"]);
|
|
4267
|
+
var ARROW_SIZES = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
|
|
4268
|
+
var DEFAULT_CONNECTOR_OUTLINE = {
|
|
4269
|
+
fill: { kind: "solid", color: { kind: "srgb", hex: "000000" } }
|
|
4270
|
+
};
|
|
4084
4271
|
function replaceTextRunPlainText(source, handle, text) {
|
|
4085
4272
|
let replaced = false;
|
|
4086
4273
|
const slides = source.slides.map((slide) => ({
|
|
@@ -4120,6 +4307,18 @@ function replaceTextRunPlainText(source, handle, text) {
|
|
|
4120
4307
|
edits: [...source.edits ?? [], { kind: "replaceTextRunPlainText", handle, text }]
|
|
4121
4308
|
};
|
|
4122
4309
|
}
|
|
4310
|
+
function setTextRunProperties(source, handle, properties) {
|
|
4311
|
+
return updateTextRunProperties(source, handle, {
|
|
4312
|
+
set: properties,
|
|
4313
|
+
clear: []
|
|
4314
|
+
});
|
|
4315
|
+
}
|
|
4316
|
+
function clearTextRunProperties(source, handle, properties) {
|
|
4317
|
+
return updateTextRunProperties(source, handle, {
|
|
4318
|
+
set: {},
|
|
4319
|
+
clear: properties
|
|
4320
|
+
});
|
|
4321
|
+
}
|
|
4123
4322
|
function replaceParagraphPlainText(source, handle, text) {
|
|
4124
4323
|
let replaced = false;
|
|
4125
4324
|
const slides = source.slides.map((slide) => ({
|
|
@@ -4165,6 +4364,83 @@ function replaceParagraphPlainText(source, handle, text) {
|
|
|
4165
4364
|
edits: [...source.edits ?? [], { kind: "replaceParagraphPlainText", handle, text }]
|
|
4166
4365
|
};
|
|
4167
4366
|
}
|
|
4367
|
+
function updateTextRunProperties(source, handle, patch) {
|
|
4368
|
+
assertEditableTextRunProperties(patch.set);
|
|
4369
|
+
assertEditableTextRunPropertyNames(patch.clear);
|
|
4370
|
+
const set = definedEditableTextRunProperties(patch.set);
|
|
4371
|
+
if (Object.values(set).every((value) => value === void 0) && patch.clear.length === 0) {
|
|
4372
|
+
throw new Error("updateTextRunProperties: patch must set or clear at least one property");
|
|
4373
|
+
}
|
|
4374
|
+
let found = false;
|
|
4375
|
+
let changed = false;
|
|
4376
|
+
const slides = source.slides.map((slide) => ({
|
|
4377
|
+
...slide,
|
|
4378
|
+
shapes: slide.shapes.map((shape) => {
|
|
4379
|
+
if (shape.kind !== "shape" || shape.textBody === void 0) return shape;
|
|
4380
|
+
let shapeChanged = false;
|
|
4381
|
+
const paragraphs = shape.textBody.paragraphs.map((paragraph) => {
|
|
4382
|
+
let paragraphChanged = false;
|
|
4383
|
+
const runs = paragraph.runs.map((run) => {
|
|
4384
|
+
if (!sourceHandlesEqual(run.handle, handle)) return run;
|
|
4385
|
+
found = true;
|
|
4386
|
+
const properties = patchTextRunProperties(run.properties, { set, clear: patch.clear });
|
|
4387
|
+
if (textRunPropertiesEqual(run.properties, properties)) return run;
|
|
4388
|
+
changed = true;
|
|
4389
|
+
paragraphChanged = true;
|
|
4390
|
+
shapeChanged = true;
|
|
4391
|
+
return {
|
|
4392
|
+
kind: run.kind,
|
|
4393
|
+
text: run.text,
|
|
4394
|
+
...run.handle !== void 0 ? { handle: run.handle } : {},
|
|
4395
|
+
...run.rawSidecars !== void 0 ? { rawSidecars: run.rawSidecars } : {},
|
|
4396
|
+
...properties !== void 0 ? { properties } : {}
|
|
4397
|
+
};
|
|
4398
|
+
});
|
|
4399
|
+
return !paragraphChanged ? paragraph : { ...paragraph, runs };
|
|
4400
|
+
});
|
|
4401
|
+
if (!shapeChanged) return shape;
|
|
4402
|
+
return {
|
|
4403
|
+
...shape,
|
|
4404
|
+
textBody: {
|
|
4405
|
+
...shape.textBody,
|
|
4406
|
+
paragraphs
|
|
4407
|
+
}
|
|
4408
|
+
};
|
|
4409
|
+
})
|
|
4410
|
+
}));
|
|
4411
|
+
if (!found) {
|
|
4412
|
+
throw new Error(
|
|
4413
|
+
"updateTextRunProperties: text run handle was not found in PptxSourceModel source"
|
|
4414
|
+
);
|
|
4415
|
+
}
|
|
4416
|
+
if (!changed) return source;
|
|
4417
|
+
return {
|
|
4418
|
+
...source,
|
|
4419
|
+
slides,
|
|
4420
|
+
edits: [
|
|
4421
|
+
...source.edits ?? [],
|
|
4422
|
+
{
|
|
4423
|
+
kind: "updateTextRunProperties",
|
|
4424
|
+
handle,
|
|
4425
|
+
...Object.keys(set).length > 0 ? { set } : {},
|
|
4426
|
+
...patch.clear.length > 0 ? { clear: patch.clear } : {}
|
|
4427
|
+
}
|
|
4428
|
+
]
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4431
|
+
function patchTextRunProperties(current, patch) {
|
|
4432
|
+
const next = { ...current ?? {} };
|
|
4433
|
+
for (const property of patch.clear) {
|
|
4434
|
+
delete next[property];
|
|
4435
|
+
}
|
|
4436
|
+
if (patch.set.bold !== void 0) next.bold = patch.set.bold;
|
|
4437
|
+
if (patch.set.italic !== void 0) next.italic = patch.set.italic;
|
|
4438
|
+
if (patch.set.underline !== void 0) next.underline = patch.set.underline;
|
|
4439
|
+
if (patch.set.fontSize !== void 0) next.fontSize = patch.set.fontSize;
|
|
4440
|
+
if (patch.set.color !== void 0) next.color = patch.set.color;
|
|
4441
|
+
if (patch.set.typeface !== void 0) next.typeface = patch.set.typeface;
|
|
4442
|
+
return Object.keys(next).length > 0 ? next : void 0;
|
|
4443
|
+
}
|
|
4168
4444
|
function findShapeNodeBySourceHandle(source, handle) {
|
|
4169
4445
|
for (const slide of source.slides) {
|
|
4170
4446
|
const shape = findShapeNodeInTree(slide.shapes, handle);
|
|
@@ -4222,9 +4498,648 @@ function updateShapeTransform(source, handle, transform) {
|
|
|
4222
4498
|
]
|
|
4223
4499
|
};
|
|
4224
4500
|
}
|
|
4225
|
-
function
|
|
4226
|
-
|
|
4227
|
-
|
|
4501
|
+
function addTextBox(source, slideHandle, input) {
|
|
4502
|
+
assertTextBoxInput(input);
|
|
4503
|
+
const slideIndex = source.slides.findIndex(
|
|
4504
|
+
(slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
|
|
4505
|
+
);
|
|
4506
|
+
if (slideIndex === -1) {
|
|
4507
|
+
throw new Error("addTextBox: slide handle was not found in PptxSourceModel source");
|
|
4508
|
+
}
|
|
4509
|
+
const slide = source.slides[slideIndex];
|
|
4510
|
+
const shapeId = nextShapeId(slide.shapes, source.edits ?? [], slide.partPath);
|
|
4511
|
+
const shapeIdValue = String(shapeId);
|
|
4512
|
+
const name = input.name?.trim() || `TextBox ${shapeIdValue}`;
|
|
4513
|
+
const orderingSlot = nextOrderingSlot(slide.shapes);
|
|
4514
|
+
const shape = createTextBoxShape(slide.partPath, shapeId, name, orderingSlot, input);
|
|
4515
|
+
const slides = source.slides.map(
|
|
4516
|
+
(candidate, index) => index === slideIndex ? {
|
|
4517
|
+
...candidate,
|
|
4518
|
+
shapes: [...candidate.shapes, shape]
|
|
4519
|
+
} : candidate
|
|
4520
|
+
);
|
|
4521
|
+
return {
|
|
4522
|
+
...source,
|
|
4523
|
+
slides,
|
|
4524
|
+
edits: [
|
|
4525
|
+
...source.edits ?? [],
|
|
4526
|
+
{
|
|
4527
|
+
kind: "addTextBox",
|
|
4528
|
+
slidePartPath: slide.partPath,
|
|
4529
|
+
shapeId: shapeIdValue,
|
|
4530
|
+
name,
|
|
4531
|
+
offsetX: input.offsetX,
|
|
4532
|
+
offsetY: input.offsetY,
|
|
4533
|
+
width: input.width,
|
|
4534
|
+
height: input.height,
|
|
4535
|
+
text: input.text
|
|
4536
|
+
}
|
|
4537
|
+
]
|
|
4538
|
+
};
|
|
4539
|
+
}
|
|
4540
|
+
function addConnector(source, slideHandle, input) {
|
|
4541
|
+
assertConnectorInput(input);
|
|
4542
|
+
const slideIndex = source.slides.findIndex(
|
|
4543
|
+
(slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
|
|
4544
|
+
);
|
|
4545
|
+
if (slideIndex === -1) {
|
|
4546
|
+
throw new Error("addConnector: slide handle was not found in PptxSourceModel source");
|
|
4547
|
+
}
|
|
4548
|
+
const slide = source.slides[slideIndex];
|
|
4549
|
+
const startShape = requireConnectorTargetShape(slide, input.start.shapeHandle, "start");
|
|
4550
|
+
const endShape = requireConnectorTargetShape(slide, input.end.shapeHandle, "end");
|
|
4551
|
+
const shapeId = nextShapeId(slide.shapes, source.edits ?? [], slide.partPath);
|
|
4552
|
+
const shapeIdValue = String(shapeId);
|
|
4553
|
+
const name = input.name?.trim() || `Connector ${shapeIdValue}`;
|
|
4554
|
+
const orderingSlot = nextOrderingSlot(slide.shapes);
|
|
4555
|
+
const connector = createConnectorShape(
|
|
4556
|
+
slide.partPath,
|
|
4557
|
+
shapeId,
|
|
4558
|
+
name,
|
|
4559
|
+
orderingSlot,
|
|
4560
|
+
startShape,
|
|
4561
|
+
endShape,
|
|
4562
|
+
input
|
|
4563
|
+
);
|
|
4564
|
+
const slides = source.slides.map(
|
|
4565
|
+
(candidate, index) => index === slideIndex ? {
|
|
4566
|
+
...candidate,
|
|
4567
|
+
shapes: [...candidate.shapes, connector]
|
|
4568
|
+
} : candidate
|
|
4569
|
+
);
|
|
4570
|
+
return {
|
|
4571
|
+
...source,
|
|
4572
|
+
slides,
|
|
4573
|
+
edits: [
|
|
4574
|
+
...source.edits ?? [],
|
|
4575
|
+
{
|
|
4576
|
+
kind: "addConnector",
|
|
4577
|
+
slidePartPath: slide.partPath,
|
|
4578
|
+
shapeId: shapeIdValue,
|
|
4579
|
+
name,
|
|
4580
|
+
preset: input.preset,
|
|
4581
|
+
offsetX: input.offsetX,
|
|
4582
|
+
offsetY: input.offsetY,
|
|
4583
|
+
width: input.width,
|
|
4584
|
+
height: input.height,
|
|
4585
|
+
startShapeId: String(startShape.nodeId),
|
|
4586
|
+
startConnectionSiteIndex: input.start.connectionSiteIndex,
|
|
4587
|
+
endShapeId: String(endShape.nodeId),
|
|
4588
|
+
endConnectionSiteIndex: input.end.connectionSiteIndex,
|
|
4589
|
+
...input.outline !== void 0 ? { outline: input.outline } : {}
|
|
4590
|
+
}
|
|
4591
|
+
]
|
|
4592
|
+
};
|
|
4593
|
+
}
|
|
4594
|
+
function addEmptySlideFromLayout(source, input) {
|
|
4595
|
+
const layout = source.slideLayouts.find(
|
|
4596
|
+
(candidate) => candidate.partPath === input.layoutPartPath
|
|
4597
|
+
);
|
|
4598
|
+
if (layout === void 0) {
|
|
4599
|
+
throw new Error("addEmptySlideFromLayout: slide layout part path was not found");
|
|
4600
|
+
}
|
|
4601
|
+
const presentationRels = requirePartRelationships(
|
|
4602
|
+
source,
|
|
4603
|
+
source.presentation.partPath,
|
|
4604
|
+
"addEmptySlideFromLayout"
|
|
4605
|
+
);
|
|
4606
|
+
const newSlidePartPath = nextNumberedPartPath(source, "ppt/slides/slide", ".xml");
|
|
4607
|
+
const newPresentationRelationshipId = nextRelationshipId(presentationRels.relationships);
|
|
4608
|
+
const newSlideRelationshipsPartPath = asPartPath(relationshipsPartPath(newSlidePartPath));
|
|
4609
|
+
const newSlide = {
|
|
4610
|
+
partPath: newSlidePartPath,
|
|
4611
|
+
layoutPartPath: layout.partPath,
|
|
4612
|
+
shapes: [],
|
|
4613
|
+
handle: { partPath: newSlidePartPath }
|
|
4614
|
+
};
|
|
4615
|
+
const newSlideRelationships = {
|
|
4616
|
+
sourcePartPath: newSlidePartPath,
|
|
4617
|
+
relationships: [
|
|
4618
|
+
{
|
|
4619
|
+
id: asRelationshipId("rId1"),
|
|
4620
|
+
type: SLIDE_LAYOUT_REL_TYPE,
|
|
4621
|
+
target: relativeTarget(newSlidePartPath, layout.partPath)
|
|
4622
|
+
}
|
|
4623
|
+
]
|
|
4624
|
+
};
|
|
4625
|
+
return {
|
|
4626
|
+
...source,
|
|
4627
|
+
presentation: {
|
|
4628
|
+
...source.presentation,
|
|
4629
|
+
slidePartPaths: [...source.presentation.slidePartPaths, newSlidePartPath]
|
|
4630
|
+
},
|
|
4631
|
+
slides: [...source.slides, newSlide],
|
|
4632
|
+
packageGraph: {
|
|
4633
|
+
...source.packageGraph,
|
|
4634
|
+
parts: [
|
|
4635
|
+
...source.packageGraph.parts,
|
|
4636
|
+
{ partPath: newSlidePartPath, contentType: SLIDE_CONTENT_TYPE },
|
|
4637
|
+
{ partPath: newSlideRelationshipsPartPath, contentType: RELS_CONTENT_TYPE }
|
|
4638
|
+
],
|
|
4639
|
+
contentTypes: {
|
|
4640
|
+
...source.packageGraph.contentTypes,
|
|
4641
|
+
overrides: [
|
|
4642
|
+
...source.packageGraph.contentTypes.overrides,
|
|
4643
|
+
{ partName: newSlidePartPath, contentType: SLIDE_CONTENT_TYPE },
|
|
4644
|
+
...relationshipPartOverrides(source, [newSlideRelationshipsPartPath])
|
|
4645
|
+
]
|
|
4646
|
+
},
|
|
4647
|
+
relationships: [
|
|
4648
|
+
...source.packageGraph.relationships.map(
|
|
4649
|
+
(relationships) => relationships.sourcePartPath !== source.presentation.partPath ? relationships : {
|
|
4650
|
+
...relationships,
|
|
4651
|
+
relationships: [
|
|
4652
|
+
...relationships.relationships,
|
|
4653
|
+
{
|
|
4654
|
+
id: newPresentationRelationshipId,
|
|
4655
|
+
type: SLIDE_REL_TYPE,
|
|
4656
|
+
target: relativeTarget(source.presentation.partPath, newSlidePartPath)
|
|
4657
|
+
}
|
|
4658
|
+
]
|
|
4659
|
+
}
|
|
4660
|
+
),
|
|
4661
|
+
newSlideRelationships
|
|
4662
|
+
],
|
|
4663
|
+
rawParts: [
|
|
4664
|
+
...source.packageGraph.rawParts ?? [],
|
|
4665
|
+
{
|
|
4666
|
+
kind: "binary",
|
|
4667
|
+
partPath: newSlidePartPath,
|
|
4668
|
+
contentType: SLIDE_CONTENT_TYPE,
|
|
4669
|
+
bytes: new TextEncoder().encode(EMPTY_SLIDE_XML)
|
|
4670
|
+
}
|
|
4671
|
+
]
|
|
4672
|
+
},
|
|
4673
|
+
edits: [
|
|
4674
|
+
...source.edits ?? [],
|
|
4675
|
+
{
|
|
4676
|
+
kind: "addEmptySlideFromLayout",
|
|
4677
|
+
layoutPartPath: layout.partPath,
|
|
4678
|
+
newSlidePartPath,
|
|
4679
|
+
newRelationshipId: newPresentationRelationshipId
|
|
4680
|
+
}
|
|
4681
|
+
]
|
|
4682
|
+
};
|
|
4683
|
+
}
|
|
4684
|
+
function deleteShape(source, handle) {
|
|
4685
|
+
if (handle.nodeId === void 0) {
|
|
4686
|
+
throw new Error("deleteShape: shape delete requires a node id");
|
|
4687
|
+
}
|
|
4688
|
+
let found;
|
|
4689
|
+
let deleted = false;
|
|
4690
|
+
const slides = source.slides.map((slide) => {
|
|
4691
|
+
let slideChanged = false;
|
|
4692
|
+
const nextShapes = slide.shapes.filter((shape) => {
|
|
4693
|
+
if (!sourceHandlesEqual(shape.handle, handle)) return true;
|
|
4694
|
+
found = shape;
|
|
4695
|
+
if (shape.kind !== "shape") {
|
|
4696
|
+
throw new Error("deleteShape: only top-level sp shapes can be deleted");
|
|
4697
|
+
}
|
|
4698
|
+
if (hasAlternateContentSidecar(shape)) {
|
|
4699
|
+
throw new Error("deleteShape: shapes inside AlternateContent are not supported");
|
|
4700
|
+
}
|
|
4701
|
+
deleted = true;
|
|
4702
|
+
slideChanged = true;
|
|
4703
|
+
return false;
|
|
4704
|
+
});
|
|
4705
|
+
return slideChanged ? { ...slide, shapes: nextShapes } : slide;
|
|
4706
|
+
});
|
|
4707
|
+
if (!deleted) {
|
|
4708
|
+
if (found === void 0 && source.slides.some((slide) => hasNestedShapeNodeWithHandle(slide.shapes, handle))) {
|
|
4709
|
+
throw new Error("deleteShape: nested group shape deletion is not supported");
|
|
4710
|
+
}
|
|
4711
|
+
throw new Error("deleteShape: shape handle was not found in PptxSourceModel source");
|
|
4712
|
+
}
|
|
4713
|
+
const referencingConnector = findConnectorReferencingShape(source, handle);
|
|
4714
|
+
if (referencingConnector !== void 0) {
|
|
4715
|
+
throw new Error(
|
|
4716
|
+
`deleteShape: shape is referenced by connector '${referencingConnector.name ?? referencingConnector.nodeId ?? "unknown"}'`
|
|
4717
|
+
);
|
|
4718
|
+
}
|
|
4719
|
+
const retainedEdits = (source.edits ?? []).filter((edit) => !editTargetsShape(edit, handle));
|
|
4720
|
+
const deletedInsertedShape = (source.edits ?? []).some(
|
|
4721
|
+
(edit) => edit.kind === "addTextBox" && edit.slidePartPath === handle.partPath && edit.shapeId === String(handle.nodeId)
|
|
4722
|
+
);
|
|
4723
|
+
return {
|
|
4724
|
+
...source,
|
|
4725
|
+
slides,
|
|
4726
|
+
edits: deletedInsertedShape ? retainedEdits : [
|
|
4727
|
+
...retainedEdits,
|
|
4728
|
+
{
|
|
4729
|
+
kind: "deleteShape",
|
|
4730
|
+
handle
|
|
4731
|
+
}
|
|
4732
|
+
]
|
|
4733
|
+
};
|
|
4734
|
+
}
|
|
4735
|
+
function replaceImageBytes(source, handle, bytes) {
|
|
4736
|
+
const image = requireImageBySourceHandle(source, handle, "replaceImageBytes");
|
|
4737
|
+
const media = requireMediaForImage(source, image, "replaceImageBytes");
|
|
4738
|
+
const detectedContentType = detectImageContentType(bytes);
|
|
4739
|
+
if (detectedContentType === void 0) {
|
|
4740
|
+
throw new Error("replaceImageBytes: unsupported or unknown replacement image format");
|
|
4741
|
+
}
|
|
4742
|
+
if (detectedContentType !== media.contentType) {
|
|
4743
|
+
throw new Error(
|
|
4744
|
+
`replaceImageBytes: replacement image content type '${detectedContentType}' does not match existing media content type '${media.contentType}'`
|
|
4745
|
+
);
|
|
4746
|
+
}
|
|
4747
|
+
const sharedReferenceCount = countImageReferencesToMedia(source, media.partPath);
|
|
4748
|
+
const edit = {
|
|
4749
|
+
kind: "replaceImage",
|
|
4750
|
+
handle,
|
|
4751
|
+
mediaPartPath: media.partPath,
|
|
4752
|
+
contentType: media.contentType,
|
|
4753
|
+
sharedReferenceCount
|
|
4754
|
+
};
|
|
4755
|
+
return {
|
|
4756
|
+
...source,
|
|
4757
|
+
packageGraph: {
|
|
4758
|
+
...source.packageGraph,
|
|
4759
|
+
media: source.packageGraph.media.map(
|
|
4760
|
+
(part) => part.partPath === media.partPath ? { ...part, bytes: copyBytes(bytes) } : part
|
|
4761
|
+
)
|
|
4762
|
+
},
|
|
4763
|
+
edits: [...source.edits ?? [], edit]
|
|
4764
|
+
};
|
|
4765
|
+
}
|
|
4766
|
+
function duplicateSlide(source, slideHandle) {
|
|
4767
|
+
const slideIndex = source.slides.findIndex(
|
|
4768
|
+
(slide) => sourceHandlesEqual(slide.handle, slideHandle)
|
|
4769
|
+
);
|
|
4770
|
+
if (slideIndex === -1) {
|
|
4771
|
+
throw new Error("duplicateSlide: slide handle was not found in PptxSourceModel source");
|
|
4772
|
+
}
|
|
4773
|
+
const sourceSlide = source.slides[slideIndex];
|
|
4774
|
+
if (hasDirtyEditForPart(source.edits ?? [], sourceSlide.partPath)) {
|
|
4775
|
+
throw new Error(
|
|
4776
|
+
"duplicateSlide: duplicating a slide with pending dirty part edits is unsupported"
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4779
|
+
const presentationRels = requirePartRelationships(
|
|
4780
|
+
source,
|
|
4781
|
+
source.presentation.partPath,
|
|
4782
|
+
"duplicateSlide"
|
|
4783
|
+
);
|
|
4784
|
+
const sourcePresentationRelationship = requireSlideRelationship(
|
|
4785
|
+
source,
|
|
4786
|
+
presentationRels,
|
|
4787
|
+
sourceSlide.partPath,
|
|
4788
|
+
"duplicateSlide"
|
|
4789
|
+
);
|
|
4790
|
+
const sourceRawSlide = requireRawBinaryPart(source, sourceSlide.partPath, "duplicateSlide");
|
|
4791
|
+
const sourceSlideRelationships = source.packageGraph.relationships.find(
|
|
4792
|
+
(relationships) => relationships.sourcePartPath === sourceSlide.partPath
|
|
4793
|
+
);
|
|
4794
|
+
const newSlidePartPath = nextNumberedPartPath(source, "ppt/slides/slide", ".xml");
|
|
4795
|
+
const newPresentationRelationshipId = nextRelationshipId(presentationRels.relationships);
|
|
4796
|
+
const notesCopy = createNotesSlideCopy(
|
|
4797
|
+
source,
|
|
4798
|
+
sourceSlide,
|
|
4799
|
+
sourceSlideRelationships,
|
|
4800
|
+
newSlidePartPath
|
|
4801
|
+
);
|
|
4802
|
+
const newSlideRelationships = sourceSlideRelationships === void 0 ? void 0 : {
|
|
4803
|
+
sourcePartPath: newSlidePartPath,
|
|
4804
|
+
relationships: sourceSlideRelationships.relationships.map(
|
|
4805
|
+
(relationship) => notesCopy !== void 0 && relationship.id === notesCopy.slideRelationshipId ? { ...relationship, target: relativeTarget(newSlidePartPath, notesCopy.newPartPath) } : relationship
|
|
4806
|
+
)
|
|
4807
|
+
};
|
|
4808
|
+
const newRelationshipPartPaths = [
|
|
4809
|
+
...newSlideRelationships === void 0 ? [] : [asPartPath(relationshipsPartPath(newSlidePartPath))],
|
|
4810
|
+
...notesCopy?.relationships === void 0 ? [] : [asPartPath(relationshipsPartPath(notesCopy.newPartPath))]
|
|
4811
|
+
];
|
|
4812
|
+
const slideContentType = source.packageGraph.parts.find((part) => part.partPath === sourceSlide.partPath)?.contentType ?? SLIDE_CONTENT_TYPE;
|
|
4813
|
+
const insertAt = slideIndex + 1;
|
|
4814
|
+
const newSlide = withPartPath(cloneJson(sourceSlide), newSlidePartPath);
|
|
4815
|
+
return {
|
|
4816
|
+
...source,
|
|
4817
|
+
presentation: {
|
|
4818
|
+
...source.presentation,
|
|
4819
|
+
slidePartPaths: insertAtReadonly(
|
|
4820
|
+
source.presentation.slidePartPaths,
|
|
4821
|
+
insertAt,
|
|
4822
|
+
newSlidePartPath
|
|
4823
|
+
)
|
|
4824
|
+
},
|
|
4825
|
+
slides: insertAtReadonly(source.slides, insertAt, newSlide),
|
|
4826
|
+
packageGraph: {
|
|
4827
|
+
...source.packageGraph,
|
|
4828
|
+
parts: [
|
|
4829
|
+
...source.packageGraph.parts,
|
|
4830
|
+
{ partPath: newSlidePartPath, contentType: slideContentType },
|
|
4831
|
+
...newSlideRelationships === void 0 ? [] : [
|
|
4832
|
+
{
|
|
4833
|
+
partPath: asPartPath(relationshipsPartPath(newSlidePartPath)),
|
|
4834
|
+
contentType: RELS_CONTENT_TYPE
|
|
4835
|
+
}
|
|
4836
|
+
],
|
|
4837
|
+
...notesCopy === void 0 ? [] : [
|
|
4838
|
+
{ partPath: notesCopy.newPartPath, contentType: notesCopy.contentType },
|
|
4839
|
+
...notesCopy.relationships === void 0 ? [] : [
|
|
4840
|
+
{
|
|
4841
|
+
partPath: asPartPath(relationshipsPartPath(notesCopy.newPartPath)),
|
|
4842
|
+
contentType: RELS_CONTENT_TYPE
|
|
4843
|
+
}
|
|
4844
|
+
]
|
|
4845
|
+
]
|
|
4846
|
+
],
|
|
4847
|
+
contentTypes: {
|
|
4848
|
+
...source.packageGraph.contentTypes,
|
|
4849
|
+
overrides: [
|
|
4850
|
+
...source.packageGraph.contentTypes.overrides,
|
|
4851
|
+
{ partName: newSlidePartPath, contentType: slideContentType },
|
|
4852
|
+
...notesCopy === void 0 ? [] : [{ partName: notesCopy.newPartPath, contentType: notesCopy.contentType }],
|
|
4853
|
+
...relationshipPartOverrides(source, newRelationshipPartPaths)
|
|
4854
|
+
]
|
|
4855
|
+
},
|
|
4856
|
+
relationships: [
|
|
4857
|
+
...source.packageGraph.relationships.map(
|
|
4858
|
+
(relationships) => relationships.sourcePartPath !== source.presentation.partPath ? relationships : {
|
|
4859
|
+
...relationships,
|
|
4860
|
+
relationships: [
|
|
4861
|
+
...relationships.relationships,
|
|
4862
|
+
{
|
|
4863
|
+
id: newPresentationRelationshipId,
|
|
4864
|
+
type: SLIDE_REL_TYPE,
|
|
4865
|
+
target: relativeTarget(source.presentation.partPath, newSlidePartPath)
|
|
4866
|
+
}
|
|
4867
|
+
]
|
|
4868
|
+
}
|
|
4869
|
+
),
|
|
4870
|
+
...newSlideRelationships === void 0 ? [] : [newSlideRelationships],
|
|
4871
|
+
...notesCopy?.relationships === void 0 ? [] : [notesCopy.relationships]
|
|
4872
|
+
],
|
|
4873
|
+
rawParts: [
|
|
4874
|
+
...source.packageGraph.rawParts ?? [],
|
|
4875
|
+
{
|
|
4876
|
+
kind: "binary",
|
|
4877
|
+
partPath: newSlidePartPath,
|
|
4878
|
+
contentType: slideContentType,
|
|
4879
|
+
bytes: copyBytes(sourceRawSlide.bytes)
|
|
4880
|
+
},
|
|
4881
|
+
...notesCopy === void 0 ? [] : [
|
|
4882
|
+
{
|
|
4883
|
+
kind: "binary",
|
|
4884
|
+
partPath: notesCopy.newPartPath,
|
|
4885
|
+
contentType: notesCopy.contentType,
|
|
4886
|
+
bytes: copyBytes(notesCopy.raw.bytes)
|
|
4887
|
+
}
|
|
4888
|
+
]
|
|
4889
|
+
]
|
|
4890
|
+
},
|
|
4891
|
+
edits: [
|
|
4892
|
+
...source.edits ?? [],
|
|
4893
|
+
{
|
|
4894
|
+
kind: "duplicateSlide",
|
|
4895
|
+
sourceSlidePartPath: sourceSlide.partPath,
|
|
4896
|
+
sourceRelationshipId: sourcePresentationRelationship.id,
|
|
4897
|
+
newSlidePartPath,
|
|
4898
|
+
newRelationshipId: newPresentationRelationshipId
|
|
4899
|
+
}
|
|
4900
|
+
]
|
|
4901
|
+
};
|
|
4902
|
+
}
|
|
4903
|
+
function deleteSlide(source, slideHandle) {
|
|
4904
|
+
if (source.slides.length <= 1) {
|
|
4905
|
+
throw new Error("deleteSlide: cannot delete the last slide");
|
|
4906
|
+
}
|
|
4907
|
+
const slideIndex = source.slides.findIndex(
|
|
4908
|
+
(slide2) => sourceHandlesEqual(slide2.handle, slideHandle)
|
|
4909
|
+
);
|
|
4910
|
+
if (slideIndex === -1) {
|
|
4911
|
+
throw new Error("deleteSlide: slide handle was not found in PptxSourceModel source");
|
|
4912
|
+
}
|
|
4913
|
+
const slide = source.slides[slideIndex];
|
|
4914
|
+
const presentationRels = requirePartRelationships(
|
|
4915
|
+
source,
|
|
4916
|
+
source.presentation.partPath,
|
|
4917
|
+
"deleteSlide"
|
|
4918
|
+
);
|
|
4919
|
+
const presentationRelationship = requireSlideRelationship(
|
|
4920
|
+
source,
|
|
4921
|
+
presentationRels,
|
|
4922
|
+
slide.partPath,
|
|
4923
|
+
"deleteSlide"
|
|
4924
|
+
);
|
|
4925
|
+
const slideRelationships = source.packageGraph.relationships.find(
|
|
4926
|
+
(relationships) => relationships.sourcePartPath === slide.partPath
|
|
4927
|
+
);
|
|
4928
|
+
const notesPartPaths = slideRelationships?.relationships.flatMap((relationship) => {
|
|
4929
|
+
if (relationship.type !== NOTES_SLIDE_REL_TYPE) return [];
|
|
4930
|
+
const target = resolveInternalRelationshipTarget(slide.partPath, relationship);
|
|
4931
|
+
return target === void 0 ? [] : [target];
|
|
4932
|
+
}) ?? [];
|
|
4933
|
+
const removedPartPaths = /* @__PURE__ */ new Set([slide.partPath, ...notesPartPaths]);
|
|
4934
|
+
const removedRelationshipPartPaths = new Set(
|
|
4935
|
+
[slide.partPath, ...notesPartPaths].map((partPath) => relationshipsPartPath(partPath))
|
|
4936
|
+
);
|
|
4937
|
+
const retainedEdits = (source.edits ?? []).filter(
|
|
4938
|
+
(edit) => !editIsInvalidatedByDeletedParts(edit, removedPartPaths)
|
|
4939
|
+
);
|
|
4940
|
+
const deletedInsertedSlide = (source.edits ?? []).some(
|
|
4941
|
+
(edit) => (edit.kind === "addEmptySlideFromLayout" || edit.kind === "duplicateSlide") && edit.newSlidePartPath === slide.partPath
|
|
4942
|
+
);
|
|
4943
|
+
return {
|
|
4944
|
+
...source,
|
|
4945
|
+
presentation: {
|
|
4946
|
+
...source.presentation,
|
|
4947
|
+
slidePartPaths: source.presentation.slidePartPaths.filter(
|
|
4948
|
+
(partPath) => partPath !== slide.partPath
|
|
4949
|
+
)
|
|
4950
|
+
},
|
|
4951
|
+
slides: source.slides.filter((candidate) => candidate.partPath !== slide.partPath),
|
|
4952
|
+
packageGraph: {
|
|
4953
|
+
...source.packageGraph,
|
|
4954
|
+
parts: source.packageGraph.parts.filter(
|
|
4955
|
+
(part) => !removedPartPaths.has(part.partPath) && !removedRelationshipPartPaths.has(part.partPath)
|
|
4956
|
+
),
|
|
4957
|
+
contentTypes: {
|
|
4958
|
+
...source.packageGraph.contentTypes,
|
|
4959
|
+
overrides: source.packageGraph.contentTypes.overrides.filter(
|
|
4960
|
+
(override) => !removedPartPaths.has(override.partName) && !removedRelationshipPartPaths.has(override.partName)
|
|
4961
|
+
)
|
|
4962
|
+
},
|
|
4963
|
+
relationships: source.packageGraph.relationships.filter((relationships) => !removedPartPaths.has(relationships.sourcePartPath)).map(
|
|
4964
|
+
(relationships) => relationships.sourcePartPath !== source.presentation.partPath ? relationships : {
|
|
4965
|
+
...relationships,
|
|
4966
|
+
relationships: relationships.relationships.filter(
|
|
4967
|
+
(relationship) => relationship.id !== presentationRelationship.id
|
|
4968
|
+
)
|
|
4969
|
+
}
|
|
4970
|
+
),
|
|
4971
|
+
rawParts: source.packageGraph.rawParts?.filter(
|
|
4972
|
+
(part) => !removedPartPaths.has(part.partPath)
|
|
4973
|
+
)
|
|
4974
|
+
},
|
|
4975
|
+
edits: deletedInsertedSlide ? retainedEdits : [
|
|
4976
|
+
...retainedEdits,
|
|
4977
|
+
{
|
|
4978
|
+
kind: "deleteSlide",
|
|
4979
|
+
slidePartPath: slide.partPath,
|
|
4980
|
+
relationshipId: presentationRelationship.id
|
|
4981
|
+
}
|
|
4982
|
+
]
|
|
4983
|
+
};
|
|
4984
|
+
}
|
|
4985
|
+
function requireImageBySourceHandle(source, handle, operation) {
|
|
4986
|
+
const shape = findShapeNodeBySourceHandle(source, handle);
|
|
4987
|
+
if (shape === void 0) {
|
|
4988
|
+
throw new Error(`${operation}: image handle was not found in PptxSourceModel source`);
|
|
4989
|
+
}
|
|
4990
|
+
if (shape.kind !== "image") {
|
|
4991
|
+
throw new Error(`${operation}: shape handle does not reference a pic image shape`);
|
|
4992
|
+
}
|
|
4993
|
+
return shape;
|
|
4994
|
+
}
|
|
4995
|
+
function requireMediaForImage(source, image, operation) {
|
|
4996
|
+
if (image.blipRelationshipId === void 0) {
|
|
4997
|
+
throw new Error(`${operation}: image shape has no embedded blip relationship`);
|
|
4998
|
+
}
|
|
4999
|
+
const partPath = image.handle?.partPath;
|
|
5000
|
+
if (partPath === void 0) {
|
|
5001
|
+
throw new Error(`${operation}: image handle has no source part path`);
|
|
5002
|
+
}
|
|
5003
|
+
const relationships = requirePartRelationships(source, partPath, operation);
|
|
5004
|
+
const relationship = relationships.relationships.find(
|
|
5005
|
+
(candidate) => candidate.id === image.blipRelationshipId && candidate.type === IMAGE_REL_TYPE
|
|
5006
|
+
);
|
|
5007
|
+
if (relationship === void 0) {
|
|
5008
|
+
throw new Error(`${operation}: image relationship was not found`);
|
|
5009
|
+
}
|
|
5010
|
+
const mediaPartPath = resolveInternalRelationshipTarget(
|
|
5011
|
+
relationships.sourcePartPath,
|
|
5012
|
+
relationship
|
|
5013
|
+
);
|
|
5014
|
+
if (mediaPartPath === void 0) {
|
|
5015
|
+
throw new Error(`${operation}: image relationship does not target an internal media part`);
|
|
5016
|
+
}
|
|
5017
|
+
const media = source.packageGraph.media.find((part) => part.partPath === mediaPartPath);
|
|
5018
|
+
if (media === void 0) {
|
|
5019
|
+
throw new Error(`${operation}: image media part was not found`);
|
|
5020
|
+
}
|
|
5021
|
+
return media;
|
|
5022
|
+
}
|
|
5023
|
+
function countImageReferencesToMedia(source, mediaPartPath) {
|
|
5024
|
+
const parsedImageRelationshipKeys = /* @__PURE__ */ new Set();
|
|
5025
|
+
let count = 0;
|
|
5026
|
+
const countParsedImages = (partPath, shapes) => {
|
|
5027
|
+
for (const image of findImagesInTree(shapes)) {
|
|
5028
|
+
if (image.blipRelationshipId === void 0) continue;
|
|
5029
|
+
const relationships = source.packageGraph.relationships.find(
|
|
5030
|
+
(candidate) => candidate.sourcePartPath === partPath
|
|
5031
|
+
);
|
|
5032
|
+
const relationship = relationships?.relationships.find(
|
|
5033
|
+
(candidate) => candidate.id === image.blipRelationshipId && candidate.type === IMAGE_REL_TYPE
|
|
5034
|
+
);
|
|
5035
|
+
if (relationship === void 0) continue;
|
|
5036
|
+
if (resolveInternalRelationshipTarget(partPath, relationship) === mediaPartPath) {
|
|
5037
|
+
count += 1;
|
|
5038
|
+
parsedImageRelationshipKeys.add(imageRelationshipKey(partPath, relationship.id));
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
};
|
|
5042
|
+
for (const slide of source.slides) countParsedImages(slide.partPath, slide.shapes);
|
|
5043
|
+
for (const layout of source.slideLayouts) countParsedImages(layout.partPath, layout.shapes);
|
|
5044
|
+
for (const master of source.slideMasters) countParsedImages(master.partPath, master.shapes);
|
|
5045
|
+
for (const relationships of source.packageGraph.relationships) {
|
|
5046
|
+
for (const relationship of relationships.relationships) {
|
|
5047
|
+
if (relationship.type !== IMAGE_REL_TYPE) continue;
|
|
5048
|
+
if (parsedImageRelationshipKeys.has(
|
|
5049
|
+
imageRelationshipKey(relationships.sourcePartPath, relationship.id)
|
|
5050
|
+
)) {
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
5053
|
+
if (resolveInternalRelationshipTarget(relationships.sourcePartPath, relationship) === mediaPartPath) {
|
|
5054
|
+
count += 1;
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
}
|
|
5058
|
+
return count;
|
|
5059
|
+
}
|
|
5060
|
+
function imageRelationshipKey(partPath, relationshipId) {
|
|
5061
|
+
return `${partPath}\0${relationshipId}`;
|
|
5062
|
+
}
|
|
5063
|
+
function findImagesInTree(shapes) {
|
|
5064
|
+
return shapes.flatMap((shape) => {
|
|
5065
|
+
if (shape.kind === "image") return [shape];
|
|
5066
|
+
if (shape.kind === "group") return findImagesInTree(shape.children);
|
|
5067
|
+
return [];
|
|
5068
|
+
});
|
|
5069
|
+
}
|
|
5070
|
+
function detectImageContentType(bytes) {
|
|
5071
|
+
if (startsWithBytes(bytes, [137, 80, 78, 71, 13, 10, 26, 10])) {
|
|
5072
|
+
return "image/png";
|
|
5073
|
+
}
|
|
5074
|
+
if (startsWithBytes(bytes, [255, 216, 255])) return "image/jpeg";
|
|
5075
|
+
if (startsWithBytes(bytes, [71, 73, 70, 56, 55, 97]) || startsWithBytes(bytes, [71, 73, 70, 56, 57, 97])) {
|
|
5076
|
+
return "image/gif";
|
|
5077
|
+
}
|
|
5078
|
+
if (startsWithBytes(bytes, [66, 77])) return "image/bmp";
|
|
5079
|
+
if (startsWithBytes(bytes, [73, 73, 42, 0]) || startsWithBytes(bytes, [77, 77, 0, 42])) {
|
|
5080
|
+
return "image/tiff";
|
|
5081
|
+
}
|
|
5082
|
+
if (startsWithBytes(bytes, [82, 73, 70, 70]) && bytes[8] === 87 && bytes[9] === 69 && bytes[10] === 66 && bytes[11] === 80) {
|
|
5083
|
+
return "image/webp";
|
|
5084
|
+
}
|
|
5085
|
+
return void 0;
|
|
5086
|
+
}
|
|
5087
|
+
function startsWithBytes(bytes, prefix) {
|
|
5088
|
+
if (bytes.length < prefix.length) return false;
|
|
5089
|
+
return prefix.every((value, index) => bytes[index] === value);
|
|
5090
|
+
}
|
|
5091
|
+
function assertEditableTextRunProperties(properties) {
|
|
5092
|
+
for (const property of Object.keys(properties)) {
|
|
5093
|
+
if (!EDITABLE_TEXT_RUN_PROPERTY_SET.has(property)) {
|
|
5094
|
+
throw new Error(`updateTextRunProperties: unsupported text run property '${property}'`);
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
requireBooleanOrUndefined(properties.bold, "bold");
|
|
5098
|
+
requireBooleanOrUndefined(properties.italic, "italic");
|
|
5099
|
+
requireBooleanOrUndefined(properties.underline, "underline");
|
|
5100
|
+
if (properties.fontSize !== void 0 && (!Number.isFinite(properties.fontSize) || properties.fontSize <= 0)) {
|
|
5101
|
+
throw new Error("updateTextRunProperties: fontSize must be a finite positive pt value");
|
|
5102
|
+
}
|
|
5103
|
+
if (properties.typeface !== void 0 && properties.typeface.trim() === "") {
|
|
5104
|
+
throw new Error("updateTextRunProperties: typeface must be a non-empty string");
|
|
5105
|
+
}
|
|
5106
|
+
if (properties.color !== void 0) {
|
|
5107
|
+
if (properties.color.kind !== "srgb") {
|
|
5108
|
+
throw new Error("updateTextRunProperties: only srgb text run color is supported");
|
|
5109
|
+
}
|
|
5110
|
+
if (!/^[0-9A-Fa-f]{6}$/.test(properties.color.hex)) {
|
|
5111
|
+
throw new Error("updateTextRunProperties: srgb text run color must be a 6-digit hex value");
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
}
|
|
5115
|
+
function requireBooleanOrUndefined(value, fieldName) {
|
|
5116
|
+
if (value !== void 0 && typeof value !== "boolean") {
|
|
5117
|
+
throw new Error(`updateTextRunProperties: ${fieldName} must be a boolean value`);
|
|
5118
|
+
}
|
|
5119
|
+
}
|
|
5120
|
+
function assertEditableTextRunPropertyNames(properties) {
|
|
5121
|
+
for (const property of properties) {
|
|
5122
|
+
if (!EDITABLE_TEXT_RUN_PROPERTY_SET.has(property)) {
|
|
5123
|
+
throw new Error(`updateTextRunProperties: unsupported text run property '${property}'`);
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
function definedEditableTextRunProperties(properties) {
|
|
5128
|
+
const defined = {};
|
|
5129
|
+
if (properties.bold !== void 0) defined.bold = properties.bold;
|
|
5130
|
+
if (properties.italic !== void 0) defined.italic = properties.italic;
|
|
5131
|
+
if (properties.underline !== void 0) defined.underline = properties.underline;
|
|
5132
|
+
if (properties.fontSize !== void 0) defined.fontSize = properties.fontSize;
|
|
5133
|
+
if (properties.color !== void 0) defined.color = properties.color;
|
|
5134
|
+
if (properties.typeface !== void 0) defined.typeface = properties.typeface;
|
|
5135
|
+
return defined;
|
|
5136
|
+
}
|
|
5137
|
+
function textRunPropertiesEqual(left, right) {
|
|
5138
|
+
return JSON.stringify(left ?? {}) === JSON.stringify(right ?? {});
|
|
5139
|
+
}
|
|
5140
|
+
function createReplacementRunHandle(paragraph) {
|
|
5141
|
+
if (paragraph.runs[0]?.handle !== void 0) return paragraph.runs[0].handle;
|
|
5142
|
+
if (paragraph.handle?.nodeId === void 0) return void 0;
|
|
4228
5143
|
return {
|
|
4229
5144
|
...paragraph.handle,
|
|
4230
5145
|
nodeId: asSourceNodeId(`${paragraph.handle.nodeId}:r:0`),
|
|
@@ -4234,6 +5149,181 @@ function createReplacementRunHandle(paragraph) {
|
|
|
4234
5149
|
function hasEditableTransform(shape) {
|
|
4235
5150
|
return shape.kind !== "raw" && shape.transform !== void 0;
|
|
4236
5151
|
}
|
|
5152
|
+
function assertTextBoxInput(input) {
|
|
5153
|
+
assertFiniteEmu(input.offsetX, "addTextBox", "offsetX");
|
|
5154
|
+
assertFiniteEmu(input.offsetY, "addTextBox", "offsetY");
|
|
5155
|
+
assertPositiveFiniteEmu(input.width, "addTextBox", "width");
|
|
5156
|
+
assertPositiveFiniteEmu(input.height, "addTextBox", "height");
|
|
5157
|
+
if (typeof input.text !== "string") {
|
|
5158
|
+
throw new Error("addTextBox: text must be a string");
|
|
5159
|
+
}
|
|
5160
|
+
if (input.name !== void 0 && input.name.trim() === "") {
|
|
5161
|
+
throw new Error("addTextBox: name must be a non-empty string when provided");
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
function assertConnectorInput(input) {
|
|
5165
|
+
assertFiniteEmu(input.offsetX, "addConnector", "offsetX");
|
|
5166
|
+
assertFiniteEmu(input.offsetY, "addConnector", "offsetY");
|
|
5167
|
+
assertPositiveFiniteEmu(input.width, "addConnector", "width");
|
|
5168
|
+
assertPositiveFiniteEmu(input.height, "addConnector", "height");
|
|
5169
|
+
if (!CONNECTOR_PRESETS.has(input.preset)) {
|
|
5170
|
+
throw new Error(
|
|
5171
|
+
"addConnector: preset must be straightConnector1, bentConnector3, or curvedConnector3"
|
|
5172
|
+
);
|
|
5173
|
+
}
|
|
5174
|
+
assertConnectionSiteIndex(input.start.connectionSiteIndex, "start");
|
|
5175
|
+
assertConnectionSiteIndex(input.end.connectionSiteIndex, "end");
|
|
5176
|
+
if (input.name !== void 0 && input.name.trim() === "") {
|
|
5177
|
+
throw new Error("addConnector: name must be a non-empty string when provided");
|
|
5178
|
+
}
|
|
5179
|
+
assertArrowEndpoint(input.outline?.headEnd, "headEnd");
|
|
5180
|
+
assertArrowEndpoint(input.outline?.tailEnd, "tailEnd");
|
|
5181
|
+
}
|
|
5182
|
+
function assertConnectionSiteIndex(value, fieldName) {
|
|
5183
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
5184
|
+
throw new Error(
|
|
5185
|
+
`addConnector: ${fieldName}.connectionSiteIndex must be a non-negative integer`
|
|
5186
|
+
);
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
function assertArrowEndpoint(endpoint, fieldName) {
|
|
5190
|
+
if (endpoint === void 0) return;
|
|
5191
|
+
if (!ARROW_TYPES.has(endpoint.type)) {
|
|
5192
|
+
throw new Error(`addConnector: outline.${fieldName}.type is not supported`);
|
|
5193
|
+
}
|
|
5194
|
+
if (!ARROW_SIZES.has(endpoint.width)) {
|
|
5195
|
+
throw new Error(`addConnector: outline.${fieldName}.width is not supported`);
|
|
5196
|
+
}
|
|
5197
|
+
if (!ARROW_SIZES.has(endpoint.length)) {
|
|
5198
|
+
throw new Error(`addConnector: outline.${fieldName}.length is not supported`);
|
|
5199
|
+
}
|
|
5200
|
+
}
|
|
5201
|
+
function assertFiniteEmu(value, operationName, fieldName) {
|
|
5202
|
+
if (!Number.isFinite(value)) {
|
|
5203
|
+
throw new Error(`${operationName}: ${fieldName} must be a finite EMU value`);
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
function assertPositiveFiniteEmu(value, operationName, fieldName) {
|
|
5207
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
5208
|
+
throw new Error(`${operationName}: ${fieldName} must be a finite positive EMU value`);
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
function createTextBoxShape(partPath, shapeId, name, orderingSlot, input) {
|
|
5212
|
+
const transform = {
|
|
5213
|
+
offsetX: input.offsetX,
|
|
5214
|
+
offsetY: input.offsetY,
|
|
5215
|
+
width: input.width,
|
|
5216
|
+
height: input.height
|
|
5217
|
+
};
|
|
5218
|
+
return {
|
|
5219
|
+
kind: "shape",
|
|
5220
|
+
nodeId: shapeId,
|
|
5221
|
+
name,
|
|
5222
|
+
transform,
|
|
5223
|
+
geometry: { preset: "rect" },
|
|
5224
|
+
textBody: {
|
|
5225
|
+
handle: { partPath, nodeId: asSourceNodeId(`text:shape:${shapeId}`), orderingSlot },
|
|
5226
|
+
paragraphs: [
|
|
5227
|
+
{
|
|
5228
|
+
handle: {
|
|
5229
|
+
partPath,
|
|
5230
|
+
nodeId: asSourceNodeId(`text:shape:${shapeId}:p:0`),
|
|
5231
|
+
orderingSlot: 0
|
|
5232
|
+
},
|
|
5233
|
+
runs: [
|
|
5234
|
+
{
|
|
5235
|
+
kind: "textRun",
|
|
5236
|
+
text: input.text,
|
|
5237
|
+
handle: {
|
|
5238
|
+
partPath,
|
|
5239
|
+
nodeId: asSourceNodeId(`text:shape:${shapeId}:p:0:r:0`),
|
|
5240
|
+
orderingSlot: 0
|
|
5241
|
+
}
|
|
5242
|
+
}
|
|
5243
|
+
]
|
|
5244
|
+
}
|
|
5245
|
+
]
|
|
5246
|
+
},
|
|
5247
|
+
handle: { partPath, nodeId: shapeId, orderingSlot }
|
|
5248
|
+
};
|
|
5249
|
+
}
|
|
5250
|
+
function createConnectorShape(partPath, shapeId, name, orderingSlot, startShape, endShape, input) {
|
|
5251
|
+
const transform = {
|
|
5252
|
+
offsetX: input.offsetX,
|
|
5253
|
+
offsetY: input.offsetY,
|
|
5254
|
+
width: input.width,
|
|
5255
|
+
height: input.height
|
|
5256
|
+
};
|
|
5257
|
+
return {
|
|
5258
|
+
kind: "connector",
|
|
5259
|
+
nodeId: shapeId,
|
|
5260
|
+
name,
|
|
5261
|
+
connection: {
|
|
5262
|
+
start: {
|
|
5263
|
+
shapeId: startShape.nodeId,
|
|
5264
|
+
connectionSiteIndex: input.start.connectionSiteIndex
|
|
5265
|
+
},
|
|
5266
|
+
end: {
|
|
5267
|
+
shapeId: endShape.nodeId,
|
|
5268
|
+
connectionSiteIndex: input.end.connectionSiteIndex
|
|
5269
|
+
}
|
|
5270
|
+
},
|
|
5271
|
+
transform,
|
|
5272
|
+
geometry: { preset: input.preset },
|
|
5273
|
+
outline: createConnectorOutline(input.outline),
|
|
5274
|
+
handle: { partPath, nodeId: shapeId, orderingSlot }
|
|
5275
|
+
};
|
|
5276
|
+
}
|
|
5277
|
+
function createConnectorOutline(input) {
|
|
5278
|
+
return {
|
|
5279
|
+
...DEFAULT_CONNECTOR_OUTLINE,
|
|
5280
|
+
...input?.headEnd !== void 0 ? { headEnd: input.headEnd } : {},
|
|
5281
|
+
...input?.tailEnd !== void 0 ? { tailEnd: input.tailEnd } : {}
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
function requireConnectorTargetShape(slide, handle, endpointName) {
|
|
5285
|
+
const shape = slide.shapes.find((candidate) => sourceHandlesEqual(candidate.handle, handle));
|
|
5286
|
+
if (shape === void 0) {
|
|
5287
|
+
throw new Error(`addConnector: ${endpointName} shape handle was not found on the target slide`);
|
|
5288
|
+
}
|
|
5289
|
+
if (shape.kind !== "shape") {
|
|
5290
|
+
throw new Error(`addConnector: ${endpointName} target must be a top-level sp shape`);
|
|
5291
|
+
}
|
|
5292
|
+
const nodeId = shape.nodeId;
|
|
5293
|
+
if (nodeId === void 0) {
|
|
5294
|
+
throw new Error(`addConnector: ${endpointName} target shape requires a node id`);
|
|
5295
|
+
}
|
|
5296
|
+
return { ...shape, nodeId };
|
|
5297
|
+
}
|
|
5298
|
+
function nextShapeId(shapes, edits, slidePartPath) {
|
|
5299
|
+
const used = /* @__PURE__ */ new Set();
|
|
5300
|
+
collectNumericShapeIds(shapes, used);
|
|
5301
|
+
collectNumericShapeEditIds(edits, slidePartPath, used);
|
|
5302
|
+
const max = Math.max(0, ...used);
|
|
5303
|
+
for (let candidate = max + 1; ; candidate += 1) {
|
|
5304
|
+
if (!used.has(candidate)) return asSourceNodeId(String(candidate));
|
|
5305
|
+
}
|
|
5306
|
+
}
|
|
5307
|
+
function collectNumericShapeIds(shapes, used) {
|
|
5308
|
+
for (const shape of shapes) {
|
|
5309
|
+
const numericId = Number(shape.nodeId);
|
|
5310
|
+
if (Number.isInteger(numericId) && numericId > 0) used.add(numericId);
|
|
5311
|
+
if (shape.kind === "group") collectNumericShapeIds(shape.children, used);
|
|
5312
|
+
}
|
|
5313
|
+
}
|
|
5314
|
+
function collectNumericShapeEditIds(edits, slidePartPath, used) {
|
|
5315
|
+
for (const edit of edits) {
|
|
5316
|
+
const id = edit.kind === "addTextBox" && edit.slidePartPath === slidePartPath ? edit.shapeId : edit.kind === "addConnector" && edit.slidePartPath === slidePartPath ? edit.shapeId : edit.kind === "deleteShape" && edit.handle.partPath === slidePartPath ? edit.handle.nodeId : void 0;
|
|
5317
|
+
const numericId = Number(id);
|
|
5318
|
+
if (Number.isInteger(numericId) && numericId > 0) used.add(numericId);
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
function nextOrderingSlot(shapes) {
|
|
5322
|
+
return shapes.reduce((current, shape) => {
|
|
5323
|
+
const slot = shape.handle?.orderingSlot ?? -1;
|
|
5324
|
+
return Math.max(current, slot);
|
|
5325
|
+
}, -1) + 1;
|
|
5326
|
+
}
|
|
4237
5327
|
function findShapeNodeInTree(shapes, handle) {
|
|
4238
5328
|
for (const shape of shapes) {
|
|
4239
5329
|
if (sourceHandlesEqual(shape.handle, handle)) return shape;
|
|
@@ -4253,10 +5343,267 @@ function hasAlternateContentSidecar(shape) {
|
|
|
4253
5343
|
if (shape.kind === "raw") return false;
|
|
4254
5344
|
return shape.rawSidecars?.some((sidecar) => sidecar.node.name === "mc:AlternateContent") ?? false;
|
|
4255
5345
|
}
|
|
5346
|
+
function findConnectorReferencingShape(source, handle) {
|
|
5347
|
+
if (handle.nodeId === void 0) return void 0;
|
|
5348
|
+
for (const slide of source.slides) {
|
|
5349
|
+
if (slide.partPath !== handle.partPath) continue;
|
|
5350
|
+
for (const shape of slide.shapes) {
|
|
5351
|
+
if (shape.kind !== "connector") continue;
|
|
5352
|
+
if (shape.connection?.start?.shapeId === handle.nodeId || shape.connection?.end?.shapeId === handle.nodeId) {
|
|
5353
|
+
return shape;
|
|
5354
|
+
}
|
|
5355
|
+
}
|
|
5356
|
+
}
|
|
5357
|
+
return void 0;
|
|
5358
|
+
}
|
|
4256
5359
|
function sourceHandlesEqual(left, right) {
|
|
4257
5360
|
if (left === void 0) return false;
|
|
4258
5361
|
return left.partPath === right.partPath && left.nodeId === right.nodeId && left.relationshipId === right.relationshipId && left.orderingSlot === right.orderingSlot;
|
|
4259
5362
|
}
|
|
5363
|
+
function createNotesSlideCopy(source, sourceSlide, slideRelationships, newSlidePartPath) {
|
|
5364
|
+
const notesRelationship = slideRelationships?.relationships.find(
|
|
5365
|
+
(relationship) => relationship.type === NOTES_SLIDE_REL_TYPE
|
|
5366
|
+
);
|
|
5367
|
+
if (notesRelationship === void 0) return void 0;
|
|
5368
|
+
const notesPartPath = resolveInternalRelationshipTarget(sourceSlide.partPath, notesRelationship);
|
|
5369
|
+
if (notesPartPath === void 0) return void 0;
|
|
5370
|
+
const raw = requireRawBinaryPart(source, notesPartPath, "duplicateSlide");
|
|
5371
|
+
const contentType = source.packageGraph.parts.find((part) => part.partPath === notesPartPath)?.contentType ?? NOTES_SLIDE_CONTENT_TYPE;
|
|
5372
|
+
const newPartPath = nextNumberedPartPath(source, "ppt/notesSlides/notesSlide", ".xml");
|
|
5373
|
+
const notesRelationships = source.packageGraph.relationships.find(
|
|
5374
|
+
(relationships) => relationships.sourcePartPath === notesPartPath
|
|
5375
|
+
);
|
|
5376
|
+
return {
|
|
5377
|
+
slideRelationshipId: notesRelationship.id,
|
|
5378
|
+
newPartPath,
|
|
5379
|
+
contentType,
|
|
5380
|
+
raw,
|
|
5381
|
+
...notesRelationships === void 0 ? {} : {
|
|
5382
|
+
relationships: {
|
|
5383
|
+
sourcePartPath: newPartPath,
|
|
5384
|
+
relationships: notesRelationships.relationships.map(
|
|
5385
|
+
(relationship) => relationship.type === SLIDE_REL_TYPE && relationship.targetMode !== "External" ? { ...relationship, target: relativeTarget(newPartPath, newSlidePartPath) } : relationship
|
|
5386
|
+
)
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
};
|
|
5390
|
+
}
|
|
5391
|
+
function requirePartRelationships(source, partPath, operationName) {
|
|
5392
|
+
const relationships = source.packageGraph.relationships.find(
|
|
5393
|
+
(candidate) => candidate.sourcePartPath === partPath
|
|
5394
|
+
);
|
|
5395
|
+
if (relationships === void 0) {
|
|
5396
|
+
throw new Error(`${operationName}: presentation relationships were not found`);
|
|
5397
|
+
}
|
|
5398
|
+
return relationships;
|
|
5399
|
+
}
|
|
5400
|
+
function requireSlideRelationship(source, relationships, slidePartPath, operationName) {
|
|
5401
|
+
const relationship = relationships.relationships.find(
|
|
5402
|
+
(candidate) => candidate.type === SLIDE_REL_TYPE && candidate.targetMode !== "External" && resolveInternalRelationshipTarget(source.presentation.partPath, candidate) === slidePartPath
|
|
5403
|
+
);
|
|
5404
|
+
if (relationship === void 0) {
|
|
5405
|
+
throw new Error(`${operationName}: slide relationship was not found in presentation.xml.rels`);
|
|
5406
|
+
}
|
|
5407
|
+
return relationship;
|
|
5408
|
+
}
|
|
5409
|
+
function requireRawBinaryPart(source, partPath, operationName) {
|
|
5410
|
+
const rawPart = source.packageGraph.rawParts?.find((part) => part.partPath === partPath);
|
|
5411
|
+
if (rawPart === void 0) {
|
|
5412
|
+
throw new Error(`${operationName}: part '${partPath}' has no preserved raw package material`);
|
|
5413
|
+
}
|
|
5414
|
+
if (rawPart.kind !== "binary") {
|
|
5415
|
+
throw new Error(
|
|
5416
|
+
`${operationName}: part '${partPath}' is not backed by binary package material`
|
|
5417
|
+
);
|
|
5418
|
+
}
|
|
5419
|
+
return rawPart;
|
|
5420
|
+
}
|
|
5421
|
+
function nextRelationshipId(relationships) {
|
|
5422
|
+
const used = new Set(relationships.map((relationship) => relationship.id));
|
|
5423
|
+
const max = relationships.reduce((current, relationship) => {
|
|
5424
|
+
const match = /(\d+)$/.exec(relationship.id);
|
|
5425
|
+
return match === null ? current : Math.max(current, Number(match[1]));
|
|
5426
|
+
}, 0);
|
|
5427
|
+
for (let index = max + 1; ; index += 1) {
|
|
5428
|
+
const candidate = asRelationshipId(`rId${index}`);
|
|
5429
|
+
if (!used.has(candidate)) return candidate;
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
function nextNumberedPartPath(source, prefix, suffix) {
|
|
5433
|
+
const used = /* @__PURE__ */ new Set([
|
|
5434
|
+
...source.packageGraph.parts.map((part) => part.partPath),
|
|
5435
|
+
...source.packageGraph.contentTypes.overrides.map((override) => override.partName),
|
|
5436
|
+
...(source.packageGraph.rawParts ?? []).map((part) => part.partPath),
|
|
5437
|
+
...source.edits?.flatMap((edit) => topologyEditPartPaths(edit)) ?? []
|
|
5438
|
+
]);
|
|
5439
|
+
const pattern = new RegExp(`^${escapeRegExp(prefix)}(\\d+)${escapeRegExp(suffix)}$`);
|
|
5440
|
+
const max = [...used].reduce((current, path) => {
|
|
5441
|
+
const match = pattern.exec(path);
|
|
5442
|
+
return match === null ? current : Math.max(current, Number(match[1]));
|
|
5443
|
+
}, 0);
|
|
5444
|
+
for (let index = max + 1; ; index += 1) {
|
|
5445
|
+
const candidate = asPartPath(`${prefix}${index}${suffix}`);
|
|
5446
|
+
if (!used.has(candidate)) return candidate;
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
function relationshipPartOverrides(source, partPaths) {
|
|
5450
|
+
if (source.packageGraph.contentTypes.defaults.some(
|
|
5451
|
+
(entry) => entry.extension === "rels" && entry.contentType === RELS_CONTENT_TYPE
|
|
5452
|
+
)) {
|
|
5453
|
+
return [];
|
|
5454
|
+
}
|
|
5455
|
+
const existingOverrides = new Set(
|
|
5456
|
+
source.packageGraph.contentTypes.overrides.map((override) => override.partName)
|
|
5457
|
+
);
|
|
5458
|
+
return partPaths.filter((partPath) => !existingOverrides.has(partPath)).map((partName) => ({ partName, contentType: RELS_CONTENT_TYPE }));
|
|
5459
|
+
}
|
|
5460
|
+
function topologyEditPartPaths(edit) {
|
|
5461
|
+
switch (edit.kind) {
|
|
5462
|
+
case "addEmptySlideFromLayout":
|
|
5463
|
+
return [edit.layoutPartPath, edit.newSlidePartPath];
|
|
5464
|
+
case "duplicateSlide":
|
|
5465
|
+
return [edit.sourceSlidePartPath, edit.newSlidePartPath];
|
|
5466
|
+
case "deleteSlide":
|
|
5467
|
+
return [edit.slidePartPath];
|
|
5468
|
+
case "addTextBox":
|
|
5469
|
+
case "addConnector":
|
|
5470
|
+
return [edit.slidePartPath];
|
|
5471
|
+
case "replaceTextRunPlainText":
|
|
5472
|
+
case "updateTextRunProperties":
|
|
5473
|
+
case "replaceParagraphPlainText":
|
|
5474
|
+
case "updateShapeTransform":
|
|
5475
|
+
case "deleteShape":
|
|
5476
|
+
case "replaceImage":
|
|
5477
|
+
return [];
|
|
5478
|
+
}
|
|
5479
|
+
}
|
|
5480
|
+
function relativeTarget(sourcePartPath, targetPartPath) {
|
|
5481
|
+
const sourceDir = sourcePartPath.split("/").slice(0, -1);
|
|
5482
|
+
const targetSegments = targetPartPath.split("/");
|
|
5483
|
+
while (sourceDir.length > 0 && targetSegments.length > 0 && sourceDir[0] === targetSegments[0]) {
|
|
5484
|
+
sourceDir.shift();
|
|
5485
|
+
targetSegments.shift();
|
|
5486
|
+
}
|
|
5487
|
+
return [...sourceDir.map(() => ".."), ...targetSegments].join("/");
|
|
5488
|
+
}
|
|
5489
|
+
function withPartPath(slide, partPath) {
|
|
5490
|
+
return {
|
|
5491
|
+
...slide,
|
|
5492
|
+
partPath,
|
|
5493
|
+
handle: { partPath },
|
|
5494
|
+
shapes: slide.shapes.map((shape) => withShapePartPath(shape, partPath))
|
|
5495
|
+
};
|
|
5496
|
+
}
|
|
5497
|
+
function withShapePartPath(shape, partPath) {
|
|
5498
|
+
if (shape.kind === "raw") return shape;
|
|
5499
|
+
const handle = shape.handle === void 0 ? void 0 : { ...shape.handle, partPath };
|
|
5500
|
+
if (shape.kind === "group") {
|
|
5501
|
+
return {
|
|
5502
|
+
...shape,
|
|
5503
|
+
...handle !== void 0 ? { handle } : {},
|
|
5504
|
+
children: shape.children.map((child) => withShapePartPath(child, partPath))
|
|
5505
|
+
};
|
|
5506
|
+
}
|
|
5507
|
+
if (shape.kind !== "shape" || shape.textBody === void 0) {
|
|
5508
|
+
return { ...shape, ...handle !== void 0 ? { handle } : {} };
|
|
5509
|
+
}
|
|
5510
|
+
return {
|
|
5511
|
+
...shape,
|
|
5512
|
+
...handle !== void 0 ? { handle } : {},
|
|
5513
|
+
textBody: {
|
|
5514
|
+
...shape.textBody,
|
|
5515
|
+
paragraphs: shape.textBody.paragraphs.map((paragraph) => ({
|
|
5516
|
+
...paragraph,
|
|
5517
|
+
...paragraph.handle !== void 0 ? { handle: { ...paragraph.handle, partPath } } : {},
|
|
5518
|
+
runs: paragraph.runs.map((run) => ({
|
|
5519
|
+
...run,
|
|
5520
|
+
...run.handle !== void 0 ? { handle: { ...run.handle, partPath } } : {}
|
|
5521
|
+
}))
|
|
5522
|
+
}))
|
|
5523
|
+
}
|
|
5524
|
+
};
|
|
5525
|
+
}
|
|
5526
|
+
function cloneJson(value) {
|
|
5527
|
+
return structuredClone(value);
|
|
5528
|
+
}
|
|
5529
|
+
function copyBytes(bytes) {
|
|
5530
|
+
return new Uint8Array(bytes);
|
|
5531
|
+
}
|
|
5532
|
+
function insertAtReadonly(items, index, item) {
|
|
5533
|
+
return [...items.slice(0, index), item, ...items.slice(index)];
|
|
5534
|
+
}
|
|
5535
|
+
function hasDirtyEditForPart(edits, partPath) {
|
|
5536
|
+
return edits.some((edit) => {
|
|
5537
|
+
switch (edit.kind) {
|
|
5538
|
+
case "replaceTextRunPlainText":
|
|
5539
|
+
case "updateTextRunProperties":
|
|
5540
|
+
case "replaceParagraphPlainText":
|
|
5541
|
+
case "updateShapeTransform":
|
|
5542
|
+
case "deleteShape":
|
|
5543
|
+
return edit.handle.partPath === partPath;
|
|
5544
|
+
case "replaceImage":
|
|
5545
|
+
return false;
|
|
5546
|
+
case "addTextBox":
|
|
5547
|
+
case "addConnector":
|
|
5548
|
+
return edit.slidePartPath === partPath;
|
|
5549
|
+
case "addEmptySlideFromLayout":
|
|
5550
|
+
case "duplicateSlide":
|
|
5551
|
+
case "deleteSlide":
|
|
5552
|
+
return false;
|
|
5553
|
+
}
|
|
5554
|
+
});
|
|
5555
|
+
}
|
|
5556
|
+
function editTargetsShape(edit, handle) {
|
|
5557
|
+
switch (edit.kind) {
|
|
5558
|
+
case "replaceTextRunPlainText":
|
|
5559
|
+
case "updateTextRunProperties":
|
|
5560
|
+
return edit.handle.partPath === handle.partPath && textRunShapeId(edit.handle) === handle.nodeId;
|
|
5561
|
+
case "replaceParagraphPlainText":
|
|
5562
|
+
return edit.handle.partPath === handle.partPath && paragraphShapeId(edit.handle) === handle.nodeId;
|
|
5563
|
+
case "updateShapeTransform":
|
|
5564
|
+
case "deleteShape":
|
|
5565
|
+
return sourceHandlesEqual(edit.handle, handle);
|
|
5566
|
+
case "replaceImage":
|
|
5567
|
+
return sourceHandlesEqual(edit.handle, handle);
|
|
5568
|
+
case "addTextBox":
|
|
5569
|
+
case "addConnector":
|
|
5570
|
+
return edit.slidePartPath === handle.partPath && edit.shapeId === String(handle.nodeId);
|
|
5571
|
+
case "addEmptySlideFromLayout":
|
|
5572
|
+
case "duplicateSlide":
|
|
5573
|
+
case "deleteSlide":
|
|
5574
|
+
return false;
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
function textRunShapeId(handle) {
|
|
5578
|
+
return /^text:shape:([^:]+):p:\d+:r:\d+$/.exec(String(handle.nodeId ?? ""))?.[1];
|
|
5579
|
+
}
|
|
5580
|
+
function paragraphShapeId(handle) {
|
|
5581
|
+
return /^text:shape:([^:]+):p:\d+$/.exec(String(handle.nodeId ?? ""))?.[1];
|
|
5582
|
+
}
|
|
5583
|
+
function editIsInvalidatedByDeletedParts(edit, partPaths) {
|
|
5584
|
+
switch (edit.kind) {
|
|
5585
|
+
case "replaceTextRunPlainText":
|
|
5586
|
+
case "updateTextRunProperties":
|
|
5587
|
+
case "replaceParagraphPlainText":
|
|
5588
|
+
case "updateShapeTransform":
|
|
5589
|
+
case "replaceImage":
|
|
5590
|
+
return partPaths.has(edit.handle.partPath);
|
|
5591
|
+
case "addTextBox":
|
|
5592
|
+
case "addConnector":
|
|
5593
|
+
return partPaths.has(edit.slidePartPath);
|
|
5594
|
+
case "addEmptySlideFromLayout":
|
|
5595
|
+
return partPaths.has(edit.newSlidePartPath);
|
|
5596
|
+
case "deleteShape":
|
|
5597
|
+
return partPaths.has(edit.handle.partPath);
|
|
5598
|
+
case "duplicateSlide":
|
|
5599
|
+
return partPaths.has(edit.newSlidePartPath);
|
|
5600
|
+
case "deleteSlide":
|
|
5601
|
+
return partPaths.has(edit.slidePartPath);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
5604
|
+
function escapeRegExp(value) {
|
|
5605
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5606
|
+
}
|
|
4260
5607
|
function asEmu2(value) {
|
|
4261
5608
|
return unsafeBrandAssertion2(value);
|
|
4262
5609
|
}
|
|
@@ -4866,14 +6213,14 @@ function parseLineJoin(ln) {
|
|
|
4866
6213
|
}
|
|
4867
6214
|
function parseArrowEndpoint(node) {
|
|
4868
6215
|
if (!node) return void 0;
|
|
4869
|
-
const type = parseEnumValue(getAttr(node, "type"),
|
|
6216
|
+
const type = parseEnumValue(getAttr(node, "type"), ARROW_TYPES2);
|
|
4870
6217
|
if (type === void 0) return void 0;
|
|
4871
6218
|
const width = getAttr(node, "w") ?? "med";
|
|
4872
6219
|
const length = getAttr(node, "len") ?? "med";
|
|
4873
6220
|
return {
|
|
4874
6221
|
type,
|
|
4875
|
-
width: parseEnumValueWithDefault(width,
|
|
4876
|
-
length: parseEnumValueWithDefault(length,
|
|
6222
|
+
width: parseEnumValueWithDefault(width, ARROW_SIZES2, "med"),
|
|
6223
|
+
length: parseEnumValueWithDefault(length, ARROW_SIZES2, "med")
|
|
4877
6224
|
};
|
|
4878
6225
|
}
|
|
4879
6226
|
var DASH_STYLES = /* @__PURE__ */ new Set([
|
|
@@ -4886,14 +6233,14 @@ var DASH_STYLES = /* @__PURE__ */ new Set([
|
|
|
4886
6233
|
"sysDash",
|
|
4887
6234
|
"sysDot"
|
|
4888
6235
|
]);
|
|
4889
|
-
var
|
|
6236
|
+
var ARROW_TYPES2 = /* @__PURE__ */ new Set([
|
|
4890
6237
|
"triangle",
|
|
4891
6238
|
"stealth",
|
|
4892
6239
|
"diamond",
|
|
4893
6240
|
"oval",
|
|
4894
6241
|
"arrow"
|
|
4895
6242
|
]);
|
|
4896
|
-
var
|
|
6243
|
+
var ARROW_SIZES2 = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
|
|
4897
6244
|
function parseCustomGeometry(custGeom, orderedCustGeom) {
|
|
4898
6245
|
const pathLst = getChild(custGeom, "pathLst");
|
|
4899
6246
|
const paths = getChildArray(pathLst, "path");
|
|
@@ -5832,8 +7179,10 @@ function parseShape(sp, partPath, nextId, orderingSlot, orderedNode) {
|
|
|
5832
7179
|
function parseConnector(cxnSp, partPath, nextId, orderingSlot, orderedNode) {
|
|
5833
7180
|
const nvCxnSpPr = getChild(cxnSp, "nvCxnSpPr");
|
|
5834
7181
|
const cNvPr = getChild(nvCxnSpPr, "cNvPr");
|
|
7182
|
+
const cNvCxnSpPr = getChild(nvCxnSpPr, "cNvCxnSpPr");
|
|
5835
7183
|
const nodeId = sourceNodeId(cNvPr);
|
|
5836
7184
|
const name = getAttr(cNvPr, "name");
|
|
7185
|
+
const connection = parseConnectorConnection(cNvCxnSpPr);
|
|
5837
7186
|
const spPr = getChild(cxnSp, "spPr");
|
|
5838
7187
|
const transform = parseTransform(spPr);
|
|
5839
7188
|
const geometry = parseGeometry(spPr, orderedNestedChildChildren(orderedNode, "cxnSp", "spPr"));
|
|
@@ -5849,6 +7198,7 @@ function parseConnector(cxnSp, partPath, nextId, orderingSlot, orderedNode) {
|
|
|
5849
7198
|
kind: "connector",
|
|
5850
7199
|
...nodeId !== void 0 ? { nodeId } : {},
|
|
5851
7200
|
...name !== void 0 ? { name } : {},
|
|
7201
|
+
...connection !== void 0 ? { connection } : {},
|
|
5852
7202
|
...transform !== void 0 ? { transform } : {},
|
|
5853
7203
|
...geometry !== void 0 ? { geometry } : {},
|
|
5854
7204
|
...outline !== void 0 ? { outline } : {},
|
|
@@ -5858,6 +7208,23 @@ function parseConnector(cxnSp, partPath, nextId, orderingSlot, orderedNode) {
|
|
|
5858
7208
|
...rawSidecars.length > 0 ? { rawSidecars } : {}
|
|
5859
7209
|
};
|
|
5860
7210
|
}
|
|
7211
|
+
function parseConnectorConnection(cNvCxnSpPr) {
|
|
7212
|
+
const start = parseConnectorConnectionEndpoint(getChild(cNvCxnSpPr, "stCxn"));
|
|
7213
|
+
const end = parseConnectorConnectionEndpoint(getChild(cNvCxnSpPr, "endCxn"));
|
|
7214
|
+
return start !== void 0 || end !== void 0 ? {
|
|
7215
|
+
...start !== void 0 ? { start } : {},
|
|
7216
|
+
...end !== void 0 ? { end } : {}
|
|
7217
|
+
} : void 0;
|
|
7218
|
+
}
|
|
7219
|
+
function parseConnectorConnectionEndpoint(node) {
|
|
7220
|
+
const shapeId = getAttr(node, "id");
|
|
7221
|
+
const connectionSiteIndex = numericAttr(node, "idx");
|
|
7222
|
+
if (shapeId === void 0 || connectionSiteIndex === void 0) return void 0;
|
|
7223
|
+
return {
|
|
7224
|
+
shapeId: asSourceNodeId(shapeId),
|
|
7225
|
+
connectionSiteIndex
|
|
7226
|
+
};
|
|
7227
|
+
}
|
|
5861
7228
|
function parseShapeStyle(style) {
|
|
5862
7229
|
if (style === void 0) return void 0;
|
|
5863
7230
|
const fillRef = parseStyleReference(getChild(style, "fillRef"));
|
|
@@ -6725,68 +8092,18 @@ function findMatchingPlaceholder(type, index, shapes) {
|
|
|
6725
8092
|
(shape) => (shape.placeholder?.type ?? "body") === fallbackType && shape.transform !== void 0
|
|
6726
8093
|
);
|
|
6727
8094
|
if (byFallbackWithTransform !== void 0) return byFallbackWithTransform;
|
|
6728
|
-
}
|
|
6729
|
-
const byType = placeholders.find((shape) => (shape.placeholder?.type ?? "body") === type);
|
|
6730
|
-
if (byType !== void 0) return byType;
|
|
6731
|
-
if (fallbackType !== void 0) {
|
|
6732
|
-
return placeholders.find((shape) => (shape.placeholder?.type ?? "body") === fallbackType);
|
|
6733
|
-
}
|
|
6734
|
-
return void 0;
|
|
6735
|
-
}
|
|
6736
|
-
function getPlaceholderFallbackType(type) {
|
|
6737
|
-
if (type === "ctrTitle") return "title";
|
|
6738
|
-
if (type === "subTitle") return "body";
|
|
6739
|
-
return void 0;
|
|
6740
|
-
}
|
|
6741
|
-
var RELS_SUFFIX = ".rels";
|
|
6742
|
-
var RELS_MARKER = "_rels/";
|
|
6743
|
-
var ABSOLUTE_URI_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;
|
|
6744
|
-
function isRelationshipPart(path) {
|
|
6745
|
-
return path.endsWith(RELS_SUFFIX) && path.includes(RELS_MARKER);
|
|
6746
|
-
}
|
|
6747
|
-
function relationshipsPartPath(sourcePartPath) {
|
|
6748
|
-
if (sourcePartPath === "") return "_rels/.rels";
|
|
6749
|
-
const slash = sourcePartPath.lastIndexOf("/");
|
|
6750
|
-
const dir = slash === -1 ? "" : sourcePartPath.slice(0, slash + 1);
|
|
6751
|
-
const file = slash === -1 ? sourcePartPath : sourcePartPath.slice(slash + 1);
|
|
6752
|
-
return `${dir}_rels/${file}.rels`;
|
|
6753
|
-
}
|
|
6754
|
-
function relationshipsSourcePartPath(relsPath) {
|
|
6755
|
-
const idx = relsPath.lastIndexOf(RELS_MARKER);
|
|
6756
|
-
const dir = relsPath.slice(0, idx);
|
|
6757
|
-
const file = relsPath.slice(idx + RELS_MARKER.length);
|
|
6758
|
-
const base = file.endsWith(RELS_SUFFIX) ? file.slice(0, -RELS_SUFFIX.length) : file;
|
|
6759
|
-
return asPartPath(dir + base);
|
|
6760
|
-
}
|
|
6761
|
-
function resolveRelationshipTarget(sourcePartPath, target) {
|
|
6762
|
-
if (ABSOLUTE_URI_PATTERN.test(target)) return target;
|
|
6763
|
-
const combined = target.startsWith("/") ? target.slice(1) : joinPackageRelativeTarget(sourcePartPath, target);
|
|
6764
|
-
return normalizePackagePath(combined);
|
|
6765
|
-
}
|
|
6766
|
-
function resolveInternalRelationshipTarget(sourcePartPath, relationship) {
|
|
6767
|
-
if (relationship.targetMode === "External") return void 0;
|
|
6768
|
-
return asPartPath(resolveRelationshipTarget(sourcePartPath, relationship.target));
|
|
6769
|
-
}
|
|
6770
|
-
function parseRelationshipTargetMode(value) {
|
|
6771
|
-
if (value === "Internal" || value === "External") return value;
|
|
8095
|
+
}
|
|
8096
|
+
const byType = placeholders.find((shape) => (shape.placeholder?.type ?? "body") === type);
|
|
8097
|
+
if (byType !== void 0) return byType;
|
|
8098
|
+
if (fallbackType !== void 0) {
|
|
8099
|
+
return placeholders.find((shape) => (shape.placeholder?.type ?? "body") === fallbackType);
|
|
8100
|
+
}
|
|
6772
8101
|
return void 0;
|
|
6773
8102
|
}
|
|
6774
|
-
function
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
return
|
|
6778
|
-
}
|
|
6779
|
-
function normalizePackagePath(path) {
|
|
6780
|
-
const segments = [];
|
|
6781
|
-
for (const segment of path.split("/")) {
|
|
6782
|
-
if (segment === "" || segment === ".") continue;
|
|
6783
|
-
if (segment === "..") {
|
|
6784
|
-
segments.pop();
|
|
6785
|
-
continue;
|
|
6786
|
-
}
|
|
6787
|
-
segments.push(segment);
|
|
6788
|
-
}
|
|
6789
|
-
return segments.join("/");
|
|
8103
|
+
function getPlaceholderFallbackType(type) {
|
|
8104
|
+
if (type === "ctrTitle") return "title";
|
|
8105
|
+
if (type === "subTitle") return "body";
|
|
8106
|
+
return void 0;
|
|
6790
8107
|
}
|
|
6791
8108
|
function resolveComputedRelationships(source, sourcePartPath) {
|
|
6792
8109
|
const rels = source.packageGraph.relationships.find((entry) => entry.sourcePartPath === sourcePartPath)?.relationships ?? [];
|
|
@@ -6809,7 +8126,7 @@ function resolveComputedRelationships(source, sourcePartPath) {
|
|
|
6809
8126
|
function findMedia(media, partPath) {
|
|
6810
8127
|
return media.find((part) => part.partPath === partPath);
|
|
6811
8128
|
}
|
|
6812
|
-
var
|
|
8129
|
+
var IMAGE_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
6813
8130
|
var CHART_REL_TYPES = /* @__PURE__ */ new Set([
|
|
6814
8131
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart",
|
|
6815
8132
|
"http://purl.oclc.org/ooxml/officeDocument/relationships/chart"
|
|
@@ -7026,7 +8343,7 @@ function computeShapeElement(context, shape, layer, partPath) {
|
|
|
7026
8343
|
}
|
|
7027
8344
|
function computeImageElement(context, image, layer, partPath) {
|
|
7028
8345
|
const relationship = resolveComputedRelationships(context.source, partPath).find(
|
|
7029
|
-
(rel) => rel.id === image.blipRelationshipId && rel.type ===
|
|
8346
|
+
(rel) => rel.id === image.blipRelationshipId && rel.type === IMAGE_REL_TYPE2
|
|
7030
8347
|
);
|
|
7031
8348
|
const effects = image.effects !== void 0 ? computeEffectList(context, image.effects) : void 0;
|
|
7032
8349
|
const blipEffects = image.blipEffects !== void 0 ? computeBlipEffects(context, image.blipEffects) : void 0;
|
|
@@ -7224,7 +8541,7 @@ function computeFill(context, fill, partPath) {
|
|
|
7224
8541
|
};
|
|
7225
8542
|
case "image": {
|
|
7226
8543
|
const relationship = resolveComputedRelationships(context.source, partPath).find(
|
|
7227
|
-
(rel) => rel.id === fill.blipRelationshipId && rel.type ===
|
|
8544
|
+
(rel) => rel.id === fill.blipRelationshipId && rel.type === IMAGE_REL_TYPE2
|
|
7228
8545
|
);
|
|
7229
8546
|
return {
|
|
7230
8547
|
kind: "image",
|
|
@@ -7590,6 +8907,7 @@ function parseSlide(root, partPath, layoutPartPath, nextId, orderedSpTree) {
|
|
|
7590
8907
|
function parseSlideLayout(root, partPath, masterPartPath, nextId, orderedSpTree) {
|
|
7591
8908
|
const cSld = getChild(root, "cSld");
|
|
7592
8909
|
const type = getAttr(root, "type");
|
|
8910
|
+
const show = booleanAttr(root, "show");
|
|
7593
8911
|
const background = parseBackground(getChild(cSld, "bg"), nextId);
|
|
7594
8912
|
const colorMapOverride = parseColorMapOverride(getChild(root, "clrMapOvr"));
|
|
7595
8913
|
const showMasterShapes = booleanAttr(root, "showMasterSp");
|
|
@@ -7597,6 +8915,7 @@ function parseSlideLayout(root, partPath, masterPartPath, nextId, orderedSpTree)
|
|
|
7597
8915
|
partPath,
|
|
7598
8916
|
masterPartPath,
|
|
7599
8917
|
...type !== void 0 ? { type } : {},
|
|
8918
|
+
...show !== void 0 ? { show } : {},
|
|
7600
8919
|
...background !== void 0 ? { background } : {},
|
|
7601
8920
|
...colorMapOverride !== void 0 ? { colorMapOverride } : {},
|
|
7602
8921
|
...showMasterShapes !== void 0 ? { showMasterShapes } : {},
|
|
@@ -7831,8 +9150,8 @@ function isNonEmpty(value) {
|
|
|
7831
9150
|
var CONTENT_TYPES_PART = "[Content_Types].xml";
|
|
7832
9151
|
var PACKAGE_ROOT_PART = "";
|
|
7833
9152
|
var OFFICE_DOCUMENT_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
7834
|
-
var
|
|
7835
|
-
var
|
|
9153
|
+
var SLIDE_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
|
|
9154
|
+
var SLIDE_LAYOUT_REL_TYPE2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
7836
9155
|
var SLIDE_MASTER_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
7837
9156
|
var THEME_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
7838
9157
|
var PRESENTATION_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml";
|
|
@@ -7885,7 +9204,7 @@ function readSlideHierarchy(entries, relationships, presentation, diagnostics) {
|
|
|
7885
9204
|
for (const slidePath of presentation.slidePartPaths) {
|
|
7886
9205
|
const part = parsePartRoot(entries, slidePath, "sld", diagnostics, true);
|
|
7887
9206
|
if (part === void 0) continue;
|
|
7888
|
-
const layoutPath = resolveSingleRel(relationships, slidePath,
|
|
9207
|
+
const layoutPath = resolveSingleRel(relationships, slidePath, SLIDE_LAYOUT_REL_TYPE2);
|
|
7889
9208
|
if (layoutPath === void 0) {
|
|
7890
9209
|
diagnostics.push({
|
|
7891
9210
|
severity: "warning",
|
|
@@ -7923,6 +9242,13 @@ function readSlideHierarchy(entries, relationships, presentation, diagnostics) {
|
|
|
7923
9242
|
)
|
|
7924
9243
|
);
|
|
7925
9244
|
}
|
|
9245
|
+
for (const masterPath of resolveAllRels(
|
|
9246
|
+
relationships,
|
|
9247
|
+
presentation.partPath,
|
|
9248
|
+
SLIDE_MASTER_REL_TYPE
|
|
9249
|
+
)) {
|
|
9250
|
+
masterPaths.add(masterPath);
|
|
9251
|
+
}
|
|
7926
9252
|
const slideMasters = [];
|
|
7927
9253
|
const themePaths = new OrderedPathSet();
|
|
7928
9254
|
for (const masterPath of masterPaths.values()) {
|
|
@@ -7930,7 +9256,7 @@ function readSlideHierarchy(entries, relationships, presentation, diagnostics) {
|
|
|
7930
9256
|
if (part === void 0) continue;
|
|
7931
9257
|
const themePath = resolveSingleRel(relationships, masterPath, THEME_REL_TYPE);
|
|
7932
9258
|
if (themePath !== void 0) themePaths.add(themePath);
|
|
7933
|
-
const masterLayoutPaths = resolveAllRels(relationships, masterPath,
|
|
9259
|
+
const masterLayoutPaths = resolveAllRels(relationships, masterPath, SLIDE_LAYOUT_REL_TYPE2);
|
|
7934
9260
|
slideMasters.push(
|
|
7935
9261
|
parseSlideMaster(
|
|
7936
9262
|
part.root,
|
|
@@ -7942,6 +9268,23 @@ function readSlideHierarchy(entries, relationships, presentation, diagnostics) {
|
|
|
7942
9268
|
)
|
|
7943
9269
|
);
|
|
7944
9270
|
}
|
|
9271
|
+
const readLayoutPaths = new Set(slideLayouts.map((layout) => layout.partPath));
|
|
9272
|
+
for (const layoutPath of slideMasters.flatMap((master) => master.layoutPartPaths)) {
|
|
9273
|
+
if (readLayoutPaths.has(layoutPath)) continue;
|
|
9274
|
+
const part = parsePartRoot(entries, layoutPath, "sldLayout", diagnostics, true);
|
|
9275
|
+
if (part === void 0) continue;
|
|
9276
|
+
const masterPath = resolveSingleRel(relationships, layoutPath, SLIDE_MASTER_REL_TYPE);
|
|
9277
|
+
slideLayouts.push(
|
|
9278
|
+
parseSlideLayout(
|
|
9279
|
+
part.root,
|
|
9280
|
+
layoutPath,
|
|
9281
|
+
masterPath ?? asPartPath(""),
|
|
9282
|
+
createSidecarIdFactory(layoutPath),
|
|
9283
|
+
navigateOrdered(part.orderedRoot, ["cSld", "spTree"])
|
|
9284
|
+
)
|
|
9285
|
+
);
|
|
9286
|
+
readLayoutPaths.add(layoutPath);
|
|
9287
|
+
}
|
|
7945
9288
|
const themes = [];
|
|
7946
9289
|
for (const themePath of themePaths.values()) {
|
|
7947
9290
|
const part = parsePartRoot(entries, themePath, "theme", diagnostics, true);
|
|
@@ -8093,7 +9436,7 @@ function readPresentation(entries, relationships, overrides, diagnostics) {
|
|
|
8093
9436
|
});
|
|
8094
9437
|
continue;
|
|
8095
9438
|
}
|
|
8096
|
-
if (relationship.type !==
|
|
9439
|
+
if (relationship.type !== SLIDE_REL_TYPE2 || relationship.targetMode === "External") {
|
|
8097
9440
|
diagnostics.push({
|
|
8098
9441
|
severity: "warning",
|
|
8099
9442
|
code: "slide-relationship-invalid",
|
|
@@ -8163,7 +9506,7 @@ function stripLeadingSlash(path) {
|
|
|
8163
9506
|
return path.startsWith("/") ? path.slice(1) : path;
|
|
8164
9507
|
}
|
|
8165
9508
|
var CONTENT_TYPES_PART2 = "[Content_Types].xml";
|
|
8166
|
-
var
|
|
9509
|
+
var RELS_CONTENT_TYPE2 = "application/vnd.openxmlformats-package.relationships+xml";
|
|
8167
9510
|
var XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n';
|
|
8168
9511
|
var textEncoder = new TextEncoder();
|
|
8169
9512
|
var textDecoder3 = new TextDecoder();
|
|
@@ -8175,14 +9518,34 @@ var xmlBuilder = new XMLBuilder({
|
|
|
8175
9518
|
});
|
|
8176
9519
|
function writePptx(source) {
|
|
8177
9520
|
const textRunEdits = source.edits?.filter(isTextRunEdit) ?? [];
|
|
9521
|
+
const textRunPropertiesEdits = source.edits?.filter(isTextRunPropertiesEdit) ?? [];
|
|
8178
9522
|
const paragraphTextEdits = source.edits?.filter(isParagraphTextEdit) ?? [];
|
|
8179
9523
|
const shapeTransformEdits = source.edits?.filter(isShapeTransformEdit) ?? [];
|
|
8180
|
-
|
|
8181
|
-
const
|
|
8182
|
-
|
|
8183
|
-
|
|
8184
|
-
|
|
9524
|
+
const addTextBoxEdits = source.edits?.filter(isAddTextBoxEdit) ?? [];
|
|
9525
|
+
const addConnectorEdits = source.edits?.filter(isAddConnectorEdit) ?? [];
|
|
9526
|
+
const deleteShapeEdits = source.edits?.filter(isDeleteShapeEdit) ?? [];
|
|
9527
|
+
const addEmptySlideFromLayoutEdits = source.edits?.filter(isAddEmptySlideFromLayoutEdit) ?? [];
|
|
9528
|
+
const duplicateSlideEdits = source.edits?.filter(isDuplicateSlideEdit) ?? [];
|
|
9529
|
+
const deleteSlideEdits = source.edits?.filter(isDeleteSlideEdit) ?? [];
|
|
9530
|
+
validateEdits(
|
|
9531
|
+
textRunEdits,
|
|
9532
|
+
textRunPropertiesEdits,
|
|
9533
|
+
paragraphTextEdits,
|
|
9534
|
+
shapeTransformEdits,
|
|
9535
|
+
addTextBoxEdits,
|
|
9536
|
+
addConnectorEdits,
|
|
9537
|
+
deleteShapeEdits
|
|
8185
9538
|
);
|
|
9539
|
+
const dirtyPartPaths = /* @__PURE__ */ new Set([
|
|
9540
|
+
...textRunEdits.map((edit) => edit.handle.partPath),
|
|
9541
|
+
...textRunPropertiesEdits.map((edit) => edit.handle.partPath),
|
|
9542
|
+
...paragraphTextEdits.map((edit) => edit.handle.partPath),
|
|
9543
|
+
...shapeTransformEdits.map((edit) => edit.handle.partPath),
|
|
9544
|
+
...deleteShapeEdits.map((edit) => edit.handle.partPath),
|
|
9545
|
+
...addTextBoxEdits.map((edit) => edit.slidePartPath),
|
|
9546
|
+
...addConnectorEdits.map((edit) => edit.slidePartPath)
|
|
9547
|
+
]);
|
|
9548
|
+
const hasSlideTopologyEdits = addEmptySlideFromLayoutEdits.length > 0 || duplicateSlideEdits.length > 0 || deleteSlideEdits.length > 0;
|
|
8186
9549
|
const files = {
|
|
8187
9550
|
[CONTENT_TYPES_PART2]: encodeXml(serializeContentTypes(source.packageGraph.contentTypes))
|
|
8188
9551
|
};
|
|
@@ -8197,6 +9560,7 @@ function writePptx(source) {
|
|
|
8197
9560
|
written.add(media.partPath);
|
|
8198
9561
|
}
|
|
8199
9562
|
for (const rawPart of source.packageGraph.rawParts ?? []) {
|
|
9563
|
+
if (hasSlideTopologyEdits && rawPart.partPath === source.presentation.partPath) continue;
|
|
8200
9564
|
if (dirtyPartPaths.has(rawPart.partPath)) continue;
|
|
8201
9565
|
files[rawPart.partPath] = serializeRawPackagePart(rawPart);
|
|
8202
9566
|
written.add(rawPart.partPath);
|
|
@@ -8206,14 +9570,25 @@ function writePptx(source) {
|
|
|
8206
9570
|
source,
|
|
8207
9571
|
partPath,
|
|
8208
9572
|
textRunEdits,
|
|
9573
|
+
textRunPropertiesEdits,
|
|
8209
9574
|
paragraphTextEdits,
|
|
8210
|
-
shapeTransformEdits
|
|
9575
|
+
shapeTransformEdits,
|
|
9576
|
+
addTextBoxEdits,
|
|
9577
|
+
addConnectorEdits,
|
|
9578
|
+
deleteShapeEdits
|
|
8211
9579
|
);
|
|
8212
9580
|
written.add(partPath);
|
|
8213
9581
|
}
|
|
9582
|
+
if (hasSlideTopologyEdits) {
|
|
9583
|
+
files[source.presentation.partPath] = serializePresentationWithSlideTopologyEdits(
|
|
9584
|
+
source,
|
|
9585
|
+
mergeSlideTopologyEdits(source.edits ?? [])
|
|
9586
|
+
);
|
|
9587
|
+
written.add(source.presentation.partPath);
|
|
9588
|
+
}
|
|
8214
9589
|
for (const part of source.packageGraph.parts) {
|
|
8215
9590
|
if (written.has(part.partPath)) continue;
|
|
8216
|
-
if (part.contentType ===
|
|
9591
|
+
if (part.contentType === RELS_CONTENT_TYPE2 || isRelationshipPart(part.partPath)) continue;
|
|
8217
9592
|
throw new Error(
|
|
8218
9593
|
`writePptx: no preserved package material for part '${part.partPath}'; edited part generation is not implemented in the no-edit writer`
|
|
8219
9594
|
);
|
|
@@ -8223,13 +9598,34 @@ function writePptx(source) {
|
|
|
8223
9598
|
function isTextRunEdit(edit) {
|
|
8224
9599
|
return edit.kind === "replaceTextRunPlainText";
|
|
8225
9600
|
}
|
|
9601
|
+
function isTextRunPropertiesEdit(edit) {
|
|
9602
|
+
return edit.kind === "updateTextRunProperties";
|
|
9603
|
+
}
|
|
8226
9604
|
function isParagraphTextEdit(edit) {
|
|
8227
9605
|
return edit.kind === "replaceParagraphPlainText";
|
|
8228
9606
|
}
|
|
8229
9607
|
function isShapeTransformEdit(edit) {
|
|
8230
9608
|
return edit.kind === "updateShapeTransform";
|
|
8231
9609
|
}
|
|
8232
|
-
function
|
|
9610
|
+
function isAddTextBoxEdit(edit) {
|
|
9611
|
+
return edit.kind === "addTextBox";
|
|
9612
|
+
}
|
|
9613
|
+
function isAddConnectorEdit(edit) {
|
|
9614
|
+
return edit.kind === "addConnector";
|
|
9615
|
+
}
|
|
9616
|
+
function isDeleteShapeEdit(edit) {
|
|
9617
|
+
return edit.kind === "deleteShape";
|
|
9618
|
+
}
|
|
9619
|
+
function isAddEmptySlideFromLayoutEdit(edit) {
|
|
9620
|
+
return edit.kind === "addEmptySlideFromLayout";
|
|
9621
|
+
}
|
|
9622
|
+
function isDuplicateSlideEdit(edit) {
|
|
9623
|
+
return edit.kind === "duplicateSlide";
|
|
9624
|
+
}
|
|
9625
|
+
function isDeleteSlideEdit(edit) {
|
|
9626
|
+
return edit.kind === "deleteSlide";
|
|
9627
|
+
}
|
|
9628
|
+
function serializeDirtyXmlPart(source, partPath, textRunEdits, textRunPropertiesEdits, paragraphTextEdits, shapeTransformEdits, addTextBoxEdits, addConnectorEdits, deleteShapeEdits) {
|
|
8233
9629
|
const rawPart = source.packageGraph.rawParts?.find((part) => part.partPath === partPath);
|
|
8234
9630
|
if (rawPart === void 0) {
|
|
8235
9631
|
throw new Error(`writePptx: dirty part '${partPath}' has no preserved raw package material`);
|
|
@@ -8238,17 +9634,142 @@ function serializeDirtyXmlPart(source, partPath, textRunEdits, paragraphTextEdit
|
|
|
8238
9634
|
throw new Error(`writePptx: dirty XML tree part '${partPath}' patching is not implemented`);
|
|
8239
9635
|
}
|
|
8240
9636
|
const root = parseXml(textDecoder3.decode(rawPart.bytes));
|
|
9637
|
+
for (const edit of addTextBoxEdits.filter((edit2) => edit2.slidePartPath === partPath)) {
|
|
9638
|
+
applyAddTextBoxEdit(root, edit);
|
|
9639
|
+
}
|
|
9640
|
+
for (const edit of addConnectorEdits.filter((edit2) => edit2.slidePartPath === partPath)) {
|
|
9641
|
+
applyAddConnectorEdit(root, edit);
|
|
9642
|
+
}
|
|
8241
9643
|
for (const edit of paragraphTextEdits.filter((edit2) => edit2.handle.partPath === partPath)) {
|
|
8242
9644
|
applyParagraphTextEdit(root, edit);
|
|
8243
9645
|
}
|
|
8244
9646
|
for (const edit of textRunEdits.filter((edit2) => edit2.handle.partPath === partPath)) {
|
|
8245
9647
|
applyTextRunEdit(root, edit);
|
|
8246
9648
|
}
|
|
9649
|
+
for (const edit of textRunPropertiesEdits.filter((edit2) => edit2.handle.partPath === partPath)) {
|
|
9650
|
+
applyTextRunPropertiesEdit(root, edit);
|
|
9651
|
+
}
|
|
8247
9652
|
for (const edit of shapeTransformEdits.filter((edit2) => edit2.handle.partPath === partPath)) {
|
|
8248
9653
|
applyShapeTransformEdit(root, edit);
|
|
8249
9654
|
}
|
|
9655
|
+
for (const edit of deleteShapeEdits.filter((edit2) => edit2.handle.partPath === partPath)) {
|
|
9656
|
+
applyDeleteShapeEdit(root, edit);
|
|
9657
|
+
}
|
|
9658
|
+
return encodeXml(XML_DECLARATION + xmlBuilder.build(stripXmlProcessingInstruction(root)));
|
|
9659
|
+
}
|
|
9660
|
+
function mergeSlideTopologyEdits(edits) {
|
|
9661
|
+
return edits.filter(
|
|
9662
|
+
(edit) => edit.kind === "addEmptySlideFromLayout" || edit.kind === "duplicateSlide" || edit.kind === "deleteSlide"
|
|
9663
|
+
);
|
|
9664
|
+
}
|
|
9665
|
+
function serializePresentationWithSlideTopologyEdits(source, edits) {
|
|
9666
|
+
const rawPart = source.packageGraph.rawParts?.find(
|
|
9667
|
+
(part) => part.partPath === source.presentation.partPath
|
|
9668
|
+
);
|
|
9669
|
+
if (rawPart === void 0) {
|
|
9670
|
+
throw new Error(
|
|
9671
|
+
`writePptx: presentation part '${source.presentation.partPath}' has no preserved raw package material`
|
|
9672
|
+
);
|
|
9673
|
+
}
|
|
9674
|
+
if (rawPart.kind !== "binary") {
|
|
9675
|
+
throw new Error("writePptx: presentation XML tree part patching is not implemented");
|
|
9676
|
+
}
|
|
9677
|
+
const root = parseXml(textDecoder3.decode(rawPart.bytes));
|
|
9678
|
+
const presentation = getChild(root, "presentation");
|
|
9679
|
+
if (presentation === void 0) {
|
|
9680
|
+
throw new Error("writePptx: presentation part does not contain p:presentation root");
|
|
9681
|
+
}
|
|
9682
|
+
const sldIdLst = ensureSlideIdList(presentation);
|
|
9683
|
+
for (const edit of edits) {
|
|
9684
|
+
if (edit.kind === "addEmptySlideFromLayout") {
|
|
9685
|
+
appendSlideId(sldIdLst, edit.newRelationshipId);
|
|
9686
|
+
} else if (edit.kind === "duplicateSlide") {
|
|
9687
|
+
insertSlideIdAfter(sldIdLst, edit.sourceRelationshipId, edit.newRelationshipId);
|
|
9688
|
+
} else {
|
|
9689
|
+
removeSlideId(sldIdLst, edit.relationshipId);
|
|
9690
|
+
}
|
|
9691
|
+
}
|
|
8250
9692
|
return encodeXml(XML_DECLARATION + xmlBuilder.build(stripXmlProcessingInstruction(root)));
|
|
8251
9693
|
}
|
|
9694
|
+
function ensureSlideIdList(presentation) {
|
|
9695
|
+
const existing = getChild(presentation, "sldIdLst");
|
|
9696
|
+
if (existing !== void 0) return existing;
|
|
9697
|
+
const key = namespacedChildKey(presentation, "p:sldIdLst", "sldIdLst");
|
|
9698
|
+
const created = {};
|
|
9699
|
+
presentation[key] = created;
|
|
9700
|
+
return created;
|
|
9701
|
+
}
|
|
9702
|
+
function appendSlideId(sldIdLst, newRelationshipId) {
|
|
9703
|
+
const { key, items } = slideIdEntries(sldIdLst);
|
|
9704
|
+
if (items.some((item) => getRelationshipAttr(item) === newRelationshipId)) return;
|
|
9705
|
+
const relationshipAttrKey = items[0] === void 0 ? "@_r:id" : namespacedAttributeKey(items[0], "r:id", "id");
|
|
9706
|
+
const newNode = {
|
|
9707
|
+
"@_id": String(nextSlideNumericId(items)),
|
|
9708
|
+
[relationshipAttrKey]: newRelationshipId
|
|
9709
|
+
};
|
|
9710
|
+
sldIdLst[key] = [...items, newNode];
|
|
9711
|
+
}
|
|
9712
|
+
function insertSlideIdAfter(sldIdLst, sourceRelationshipId, newRelationshipId) {
|
|
9713
|
+
const { key, items } = slideIdEntries(sldIdLst);
|
|
9714
|
+
const sourceIndex = items.findIndex((item) => getRelationshipAttr(item) === sourceRelationshipId);
|
|
9715
|
+
if (sourceIndex === -1) {
|
|
9716
|
+
throw new Error(
|
|
9717
|
+
`writePptx: slide relationship '${sourceRelationshipId}' was not found in p:sldIdLst`
|
|
9718
|
+
);
|
|
9719
|
+
}
|
|
9720
|
+
if (items.some((item) => getRelationshipAttr(item) === newRelationshipId)) return;
|
|
9721
|
+
const newSlideId = String(nextSlideNumericId(items));
|
|
9722
|
+
const relationshipAttrKey = namespacedAttributeKey(items[sourceIndex], "r:id", "id");
|
|
9723
|
+
const newNode = {
|
|
9724
|
+
"@_id": newSlideId,
|
|
9725
|
+
[relationshipAttrKey]: newRelationshipId
|
|
9726
|
+
};
|
|
9727
|
+
sldIdLst[key] = [...items.slice(0, sourceIndex + 1), newNode, ...items.slice(sourceIndex + 1)];
|
|
9728
|
+
}
|
|
9729
|
+
function removeSlideId(sldIdLst, relationshipId) {
|
|
9730
|
+
const { key, items } = slideIdEntries(sldIdLst);
|
|
9731
|
+
sldIdLst[key] = items.filter((item) => getRelationshipAttr(item) !== relationshipId);
|
|
9732
|
+
}
|
|
9733
|
+
function slideIdEntries(sldIdLst) {
|
|
9734
|
+
const key = namespacedChildKey(sldIdLst, "p:sldId", "sldId");
|
|
9735
|
+
const value = sldIdLst[key];
|
|
9736
|
+
if (value === void 0 || value === null) return { key, items: [] };
|
|
9737
|
+
return {
|
|
9738
|
+
key,
|
|
9739
|
+
items: Array.isArray(value) ? unsafeOoxmlBoundaryAssertion(value) : [unsafeOoxmlBoundaryAssertion(value)]
|
|
9740
|
+
};
|
|
9741
|
+
}
|
|
9742
|
+
function nextSlideNumericId(items) {
|
|
9743
|
+
const used = new Set(
|
|
9744
|
+
items.flatMap((item) => {
|
|
9745
|
+
const id = Number(getAttr(item, "id"));
|
|
9746
|
+
return Number.isFinite(id) ? [id] : [];
|
|
9747
|
+
})
|
|
9748
|
+
);
|
|
9749
|
+
const max = Math.max(255, ...used);
|
|
9750
|
+
for (let candidate = max + 1; ; candidate += 1) {
|
|
9751
|
+
if (!used.has(candidate)) return candidate;
|
|
9752
|
+
}
|
|
9753
|
+
}
|
|
9754
|
+
function getRelationshipAttr(node) {
|
|
9755
|
+
return getNamespacedAttr(node, "id");
|
|
9756
|
+
}
|
|
9757
|
+
function namespacedChildKey(node, fallback, local) {
|
|
9758
|
+
for (const key of Object.keys(node)) {
|
|
9759
|
+
if (key.startsWith("@_")) continue;
|
|
9760
|
+
if (localName(key) === local) return key;
|
|
9761
|
+
}
|
|
9762
|
+
return fallback;
|
|
9763
|
+
}
|
|
9764
|
+
function namespacedAttributeKey(node, fallback, local) {
|
|
9765
|
+
for (const key of Object.keys(node)) {
|
|
9766
|
+
if (!key.startsWith("@_")) continue;
|
|
9767
|
+
const name = key.slice(2);
|
|
9768
|
+
const colon = name.indexOf(":");
|
|
9769
|
+
if (colon !== -1 && name.slice(colon + 1) === local) return key;
|
|
9770
|
+
}
|
|
9771
|
+
return `@_${fallback}`;
|
|
9772
|
+
}
|
|
8252
9773
|
function stripXmlProcessingInstruction(root) {
|
|
8253
9774
|
const stripped = { ...root };
|
|
8254
9775
|
delete stripped["?xml"];
|
|
@@ -8270,6 +9791,37 @@ function applyTextRunEdit(root, edit) {
|
|
|
8270
9791
|
}
|
|
8271
9792
|
setChildText(run, "t", edit.text);
|
|
8272
9793
|
}
|
|
9794
|
+
function applyTextRunPropertiesEdit(root, edit) {
|
|
9795
|
+
assertTextRunPropertiesEdit(edit);
|
|
9796
|
+
const run = locateTextRun(root, edit.handle.nodeId);
|
|
9797
|
+
if (run === void 0) {
|
|
9798
|
+
throw new Error(
|
|
9799
|
+
`writePptx: text run properties handle '${edit.handle.nodeId}' no longer matches source XML`
|
|
9800
|
+
);
|
|
9801
|
+
}
|
|
9802
|
+
const set = edit.set ?? {};
|
|
9803
|
+
const hasSet = hasTextRunPropertiesSetValues(set);
|
|
9804
|
+
const existingRunProperties = getChild(run, "rPr");
|
|
9805
|
+
if (existingRunProperties === void 0 && !hasSet) return;
|
|
9806
|
+
const rPr = existingRunProperties ?? ensureRunProperties(run);
|
|
9807
|
+
let cleared = false;
|
|
9808
|
+
for (const property of edit.clear ?? []) {
|
|
9809
|
+
cleared = clearRunProperty(rPr, property) || cleared;
|
|
9810
|
+
}
|
|
9811
|
+
if (set.bold !== void 0) rPr["@_b"] = booleanOoxmlValue(set.bold);
|
|
9812
|
+
if (set.italic !== void 0) rPr["@_i"] = booleanOoxmlValue(set.italic);
|
|
9813
|
+
if (set.underline !== void 0) rPr["@_u"] = set.underline ? "sng" : "none";
|
|
9814
|
+
if (set.fontSize !== void 0) rPr["@_sz"] = String(Math.round(set.fontSize * 100));
|
|
9815
|
+
if (set.typeface !== void 0) ensureChild(rPr, "latin")["@_typeface"] = set.typeface;
|
|
9816
|
+
if (set.color !== void 0) {
|
|
9817
|
+
replaceChild(rPr, "solidFill", {
|
|
9818
|
+
"a:srgbClr": {
|
|
9819
|
+
"@_val": set.color.hex.toUpperCase()
|
|
9820
|
+
}
|
|
9821
|
+
});
|
|
9822
|
+
}
|
|
9823
|
+
if (!hasSet && cleared && xmlNodeIsEmpty(rPr)) deleteChild(run, "rPr");
|
|
9824
|
+
}
|
|
8273
9825
|
function applyParagraphTextEdit(root, edit) {
|
|
8274
9826
|
const locator = parseParagraphLocator(edit.handle.nodeId);
|
|
8275
9827
|
const slide = getChild(root, "sld");
|
|
@@ -8285,6 +9837,16 @@ function applyParagraphTextEdit(root, edit) {
|
|
|
8285
9837
|
}
|
|
8286
9838
|
replaceParagraphRunsWithSingleTextRun(paragraph, edit.text);
|
|
8287
9839
|
}
|
|
9840
|
+
function locateTextRun(root, nodeId) {
|
|
9841
|
+
const locator = parseTextRunLocator(nodeId);
|
|
9842
|
+
const slide = getChild(root, "sld");
|
|
9843
|
+
const cSld = getChild(slide, "cSld");
|
|
9844
|
+
const spTree = getChild(cSld, "spTree");
|
|
9845
|
+
const shape = locateShape(spTree, locator);
|
|
9846
|
+
const paragraphs = getChildArray(getChild(shape, "txBody"), "p");
|
|
9847
|
+
const paragraph = paragraphs[locator.paragraphIndex];
|
|
9848
|
+
return getChildArray(paragraph, "r")[locator.runIndex];
|
|
9849
|
+
}
|
|
8288
9850
|
function applyShapeTransformEdit(root, edit) {
|
|
8289
9851
|
const locator = parseShapeLocator(edit.handle);
|
|
8290
9852
|
const slide = getChild(root, "sld");
|
|
@@ -8309,6 +9871,147 @@ function applyShapeTransformEdit(root, edit) {
|
|
|
8309
9871
|
ext["@_cx"] = String(edit.width);
|
|
8310
9872
|
ext["@_cy"] = String(edit.height);
|
|
8311
9873
|
}
|
|
9874
|
+
function applyAddTextBoxEdit(root, edit) {
|
|
9875
|
+
const slide = getChild(root, "sld");
|
|
9876
|
+
const cSld = getChild(slide, "cSld");
|
|
9877
|
+
const spTree = getChild(cSld, "spTree");
|
|
9878
|
+
if (spTree === void 0) {
|
|
9879
|
+
throw new Error(`writePptx: slide '${edit.slidePartPath}' has no spTree`);
|
|
9880
|
+
}
|
|
9881
|
+
if (locateShapeTreeNode(spTree, { nodeId: edit.shapeId }) !== void 0) {
|
|
9882
|
+
throw new Error(`writePptx: shape id '${edit.shapeId}' already exists in source XML`);
|
|
9883
|
+
}
|
|
9884
|
+
appendChild(spTree, "p:sp", createTextBoxXml(edit));
|
|
9885
|
+
}
|
|
9886
|
+
function applyAddConnectorEdit(root, edit) {
|
|
9887
|
+
const slide = getChild(root, "sld");
|
|
9888
|
+
const cSld = getChild(slide, "cSld");
|
|
9889
|
+
const spTree = getChild(cSld, "spTree");
|
|
9890
|
+
if (spTree === void 0) {
|
|
9891
|
+
throw new Error(`writePptx: slide '${edit.slidePartPath}' has no spTree`);
|
|
9892
|
+
}
|
|
9893
|
+
if (locateShapeTreeNode(spTree, { nodeId: edit.shapeId }) !== void 0) {
|
|
9894
|
+
throw new Error(`writePptx: shape id '${edit.shapeId}' already exists in source XML`);
|
|
9895
|
+
}
|
|
9896
|
+
if (locateShapeTreeNode(spTree, { nodeId: edit.startShapeId }) === void 0) {
|
|
9897
|
+
throw new Error(`writePptx: connector start shape '${edit.startShapeId}' was not found`);
|
|
9898
|
+
}
|
|
9899
|
+
if (locateShapeTreeNode(spTree, { nodeId: edit.endShapeId }) === void 0) {
|
|
9900
|
+
throw new Error(`writePptx: connector end shape '${edit.endShapeId}' was not found`);
|
|
9901
|
+
}
|
|
9902
|
+
appendChild(spTree, "p:cxnSp", createConnectorXml(edit));
|
|
9903
|
+
}
|
|
9904
|
+
function applyDeleteShapeEdit(root, edit) {
|
|
9905
|
+
const locator = parseShapeLocator(edit.handle);
|
|
9906
|
+
const slide = getChild(root, "sld");
|
|
9907
|
+
const cSld = getChild(slide, "cSld");
|
|
9908
|
+
const spTree = getChild(cSld, "spTree");
|
|
9909
|
+
if (!deleteShapeXml(spTree, locator.nodeId)) {
|
|
9910
|
+
throw new Error(`writePptx: shape delete handle '${locator.nodeId}' no longer matches p:sp`);
|
|
9911
|
+
}
|
|
9912
|
+
}
|
|
9913
|
+
function createTextBoxXml(edit) {
|
|
9914
|
+
return {
|
|
9915
|
+
"p:nvSpPr": {
|
|
9916
|
+
"p:cNvPr": {
|
|
9917
|
+
"@_id": edit.shapeId,
|
|
9918
|
+
"@_name": edit.name
|
|
9919
|
+
},
|
|
9920
|
+
"p:cNvSpPr": {
|
|
9921
|
+
"@_txBox": "1"
|
|
9922
|
+
},
|
|
9923
|
+
"p:nvPr": {}
|
|
9924
|
+
},
|
|
9925
|
+
"p:spPr": {
|
|
9926
|
+
"a:xfrm": {
|
|
9927
|
+
"a:off": {
|
|
9928
|
+
"@_x": String(edit.offsetX),
|
|
9929
|
+
"@_y": String(edit.offsetY)
|
|
9930
|
+
},
|
|
9931
|
+
"a:ext": {
|
|
9932
|
+
"@_cx": String(edit.width),
|
|
9933
|
+
"@_cy": String(edit.height)
|
|
9934
|
+
}
|
|
9935
|
+
},
|
|
9936
|
+
"a:prstGeom": {
|
|
9937
|
+
"@_prst": "rect",
|
|
9938
|
+
"a:avLst": {}
|
|
9939
|
+
},
|
|
9940
|
+
"a:noFill": {},
|
|
9941
|
+
"a:ln": {
|
|
9942
|
+
"a:noFill": {}
|
|
9943
|
+
}
|
|
9944
|
+
},
|
|
9945
|
+
"p:txBody": {
|
|
9946
|
+
"a:bodyPr": {
|
|
9947
|
+
"@_wrap": "square"
|
|
9948
|
+
},
|
|
9949
|
+
"a:lstStyle": {},
|
|
9950
|
+
"a:p": {
|
|
9951
|
+
"a:r": {
|
|
9952
|
+
"a:t": textElementValue(void 0, edit.text)
|
|
9953
|
+
},
|
|
9954
|
+
"a:endParaRPr": {}
|
|
9955
|
+
}
|
|
9956
|
+
}
|
|
9957
|
+
};
|
|
9958
|
+
}
|
|
9959
|
+
function createConnectorXml(edit) {
|
|
9960
|
+
return {
|
|
9961
|
+
"p:nvCxnSpPr": {
|
|
9962
|
+
"p:cNvPr": {
|
|
9963
|
+
"@_id": edit.shapeId,
|
|
9964
|
+
"@_name": edit.name
|
|
9965
|
+
},
|
|
9966
|
+
"p:cNvCxnSpPr": {
|
|
9967
|
+
"a:stCxn": {
|
|
9968
|
+
"@_id": edit.startShapeId,
|
|
9969
|
+
"@_idx": String(edit.startConnectionSiteIndex)
|
|
9970
|
+
},
|
|
9971
|
+
"a:endCxn": {
|
|
9972
|
+
"@_id": edit.endShapeId,
|
|
9973
|
+
"@_idx": String(edit.endConnectionSiteIndex)
|
|
9974
|
+
}
|
|
9975
|
+
},
|
|
9976
|
+
"p:nvPr": {}
|
|
9977
|
+
},
|
|
9978
|
+
"p:spPr": {
|
|
9979
|
+
"a:xfrm": {
|
|
9980
|
+
"a:off": {
|
|
9981
|
+
"@_x": String(edit.offsetX),
|
|
9982
|
+
"@_y": String(edit.offsetY)
|
|
9983
|
+
},
|
|
9984
|
+
"a:ext": {
|
|
9985
|
+
"@_cx": String(edit.width),
|
|
9986
|
+
"@_cy": String(edit.height)
|
|
9987
|
+
}
|
|
9988
|
+
},
|
|
9989
|
+
"a:prstGeom": {
|
|
9990
|
+
"@_prst": edit.preset,
|
|
9991
|
+
"a:avLst": {}
|
|
9992
|
+
},
|
|
9993
|
+
"a:ln": createConnectorLineXml(edit)
|
|
9994
|
+
}
|
|
9995
|
+
};
|
|
9996
|
+
}
|
|
9997
|
+
function createConnectorLineXml(edit) {
|
|
9998
|
+
return {
|
|
9999
|
+
"a:solidFill": {
|
|
10000
|
+
"a:srgbClr": {
|
|
10001
|
+
"@_val": "000000"
|
|
10002
|
+
}
|
|
10003
|
+
},
|
|
10004
|
+
...edit.outline?.headEnd !== void 0 ? { "a:headEnd": createArrowEndpointXml(edit.outline.headEnd) } : {},
|
|
10005
|
+
...edit.outline?.tailEnd !== void 0 ? { "a:tailEnd": createArrowEndpointXml(edit.outline.tailEnd) } : {}
|
|
10006
|
+
};
|
|
10007
|
+
}
|
|
10008
|
+
function createArrowEndpointXml(endpoint) {
|
|
10009
|
+
return {
|
|
10010
|
+
"@_type": endpoint.type,
|
|
10011
|
+
"@_w": endpoint.width,
|
|
10012
|
+
"@_len": endpoint.length
|
|
10013
|
+
};
|
|
10014
|
+
}
|
|
8312
10015
|
function parseShapeLocator(handle) {
|
|
8313
10016
|
if (handle.nodeId !== void 0) return { nodeId: String(handle.nodeId) };
|
|
8314
10017
|
throw new Error("writePptx: shape transform edit requires nodeId in handle");
|
|
@@ -8375,6 +10078,22 @@ function locateShapeTreeNode(spTree, locator) {
|
|
|
8375
10078
|
}
|
|
8376
10079
|
return void 0;
|
|
8377
10080
|
}
|
|
10081
|
+
function deleteShapeXml(spTree, nodeId) {
|
|
10082
|
+
if (spTree === void 0) return false;
|
|
10083
|
+
const entry = Object.entries(spTree).find(
|
|
10084
|
+
([key2]) => !key2.startsWith("@_") && localName(key2) === "sp"
|
|
10085
|
+
);
|
|
10086
|
+
if (entry === void 0) return false;
|
|
10087
|
+
const [key, value] = entry;
|
|
10088
|
+
const shapes = Array.isArray(value) ? unsafeOoxmlBoundaryAssertion(value) : [value];
|
|
10089
|
+
const nextShapes = shapes.filter(
|
|
10090
|
+
(shape) => getShapeTreeNodeId(unsafeOoxmlBoundaryAssertion(shape)) !== nodeId
|
|
10091
|
+
);
|
|
10092
|
+
if (nextShapes.length === shapes.length) return false;
|
|
10093
|
+
if (nextShapes.length === 0) delete spTree[key];
|
|
10094
|
+
else spTree[key] = Array.isArray(value) ? nextShapes : nextShapes[0];
|
|
10095
|
+
return true;
|
|
10096
|
+
}
|
|
8378
10097
|
function getShapeByOrderingSlot(spTree, orderingSlot) {
|
|
8379
10098
|
if (!spTree) return void 0;
|
|
8380
10099
|
let currentSlot = 0;
|
|
@@ -8415,6 +10134,111 @@ function setChildText(node, name, text) {
|
|
|
8415
10134
|
}
|
|
8416
10135
|
node[`a:${name}`] = textRequiresPreserve(text) ? { "@_xml:space": "preserve", "#text": text } : text;
|
|
8417
10136
|
}
|
|
10137
|
+
function appendChild(node, preferredKey, value) {
|
|
10138
|
+
const local = localName(preferredKey);
|
|
10139
|
+
const existingKey = Object.keys(node).find(
|
|
10140
|
+
(key) => !key.startsWith("@_") && localName(key) === local
|
|
10141
|
+
);
|
|
10142
|
+
if (existingKey === void 0) {
|
|
10143
|
+
node[preferredKey] = [value];
|
|
10144
|
+
return;
|
|
10145
|
+
}
|
|
10146
|
+
const current = node[existingKey];
|
|
10147
|
+
const currentItems = Array.isArray(current) ? unsafeOoxmlBoundaryAssertion(current) : [current];
|
|
10148
|
+
node[existingKey] = [...currentItems, value];
|
|
10149
|
+
}
|
|
10150
|
+
function ensureRunProperties(run) {
|
|
10151
|
+
const existing = getChild(run, "rPr");
|
|
10152
|
+
if (existing !== void 0) return existing;
|
|
10153
|
+
const entries = [];
|
|
10154
|
+
let inserted = false;
|
|
10155
|
+
for (const [key, value] of Object.entries(run)) {
|
|
10156
|
+
if (!inserted && !key.startsWith("@_")) {
|
|
10157
|
+
entries.push(["a:rPr", {}]);
|
|
10158
|
+
inserted = true;
|
|
10159
|
+
}
|
|
10160
|
+
entries.push([key, value]);
|
|
10161
|
+
}
|
|
10162
|
+
if (!inserted) entries.push(["a:rPr", {}]);
|
|
10163
|
+
replaceNodeEntries(run, entries);
|
|
10164
|
+
return getChild(run, "rPr") ?? {};
|
|
10165
|
+
}
|
|
10166
|
+
function clearRunProperty(rPr, property) {
|
|
10167
|
+
switch (property) {
|
|
10168
|
+
case "bold":
|
|
10169
|
+
if (rPr["@_b"] === void 0) return false;
|
|
10170
|
+
delete rPr["@_b"];
|
|
10171
|
+
return true;
|
|
10172
|
+
case "italic":
|
|
10173
|
+
if (rPr["@_i"] === void 0) return false;
|
|
10174
|
+
delete rPr["@_i"];
|
|
10175
|
+
return true;
|
|
10176
|
+
case "underline":
|
|
10177
|
+
if (rPr["@_u"] === void 0) return false;
|
|
10178
|
+
delete rPr["@_u"];
|
|
10179
|
+
return true;
|
|
10180
|
+
case "fontSize":
|
|
10181
|
+
if (rPr["@_sz"] === void 0) return false;
|
|
10182
|
+
delete rPr["@_sz"];
|
|
10183
|
+
return true;
|
|
10184
|
+
case "typeface": {
|
|
10185
|
+
const latin = getChild(rPr, "latin");
|
|
10186
|
+
if (latin?.["@_typeface"] === void 0) return false;
|
|
10187
|
+
delete latin["@_typeface"];
|
|
10188
|
+
if (xmlNodeIsEmpty(latin)) deleteChild(rPr, "latin");
|
|
10189
|
+
return true;
|
|
10190
|
+
}
|
|
10191
|
+
case "color":
|
|
10192
|
+
return deleteChild(rPr, "solidFill");
|
|
10193
|
+
}
|
|
10194
|
+
}
|
|
10195
|
+
function ensureChild(node, name) {
|
|
10196
|
+
const existing = getChild(node, name);
|
|
10197
|
+
if (existing !== void 0) return existing;
|
|
10198
|
+
node[`a:${name}`] = {};
|
|
10199
|
+
return unsafeOoxmlBoundaryAssertion(node[`a:${name}`]);
|
|
10200
|
+
}
|
|
10201
|
+
function replaceChild(node, name, value) {
|
|
10202
|
+
const entries = [];
|
|
10203
|
+
let replaced = false;
|
|
10204
|
+
for (const [key, entryValue] of Object.entries(node)) {
|
|
10205
|
+
if (!key.startsWith("@_") && localName(key) === name) {
|
|
10206
|
+
if (!replaced) entries.push([key, value]);
|
|
10207
|
+
replaced = true;
|
|
10208
|
+
continue;
|
|
10209
|
+
}
|
|
10210
|
+
entries.push([key, entryValue]);
|
|
10211
|
+
}
|
|
10212
|
+
if (!replaced) entries.push([`a:${name}`, value]);
|
|
10213
|
+
replaceNodeEntries(node, entries);
|
|
10214
|
+
}
|
|
10215
|
+
function deleteChild(node, name) {
|
|
10216
|
+
let deleted = false;
|
|
10217
|
+
replaceNodeEntries(
|
|
10218
|
+
node,
|
|
10219
|
+
Object.entries(node).filter(([key]) => {
|
|
10220
|
+
const keep = key.startsWith("@_") || localName(key) !== name;
|
|
10221
|
+
if (!keep) deleted = true;
|
|
10222
|
+
return keep;
|
|
10223
|
+
})
|
|
10224
|
+
);
|
|
10225
|
+
return deleted;
|
|
10226
|
+
}
|
|
10227
|
+
function booleanOoxmlValue(value) {
|
|
10228
|
+
return value ? "1" : "0";
|
|
10229
|
+
}
|
|
10230
|
+
function hasTextRunPropertiesSetValues(properties) {
|
|
10231
|
+
return properties.bold !== void 0 || properties.italic !== void 0 || properties.underline !== void 0 || properties.fontSize !== void 0 || properties.color !== void 0 || properties.typeface !== void 0;
|
|
10232
|
+
}
|
|
10233
|
+
function assertTextRunPropertiesEdit(edit) {
|
|
10234
|
+
const clear = edit.clear ?? [];
|
|
10235
|
+
if (!hasTextRunPropertiesSetValues(edit.set ?? {}) && clear.length === 0) {
|
|
10236
|
+
throw new Error("writePptx: text run properties edit must set or clear at least one property");
|
|
10237
|
+
}
|
|
10238
|
+
}
|
|
10239
|
+
function xmlNodeIsEmpty(node) {
|
|
10240
|
+
return Object.keys(node).length === 0;
|
|
10241
|
+
}
|
|
8418
10242
|
function replaceParagraphRunsWithSingleTextRun(paragraph, text) {
|
|
8419
10243
|
const firstRunProperties = getChild(getFirstRunLikeNode(paragraph), "rPr");
|
|
8420
10244
|
const replacementRun = {
|
|
@@ -8506,7 +10330,15 @@ function textElementValue(existing, text) {
|
|
|
8506
10330
|
function textRequiresPreserve(text) {
|
|
8507
10331
|
return text.startsWith(" ") || text.endsWith(" ");
|
|
8508
10332
|
}
|
|
8509
|
-
function validateEdits(textRunEdits, paragraphTextEdits, shapeTransformEdits) {
|
|
10333
|
+
function validateEdits(textRunEdits, textRunPropertiesEdits, paragraphTextEdits, shapeTransformEdits, addTextBoxEdits, addConnectorEdits, deleteShapeEdits) {
|
|
10334
|
+
const addedShapeKeys = /* @__PURE__ */ new Set();
|
|
10335
|
+
for (const edit of [...addTextBoxEdits, ...addConnectorEdits]) {
|
|
10336
|
+
const key = [edit.slidePartPath, edit.shapeId].join("\0");
|
|
10337
|
+
if (addedShapeKeys.has(key)) {
|
|
10338
|
+
throw new Error(`writePptx: conflicting shape additions for shape id '${edit.shapeId}'`);
|
|
10339
|
+
}
|
|
10340
|
+
addedShapeKeys.add(key);
|
|
10341
|
+
}
|
|
8510
10342
|
const runKeys = /* @__PURE__ */ new Set();
|
|
8511
10343
|
for (const edit of textRunEdits) {
|
|
8512
10344
|
const key = editHandleNodeKey(edit);
|
|
@@ -8533,6 +10365,14 @@ function validateEdits(textRunEdits, paragraphTextEdits, shapeTransformEdits) {
|
|
|
8533
10365
|
);
|
|
8534
10366
|
}
|
|
8535
10367
|
}
|
|
10368
|
+
for (const runPropertiesEdit of textRunPropertiesEdits) {
|
|
10369
|
+
const paragraphKey = textRunParagraphEditKey(runPropertiesEdit);
|
|
10370
|
+
if (paragraphKey !== void 0 && paragraphKeys.has(paragraphKey)) {
|
|
10371
|
+
throw new Error(
|
|
10372
|
+
`writePptx: conflicting text run properties and paragraph edits for handle '${runPropertiesEdit.handle.nodeId}'`
|
|
10373
|
+
);
|
|
10374
|
+
}
|
|
10375
|
+
}
|
|
8536
10376
|
const shapeKeys = /* @__PURE__ */ new Set();
|
|
8537
10377
|
for (const edit of shapeTransformEdits) {
|
|
8538
10378
|
const key = editHandleNodeKey(edit);
|
|
@@ -8543,6 +10383,16 @@ function validateEdits(textRunEdits, paragraphTextEdits, shapeTransformEdits) {
|
|
|
8543
10383
|
}
|
|
8544
10384
|
shapeKeys.add(key);
|
|
8545
10385
|
}
|
|
10386
|
+
const deletedShapeKeys = /* @__PURE__ */ new Set();
|
|
10387
|
+
for (const edit of deleteShapeEdits) {
|
|
10388
|
+
const key = editHandleNodeKey(edit);
|
|
10389
|
+
if (deletedShapeKeys.has(key)) {
|
|
10390
|
+
throw new Error(
|
|
10391
|
+
`writePptx: conflicting shape delete edits for handle '${String(edit.handle.nodeId)}'`
|
|
10392
|
+
);
|
|
10393
|
+
}
|
|
10394
|
+
deletedShapeKeys.add(key);
|
|
10395
|
+
}
|
|
8546
10396
|
}
|
|
8547
10397
|
function editHandleNodeKey(edit) {
|
|
8548
10398
|
return [edit.handle.partPath, edit.handle.nodeId ?? "", edit.handle.relationshipId ?? ""].join(
|
|
@@ -9244,74 +11094,65 @@ function pushAdapterWarning(diagnostics, code, message, slide, sourcePartPath) {
|
|
|
9244
11094
|
|
|
9245
11095
|
// src/svg-converter.ts
|
|
9246
11096
|
async function convertPptxToSvg(input, options, loadSystemFontSetup) {
|
|
11097
|
+
const source = readPptx(input);
|
|
11098
|
+
return renderPptxSourceModelToSvg(source, options, loadSystemFontSetup);
|
|
11099
|
+
}
|
|
11100
|
+
async function renderPptxSourceModelToSvg(source, options, loadSystemFontSetup) {
|
|
9247
11101
|
const textOutput = options?.textOutput ?? "path";
|
|
9248
11102
|
const logLevel = options?.logLevel ?? "off";
|
|
9249
11103
|
const setup = await createOpentypeSetup(options, loadSystemFontSetup);
|
|
9250
|
-
if (setup) {
|
|
9251
|
-
setTextMeasurer(setup.measurer);
|
|
9252
|
-
if (textOutput !== "text") {
|
|
9253
|
-
setTextPathFontResolver(setup.fontResolver);
|
|
9254
|
-
}
|
|
9255
|
-
}
|
|
9256
11104
|
const fontUsageCollector = textOutput === "text" ? new FontUsageCollector() : null;
|
|
9257
|
-
|
|
9258
|
-
|
|
11105
|
+
const scriptFontScheme = findScriptFontScheme(source);
|
|
11106
|
+
const warningLogger = createWarningLogger(logLevel === "off" ? "warn" : logLevel);
|
|
11107
|
+
const context = createRendererContext({
|
|
11108
|
+
...setup !== null ? { textMeasurer: setup.measurer } : {},
|
|
11109
|
+
textPathFontResolver: setup !== null && textOutput !== "text" ? setup.fontResolver : null,
|
|
11110
|
+
fontUsageCollector,
|
|
11111
|
+
fontMapping: createFontMapping(options?.fontMapping),
|
|
11112
|
+
scriptFonts: {
|
|
11113
|
+
majorJpan: scriptFontScheme?.majorJapanese ?? null,
|
|
11114
|
+
minorJpan: scriptFontScheme?.minorJapanese ?? null
|
|
11115
|
+
},
|
|
11116
|
+
warningLogger
|
|
11117
|
+
});
|
|
11118
|
+
if (source.presentation.slidePartPaths.length === 0) {
|
|
11119
|
+
context.warningLogger.warn("presentation.noSlides", "No slides found in the PPTX file");
|
|
9259
11120
|
}
|
|
9260
|
-
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
|
|
9268
|
-
);
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9272
|
-
|
|
9273
|
-
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
for (const slide of adapted.slides) {
|
|
9280
|
-
if (slideSize === void 0) continue;
|
|
9281
|
-
fontUsageCollector?.reset();
|
|
9282
|
-
let svg = renderSlideToSvg(slide, slideSize);
|
|
9283
|
-
if (fontUsageCollector && setup) {
|
|
9284
|
-
const style = await buildFontFaceStyle(fontUsageCollector.getUsages(), setup.fontResolver);
|
|
9285
|
-
if (style) {
|
|
9286
|
-
svg = injectIntoSvgDefs(svg, style);
|
|
9287
|
-
}
|
|
11121
|
+
const computed = createComputedView(source, { slides: options?.slides });
|
|
11122
|
+
const adapted = adaptComputedViewToRendererModel(computed);
|
|
11123
|
+
const slideSize = adapted.slideSize;
|
|
11124
|
+
if (slideSize === void 0 && adapted.slides.length > 0) {
|
|
11125
|
+
throw new Error("Converter requires a computed slide size");
|
|
11126
|
+
}
|
|
11127
|
+
const slides = [];
|
|
11128
|
+
for (const slide of adapted.slides) {
|
|
11129
|
+
if (slideSize === void 0) continue;
|
|
11130
|
+
fontUsageCollector?.reset();
|
|
11131
|
+
let svg = renderSlideToSvg(slide, slideSize, context);
|
|
11132
|
+
if (fontUsageCollector && setup) {
|
|
11133
|
+
const style = await buildFontFaceStyle(
|
|
11134
|
+
fontUsageCollector.getUsages(),
|
|
11135
|
+
setup.fontResolver,
|
|
11136
|
+
context
|
|
11137
|
+
);
|
|
11138
|
+
if (style) {
|
|
11139
|
+
svg = injectIntoSvgDefs(svg, style);
|
|
9288
11140
|
}
|
|
9289
|
-
slides.push({ slideNumber: slide.slideNumber, svg });
|
|
9290
|
-
}
|
|
9291
|
-
const rendererWarningEntries = [...getWarningEntries()];
|
|
9292
|
-
if (logLevel === "off") {
|
|
9293
|
-
initWarningLogger("off");
|
|
9294
|
-
} else {
|
|
9295
|
-
flushWarnings();
|
|
9296
|
-
}
|
|
9297
|
-
const diagnostics = [
|
|
9298
|
-
...normalizeDocumentDiagnostics(source.diagnostics),
|
|
9299
|
-
...collectSmartArtComputedViewDiagnostics(computed),
|
|
9300
|
-
...normalizeRendererAdapterDiagnostics(adapted.diagnostics),
|
|
9301
|
-
...normalizeRendererWarningDiagnostics(rendererWarningEntries)
|
|
9302
|
-
];
|
|
9303
|
-
const supportCoverage = buildSupportCoverage(computed, adapted.slides, diagnostics);
|
|
9304
|
-
return { slides, diagnostics, supportCoverage };
|
|
9305
|
-
} finally {
|
|
9306
|
-
if (logLevel === "off") {
|
|
9307
|
-
initWarningLogger("off");
|
|
9308
11141
|
}
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
11142
|
+
slides.push({ slideNumber: slide.slideNumber, svg });
|
|
11143
|
+
}
|
|
11144
|
+
const rendererWarningEntries = [...context.warningLogger.getWarningEntries()];
|
|
11145
|
+
if (logLevel !== "off") {
|
|
11146
|
+
context.warningLogger.flushWarnings();
|
|
9314
11147
|
}
|
|
11148
|
+
const diagnostics = [
|
|
11149
|
+
...normalizeDocumentDiagnostics(source.diagnostics),
|
|
11150
|
+
...collectSmartArtComputedViewDiagnostics(computed),
|
|
11151
|
+
...normalizeRendererAdapterDiagnostics(adapted.diagnostics),
|
|
11152
|
+
...normalizeRendererWarningDiagnostics(rendererWarningEntries)
|
|
11153
|
+
];
|
|
11154
|
+
const supportCoverage = buildSupportCoverage(computed, adapted.slides, diagnostics);
|
|
11155
|
+
return { slides, diagnostics, supportCoverage };
|
|
9315
11156
|
}
|
|
9316
11157
|
function findScriptFontScheme(source) {
|
|
9317
11158
|
const firstThemePartPath = source.slideMasters.find(
|
|
@@ -9480,150 +11321,29 @@ function shouldLoadSystemFonts(options) {
|
|
|
9480
11321
|
return options?.skipSystemFonts !== true || (options?.fontDirs?.length ?? 0) > 0;
|
|
9481
11322
|
}
|
|
9482
11323
|
|
|
9483
|
-
// src/font/font-collector.ts
|
|
9484
|
-
var DEFAULT_THEME_FONTS = {
|
|
9485
|
-
majorLatin: "Calibri",
|
|
9486
|
-
minorLatin: "Calibri"
|
|
9487
|
-
};
|
|
9488
|
-
function collectUsedFonts(input) {
|
|
9489
|
-
const source = readPptx(input);
|
|
9490
|
-
const defaultFontScheme = findDefaultThemeFontScheme(source);
|
|
9491
|
-
const computed = createComputedView(source);
|
|
9492
|
-
const fonts = /* @__PURE__ */ new Set();
|
|
9493
|
-
collectThemeFonts(defaultFontScheme, fonts);
|
|
9494
|
-
const visitedTemplateParts = /* @__PURE__ */ new Set();
|
|
9495
|
-
for (const slide of computed.slides) {
|
|
9496
|
-
const slideFontScheme = findThemeFontScheme(source, slide.themePartPath) ?? defaultFontScheme;
|
|
9497
|
-
const templateElementsByPart = /* @__PURE__ */ new Map();
|
|
9498
|
-
for (const element of slide.elements) {
|
|
9499
|
-
if (element.sourceLayer === "slide") {
|
|
9500
|
-
collectFontsFromElements([element], fonts, slideFontScheme);
|
|
9501
|
-
continue;
|
|
9502
|
-
}
|
|
9503
|
-
const key = `${element.sourceLayer}:${element.sourcePartPath}`;
|
|
9504
|
-
const elements = templateElementsByPart.get(key);
|
|
9505
|
-
if (elements) {
|
|
9506
|
-
elements.push(element);
|
|
9507
|
-
} else {
|
|
9508
|
-
templateElementsByPart.set(key, [element]);
|
|
9509
|
-
}
|
|
9510
|
-
}
|
|
9511
|
-
for (const [key, elements] of templateElementsByPart) {
|
|
9512
|
-
if (visitedTemplateParts.has(key)) continue;
|
|
9513
|
-
visitedTemplateParts.add(key);
|
|
9514
|
-
collectFontsFromElements(elements, fonts, slideFontScheme);
|
|
9515
|
-
}
|
|
9516
|
-
}
|
|
9517
|
-
return {
|
|
9518
|
-
theme: {
|
|
9519
|
-
majorFont: defaultFontScheme.majorLatin,
|
|
9520
|
-
minorFont: defaultFontScheme.minorLatin,
|
|
9521
|
-
majorFontEa: defaultFontScheme.majorEastAsian ?? null,
|
|
9522
|
-
minorFontEa: defaultFontScheme.minorEastAsian ?? null,
|
|
9523
|
-
majorFontCs: defaultFontScheme.majorComplexScript ?? null,
|
|
9524
|
-
minorFontCs: defaultFontScheme.minorComplexScript ?? null
|
|
9525
|
-
},
|
|
9526
|
-
fonts: [...fonts].sort()
|
|
9527
|
-
};
|
|
9528
|
-
}
|
|
9529
|
-
function findDefaultThemeFontScheme(source) {
|
|
9530
|
-
const firstThemePartPath = source.slideMasters.find(
|
|
9531
|
-
(master) => master.themePartPath !== void 0
|
|
9532
|
-
)?.themePartPath;
|
|
9533
|
-
const scheme = findThemeFontScheme(source, firstThemePartPath) ?? source.themes[0]?.fontScheme;
|
|
9534
|
-
return applyDefaultThemeFonts(scheme);
|
|
9535
|
-
}
|
|
9536
|
-
function findThemeFontScheme(source, themePartPath) {
|
|
9537
|
-
if (themePartPath === void 0) return void 0;
|
|
9538
|
-
const scheme = source.themes.find((theme) => theme.partPath === themePartPath)?.fontScheme;
|
|
9539
|
-
return scheme !== void 0 ? applyDefaultThemeFonts(scheme) : void 0;
|
|
9540
|
-
}
|
|
9541
|
-
function applyDefaultThemeFonts(scheme) {
|
|
9542
|
-
return {
|
|
9543
|
-
...DEFAULT_THEME_FONTS,
|
|
9544
|
-
...scheme
|
|
9545
|
-
};
|
|
9546
|
-
}
|
|
9547
|
-
function collectThemeFonts(fontScheme, fonts) {
|
|
9548
|
-
addFont(fonts, fontScheme.majorLatin, fontScheme);
|
|
9549
|
-
addFont(fonts, fontScheme.minorLatin, fontScheme);
|
|
9550
|
-
addFont(fonts, fontScheme.majorEastAsian, fontScheme);
|
|
9551
|
-
addFont(fonts, fontScheme.minorEastAsian, fontScheme);
|
|
9552
|
-
addFont(fonts, fontScheme.majorComplexScript, fontScheme);
|
|
9553
|
-
addFont(fonts, fontScheme.minorComplexScript, fontScheme);
|
|
9554
|
-
addFont(fonts, fontScheme.majorJapanese, fontScheme);
|
|
9555
|
-
addFont(fonts, fontScheme.minorJapanese, fontScheme);
|
|
9556
|
-
}
|
|
9557
|
-
function collectFontsFromElements(elements, fonts, fontScheme) {
|
|
9558
|
-
for (const el of elements) {
|
|
9559
|
-
switch (el.kind) {
|
|
9560
|
-
case "shape":
|
|
9561
|
-
if (el.textBody) collectFontsFromTextBody(el.textBody, fonts, fontScheme);
|
|
9562
|
-
break;
|
|
9563
|
-
case "group":
|
|
9564
|
-
collectFontsFromElements(el.children, fonts, fontScheme);
|
|
9565
|
-
break;
|
|
9566
|
-
case "table":
|
|
9567
|
-
for (const row of el.table.rows) {
|
|
9568
|
-
for (const cell of row.cells) {
|
|
9569
|
-
if (cell.textBody) collectFontsFromTextBody(cell.textBody, fonts, fontScheme);
|
|
9570
|
-
}
|
|
9571
|
-
}
|
|
9572
|
-
break;
|
|
9573
|
-
case "connector":
|
|
9574
|
-
case "image":
|
|
9575
|
-
case "chart":
|
|
9576
|
-
case "smartArt":
|
|
9577
|
-
case "raw":
|
|
9578
|
-
break;
|
|
9579
|
-
}
|
|
9580
|
-
}
|
|
9581
|
-
}
|
|
9582
|
-
function collectFontsFromTextBody(textBody, fonts, fontScheme) {
|
|
9583
|
-
for (const para of textBody.paragraphs) {
|
|
9584
|
-
addFont(fonts, para.properties?.bulletFont, fontScheme);
|
|
9585
|
-
for (const run of para.runs) {
|
|
9586
|
-
addFont(fonts, run.properties?.typeface, fontScheme);
|
|
9587
|
-
addFont(fonts, run.properties?.typefaceEa, fontScheme);
|
|
9588
|
-
addFont(fonts, run.properties?.typefaceCs, fontScheme);
|
|
9589
|
-
}
|
|
9590
|
-
}
|
|
9591
|
-
}
|
|
9592
|
-
function addFont(fonts, font, fontScheme) {
|
|
9593
|
-
const resolved = resolveThemeFontAlias(font, fontScheme);
|
|
9594
|
-
if (resolved) fonts.add(resolved);
|
|
9595
|
-
}
|
|
9596
|
-
function resolveThemeFontAlias(font, fontScheme) {
|
|
9597
|
-
if (font === void 0) return void 0;
|
|
9598
|
-
switch (font) {
|
|
9599
|
-
case "+mj-lt":
|
|
9600
|
-
return fontScheme.majorLatin;
|
|
9601
|
-
case "+mn-lt":
|
|
9602
|
-
return fontScheme.minorLatin;
|
|
9603
|
-
case "+mj-ea":
|
|
9604
|
-
return fontScheme.majorEastAsian ?? fontScheme.majorJapanese;
|
|
9605
|
-
case "+mn-ea":
|
|
9606
|
-
return fontScheme.minorEastAsian ?? fontScheme.minorJapanese;
|
|
9607
|
-
case "+mj-cs":
|
|
9608
|
-
return fontScheme.majorComplexScript;
|
|
9609
|
-
case "+mn-cs":
|
|
9610
|
-
return fontScheme.minorComplexScript;
|
|
9611
|
-
default:
|
|
9612
|
-
return font.startsWith("+mj-") || font.startsWith("+mn-") ? void 0 : font;
|
|
9613
|
-
}
|
|
9614
|
-
}
|
|
9615
|
-
|
|
9616
11324
|
export {
|
|
9617
11325
|
DEFAULT_OUTPUT_WIDTH,
|
|
9618
11326
|
asPartPath,
|
|
9619
11327
|
asRelationshipId,
|
|
9620
11328
|
asSourceNodeId,
|
|
9621
11329
|
replaceTextRunPlainText,
|
|
11330
|
+
setTextRunProperties,
|
|
11331
|
+
clearTextRunProperties,
|
|
9622
11332
|
replaceParagraphPlainText,
|
|
9623
11333
|
findShapeNodeBySourceHandle,
|
|
9624
11334
|
updateShapeTransform,
|
|
11335
|
+
addTextBox,
|
|
11336
|
+
addConnector,
|
|
11337
|
+
addEmptySlideFromLayout,
|
|
11338
|
+
deleteShape,
|
|
11339
|
+
replaceImageBytes,
|
|
11340
|
+
duplicateSlide,
|
|
11341
|
+
deleteSlide,
|
|
11342
|
+
asEmu2 as asEmu,
|
|
11343
|
+
createComputedView,
|
|
9625
11344
|
readPptx,
|
|
9626
11345
|
writePptx,
|
|
11346
|
+
unsafeBrandAssertion3 as unsafeBrandAssertion,
|
|
9627
11347
|
convertPptxToSvg,
|
|
9628
|
-
|
|
11348
|
+
renderPptxSourceModelToSvg
|
|
9629
11349
|
};
|