@json-to-office/core-pptx 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ var init_warn = __esm({
39
39
  UNKNOWN_COLOR: "UNKNOWN_COLOR",
40
40
  GRID_POSITION_CLAMPED: "GRID_POSITION_CLAMPED",
41
41
  TEXT_NO_CONTENT: "TEXT_NO_CONTENT",
42
+ TEXT_OVERLAP_UNPOSITIONED: "TEXT_OVERLAP_UNPOSITIONED",
42
43
  UNKNOWN_PATTERN_PRESET: "UNKNOWN_PATTERN_PRESET",
43
44
  ADVANCED_FILL_FALLBACK: "ADVANCED_FILL_FALLBACK",
44
45
  IMAGE_ZERO_BOX: "IMAGE_ZERO_BOX",
@@ -122,114 +123,6 @@ var init_units = __esm({
122
123
  }
123
124
  });
124
125
 
125
- // src/ir/features.ts
126
- var PPTX_FEATURES, ALL_PPTX_FEATURES;
127
- var init_features = __esm({
128
- "src/ir/features.ts"() {
129
- "use strict";
130
- PPTX_FEATURES = [
131
- /** Slide masters compiled from authored templates. */
132
- "masters",
133
- /** Named placeholder regions declared by a master. */
134
- "placeholders",
135
- /** Multiple independently-formatted runs inside one text body. */
136
- "rich-text",
137
- /**
138
- * Bullet glyphs outside PptxGenJS's single-BMP-character API: astral
139
- * characters and multi-code-point graphemes.
140
- */
141
- "complex-bullet-glyphs",
142
- /** Text bodies at all (a single run, uniform formatting). */
143
- "text",
144
- /** Preset-geometry shapes. */
145
- "shapes",
146
- /** Raster and vector images. */
147
- "images",
148
- /** SVG images specifically — many backends need a raster fallback. */
149
- "svg",
150
- /**
151
- * Drawing only part of a picture: `cover`, `crop`, or a `contain` fit the
152
- * compiler could not resolve into the frame.
153
- *
154
- * A backend without it draws the whole picture into the frame, which is a
155
- * different image — the wrong region at the wrong scale — with nothing to
156
- * show that anything was lost.
157
- */
158
- "image-crop",
159
- /** Masking a picture to rounded corners or a circle. */
160
- "image-rounding",
161
- "tables",
162
- /** Merged table cells (colSpan / rowSpan). */
163
- "table-merged-cells",
164
- /**
165
- * Cell insets on a table.
166
- *
167
- * Separate from the inset on a text body: a reader takes a cell's padding
168
- * from `a:tcPr`, and a backend that can only write text-body insets puts the
169
- * numbers somewhere nothing reads them.
170
- */
171
- "table-insets",
172
- /**
173
- * Rounded table corners.
174
- *
175
- * A separate ability because OOXML has no such property: a backend realises
176
- * it by drawing shapes behind the table, or it cannot realise it at all.
177
- */
178
- "table-rounded-corners",
179
- /**
180
- * Flowing an over-long table onto further slides, header row included.
181
- *
182
- * Pagination is the backend's, not the IR's — the IR describes one table and
183
- * says it may split, so a backend that cannot split has to refuse rather than
184
- * emit a table running off the slide.
185
- */
186
- "table-auto-page",
187
- /** Native OOXML charts with an embedded workbook. */
188
- "charts",
189
- "solid-fills",
190
- "gradient-fills",
191
- "pattern-fills",
192
- "image-fills",
193
- "lines",
194
- "shadows",
195
- /** Slide backgrounds (solid or image). */
196
- "backgrounds",
197
- "speaker-notes",
198
- "hidden-slides",
199
- "transitions",
200
- /** Hyperlinks to an external URL. */
201
- "external-links",
202
- /** Hyperlinks targeting another slide in the same deck. */
203
- "internal-links",
204
- /** A link covering a text body — realisable on the runs inside it. */
205
- "text-hyperlinks",
206
- /** A link on a shape or image as a whole, which runs cannot stand in for. */
207
- "element-hyperlinks",
208
- /** Rotation on a shape, text box or table. */
209
- "rotation",
210
- /**
211
- * Any transform on a picture — rotation or flip.
212
- *
213
- * One feature rather than three: a backend that cannot rotate a picture
214
- * generally cannot flip one either, because the option carrying both is
215
- * simply absent from its picture type.
216
- */
217
- "image-transform",
218
- "flip-horizontal",
219
- "flip-vertical",
220
- /** Grouped elements sharing a transform. */
221
- "groups",
222
- /** Per-run proofing language. */
223
- "proofing-language",
224
- /** Right-to-left reading order. */
225
- "rtl"
226
- ];
227
- ALL_PPTX_FEATURES = new Set(
228
- PPTX_FEATURES
229
- );
230
- }
231
- });
232
-
233
126
  // src/renderers/pptxgenjs/fills.ts
234
127
  function colorXml(color2) {
235
128
  const alpha = color2.transparency !== void 0 ? `<a:alpha val="${Math.round((100 - color2.transparency) * PCT_UNIT)}"/>` : "";
@@ -938,11 +831,6 @@ async function finalizePackage(zip, generatedAt) {
938
831
  await canonicalizeChartIds(zip);
939
832
  await canonicalizePackage(zip, generatedAt);
940
833
  }
941
- async function finalizePackageBuffer(buffer, options = {}) {
942
- const zip = await readPackage(buffer);
943
- await finalizePackage(zip, resolveGeneratedAt(options.generatedAt));
944
- return writePackage(zip);
945
- }
946
834
  function replaceCoreTimestamp(xml, tag, value) {
947
835
  const expression = new RegExp(
948
836
  `(<dcterms:${tag}\\b[^>]*>)[^<]*(</dcterms:${tag}>)`,
@@ -1335,17 +1223,193 @@ var PPTXGENJS_RENDERER_ID, PPTXGENJS_CAPABILITIES;
1335
1223
  var init_pptxgenjs = __esm({
1336
1224
  "src/renderers/pptxgenjs/index.ts"() {
1337
1225
  "use strict";
1338
- init_features();
1339
1226
  init_units();
1340
1227
  init_emit();
1341
1228
  init_packaging();
1342
1229
  PPTXGENJS_RENDERER_ID = "pptxgenjs";
1343
- PPTXGENJS_CAPABILITIES = new Set(
1344
- [...ALL_PPTX_FEATURES].filter(
1345
- (feature) => feature !== "image-fills" && feature !== "transitions" && feature !== "groups" && feature !== "complex-bullet-glyphs"
1346
- )
1230
+ PPTXGENJS_CAPABILITIES = /* @__PURE__ */ new Set([
1231
+ "masters",
1232
+ "placeholders",
1233
+ "rich-text",
1234
+ "text",
1235
+ "shapes",
1236
+ "images",
1237
+ "svg",
1238
+ "image-crop",
1239
+ "image-rounding",
1240
+ "tables",
1241
+ "table-merged-cells",
1242
+ "table-insets",
1243
+ "table-rounded-corners",
1244
+ "table-auto-page",
1245
+ "charts",
1246
+ "chart-bar-style",
1247
+ "chart-pie-style",
1248
+ "chart-line-style",
1249
+ "chart-radar-style",
1250
+ "chart-data-labels",
1251
+ "chart-data-border",
1252
+ "chart-axis-scale",
1253
+ "chart-axis-visibility",
1254
+ "chart-axis-style",
1255
+ "chart-text-style",
1256
+ "solid-fills",
1257
+ "gradient-fills",
1258
+ "pattern-fills",
1259
+ "lines",
1260
+ "shadows",
1261
+ "backgrounds",
1262
+ "speaker-notes",
1263
+ "hidden-slides",
1264
+ "external-links",
1265
+ "internal-links",
1266
+ "text-hyperlinks",
1267
+ "element-hyperlinks",
1268
+ "rotation",
1269
+ "image-transform",
1270
+ "flip-horizontal",
1271
+ "flip-vertical",
1272
+ "proofing-language",
1273
+ "rtl"
1274
+ ]);
1275
+ }
1276
+ });
1277
+
1278
+ // src/renderers/office-open/chartParts.ts
1279
+ import JSZip2 from "jszip";
1280
+ import {
1281
+ CHART_WORKBOOK_CONTENT_TYPE,
1282
+ chartWorkbookParts,
1283
+ chartWorkbookRelsXml,
1284
+ matchChartParts,
1285
+ spliceChartXml
1286
+ } from "@json-to-office/shared/rendering";
1287
+ function spliceInput(element) {
1288
+ return {
1289
+ chartType: element.chartType,
1290
+ series: element.series.map((series) => ({
1291
+ ...series.name !== void 0 ? { name: series.name } : {},
1292
+ labels: series.labels ?? [],
1293
+ values: series.values ?? []
1294
+ })),
1295
+ colors: element.options.colors,
1296
+ ...element.options.barGrouping ? { barGrouping: element.options.barGrouping } : {},
1297
+ // Spliced rather than emitted: see `chartChild`, which cannot pass `axes`
1298
+ // without also inventing the axis ids the plot area references.
1299
+ categoryAxis: axisEdits(element.options.categoryAxis),
1300
+ valueAxis: axisEdits(element.options.valueAxis),
1301
+ // Three the backend has nowhere to put: `ChartSeriesCommon` has no line
1302
+ // width, no data-element outline, and `c:radarStyle` is written from a
1303
+ // literal rather than an option.
1304
+ ...element.options.lineSize !== void 0 ? { lineWidthPoints: element.options.lineSize } : {},
1305
+ ...element.options.dataBorder ? {
1306
+ dataBorder: {
1307
+ widthPoints: element.options.dataBorder.widthPoints,
1308
+ color: element.options.dataBorder.color.hex
1309
+ }
1310
+ } : {},
1311
+ ...element.options.radarStyle ? { radarStyle: element.options.radarStyle } : {},
1312
+ // Fonts: `c:txPr` and `a:defRPr` on four different elements, none of which
1313
+ // the backend exposes an option for.
1314
+ ...textStyle(element.options.titleFont) ? { titleFont: textStyle(element.options.titleFont) } : {},
1315
+ ...textStyle(element.options.legendFont) ? { legendFont: textStyle(element.options.legendFont) } : {},
1316
+ ...textStyle(element.options.dataLabelFont) ? { dataLabelFont: textStyle(element.options.dataLabelFont) } : {}
1317
+ };
1318
+ }
1319
+ function textStyle(font) {
1320
+ if (!font) return void 0;
1321
+ const style = {
1322
+ ...font.fontFamily !== void 0 ? { fontFamily: font.fontFamily } : {},
1323
+ ...font.fontSize !== void 0 ? { fontSize: font.fontSize } : {},
1324
+ ...font.bold !== void 0 ? { bold: font.bold } : {},
1325
+ ...font.color ? { color: font.color.hex } : {}
1326
+ };
1327
+ return Object.keys(style).length > 0 ? style : void 0;
1328
+ }
1329
+ function axisEdits(axis) {
1330
+ const value = axis;
1331
+ return {
1332
+ ...axis.title !== void 0 ? { title: axis.title } : {},
1333
+ ...axis.hidden !== void 0 ? { hidden: axis.hidden } : {},
1334
+ ...axis.showLine !== void 0 ? { lineVisible: axis.showLine } : {},
1335
+ ...axis.labelRotate !== void 0 ? { labelRotation: axis.labelRotate } : {},
1336
+ ...textStyle(axis.labelFont) ? { labelFont: textStyle(axis.labelFont) } : {},
1337
+ ...axis.gridLine ? {
1338
+ gridLine: {
1339
+ ...textStyle(axis.labelFont) ? { labelFont: textStyle(axis.labelFont) } : {},
1340
+ ...axis.gridLine.style !== void 0 ? { style: axis.gridLine.style } : {},
1341
+ ...textStyle(axis.labelFont) ? { labelFont: textStyle(axis.labelFont) } : {},
1342
+ ...axis.gridLine.size !== void 0 ? { size: axis.gridLine.size } : {},
1343
+ ...textStyle(axis.labelFont) ? { labelFont: textStyle(axis.labelFont) } : {},
1344
+ ...axis.gridLine.color ? { color: axis.gridLine.color.hex } : {}
1345
+ }
1346
+ } : {},
1347
+ ...value.minValue !== void 0 ? { min: value.minValue } : {},
1348
+ ...value.maxValue !== void 0 ? { max: value.maxValue } : {},
1349
+ ...value.majorUnit !== void 0 ? { majorUnit: value.majorUnit } : {},
1350
+ ...value.labelFormatCode !== void 0 ? { numberFormat: value.labelFormatCode } : {}
1351
+ };
1352
+ }
1353
+ function declareWorkbookContentType(zip, xml) {
1354
+ if (xml.includes(`Extension="xlsx"`)) return;
1355
+ const patched = xml.replace(
1356
+ '<Default Extension="xml"',
1357
+ `<Default Extension="xlsx" ContentType="${CHART_WORKBOOK_CONTENT_TYPE}"/><Default Extension="xml"`
1358
+ );
1359
+ if (patched === xml) {
1360
+ throw new Error(
1361
+ 'Could not declare the embedded workbook content type: [Content_Types].xml has no `<Default Extension="xml"` to anchor on. The package would ship an .xlsx part no content type covers.'
1347
1362
  );
1348
1363
  }
1364
+ zip.file("[Content_Types].xml", patched, { createFolders: false });
1365
+ }
1366
+ async function workbookBytes(chart) {
1367
+ const book = new JSZip2();
1368
+ for (const [path4, content] of chartWorkbookParts(chart.series)) {
1369
+ book.file(path4, content, { createFolders: false });
1370
+ }
1371
+ return book.generateAsync({
1372
+ type: "uint8array",
1373
+ compression: "DEFLATE",
1374
+ compressionOptions: { level: 6 }
1375
+ });
1376
+ }
1377
+ async function spliceChartParts(zip, charts) {
1378
+ if (charts.length === 0) return;
1379
+ const inputs = charts.map(spliceInput);
1380
+ const parts = [];
1381
+ for (const path4 of Object.keys(zip.files)) {
1382
+ const match = path4.match(/^ppt\/charts\/chart(\d+)\.xml$/);
1383
+ if (!match) continue;
1384
+ parts.push([Number(match[1]), await zip.file(path4).async("string")]);
1385
+ }
1386
+ parts.sort(([a], [b]) => a - b);
1387
+ for (const { ordinal, xml, chart } of matchChartParts(parts, inputs)) {
1388
+ const workbook = workbookName(ordinal);
1389
+ zip.file(`ppt/charts/chart${ordinal}.xml`, spliceChartXml(xml, chart), {
1390
+ createFolders: false
1391
+ });
1392
+ zip.file(`ppt/embeddings/${workbook}`, await workbookBytes(chart), {
1393
+ binary: true,
1394
+ createFolders: false
1395
+ });
1396
+ zip.file(
1397
+ `ppt/charts/_rels/chart${ordinal}.xml.rels`,
1398
+ chartWorkbookRelsXml(workbook),
1399
+ { createFolders: false }
1400
+ );
1401
+ }
1402
+ const contentTypes = zip.file("[Content_Types].xml");
1403
+ if (contentTypes) {
1404
+ declareWorkbookContentType(zip, await contentTypes.async("string"));
1405
+ }
1406
+ }
1407
+ var workbookName;
1408
+ var init_chartParts = __esm({
1409
+ "src/renderers/office-open/chartParts.ts"() {
1410
+ "use strict";
1411
+ workbookName = (ordinal) => `Microsoft_Excel_Worksheet${ordinal}.xlsx`;
1412
+ }
1349
1413
  });
1350
1414
 
1351
1415
  // src/renderers/office-open/emit.ts
@@ -1684,6 +1748,82 @@ function groupChild(element, ctx) {
1684
1748
  }
1685
1749
  return { group };
1686
1750
  }
1751
+ function chartChild(element, ctx) {
1752
+ const { options, transform } = element;
1753
+ const series = element.series;
1754
+ const dataLabels = dataLabelOptions(options);
1755
+ const marker = markerOptions(options);
1756
+ if (element.chartType === "bubble") {
1757
+ throw new Error(
1758
+ `the office-open renderer does not draw bubble charts (${element.path}); use the pptxgenjs renderer for this chart`
1759
+ );
1760
+ }
1761
+ return {
1762
+ // Stated, never left to the backend: `_nextChartId` in
1763
+ // `@office-open/pptx` is module-level and never resets, so an unnamed
1764
+ // chart is numbered differently on every render in the same process.
1765
+ id: ctx.nextId(),
1766
+ ...chartTypeOptions(element),
1767
+ categories: series[0]?.labels ?? [],
1768
+ series: series.map((entry, index) => ({
1769
+ name: entry.name ?? `Series ${index + 1}`,
1770
+ values: entry.values ?? [],
1771
+ // Every series, not just the first: PowerPoint labels each one, and a
1772
+ // chart that labelled only its first series would be a different chart.
1773
+ ...dataLabels ? { dataLabels } : {},
1774
+ ...options.lineSmooth !== void 0 ? { smooth: options.lineSmooth } : {},
1775
+ ...marker ? { marker } : {}
1776
+ })),
1777
+ ...options.title && options.showTitle !== false ? { title: options.title } : {},
1778
+ ...options.showLegend !== void 0 ? { showLegend: options.showLegend } : {},
1779
+ ...chartFamilyOptions(options),
1780
+ ...options.legendPosition ? { legendPosition: options.legendPosition } : {},
1781
+ x: transform.xEmu,
1782
+ y: transform.yEmu,
1783
+ width: transform.widthEmu,
1784
+ height: transform.heightEmu,
1785
+ ...element.altText ? { description: element.altText } : {}
1786
+ };
1787
+ }
1788
+ function chartFamilyOptions(options) {
1789
+ return {
1790
+ ...options.barGapWidthPercent !== void 0 ? { gapWidth: options.barGapWidthPercent } : {},
1791
+ ...options.barOverlapPercent !== void 0 ? { overlap: options.barOverlapPercent } : {},
1792
+ ...options.holeSize !== void 0 ? { holeSize: options.holeSize } : {},
1793
+ ...options.firstSliceAngle !== void 0 ? { firstSliceAngle: options.firstSliceAngle } : {}
1794
+ };
1795
+ }
1796
+ function dataLabelOptions(options) {
1797
+ const authored = options.showValue !== void 0 || options.showPercent !== void 0 || options.showLabel !== void 0 || options.showSeriesName !== void 0 || options.dataLabelPosition !== void 0;
1798
+ if (!authored) return void 0;
1799
+ return {
1800
+ showVal: options.showValue ?? false,
1801
+ showPercent: options.showPercent ?? false,
1802
+ showCatName: options.showLabel ?? false,
1803
+ showSerName: options.showSeriesName ?? false,
1804
+ showBubbleSize: false,
1805
+ showLegendKey: false,
1806
+ ...options.dataLabelPosition ? { position: options.dataLabelPosition } : {}
1807
+ };
1808
+ }
1809
+ function markerOptions(options) {
1810
+ const marker = {
1811
+ ...options.lineDataSymbol ? { symbol: options.lineDataSymbol } : {},
1812
+ ...options.lineDataSymbolSize !== void 0 ? { size: options.lineDataSymbolSize } : {}
1813
+ };
1814
+ return Object.keys(marker).length > 0 ? marker : void 0;
1815
+ }
1816
+ function chartTypeOptions(element) {
1817
+ const horizontal = element.options.barDirection === "bar";
1818
+ switch (element.chartType) {
1819
+ case "bar":
1820
+ return { type: horizontal ? "bar" : "column" };
1821
+ case "bar3D":
1822
+ return { type: horizontal ? "bar" : "column", threeD: true };
1823
+ default:
1824
+ return { type: element.chartType };
1825
+ }
1826
+ }
1687
1827
  function slideChild(element, ctx) {
1688
1828
  switch (element.kind) {
1689
1829
  case "textBox":
@@ -1697,9 +1837,8 @@ function slideChild(element, ctx) {
1697
1837
  case "group":
1698
1838
  return groupChild(element, ctx);
1699
1839
  case "chart":
1700
- throw new Error(
1701
- `the office-open renderer does not emit charts (${element.path})`
1702
- );
1840
+ ctx.charts?.push(element);
1841
+ return { chart: chartChild(element, ctx) };
1703
1842
  default:
1704
1843
  return assertNever2(element, "PptxIrElement");
1705
1844
  }
@@ -1801,26 +1940,30 @@ async function createOfficeOpenPptxRenderer() {
1801
1940
  format: "pptx",
1802
1941
  capabilities: OFFICE_OPEN_CAPABILITIES,
1803
1942
  async render(ir, options) {
1804
- const presentation = await buildPresentationOptions(ir);
1943
+ const charts = [];
1944
+ const presentation = await buildPresentationOptions(ir, charts);
1805
1945
  const bytes = await backend.generatePresentation(presentation, {
1806
1946
  type: "uint8array"
1807
1947
  });
1808
1948
  const raw = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
1809
- if (options?.deterministic === false) return raw;
1810
- return new Uint8Array(
1811
- await finalizePackageBuffer(Buffer.from(raw), {
1812
- generatedAt: options?.generatedAt
1813
- })
1814
- );
1949
+ if (charts.length === 0 && options?.deterministic === false) return raw;
1950
+ const zip = await readPackage(Buffer.from(raw));
1951
+ await spliceChartParts(zip, charts);
1952
+ if (options?.deterministic === false) {
1953
+ return new Uint8Array(await writePackage(zip));
1954
+ }
1955
+ await finalizePackage(zip, resolveGeneratedAt(options?.generatedAt));
1956
+ return new Uint8Array(await writePackage(zip));
1815
1957
  }
1816
1958
  };
1817
1959
  }
1818
- async function buildPresentationOptions(ir) {
1960
+ async function buildPresentationOptions(ir, charts = []) {
1819
1961
  let nextDrawingId = 2;
1820
1962
  const ctx = {
1821
1963
  resources: new Map(ir.resources.map((r) => [r.id, r])),
1822
1964
  resourceBytes: await loadResourceBytes(ir.resources),
1823
- nextId: () => nextDrawingId++
1965
+ nextId: () => nextDrawingId++,
1966
+ charts
1824
1967
  };
1825
1968
  const presentation = {
1826
1969
  size: { width: ir.size.widthEmu, height: ir.size.heightEmu },
@@ -1906,33 +2049,52 @@ async function loadResourceBytes(resources) {
1906
2049
  );
1907
2050
  return new Map(entries);
1908
2051
  }
1909
- var OFFICE_OPEN_PPTX_RENDERER_ID, OFFICE_OPEN_PPTX, UNSUPPORTED, OFFICE_OPEN_CAPABILITIES, SCHEME_SLOTS;
2052
+ var OFFICE_OPEN_PPTX_RENDERER_ID, OFFICE_OPEN_PPTX, OFFICE_OPEN_CAPABILITIES, SCHEME_SLOTS;
1910
2053
  var init_office_open = __esm({
1911
2054
  "src/renderers/office-open/index.ts"() {
1912
2055
  "use strict";
1913
2056
  init_finalizePackage();
1914
- init_features();
2057
+ init_chartParts();
1915
2058
  init_emit2();
1916
2059
  OFFICE_OPEN_PPTX_RENDERER_ID = "office-open";
1917
2060
  OFFICE_OPEN_PPTX = "@office-open/pptx";
1918
- UNSUPPORTED = /* @__PURE__ */ new Set([
1919
- "svg",
2061
+ OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
2062
+ "rich-text",
2063
+ "complex-bullet-glyphs",
2064
+ "text",
2065
+ "shapes",
2066
+ "images",
2067
+ "tables",
1920
2068
  "charts",
1921
- "image-transform",
1922
- "image-crop",
1923
- "image-rounding",
1924
- "flip-vertical",
1925
- "element-hyperlinks",
1926
- "masters",
1927
- "placeholders",
1928
- "table-merged-cells",
1929
- "table-rounded-corners",
1930
- "table-auto-page",
1931
- "table-insets"
2069
+ "chart-bar-style",
2070
+ "chart-pie-style",
2071
+ "chart-line-style",
2072
+ "chart-radar-style",
2073
+ "chart-data-labels",
2074
+ "chart-data-border",
2075
+ "chart-axis-scale",
2076
+ "chart-axis-visibility",
2077
+ "chart-axis-style",
2078
+ "chart-text-style",
2079
+ "solid-fills",
2080
+ "gradient-fills",
2081
+ "pattern-fills",
2082
+ "image-fills",
2083
+ "lines",
2084
+ "shadows",
2085
+ "backgrounds",
2086
+ "speaker-notes",
2087
+ "hidden-slides",
2088
+ "transitions",
2089
+ "external-links",
2090
+ "internal-links",
2091
+ "text-hyperlinks",
2092
+ "rotation",
2093
+ "flip-horizontal",
2094
+ "groups",
2095
+ "proofing-language",
2096
+ "rtl"
1932
2097
  ]);
1933
- OFFICE_OPEN_CAPABILITIES = new Set(
1934
- [...ALL_PPTX_FEATURES].filter((feature) => !UNSUPPORTED.has(feature))
1935
- );
1936
2098
  SCHEME_SLOTS = [
1937
2099
  ["dark1", "text"],
1938
2100
  ["light1", "background"],
@@ -2429,7 +2591,8 @@ function compilePresentation(processed, warnings = []) {
2429
2591
  extent: {
2430
2592
  widthEmu: inchesToEmu(processed.slideWidth),
2431
2593
  heightEmu: inchesToEmu(processed.slideHeight)
2432
- }
2594
+ },
2595
+ positionlessText: []
2433
2596
  };
2434
2597
  const masters = (processed.templates ?? []).map(
2435
2598
  (template, index) => compileMaster(template, index, processed, ctx)
@@ -2538,6 +2701,7 @@ function compileSlide(slide, slideIndex, processed, ctx) {
2538
2701
  if (element) elements.push(element);
2539
2702
  nextIndex += 1;
2540
2703
  };
2704
+ ctx.positionlessText = [];
2541
2705
  const gradientSource = slide.background?.gradient ?? (slide.background ? void 0 : template?.background?.gradient);
2542
2706
  let background2;
2543
2707
  if (gradientSource) {
@@ -2611,6 +2775,7 @@ function compileSlide(slide, slideIndex, processed, ctx) {
2611
2775
  })
2612
2776
  );
2613
2777
  }
2778
+ warnOverlappingText(slideIndex, ctx);
2614
2779
  if (slide.notes) ctx.features.require("speaker-notes", `${path4}.notes`);
2615
2780
  if (slide.hidden) ctx.features.require("hidden-slides", `${path4}.hidden`);
2616
2781
  if (background2) ctx.features.require("backgrounds", `${path4}.background`);
@@ -2627,6 +2792,23 @@ function compileSlide(slide, slideIndex, processed, ctx) {
2627
2792
  ...transition ? { transition } : {}
2628
2793
  };
2629
2794
  }
2795
+ function warnOverlappingText(slideIndex, ctx) {
2796
+ const boxes = ctx.positionlessText;
2797
+ for (let i = 0; i < boxes.length; i += 1) {
2798
+ for (let j = i + 1; j < boxes.length; j += 1) {
2799
+ const a = boxes[i];
2800
+ const b = boxes[j];
2801
+ const overlaps = a.transform.xEmu < b.transform.xEmu + b.transform.widthEmu && b.transform.xEmu < a.transform.xEmu + a.transform.widthEmu && a.transform.yEmu < b.transform.yEmu + b.transform.heightEmu && b.transform.yEmu < a.transform.yEmu + a.transform.heightEmu;
2802
+ if (!overlaps) continue;
2803
+ warn(
2804
+ ctx.warnings,
2805
+ W.TEXT_OVERLAP_UNPOSITIONED,
2806
+ `Text at ${a.path} and ${b.path} give no x/y and overlap on this slide. Give at least one of them explicit coordinates, or a named style whose default band differs.`,
2807
+ { slide: slideIndex, component: "text" }
2808
+ );
2809
+ }
2810
+ }
2811
+ }
2630
2812
  function compileTransition(transition) {
2631
2813
  const type = transition?.type;
2632
2814
  if (!type || type === "none") return void 0;
@@ -2761,6 +2943,9 @@ function compileText(component, scope) {
2761
2943
  const heightFontSize = props.fontSize ?? ctx.theme.defaults.fontSize ?? 18;
2762
2944
  const transform = textTransform(props, heightFontSize, lineCount, ctx);
2763
2945
  const autoFit = props.h === void 0;
2946
+ if (props.x === void 0 && props.y === void 0) {
2947
+ ctx.positionlessText.push({ path: path4, transform });
2948
+ }
2764
2949
  const hyperlink2 = compileHyperlink(props.hyperlink, "text", ctx, path4);
2765
2950
  const runs = runProps ? runProps.map(
2766
2951
  (run) => compileRichRun(run, cascade, void 0, scope.slideCtx, ctx)
@@ -2966,12 +3151,26 @@ function requireBulletFeatures(bullet, path4, ctx) {
2966
3151
  ctx.features.require("complex-bullet-glyphs", `${path4}.bullet.style`);
2967
3152
  }
2968
3153
  }
3154
+ var STYLE_BANDS = {
3155
+ // A title slide's title and its subtitle, as two bands in the middle third.
3156
+ title: { x: 0.08, y: 0.34, w: 0.84 },
3157
+ subtitle: { x: 0.08, y: 0.56, w: 0.84 },
3158
+ // Content-slide headings sit in the top margin, all three at the same place:
3159
+ // the level changes the type, not where the slide starts.
3160
+ heading1: { x: 0.06, y: 0.06, w: 0.88 },
3161
+ heading2: { x: 0.06, y: 0.06, w: 0.88 },
3162
+ heading3: { x: 0.06, y: 0.06, w: 0.88 },
3163
+ // Below a heading, in the content-safe area.
3164
+ body: { x: 0.06, y: 0.26, w: 0.88 },
3165
+ caption: { x: 0.06, y: 0.88, w: 0.88 }
3166
+ };
2969
3167
  function textTransform(props, fontSize, lineCount, ctx) {
2970
3168
  const heightEmu = props.h !== void 0 ? resolveDimensionEmu(props.h, "Y", ctx.extent) : inchesToEmu(Math.max(0.5, fontSize / 72 * 1.6 * lineCount));
3169
+ const band = typeof props.style === "string" ? STYLE_BANDS[props.style] : void 0;
2971
3170
  return {
2972
- xEmu: props.x !== void 0 ? resolveDimensionEmu(props.x, "X", ctx.extent) : 0,
2973
- yEmu: props.y !== void 0 ? resolveDimensionEmu(props.y, "Y", ctx.extent) : 0,
2974
- widthEmu: props.w !== void 0 ? resolveDimensionEmu(props.w, "X", ctx.extent) : defaultWidthEmu(ctx.extent),
3171
+ xEmu: props.x !== void 0 ? resolveDimensionEmu(props.x, "X", ctx.extent) : Math.round((band?.x ?? 0) * ctx.extent.widthEmu),
3172
+ yEmu: props.y !== void 0 ? resolveDimensionEmu(props.y, "Y", ctx.extent) : Math.round((band?.y ?? 0) * ctx.extent.heightEmu),
3173
+ widthEmu: props.w !== void 0 ? resolveDimensionEmu(props.w, "X", ctx.extent) : band ? Math.round(band.w * ctx.extent.widthEmu) : defaultWidthEmu(ctx.extent),
2975
3174
  heightEmu,
2976
3175
  ...rotationProperty(props.rotate)
2977
3176
  };
@@ -3193,6 +3392,11 @@ function compileTable(component, scope) {
3193
3392
  const rows = authoredRows.map((row) => ({
3194
3393
  cells: row.map((cell) => compileTableCell(cell, props, ctx))
3195
3394
  }));
3395
+ if (props.headerRow && rows.length > 0) {
3396
+ rows[0] = {
3397
+ cells: rows[0].cells.map((cell) => headerCell(cell, props, ctx))
3398
+ };
3399
+ }
3196
3400
  const border = compileTableBorder(props.border, ctx);
3197
3401
  const cornerRadius = typeof props.borderRadius === "number" && props.borderRadius > 0 ? props.borderRadius : void 0;
3198
3402
  ctx.features.require("tables", path4);
@@ -3257,6 +3461,19 @@ function toEmuList(value, axis, ctx) {
3257
3461
  const values = Array.isArray(value) ? value : [value];
3258
3462
  return values.map((v) => resolveDimensionEmu(v, axis, ctx.extent));
3259
3463
  }
3464
+ function headerCell(cell, tableProps, ctx) {
3465
+ const formatting = {
3466
+ ...tableProps.fontWeight == null ? { bold: true } : {},
3467
+ ...tableProps.color ? {} : { color: irColor(resolveColor("text", ctx.theme, ctx.warnings)) },
3468
+ ...cell.formatting
3469
+ };
3470
+ const fill2 = cell.fill ?? (tableProps.fill ? void 0 : irColor(resolveColor("background2", ctx.theme, ctx.warnings)));
3471
+ return {
3472
+ ...cell,
3473
+ formatting,
3474
+ ...fill2 ? { fill: fill2 } : {}
3475
+ };
3476
+ }
3260
3477
  function compileTableDefaults(props, ctx) {
3261
3478
  const family = props.fontFace ?? ctx.theme.fonts.body;
3262
3479
  let fontFamily = family;
@@ -3343,6 +3560,61 @@ var CHART_TYPES = [
3343
3560
  "radar",
3344
3561
  "scatter"
3345
3562
  ];
3563
+ var CHART_STYLE_FEATURES = {
3564
+ showValue: "chart-data-labels",
3565
+ showPercent: "chart-data-labels",
3566
+ showLabel: "chart-data-labels",
3567
+ showSerName: "chart-data-labels",
3568
+ dataLabelPosition: "chart-data-labels",
3569
+ dataBorder: "chart-data-border",
3570
+ catAxisHidden: "chart-axis-visibility",
3571
+ valAxisHidden: "chart-axis-visibility",
3572
+ catAxisLineShow: "chart-axis-visibility",
3573
+ valAxisLineShow: "chart-axis-visibility",
3574
+ catAxisLabelRotate: "chart-axis-style",
3575
+ catGridLine: "chart-axis-style",
3576
+ valGridLine: "chart-axis-style",
3577
+ valAxisMinVal: "chart-axis-scale",
3578
+ valAxisMaxVal: "chart-axis-scale",
3579
+ valAxisMajorUnit: "chart-axis-scale",
3580
+ valAxisLabelFormatCode: "chart-axis-scale",
3581
+ barGapWidthPct: "chart-bar-style",
3582
+ barOverlapPct: "chart-bar-style",
3583
+ firstSliceAng: "chart-pie-style",
3584
+ holeSize: "chart-pie-style",
3585
+ lineSmooth: "chart-line-style",
3586
+ lineDataSymbol: "chart-line-style",
3587
+ lineSize: "chart-line-style",
3588
+ lineDataSymbolSize: "chart-line-style",
3589
+ radarStyle: "chart-radar-style",
3590
+ titleFontSize: "chart-text-style",
3591
+ titleColor: "chart-text-style",
3592
+ titleFontFace: "chart-text-style",
3593
+ titleFontWeight: "chart-text-style",
3594
+ legendFontSize: "chart-text-style",
3595
+ legendFontFace: "chart-text-style",
3596
+ legendFontWeight: "chart-text-style",
3597
+ legendColor: "chart-text-style",
3598
+ catAxisLabelFontSize: "chart-text-style",
3599
+ catAxisLabelColor: "chart-text-style",
3600
+ catAxisLabelFontFace: "chart-text-style",
3601
+ catAxisLabelFontWeight: "chart-text-style",
3602
+ valAxisLabelFontSize: "chart-text-style",
3603
+ valAxisLabelColor: "chart-text-style",
3604
+ valAxisLabelFontFace: "chart-text-style",
3605
+ valAxisLabelFontWeight: "chart-text-style",
3606
+ dataLabelColor: "chart-text-style",
3607
+ dataLabelFontSize: "chart-text-style",
3608
+ dataLabelFontFace: "chart-text-style",
3609
+ dataLabelFontWeight: "chart-text-style",
3610
+ dataLabelFontBold: "chart-text-style"
3611
+ };
3612
+ function requireChartStyleFeatures(props, ctx, path4) {
3613
+ for (const [prop, feature] of Object.entries(CHART_STYLE_FEATURES)) {
3614
+ if (props[prop] === void 0) continue;
3615
+ ctx.features.require(feature, `${path4}.${prop}`);
3616
+ }
3617
+ }
3346
3618
  function compileChart(component, scope) {
3347
3619
  const { ctx, path: path4 } = scope;
3348
3620
  const props = component.props;
@@ -3385,6 +3657,7 @@ function compileChart(component, scope) {
3385
3657
  );
3386
3658
  }
3387
3659
  ctx.features.require("charts", path4);
3660
+ requireChartStyleFeatures(props, ctx, path4);
3388
3661
  return {
3389
3662
  kind: "chart",
3390
3663
  id: scope.id,
@@ -3745,7 +4018,9 @@ function optionalTransform(x, y, w, h, ctx) {
3745
4018
  }
3746
4019
 
3747
4020
  // src/renderers/registry.ts
3748
- import { RendererRegistry } from "@json-to-office/shared/rendering";
4021
+ import {
4022
+ RendererRegistry
4023
+ } from "@json-to-office/shared/rendering";
3749
4024
 
3750
4025
  // src/renderers/types.ts
3751
4026
  import {
@@ -3771,6 +4046,9 @@ function resolvePptxRenderer(id) {
3771
4046
  function pptxRendererIds() {
3772
4047
  return registry.ids();
3773
4048
  }
4049
+ function pptxRendererStatuses() {
4050
+ return registry.statuses();
4051
+ }
3774
4052
  function isPptxRendererId(value) {
3775
4053
  return registry.has(value);
3776
4054
  }
@@ -3850,6 +4128,10 @@ async function resolveDocumentFonts(document, theme, warnings, fonts) {
3850
4128
  import { applyExportMode } from "@json-to-office/shared";
3851
4129
 
3852
4130
  // src/themes/defaults.ts
4131
+ var DEFAULT_TABLE = {
4132
+ border: { type: "solid", pt: 1, color: "background2" },
4133
+ headerRow: true
4134
+ };
3853
4135
  var DEFAULT_STYLES = {
3854
4136
  title: { fontSize: 36, bold: true, fontColor: "text", align: "center" },
3855
4137
  subtitle: { fontSize: 20, italic: true, fontColor: "text2", align: "center" },
@@ -3881,7 +4163,8 @@ var DEFAULT_PPTX_THEME = {
3881
4163
  fontSize: 18,
3882
4164
  fontColor: "#333333"
3883
4165
  },
3884
- styles: DEFAULT_STYLES
4166
+ styles: DEFAULT_STYLES,
4167
+ componentDefaults: { table: DEFAULT_TABLE }
3885
4168
  };
3886
4169
  var PPTX_THEMES = {
3887
4170
  default: DEFAULT_PPTX_THEME,
@@ -3907,7 +4190,8 @@ var PPTX_THEMES = {
3907
4190
  fontSize: 18,
3908
4191
  fontColor: "#FFFFFF"
3909
4192
  },
3910
- styles: DEFAULT_STYLES
4193
+ styles: DEFAULT_STYLES,
4194
+ componentDefaults: { table: DEFAULT_TABLE }
3911
4195
  },
3912
4196
  minimal: {
3913
4197
  name: "minimal",
@@ -3931,7 +4215,8 @@ var PPTX_THEMES = {
3931
4215
  fontSize: 18,
3932
4216
  fontColor: "#000000"
3933
4217
  },
3934
- styles: DEFAULT_STYLES
4218
+ styles: DEFAULT_STYLES,
4219
+ componentDefaults: { table: DEFAULT_TABLE }
3935
4220
  }
3936
4221
  };
3937
4222
  function getPptxTheme(name) {
@@ -5096,6 +5381,7 @@ export {
5096
5381
  isPresentationComponentDefinition,
5097
5382
  isSlideComponent,
5098
5383
  pptxRendererIds,
5384
+ pptxRendererStatuses,
5099
5385
  pptxThemes,
5100
5386
  resolveComponentVersion3 as resolveComponentVersion,
5101
5387
  validateComponentProps,