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.
@@ -1,30 +1,111 @@
1
1
  import {
2
+ DEFAULT_FONT_MAPPING,
2
3
  createFontMapping,
3
4
  createOpentypeSetupFromBuffers,
4
- debug,
5
- flushWarnings,
5
+ createWarningLogger,
6
+ getActiveWarningLogger,
6
7
  getAscenderRatio,
7
- getCurrentMappedFont,
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-2AYNMYMC.js";
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
- warn(
107
- "font.subsetFailed",
108
- `Failed to subset font "${familyName}": ${e instanceof Error ? e.message : String(e)}`
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 renderBarChart(chart, x, y, w, h) {
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
- debug("chart.bar", "series is empty");
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
- debug("chart.bar", "max value is 0");
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
- debug("chart.bar", "category count is 0");
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
- debug("chart.line", "series is empty");
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
- debug("chart.line", "max value is 0");
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
- debug("chart.line", "category count is 0");
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
- debug("chart.area", "series is empty");
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
- debug("chart.area", "max value is 0");
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
- debug("chart.area", "category count is 0");
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
- debug("chart.pie", "series is empty or has no values");
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
- debug("chart.pie", "total value is 0");
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
- debug("chart.doughnut", "series is empty or has no values");
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
- debug("chart.doughnut", "total value is 0");
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
- debug("chart.scatter", "series is empty");
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
- debug("chart.bubble", "series is empty");
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
- debug("chart.radar", "series is empty");
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
- debug("chart.radar", "max value is 0");
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
- debug("chart.radar", "category count is 0");
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
- debug("chart.stock", `insufficient series count: ${series.length} (need at least 3)`);
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
- debug("chart.stock", "category count is 0");
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
- debug("chart.stock", "max equals min value");
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
- debug("chart.surface", "series is empty");
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
- debug("chart.surface", "column count is 0");
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
- debug("chart.ofPie", "series is empty or has no values");
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
- debug("chart.ofPie", "total value is 0");
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 = getTextMeasurer().measureTextWidth(
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 = getTextMeasurer().measureTextWidth(
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 = getTextMeasurer().measureTextWidth(
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(token, availableWidth, defaultFontSize, fontScale);
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(paragraph.runs, defaultFontSize, fontScale);
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(tokens, safeWidth, defaultFontSize, fontScale);
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 = getTextPathFontResolver();
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
- getFontUsageCollector()?.record(bulletFontChain, bulletText);
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(para.runs, defaultFontSize, fontScale);
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
- getFontUsageCollector()?.record(bulletFontChain, bulletText);
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(para.runs, defaultFontSize, fontScale);
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 = getTextMeasurer().getLineHeightRatio(
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
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 = getCurrentMappedFont(font);
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
- getFontUsageCollector()?.record([props.fontFamily, props.fontFamilyEa], text);
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, getJpanFallbackFont(), props.fontFamily] : [props.fontFamily, props.fontFamilyEa];
3285
- const styles = buildStyleAttrs(props, fontScale, fonts);
3286
- getFontUsageCollector()?.record(fonts, part.text);
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 getTextMeasurer().getLineHeightRatio(
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 getTextMeasurer().getAscenderRatio(
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(paragraphs, defaultFontSize, shouldWrap, textWidth);
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(para, textWidth, scaledDefaultForWrap, fontScale);
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 ? getJpanFallbackFont() : null;
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 += getTextMeasurer().measureTextWidth(
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 = getJpanFallbackFont();
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) : getTextMeasurer().measureTextWidth(
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) : getTextMeasurer().measureTextWidth(
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(line.segments, defaultFontSize, fontScale, fontResolver);
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(para.runs, defaultFontSize, fontScale);
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(runsAsSegments, defaultFontSize, fontScale, fontResolver);
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, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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 createReplacementRunHandle(paragraph) {
4226
- if (paragraph.runs[0]?.handle !== void 0) return paragraph.runs[0].handle;
4227
- if (paragraph.handle?.nodeId === void 0) return void 0;
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"), ARROW_TYPES);
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, ARROW_SIZES, "med"),
4876
- length: parseEnumValueWithDefault(length, ARROW_SIZES, "med")
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 ARROW_TYPES = /* @__PURE__ */ new Set([
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 ARROW_SIZES = /* @__PURE__ */ new Set(["sm", "med", "lg"]);
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 joinPackageRelativeTarget(sourcePartPath, target) {
6775
- const slash = sourcePartPath.lastIndexOf("/");
6776
- const baseDir = slash === -1 ? "" : sourcePartPath.slice(0, slash);
6777
- return baseDir === "" ? target : `${baseDir}/${target}`;
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 IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
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 === IMAGE_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 === IMAGE_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 SLIDE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
7835
- var SLIDE_LAYOUT_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
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, SLIDE_LAYOUT_REL_TYPE);
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, SLIDE_LAYOUT_REL_TYPE);
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 !== SLIDE_REL_TYPE || relationship.targetMode === "External") {
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 RELS_CONTENT_TYPE = "application/vnd.openxmlformats-package.relationships+xml";
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
- validateEdits(textRunEdits, paragraphTextEdits, shapeTransformEdits);
8181
- const dirtyPartPaths = new Set(
8182
- [...textRunEdits, ...paragraphTextEdits, ...shapeTransformEdits].map(
8183
- (edit) => edit.handle.partPath
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 === RELS_CONTENT_TYPE || isRelationshipPart(part.partPath)) continue;
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 serializeDirtyXmlPart(source, partPath, textRunEdits, paragraphTextEdits, shapeTransformEdits) {
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
- if (fontUsageCollector) {
9258
- setFontUsageCollector(fontUsageCollector);
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
- setFontMapping(createFontMapping(options?.fontMapping));
9261
- try {
9262
- initWarningLogger(logLevel === "off" ? "warn" : logLevel);
9263
- const source = readPptx(input);
9264
- const scriptFontScheme = findScriptFontScheme(source);
9265
- setScriptFonts(
9266
- scriptFontScheme?.majorJapanese ?? null,
9267
- scriptFontScheme?.minorJapanese ?? null
9268
- );
9269
- if (source.presentation.slidePartPaths.length === 0) {
9270
- warn("presentation.noSlides", "No slides found in the PPTX file");
9271
- }
9272
- const computed = createComputedView(source, { slides: options?.slides });
9273
- const adapted = adaptComputedViewToRendererModel(computed);
9274
- const slideSize = adapted.slideSize;
9275
- if (slideSize === void 0 && adapted.slides.length > 0) {
9276
- throw new Error("Converter requires a computed slide size");
9277
- }
9278
- const slides = [];
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
- resetTextMeasurer();
9310
- resetTextPathFontResolver();
9311
- resetFontUsageCollector();
9312
- resetFontMapping();
9313
- resetScriptFonts();
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
- collectUsedFonts
11348
+ renderPptxSourceModelToSvg
9629
11349
  };