@json-to-office/core-pptx 6.8.0 → 7.0.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/blocks/document.d.ts.map +1 -1
- package/dist/core/generationContext.d.ts +3 -2
- package/dist/core/generationContext.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +495 -216
- package/dist/index.js.map +1 -1
- package/dist/plugin/createPresentationGenerator.d.ts.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/types.d.ts +24 -0
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/quality/facts.d.ts +59 -2
- package/dist/quality/facts.d.ts.map +1 -1
- package/dist/quality/rules.d.ts +31 -6
- package/dist/quality/rules.d.ts.map +1 -1
- package/dist/themes/defaults.d.ts +8 -1
- package/dist/themes/defaults.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -5163,36 +5163,8 @@ var DEFAULT_STYLES = {
|
|
|
5163
5163
|
body: { fontSize: 14 },
|
|
5164
5164
|
caption: { fontSize: 10, italic: true, fontColor: "text2" }
|
|
5165
5165
|
};
|
|
5166
|
-
var DEFAULT_PPTX_THEME =
|
|
5167
|
-
name: "default",
|
|
5168
|
-
displayName: "Office Default",
|
|
5169
|
-
description: "The stock Office look: blue primary, orange and green accents, Arial throughout, centred titles",
|
|
5170
|
-
whenToUse: "A deck that has to match a plain PowerPoint look; pick a designed theme for anything a client will see.",
|
|
5171
|
-
colors: {
|
|
5172
|
-
primary: "#4472C4",
|
|
5173
|
-
secondary: "#ED7D31",
|
|
5174
|
-
accent: "#70AD47",
|
|
5175
|
-
background: "#FFFFFF",
|
|
5176
|
-
text: "#333333",
|
|
5177
|
-
text2: "#44546A",
|
|
5178
|
-
background2: "#E7E6E6",
|
|
5179
|
-
accent4: "#FFC000",
|
|
5180
|
-
accent5: "#5B9BD5",
|
|
5181
|
-
accent6: "#70AD47"
|
|
5182
|
-
},
|
|
5183
|
-
fonts: {
|
|
5184
|
-
heading: "Arial",
|
|
5185
|
-
body: "Arial"
|
|
5186
|
-
},
|
|
5187
|
-
defaults: {
|
|
5188
|
-
fontSize: 18,
|
|
5189
|
-
fontColor: "#333333"
|
|
5190
|
-
},
|
|
5191
|
-
styles: DEFAULT_STYLES,
|
|
5192
|
-
componentDefaults: { table: DEFAULT_TABLE }
|
|
5193
|
-
};
|
|
5166
|
+
var DEFAULT_PPTX_THEME = CONSULTING_PPTX_THEME;
|
|
5194
5167
|
var PPTX_THEMES = {
|
|
5195
|
-
default: DEFAULT_PPTX_THEME,
|
|
5196
5168
|
consulting: CONSULTING_PPTX_THEME,
|
|
5197
5169
|
vermilion: VERMILION_PPTX_THEME,
|
|
5198
5170
|
devportal: DEVPORTAL_PPTX_THEME,
|
|
@@ -5332,7 +5304,7 @@ function resolveThemeContext(documentIn, options = {}) {
|
|
|
5332
5304
|
inlineTheme = document.props.theme;
|
|
5333
5305
|
}
|
|
5334
5306
|
const authoredThemeName = typeof document.props.theme === "string" ? document.props.theme : void 0;
|
|
5335
|
-
const baseThemeName = inlineTheme ? inlineTheme.name || "inline-theme" : authoredThemeName ?? defaultThemeName ?? "
|
|
5307
|
+
const baseThemeName = inlineTheme ? inlineTheme.name || "inline-theme" : authoredThemeName ?? defaultThemeName ?? "consulting";
|
|
5336
5308
|
let theme = inlineTheme ?? (resolveNamedTheme ? resolveNamedTheme(baseThemeName, authoredThemeName !== void 0) : customThemes?.[baseThemeName] ?? getPptxTheme(baseThemeName));
|
|
5337
5309
|
theme = resolvePptxDesignSystem(
|
|
5338
5310
|
theme,
|
|
@@ -5913,10 +5885,22 @@ async function expandPptxBlocksWithPlugins(document, theme, plugins, render, pre
|
|
|
5913
5885
|
preserve
|
|
5914
5886
|
});
|
|
5915
5887
|
const finished = finishPptxBlocks(composed.standard, evaluator, effects2);
|
|
5916
|
-
return {
|
|
5888
|
+
return {
|
|
5889
|
+
...finished,
|
|
5890
|
+
preserved: composed.preserved,
|
|
5891
|
+
pluginOutputs: evaluator.pluginOutputs
|
|
5892
|
+
};
|
|
5917
5893
|
}
|
|
5918
5894
|
|
|
5919
5895
|
// src/quality/facts.ts
|
|
5896
|
+
function textInsetsPt(props, componentName) {
|
|
5897
|
+
const margin = props.margin;
|
|
5898
|
+
if (typeof margin === "number" && Number.isFinite(margin))
|
|
5899
|
+
return [margin, margin, margin, margin];
|
|
5900
|
+
if (Array.isArray(margin) && margin.length === 4 && margin.every((side) => typeof side === "number" && Number.isFinite(side)))
|
|
5901
|
+
return margin;
|
|
5902
|
+
return componentName === "shape" ? [3.6, 7.2, 3.6, 7.2] : [0, 0, 0, 0];
|
|
5903
|
+
}
|
|
5920
5904
|
function asRecord(value) {
|
|
5921
5905
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
5922
5906
|
}
|
|
@@ -6174,7 +6158,13 @@ function collectSlideNodes(component, path4, text, richText, surfaces, boxes, co
|
|
|
6174
6158
|
const content = typeof props.text === "string" ? props.text : void 0;
|
|
6175
6159
|
if (content !== void 0 && content.trim() !== "" && props.runs === void 0) {
|
|
6176
6160
|
if (rec.name === "text" || rec.name === "shape") {
|
|
6177
|
-
text.push({
|
|
6161
|
+
text.push({
|
|
6162
|
+
props,
|
|
6163
|
+
path: path4,
|
|
6164
|
+
componentName: rec.name,
|
|
6165
|
+
text: content,
|
|
6166
|
+
order
|
|
6167
|
+
});
|
|
6178
6168
|
}
|
|
6179
6169
|
}
|
|
6180
6170
|
if (Array.isArray(props.runs) && (rec.name === "text" || rec.name === "shape")) {
|
|
@@ -6182,7 +6172,13 @@ function collectSlideNodes(component, path4, text, richText, surfaces, boxes, co
|
|
|
6182
6172
|
(run) => run && typeof run.text === "string" ? `${run.text}${run.breakLine === true ? "\n" : ""}` : ""
|
|
6183
6173
|
).join("");
|
|
6184
6174
|
if (joined.trim() !== "")
|
|
6185
|
-
richText.push({
|
|
6175
|
+
richText.push({
|
|
6176
|
+
props,
|
|
6177
|
+
path: path4,
|
|
6178
|
+
componentName: String(rec.name),
|
|
6179
|
+
text: joined,
|
|
6180
|
+
order
|
|
6181
|
+
});
|
|
6186
6182
|
}
|
|
6187
6183
|
const componentName = typeof rec.name === "string" ? rec.name : "";
|
|
6188
6184
|
if (componentName === "chart" || componentName === "highcharts" || componentName === "table") {
|
|
@@ -6357,6 +6353,16 @@ function tableFact(node, slidePath, authoredProps) {
|
|
|
6357
6353
|
}
|
|
6358
6354
|
return { ...base, columns };
|
|
6359
6355
|
}
|
|
6356
|
+
function authoredNameAtPointer(root, pointer) {
|
|
6357
|
+
let node = root;
|
|
6358
|
+
for (const token of pointer.split("/").slice(1)) {
|
|
6359
|
+
const key = token.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
6360
|
+
node = Array.isArray(node) ? node[Number(key)] : asRecord(node)?.[key];
|
|
6361
|
+
if (node === void 0) return void 0;
|
|
6362
|
+
}
|
|
6363
|
+
const name = asRecord(node)?.name;
|
|
6364
|
+
return typeof name === "string" ? name : void 0;
|
|
6365
|
+
}
|
|
6360
6366
|
function authoredPropsAtPointer(root, pointer) {
|
|
6361
6367
|
let node = root;
|
|
6362
6368
|
for (const token of pointer.split("/").slice(1)) {
|
|
@@ -6374,7 +6380,7 @@ function authoredPropsAtPointer(root, pointer) {
|
|
|
6374
6380
|
}
|
|
6375
6381
|
return asRecord(asRecord(node)?.props);
|
|
6376
6382
|
}
|
|
6377
|
-
function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn, slideHeightIn, ctx, theme, slideBackground, analyzedTextPaths, analyzedContentPaths, paletteTokens, authoredPropsAt, statedPropsAt, authoredPath, addFact) {
|
|
6383
|
+
function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn, slideHeightIn, ctx, theme, slideBackground, analyzedTextPaths, analyzedContentPaths, paletteTokens, authoredPropsAt, statedPropsAt, authoredPath, addFact, ownsProps = () => true) {
|
|
6378
6384
|
const nodes = [];
|
|
6379
6385
|
const richNodes = [];
|
|
6380
6386
|
const surfaces = [];
|
|
@@ -6406,6 +6412,8 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6406
6412
|
statedPropsAt
|
|
6407
6413
|
);
|
|
6408
6414
|
if (!isCompleteBox(box)) return;
|
|
6415
|
+
const onSlide = /^\/children\/\d+\/children\/\d+$/.test(node.path) && authoredPath(node.path) === node.path;
|
|
6416
|
+
const authored = onSlide ? authoredPropsAt(node.path) : void 0;
|
|
6409
6417
|
addFact({
|
|
6410
6418
|
id: `pptx:box:${renderedIndex}:${boxIndex}:${node.path}`,
|
|
6411
6419
|
kind: "pptx/box",
|
|
@@ -6418,7 +6426,10 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6418
6426
|
xPt: box.xPt,
|
|
6419
6427
|
yPt: box.yPt,
|
|
6420
6428
|
widthPt: box.widthPt,
|
|
6421
|
-
heightPt: box.heightPt
|
|
6429
|
+
heightPt: box.heightPt,
|
|
6430
|
+
...typeof authored?.x === "number" && typeof authored?.y === "number" && authored.grid === void 0 && {
|
|
6431
|
+
authoredPositionIn: { x: authored.x, y: authored.y }
|
|
6432
|
+
}
|
|
6422
6433
|
});
|
|
6423
6434
|
});
|
|
6424
6435
|
const surfaceBoxes = surfaces.flatMap((surface) => {
|
|
@@ -6522,6 +6533,8 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6522
6533
|
}
|
|
6523
6534
|
}
|
|
6524
6535
|
const colorHex = typeof node.props.color === "string" ? resolveColor(node.props.color, theme)?.toUpperCase() : void 0;
|
|
6536
|
+
const stated = statedPropsAt(node.path);
|
|
6537
|
+
const setSize = stated && node.props.fit !== void 0 ? resolveTypography(stated, ctx).fontSize : void 0;
|
|
6525
6538
|
addFact({
|
|
6526
6539
|
id: `pptx:text:${renderedIndex}:${nodeIndex}:${node.path}`,
|
|
6527
6540
|
kind: "pptx/text",
|
|
@@ -6531,6 +6544,7 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6531
6544
|
nodePath: node.path,
|
|
6532
6545
|
text: node.text,
|
|
6533
6546
|
fontSizePt: typography.fontSize,
|
|
6547
|
+
...setSize !== void 0 && setSize !== typography.fontSize && { fitFromPt: setSize },
|
|
6534
6548
|
lineSpacingPt: typography.lineSpacing,
|
|
6535
6549
|
paraSpaceBeforePt: typography.paraSpaceBefore,
|
|
6536
6550
|
paraSpaceAfterPt: typography.paraSpaceAfter,
|
|
@@ -6539,6 +6553,7 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6539
6553
|
...boxYPt !== void 0 && { boxYPt },
|
|
6540
6554
|
...boxWidthPt !== void 0 && boxWidthPt > 0 && { boxWidthPt },
|
|
6541
6555
|
...boxHeightPt !== void 0 && boxHeightPt > 0 && { boxHeightPt },
|
|
6556
|
+
insetsPt: textInsetsPt(node.props, node.componentName),
|
|
6542
6557
|
verticalAlign: node.props.valign === "middle" || node.props.valign === "bottom" ? node.props.valign : "top",
|
|
6543
6558
|
align: horizontalAlign(node.props, ctx),
|
|
6544
6559
|
rotationDeg: asNumber(node.props.rotate) ?? 0,
|
|
@@ -6550,6 +6565,7 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6550
6565
|
sizePath: `${node.path}/props/fontSize`
|
|
6551
6566
|
},
|
|
6552
6567
|
generated: authoredPath(node.path) !== node.path,
|
|
6568
|
+
ownsProps: ownsProps(node.path),
|
|
6553
6569
|
autoFit: node.props.h === void 0 && gridPos === void 0,
|
|
6554
6570
|
...colorHex !== void 0 && { colorHex },
|
|
6555
6571
|
...!backgroundUnknown && backgroundHexes.length > 0 && { backgroundHexes }
|
|
@@ -6630,7 +6646,10 @@ function addSlideFacts(roots, slidePath, renderedIndex, page, grid, slideWidthIn
|
|
|
6630
6646
|
...stretched && isCompleteBox(resolved) && resolved.heightPt > 0 && {
|
|
6631
6647
|
drawnRatio: resolved.widthPt / resolved.heightPt
|
|
6632
6648
|
},
|
|
6633
|
-
...natural !== void 0 && { naturalRatio: natural }
|
|
6649
|
+
...natural !== void 0 && { naturalRatio: natural },
|
|
6650
|
+
...stretched && typeof authored.w === "number" && typeof authored.h === "number" && {
|
|
6651
|
+
authoredSizeIn: { w: authored.w, h: authored.h }
|
|
6652
|
+
}
|
|
6634
6653
|
});
|
|
6635
6654
|
}
|
|
6636
6655
|
const bullets = bulletItems(box.props);
|
|
@@ -6651,7 +6670,7 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6651
6670
|
const facts = [];
|
|
6652
6671
|
const provenance = {};
|
|
6653
6672
|
let sourceMap = {};
|
|
6654
|
-
const authoredPath = (path4) => toAuthoredPointer(sourceMap, path4);
|
|
6673
|
+
const authoredPath = (path4) => toAuthoredPointer(sourceMap, path4, options.expanded?.pluginOutputs);
|
|
6655
6674
|
const addFact = (raw) => {
|
|
6656
6675
|
const fact = {
|
|
6657
6676
|
...raw,
|
|
@@ -6698,12 +6717,12 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6698
6717
|
});
|
|
6699
6718
|
});
|
|
6700
6719
|
const warnings = options.warnings ?? [];
|
|
6701
|
-
const context = resolveThemeContext(document, {
|
|
6720
|
+
const context = options.context ?? resolveThemeContext(document, {
|
|
6702
6721
|
customThemes: options.customThemes,
|
|
6703
6722
|
fonts: options.fonts,
|
|
6704
6723
|
warnings
|
|
6705
6724
|
});
|
|
6706
|
-
const expanded = expandPptxBlocks(context.document, context.theme);
|
|
6725
|
+
const expanded = options.expanded ?? expandPptxBlocks(context.document, context.theme);
|
|
6707
6726
|
sourceMap = expanded.sourceMap;
|
|
6708
6727
|
const processed = processPresentation(expanded.document, {
|
|
6709
6728
|
theme: context.theme,
|
|
@@ -6797,6 +6816,10 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6797
6816
|
(value) => `#${resolveColor(value, processed.theme)}`
|
|
6798
6817
|
) ?? SERIES_COLOR_TOKENS.filter((token) => paletteHexes[token] !== void 0);
|
|
6799
6818
|
const authoredPropsAt = (pointer) => authoredPropsAtPointer(document, authoredPath(pointer));
|
|
6819
|
+
const pluginOutputs = options.expanded?.pluginOutputs ?? [];
|
|
6820
|
+
const ownsProps = (pointer) => !pluginOutputs.some(
|
|
6821
|
+
(prefix) => pointer === prefix || pointer.startsWith(`${prefix}/`)
|
|
6822
|
+
) && authoredNameAtPointer(document, authoredPath(pointer)) !== "block";
|
|
6800
6823
|
const statedPropsAt = (pointer) => authoredPropsAtPointer(expanded.document, pointer);
|
|
6801
6824
|
let exportedPages = 0;
|
|
6802
6825
|
processed.slides.forEach((slide, renderedIndex) => {
|
|
@@ -6834,7 +6857,8 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6834
6857
|
authoredPropsAt,
|
|
6835
6858
|
statedPropsAt,
|
|
6836
6859
|
authoredPath,
|
|
6837
|
-
addFact
|
|
6860
|
+
addFact,
|
|
6861
|
+
ownsProps
|
|
6838
6862
|
);
|
|
6839
6863
|
});
|
|
6840
6864
|
for (const budget of blockSlotBudgets(context.document, expanded.blocks)) {
|
|
@@ -6844,10 +6868,10 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6844
6868
|
...budget
|
|
6845
6869
|
});
|
|
6846
6870
|
}
|
|
6847
|
-
const
|
|
6871
|
+
const slotNodes = nodesBySlot(processed, slideIndexes, sourceMap);
|
|
6848
6872
|
for (const role of blockSlotRoles(context.document, expanded.blocks)) {
|
|
6849
6873
|
const present = role.value !== void 0 && role.value !== null && role.value !== false && (typeof role.value !== "string" || role.value.trim() !== "") && (!Array.isArray(role.value) || role.value.length > 0);
|
|
6850
|
-
const bound =
|
|
6874
|
+
const bound = slotNodes.get(role.path);
|
|
6851
6875
|
let measured;
|
|
6852
6876
|
if (bound && typeof bound.props.text === "string") {
|
|
6853
6877
|
const typography = resolveTypography(bound.props, ctx);
|
|
@@ -6885,6 +6909,30 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6885
6909
|
...measured
|
|
6886
6910
|
});
|
|
6887
6911
|
}
|
|
6912
|
+
const footerType = typeof processed.theme.chrome?.confidentialFooter?.type === "string" ? processed.theme.chrome.confidentialFooter.type : "footer";
|
|
6913
|
+
processed.slides.forEach((slide, renderedIndex) => {
|
|
6914
|
+
const authoredIndex = slideIndexes[renderedIndex];
|
|
6915
|
+
if (authoredIndex === void 0) return;
|
|
6916
|
+
const slidePath = `/children/${authoredIndex}`;
|
|
6917
|
+
const onSlide = (fact) => "slidePath" in fact && fact.slidePath === slidePath;
|
|
6918
|
+
const texts = facts.filter(
|
|
6919
|
+
(fact) => (fact.kind === "pptx/text" || fact.kind === "pptx/rich-text") && onSlide(fact)
|
|
6920
|
+
);
|
|
6921
|
+
addFact({
|
|
6922
|
+
id: `pptx:slide-chrome:${slidePath}`,
|
|
6923
|
+
kind: "pptx/slide-chrome",
|
|
6924
|
+
path: slidePath,
|
|
6925
|
+
index: renderedIndex,
|
|
6926
|
+
...slide.hidden === true && { hidden: true },
|
|
6927
|
+
blocks: expanded.blocks.filter(
|
|
6928
|
+
(pointer) => pointer.startsWith(`${slidePath}/children/`)
|
|
6929
|
+
).length,
|
|
6930
|
+
pageNumber: texts.some((fact) => fact.text.includes("{PAGE_NUMBER}")),
|
|
6931
|
+
footer: texts.some((fact) => fact.styleName === footerType) || facts.some(
|
|
6932
|
+
(fact) => fact.kind === "pptx/chrome-slot" && fact.role === "footer" && fact.present && onSlide(fact)
|
|
6933
|
+
)
|
|
6934
|
+
});
|
|
6935
|
+
});
|
|
6888
6936
|
return {
|
|
6889
6937
|
format: "pptx",
|
|
6890
6938
|
model: {
|
|
@@ -6907,7 +6955,7 @@ function preparePptxQualityDocument(document, options = {}) {
|
|
|
6907
6955
|
}
|
|
6908
6956
|
};
|
|
6909
6957
|
}
|
|
6910
|
-
function
|
|
6958
|
+
function nodesBySlot(processed, slideIndexes, sourceMap) {
|
|
6911
6959
|
const found = /* @__PURE__ */ new Map();
|
|
6912
6960
|
const visit = (component, path4) => {
|
|
6913
6961
|
if (component.enabled === false) return;
|
|
@@ -6915,6 +6963,10 @@ function textNodesBySlot(processed, slideIndexes, sourceMap) {
|
|
|
6915
6963
|
const origin = toAuthoredPointer(sourceMap, `${path4}/props/text`);
|
|
6916
6964
|
if (origin !== `${path4}/props/text` && !found.has(origin))
|
|
6917
6965
|
found.set(origin, { props: asRecord(component.props) ?? {}, path: path4 });
|
|
6966
|
+
} else {
|
|
6967
|
+
const origin = toAuthoredPointer(sourceMap, path4);
|
|
6968
|
+
if (origin !== path4 && origin.includes("/props/slots/") && !found.has(origin))
|
|
6969
|
+
found.set(origin, { props: asRecord(component.props) ?? {}, path: path4 });
|
|
6918
6970
|
}
|
|
6919
6971
|
(component.children ?? []).forEach(
|
|
6920
6972
|
(child, index) => visit(child, `${path4}/children/${index}`)
|
|
@@ -7673,6 +7725,8 @@ import {
|
|
|
7673
7725
|
// src/quality/rules.ts
|
|
7674
7726
|
import {
|
|
7675
7727
|
chartInfoDesignFindings,
|
|
7728
|
+
configurationLabel,
|
|
7729
|
+
configurationSource,
|
|
7676
7730
|
DEFAULT_MAXIMUM_CHART_SERIES,
|
|
7677
7731
|
DEFAULT_MAXIMUM_PIE_SLICES,
|
|
7678
7732
|
fontCountFinding,
|
|
@@ -7813,19 +7867,24 @@ var pptxMinimumFontRule = {
|
|
|
7813
7867
|
);
|
|
7814
7868
|
return textFacts(facts).filter((fact) => fact.fontSizePt < minimum).map((fact) => ({
|
|
7815
7869
|
message: `Effective font size is ${fact.fontSizePt}pt \u2014 unreadable on a projected slide.`,
|
|
7816
|
-
|
|
7870
|
+
// Text a definition or a plugin generated reports at the invocation
|
|
7871
|
+
// the author wrote, whose props are the block's or the plugin's: no
|
|
7872
|
+
// fontSize of theirs to lift, so no patch.
|
|
7873
|
+
path: fact.ownsProps ? `${fact.path}/props` : fact.path,
|
|
7817
7874
|
suggestion: `Use at least ${minimum}pt; captions rarely work below 10pt.`,
|
|
7818
7875
|
context: { fontSize: fact.fontSizePt, threshold: minimum },
|
|
7819
7876
|
evidence: { actual: fact.fontSizePt, expected: minimum, unit: "pt" },
|
|
7820
7877
|
// `add` replaces an existing member, so this lifts an explicit
|
|
7821
7878
|
// fontSize and overrides an inherited style value alike.
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7879
|
+
...fact.ownsProps && {
|
|
7880
|
+
fixes: [
|
|
7881
|
+
{
|
|
7882
|
+
op: "add",
|
|
7883
|
+
path: `${fact.path}/props/fontSize`,
|
|
7884
|
+
value: minimum
|
|
7885
|
+
}
|
|
7886
|
+
]
|
|
7887
|
+
}
|
|
7829
7888
|
}));
|
|
7830
7889
|
}
|
|
7831
7890
|
};
|
|
@@ -8429,18 +8488,67 @@ var pptxRequiredChromeRule = {
|
|
|
8429
8488
|
if (required.length === 0) return [];
|
|
8430
8489
|
return facts.filter(
|
|
8431
8490
|
(fact) => fact.kind === "pptx/chrome-slot"
|
|
8432
|
-
).filter((fact) => required.includes(fact.role) && !fact.present).map((fact) =>
|
|
8433
|
-
|
|
8434
|
-
|
|
8435
|
-
|
|
8436
|
-
|
|
8437
|
-
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8491
|
+
).filter((fact) => required.includes(fact.role) && !fact.present).map((fact) => {
|
|
8492
|
+
const source = configurationSource(configuration, "required");
|
|
8493
|
+
return {
|
|
8494
|
+
path: fact.path,
|
|
8495
|
+
relatedPaths: [fact.invocation],
|
|
8496
|
+
message: `${fact.block} states no ${fact.role} in its "${fact.slot}" slot; ${configurationLabel(source, profile)} expects one on every ${fact.block}.`,
|
|
8497
|
+
suggestion: `Fill the "${fact.slot}" slot. The theme already styles it.`,
|
|
8498
|
+
context: { block: fact.block, slot: fact.slot, role: fact.role },
|
|
8499
|
+
evidence: {
|
|
8500
|
+
actual: "empty",
|
|
8501
|
+
expected: fact.role,
|
|
8502
|
+
values: { source, required }
|
|
8503
|
+
}
|
|
8504
|
+
};
|
|
8505
|
+
});
|
|
8506
|
+
}
|
|
8507
|
+
};
|
|
8508
|
+
var SLIDE_CHROME_PARTS = ["pageNumber", "footer"];
|
|
8509
|
+
var pptxSlideFooterRule = {
|
|
8510
|
+
id: "pptx/slide-footer",
|
|
8511
|
+
description: "A block-built slide without the running chrome a profile or policy expects: a page number, a footer line. Off until one names parts.",
|
|
8512
|
+
code: QUALITY_CODES.CHROME_MISSING,
|
|
8513
|
+
category: "consistency",
|
|
8514
|
+
defaultSeverity: "warning",
|
|
8515
|
+
defaultCertainty: "deterministic",
|
|
8516
|
+
formats: ["pptx"],
|
|
8517
|
+
defaultParameters: { required: [], fromSlide: 1 },
|
|
8518
|
+
evaluate: ({ facts, configuration, profile }) => {
|
|
8519
|
+
const required = stringListParameter(
|
|
8520
|
+
configuration.parameters,
|
|
8521
|
+
"required"
|
|
8522
|
+
).filter(
|
|
8523
|
+
(part) => SLIDE_CHROME_PARTS.includes(part)
|
|
8524
|
+
);
|
|
8525
|
+
if (required.length === 0) return [];
|
|
8526
|
+
const from = numberParameter(configuration.parameters, "fromSlide", 1);
|
|
8527
|
+
return facts.filter(
|
|
8528
|
+
(fact) => fact.kind === "pptx/slide-chrome" && fact.blocks > 0 && fact.hidden !== true && fact.index >= from
|
|
8529
|
+
).flatMap((fact) => {
|
|
8530
|
+
const missing = required.filter((part) => !fact[part]);
|
|
8531
|
+
if (missing.length === 0) return [];
|
|
8532
|
+
const parts = missing.map((part) => part === "pageNumber" ? "page number" : "footer").join(" or ");
|
|
8533
|
+
const source = configurationSource(
|
|
8534
|
+
configuration,
|
|
8535
|
+
"required",
|
|
8536
|
+
"fromSlide"
|
|
8537
|
+
);
|
|
8538
|
+
return [
|
|
8539
|
+
{
|
|
8540
|
+
path: fact.path,
|
|
8541
|
+
message: `Slide ${fact.index + 1} carries no ${parts}; ${configurationLabel(source, profile)} expects one on every slide a block builds from slide ${from + 1} on.`,
|
|
8542
|
+
suggestion: "Build the slide with a block that draws the running footer \u2014 the house blocks set {PAGE_NUMBER} / {PAGE_COUNT} in the footer role \u2014 or add that text to the definition the slide invokes.",
|
|
8543
|
+
context: { slide: fact.index, missing },
|
|
8544
|
+
evidence: {
|
|
8545
|
+
actual: required.filter((part) => fact[part]),
|
|
8546
|
+
expected: required,
|
|
8547
|
+
values: { source, fromSlide: from }
|
|
8548
|
+
}
|
|
8549
|
+
}
|
|
8550
|
+
];
|
|
8551
|
+
});
|
|
8444
8552
|
}
|
|
8445
8553
|
};
|
|
8446
8554
|
var pptxActionTitleRule = {
|
|
@@ -8473,6 +8581,8 @@ var pptxActionTitleRule = {
|
|
|
8473
8581
|
}
|
|
8474
8582
|
};
|
|
8475
8583
|
var TITLE_DRIFT_TOLERANCE_PT = 2;
|
|
8584
|
+
var FIRST_BASELINE_EM = 0.8;
|
|
8585
|
+
var TITLE_PLACEMENT_LINES = 2;
|
|
8476
8586
|
var PPTX_TYPE_VOCABULARY = {
|
|
8477
8587
|
subject: "deck",
|
|
8478
8588
|
keepTo: "Keep to the theme styles \u2014 title, heading, body, label, statistic \u2014 and drop the ad-hoc sizes."
|
|
@@ -8549,11 +8659,15 @@ var pptxSizeCountRule = {
|
|
|
8549
8659
|
defaultParameters: { maximumSizes: 8, maximumSizesPerSlide: 0 },
|
|
8550
8660
|
evaluate: ({ facts, configuration, profile }) => {
|
|
8551
8661
|
const sizes = paintedSizes(facts);
|
|
8662
|
+
const setBy = (parameter) => {
|
|
8663
|
+
const source = configurationSource(configuration, parameter);
|
|
8664
|
+
return { source, label: configurationLabel(source, profile) };
|
|
8665
|
+
};
|
|
8552
8666
|
const findings = sizeCountFinding(
|
|
8553
8667
|
sizes,
|
|
8554
8668
|
numberParameter(configuration.parameters, "maximumSizes", 8),
|
|
8555
8669
|
pptxThemeFact(facts)?.path ?? "/props",
|
|
8556
|
-
|
|
8670
|
+
setBy("maximumSizes"),
|
|
8557
8671
|
PPTX_TYPE_VOCABULARY
|
|
8558
8672
|
).map((finding) => ({
|
|
8559
8673
|
...finding,
|
|
@@ -8576,7 +8690,7 @@ var pptxSizeCountRule = {
|
|
|
8576
8690
|
onSlide,
|
|
8577
8691
|
perSlide,
|
|
8578
8692
|
slidePath,
|
|
8579
|
-
|
|
8693
|
+
setBy("maximumSizesPerSlide"),
|
|
8580
8694
|
PPTX_SLIDE_TYPE_VOCABULARY
|
|
8581
8695
|
))
|
|
8582
8696
|
findings.push({
|
|
@@ -8600,53 +8714,107 @@ var pptxRoleDriftRule = {
|
|
|
8600
8714
|
return theme ? roleDriftFindings(paintedSizes(facts), theme.roleSizesPt) : [];
|
|
8601
8715
|
}
|
|
8602
8716
|
};
|
|
8603
|
-
|
|
8604
|
-
|
|
8605
|
-
|
|
8717
|
+
var EDGE_NAMES = {
|
|
8718
|
+
left: "left edge",
|
|
8719
|
+
center: "centre line",
|
|
8720
|
+
right: "right edge"
|
|
8721
|
+
};
|
|
8722
|
+
function laidOutTitle(fact, path4, order) {
|
|
8723
|
+
if (fact.boxXPt === void 0 || fact.boxYPt === void 0 || fact.boxWidthPt === void 0 || fact.rotationDeg % 360 !== 0)
|
|
8724
|
+
return void 0;
|
|
8725
|
+
const [top, right, bottom, left] = fact.insetsPt;
|
|
8726
|
+
const innerWidth = Math.max(1, fact.boxWidthPt - left - right);
|
|
8727
|
+
const { heightPt, lines } = estimateTextHeightPt(
|
|
8728
|
+
fact.text,
|
|
8729
|
+
innerWidth,
|
|
8730
|
+
fact.fontSizePt,
|
|
8731
|
+
fact.lineSpacingPt,
|
|
8732
|
+
fact.paraSpaceBeforePt,
|
|
8733
|
+
fact.paraSpaceAfterPt
|
|
8606
8734
|
);
|
|
8607
|
-
|
|
8735
|
+
let textTop = fact.boxYPt + top;
|
|
8736
|
+
if (fact.boxHeightPt !== void 0 && !fact.autoFit) {
|
|
8737
|
+
const room = fact.boxHeightPt - top - bottom - heightPt;
|
|
8738
|
+
if (fact.verticalAlign === "middle") textTop += room / 2;
|
|
8739
|
+
else if (fact.verticalAlign === "bottom") textTop += room;
|
|
8740
|
+
}
|
|
8741
|
+
const align = fact.align === "justify" ? "left" : fact.align;
|
|
8742
|
+
const edgePt = align === "center" ? fact.boxXPt + left + innerWidth / 2 : align === "right" ? fact.boxXPt + fact.boxWidthPt - right : fact.boxXPt + left;
|
|
8743
|
+
const style = fact.styleName ?? "unstyled";
|
|
8744
|
+
const setSizePt = fact.fitFromPt ?? fact.fontSizePt;
|
|
8745
|
+
return {
|
|
8746
|
+
fact,
|
|
8747
|
+
path: path4,
|
|
8748
|
+
kind: `${style}|${setSizePt}|${align}`,
|
|
8749
|
+
style,
|
|
8750
|
+
setSizePt,
|
|
8751
|
+
align,
|
|
8752
|
+
lines,
|
|
8753
|
+
baselinePt: textTop + FIRST_BASELINE_EM * fact.fontSizePt,
|
|
8754
|
+
edgePt,
|
|
8755
|
+
order
|
|
8756
|
+
};
|
|
8757
|
+
}
|
|
8758
|
+
function deckTitles(facts, styles) {
|
|
8759
|
+
const texts = textFacts(facts).filter((fact) => !fact.slideHidden);
|
|
8760
|
+
const slideIndex = (fact) => Number(/^\/children\/(\d+)/.exec(fact.slidePath)?.[1] ?? 0);
|
|
8761
|
+
const order = new Map(
|
|
8762
|
+
texts.map((fact, index) => [fact, slideIndex(fact) * 1e4 + index])
|
|
8763
|
+
);
|
|
8764
|
+
const byNode = new Map(texts.map((fact) => [fact.nodePath, fact]));
|
|
8765
|
+
const titles = [];
|
|
8608
8766
|
const claimed = /* @__PURE__ */ new Set();
|
|
8609
|
-
const
|
|
8767
|
+
const add = (fact, path4) => {
|
|
8610
8768
|
if (claimed.has(fact)) return;
|
|
8611
8769
|
claimed.add(fact);
|
|
8612
|
-
|
|
8770
|
+
const title = laidOutTitle(fact, path4, order.get(fact));
|
|
8771
|
+
if (title) titles.push(title);
|
|
8613
8772
|
};
|
|
8614
|
-
for (const slot of facts)
|
|
8615
|
-
if (slot.kind
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
const painted = texts.find(
|
|
8620
|
-
(fact) => fact.slidePath === chrome.slidePath && fact.text === chrome.text
|
|
8621
|
-
);
|
|
8622
|
-
if (painted) push(chrome.block, painted);
|
|
8623
|
-
}
|
|
8773
|
+
for (const slot of facts)
|
|
8774
|
+
if (slot.kind === "pptx/chrome-slot" && slot.role === "actionTitle" && slot.nodePath !== void 0) {
|
|
8775
|
+
const painted = byNode.get(slot.nodePath);
|
|
8776
|
+
if (painted) add(painted, slot.path);
|
|
8777
|
+
}
|
|
8624
8778
|
for (const fact of texts)
|
|
8625
8779
|
if (fact.styleName !== void 0 && styles.includes(fact.styleName))
|
|
8626
|
-
|
|
8627
|
-
return
|
|
8628
|
-
}
|
|
8629
|
-
function
|
|
8630
|
-
const
|
|
8631
|
-
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
|
|
8635
|
-
|
|
8636
|
-
|
|
8637
|
-
|
|
8638
|
-
|
|
8780
|
+
add(fact, fact.path);
|
|
8781
|
+
return titles.sort((a, b) => a.order - b.order);
|
|
8782
|
+
}
|
|
8783
|
+
function titlePlacements(titles, tolerance) {
|
|
8784
|
+
const lands = (a, b) => Math.abs(a.baselinePt - b.baselinePt) <= tolerance && Math.abs(a.edgePt - b.edgePt) <= tolerance;
|
|
8785
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
8786
|
+
for (const title of titles)
|
|
8787
|
+
byKind.set(title.kind, [...byKind.get(title.kind) ?? [], title]);
|
|
8788
|
+
const placements = [];
|
|
8789
|
+
for (const kind of byKind.values()) {
|
|
8790
|
+
let pending = kind;
|
|
8791
|
+
while (pending.length > 0) {
|
|
8792
|
+
let reference = pending[0];
|
|
8793
|
+
let most = 0;
|
|
8794
|
+
for (const candidate of pending) {
|
|
8795
|
+
const count = pending.filter((other) => lands(other, candidate)).length;
|
|
8796
|
+
if (count > most) {
|
|
8797
|
+
reference = candidate;
|
|
8798
|
+
most = count;
|
|
8799
|
+
}
|
|
8800
|
+
}
|
|
8801
|
+
const reach = TITLE_PLACEMENT_LINES * reference.fact.lineSpacingPt;
|
|
8802
|
+
const members = pending.filter(
|
|
8803
|
+
(title) => Math.abs(title.baselinePt - reference.baselinePt) <= reach && Math.abs(title.edgePt - reference.edgePt) <= reach
|
|
8804
|
+
);
|
|
8805
|
+
placements.push({ reference, members });
|
|
8806
|
+
pending = pending.filter((title) => !members.includes(title));
|
|
8639
8807
|
}
|
|
8640
8808
|
}
|
|
8641
|
-
return
|
|
8809
|
+
return placements;
|
|
8642
8810
|
}
|
|
8643
8811
|
var pptxTitleDriftRule = {
|
|
8644
8812
|
id: "pptx/title-drift",
|
|
8645
|
-
description: "A slide title
|
|
8813
|
+
description: "A slide title whose laid-out first baseline or aligned edge is away from where the deck\u2019s other titles of that kind land. Off until a profile or policy enables it.",
|
|
8646
8814
|
code: QUALITY_CODES.TITLE_DRIFT,
|
|
8647
8815
|
category: "consistency",
|
|
8648
8816
|
defaultSeverity: "warning",
|
|
8649
|
-
defaultCertainty: "
|
|
8817
|
+
defaultCertainty: "estimated",
|
|
8650
8818
|
formats: ["pptx"],
|
|
8651
8819
|
defaultEnabled: false,
|
|
8652
8820
|
defaultParameters: {
|
|
@@ -8661,40 +8829,53 @@ var pptxTitleDriftRule = {
|
|
|
8661
8829
|
TITLE_DRIFT_TOLERANCE_PT
|
|
8662
8830
|
);
|
|
8663
8831
|
const findings = [];
|
|
8664
|
-
for (const
|
|
8665
|
-
|
|
8666
|
-
|
|
8667
|
-
|
|
8668
|
-
|
|
8669
|
-
|
|
8670
|
-
|
|
8671
|
-
|
|
8672
|
-
|
|
8673
|
-
|
|
8674
|
-
|
|
8675
|
-
|
|
8676
|
-
|
|
8677
|
-
|
|
8678
|
-
|
|
8679
|
-
|
|
8680
|
-
|
|
8832
|
+
for (const { reference, members } of titlePlacements(
|
|
8833
|
+
deckTitles(facts, styles),
|
|
8834
|
+
tolerance
|
|
8835
|
+
)) {
|
|
8836
|
+
if (members.length < 2) continue;
|
|
8837
|
+
const agreeing = members.filter(
|
|
8838
|
+
(title) => Math.abs(title.baselinePt - reference.baselinePt) <= tolerance && Math.abs(title.edgePt - reference.edgePt) <= tolerance
|
|
8839
|
+
);
|
|
8840
|
+
for (const title of members) {
|
|
8841
|
+
if (agreeing.includes(title)) continue;
|
|
8842
|
+
const edge = EDGE_NAMES[title.align];
|
|
8843
|
+
const off = [
|
|
8844
|
+
{
|
|
8845
|
+
axis: "first baseline",
|
|
8846
|
+
actual: title.baselinePt,
|
|
8847
|
+
expected: reference.baselinePt
|
|
8848
|
+
},
|
|
8849
|
+
{ axis: edge, actual: title.edgePt, expected: reference.edgePt }
|
|
8850
|
+
].filter(
|
|
8851
|
+
(entry) => Math.abs(entry.actual - entry.expected) > tolerance
|
|
8681
8852
|
);
|
|
8682
|
-
if (off.length === 0) continue;
|
|
8683
8853
|
const [first] = off;
|
|
8854
|
+
const size = title.fact.fontSizePt !== reference.fact.fontSizePt ? ` The fit pass sets it at ${title.fact.fontSizePt}pt, where they set at ${reference.fact.fontSizePt}pt.` : "";
|
|
8684
8855
|
findings.push({
|
|
8685
|
-
path:
|
|
8686
|
-
|
|
8687
|
-
|
|
8856
|
+
path: title.path,
|
|
8857
|
+
relatedPaths: agreeing.map((other) => other.path),
|
|
8858
|
+
message: `This ${title.style} title lays out at ${off.map((entry) => `${entry.axis} ${round(entry.actual)}pt`).join(", ")}; the deck's other titles of that kind land at ${off.map((entry) => `${entry.axis} ${round(entry.expected)}pt`).join(", ")}.${size}`,
|
|
8859
|
+
suggestion: "Place every title of one kind with the same block or the same coordinates, anchor them alike, and keep each short enough to set at the size the others keep.",
|
|
8688
8860
|
context: {
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8861
|
+
style: title.style,
|
|
8862
|
+
setSizePt: title.setSizePt,
|
|
8863
|
+
align: title.align,
|
|
8864
|
+
axes: off.map((entry) => entry.axis),
|
|
8865
|
+
slide: title.fact.slidePath
|
|
8692
8866
|
},
|
|
8693
8867
|
evidence: {
|
|
8694
|
-
|
|
8695
|
-
|
|
8868
|
+
summary: "estimated first baseline and aligned edge",
|
|
8869
|
+
actual: round(first.actual),
|
|
8870
|
+
expected: round(first.expected),
|
|
8696
8871
|
unit: "pt",
|
|
8697
|
-
values: {
|
|
8872
|
+
values: {
|
|
8873
|
+
axis: first.axis,
|
|
8874
|
+
lines: title.lines,
|
|
8875
|
+
fontSizePt: title.fact.fontSizePt,
|
|
8876
|
+
verticalAlign: title.fact.verticalAlign,
|
|
8877
|
+
source: configurationSource(configuration, "tolerancePt")
|
|
8878
|
+
}
|
|
8698
8879
|
}
|
|
8699
8880
|
});
|
|
8700
8881
|
}
|
|
@@ -8747,7 +8928,9 @@ var pptxBulletRule = {
|
|
|
8747
8928
|
actual: bullets.items,
|
|
8748
8929
|
expected: maximumBullets,
|
|
8749
8930
|
unit: "bullets",
|
|
8750
|
-
values: {
|
|
8931
|
+
values: {
|
|
8932
|
+
source: configurationSource(configuration, "maximumBullets")
|
|
8933
|
+
}
|
|
8751
8934
|
}
|
|
8752
8935
|
});
|
|
8753
8936
|
if (maximumWords > 0 && bullets.longestWords > maximumWords)
|
|
@@ -8761,16 +8944,39 @@ var pptxBulletRule = {
|
|
|
8761
8944
|
actual: bullets.longestWords,
|
|
8762
8945
|
expected: maximumWords,
|
|
8763
8946
|
unit: "words",
|
|
8764
|
-
values: {
|
|
8947
|
+
values: {
|
|
8948
|
+
source: configurationSource(
|
|
8949
|
+
configuration,
|
|
8950
|
+
"maximumWordsPerBullet"
|
|
8951
|
+
)
|
|
8952
|
+
}
|
|
8765
8953
|
}
|
|
8766
8954
|
});
|
|
8767
8955
|
}
|
|
8768
8956
|
return findings;
|
|
8769
8957
|
}
|
|
8770
8958
|
};
|
|
8959
|
+
function safeAreaFixes(fact, canvas, safe) {
|
|
8960
|
+
const position = fact.authoredPositionIn;
|
|
8961
|
+
if (position === void 0 || fact.widthPt > canvas.widthPt - 2 * safe || fact.heightPt > canvas.heightPt - 2 * safe)
|
|
8962
|
+
return [];
|
|
8963
|
+
const clamp = (valuePt, sizePt, extentPt) => {
|
|
8964
|
+
const low = Math.ceil(safe / 72 * 1e3) / 1e3;
|
|
8965
|
+
const high = Math.floor((extentPt - safe - sizePt) / 72 * 1e3) / 1e3;
|
|
8966
|
+
return Math.min(Math.max(valuePt / 72, low), high);
|
|
8967
|
+
};
|
|
8968
|
+
const fixes = [];
|
|
8969
|
+
const x = clamp(fact.xPt, fact.widthPt, canvas.widthPt);
|
|
8970
|
+
const y = clamp(fact.yPt, fact.heightPt, canvas.heightPt);
|
|
8971
|
+
if (Math.abs(x - position.x) > 5e-4)
|
|
8972
|
+
fixes.push({ op: "replace", path: `${fact.path}/props/x`, value: x });
|
|
8973
|
+
if (Math.abs(y - position.y) > 5e-4)
|
|
8974
|
+
fixes.push({ op: "replace", path: `${fact.path}/props/y`, value: y });
|
|
8975
|
+
return fixes;
|
|
8976
|
+
}
|
|
8771
8977
|
var pptxSafeAreaRule = {
|
|
8772
8978
|
id: "pptx/safe-area",
|
|
8773
|
-
description: "Content outside the theme\u2019s safe area that is neither chrome nor a full bleed. Off until a profile or policy enables it.",
|
|
8979
|
+
description: "Content outside the theme\u2019s safe area that is neither chrome (a tracker, footer, source or logo) nor a full bleed. Off until a profile or policy enables it.",
|
|
8774
8980
|
code: QUALITY_CODES.SAFE_AREA,
|
|
8775
8981
|
category: "composition",
|
|
8776
8982
|
defaultSeverity: "warning",
|
|
@@ -8791,7 +8997,7 @@ var pptxSafeAreaRule = {
|
|
|
8791
8997
|
if (canvas === void 0 || safe === void 0 || safe <= 0) return [];
|
|
8792
8998
|
const chromeNodes = new Set(
|
|
8793
8999
|
facts.filter(
|
|
8794
|
-
(fact) => fact.kind === "pptx/chrome-slot" && (fact.role === "tracker" || fact.role === "footer" || fact.role === "source")
|
|
9000
|
+
(fact) => fact.kind === "pptx/chrome-slot" && (fact.role === "tracker" || fact.role === "footer" || fact.role === "source" || fact.role === "logo")
|
|
8795
9001
|
).flatMap((fact) => fact.nodePath ? [fact.nodePath] : [])
|
|
8796
9002
|
);
|
|
8797
9003
|
const chromeStyles = /* @__PURE__ */ new Set(["footer", "tracker", "source"]);
|
|
@@ -8817,6 +9023,7 @@ var pptxSafeAreaRule = {
|
|
|
8817
9023
|
right - (canvas.widthPt - safe),
|
|
8818
9024
|
bottom - (canvas.heightPt - safe)
|
|
8819
9025
|
);
|
|
9026
|
+
const fixes = safeAreaFixes(fact, canvas, safe);
|
|
8820
9027
|
return [
|
|
8821
9028
|
{
|
|
8822
9029
|
path: fact.path,
|
|
@@ -8828,7 +9035,8 @@ var pptxSafeAreaRule = {
|
|
|
8828
9035
|
expected: 0,
|
|
8829
9036
|
unit: "pt",
|
|
8830
9037
|
values: { source: "theme" }
|
|
8831
|
-
}
|
|
9038
|
+
},
|
|
9039
|
+
...fixes.length > 0 && { fixes }
|
|
8832
9040
|
}
|
|
8833
9041
|
];
|
|
8834
9042
|
});
|
|
@@ -8864,7 +9072,7 @@ var pptxSlideTitleRule = {
|
|
|
8864
9072
|
actual: 0,
|
|
8865
9073
|
expected: 1,
|
|
8866
9074
|
unit: "titles",
|
|
8867
|
-
values: { source:
|
|
9075
|
+
values: { source: configurationSource(configuration) }
|
|
8868
9076
|
}
|
|
8869
9077
|
}));
|
|
8870
9078
|
}
|
|
@@ -8913,7 +9121,20 @@ var pptxImageAspectRule = {
|
|
|
8913
9121
|
{
|
|
8914
9122
|
path: fact.path,
|
|
8915
9123
|
drawn: fact.drawnRatio,
|
|
8916
|
-
natural: fact.naturalRatio
|
|
9124
|
+
natural: fact.naturalRatio,
|
|
9125
|
+
...fact.authoredSizeIn && {
|
|
9126
|
+
sides: {
|
|
9127
|
+
width: {
|
|
9128
|
+
path: `${fact.path}/props/w`,
|
|
9129
|
+
value: fact.authoredSizeIn.w
|
|
9130
|
+
},
|
|
9131
|
+
height: {
|
|
9132
|
+
path: `${fact.path}/props/h`,
|
|
9133
|
+
value: fact.authoredSizeIn.h
|
|
9134
|
+
},
|
|
9135
|
+
decimals: 3
|
|
9136
|
+
}
|
|
9137
|
+
}
|
|
8917
9138
|
},
|
|
8918
9139
|
"slide",
|
|
8919
9140
|
tolerance
|
|
@@ -8939,6 +9160,7 @@ var PPTX_QUALITY_RULES = {
|
|
|
8939
9160
|
pptxOffCanvasRule,
|
|
8940
9161
|
pptxSlotBudgetRule,
|
|
8941
9162
|
pptxRequiredChromeRule,
|
|
9163
|
+
pptxSlideFooterRule,
|
|
8942
9164
|
pptxActionTitleRule,
|
|
8943
9165
|
pptxTypeScaleRule,
|
|
8944
9166
|
pptxSizeCountRule,
|
|
@@ -8973,11 +9195,12 @@ var PPTX_QUALITY_PROFILES = {
|
|
|
8973
9195
|
"consulting-deck": {
|
|
8974
9196
|
id: "consulting-deck",
|
|
8975
9197
|
formats: ["pptx"],
|
|
8976
|
-
description: "Consulting readout: every content slide leads with a two-line action title, every chart carries a takeaway and a source, content stays inside the theme\u2019s safe area, a box holds at most five bullets of at most twelve words, every figure carries alt text, every size is on the theme scale with at most nine in the deck and six on a slide, and titles of one kind hold one line.",
|
|
9198
|
+
description: "Consulting readout: every content slide leads with a two-line action title, every chart carries a takeaway and a source, every slide a block builds after the cover carries its page number, content stays inside the theme\u2019s safe area, a box holds at most five bullets of at most twelve words, every figure carries alt text, every size is on the theme scale with at most nine in the deck and six on a slide, and titles of one kind hold one line.",
|
|
8977
9199
|
rules: {
|
|
8978
9200
|
"pptx/required-chrome": {
|
|
8979
9201
|
parameters: { required: ["takeaway", "source"] }
|
|
8980
9202
|
},
|
|
9203
|
+
"pptx/slide-footer": { parameters: { required: ["pageNumber"] } },
|
|
8981
9204
|
"pptx/action-title": { parameters: { maxLines: 2 } },
|
|
8982
9205
|
"pptx/slide-density": { parameters: { maximumBodyWords: 90 } },
|
|
8983
9206
|
"pptx/type-scale": { enabled: true },
|
|
@@ -9270,97 +9493,152 @@ function createBuilderImpl(state) {
|
|
|
9270
9493
|
newState
|
|
9271
9494
|
);
|
|
9272
9495
|
}
|
|
9273
|
-
async function
|
|
9274
|
-
|
|
9275
|
-
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9496
|
+
async function expandPresentation(document, options) {
|
|
9497
|
+
let internalDocument = document;
|
|
9498
|
+
const renderer = options?.renderer ?? state.renderer ?? internalDocument.renderer;
|
|
9499
|
+
const validationDocument = renderer === void 0 ? internalDocument : { ...internalDocument, renderer };
|
|
9500
|
+
const validationOptions = {
|
|
9501
|
+
...state.validation,
|
|
9502
|
+
...options?.validation
|
|
9503
|
+
};
|
|
9504
|
+
if (validationOptions.enabled !== false) {
|
|
9505
|
+
const result = validatePresentation(
|
|
9506
|
+
validationDocument,
|
|
9507
|
+
state.components,
|
|
9508
|
+
{ allowUnknownFields: validationOptions.allowUnknownFields }
|
|
9509
|
+
);
|
|
9510
|
+
if (!result.valid) {
|
|
9511
|
+
throw new ComponentValidationError3(result.errors, internalDocument);
|
|
9512
|
+
}
|
|
9513
|
+
} else if (!internalDocument || internalDocument.name !== "pptx") {
|
|
9514
|
+
throw new Error("Top-level component must be a pptx component");
|
|
9515
|
+
}
|
|
9516
|
+
const warnings = [];
|
|
9517
|
+
const context = resolveThemeContext(internalDocument, {
|
|
9518
|
+
customThemes: state.customThemes,
|
|
9519
|
+
fonts: state.fonts,
|
|
9520
|
+
warnings,
|
|
9521
|
+
defaultThemeName: typeof state.theme === "string" ? state.theme : void 0,
|
|
9522
|
+
resolveNamedTheme: (name, authored) => state.customThemes?.[name] ?? (authored && hasPptxTheme(name) ? getPptxTheme(name) : void 0) ?? (typeof state.theme === "object" && state.theme !== null ? state.theme : getPptxTheme(name))
|
|
9523
|
+
});
|
|
9524
|
+
const modedRoot = context.document;
|
|
9525
|
+
const resolvedTheme = context.theme;
|
|
9526
|
+
const validateEmitted = validationOptions.enabled === false ? void 0 : (emitted, componentLabel, parentName) => {
|
|
9527
|
+
let validationDocument2;
|
|
9528
|
+
if (parentName === "pptx") {
|
|
9529
|
+
validationDocument2 = {
|
|
9530
|
+
...modedRoot,
|
|
9531
|
+
...renderer !== void 0 ? { renderer } : {},
|
|
9532
|
+
children: emitted
|
|
9533
|
+
};
|
|
9534
|
+
} else if (parentName === "slide") {
|
|
9535
|
+
validationDocument2 = {
|
|
9536
|
+
...modedRoot,
|
|
9537
|
+
...renderer !== void 0 ? { renderer } : {},
|
|
9538
|
+
children: [{ name: "slide", props: {}, children: emitted }]
|
|
9539
|
+
};
|
|
9540
|
+
} else {
|
|
9541
|
+
return;
|
|
9542
|
+
}
|
|
9543
|
+
const result = validatePresentation(
|
|
9544
|
+
validationDocument2,
|
|
9545
|
+
state.components,
|
|
9546
|
+
{ allowUnknownFields: validationOptions.allowUnknownFields }
|
|
9547
|
+
);
|
|
9548
|
+
if (!result.valid) {
|
|
9549
|
+
throw new ComponentValidationError3(
|
|
9550
|
+
result.errors.map((error) => ({
|
|
9551
|
+
...error,
|
|
9552
|
+
message: `custom component '${componentLabel}' emitted invalid output \u2014 ${error.message}`
|
|
9553
|
+
})),
|
|
9554
|
+
emitted
|
|
9287
9555
|
);
|
|
9288
|
-
|
|
9289
|
-
|
|
9556
|
+
}
|
|
9557
|
+
};
|
|
9558
|
+
const expanded = await expandPptxBlocksWithPlugins(
|
|
9559
|
+
modedRoot,
|
|
9560
|
+
resolvedTheme,
|
|
9561
|
+
new Set(componentMap.keys()),
|
|
9562
|
+
pluginRenderer(warnings, resolvedTheme, validateEmitted)
|
|
9563
|
+
);
|
|
9564
|
+
const processedDocument = expanded.document;
|
|
9565
|
+
if (validationOptions.enabled !== false) {
|
|
9566
|
+
const result = validatePresentation(
|
|
9567
|
+
{
|
|
9568
|
+
...processedDocument,
|
|
9569
|
+
...renderer !== void 0 ? { renderer } : {}
|
|
9570
|
+
},
|
|
9571
|
+
[],
|
|
9572
|
+
{
|
|
9573
|
+
allowUnknownFields: validationOptions.allowUnknownFields
|
|
9290
9574
|
}
|
|
9291
|
-
|
|
9292
|
-
|
|
9575
|
+
);
|
|
9576
|
+
if (!result.valid) {
|
|
9577
|
+
throw new ComponentValidationError3(
|
|
9578
|
+
result.errors.map((error) => ({
|
|
9579
|
+
...error,
|
|
9580
|
+
message: `expanded plugin output failed validation \u2014 ${error.message}`
|
|
9581
|
+
})),
|
|
9582
|
+
processedDocument
|
|
9583
|
+
);
|
|
9293
9584
|
}
|
|
9294
|
-
|
|
9295
|
-
|
|
9585
|
+
}
|
|
9586
|
+
return { context, expanded, warnings, renderer };
|
|
9587
|
+
}
|
|
9588
|
+
async function prepareQuality(document, options) {
|
|
9589
|
+
const { context, expanded, warnings, renderer } = await expandPresentation(
|
|
9590
|
+
document,
|
|
9591
|
+
options
|
|
9592
|
+
);
|
|
9593
|
+
const prepared = preparePptxQualityDocument(
|
|
9594
|
+
document,
|
|
9595
|
+
{
|
|
9596
|
+
context,
|
|
9597
|
+
expanded,
|
|
9296
9598
|
customThemes: state.customThemes,
|
|
9297
9599
|
fonts: state.fonts,
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
}
|
|
9302
|
-
|
|
9303
|
-
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9600
|
+
services: state.services,
|
|
9601
|
+
...renderer !== void 0 && { renderer },
|
|
9602
|
+
warnings
|
|
9603
|
+
}
|
|
9604
|
+
);
|
|
9605
|
+
return { prepared, warnings };
|
|
9606
|
+
}
|
|
9607
|
+
async function generateFromPrepared(options) {
|
|
9608
|
+
const { prepared } = options;
|
|
9609
|
+
const { document: processedDocument, theme, processed } = prepared.model;
|
|
9610
|
+
const warnings = [];
|
|
9611
|
+
const renderer = options.renderer ?? state.renderer ?? prepared.renderer;
|
|
9612
|
+
const hasHighcharts = containsHighcharts(processed);
|
|
9613
|
+
const resolvedFonts = await resolveDocumentFonts(
|
|
9614
|
+
processedDocument,
|
|
9615
|
+
theme,
|
|
9616
|
+
warnings,
|
|
9617
|
+
state.fonts,
|
|
9618
|
+
hasHighcharts
|
|
9619
|
+
);
|
|
9620
|
+
const chartFonts = hasHighcharts ? toChartFontFaces2(resolvedFonts) : [];
|
|
9621
|
+
const buffer = await runWithBaseDir(
|
|
9622
|
+
options.baseDir ?? state.baseDir,
|
|
9623
|
+
() => renderProcessedViaIr(processed, warnings, {
|
|
9624
|
+
renderer,
|
|
9625
|
+
services: state.services,
|
|
9626
|
+
deterministic: options.deterministic ?? state.packaging.deterministic,
|
|
9627
|
+
generatedAt: options.generatedAt ?? state.packaging.generatedAt,
|
|
9628
|
+
...chartFonts.length > 0 ? { chartFonts } : {}
|
|
9629
|
+
})
|
|
9630
|
+
);
|
|
9631
|
+
return { buffer, warnings };
|
|
9632
|
+
}
|
|
9633
|
+
async function generate(document, options) {
|
|
9634
|
+
try {
|
|
9635
|
+
if (options?.prepared)
|
|
9636
|
+
return await generateFromPrepared(
|
|
9637
|
+
options
|
|
9325
9638
|
);
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
result.errors.map((error) => ({
|
|
9329
|
-
...error,
|
|
9330
|
-
message: `custom component '${componentLabel}' emitted invalid output \u2014 ${error.message}`
|
|
9331
|
-
})),
|
|
9332
|
-
emitted
|
|
9333
|
-
);
|
|
9334
|
-
}
|
|
9335
|
-
};
|
|
9336
|
-
const expanded = await expandPptxBlocksWithPlugins(
|
|
9337
|
-
modedRoot,
|
|
9338
|
-
resolvedTheme,
|
|
9339
|
-
new Set(componentMap.keys()),
|
|
9340
|
-
pluginRenderer(warnings, resolvedTheme, validateEmitted)
|
|
9341
|
-
);
|
|
9639
|
+
const { context, expanded, warnings, renderer } = await expandPresentation(document, options);
|
|
9640
|
+
const resolvedTheme = context.theme;
|
|
9342
9641
|
const processedDocument = expanded.document;
|
|
9343
|
-
if (validationOptions.enabled !== false) {
|
|
9344
|
-
const result = validatePresentation(
|
|
9345
|
-
{
|
|
9346
|
-
...processedDocument,
|
|
9347
|
-
...renderer !== void 0 ? { renderer } : {}
|
|
9348
|
-
},
|
|
9349
|
-
[],
|
|
9350
|
-
{
|
|
9351
|
-
allowUnknownFields: validationOptions.allowUnknownFields
|
|
9352
|
-
}
|
|
9353
|
-
);
|
|
9354
|
-
if (!result.valid) {
|
|
9355
|
-
throw new ComponentValidationError3(
|
|
9356
|
-
result.errors.map((error) => ({
|
|
9357
|
-
...error,
|
|
9358
|
-
message: `expanded plugin output failed validation \u2014 ${error.message}`
|
|
9359
|
-
})),
|
|
9360
|
-
processedDocument
|
|
9361
|
-
);
|
|
9362
|
-
}
|
|
9363
|
-
}
|
|
9364
9642
|
const processed = processPresentation(processedDocument, {
|
|
9365
9643
|
theme: resolvedTheme,
|
|
9366
9644
|
services: state.services,
|
|
@@ -9459,6 +9737,7 @@ function createBuilderImpl(state) {
|
|
|
9459
9737
|
generate,
|
|
9460
9738
|
generateBuffer: generate,
|
|
9461
9739
|
generateFile,
|
|
9740
|
+
prepareQuality,
|
|
9462
9741
|
getComponentNames,
|
|
9463
9742
|
validate,
|
|
9464
9743
|
generateSchema,
|