@json-to-office/core-pptx 1.3.0 → 1.5.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/core/generateFromIr.d.ts.map +1 -1
- package/dist/core/generationOptions.d.ts +4 -0
- package/dist/core/generationOptions.d.ts.map +1 -1
- package/dist/core/grid.d.ts.map +1 -1
- package/dist/core/placeholders.d.ts +21 -0
- package/dist/core/placeholders.d.ts.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1414 -490
- package/dist/index.js.map +1 -1
- package/dist/ir/compiler.d.ts.map +1 -1
- package/dist/quality/facts.d.ts +59 -0
- package/dist/quality/facts.d.ts.map +1 -0
- package/dist/quality/preflight.d.ts +15 -0
- package/dist/quality/preflight.d.ts.map +1 -0
- package/dist/quality/rules.d.ts +41 -0
- package/dist/quality/rules.d.ts.map +1 -0
- package/dist/renderers/office-open/index.d.ts.map +1 -1
- package/dist/renderers/pptxgenjs/index.d.ts.map +1 -1
- package/dist/renderers/registry.d.ts +18 -4
- package/dist/renderers/registry.d.ts.map +1 -1
- package/dist/themes/defaults.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/warn.d.ts +1 -0
- package/dist/utils/warn.d.ts.map +1 -1
- package/package.json +8 -15
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,147 +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
|
-
/*
|
|
190
|
-
* Chart styling, split finer than `charts` itself.
|
|
191
|
-
*
|
|
192
|
-
* `charts` says a backend can draw a chart from data. It says nothing about
|
|
193
|
-
* whether that backend honours the options the author styled it with, and one
|
|
194
|
-
* coarse feature let a renderer accept a chart, draw it, and quietly ignore
|
|
195
|
-
* half of what was asked for — an ignored axis maximum draws a different
|
|
196
|
-
* chart from the authored one and the file says nothing about it.
|
|
197
|
-
*
|
|
198
|
-
* Each name below is required by the compiler *only when the matching prop
|
|
199
|
-
* was authored*, at that prop's own path, so a default never demands a
|
|
200
|
-
* capability and a refusal always names the line that caused it.
|
|
201
|
-
*/
|
|
202
|
-
/** `barGapWidthPct`, `barOverlapPct`. */
|
|
203
|
-
"chart-bar-style",
|
|
204
|
-
/** `firstSliceAng`, `holeSize`. */
|
|
205
|
-
"chart-pie-style",
|
|
206
|
-
/** `lineSmooth`, `lineDataSymbol`, `lineSize`, `lineDataSymbolSize`. */
|
|
207
|
-
"chart-line-style",
|
|
208
|
-
/** `radarStyle` — anything but the `standard` a backend may hardcode. */
|
|
209
|
-
"chart-radar-style",
|
|
210
|
-
/** `showValue`, `showPercent`, `showLabel`, `showSerName`, `dataLabelPosition`. */
|
|
211
|
-
"chart-data-labels",
|
|
212
|
-
/** `dataBorder`: an outline on bars, slices and areas. */
|
|
213
|
-
"chart-data-border",
|
|
214
|
-
/** Value-axis bounds and number format: min, max, major unit, format code. */
|
|
215
|
-
"chart-axis-scale",
|
|
216
|
-
/** Hiding an axis or its line. */
|
|
217
|
-
"chart-axis-visibility",
|
|
218
|
-
/** Axis label rotation and grid lines. */
|
|
219
|
-
"chart-axis-style",
|
|
220
|
-
/** Font family, size, weight and colour on any chart text. */
|
|
221
|
-
"chart-text-style",
|
|
222
|
-
"solid-fills",
|
|
223
|
-
"gradient-fills",
|
|
224
|
-
"pattern-fills",
|
|
225
|
-
"image-fills",
|
|
226
|
-
"lines",
|
|
227
|
-
"shadows",
|
|
228
|
-
/** Slide backgrounds (solid or image). */
|
|
229
|
-
"backgrounds",
|
|
230
|
-
"speaker-notes",
|
|
231
|
-
"hidden-slides",
|
|
232
|
-
"transitions",
|
|
233
|
-
/** Hyperlinks to an external URL. */
|
|
234
|
-
"external-links",
|
|
235
|
-
/** Hyperlinks targeting another slide in the same deck. */
|
|
236
|
-
"internal-links",
|
|
237
|
-
/** A link covering a text body — realisable on the runs inside it. */
|
|
238
|
-
"text-hyperlinks",
|
|
239
|
-
/** A link on a shape or image as a whole, which runs cannot stand in for. */
|
|
240
|
-
"element-hyperlinks",
|
|
241
|
-
/** Rotation on a shape, text box or table. */
|
|
242
|
-
"rotation",
|
|
243
|
-
/**
|
|
244
|
-
* Any transform on a picture — rotation or flip.
|
|
245
|
-
*
|
|
246
|
-
* One feature rather than three: a backend that cannot rotate a picture
|
|
247
|
-
* generally cannot flip one either, because the option carrying both is
|
|
248
|
-
* simply absent from its picture type.
|
|
249
|
-
*/
|
|
250
|
-
"image-transform",
|
|
251
|
-
"flip-horizontal",
|
|
252
|
-
"flip-vertical",
|
|
253
|
-
/** Grouped elements sharing a transform. */
|
|
254
|
-
"groups",
|
|
255
|
-
/** Per-run proofing language. */
|
|
256
|
-
"proofing-language",
|
|
257
|
-
/** Right-to-left reading order. */
|
|
258
|
-
"rtl"
|
|
259
|
-
];
|
|
260
|
-
ALL_PPTX_FEATURES = new Set(
|
|
261
|
-
PPTX_FEATURES
|
|
262
|
-
);
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
|
|
266
126
|
// src/renderers/pptxgenjs/fills.ts
|
|
267
127
|
function colorXml(color2) {
|
|
268
128
|
const alpha = color2.transparency !== void 0 ? `<a:alpha val="${Math.round((100 - color2.transparency) * PCT_UNIT)}"/>` : "";
|
|
@@ -1363,16 +1223,55 @@ var PPTXGENJS_RENDERER_ID, PPTXGENJS_CAPABILITIES;
|
|
|
1363
1223
|
var init_pptxgenjs = __esm({
|
|
1364
1224
|
"src/renderers/pptxgenjs/index.ts"() {
|
|
1365
1225
|
"use strict";
|
|
1366
|
-
init_features();
|
|
1367
1226
|
init_units();
|
|
1368
1227
|
init_emit();
|
|
1369
1228
|
init_packaging();
|
|
1370
1229
|
PPTXGENJS_RENDERER_ID = "pptxgenjs";
|
|
1371
|
-
PPTXGENJS_CAPABILITIES = new Set(
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
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
|
+
]);
|
|
1376
1275
|
}
|
|
1377
1276
|
});
|
|
1378
1277
|
|
|
@@ -2150,33 +2049,52 @@ async function loadResourceBytes(resources) {
|
|
|
2150
2049
|
);
|
|
2151
2050
|
return new Map(entries);
|
|
2152
2051
|
}
|
|
2153
|
-
var OFFICE_OPEN_PPTX_RENDERER_ID, OFFICE_OPEN_PPTX,
|
|
2052
|
+
var OFFICE_OPEN_PPTX_RENDERER_ID, OFFICE_OPEN_PPTX, OFFICE_OPEN_CAPABILITIES, SCHEME_SLOTS;
|
|
2154
2053
|
var init_office_open = __esm({
|
|
2155
2054
|
"src/renderers/office-open/index.ts"() {
|
|
2156
2055
|
"use strict";
|
|
2157
2056
|
init_finalizePackage();
|
|
2158
2057
|
init_chartParts();
|
|
2159
|
-
init_features();
|
|
2160
2058
|
init_emit2();
|
|
2161
2059
|
OFFICE_OPEN_PPTX_RENDERER_ID = "office-open";
|
|
2162
2060
|
OFFICE_OPEN_PPTX = "@office-open/pptx";
|
|
2163
|
-
|
|
2164
|
-
"
|
|
2165
|
-
"
|
|
2166
|
-
"
|
|
2167
|
-
"
|
|
2168
|
-
"
|
|
2169
|
-
"
|
|
2170
|
-
"
|
|
2171
|
-
"
|
|
2172
|
-
"
|
|
2173
|
-
"
|
|
2174
|
-
"
|
|
2175
|
-
"
|
|
2061
|
+
OFFICE_OPEN_CAPABILITIES = /* @__PURE__ */ new Set([
|
|
2062
|
+
"rich-text",
|
|
2063
|
+
"complex-bullet-glyphs",
|
|
2064
|
+
"text",
|
|
2065
|
+
"shapes",
|
|
2066
|
+
"images",
|
|
2067
|
+
"tables",
|
|
2068
|
+
"charts",
|
|
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"
|
|
2176
2097
|
]);
|
|
2177
|
-
OFFICE_OPEN_CAPABILITIES = new Set(
|
|
2178
|
-
[...ALL_PPTX_FEATURES].filter((feature) => !UNSUPPORTED.has(feature))
|
|
2179
|
-
);
|
|
2180
2098
|
SCHEME_SLOTS = [
|
|
2181
2099
|
["dark1", "text"],
|
|
2182
2100
|
["light1", "background"],
|
|
@@ -2317,99 +2235,6 @@ function applyFontWeight(params) {
|
|
|
2317
2235
|
return { fontFace: synth.family, bold: synth.bold, italic: synth.italic };
|
|
2318
2236
|
}
|
|
2319
2237
|
|
|
2320
|
-
// src/utils/componentDefaults.ts
|
|
2321
|
-
import { mergeWithDefaults } from "@json-to-office/shared";
|
|
2322
|
-
function getComponentDefaults(theme) {
|
|
2323
|
-
return theme.componentDefaults || {};
|
|
2324
|
-
}
|
|
2325
|
-
function getTextDefaults(theme) {
|
|
2326
|
-
return getComponentDefaults(theme).text || {};
|
|
2327
|
-
}
|
|
2328
|
-
function getImageDefaults(theme) {
|
|
2329
|
-
return getComponentDefaults(theme).image || {};
|
|
2330
|
-
}
|
|
2331
|
-
function getShapeDefaults(theme) {
|
|
2332
|
-
return getComponentDefaults(theme).shape || {};
|
|
2333
|
-
}
|
|
2334
|
-
function getTableDefaults(theme) {
|
|
2335
|
-
return getComponentDefaults(theme).table || {};
|
|
2336
|
-
}
|
|
2337
|
-
function getHighchartsDefaults(theme) {
|
|
2338
|
-
return getComponentDefaults(theme).highcharts || {};
|
|
2339
|
-
}
|
|
2340
|
-
function getChartDefaults(theme) {
|
|
2341
|
-
return getComponentDefaults(theme).chart || {};
|
|
2342
|
-
}
|
|
2343
|
-
function getCustomComponentDefaults(theme, componentName) {
|
|
2344
|
-
const defaults = getComponentDefaults(theme);
|
|
2345
|
-
return defaults?.[componentName] || {};
|
|
2346
|
-
}
|
|
2347
|
-
function resolveTextProps(props, theme) {
|
|
2348
|
-
return mergeWithDefaults(props, getTextDefaults(theme));
|
|
2349
|
-
}
|
|
2350
|
-
function resolveImageProps(props, theme) {
|
|
2351
|
-
return mergeWithDefaults(props, getImageDefaults(theme));
|
|
2352
|
-
}
|
|
2353
|
-
function resolveShapeProps(props, theme) {
|
|
2354
|
-
return mergeWithDefaults(props, getShapeDefaults(theme));
|
|
2355
|
-
}
|
|
2356
|
-
function resolveTableProps(props, theme) {
|
|
2357
|
-
return mergeWithDefaults(props, getTableDefaults(theme));
|
|
2358
|
-
}
|
|
2359
|
-
function resolveHighchartsProps(props, theme) {
|
|
2360
|
-
return mergeWithDefaults(props, getHighchartsDefaults(theme));
|
|
2361
|
-
}
|
|
2362
|
-
function resolveChartProps(props, theme) {
|
|
2363
|
-
return mergeWithDefaults(props, getChartDefaults(theme));
|
|
2364
|
-
}
|
|
2365
|
-
function resolveCustomComponentProps(props, theme, componentName) {
|
|
2366
|
-
const defaults = getCustomComponentDefaults(theme, componentName);
|
|
2367
|
-
return mergeWithDefaults(props, defaults);
|
|
2368
|
-
}
|
|
2369
|
-
var TYPE_GETTERS = {
|
|
2370
|
-
text: getTextDefaults,
|
|
2371
|
-
image: getImageDefaults,
|
|
2372
|
-
shape: getShapeDefaults,
|
|
2373
|
-
table: getTableDefaults,
|
|
2374
|
-
highcharts: getHighchartsDefaults,
|
|
2375
|
-
chart: getChartDefaults
|
|
2376
|
-
};
|
|
2377
|
-
function getDefaultsForType(componentName, theme) {
|
|
2378
|
-
const getter = TYPE_GETTERS[componentName];
|
|
2379
|
-
return getter ? getter(theme) : getCustomComponentDefaults(theme, componentName);
|
|
2380
|
-
}
|
|
2381
|
-
|
|
2382
|
-
// src/utils/resolveComponentTree.ts
|
|
2383
|
-
var RESOLVER_MAP = {
|
|
2384
|
-
text: resolveTextProps,
|
|
2385
|
-
image: resolveImageProps,
|
|
2386
|
-
shape: resolveShapeProps,
|
|
2387
|
-
table: resolveTableProps,
|
|
2388
|
-
highcharts: resolveHighchartsProps,
|
|
2389
|
-
chart: resolveChartProps
|
|
2390
|
-
};
|
|
2391
|
-
function resolveComponentDefaults(component, theme) {
|
|
2392
|
-
const resolver = RESOLVER_MAP[component.name];
|
|
2393
|
-
const resolvedProps = resolver ? resolver(component.props, theme) : resolveCustomComponentProps(
|
|
2394
|
-
component.props,
|
|
2395
|
-
theme,
|
|
2396
|
-
component.name
|
|
2397
|
-
);
|
|
2398
|
-
return { ...component, props: resolvedProps };
|
|
2399
|
-
}
|
|
2400
|
-
function resolveComponentTree(components, theme) {
|
|
2401
|
-
return components.map((component) => {
|
|
2402
|
-
const resolved = resolveComponentDefaults(component, theme);
|
|
2403
|
-
if (resolved.children && resolved.children.length > 0) {
|
|
2404
|
-
return {
|
|
2405
|
-
...resolved,
|
|
2406
|
-
children: resolveComponentTree(resolved.children, theme)
|
|
2407
|
-
};
|
|
2408
|
-
}
|
|
2409
|
-
return resolved;
|
|
2410
|
-
});
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
2238
|
// src/utils/imageSource.ts
|
|
2414
2239
|
import path from "path";
|
|
2415
2240
|
var hasValue = (v) => typeof v === "string" && v.trim().length > 0;
|
|
@@ -2482,7 +2307,8 @@ var DEFAULT_GRID_CONFIG = {
|
|
|
2482
2307
|
};
|
|
2483
2308
|
function resolveMargin(margin) {
|
|
2484
2309
|
if (margin == null) return DEFAULT_GRID_CONFIG.margin;
|
|
2485
|
-
if (typeof margin === "number")
|
|
2310
|
+
if (typeof margin === "number")
|
|
2311
|
+
return { top: margin, right: margin, bottom: margin, left: margin };
|
|
2486
2312
|
return margin;
|
|
2487
2313
|
}
|
|
2488
2314
|
function resolveGutter(gutter) {
|
|
@@ -2546,36 +2372,210 @@ function resolveGridPosition(gridPos, gridConfig, slideWidth, slideHeight, warni
|
|
|
2546
2372
|
function resolveComponentGridPosition(component, gridConfig, slideWidth, slideHeight, warnings) {
|
|
2547
2373
|
const gridPos = component.props.grid;
|
|
2548
2374
|
if (!gridPos) return component;
|
|
2549
|
-
const resolved = resolveGridPosition(
|
|
2375
|
+
const resolved = resolveGridPosition(
|
|
2376
|
+
gridPos,
|
|
2377
|
+
gridConfig,
|
|
2378
|
+
slideWidth,
|
|
2379
|
+
slideHeight,
|
|
2380
|
+
warnings
|
|
2381
|
+
);
|
|
2550
2382
|
const { grid: _grid, ...restProps } = component.props;
|
|
2551
2383
|
const newProps = { ...restProps };
|
|
2552
2384
|
const hasPercentX = typeof newProps.x === "string" || typeof newProps.w === "string";
|
|
2553
2385
|
const hasPercentY = typeof newProps.y === "string" || typeof newProps.h === "string";
|
|
2554
2386
|
const toPercX = (v) => `${+(v / slideWidth * 100).toFixed(2)}%`;
|
|
2555
2387
|
const toPercY = (v) => `${+(v / slideHeight * 100).toFixed(2)}%`;
|
|
2556
|
-
if (newProps.x == null)
|
|
2557
|
-
|
|
2558
|
-
if (newProps.
|
|
2559
|
-
|
|
2388
|
+
if (newProps.x == null)
|
|
2389
|
+
newProps.x = hasPercentX ? toPercX(resolved.x) : resolved.x;
|
|
2390
|
+
if (newProps.y == null)
|
|
2391
|
+
newProps.y = hasPercentY ? toPercY(resolved.y) : resolved.y;
|
|
2392
|
+
if (newProps.w == null)
|
|
2393
|
+
newProps.w = hasPercentX ? toPercX(resolved.w) : resolved.w;
|
|
2394
|
+
if (newProps.h == null)
|
|
2395
|
+
newProps.h = hasPercentY ? toPercY(resolved.h) : resolved.h;
|
|
2560
2396
|
return { ...component, props: newProps };
|
|
2561
2397
|
}
|
|
2562
2398
|
|
|
2563
2399
|
// src/ir/compiler.ts
|
|
2564
2400
|
init_warn();
|
|
2401
|
+
|
|
2402
|
+
// src/core/placeholders.ts
|
|
2565
2403
|
import { mergeWithDefaults as mergeWithDefaults2 } from "@json-to-office/shared";
|
|
2566
2404
|
|
|
2567
|
-
// src/
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2405
|
+
// src/utils/componentDefaults.ts
|
|
2406
|
+
import { mergeWithDefaults } from "@json-to-office/shared";
|
|
2407
|
+
function getComponentDefaults(theme) {
|
|
2408
|
+
return theme.componentDefaults || {};
|
|
2409
|
+
}
|
|
2410
|
+
function getTextDefaults(theme) {
|
|
2411
|
+
return getComponentDefaults(theme).text || {};
|
|
2412
|
+
}
|
|
2413
|
+
function getImageDefaults(theme) {
|
|
2414
|
+
return getComponentDefaults(theme).image || {};
|
|
2415
|
+
}
|
|
2416
|
+
function getShapeDefaults(theme) {
|
|
2417
|
+
return getComponentDefaults(theme).shape || {};
|
|
2418
|
+
}
|
|
2419
|
+
function getTableDefaults(theme) {
|
|
2420
|
+
return getComponentDefaults(theme).table || {};
|
|
2421
|
+
}
|
|
2422
|
+
function getHighchartsDefaults(theme) {
|
|
2423
|
+
return getComponentDefaults(theme).highcharts || {};
|
|
2424
|
+
}
|
|
2425
|
+
function getChartDefaults(theme) {
|
|
2426
|
+
return getComponentDefaults(theme).chart || {};
|
|
2427
|
+
}
|
|
2428
|
+
function getCustomComponentDefaults(theme, componentName) {
|
|
2429
|
+
const defaults = getComponentDefaults(theme);
|
|
2430
|
+
return defaults?.[componentName] || {};
|
|
2431
|
+
}
|
|
2432
|
+
function resolveTextProps(props, theme) {
|
|
2433
|
+
return mergeWithDefaults(props, getTextDefaults(theme));
|
|
2434
|
+
}
|
|
2435
|
+
function resolveImageProps(props, theme) {
|
|
2436
|
+
return mergeWithDefaults(props, getImageDefaults(theme));
|
|
2437
|
+
}
|
|
2438
|
+
function resolveShapeProps(props, theme) {
|
|
2439
|
+
return mergeWithDefaults(props, getShapeDefaults(theme));
|
|
2440
|
+
}
|
|
2441
|
+
function resolveTableProps(props, theme) {
|
|
2442
|
+
return mergeWithDefaults(props, getTableDefaults(theme));
|
|
2443
|
+
}
|
|
2444
|
+
function resolveHighchartsProps(props, theme) {
|
|
2445
|
+
return mergeWithDefaults(props, getHighchartsDefaults(theme));
|
|
2446
|
+
}
|
|
2447
|
+
function resolveChartProps(props, theme) {
|
|
2448
|
+
return mergeWithDefaults(props, getChartDefaults(theme));
|
|
2449
|
+
}
|
|
2450
|
+
function resolveCustomComponentProps(props, theme, componentName) {
|
|
2451
|
+
const defaults = getCustomComponentDefaults(theme, componentName);
|
|
2452
|
+
return mergeWithDefaults(props, defaults);
|
|
2453
|
+
}
|
|
2454
|
+
var TYPE_GETTERS = {
|
|
2455
|
+
text: getTextDefaults,
|
|
2456
|
+
image: getImageDefaults,
|
|
2457
|
+
shape: getShapeDefaults,
|
|
2458
|
+
table: getTableDefaults,
|
|
2459
|
+
highcharts: getHighchartsDefaults,
|
|
2460
|
+
chart: getChartDefaults
|
|
2461
|
+
};
|
|
2462
|
+
function getDefaultsForType(componentName, theme) {
|
|
2463
|
+
const getter = TYPE_GETTERS[componentName];
|
|
2464
|
+
return getter ? getter(theme) : getCustomComponentDefaults(theme, componentName);
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
// src/utils/resolveComponentTree.ts
|
|
2468
|
+
var RESOLVER_MAP = {
|
|
2469
|
+
text: resolveTextProps,
|
|
2470
|
+
image: resolveImageProps,
|
|
2471
|
+
shape: resolveShapeProps,
|
|
2472
|
+
table: resolveTableProps,
|
|
2473
|
+
highcharts: resolveHighchartsProps,
|
|
2474
|
+
chart: resolveChartProps
|
|
2475
|
+
};
|
|
2476
|
+
function resolveComponentDefaults(component, theme) {
|
|
2477
|
+
const resolver = RESOLVER_MAP[component.name];
|
|
2478
|
+
const resolvedProps = resolver ? resolver(component.props, theme) : resolveCustomComponentProps(
|
|
2479
|
+
component.props,
|
|
2480
|
+
theme,
|
|
2481
|
+
component.name
|
|
2482
|
+
);
|
|
2483
|
+
return { ...component, props: resolvedProps };
|
|
2484
|
+
}
|
|
2485
|
+
function resolveComponentTree(components, theme) {
|
|
2486
|
+
return components.map((component) => {
|
|
2487
|
+
const resolved = resolveComponentDefaults(component, theme);
|
|
2488
|
+
if (resolved.children && resolved.children.length > 0) {
|
|
2489
|
+
return {
|
|
2490
|
+
...resolved,
|
|
2491
|
+
children: resolveComponentTree(resolved.children, theme)
|
|
2492
|
+
};
|
|
2493
|
+
}
|
|
2494
|
+
return resolved;
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
// src/core/placeholders.ts
|
|
2499
|
+
init_warn();
|
|
2500
|
+
function resolvePlaceholderComponents(slide, template, effectiveGrid, options) {
|
|
2501
|
+
if (!slide.placeholders) return [];
|
|
2502
|
+
const out = [];
|
|
2503
|
+
if (!template) {
|
|
2504
|
+
for (const [name, component] of Object.entries(slide.placeholders)) {
|
|
2505
|
+
const defaulted = resolveComponentDefaults(component, options.theme);
|
|
2506
|
+
const positioned = defaulted.props.x != null || defaulted.props.y != null || defaulted.props.grid;
|
|
2507
|
+
if (!positioned) {
|
|
2508
|
+
warn(
|
|
2509
|
+
options.warnings,
|
|
2510
|
+
W.PLACEHOLDER_NO_POSITION,
|
|
2511
|
+
`Placeholder "${name}" has no template and no explicit position \u2014 skipped`,
|
|
2512
|
+
{ slide: options.slideIndex }
|
|
2513
|
+
);
|
|
2514
|
+
continue;
|
|
2515
|
+
}
|
|
2516
|
+
out.push({
|
|
2517
|
+
name,
|
|
2518
|
+
component: resolveComponentGridPosition(
|
|
2519
|
+
defaulted,
|
|
2520
|
+
effectiveGrid,
|
|
2521
|
+
options.slideWidth,
|
|
2522
|
+
options.slideHeight,
|
|
2523
|
+
options.warnings
|
|
2524
|
+
)
|
|
2525
|
+
});
|
|
2526
|
+
}
|
|
2527
|
+
return out;
|
|
2528
|
+
}
|
|
2529
|
+
const declared = new Map(
|
|
2530
|
+
(template.placeholders ?? []).map((placeholder) => [
|
|
2531
|
+
placeholder.name,
|
|
2532
|
+
placeholder
|
|
2533
|
+
])
|
|
2534
|
+
);
|
|
2535
|
+
for (const [name, component] of Object.entries(slide.placeholders)) {
|
|
2536
|
+
const definition = declared.get(name);
|
|
2537
|
+
if (!definition) {
|
|
2538
|
+
warn(
|
|
2539
|
+
options.warnings,
|
|
2540
|
+
W.UNKNOWN_PLACEHOLDER,
|
|
2541
|
+
`Unknown placeholder "${name}" in template "${slide.template}". Available: ${[...declared.keys()].join(", ")}`,
|
|
2542
|
+
{ slide: options.slideIndex }
|
|
2543
|
+
);
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
const gridResolved = resolveComponentGridPosition(
|
|
2547
|
+
component,
|
|
2548
|
+
effectiveGrid,
|
|
2549
|
+
options.slideWidth,
|
|
2550
|
+
options.slideHeight,
|
|
2551
|
+
options.warnings
|
|
2552
|
+
);
|
|
2553
|
+
const typeDefaults = getDefaultsForType(component.name, options.theme);
|
|
2554
|
+
const positionDefaults = {};
|
|
2555
|
+
if (definition.x != null) positionDefaults.x = definition.x;
|
|
2556
|
+
if (definition.y != null) positionDefaults.y = definition.y;
|
|
2557
|
+
if (definition.w != null) positionDefaults.w = definition.w;
|
|
2558
|
+
if (definition.h != null) positionDefaults.h = definition.h;
|
|
2559
|
+
let props = mergeWithDefaults2(positionDefaults, typeDefaults);
|
|
2560
|
+
props = mergeWithDefaults2(definition.defaults?.props ?? {}, props);
|
|
2561
|
+
props = mergeWithDefaults2(gridResolved.props, props);
|
|
2562
|
+
out.push({ name, component: { ...gridResolved, props } });
|
|
2563
|
+
}
|
|
2564
|
+
return out;
|
|
2565
|
+
}
|
|
2566
|
+
|
|
2567
|
+
// src/ir/resources.ts
|
|
2568
|
+
init_units();
|
|
2569
|
+
var ResourceTable = class {
|
|
2570
|
+
byKey = /* @__PURE__ */ new Map();
|
|
2571
|
+
order = [];
|
|
2572
|
+
/**
|
|
2573
|
+
* Add a resource (or return the existing one with matching identity) and
|
|
2574
|
+
* return its id.
|
|
2575
|
+
*/
|
|
2576
|
+
intern(input, intrinsic) {
|
|
2577
|
+
const origin = toOrigin(input);
|
|
2578
|
+
const key = identity(origin);
|
|
2579
2579
|
const existing = this.byKey.get(key);
|
|
2580
2580
|
if (existing) {
|
|
2581
2581
|
if (!existing.intrinsic && intrinsic) existing.intrinsic = intrinsic;
|
|
@@ -2673,7 +2673,8 @@ function compilePresentation(processed, warnings = []) {
|
|
|
2673
2673
|
extent: {
|
|
2674
2674
|
widthEmu: inchesToEmu(processed.slideWidth),
|
|
2675
2675
|
heightEmu: inchesToEmu(processed.slideHeight)
|
|
2676
|
-
}
|
|
2676
|
+
},
|
|
2677
|
+
positionlessText: []
|
|
2677
2678
|
};
|
|
2678
2679
|
const masters = (processed.templates ?? []).map(
|
|
2679
2680
|
(template, index) => compileMaster(template, index, processed, ctx)
|
|
@@ -2782,6 +2783,7 @@ function compileSlide(slide, slideIndex, processed, ctx) {
|
|
|
2782
2783
|
if (element) elements.push(element);
|
|
2783
2784
|
nextIndex += 1;
|
|
2784
2785
|
};
|
|
2786
|
+
ctx.positionlessText = [];
|
|
2785
2787
|
const gradientSource = slide.background?.gradient ?? (slide.background ? void 0 : template?.background?.gradient);
|
|
2786
2788
|
let background2;
|
|
2787
2789
|
if (gradientSource) {
|
|
@@ -2839,12 +2841,17 @@ function compileSlide(slide, slideIndex, processed, ctx) {
|
|
|
2839
2841
|
})
|
|
2840
2842
|
);
|
|
2841
2843
|
}
|
|
2842
|
-
for (const component of
|
|
2844
|
+
for (const { component } of resolvePlaceholderComponents(
|
|
2843
2845
|
slide,
|
|
2844
2846
|
template,
|
|
2845
2847
|
effectiveGrid,
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
+
{
|
|
2849
|
+
theme: ctx.theme,
|
|
2850
|
+
slideWidth: ctx.slideWidthInches,
|
|
2851
|
+
slideHeight: ctx.slideHeightInches,
|
|
2852
|
+
slideIndex,
|
|
2853
|
+
warnings: ctx.warnings
|
|
2854
|
+
}
|
|
2848
2855
|
)) {
|
|
2849
2856
|
push(
|
|
2850
2857
|
compileComponent(component, {
|
|
@@ -2855,6 +2862,7 @@ function compileSlide(slide, slideIndex, processed, ctx) {
|
|
|
2855
2862
|
})
|
|
2856
2863
|
);
|
|
2857
2864
|
}
|
|
2865
|
+
warnOverlappingText(slideIndex, ctx);
|
|
2858
2866
|
if (slide.notes) ctx.features.require("speaker-notes", `${path4}.notes`);
|
|
2859
2867
|
if (slide.hidden) ctx.features.require("hidden-slides", `${path4}.hidden`);
|
|
2860
2868
|
if (background2) ctx.features.require("backgrounds", `${path4}.background`);
|
|
@@ -2871,6 +2879,23 @@ function compileSlide(slide, slideIndex, processed, ctx) {
|
|
|
2871
2879
|
...transition ? { transition } : {}
|
|
2872
2880
|
};
|
|
2873
2881
|
}
|
|
2882
|
+
function warnOverlappingText(slideIndex, ctx) {
|
|
2883
|
+
const boxes = ctx.positionlessText;
|
|
2884
|
+
for (let i = 0; i < boxes.length; i += 1) {
|
|
2885
|
+
for (let j = i + 1; j < boxes.length; j += 1) {
|
|
2886
|
+
const a = boxes[i];
|
|
2887
|
+
const b = boxes[j];
|
|
2888
|
+
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;
|
|
2889
|
+
if (!overlaps) continue;
|
|
2890
|
+
warn(
|
|
2891
|
+
ctx.warnings,
|
|
2892
|
+
W.TEXT_OVERLAP_UNPOSITIONED,
|
|
2893
|
+
`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.`,
|
|
2894
|
+
{ slide: slideIndex, component: "text" }
|
|
2895
|
+
);
|
|
2896
|
+
}
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2874
2899
|
function compileTransition(transition) {
|
|
2875
2900
|
const type = transition?.type;
|
|
2876
2901
|
if (!type || type === "none") return void 0;
|
|
@@ -2887,71 +2912,6 @@ function slideContextFor(slideIndex, processed) {
|
|
|
2887
2912
|
language: processed.language
|
|
2888
2913
|
};
|
|
2889
2914
|
}
|
|
2890
|
-
function compilePlaceholderComponents(slide, template, effectiveGrid, slideIndex, ctx) {
|
|
2891
|
-
if (!slide.placeholders) return [];
|
|
2892
|
-
const out = [];
|
|
2893
|
-
if (!template) {
|
|
2894
|
-
for (const [name, component] of Object.entries(slide.placeholders)) {
|
|
2895
|
-
const defaulted = resolveComponentDefaults(component, ctx.theme);
|
|
2896
|
-
const positioned = defaulted.props.x != null || defaulted.props.y != null || defaulted.props.grid;
|
|
2897
|
-
if (!positioned) {
|
|
2898
|
-
warn(
|
|
2899
|
-
ctx.warnings,
|
|
2900
|
-
W.PLACEHOLDER_NO_POSITION,
|
|
2901
|
-
`Placeholder "${name}" has no template and no explicit position \u2014 skipped`,
|
|
2902
|
-
{ slide: slideIndex }
|
|
2903
|
-
);
|
|
2904
|
-
continue;
|
|
2905
|
-
}
|
|
2906
|
-
out.push(
|
|
2907
|
-
resolveComponentGridPosition(
|
|
2908
|
-
defaulted,
|
|
2909
|
-
effectiveGrid,
|
|
2910
|
-
ctx.slideWidthInches,
|
|
2911
|
-
ctx.slideHeightInches,
|
|
2912
|
-
ctx.warnings
|
|
2913
|
-
)
|
|
2914
|
-
);
|
|
2915
|
-
}
|
|
2916
|
-
return out;
|
|
2917
|
-
}
|
|
2918
|
-
const declared = new Map(
|
|
2919
|
-
(template.placeholders ?? []).map((placeholder) => [
|
|
2920
|
-
placeholder.name,
|
|
2921
|
-
placeholder
|
|
2922
|
-
])
|
|
2923
|
-
);
|
|
2924
|
-
for (const [name, component] of Object.entries(slide.placeholders)) {
|
|
2925
|
-
const definition = declared.get(name);
|
|
2926
|
-
if (!definition) {
|
|
2927
|
-
warn(
|
|
2928
|
-
ctx.warnings,
|
|
2929
|
-
W.UNKNOWN_PLACEHOLDER,
|
|
2930
|
-
`Unknown placeholder "${name}" in template "${slide.template}". Available: ${[...declared.keys()].join(", ")}`,
|
|
2931
|
-
{ slide: slideIndex }
|
|
2932
|
-
);
|
|
2933
|
-
continue;
|
|
2934
|
-
}
|
|
2935
|
-
const gridResolved = resolveComponentGridPosition(
|
|
2936
|
-
component,
|
|
2937
|
-
effectiveGrid,
|
|
2938
|
-
ctx.slideWidthInches,
|
|
2939
|
-
ctx.slideHeightInches,
|
|
2940
|
-
ctx.warnings
|
|
2941
|
-
);
|
|
2942
|
-
const typeDefaults = getDefaultsForType(component.name, ctx.theme);
|
|
2943
|
-
const positionDefaults = {};
|
|
2944
|
-
if (definition.x != null) positionDefaults.x = definition.x;
|
|
2945
|
-
if (definition.y != null) positionDefaults.y = definition.y;
|
|
2946
|
-
if (definition.w != null) positionDefaults.w = definition.w;
|
|
2947
|
-
if (definition.h != null) positionDefaults.h = definition.h;
|
|
2948
|
-
let props = mergeWithDefaults2(positionDefaults, typeDefaults);
|
|
2949
|
-
props = mergeWithDefaults2(definition.defaults?.props ?? {}, props);
|
|
2950
|
-
props = mergeWithDefaults2(gridResolved.props, props);
|
|
2951
|
-
out.push({ ...gridResolved, props });
|
|
2952
|
-
}
|
|
2953
|
-
return out;
|
|
2954
|
-
}
|
|
2955
2915
|
function compileComponent(component, scope) {
|
|
2956
2916
|
if (component.enabled === false) return void 0;
|
|
2957
2917
|
switch (component.name) {
|
|
@@ -3005,6 +2965,9 @@ function compileText(component, scope) {
|
|
|
3005
2965
|
const heightFontSize = props.fontSize ?? ctx.theme.defaults.fontSize ?? 18;
|
|
3006
2966
|
const transform = textTransform(props, heightFontSize, lineCount, ctx);
|
|
3007
2967
|
const autoFit = props.h === void 0;
|
|
2968
|
+
if (props.x === void 0 && props.y === void 0) {
|
|
2969
|
+
ctx.positionlessText.push({ path: path4, transform });
|
|
2970
|
+
}
|
|
3008
2971
|
const hyperlink2 = compileHyperlink(props.hyperlink, "text", ctx, path4);
|
|
3009
2972
|
const runs = runProps ? runProps.map(
|
|
3010
2973
|
(run) => compileRichRun(run, cascade, void 0, scope.slideCtx, ctx)
|
|
@@ -3210,12 +3173,26 @@ function requireBulletFeatures(bullet, path4, ctx) {
|
|
|
3210
3173
|
ctx.features.require("complex-bullet-glyphs", `${path4}.bullet.style`);
|
|
3211
3174
|
}
|
|
3212
3175
|
}
|
|
3176
|
+
var STYLE_BANDS = {
|
|
3177
|
+
// A title slide's title and its subtitle, as two bands in the middle third.
|
|
3178
|
+
title: { x: 0.08, y: 0.34, w: 0.84 },
|
|
3179
|
+
subtitle: { x: 0.08, y: 0.56, w: 0.84 },
|
|
3180
|
+
// Content-slide headings sit in the top margin, all three at the same place:
|
|
3181
|
+
// the level changes the type, not where the slide starts.
|
|
3182
|
+
heading1: { x: 0.06, y: 0.06, w: 0.88 },
|
|
3183
|
+
heading2: { x: 0.06, y: 0.06, w: 0.88 },
|
|
3184
|
+
heading3: { x: 0.06, y: 0.06, w: 0.88 },
|
|
3185
|
+
// Below a heading, in the content-safe area.
|
|
3186
|
+
body: { x: 0.06, y: 0.26, w: 0.88 },
|
|
3187
|
+
caption: { x: 0.06, y: 0.88, w: 0.88 }
|
|
3188
|
+
};
|
|
3213
3189
|
function textTransform(props, fontSize, lineCount, ctx) {
|
|
3214
3190
|
const heightEmu = props.h !== void 0 ? resolveDimensionEmu(props.h, "Y", ctx.extent) : inchesToEmu(Math.max(0.5, fontSize / 72 * 1.6 * lineCount));
|
|
3191
|
+
const band = typeof props.style === "string" ? STYLE_BANDS[props.style] : void 0;
|
|
3215
3192
|
return {
|
|
3216
|
-
xEmu: props.x !== void 0 ? resolveDimensionEmu(props.x, "X", ctx.extent) : 0,
|
|
3217
|
-
yEmu: props.y !== void 0 ? resolveDimensionEmu(props.y, "Y", ctx.extent) : 0,
|
|
3218
|
-
widthEmu: props.w !== void 0 ? resolveDimensionEmu(props.w, "X", ctx.extent) : defaultWidthEmu(ctx.extent),
|
|
3193
|
+
xEmu: props.x !== void 0 ? resolveDimensionEmu(props.x, "X", ctx.extent) : Math.round((band?.x ?? 0) * ctx.extent.widthEmu),
|
|
3194
|
+
yEmu: props.y !== void 0 ? resolveDimensionEmu(props.y, "Y", ctx.extent) : Math.round((band?.y ?? 0) * ctx.extent.heightEmu),
|
|
3195
|
+
widthEmu: props.w !== void 0 ? resolveDimensionEmu(props.w, "X", ctx.extent) : band ? Math.round(band.w * ctx.extent.widthEmu) : defaultWidthEmu(ctx.extent),
|
|
3219
3196
|
heightEmu,
|
|
3220
3197
|
...rotationProperty(props.rotate)
|
|
3221
3198
|
};
|
|
@@ -3437,6 +3414,11 @@ function compileTable(component, scope) {
|
|
|
3437
3414
|
const rows = authoredRows.map((row) => ({
|
|
3438
3415
|
cells: row.map((cell) => compileTableCell(cell, props, ctx))
|
|
3439
3416
|
}));
|
|
3417
|
+
if (props.headerRow && rows.length > 0) {
|
|
3418
|
+
rows[0] = {
|
|
3419
|
+
cells: rows[0].cells.map((cell) => headerCell(cell, props, ctx))
|
|
3420
|
+
};
|
|
3421
|
+
}
|
|
3440
3422
|
const border = compileTableBorder(props.border, ctx);
|
|
3441
3423
|
const cornerRadius = typeof props.borderRadius === "number" && props.borderRadius > 0 ? props.borderRadius : void 0;
|
|
3442
3424
|
ctx.features.require("tables", path4);
|
|
@@ -3501,6 +3483,19 @@ function toEmuList(value, axis, ctx) {
|
|
|
3501
3483
|
const values = Array.isArray(value) ? value : [value];
|
|
3502
3484
|
return values.map((v) => resolveDimensionEmu(v, axis, ctx.extent));
|
|
3503
3485
|
}
|
|
3486
|
+
function headerCell(cell, tableProps, ctx) {
|
|
3487
|
+
const formatting = {
|
|
3488
|
+
...tableProps.fontWeight == null ? { bold: true } : {},
|
|
3489
|
+
...tableProps.color ? {} : { color: irColor(resolveColor("text", ctx.theme, ctx.warnings)) },
|
|
3490
|
+
...cell.formatting
|
|
3491
|
+
};
|
|
3492
|
+
const fill2 = cell.fill ?? (tableProps.fill ? void 0 : irColor(resolveColor("background2", ctx.theme, ctx.warnings)));
|
|
3493
|
+
return {
|
|
3494
|
+
...cell,
|
|
3495
|
+
formatting,
|
|
3496
|
+
...fill2 ? { fill: fill2 } : {}
|
|
3497
|
+
};
|
|
3498
|
+
}
|
|
3504
3499
|
function compileTableDefaults(props, ctx) {
|
|
3505
3500
|
const family = props.fontFace ?? ctx.theme.fonts.body;
|
|
3506
3501
|
let fontFamily = family;
|
|
@@ -4045,7 +4040,9 @@ function optionalTransform(x, y, w, h, ctx) {
|
|
|
4045
4040
|
}
|
|
4046
4041
|
|
|
4047
4042
|
// src/renderers/registry.ts
|
|
4048
|
-
import {
|
|
4043
|
+
import {
|
|
4044
|
+
RendererRegistry
|
|
4045
|
+
} from "@json-to-office/shared/rendering";
|
|
4049
4046
|
|
|
4050
4047
|
// src/renderers/types.ts
|
|
4051
4048
|
import {
|
|
@@ -4071,6 +4068,9 @@ function resolvePptxRenderer(id) {
|
|
|
4071
4068
|
function pptxRendererIds() {
|
|
4072
4069
|
return registry.ids();
|
|
4073
4070
|
}
|
|
4071
|
+
function pptxRendererStatuses() {
|
|
4072
|
+
return registry.statuses();
|
|
4073
|
+
}
|
|
4074
4074
|
function isPptxRendererId(value) {
|
|
4075
4075
|
return registry.has(value);
|
|
4076
4076
|
}
|
|
@@ -4146,134 +4146,6 @@ async function resolveDocumentFonts(document, theme, warnings, fonts) {
|
|
|
4146
4146
|
return resolved;
|
|
4147
4147
|
}
|
|
4148
4148
|
|
|
4149
|
-
// src/core/generationContext.ts
|
|
4150
|
-
import { applyExportMode } from "@json-to-office/shared";
|
|
4151
|
-
|
|
4152
|
-
// src/themes/defaults.ts
|
|
4153
|
-
var DEFAULT_STYLES = {
|
|
4154
|
-
title: { fontSize: 36, bold: true, fontColor: "text", align: "center" },
|
|
4155
|
-
subtitle: { fontSize: 20, italic: true, fontColor: "text2", align: "center" },
|
|
4156
|
-
heading1: { fontSize: 28, bold: true, fontColor: "primary" },
|
|
4157
|
-
heading2: { fontSize: 22, bold: true, fontColor: "primary" },
|
|
4158
|
-
heading3: { fontSize: 18, bold: true, fontColor: "text" },
|
|
4159
|
-
body: { fontSize: 14 },
|
|
4160
|
-
caption: { fontSize: 10, italic: true, fontColor: "text2" }
|
|
4161
|
-
};
|
|
4162
|
-
var DEFAULT_PPTX_THEME = {
|
|
4163
|
-
name: "default",
|
|
4164
|
-
colors: {
|
|
4165
|
-
primary: "#4472C4",
|
|
4166
|
-
secondary: "#ED7D31",
|
|
4167
|
-
accent: "#70AD47",
|
|
4168
|
-
background: "#FFFFFF",
|
|
4169
|
-
text: "#333333",
|
|
4170
|
-
text2: "#44546A",
|
|
4171
|
-
background2: "#E7E6E6",
|
|
4172
|
-
accent4: "#FFC000",
|
|
4173
|
-
accent5: "#5B9BD5",
|
|
4174
|
-
accent6: "#70AD47"
|
|
4175
|
-
},
|
|
4176
|
-
fonts: {
|
|
4177
|
-
heading: "Arial",
|
|
4178
|
-
body: "Arial"
|
|
4179
|
-
},
|
|
4180
|
-
defaults: {
|
|
4181
|
-
fontSize: 18,
|
|
4182
|
-
fontColor: "#333333"
|
|
4183
|
-
},
|
|
4184
|
-
styles: DEFAULT_STYLES
|
|
4185
|
-
};
|
|
4186
|
-
var PPTX_THEMES = {
|
|
4187
|
-
default: DEFAULT_PPTX_THEME,
|
|
4188
|
-
dark: {
|
|
4189
|
-
name: "dark",
|
|
4190
|
-
colors: {
|
|
4191
|
-
primary: "#5B9BD5",
|
|
4192
|
-
secondary: "#FF6F61",
|
|
4193
|
-
accent: "#6BCB77",
|
|
4194
|
-
background: "#2D2D2D",
|
|
4195
|
-
text: "#FFFFFF",
|
|
4196
|
-
text2: "#CCCCCC",
|
|
4197
|
-
background2: "#3D3D3D",
|
|
4198
|
-
accent4: "#FFB347",
|
|
4199
|
-
accent5: "#77DD77",
|
|
4200
|
-
accent6: "#AEC6CF"
|
|
4201
|
-
},
|
|
4202
|
-
fonts: {
|
|
4203
|
-
heading: "Arial",
|
|
4204
|
-
body: "Arial"
|
|
4205
|
-
},
|
|
4206
|
-
defaults: {
|
|
4207
|
-
fontSize: 18,
|
|
4208
|
-
fontColor: "#FFFFFF"
|
|
4209
|
-
},
|
|
4210
|
-
styles: DEFAULT_STYLES
|
|
4211
|
-
},
|
|
4212
|
-
minimal: {
|
|
4213
|
-
name: "minimal",
|
|
4214
|
-
colors: {
|
|
4215
|
-
primary: "#000000",
|
|
4216
|
-
secondary: "#666666",
|
|
4217
|
-
accent: "#999999",
|
|
4218
|
-
background: "#FFFFFF",
|
|
4219
|
-
text: "#000000",
|
|
4220
|
-
text2: "#444444",
|
|
4221
|
-
background2: "#F5F5F5",
|
|
4222
|
-
accent4: "#BBBBBB",
|
|
4223
|
-
accent5: "#DDDDDD",
|
|
4224
|
-
accent6: "#888888"
|
|
4225
|
-
},
|
|
4226
|
-
fonts: {
|
|
4227
|
-
heading: "Helvetica",
|
|
4228
|
-
body: "Helvetica"
|
|
4229
|
-
},
|
|
4230
|
-
defaults: {
|
|
4231
|
-
fontSize: 18,
|
|
4232
|
-
fontColor: "#000000"
|
|
4233
|
-
},
|
|
4234
|
-
styles: DEFAULT_STYLES
|
|
4235
|
-
}
|
|
4236
|
-
};
|
|
4237
|
-
function getPptxTheme(name) {
|
|
4238
|
-
return PPTX_THEMES[name] || DEFAULT_PPTX_THEME;
|
|
4239
|
-
}
|
|
4240
|
-
function hasPptxTheme(name) {
|
|
4241
|
-
return Object.prototype.hasOwnProperty.call(PPTX_THEMES, name);
|
|
4242
|
-
}
|
|
4243
|
-
var pptxThemes = PPTX_THEMES;
|
|
4244
|
-
|
|
4245
|
-
// src/core/generationContext.ts
|
|
4246
|
-
function resolveThemeContext(documentIn, options = {}) {
|
|
4247
|
-
const { customThemes, fonts, warnings, defaultThemeName, resolveNamedTheme } = options;
|
|
4248
|
-
if (documentIn.props === null) {
|
|
4249
|
-
throw new Error(
|
|
4250
|
-
"Document `props` is null. Omit it, or provide an object \u2014 a null props cannot carry a theme."
|
|
4251
|
-
);
|
|
4252
|
-
}
|
|
4253
|
-
let document = documentIn.props === void 0 ? { ...documentIn, props: {} } : documentIn;
|
|
4254
|
-
let inlineTheme;
|
|
4255
|
-
if (typeof document.props.theme === "object" && document.props.theme !== null) {
|
|
4256
|
-
inlineTheme = document.props.theme;
|
|
4257
|
-
}
|
|
4258
|
-
const authoredThemeName = typeof document.props.theme === "string" ? document.props.theme : void 0;
|
|
4259
|
-
const baseThemeName = inlineTheme ? inlineTheme.name || "inline-theme" : authoredThemeName ?? defaultThemeName ?? "default";
|
|
4260
|
-
let theme = inlineTheme ?? (resolveNamedTheme ? resolveNamedTheme(baseThemeName, authoredThemeName !== void 0) : customThemes?.[baseThemeName] ?? getPptxTheme(baseThemeName));
|
|
4261
|
-
const mode = applyExportMode({ doc: document, theme, fonts });
|
|
4262
|
-
document = mode.doc;
|
|
4263
|
-
theme = mode.theme;
|
|
4264
|
-
for (const w of mode.warnings) {
|
|
4265
|
-
warnings?.push({
|
|
4266
|
-
code: w.code,
|
|
4267
|
-
message: w.message,
|
|
4268
|
-
component: "fontRegistry"
|
|
4269
|
-
});
|
|
4270
|
-
}
|
|
4271
|
-
return {
|
|
4272
|
-
document,
|
|
4273
|
-
theme
|
|
4274
|
-
};
|
|
4275
|
-
}
|
|
4276
|
-
|
|
4277
4149
|
// src/core/generationOptions.ts
|
|
4278
4150
|
import {
|
|
4279
4151
|
collectImageSourceConflicts,
|
|
@@ -4633,6 +4505,144 @@ async function probeIntrinsicSize(source, warnings) {
|
|
|
4633
4505
|
}
|
|
4634
4506
|
}
|
|
4635
4507
|
|
|
4508
|
+
// src/quality/facts.ts
|
|
4509
|
+
import { DEFAULT_PPTX_RENDERER_ID as DEFAULT_PPTX_RENDERER_ID2 } from "@json-to-office/shared-pptx";
|
|
4510
|
+
|
|
4511
|
+
// src/core/generationContext.ts
|
|
4512
|
+
import { applyExportMode } from "@json-to-office/shared";
|
|
4513
|
+
|
|
4514
|
+
// src/themes/defaults.ts
|
|
4515
|
+
var DEFAULT_TABLE = {
|
|
4516
|
+
border: { type: "solid", pt: 1, color: "background2" },
|
|
4517
|
+
headerRow: true
|
|
4518
|
+
};
|
|
4519
|
+
var DEFAULT_STYLES = {
|
|
4520
|
+
title: { fontSize: 36, bold: true, fontColor: "text", align: "center" },
|
|
4521
|
+
subtitle: { fontSize: 20, italic: true, fontColor: "text2", align: "center" },
|
|
4522
|
+
heading1: { fontSize: 28, bold: true, fontColor: "primary" },
|
|
4523
|
+
heading2: { fontSize: 22, bold: true, fontColor: "primary" },
|
|
4524
|
+
heading3: { fontSize: 18, bold: true, fontColor: "text" },
|
|
4525
|
+
body: { fontSize: 14 },
|
|
4526
|
+
caption: { fontSize: 10, italic: true, fontColor: "text2" }
|
|
4527
|
+
};
|
|
4528
|
+
var DEFAULT_PPTX_THEME = {
|
|
4529
|
+
name: "default",
|
|
4530
|
+
colors: {
|
|
4531
|
+
primary: "#4472C4",
|
|
4532
|
+
secondary: "#ED7D31",
|
|
4533
|
+
accent: "#70AD47",
|
|
4534
|
+
background: "#FFFFFF",
|
|
4535
|
+
text: "#333333",
|
|
4536
|
+
text2: "#44546A",
|
|
4537
|
+
background2: "#E7E6E6",
|
|
4538
|
+
accent4: "#FFC000",
|
|
4539
|
+
accent5: "#5B9BD5",
|
|
4540
|
+
accent6: "#70AD47"
|
|
4541
|
+
},
|
|
4542
|
+
fonts: {
|
|
4543
|
+
heading: "Arial",
|
|
4544
|
+
body: "Arial"
|
|
4545
|
+
},
|
|
4546
|
+
defaults: {
|
|
4547
|
+
fontSize: 18,
|
|
4548
|
+
fontColor: "#333333"
|
|
4549
|
+
},
|
|
4550
|
+
styles: DEFAULT_STYLES,
|
|
4551
|
+
componentDefaults: { table: DEFAULT_TABLE }
|
|
4552
|
+
};
|
|
4553
|
+
var PPTX_THEMES = {
|
|
4554
|
+
default: DEFAULT_PPTX_THEME,
|
|
4555
|
+
dark: {
|
|
4556
|
+
name: "dark",
|
|
4557
|
+
colors: {
|
|
4558
|
+
primary: "#5B9BD5",
|
|
4559
|
+
secondary: "#FF6F61",
|
|
4560
|
+
accent: "#6BCB77",
|
|
4561
|
+
background: "#2D2D2D",
|
|
4562
|
+
text: "#FFFFFF",
|
|
4563
|
+
text2: "#CCCCCC",
|
|
4564
|
+
background2: "#3D3D3D",
|
|
4565
|
+
accent4: "#FFB347",
|
|
4566
|
+
accent5: "#77DD77",
|
|
4567
|
+
accent6: "#AEC6CF"
|
|
4568
|
+
},
|
|
4569
|
+
fonts: {
|
|
4570
|
+
heading: "Arial",
|
|
4571
|
+
body: "Arial"
|
|
4572
|
+
},
|
|
4573
|
+
defaults: {
|
|
4574
|
+
fontSize: 18,
|
|
4575
|
+
fontColor: "#FFFFFF"
|
|
4576
|
+
},
|
|
4577
|
+
styles: DEFAULT_STYLES,
|
|
4578
|
+
componentDefaults: { table: DEFAULT_TABLE }
|
|
4579
|
+
},
|
|
4580
|
+
minimal: {
|
|
4581
|
+
name: "minimal",
|
|
4582
|
+
colors: {
|
|
4583
|
+
primary: "#000000",
|
|
4584
|
+
secondary: "#666666",
|
|
4585
|
+
accent: "#999999",
|
|
4586
|
+
background: "#FFFFFF",
|
|
4587
|
+
text: "#000000",
|
|
4588
|
+
text2: "#444444",
|
|
4589
|
+
background2: "#F5F5F5",
|
|
4590
|
+
accent4: "#BBBBBB",
|
|
4591
|
+
accent5: "#DDDDDD",
|
|
4592
|
+
accent6: "#888888"
|
|
4593
|
+
},
|
|
4594
|
+
fonts: {
|
|
4595
|
+
heading: "Helvetica",
|
|
4596
|
+
body: "Helvetica"
|
|
4597
|
+
},
|
|
4598
|
+
defaults: {
|
|
4599
|
+
fontSize: 18,
|
|
4600
|
+
fontColor: "#000000"
|
|
4601
|
+
},
|
|
4602
|
+
styles: DEFAULT_STYLES,
|
|
4603
|
+
componentDefaults: { table: DEFAULT_TABLE }
|
|
4604
|
+
}
|
|
4605
|
+
};
|
|
4606
|
+
function getPptxTheme(name) {
|
|
4607
|
+
return PPTX_THEMES[name] || DEFAULT_PPTX_THEME;
|
|
4608
|
+
}
|
|
4609
|
+
function hasPptxTheme(name) {
|
|
4610
|
+
return Object.prototype.hasOwnProperty.call(PPTX_THEMES, name);
|
|
4611
|
+
}
|
|
4612
|
+
var pptxThemes = PPTX_THEMES;
|
|
4613
|
+
|
|
4614
|
+
// src/core/generationContext.ts
|
|
4615
|
+
function resolveThemeContext(documentIn, options = {}) {
|
|
4616
|
+
const { customThemes, fonts, warnings, defaultThemeName, resolveNamedTheme } = options;
|
|
4617
|
+
if (documentIn.props === null) {
|
|
4618
|
+
throw new Error(
|
|
4619
|
+
"Document `props` is null. Omit it, or provide an object \u2014 a null props cannot carry a theme."
|
|
4620
|
+
);
|
|
4621
|
+
}
|
|
4622
|
+
let document = documentIn.props === void 0 ? { ...documentIn, props: {} } : documentIn;
|
|
4623
|
+
let inlineTheme;
|
|
4624
|
+
if (typeof document.props.theme === "object" && document.props.theme !== null) {
|
|
4625
|
+
inlineTheme = document.props.theme;
|
|
4626
|
+
}
|
|
4627
|
+
const authoredThemeName = typeof document.props.theme === "string" ? document.props.theme : void 0;
|
|
4628
|
+
const baseThemeName = inlineTheme ? inlineTheme.name || "inline-theme" : authoredThemeName ?? defaultThemeName ?? "default";
|
|
4629
|
+
let theme = inlineTheme ?? (resolveNamedTheme ? resolveNamedTheme(baseThemeName, authoredThemeName !== void 0) : customThemes?.[baseThemeName] ?? getPptxTheme(baseThemeName));
|
|
4630
|
+
const mode = applyExportMode({ doc: document, theme, fonts });
|
|
4631
|
+
document = mode.doc;
|
|
4632
|
+
theme = mode.theme;
|
|
4633
|
+
for (const w of mode.warnings) {
|
|
4634
|
+
warnings?.push({
|
|
4635
|
+
code: w.code,
|
|
4636
|
+
message: w.message,
|
|
4637
|
+
component: "fontRegistry"
|
|
4638
|
+
});
|
|
4639
|
+
}
|
|
4640
|
+
return {
|
|
4641
|
+
document,
|
|
4642
|
+
theme
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4645
|
+
|
|
4636
4646
|
// src/core/structure.ts
|
|
4637
4647
|
import { mergeWithDefaults as mergeWithDefaults3 } from "@json-to-office/shared";
|
|
4638
4648
|
function isSlideEnabled(child) {
|
|
@@ -4755,6 +4765,458 @@ function processPresentation(document, options) {
|
|
|
4755
4765
|
};
|
|
4756
4766
|
}
|
|
4757
4767
|
|
|
4768
|
+
// src/quality/facts.ts
|
|
4769
|
+
function asRecord(value) {
|
|
4770
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
4771
|
+
}
|
|
4772
|
+
function asNumber(value) {
|
|
4773
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
4774
|
+
}
|
|
4775
|
+
function dimToPt(value, axisIn) {
|
|
4776
|
+
if (typeof value === "number" && Number.isFinite(value)) return value * 72;
|
|
4777
|
+
if (typeof value === "string") {
|
|
4778
|
+
const trimmed = value.trim();
|
|
4779
|
+
if (trimmed.endsWith("%")) {
|
|
4780
|
+
const pct = Number(trimmed.slice(0, -1));
|
|
4781
|
+
return Number.isFinite(pct) ? pct / 100 * axisIn * 72 : void 0;
|
|
4782
|
+
}
|
|
4783
|
+
const inches = Number(trimmed);
|
|
4784
|
+
return Number.isFinite(inches) ? inches * 72 : void 0;
|
|
4785
|
+
}
|
|
4786
|
+
return void 0;
|
|
4787
|
+
}
|
|
4788
|
+
function resolveBox(props, grid, slideWidthIn, slideHeightIn) {
|
|
4789
|
+
let xPt = dimToPt(props.x, slideWidthIn);
|
|
4790
|
+
let yPt = dimToPt(props.y, slideHeightIn);
|
|
4791
|
+
let widthPt = dimToPt(props.w, slideWidthIn);
|
|
4792
|
+
let heightPt = dimToPt(props.h, slideHeightIn);
|
|
4793
|
+
const gridPos = asRecord(props.grid);
|
|
4794
|
+
if (gridPos !== void 0 && asNumber(gridPos.column) !== void 0 && asNumber(gridPos.row) !== void 0 && (xPt === void 0 || yPt === void 0 || widthPt === void 0 || heightPt === void 0)) {
|
|
4795
|
+
const resolved = resolveGridPosition(
|
|
4796
|
+
gridPos,
|
|
4797
|
+
grid,
|
|
4798
|
+
slideWidthIn,
|
|
4799
|
+
slideHeightIn
|
|
4800
|
+
);
|
|
4801
|
+
xPt ??= resolved.x * 72;
|
|
4802
|
+
yPt ??= resolved.y * 72;
|
|
4803
|
+
widthPt ??= resolved.w * 72;
|
|
4804
|
+
heightPt ??= resolved.h * 72;
|
|
4805
|
+
}
|
|
4806
|
+
return { xPt, yPt, widthPt, heightPt };
|
|
4807
|
+
}
|
|
4808
|
+
function isCompleteBox(box) {
|
|
4809
|
+
return box.xPt !== void 0 && box.yPt !== void 0 && box.widthPt !== void 0 && box.heightPt !== void 0 && box.widthPt > 0 && box.heightPt > 0;
|
|
4810
|
+
}
|
|
4811
|
+
function containsCentre(surface, text) {
|
|
4812
|
+
const cx = text.xPt + text.widthPt / 2;
|
|
4813
|
+
const cy = text.yPt + text.heightPt / 2;
|
|
4814
|
+
return cx >= surface.xPt && cx <= surface.xPt + surface.widthPt && cy >= surface.yPt && cy <= surface.yPt + surface.heightPt;
|
|
4815
|
+
}
|
|
4816
|
+
function themeContext(theme) {
|
|
4817
|
+
return {
|
|
4818
|
+
styles: theme.styles ?? {},
|
|
4819
|
+
defaultFontSize: asNumber(theme.defaults?.fontSize) ?? 18
|
|
4820
|
+
};
|
|
4821
|
+
}
|
|
4822
|
+
function fillColorHexes(fill2, theme) {
|
|
4823
|
+
const found = [];
|
|
4824
|
+
const visit = (node) => {
|
|
4825
|
+
if (Array.isArray(node)) {
|
|
4826
|
+
node.forEach(visit);
|
|
4827
|
+
return;
|
|
4828
|
+
}
|
|
4829
|
+
const rec = asRecord(node);
|
|
4830
|
+
if (!rec) return;
|
|
4831
|
+
if (typeof rec.color === "string") {
|
|
4832
|
+
const hex = resolveColor(rec.color, theme);
|
|
4833
|
+
if (hex) found.push(hex.toUpperCase());
|
|
4834
|
+
}
|
|
4835
|
+
for (const value of Object.values(rec)) {
|
|
4836
|
+
if (typeof value === "object" && value !== null) visit(value);
|
|
4837
|
+
}
|
|
4838
|
+
};
|
|
4839
|
+
visit(fill2);
|
|
4840
|
+
return [...new Set(found)];
|
|
4841
|
+
}
|
|
4842
|
+
var FOCUS_CORNERS = {
|
|
4843
|
+
topLeft: [0, 0],
|
|
4844
|
+
topRight: [1, 0],
|
|
4845
|
+
bottomLeft: [0, 1],
|
|
4846
|
+
bottomRight: [1, 1]
|
|
4847
|
+
};
|
|
4848
|
+
function lerpHex(a, b, t) {
|
|
4849
|
+
const pa = parseInt(a, 16);
|
|
4850
|
+
const pb = parseInt(b, 16);
|
|
4851
|
+
const mix = (shift) => Math.round(
|
|
4852
|
+
(pa >> shift & 255) + ((pb >> shift & 255) - (pa >> shift & 255)) * t
|
|
4853
|
+
);
|
|
4854
|
+
return [mix(16), mix(8), mix(0)].map((c) => c.toString(16).padStart(2, "0")).join("").toUpperCase();
|
|
4855
|
+
}
|
|
4856
|
+
function gradientPosition(gradient, fx, fy, widthUnits, heightUnits) {
|
|
4857
|
+
if (gradient.type === "radial") {
|
|
4858
|
+
const focus = FOCUS_CORNERS[typeof gradient.focus === "string" ? gradient.focus : "topLeft"] ?? FOCUS_CORNERS.topLeft;
|
|
4859
|
+
const radius = Math.hypot(widthUnits, heightUnits) / 2;
|
|
4860
|
+
if (radius === 0) return 0;
|
|
4861
|
+
const distance = Math.hypot(
|
|
4862
|
+
(fx - focus[0]) * widthUnits,
|
|
4863
|
+
(fy - focus[1]) * heightUnits
|
|
4864
|
+
);
|
|
4865
|
+
return Math.min(1, distance / radius);
|
|
4866
|
+
}
|
|
4867
|
+
const angle = (asNumber(gradient.angle) ?? 0) * Math.PI / 180;
|
|
4868
|
+
const dx = Math.cos(angle);
|
|
4869
|
+
const dy = Math.sin(angle);
|
|
4870
|
+
const min = Math.min(0, dx) + Math.min(0, dy);
|
|
4871
|
+
const max = Math.max(0, dx) + Math.max(0, dy);
|
|
4872
|
+
if (max === min) return 0;
|
|
4873
|
+
return Math.min(1, Math.max(0, (fx * dx + fy * dy - min) / (max - min)));
|
|
4874
|
+
}
|
|
4875
|
+
function paintedColorHexes(fill2, theme, fx, fy, widthUnits, heightUnits) {
|
|
4876
|
+
const rec = asRecord(fill2);
|
|
4877
|
+
const gradient = asRecord(rec?.gradient);
|
|
4878
|
+
const stops = Array.isArray(gradient?.stops) ? gradient.stops : void 0;
|
|
4879
|
+
if (gradient && stops && stops.length > 0) {
|
|
4880
|
+
const parsed = stops.flatMap((stop) => {
|
|
4881
|
+
const entry = asRecord(stop);
|
|
4882
|
+
const color2 = typeof entry?.color === "string" ? entry.color : void 0;
|
|
4883
|
+
if (!color2) return [];
|
|
4884
|
+
const hex = resolveColor(color2, theme);
|
|
4885
|
+
if (!hex) return [];
|
|
4886
|
+
return [{ pos: asNumber(entry?.pos) ?? 0, hex: hex.toUpperCase() }];
|
|
4887
|
+
}).sort((a, b) => a.pos - b.pos);
|
|
4888
|
+
if (parsed.length === 0) return [];
|
|
4889
|
+
const target = gradientPosition(gradient, fx, fy, widthUnits, heightUnits) * 100;
|
|
4890
|
+
if (target <= parsed[0].pos) return [parsed[0].hex];
|
|
4891
|
+
const last = parsed[parsed.length - 1];
|
|
4892
|
+
if (target >= last.pos) return [last.hex];
|
|
4893
|
+
for (let i = 1; i < parsed.length; i += 1) {
|
|
4894
|
+
const previous = parsed[i - 1];
|
|
4895
|
+
const next = parsed[i];
|
|
4896
|
+
if (target <= next.pos) {
|
|
4897
|
+
const span = next.pos - previous.pos;
|
|
4898
|
+
const t = span === 0 ? 0 : (target - previous.pos) / span;
|
|
4899
|
+
return [lerpHex(previous.hex, next.hex, t)];
|
|
4900
|
+
}
|
|
4901
|
+
}
|
|
4902
|
+
return [last.hex];
|
|
4903
|
+
}
|
|
4904
|
+
return fillColorHexes(fill2, theme);
|
|
4905
|
+
}
|
|
4906
|
+
function defaultLineHeightPt(fontSize) {
|
|
4907
|
+
if (fontSize >= 60) return fontSize * 1.05;
|
|
4908
|
+
if (fontSize >= 28) return fontSize * 1.15;
|
|
4909
|
+
return fontSize * 1.25;
|
|
4910
|
+
}
|
|
4911
|
+
function resolveTypography(props, ctx) {
|
|
4912
|
+
const styleName = typeof props.style === "string" ? props.style : void 0;
|
|
4913
|
+
const style = styleName !== void 0 ? ctx.styles[styleName] : void 0;
|
|
4914
|
+
const fontSize = asNumber(props.fontSize) ?? style?.fontSize ?? ctx.defaultFontSize;
|
|
4915
|
+
const multiple = asNumber(props.lineSpacingMultiple);
|
|
4916
|
+
const lineSpacing = multiple !== void 0 ? fontSize * multiple : asNumber(props.lineSpacing) ?? style?.lineSpacing ?? defaultLineHeightPt(fontSize);
|
|
4917
|
+
const weight = asNumber(props.fontWeight) ?? style?.fontWeight;
|
|
4918
|
+
const bold = weight !== void 0 ? weight >= 600 : typeof props.bold === "boolean" ? props.bold : style?.bold ?? false;
|
|
4919
|
+
return {
|
|
4920
|
+
fontSize,
|
|
4921
|
+
lineSpacing,
|
|
4922
|
+
paraSpaceBefore: asNumber(props.paraSpaceBefore) ?? 0,
|
|
4923
|
+
paraSpaceAfter: asNumber(props.paraSpaceAfter) ?? style?.paraSpaceAfter ?? 0,
|
|
4924
|
+
bold,
|
|
4925
|
+
...styleName && { styleName }
|
|
4926
|
+
};
|
|
4927
|
+
}
|
|
4928
|
+
function collectSlideNodes(component, path4, text, surfaces, counter) {
|
|
4929
|
+
const rec = asRecord(component);
|
|
4930
|
+
if (!rec || rec.enabled === false) return;
|
|
4931
|
+
const props = asRecord(rec.props) ?? {};
|
|
4932
|
+
const order = counter.next++;
|
|
4933
|
+
if (rec.name === "image" || rec.name === "visual" || rec.name === "chart") {
|
|
4934
|
+
surfaces.push({ order, props, isImage: true });
|
|
4935
|
+
} else if (rec.name === "shape" && props.fill !== void 0) {
|
|
4936
|
+
surfaces.push({ order, props, isImage: false });
|
|
4937
|
+
}
|
|
4938
|
+
const content = typeof props.text === "string" ? props.text : void 0;
|
|
4939
|
+
if (content !== void 0 && content.trim() !== "" && props.runs === void 0) {
|
|
4940
|
+
if (rec.name === "text" || rec.name === "shape") {
|
|
4941
|
+
text.push({ props, path: path4, text: content, order });
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
const children = Array.isArray(rec.children) ? rec.children : [];
|
|
4945
|
+
children.forEach(
|
|
4946
|
+
(child, index) => collectSlideNodes(
|
|
4947
|
+
child,
|
|
4948
|
+
`${path4}/children/${index}`,
|
|
4949
|
+
text,
|
|
4950
|
+
surfaces,
|
|
4951
|
+
counter
|
|
4952
|
+
)
|
|
4953
|
+
);
|
|
4954
|
+
}
|
|
4955
|
+
function pointerSegment(value) {
|
|
4956
|
+
return value.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
4957
|
+
}
|
|
4958
|
+
function addSlideFacts(roots, slidePath, renderedIndex, grid, slideWidthIn, slideHeightIn, ctx, theme, slideBackground, analyzedTextPaths, addFact) {
|
|
4959
|
+
const nodes = [];
|
|
4960
|
+
const surfaces = [];
|
|
4961
|
+
const counter = { next: 0 };
|
|
4962
|
+
for (const root of roots) {
|
|
4963
|
+
collectSlideNodes(root.component, root.path, nodes, surfaces, counter);
|
|
4964
|
+
}
|
|
4965
|
+
const surfaceBoxes = surfaces.flatMap((surface) => {
|
|
4966
|
+
const box = resolveBox(surface.props, grid, slideWidthIn, slideHeightIn);
|
|
4967
|
+
return isCompleteBox(box) ? [{ ...surface, box }] : [];
|
|
4968
|
+
});
|
|
4969
|
+
let bodyWords = 0;
|
|
4970
|
+
nodes.forEach((node, nodeIndex) => {
|
|
4971
|
+
const typography = resolveTypography(node.props, ctx);
|
|
4972
|
+
if (typography.styleName !== "title" && typography.styleName !== "subtitle") {
|
|
4973
|
+
bodyWords += node.text.split(/\s+/).filter(Boolean).length;
|
|
4974
|
+
}
|
|
4975
|
+
if (analyzedTextPaths.has(node.path)) return;
|
|
4976
|
+
analyzedTextPaths.add(node.path);
|
|
4977
|
+
const gridPos = asRecord(node.props.grid);
|
|
4978
|
+
const nodeBox = resolveBox(node.props, grid, slideWidthIn, slideHeightIn);
|
|
4979
|
+
const {
|
|
4980
|
+
xPt: boxXPt,
|
|
4981
|
+
yPt: boxYPt,
|
|
4982
|
+
widthPt: boxWidthPt,
|
|
4983
|
+
heightPt: boxHeightPt
|
|
4984
|
+
} = nodeBox;
|
|
4985
|
+
const sampleFractions = (box, originX, originY, widthPt, heightPt) => {
|
|
4986
|
+
if (!isCompleteBox(box) || widthPt <= 0 || heightPt <= 0) {
|
|
4987
|
+
return [[0.5, 0.5]];
|
|
4988
|
+
}
|
|
4989
|
+
const x0 = (box.xPt - originX) / widthPt;
|
|
4990
|
+
const x1 = (box.xPt + box.widthPt - originX) / widthPt;
|
|
4991
|
+
const y0 = (box.yPt - originY) / heightPt;
|
|
4992
|
+
const y1 = (box.yPt + box.heightPt - originY) / heightPt;
|
|
4993
|
+
return [
|
|
4994
|
+
[(x0 + x1) / 2, (y0 + y1) / 2],
|
|
4995
|
+
[x0, y0],
|
|
4996
|
+
[x1, y0],
|
|
4997
|
+
[x0, y1],
|
|
4998
|
+
[x1, y1]
|
|
4999
|
+
];
|
|
5000
|
+
};
|
|
5001
|
+
const slideSamples = sampleFractions(
|
|
5002
|
+
nodeBox,
|
|
5003
|
+
0,
|
|
5004
|
+
0,
|
|
5005
|
+
slideWidthIn * 72,
|
|
5006
|
+
slideHeightIn * 72
|
|
5007
|
+
);
|
|
5008
|
+
const ownFill = [
|
|
5009
|
+
...new Set(
|
|
5010
|
+
sampleFractions(
|
|
5011
|
+
nodeBox,
|
|
5012
|
+
boxXPt ?? 0,
|
|
5013
|
+
boxYPt ?? 0,
|
|
5014
|
+
boxWidthPt ?? 0,
|
|
5015
|
+
boxHeightPt ?? 0
|
|
5016
|
+
).flatMap(
|
|
5017
|
+
([fx, fy]) => paintedColorHexes(
|
|
5018
|
+
node.props.fill,
|
|
5019
|
+
theme,
|
|
5020
|
+
fx,
|
|
5021
|
+
fy,
|
|
5022
|
+
(boxWidthPt ?? 0) / 72,
|
|
5023
|
+
(boxHeightPt ?? 0) / 72
|
|
5024
|
+
)
|
|
5025
|
+
)
|
|
5026
|
+
)
|
|
5027
|
+
];
|
|
5028
|
+
let backgroundHexes = [
|
|
5029
|
+
...new Set(slideSamples.flatMap(([fx, fy]) => slideBackground(fx, fy)))
|
|
5030
|
+
];
|
|
5031
|
+
let backgroundUnknown = false;
|
|
5032
|
+
if (ownFill.length > 0) {
|
|
5033
|
+
backgroundHexes = ownFill;
|
|
5034
|
+
} else if (isCompleteBox(nodeBox)) {
|
|
5035
|
+
const covering = surfaceBoxes.filter(
|
|
5036
|
+
(surface) => surface.order < node.order && containsCentre(surface.box, nodeBox)
|
|
5037
|
+
).pop();
|
|
5038
|
+
if (covering?.isImage) {
|
|
5039
|
+
backgroundUnknown = true;
|
|
5040
|
+
} else if (covering) {
|
|
5041
|
+
const fill2 = [
|
|
5042
|
+
...new Set(
|
|
5043
|
+
sampleFractions(
|
|
5044
|
+
nodeBox,
|
|
5045
|
+
covering.box.xPt,
|
|
5046
|
+
covering.box.yPt,
|
|
5047
|
+
covering.box.widthPt,
|
|
5048
|
+
covering.box.heightPt
|
|
5049
|
+
).flatMap(
|
|
5050
|
+
([fx, fy]) => paintedColorHexes(
|
|
5051
|
+
covering.props.fill,
|
|
5052
|
+
theme,
|
|
5053
|
+
fx,
|
|
5054
|
+
fy,
|
|
5055
|
+
covering.box.widthPt / 72,
|
|
5056
|
+
covering.box.heightPt / 72
|
|
5057
|
+
)
|
|
5058
|
+
)
|
|
5059
|
+
)
|
|
5060
|
+
];
|
|
5061
|
+
if (fill2.length > 0) backgroundHexes = fill2;
|
|
5062
|
+
else backgroundUnknown = true;
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5065
|
+
const colorHex = typeof node.props.color === "string" ? resolveColor(node.props.color, theme)?.toUpperCase() : void 0;
|
|
5066
|
+
addFact({
|
|
5067
|
+
id: `pptx:text:${renderedIndex}:${nodeIndex}:${node.path}`,
|
|
5068
|
+
kind: "pptx/text",
|
|
5069
|
+
path: node.path,
|
|
5070
|
+
slidePath,
|
|
5071
|
+
text: node.text,
|
|
5072
|
+
fontSizePt: typography.fontSize,
|
|
5073
|
+
lineSpacingPt: typography.lineSpacing,
|
|
5074
|
+
paraSpaceBeforePt: typography.paraSpaceBefore,
|
|
5075
|
+
paraSpaceAfterPt: typography.paraSpaceAfter,
|
|
5076
|
+
...typography.styleName && { styleName: typography.styleName },
|
|
5077
|
+
...boxXPt !== void 0 && { boxXPt },
|
|
5078
|
+
...boxYPt !== void 0 && { boxYPt },
|
|
5079
|
+
...boxWidthPt !== void 0 && boxWidthPt > 0 && { boxWidthPt },
|
|
5080
|
+
...boxHeightPt !== void 0 && boxHeightPt > 0 && { boxHeightPt },
|
|
5081
|
+
verticalAlign: node.props.valign === "middle" || node.props.valign === "bottom" ? node.props.valign : "top",
|
|
5082
|
+
rotationDeg: asNumber(node.props.rotate) ?? 0,
|
|
5083
|
+
bold: typography.bold,
|
|
5084
|
+
autoFit: node.props.h === void 0 && gridPos === void 0,
|
|
5085
|
+
...colorHex !== void 0 && { colorHex },
|
|
5086
|
+
...!backgroundUnknown && backgroundHexes.length > 0 && { backgroundHexes }
|
|
5087
|
+
});
|
|
5088
|
+
});
|
|
5089
|
+
addFact({
|
|
5090
|
+
id: `pptx:slide:${renderedIndex}:${slidePath}`,
|
|
5091
|
+
kind: "pptx/slide",
|
|
5092
|
+
path: slidePath,
|
|
5093
|
+
bodyWords
|
|
5094
|
+
});
|
|
5095
|
+
}
|
|
5096
|
+
function preparePptxQualityDocument(document, options = {}) {
|
|
5097
|
+
const facts = [];
|
|
5098
|
+
const provenance = {};
|
|
5099
|
+
const addFact = (fact) => {
|
|
5100
|
+
facts.push(fact);
|
|
5101
|
+
provenance[fact.id] = {
|
|
5102
|
+
path: fact.path,
|
|
5103
|
+
...fact.relatedPaths && { relatedPaths: fact.relatedPaths }
|
|
5104
|
+
};
|
|
5105
|
+
};
|
|
5106
|
+
const props = asRecord(document.props) ?? {};
|
|
5107
|
+
addFact({
|
|
5108
|
+
id: "pptx:canvas",
|
|
5109
|
+
kind: "pptx/canvas",
|
|
5110
|
+
path: "/props",
|
|
5111
|
+
...asNumber(props.slideWidth) !== void 0 && {
|
|
5112
|
+
widthIn: asNumber(props.slideWidth)
|
|
5113
|
+
},
|
|
5114
|
+
...asNumber(props.slideHeight) !== void 0 && {
|
|
5115
|
+
heightIn: asNumber(props.slideHeight)
|
|
5116
|
+
}
|
|
5117
|
+
});
|
|
5118
|
+
const warnings = options.warnings ?? [];
|
|
5119
|
+
const context = resolveThemeContext(document, {
|
|
5120
|
+
customThemes: options.customThemes,
|
|
5121
|
+
fonts: options.fonts,
|
|
5122
|
+
warnings
|
|
5123
|
+
});
|
|
5124
|
+
const processed = processPresentation(context.document, {
|
|
5125
|
+
theme: context.theme,
|
|
5126
|
+
customThemes: options.customThemes,
|
|
5127
|
+
services: options.services
|
|
5128
|
+
});
|
|
5129
|
+
const ctx = themeContext(processed.theme);
|
|
5130
|
+
const authoredChildren = Array.isArray(document.children) ? document.children : [];
|
|
5131
|
+
const slideIndexes = authoredChildren.flatMap((child, index) => {
|
|
5132
|
+
const slide = asRecord(child);
|
|
5133
|
+
return slide?.name === "slide" && slide.enabled !== false ? [index] : [];
|
|
5134
|
+
});
|
|
5135
|
+
const templateIndexes = /* @__PURE__ */ new Map();
|
|
5136
|
+
const templates = new Map(
|
|
5137
|
+
(processed.templates ?? []).map((template, index) => {
|
|
5138
|
+
templateIndexes.set(template.name, index);
|
|
5139
|
+
return [template.name, template];
|
|
5140
|
+
})
|
|
5141
|
+
);
|
|
5142
|
+
const analyzedTextPaths = /* @__PURE__ */ new Set();
|
|
5143
|
+
processed.slides.forEach((slide, renderedIndex) => {
|
|
5144
|
+
const authoredIndex = slideIndexes[renderedIndex];
|
|
5145
|
+
if (authoredIndex === void 0) return;
|
|
5146
|
+
const slidePath = `/children/${authoredIndex}`;
|
|
5147
|
+
const authoredSlide = asRecord(authoredChildren[authoredIndex]);
|
|
5148
|
+
const authoredComponents = Array.isArray(authoredSlide?.children) ? authoredSlide.children : [];
|
|
5149
|
+
const template = slide.template ? templates.get(slide.template) : void 0;
|
|
5150
|
+
const effectiveGrid = mergeGridConfigs(processed.grid, template?.grid);
|
|
5151
|
+
const roots = [];
|
|
5152
|
+
const templateIndex = template ? templateIndexes.get(template.name) : void 0;
|
|
5153
|
+
if (template && templateIndex !== void 0) {
|
|
5154
|
+
template.objects?.forEach((component, index) => {
|
|
5155
|
+
roots.push({
|
|
5156
|
+
component,
|
|
5157
|
+
path: `/props/templates/${templateIndex}/objects/${index}`
|
|
5158
|
+
});
|
|
5159
|
+
});
|
|
5160
|
+
}
|
|
5161
|
+
slide.components.forEach((component, index) => {
|
|
5162
|
+
if (authoredComponents[index] === void 0) return;
|
|
5163
|
+
roots.push({
|
|
5164
|
+
component,
|
|
5165
|
+
path: `${slidePath}/children/${index}`
|
|
5166
|
+
});
|
|
5167
|
+
});
|
|
5168
|
+
for (const resolved of resolvePlaceholderComponents(
|
|
5169
|
+
slide,
|
|
5170
|
+
template,
|
|
5171
|
+
effectiveGrid,
|
|
5172
|
+
{
|
|
5173
|
+
theme: processed.theme,
|
|
5174
|
+
slideWidth: processed.slideWidth,
|
|
5175
|
+
slideHeight: processed.slideHeight,
|
|
5176
|
+
slideIndex: renderedIndex,
|
|
5177
|
+
warnings
|
|
5178
|
+
}
|
|
5179
|
+
)) {
|
|
5180
|
+
roots.push({
|
|
5181
|
+
component: resolved.component,
|
|
5182
|
+
path: `${slidePath}/props/placeholders/${pointerSegment(resolved.name)}`
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
addSlideFacts(
|
|
5186
|
+
roots,
|
|
5187
|
+
slidePath,
|
|
5188
|
+
renderedIndex,
|
|
5189
|
+
effectiveGrid,
|
|
5190
|
+
processed.slideWidth,
|
|
5191
|
+
processed.slideHeight,
|
|
5192
|
+
ctx,
|
|
5193
|
+
processed.theme,
|
|
5194
|
+
(fx, fy) => paintedColorHexes(
|
|
5195
|
+
slide.background ?? template?.background ?? props.background,
|
|
5196
|
+
processed.theme,
|
|
5197
|
+
fx,
|
|
5198
|
+
fy,
|
|
5199
|
+
processed.slideWidth,
|
|
5200
|
+
processed.slideHeight
|
|
5201
|
+
),
|
|
5202
|
+
analyzedTextPaths,
|
|
5203
|
+
addFact
|
|
5204
|
+
);
|
|
5205
|
+
});
|
|
5206
|
+
return {
|
|
5207
|
+
format: "pptx",
|
|
5208
|
+
model: {
|
|
5209
|
+
authored: document,
|
|
5210
|
+
document: context.document,
|
|
5211
|
+
theme: context.theme,
|
|
5212
|
+
processed
|
|
5213
|
+
},
|
|
5214
|
+
facts,
|
|
5215
|
+
provenance,
|
|
5216
|
+
renderer: options.renderer ?? DEFAULT_PPTX_RENDERER_ID2
|
|
5217
|
+
};
|
|
5218
|
+
}
|
|
5219
|
+
|
|
4758
5220
|
// src/core/generateFromIr.ts
|
|
4759
5221
|
var UncompiledComponentError = class extends Error {
|
|
4760
5222
|
code = "UNCOMPILED_COMPONENT";
|
|
@@ -4777,29 +5239,24 @@ async function generateBufferViaIr(jsonConfig, options) {
|
|
|
4777
5239
|
selectedRenderer && PPTX_RENDERER_IDS.includes(selectedRenderer) ? { ...component, renderer: selectedRenderer } : component,
|
|
4778
5240
|
options?.validation
|
|
4779
5241
|
);
|
|
5242
|
+
assertNoContentConflicts(component);
|
|
4780
5243
|
const warnings = [];
|
|
4781
|
-
const
|
|
5244
|
+
const prepared = options?.prepared ?? preparePptxQualityDocument(component, {
|
|
4782
5245
|
customThemes: options?.customThemes,
|
|
4783
5246
|
fonts: options?.fonts,
|
|
4784
|
-
warnings
|
|
5247
|
+
warnings,
|
|
5248
|
+
services: options?.services,
|
|
5249
|
+
renderer: selectedRenderer
|
|
4785
5250
|
});
|
|
4786
|
-
assertNoContentConflicts(context.document);
|
|
4787
5251
|
await resolveDocumentFonts(
|
|
4788
|
-
|
|
4789
|
-
|
|
5252
|
+
prepared.model.document,
|
|
5253
|
+
prepared.model.theme,
|
|
4790
5254
|
warnings,
|
|
4791
5255
|
options?.fonts
|
|
4792
5256
|
);
|
|
4793
5257
|
const buffer = await runWithBaseDir(
|
|
4794
5258
|
options?.baseDir,
|
|
4795
|
-
() => renderProcessedViaIr(
|
|
4796
|
-
processPresentation(context.document, {
|
|
4797
|
-
...effectiveOptions,
|
|
4798
|
-
theme: context.theme
|
|
4799
|
-
}),
|
|
4800
|
-
warnings,
|
|
4801
|
-
effectiveOptions
|
|
4802
|
-
)
|
|
5259
|
+
() => renderProcessedViaIr(prepared.model.processed, warnings, effectiveOptions)
|
|
4803
5260
|
);
|
|
4804
5261
|
return { buffer, warnings };
|
|
4805
5262
|
}
|
|
@@ -4868,6 +5325,465 @@ var PresentationGenerator = {
|
|
|
4868
5325
|
init_finalizePackage();
|
|
4869
5326
|
init_warn();
|
|
4870
5327
|
|
|
5328
|
+
// src/quality/preflight.ts
|
|
5329
|
+
import {
|
|
5330
|
+
assertValidQualityPolicy,
|
|
5331
|
+
assertValidQualityProfile
|
|
5332
|
+
} from "@json-to-office/quality";
|
|
5333
|
+
|
|
5334
|
+
// src/quality/rules.ts
|
|
5335
|
+
import {
|
|
5336
|
+
mergeQualityProfiles,
|
|
5337
|
+
QUALITY_CODES,
|
|
5338
|
+
QualityEngine,
|
|
5339
|
+
resolveRuleConfiguration
|
|
5340
|
+
} from "@json-to-office/quality";
|
|
5341
|
+
var RENDERER_DEFAULT_WIDTH_IN = 10;
|
|
5342
|
+
var RENDERER_DEFAULT_HEIGHT_IN = 7.5;
|
|
5343
|
+
var DEFAULT_CHAR_WIDTH_FACTOR = 0.46;
|
|
5344
|
+
var DEFAULT_SAFETY_BUFFER_PT = 8;
|
|
5345
|
+
var DEFAULT_MIN_READABLE_FONT_PT = 7;
|
|
5346
|
+
var DEFAULT_MAX_BODY_WORDS_PER_SLIDE = 130;
|
|
5347
|
+
var KNOWN_CANVASES = [
|
|
5348
|
+
{ w: 13.333, h: 7.5, label: "16:9 standard" },
|
|
5349
|
+
{ w: 10, h: 5.625, label: "16:9 small" },
|
|
5350
|
+
{ w: 7.5, h: 7.5, label: "1:1 carousel" },
|
|
5351
|
+
{ w: 7.5, h: 9.375, label: "4:5 vertical" },
|
|
5352
|
+
{ w: 4.5, h: 8, label: "9:16 story" },
|
|
5353
|
+
{ w: 10, h: 7.5, label: "4:3 legacy", legacy: true }
|
|
5354
|
+
];
|
|
5355
|
+
function numberParameter(parameters, name, fallback) {
|
|
5356
|
+
const value = parameters[name];
|
|
5357
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
5358
|
+
}
|
|
5359
|
+
function textFacts(facts) {
|
|
5360
|
+
return facts.filter(
|
|
5361
|
+
(fact) => fact.kind === "pptx/text"
|
|
5362
|
+
);
|
|
5363
|
+
}
|
|
5364
|
+
function estimateTextHeightPt(fact, charWidthFactor) {
|
|
5365
|
+
if (fact.boxWidthPt === void 0 || fact.boxHeightPt === void 0) {
|
|
5366
|
+
return void 0;
|
|
5367
|
+
}
|
|
5368
|
+
const paragraphs = fact.text.split("\n");
|
|
5369
|
+
const charsPerLine = Math.max(
|
|
5370
|
+
1,
|
|
5371
|
+
Math.floor(fact.boxWidthPt / (fact.fontSizePt * charWidthFactor))
|
|
5372
|
+
);
|
|
5373
|
+
let lines = 0;
|
|
5374
|
+
for (const paragraph of paragraphs) {
|
|
5375
|
+
const measured = paragraph.trimEnd();
|
|
5376
|
+
lines += measured === "" ? 1 : Math.max(1, Math.ceil(measured.length / charsPerLine));
|
|
5377
|
+
}
|
|
5378
|
+
let heightPt = fact.fontSizePt + Math.max(0, lines - 1) * fact.lineSpacingPt;
|
|
5379
|
+
if (paragraphs.length > 1) {
|
|
5380
|
+
heightPt += (paragraphs.length - 1) * (fact.paraSpaceBeforePt + fact.paraSpaceAfterPt);
|
|
5381
|
+
}
|
|
5382
|
+
return { heightPt, lines };
|
|
5383
|
+
}
|
|
5384
|
+
var pptxCanvasRule = {
|
|
5385
|
+
id: "pptx/canvas",
|
|
5386
|
+
code: QUALITY_CODES.CANVAS_UNSPECIFIED,
|
|
5387
|
+
category: "composition",
|
|
5388
|
+
defaultSeverity: "info",
|
|
5389
|
+
defaultCertainty: "deterministic",
|
|
5390
|
+
formats: ["pptx"],
|
|
5391
|
+
evaluate: ({ facts }) => {
|
|
5392
|
+
const canvas = facts.find(
|
|
5393
|
+
(fact) => fact.kind === "pptx/canvas"
|
|
5394
|
+
);
|
|
5395
|
+
if (!canvas) return [];
|
|
5396
|
+
const { widthIn: width, heightIn: height } = canvas;
|
|
5397
|
+
if (width === void 0 || height === void 0) {
|
|
5398
|
+
const missing = [
|
|
5399
|
+
width === void 0 ? "props.slideWidth" : void 0,
|
|
5400
|
+
height === void 0 ? "props.slideHeight" : void 0
|
|
5401
|
+
].filter((entry) => entry !== void 0);
|
|
5402
|
+
const state = missing.length === 2 ? "No slide canvas declared" : `Incomplete slide canvas (${missing[0]} missing)`;
|
|
5403
|
+
return [
|
|
5404
|
+
{
|
|
5405
|
+
code: QUALITY_CODES.CANVAS_UNSPECIFIED,
|
|
5406
|
+
severity: "warning",
|
|
5407
|
+
category: "integrity",
|
|
5408
|
+
message: `${state}: the renderer falls back to 4:3 (${RENDERER_DEFAULT_WIDTH_IN}\xD7${RENDERER_DEFAULT_HEIGHT_IN}"), and 16:9 content on that canvas leaves a dead strip at the bottom.`,
|
|
5409
|
+
path: canvas.path,
|
|
5410
|
+
suggestion: "Declare props.slideWidth and props.slideHeight \u2014 13.333 \xD7 7.5 for a standard 16:9 deck.",
|
|
5411
|
+
context: {
|
|
5412
|
+
missing,
|
|
5413
|
+
rendererDefault: {
|
|
5414
|
+
slideWidth: RENDERER_DEFAULT_WIDTH_IN,
|
|
5415
|
+
slideHeight: RENDERER_DEFAULT_HEIGHT_IN
|
|
5416
|
+
}
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
];
|
|
5420
|
+
}
|
|
5421
|
+
const match = KNOWN_CANVASES.find(
|
|
5422
|
+
(known) => Math.abs(known.w - width) < 0.01 && Math.abs(known.h - height) < 0.01
|
|
5423
|
+
);
|
|
5424
|
+
if (match?.legacy) {
|
|
5425
|
+
return [
|
|
5426
|
+
{
|
|
5427
|
+
code: QUALITY_CODES.CANVAS_LEGACY,
|
|
5428
|
+
message: `Canvas is 4:3 legacy (${match.w}\xD7${match.h}") \u2014 modern screens are 16:9.`,
|
|
5429
|
+
path: canvas.path,
|
|
5430
|
+
suggestion: "If 4:3 is not deliberate, use slideWidth 13.333 and slideHeight 7.5."
|
|
5431
|
+
}
|
|
5432
|
+
];
|
|
5433
|
+
}
|
|
5434
|
+
if (match) return [];
|
|
5435
|
+
return [
|
|
5436
|
+
{
|
|
5437
|
+
code: QUALITY_CODES.CANVAS_NONSTANDARD,
|
|
5438
|
+
message: `Canvas ${width}\xD7${height}" matches no common preset (16:9, 1:1, 4:5, 9:16).`,
|
|
5439
|
+
path: canvas.path,
|
|
5440
|
+
suggestion: "Confirm the size is deliberate; a mistyped canvas distorts every slide.",
|
|
5441
|
+
context: {
|
|
5442
|
+
knownCanvases: KNOWN_CANVASES.map(({ w, h, label }) => ({
|
|
5443
|
+
slideWidth: w,
|
|
5444
|
+
slideHeight: h,
|
|
5445
|
+
label
|
|
5446
|
+
}))
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
];
|
|
5450
|
+
}
|
|
5451
|
+
};
|
|
5452
|
+
var pptxMinimumFontRule = {
|
|
5453
|
+
id: "pptx/minimum-font-size",
|
|
5454
|
+
code: QUALITY_CODES.FONT_SIZE_MIN,
|
|
5455
|
+
category: "legibility",
|
|
5456
|
+
defaultSeverity: "warning",
|
|
5457
|
+
defaultCertainty: "measured",
|
|
5458
|
+
formats: ["pptx"],
|
|
5459
|
+
defaultParameters: { minimumFontPt: DEFAULT_MIN_READABLE_FONT_PT },
|
|
5460
|
+
evaluate: ({ facts, configuration }) => {
|
|
5461
|
+
const minimum = numberParameter(
|
|
5462
|
+
configuration.parameters,
|
|
5463
|
+
"minimumFontPt",
|
|
5464
|
+
DEFAULT_MIN_READABLE_FONT_PT
|
|
5465
|
+
);
|
|
5466
|
+
return textFacts(facts).filter((fact) => fact.fontSizePt < minimum).map((fact) => ({
|
|
5467
|
+
message: `Effective font size is ${fact.fontSizePt}pt \u2014 unreadable on a projected slide.`,
|
|
5468
|
+
path: `${fact.path}/props`,
|
|
5469
|
+
suggestion: `Use at least ${minimum}pt; captions rarely work below 10pt.`,
|
|
5470
|
+
context: { fontSize: fact.fontSizePt, threshold: minimum },
|
|
5471
|
+
evidence: { actual: fact.fontSizePt, expected: minimum, unit: "pt" },
|
|
5472
|
+
// `add` replaces an existing member, so this lifts an explicit
|
|
5473
|
+
// fontSize and overrides an inherited style value alike.
|
|
5474
|
+
fixes: [
|
|
5475
|
+
{
|
|
5476
|
+
op: "add",
|
|
5477
|
+
path: `${fact.path}/props/fontSize`,
|
|
5478
|
+
value: minimum
|
|
5479
|
+
}
|
|
5480
|
+
]
|
|
5481
|
+
}));
|
|
5482
|
+
}
|
|
5483
|
+
};
|
|
5484
|
+
function fittingFontSizePt(fact, charWidthFactor, minimumFontPt) {
|
|
5485
|
+
if (fact.autoFit === true) return void 0;
|
|
5486
|
+
const minimumWholeSize = Math.ceil(minimumFontPt);
|
|
5487
|
+
for (let size = Math.floor(fact.fontSizePt) - 1; size >= minimumWholeSize; size--) {
|
|
5488
|
+
const estimate = estimateTextHeightPt(
|
|
5489
|
+
{ ...fact, fontSizePt: size },
|
|
5490
|
+
charWidthFactor
|
|
5491
|
+
);
|
|
5492
|
+
if (estimate !== void 0 && fact.boxHeightPt !== void 0 && estimate.heightPt <= fact.boxHeightPt) {
|
|
5493
|
+
return size;
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
return void 0;
|
|
5497
|
+
}
|
|
5498
|
+
var pptxTextFitRule = {
|
|
5499
|
+
id: "pptx/text-fit",
|
|
5500
|
+
code: QUALITY_CODES.TEXT_TIGHT,
|
|
5501
|
+
category: "integrity",
|
|
5502
|
+
defaultSeverity: "info",
|
|
5503
|
+
defaultCertainty: "estimated",
|
|
5504
|
+
formats: ["pptx"],
|
|
5505
|
+
defaultParameters: {
|
|
5506
|
+
characterWidthFactor: DEFAULT_CHAR_WIDTH_FACTOR,
|
|
5507
|
+
safetyBufferPt: DEFAULT_SAFETY_BUFFER_PT
|
|
5508
|
+
},
|
|
5509
|
+
evaluate: ({ facts, configuration, profile, policy }) => {
|
|
5510
|
+
const factor = numberParameter(
|
|
5511
|
+
configuration.parameters,
|
|
5512
|
+
"characterWidthFactor",
|
|
5513
|
+
DEFAULT_CHAR_WIDTH_FACTOR
|
|
5514
|
+
);
|
|
5515
|
+
const safetyBufferPt = numberParameter(
|
|
5516
|
+
configuration.parameters,
|
|
5517
|
+
"safetyBufferPt",
|
|
5518
|
+
DEFAULT_SAFETY_BUFFER_PT
|
|
5519
|
+
);
|
|
5520
|
+
const minimumFontConfiguration = resolveRuleConfiguration(
|
|
5521
|
+
pptxMinimumFontRule,
|
|
5522
|
+
profile,
|
|
5523
|
+
policy
|
|
5524
|
+
);
|
|
5525
|
+
const minimumFontPt = minimumFontConfiguration.enabled ? numberParameter(
|
|
5526
|
+
minimumFontConfiguration.parameters,
|
|
5527
|
+
"minimumFontPt",
|
|
5528
|
+
DEFAULT_MIN_READABLE_FONT_PT
|
|
5529
|
+
) : DEFAULT_MIN_READABLE_FONT_PT;
|
|
5530
|
+
const findings = [];
|
|
5531
|
+
for (const fact of textFacts(facts)) {
|
|
5532
|
+
const estimate = estimateTextHeightPt(fact, factor);
|
|
5533
|
+
if (!estimate || fact.boxHeightPt === void 0) continue;
|
|
5534
|
+
const marginPt = fact.boxHeightPt - estimate.heightPt;
|
|
5535
|
+
const measured = {
|
|
5536
|
+
estimatedTextPt: Math.round(estimate.heightPt * 10) / 10,
|
|
5537
|
+
availablePt: Math.round(fact.boxHeightPt * 10) / 10,
|
|
5538
|
+
marginPt: Math.round(marginPt * 10) / 10,
|
|
5539
|
+
estimatedLines: estimate.lines,
|
|
5540
|
+
fontSize: fact.fontSizePt,
|
|
5541
|
+
boxWidthPt: Math.round(fact.boxWidthPt * 10) / 10
|
|
5542
|
+
};
|
|
5543
|
+
if (marginPt < -fact.lineSpacingPt) {
|
|
5544
|
+
const fittingSize = fittingFontSizePt(fact, factor, minimumFontPt);
|
|
5545
|
+
findings.push({
|
|
5546
|
+
code: QUALITY_CODES.TEXT_OVERFLOW,
|
|
5547
|
+
severity: "warning",
|
|
5548
|
+
message: `Text is estimated at ${measured.estimatedTextPt}pt tall (${estimate.lines} line${estimate.lines === 1 ? "" : "s"} of ${fact.fontSizePt}pt) in a ${measured.availablePt}pt box \u2014 it will overflow.`,
|
|
5549
|
+
path: fact.path,
|
|
5550
|
+
suggestion: "Shorten the text, reduce fontSize, or enlarge the box (h / rowSpan).",
|
|
5551
|
+
context: measured,
|
|
5552
|
+
evidence: {
|
|
5553
|
+
actual: measured.estimatedTextPt,
|
|
5554
|
+
expected: measured.availablePt,
|
|
5555
|
+
unit: "pt"
|
|
5556
|
+
},
|
|
5557
|
+
// A ready-made patch only when a readable size fits; shortening
|
|
5558
|
+
// the text or growing the box stays the author's call.
|
|
5559
|
+
...fittingSize !== void 0 && {
|
|
5560
|
+
fixes: [
|
|
5561
|
+
{
|
|
5562
|
+
op: "add",
|
|
5563
|
+
path: `${fact.path}/props/fontSize`,
|
|
5564
|
+
value: fittingSize
|
|
5565
|
+
}
|
|
5566
|
+
]
|
|
5567
|
+
}
|
|
5568
|
+
});
|
|
5569
|
+
continue;
|
|
5570
|
+
}
|
|
5571
|
+
if (marginPt >= safetyBufferPt) continue;
|
|
5572
|
+
findings.push({
|
|
5573
|
+
code: QUALITY_CODES.TEXT_TIGHT,
|
|
5574
|
+
message: marginPt < 0 ? `Text is estimated to exceed its ${measured.availablePt}pt box by ${-measured.marginPt}pt \u2014 within one line-height, so likely a harmless spill into the gap below.` : `Text fits its box with only ${measured.marginPt}pt to spare \u2014 renderer rounding can push it over.`,
|
|
5575
|
+
path: fact.path,
|
|
5576
|
+
suggestion: `Leave at least ${safetyBufferPt}pt of vertical margin.`,
|
|
5577
|
+
context: measured,
|
|
5578
|
+
evidence: {
|
|
5579
|
+
actual: measured.marginPt,
|
|
5580
|
+
expected: safetyBufferPt,
|
|
5581
|
+
unit: "pt margin"
|
|
5582
|
+
}
|
|
5583
|
+
});
|
|
5584
|
+
}
|
|
5585
|
+
return findings;
|
|
5586
|
+
}
|
|
5587
|
+
};
|
|
5588
|
+
var pptxSlideDensityRule = {
|
|
5589
|
+
id: "pptx/slide-density",
|
|
5590
|
+
code: QUALITY_CODES.SLIDE_DENSITY,
|
|
5591
|
+
category: "information-design",
|
|
5592
|
+
defaultSeverity: "warning",
|
|
5593
|
+
defaultCertainty: "estimated",
|
|
5594
|
+
formats: ["pptx"],
|
|
5595
|
+
defaultParameters: { maximumBodyWords: DEFAULT_MAX_BODY_WORDS_PER_SLIDE },
|
|
5596
|
+
evaluate: ({ facts, configuration }) => {
|
|
5597
|
+
const threshold = numberParameter(
|
|
5598
|
+
configuration.parameters,
|
|
5599
|
+
"maximumBodyWords",
|
|
5600
|
+
DEFAULT_MAX_BODY_WORDS_PER_SLIDE
|
|
5601
|
+
);
|
|
5602
|
+
return facts.filter((fact) => fact.kind === "pptx/slide").filter((fact) => fact.bodyWords > threshold).map((fact) => ({
|
|
5603
|
+
message: `${fact.bodyWords} words of body text on one slide \u2014 an audience reads a slide, it does not study one.`,
|
|
5604
|
+
path: fact.path,
|
|
5605
|
+
suggestion: "One idea per slide: split the content across more slides.",
|
|
5606
|
+
context: { bodyWords: fact.bodyWords, threshold },
|
|
5607
|
+
evidence: {
|
|
5608
|
+
actual: fact.bodyWords,
|
|
5609
|
+
expected: threshold,
|
|
5610
|
+
unit: "words"
|
|
5611
|
+
}
|
|
5612
|
+
}));
|
|
5613
|
+
}
|
|
5614
|
+
};
|
|
5615
|
+
var AA_NORMAL_RATIO = 4.5;
|
|
5616
|
+
var AA_LARGE_RATIO = 3;
|
|
5617
|
+
var LARGE_TEXT_PT = 18;
|
|
5618
|
+
var LARGE_BOLD_TEXT_PT = 14;
|
|
5619
|
+
function channelLuminance(channel) {
|
|
5620
|
+
const c = channel / 255;
|
|
5621
|
+
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
5622
|
+
}
|
|
5623
|
+
function relativeLuminance(hex) {
|
|
5624
|
+
const match = /^#?([0-9a-f]{6})$/i.exec(hex);
|
|
5625
|
+
if (!match) return void 0;
|
|
5626
|
+
const value = parseInt(match[1], 16);
|
|
5627
|
+
return 0.2126 * channelLuminance(value >> 16 & 255) + 0.7152 * channelLuminance(value >> 8 & 255) + 0.0722 * channelLuminance(value & 255);
|
|
5628
|
+
}
|
|
5629
|
+
function contrastRatio(a, b) {
|
|
5630
|
+
const la = relativeLuminance(a);
|
|
5631
|
+
const lb = relativeLuminance(b);
|
|
5632
|
+
if (la === void 0 || lb === void 0) return void 0;
|
|
5633
|
+
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
5634
|
+
}
|
|
5635
|
+
var pptxTextContrastRule = {
|
|
5636
|
+
id: "pptx/text-contrast",
|
|
5637
|
+
code: QUALITY_CODES.TEXT_CONTRAST,
|
|
5638
|
+
category: "accessibility",
|
|
5639
|
+
defaultSeverity: "warning",
|
|
5640
|
+
defaultCertainty: "deterministic",
|
|
5641
|
+
formats: ["pptx"],
|
|
5642
|
+
defaultParameters: {
|
|
5643
|
+
normalRatio: AA_NORMAL_RATIO,
|
|
5644
|
+
largeRatio: AA_LARGE_RATIO,
|
|
5645
|
+
largeTextPt: LARGE_TEXT_PT,
|
|
5646
|
+
largeBoldTextPt: LARGE_BOLD_TEXT_PT
|
|
5647
|
+
},
|
|
5648
|
+
evaluate: ({ facts, configuration }) => {
|
|
5649
|
+
const normalRatio = numberParameter(
|
|
5650
|
+
configuration.parameters,
|
|
5651
|
+
"normalRatio",
|
|
5652
|
+
AA_NORMAL_RATIO
|
|
5653
|
+
);
|
|
5654
|
+
const largeRatio = numberParameter(
|
|
5655
|
+
configuration.parameters,
|
|
5656
|
+
"largeRatio",
|
|
5657
|
+
AA_LARGE_RATIO
|
|
5658
|
+
);
|
|
5659
|
+
const largeTextPt = numberParameter(
|
|
5660
|
+
configuration.parameters,
|
|
5661
|
+
"largeTextPt",
|
|
5662
|
+
LARGE_TEXT_PT
|
|
5663
|
+
);
|
|
5664
|
+
const largeBoldTextPt = numberParameter(
|
|
5665
|
+
configuration.parameters,
|
|
5666
|
+
"largeBoldTextPt",
|
|
5667
|
+
LARGE_BOLD_TEXT_PT
|
|
5668
|
+
);
|
|
5669
|
+
return textFacts(facts).flatMap((fact) => {
|
|
5670
|
+
const { colorHex, backgroundHexes } = fact;
|
|
5671
|
+
if (!colorHex || !backgroundHexes?.length) return [];
|
|
5672
|
+
let worst;
|
|
5673
|
+
for (const background2 of backgroundHexes) {
|
|
5674
|
+
const ratio = contrastRatio(colorHex, background2);
|
|
5675
|
+
if (ratio === void 0) continue;
|
|
5676
|
+
if (!worst || ratio < worst.ratio) worst = { ratio, background: background2 };
|
|
5677
|
+
}
|
|
5678
|
+
if (!worst) return [];
|
|
5679
|
+
const isLarge = fact.fontSizePt >= largeTextPt || fact.bold && fact.fontSizePt >= largeBoldTextPt;
|
|
5680
|
+
const required = isLarge ? largeRatio : normalRatio;
|
|
5681
|
+
if (worst.ratio >= required) return [];
|
|
5682
|
+
const rounded = Math.round(worst.ratio * 100) / 100;
|
|
5683
|
+
return [
|
|
5684
|
+
{
|
|
5685
|
+
message: `Text at #${colorHex} on #${worst.background} has ${rounded}:1 contrast \u2014 below the ${required}:1 needed at ${fact.fontSizePt}pt${fact.bold ? " bold" : ""}.`,
|
|
5686
|
+
path: fact.path,
|
|
5687
|
+
suggestion: "Darken the text, lighten it further, or change the surface behind it.",
|
|
5688
|
+
context: {
|
|
5689
|
+
colorHex,
|
|
5690
|
+
backgroundHex: worst.background,
|
|
5691
|
+
ratio: rounded,
|
|
5692
|
+
required,
|
|
5693
|
+
fontSizePt: fact.fontSizePt,
|
|
5694
|
+
bold: fact.bold,
|
|
5695
|
+
backgroundHexes
|
|
5696
|
+
},
|
|
5697
|
+
evidence: {
|
|
5698
|
+
actual: rounded,
|
|
5699
|
+
expected: required,
|
|
5700
|
+
unit: ":1"
|
|
5701
|
+
}
|
|
5702
|
+
}
|
|
5703
|
+
];
|
|
5704
|
+
});
|
|
5705
|
+
}
|
|
5706
|
+
};
|
|
5707
|
+
var PPTX_QUALITY_RULES = {
|
|
5708
|
+
id: "pptx/default",
|
|
5709
|
+
rules: [
|
|
5710
|
+
pptxCanvasRule,
|
|
5711
|
+
pptxMinimumFontRule,
|
|
5712
|
+
pptxTextFitRule,
|
|
5713
|
+
pptxSlideDensityRule,
|
|
5714
|
+
pptxTextContrastRule
|
|
5715
|
+
]
|
|
5716
|
+
};
|
|
5717
|
+
var PPTX_QUALITY_PROFILES = {
|
|
5718
|
+
"executive-presentation": {
|
|
5719
|
+
id: "executive-presentation",
|
|
5720
|
+
formats: ["pptx"],
|
|
5721
|
+
description: "Decision deck optimized for scan speed and projection.",
|
|
5722
|
+
rules: {
|
|
5723
|
+
"pptx/minimum-font-size": { parameters: { minimumFontPt: 14 } },
|
|
5724
|
+
"pptx/slide-density": { parameters: { maximumBodyWords: 70 } }
|
|
5725
|
+
}
|
|
5726
|
+
},
|
|
5727
|
+
"technical-presentation": {
|
|
5728
|
+
id: "technical-presentation",
|
|
5729
|
+
formats: ["pptx"],
|
|
5730
|
+
description: "Portable professional presentation defaults."
|
|
5731
|
+
}
|
|
5732
|
+
};
|
|
5733
|
+
var PPTX_DEFAULT_QUALITY_PROFILE = PPTX_QUALITY_PROFILES["technical-presentation"];
|
|
5734
|
+
var PPTX_PROFILES_BY_ID = PPTX_QUALITY_PROFILES;
|
|
5735
|
+
function resolvePptxQualityProfile(requested) {
|
|
5736
|
+
if (!requested) return void 0;
|
|
5737
|
+
const registered = PPTX_PROFILES_BY_ID[requested.id];
|
|
5738
|
+
if (!registered) return requested;
|
|
5739
|
+
return mergeQualityProfiles(registered, requested);
|
|
5740
|
+
}
|
|
5741
|
+
var pptxQualityEngine = new QualityEngine(PPTX_QUALITY_RULES.rules);
|
|
5742
|
+
|
|
5743
|
+
// src/quality/preflight.ts
|
|
5744
|
+
var PREPARE_RULE_ID = "quality/prepare";
|
|
5745
|
+
function emptyAnalysis(ruleErrors = [], blocked = false) {
|
|
5746
|
+
return {
|
|
5747
|
+
diagnostics: [],
|
|
5748
|
+
counts: { error: 0, warning: 0, info: 0 },
|
|
5749
|
+
blocked,
|
|
5750
|
+
truncated: false,
|
|
5751
|
+
suppressedCount: 0,
|
|
5752
|
+
evaluatedRuleIds: [],
|
|
5753
|
+
ruleErrors
|
|
5754
|
+
};
|
|
5755
|
+
}
|
|
5756
|
+
function analyzePptxQuality(doc, options = {}) {
|
|
5757
|
+
assertValidQualityPolicy(options.policy);
|
|
5758
|
+
assertValidQualityProfile(options.profile);
|
|
5759
|
+
if (typeof doc !== "object" || doc === null || Array.isArray(doc) || doc.name !== "pptx") {
|
|
5760
|
+
return emptyAnalysis();
|
|
5761
|
+
}
|
|
5762
|
+
let prepared = options.prepared;
|
|
5763
|
+
try {
|
|
5764
|
+
prepared ??= preparePptxQualityDocument(
|
|
5765
|
+
doc,
|
|
5766
|
+
options
|
|
5767
|
+
);
|
|
5768
|
+
} catch (error) {
|
|
5769
|
+
if (options.policy?.onRuleError === "throw") throw error;
|
|
5770
|
+
const gate = options.policy?.gate;
|
|
5771
|
+
return emptyAnalysis(
|
|
5772
|
+
[
|
|
5773
|
+
{
|
|
5774
|
+
ruleId: PREPARE_RULE_ID,
|
|
5775
|
+
message: error instanceof Error ? error.message : String(error)
|
|
5776
|
+
}
|
|
5777
|
+
],
|
|
5778
|
+
gate !== void 0 && gate !== "none"
|
|
5779
|
+
);
|
|
5780
|
+
}
|
|
5781
|
+
return pptxQualityEngine.analyzeSync(prepared, {
|
|
5782
|
+
profile: resolvePptxQualityProfile(options.profile) ?? PPTX_DEFAULT_QUALITY_PROFILE,
|
|
5783
|
+
policy: options.policy
|
|
5784
|
+
});
|
|
5785
|
+
}
|
|
5786
|
+
|
|
4871
5787
|
// src/plugin/index.ts
|
|
4872
5788
|
import {
|
|
4873
5789
|
createComponent,
|
|
@@ -5375,10 +6291,14 @@ export {
|
|
|
5375
6291
|
DEFAULT_PPTX_RENDERER_ID,
|
|
5376
6292
|
DEFAULT_PPTX_THEME,
|
|
5377
6293
|
DuplicateComponentError,
|
|
6294
|
+
PPTX_DEFAULT_QUALITY_PROFILE,
|
|
6295
|
+
PPTX_QUALITY_PROFILES,
|
|
6296
|
+
PPTX_QUALITY_RULES,
|
|
5378
6297
|
PresentationGenerator,
|
|
5379
6298
|
PresentationValidationError,
|
|
5380
6299
|
UncompiledComponentError,
|
|
5381
6300
|
W as WarningCodes,
|
|
6301
|
+
analyzePptxQuality,
|
|
5382
6302
|
cleanComponentProps,
|
|
5383
6303
|
createComponent,
|
|
5384
6304
|
createPresentationGenerator,
|
|
@@ -5395,9 +6315,13 @@ export {
|
|
|
5395
6315
|
isPresentationComponent,
|
|
5396
6316
|
isPresentationComponentDefinition,
|
|
5397
6317
|
isSlideComponent,
|
|
6318
|
+
pptxQualityEngine,
|
|
5398
6319
|
pptxRendererIds,
|
|
6320
|
+
pptxRendererStatuses,
|
|
5399
6321
|
pptxThemes,
|
|
6322
|
+
preparePptxQualityDocument,
|
|
5400
6323
|
resolveComponentVersion3 as resolveComponentVersion,
|
|
6324
|
+
resolvePptxQualityProfile,
|
|
5401
6325
|
validateComponentProps,
|
|
5402
6326
|
validatePresentation
|
|
5403
6327
|
};
|