@openpresentation/opf 0.15.1 → 0.17.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/README.md +19 -1
- package/dist/{catalog-refs-BSfGELpt.d.ts → catalog-refs-C4K4y-vh.d.ts} +1 -1
- package/dist/{catalog-schemas-CinPh5WA.d.ts → catalog-schemas-g7RFQBdc.d.ts} +1 -1
- package/dist/catalog.d.ts +2 -2
- package/dist/catalog.js +3 -23224
- package/dist/{chart-data-BnNqgnC-.d.ts → chart-data-DTxdRRT1.d.ts} +1 -1
- package/dist/{chunk-3JG4YXWF.js → chunk-BIC3SWPK.js} +12 -5
- package/dist/{chunk-O744SXXA.js → chunk-CW7GEAV3.js} +350 -141
- package/dist/{chunk-374R35TJ.js → chunk-ESRYBYLY.js} +1 -1
- package/dist/chunk-IPW5BS3X.js +973 -0
- package/dist/{chunk-3RKB4RDJ.js → chunk-IS5H5JKH.js} +4 -2
- package/dist/{chunk-TWQJLI6E.js → chunk-M4UR3NPN.js} +2 -2
- package/dist/chunk-N4EVOFEA.js +58 -0
- package/dist/{chunk-QDI75P2M.js → chunk-NLNYNBTS.js} +217 -20
- package/dist/{chunk-U4J75TRG.js → chunk-O263APNI.js} +5 -2
- package/dist/{chunk-UBFJ2QHH.js → chunk-O2CX5NEH.js} +36 -14
- package/dist/{chunk-5FTUIDUH.js → chunk-O3CVNJTL.js} +1 -1
- package/dist/{chunk-VGAOJEY7.js → chunk-PPROJP2E.js} +1 -1
- package/dist/{chunk-2CJPC4HI.js → chunk-RA64UQKI.js} +203 -53
- package/dist/chunk-TCD2FB2K.js +319 -0
- package/dist/chunk-WCG3THTD.js +23712 -0
- package/dist/chunk-WWYWFVOI.js +793 -0
- package/dist/{chunk-ZPU46TFA.js → chunk-YT7FMQER.js} +1 -1
- package/dist/{composition-D2tghM82.d.ts → composition-BbZ_9C-c.d.ts} +238 -139
- package/dist/composition.d.ts +3 -3
- package/dist/composition.js +1 -1
- package/dist/conversion-SBpTOwdg.d.ts +444 -0
- package/dist/convert.d.ts +4 -4
- package/dist/convert.js +7 -7
- package/dist/data.d.ts +28 -11
- package/dist/data.js +2 -2
- package/dist/deck-report-Bx3naFlF.d.ts +40 -0
- package/dist/deck.d.ts +47 -0
- package/dist/deck.js +12 -0
- package/dist/diff.d.ts +1 -1
- package/dist/diff.js +5 -5
- package/dist/docs.js +33 -27
- package/dist/examples.d.ts +1 -1
- package/dist/examples.js +3229 -985
- package/dist/{finding-CT_f_vbD.d.ts → finding-CDQ_wH8j.d.ts} +1 -1
- package/dist/format.js +2 -2
- package/dist/index.d.ts +43 -12
- package/dist/index.js +15 -805
- package/dist/markdown.d.ts +7 -8
- package/dist/markdown.js +7 -7
- package/dist/node-engine.d.ts +94 -0
- package/dist/node-engine.js +21 -0
- package/dist/node.d.ts +60 -0
- package/dist/node.js +54 -0
- package/dist/{pagination-wedbe7eq.d.ts → pagination-BhX4M48Y.d.ts} +21 -7
- package/dist/pagination.d.ts +4 -4
- package/dist/pagination.js +4 -4
- package/dist/patch.d.ts +1 -1
- package/dist/patch.js +4 -4
- package/dist/{presentation-CDKQVkyI.d.ts → presentation-fb8CZmRq.d.ts} +28 -83
- package/dist/repo-readme.js +1 -1
- package/dist/schemas.d.ts +14 -0
- package/dist/schemas.js +1 -1
- package/dist/spec/catalogs/layouts/business-model-canvas.json +66 -1
- package/dist/spec/catalogs/layouts/capability-map.json +53 -3
- package/dist/spec/catalogs/layouts/chart-2x.json +32 -8
- package/dist/spec/catalogs/layouts/chart-3x-bottom-vertical-title-left.json +16 -10
- package/dist/spec/catalogs/layouts/chart-3x-left-title-center.json +16 -10
- package/dist/spec/catalogs/layouts/chart-3x-left.json +16 -10
- package/dist/spec/catalogs/layouts/chart-3x-right-title-center.json +16 -10
- package/dist/spec/catalogs/layouts/chart-3x-right.json +16 -10
- package/dist/spec/catalogs/layouts/chart-3x-top-vertical-title-left.json +16 -10
- package/dist/spec/catalogs/layouts/chart-3x.json +48 -12
- package/dist/spec/catalogs/layouts/dashboard.json +37 -5
- package/dist/spec/catalogs/layouts/image-2x.json +32 -8
- package/dist/spec/catalogs/layouts/image-3x.json +48 -12
- package/dist/spec/catalogs/layouts/index.json +1 -1
- package/dist/spec/catalogs/layouts/kpi-scorecard.json +48 -3
- package/dist/spec/catalogs/layouts/logic-tree.json +69 -0
- package/dist/spec/catalogs/layouts/market-size.json +26 -2
- package/dist/spec/catalogs/layouts/org-chart.json +53 -6
- package/dist/spec/catalogs/manifest.json +3 -3
- package/dist/spec/schemas/layout.schema.json +172 -5
- package/dist/spec/schemas/opf.schema.json +27 -43
- package/dist/types.d.ts +10 -32
- package/dist/{validation-types-BOcY9GTs.d.ts → validation-types-iZbXfES6.d.ts} +19 -8
- package/dist/validator.d.ts +8 -8
- package/dist/validator.js +3 -3
- package/dist/yaml.d.ts +6 -6
- package/dist/yaml.js +8 -321
- package/package.json +38 -1
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { isRecord, same } from './chunk-
|
|
1
|
+
import { isRecord, same } from './chunk-PPROJP2E.js';
|
|
2
2
|
import { parseDocument, YAMLMap, Scalar, stringify } from 'yaml';
|
|
3
3
|
|
|
4
|
+
// src/not-checked.ts
|
|
5
|
+
var NOT_CHECKED = { syntax: "checked", schema: "not-run", references: "not-run", policy: "not-run", accessibility: "not-run", content: "not-run", layout: "not-run", backgroundPixels: "not-read", imageBytes: "embedded-only", nativeExport: "not-checked" };
|
|
4
6
|
function splitLines(source) {
|
|
5
7
|
const lines = [];
|
|
6
8
|
let start = 0;
|
|
@@ -189,4 +191,4 @@ var OPFMarkdownError = class extends Error {
|
|
|
189
191
|
}
|
|
190
192
|
};
|
|
191
193
|
|
|
192
|
-
export { Ctx, DECIMAL, OPFMarkdownError, csvField, lineRange, readCsv, readYamlMapping, scalarText, splitLines, writeYaml };
|
|
194
|
+
export { Ctx, DECIMAL, NOT_CHECKED, OPFMarkdownError, csvField, lineRange, readCsv, readYamlMapping, scalarText, splitLines, writeYaml };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { jsonEqual, formatPointer } from './chunk-
|
|
2
|
-
import { annotationText } from './chunk-
|
|
1
|
+
import { jsonEqual, formatPointer } from './chunk-O3CVNJTL.js';
|
|
2
|
+
import { annotationText } from './chunk-CW7GEAV3.js';
|
|
3
3
|
|
|
4
4
|
// src/diff-match.ts
|
|
5
5
|
var DEFAULT_MATCH_THRESHOLD = 0.5;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { fromMarkdown, toMarkdown } from './chunk-BIC3SWPK.js';
|
|
2
|
+
import { fromYaml, toYaml } from './chunk-TCD2FB2K.js';
|
|
3
|
+
import { NOT_CHECKED } from './chunk-IS5H5JKH.js';
|
|
4
|
+
import { parseSource, validate, OPFValidationError } from './chunk-NLNYNBTS.js';
|
|
5
|
+
import { checkCatalogsOption } from './chunk-SPFV27O5.js';
|
|
6
|
+
|
|
7
|
+
// src/deck-report.ts
|
|
8
|
+
var DECK_FORMATS = ["json", "yaml", "markdown"];
|
|
9
|
+
function deckFormatOf(filename) {
|
|
10
|
+
if (filename === void 0) return "json";
|
|
11
|
+
if (/\.opf\.md$/i.test(filename)) return "markdown";
|
|
12
|
+
if (/\.ya?ml$/i.test(filename)) return "yaml";
|
|
13
|
+
return "json";
|
|
14
|
+
}
|
|
15
|
+
function resolveFormat(caller, options) {
|
|
16
|
+
if (options.format === void 0) return deckFormatOf(options.filename);
|
|
17
|
+
if (!DECK_FORMATS.includes(options.format)) throw new TypeError(`${caller}: unknown format ${JSON.stringify(options.format)}. Formats: ${DECK_FORMATS.join(", ")}.`);
|
|
18
|
+
return options.format;
|
|
19
|
+
}
|
|
20
|
+
function readDeckReport(text, options = {}, caller = "parse") {
|
|
21
|
+
if (typeof text !== "string") throw new TypeError(`${caller} expects a string.`);
|
|
22
|
+
checkCatalogsOption(options.catalogs, caller);
|
|
23
|
+
const format = resolveFormat(caller, options);
|
|
24
|
+
const { catalogs } = options;
|
|
25
|
+
const check = options.validate ?? true;
|
|
26
|
+
const validateOptions = typeof check === "object" ? { ...catalogs && check.catalogs === void 0 ? { catalogs } : {}, ...check } : { only: ["format", "references"], ...catalogs ? { catalogs } : {} };
|
|
27
|
+
const checked = check === false ? false : validateOptions;
|
|
28
|
+
if (format === "yaml") return { ...fromYaml(text, { validate: checked, catalogs }), format };
|
|
29
|
+
if (format === "markdown") return { ...fromMarkdown(text, { validate: checked, catalogs }), format };
|
|
30
|
+
if (checked === false) {
|
|
31
|
+
const parsed = parseSource(text);
|
|
32
|
+
return { ...report(parsed.syntax), presentation: parsed.syntax.length ? {} : parsed.value, format };
|
|
33
|
+
}
|
|
34
|
+
const result = validate(text, checked);
|
|
35
|
+
const broken = result.findings.some((found) => found.ruleId === "opf/json-syntax");
|
|
36
|
+
return { ...result, presentation: broken ? {} : JSON.parse(text.charCodeAt(0) === 65279 ? text.slice(1) : text), format };
|
|
37
|
+
}
|
|
38
|
+
function report(findings) {
|
|
39
|
+
const counts = { error: 0, warning: 0, info: 0 };
|
|
40
|
+
for (const found of findings) counts[found.severity]++;
|
|
41
|
+
return { valid: counts.error === 0, schemaValid: null, findings, counts, checks: NOT_CHECKED };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/deck.ts
|
|
45
|
+
function parse(text, options = {}) {
|
|
46
|
+
const { presentation, format: _format, ...result } = readDeckReport(text, { ...options, validate: true }, "parse");
|
|
47
|
+
if (!result.valid) throw new OPFValidationError(result);
|
|
48
|
+
return presentation;
|
|
49
|
+
}
|
|
50
|
+
function stringify(presentation, options = {}) {
|
|
51
|
+
const format = resolveFormat("stringify", options);
|
|
52
|
+
if (format === "yaml") return toYaml(presentation, { schemaComment: options.schemaComment === true }).yaml;
|
|
53
|
+
if (format === "markdown") return toMarkdown(presentation).markdown;
|
|
54
|
+
return `${JSON.stringify(presentation, null, 2)}
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export { DECK_FORMATS, deckFormatOf, parse, readDeckReport, stringify };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords,
|
|
1
|
+
import { promotedRegionKeys, VARIABLE_KINDS, resolveChartData, chartPaletteForFill, imageBackground, tableGrid, timelineTextColor, visitContentPayloads, readingRows, visualReadingOrder, intrinsicImageSize, textWidthMeasurer, normalizeHexColor, resolveColorRef, resolveDesignRecords, resolveFontFamilies, resolveColorRoles, resolveCanvasDimensions, resolveSlideVariables, defaultSlideBackground, isRecord, hasContentVariables, isTemplate, instantiateForValidation, resolveScriptFonts, unusedDatasets, MAX_COMPOSITION_DEPTH, datasetDiagnostics, pathFor, collectCitations, numberingFindings, walkCitationRuns, resolveTableData, resolveChartOptions, chartOptionTarget, codeHighlightLines, chartNumberFixesByCell, MAX_PLACEHOLDER_GROUP_DEPTH, OPFCompositionError, composeSlide } from './chunk-CW7GEAV3.js';
|
|
2
2
|
import { catalogSchemaNames } from './chunk-TRDXRRQK.js';
|
|
3
3
|
import { resolveReference, checkCatalogsOption, provenanceOf, unresolvedReference, OPFUnresolvedReferenceError, catalogReferenceSites, parseReference, catalogGroupDeclared, pointerPath, catalogRecords, catalogKinds, catalogGroupSource, registeredCatalog } from './chunk-SPFV27O5.js';
|
|
4
|
-
import { schemaEntries, schemas } from './chunk-
|
|
4
|
+
import { schemaEntries, schemas } from './chunk-RA64UQKI.js';
|
|
5
5
|
import Ajv2020 from 'ajv/dist/2020.js';
|
|
6
6
|
import addFormats from 'ajv-formats';
|
|
7
7
|
import { parseTree, printParseErrorCode, getNodePath } from 'jsonc-parser';
|
|
@@ -864,6 +864,33 @@ function dataUnionErrors(errors, value) {
|
|
|
864
864
|
return { ...error, message: field === "fields" ? "'fields' applies only to a dataset-backed table; add 'dataset' or remove 'fields'" : datasetTableMessage(field) };
|
|
865
865
|
});
|
|
866
866
|
}
|
|
867
|
+
var placeholderHeadings = /* @__PURE__ */ new Set(["title", "subtitle", "tag"]);
|
|
868
|
+
function placeholderGroupIssues(value) {
|
|
869
|
+
const issues = [];
|
|
870
|
+
if (!isRecord(value) || !Array.isArray(value.placeholders)) return issues;
|
|
871
|
+
const walk = (entries, path, depth) => entries.forEach((entry, index) => {
|
|
872
|
+
if (!isRecord(entry)) return;
|
|
873
|
+
const at2 = `${path}/${index}`;
|
|
874
|
+
if (entry.type === "group") {
|
|
875
|
+
if (depth >= MAX_PLACEHOLDER_GROUP_DEPTH) issues.push(semanticIssue(at2, `placeholder groups nest at most ${MAX_PLACEHOLDER_GROUP_DEPTH} levels inside a layout record; this group would be level ${depth + 1}`, { code: "layout-placeholder-group", depth: depth + 1, maxDepth: MAX_PLACEHOLDER_GROUP_DEPTH }));
|
|
876
|
+
else if (Array.isArray(entry.placeholders)) walk(entry.placeholders, `${at2}/placeholders`, depth + 1);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (depth === 0) return;
|
|
880
|
+
if (typeof entry.type === "string" && placeholderHeadings.has(entry.type)) issues.push(semanticIssue(`${at2}/type`, `a placeholder group holds body regions only; a '${entry.type}' placeholder belongs at the record's top level`, { code: "layout-placeholder-group" }));
|
|
881
|
+
if (hasOwn(entry, "placement")) issues.push(semanticIssue(`${at2}/placement`, "placement is only valid on a top-level image placeholder; a region inside a placeholder group cannot bleed to a slide edge", { code: "image-placement-invalid" }));
|
|
882
|
+
});
|
|
883
|
+
walk(value.placeholders, "/placeholders", 0);
|
|
884
|
+
return issues;
|
|
885
|
+
}
|
|
886
|
+
function withPlaceholderGroupIssues(errors, value) {
|
|
887
|
+
const issues = placeholderGroupIssues(value);
|
|
888
|
+
const entries = issues.map((issue2) => issue2.path.replace(/\/(type|placement)$/, ""));
|
|
889
|
+
const covered = (error) => entries.some((entry) => error.path === entry || error.path.startsWith(`${entry}/`));
|
|
890
|
+
const kept = errors.filter((error) => !covered(error));
|
|
891
|
+
const reported = [...kept.filter((error) => error.keyword !== "if"), ...issues].map((error) => error.path);
|
|
892
|
+
return [...kept.filter((error) => error.keyword !== "if" || !reported.some((path) => path === error.path || path.startsWith(`${error.path}/`))), ...issues];
|
|
893
|
+
}
|
|
867
894
|
function validateAgainstSchema(value, schemaOrKind = "presentation", options = {}) {
|
|
868
895
|
const resolved = resolveValidator(schemaOrKind);
|
|
869
896
|
if (resolved.schemaName === "presentation") {
|
|
@@ -895,7 +922,8 @@ function validateAgainstSchema(value, schemaOrKind = "presentation", options = {
|
|
|
895
922
|
}
|
|
896
923
|
const valid = resolved.validate(subject) === true;
|
|
897
924
|
const ajvErrors = valid ? [] : typedVariableErrors(resolved.validate.errors ?? [], subject);
|
|
898
|
-
const
|
|
925
|
+
const mapped = (resolved.schemaName === "presentation" ? dataUnionErrors(ajvErrors, subject) : ajvErrors).map(toIssue);
|
|
926
|
+
const errors = resolved.schemaName === "layout" ? withPlaceholderGroupIssues(mapped, subject) : mapped;
|
|
899
927
|
const warnings = [];
|
|
900
928
|
if (resolved.schemaName === "presentation") {
|
|
901
929
|
errors.push(...presentationSemanticIssues(subject));
|
|
@@ -1918,7 +1946,7 @@ var languageRule = rule(
|
|
|
1918
1946
|
"info",
|
|
1919
1947
|
"The presentation does not declare its language.",
|
|
1920
1948
|
"Screen readers and text-to-speech choose pronunciation and hyphenation from the declared language; spell checkers and translation tools use it too.",
|
|
1921
|
-
{ standard: "WCAG 2.2 SC 3.1.1 Language of Page, level A", approximations: "Only the presentation-level `language` is checked, not the language of individual runs (
|
|
1949
|
+
{ standard: "WCAG 2.2 SC 3.1.1 Language of Page, level A", approximations: "Only the presentation-level `language` is checked, not the language of individual runs (`TextRun.lang`; opf/language-tag checks how those tags are written)." }
|
|
1922
1950
|
);
|
|
1923
1951
|
var languageRules = [
|
|
1924
1952
|
{
|
|
@@ -1944,6 +1972,105 @@ var accessibilityRules = [
|
|
|
1944
1972
|
...languageRules
|
|
1945
1973
|
];
|
|
1946
1974
|
|
|
1975
|
+
// src/language-tag.ts
|
|
1976
|
+
var REGION_ALIASES = { UK: "GB" };
|
|
1977
|
+
var privateRegion = /^(?:AA|Q[M-Z]|X[A-Z]|ZZ)$/;
|
|
1978
|
+
var regionNames;
|
|
1979
|
+
var assignedRegion = (region) => {
|
|
1980
|
+
if (privateRegion.test(region)) return true;
|
|
1981
|
+
regionNames ??= new Intl.DisplayNames(["en"], { type: "region", fallback: "code" });
|
|
1982
|
+
try {
|
|
1983
|
+
return regionNames.of(region) !== region;
|
|
1984
|
+
} catch {
|
|
1985
|
+
return false;
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
var canonical = (tag) => {
|
|
1989
|
+
try {
|
|
1990
|
+
return Intl.getCanonicalLocales(tag)[0];
|
|
1991
|
+
} catch {
|
|
1992
|
+
return void 0;
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
function regionSubtag(tag) {
|
|
1996
|
+
const subtags = tag.split("-");
|
|
1997
|
+
for (let index = 1; index < subtags.length; index++) {
|
|
1998
|
+
const subtag = subtags[index];
|
|
1999
|
+
if (/^(?:[a-z]{2}|\d{3})$/i.test(subtag)) return { region: subtag.toUpperCase(), index };
|
|
2000
|
+
if (!/^[a-z]{3,4}$/i.test(subtag)) return void 0;
|
|
2001
|
+
}
|
|
2002
|
+
return void 0;
|
|
2003
|
+
}
|
|
2004
|
+
function languageTagProblem(tag) {
|
|
2005
|
+
if (/^[ix]-/i.test(tag)) return void 0;
|
|
2006
|
+
const parsed = canonical(tag);
|
|
2007
|
+
if (parsed === void 0) return { message: `'${tag}' is not a well-formed BCP 47 language tag (subtags are letters and digits joined by hyphens, such as 'en-US' or 'zh-Hans').`, caseOnly: false };
|
|
2008
|
+
const found = regionSubtag(tag);
|
|
2009
|
+
if (found) {
|
|
2010
|
+
const alias = REGION_ALIASES[found.region];
|
|
2011
|
+
if (alias || !assignedRegion(found.region)) {
|
|
2012
|
+
const subtags = tag.split("-");
|
|
2013
|
+
subtags[found.index] = alias ?? found.region;
|
|
2014
|
+
const suggestion = alias ? canonical(subtags.join("-")) : void 0;
|
|
2015
|
+
return {
|
|
2016
|
+
message: `'${tag}' names the region '${found.region}', which is not an assigned region${suggestion ? `; '${suggestion}' is the tag for ${new Intl.DisplayNames(["en"], { type: "region" }).of(alias)}` : ""}.`,
|
|
2017
|
+
...suggestion ? { suggestion } : {},
|
|
2018
|
+
caseOnly: false
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
if (parsed === tag) return void 0;
|
|
2023
|
+
if (parsed.toLowerCase() === tag.toLowerCase())
|
|
2024
|
+
return { message: `'${tag}' is not in canonical case; the canonical form is '${parsed}' (language lower case, script title case, region upper case).`, suggestion: parsed, caseOnly: true };
|
|
2025
|
+
const replacement = found && regionSubtag(parsed)?.region;
|
|
2026
|
+
if (found && replacement && replacement !== found.region) {
|
|
2027
|
+
const subtags = tag.split("-");
|
|
2028
|
+
subtags[found.index] = replacement;
|
|
2029
|
+
return { message: `'${tag}' names the region '${found.region}', which is deprecated; use '${replacement}'.`, suggestion: subtags.join("-"), caseOnly: false };
|
|
2030
|
+
}
|
|
2031
|
+
return void 0;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
// src/rules-language.ts
|
|
2035
|
+
var languageTagRule = rule(
|
|
2036
|
+
"language-tag",
|
|
2037
|
+
"content",
|
|
2038
|
+
"warning",
|
|
2039
|
+
"A language tag is malformed, uses an unassigned region or is not in canonical case.",
|
|
2040
|
+
"Engines read the deck language and each run's `lang` as BCP 47 tags to choose script fonts, text direction, proofing language and the PowerPoint `lang` attribute. A tag such as `en-a`, or a region that does not exist (`en-UK`; the United Kingdom is `GB`), is guessed at or dropped, and a wrong-case tag (`EN-us`) is not what a locale lookup expects.",
|
|
2041
|
+
{
|
|
2042
|
+
standard: "BCP 47 (RFC 5646, section 2.1) and the platform locale data (Intl.getCanonicalLocales, Intl.DisplayNames)",
|
|
2043
|
+
approximations: "Checks the root `language` (a tag string, or the `bcp47` of an inline Language object) and `lang` on the TextRun objects of titles, subtitles, tags, text, lists, tables, quotes, metrics and timelines. A region is assigned when the runtime's CLDR data names it (ISO 3166-1 codes and UN M.49 areas such as 419); private-use regions (`AA`, `QM`-`QZ`, `XA`-`XZ`) and `ZZ` are accepted, as are private-use (`x-...`) tags. Only the spelling of a tag is checked, not whether the language subtag is assigned or whether the language suits the text. The schema rejects tags with characters other than letters, digits and hyphens (`en_US` is a schema error, with no suggestion), and `en-UK` as the deck language is already an error of the schema rule, so those are not reported twice."
|
|
2044
|
+
}
|
|
2045
|
+
);
|
|
2046
|
+
var report = (context, tag, path, slide) => {
|
|
2047
|
+
if (typeof tag !== "string") return;
|
|
2048
|
+
const problem = languageTagProblem(tag);
|
|
2049
|
+
if (!problem) return;
|
|
2050
|
+
context.report(languageTagRule, {
|
|
2051
|
+
path,
|
|
2052
|
+
slide,
|
|
2053
|
+
message: problem.message,
|
|
2054
|
+
help: problem.suggestion ? `Write '${problem.suggestion}'.` : 'Write a well-formed BCP 47 tag with an assigned region, such as "en-US", "pt-BR" or "es-419".',
|
|
2055
|
+
...problem.suggestion ? { fixes: [{ id: "use-canonical-tag", title: `Use '${problem.suggestion}'`, kind: "patch", safe: problem.caseOnly, patch: [{ op: "replace", path, value: problem.suggestion }] }] } : {}
|
|
2056
|
+
});
|
|
2057
|
+
};
|
|
2058
|
+
var isEnUk = (tag) => typeof tag === "string" && tag.toLowerCase() === "en-uk";
|
|
2059
|
+
var languageTagRules = [
|
|
2060
|
+
{
|
|
2061
|
+
info: languageTagRule,
|
|
2062
|
+
run(context) {
|
|
2063
|
+
const language = context.document.language;
|
|
2064
|
+
if (typeof language === "string") {
|
|
2065
|
+
if (!isEnUk(language)) report(context, language, "/language");
|
|
2066
|
+
} else if (language !== void 0 && !isEnUk(rec(language).bcp47)) report(context, rec(language).bcp47, "/language/bcp47");
|
|
2067
|
+
for (const slide of context.slides)
|
|
2068
|
+
for (const tv of slide.texts)
|
|
2069
|
+
if (typeof tv.value !== "string") for (const run of runsOf(tv)) report(context, run.style.lang, `${run.path}/lang`, slide);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
];
|
|
2073
|
+
|
|
1947
2074
|
// src/rules-layout-content.ts
|
|
1948
2075
|
var PX_TO_PT2 = 0.75;
|
|
1949
2076
|
var round2 = (value, places = 1) => Math.round(value * 10 ** places) / 10 ** places;
|
|
@@ -2287,13 +2414,14 @@ var formatRules = [
|
|
|
2287
2414
|
rule("caption-unsupported-payload", "format", "error", "A caption is on a payload that cannot carry one.", "A caption belongs to exactly one image, chart, table or video; a group, text or code block has no place to draw it.", { standard: SCHEMA }),
|
|
2288
2415
|
rule("image-option-unsupported-payload", "format", "error", "An image option is on a payload that is not an image.", "`fit`, `focus`, the image treatments (`shape`, `cornerRadius`, `border`, `opacity`, `recolor`, `overlay`, `aspectRatio`) and `placement` describe how a picture is drawn; a group, text or chart block has no picture for them to apply to.", { standard: SCHEMA }),
|
|
2289
2416
|
rule("image-placement-invalid", "format", "error", "An image placement cannot be honoured.", "A placed image bleeds to a slide edge and the rest of the slide composes beside it. Only a top-level block (slides.N.blocks.I) can do that, and a slide edge holds one placed image; a block in a group or a promoted region, or a second block on the same edge, has nowhere to go.", { standard: SCHEMA }),
|
|
2417
|
+
rule("layout-placeholder-group", "format", "error", "A layout record's placeholder group breaks a nesting rule.", "A placeholder group arranges body regions inside one cell of its layout record. Groups nest at most three levels inside a record, and a group holds body regions and groups only: the headings (title, subtitle, tag) stay at the record's top level, where the engine places them. Composition cannot use a record that breaks either rule.", { standard: SCHEMA }),
|
|
2290
2418
|
rule("dataset-unknown", "format", "error", "A chart or table names a dataset that does not exist.", "The data cannot be drawn without the dataset it refers to.", { standard: SCHEMA }),
|
|
2291
2419
|
rule("dataset-field-unknown", "format", "error", "A table asks a dataset for a column it does not have.", "The column cannot be shown when the dataset has no column of that name.", { standard: SCHEMA }),
|
|
2292
2420
|
rule("data-column-duplicate", "format", "error", "A dataset or table has two columns with the same name.", "Series, mappings and fields address columns by name, so a repeated name makes them ambiguous.", { standard: SCHEMA }),
|
|
2293
2421
|
rule("chart-mapping-unknown-column", "format", "error", "A chart mapping names a column the data does not have.", "The series cannot be built from a column that is not there.", { standard: SCHEMA }),
|
|
2294
2422
|
rule("number-format-invalid", "format", "error", "A number format cannot be read.", "A format code that no engine can parse is drawn as the plain number or fails to export.", { standard: "Excel number format codes, as far as OPF supports them" }),
|
|
2295
2423
|
rule("variable-invalid-value", "format", "error", "A variable value does not match its declared type.", "A number variable given text, or a date given a non-date, cannot be filled into the deck.", { standard: SCHEMA }),
|
|
2296
|
-
rule("variable-unknown-builtin", "format", "error", "A token names a built-in variable that does not exist.", "The `deck.`, `speaker.` and `
|
|
2424
|
+
rule("variable-unknown-builtin", "format", "error", "A token names a built-in variable that does not exist.", "The `deck.`, `speaker.`, `organization.` and `slide.` names (and `speakers`) are reserved for built-in variables; a name under them that is not a built-in can never be filled, so the token would show literally. The slide-scoped `slide.number`, `slide.section` and `deck.slideCount` are inline tokens only, so `var:slide.*` and `var:deck.slideCount` are reported too.", { standard: SCHEMA }),
|
|
2297
2425
|
rule("chart-highlight-unknown-name", "format", "error", "A chart highlight names a series or category the chart does not have.", "`chart.highlight` emphasises marks by name; a name that matches no series or category in the chart data highlights nothing, so the chart is not drawn as authored.", { standard: SCHEMA }),
|
|
2298
2426
|
rule("variable-format", "format", "error", "A variable cannot be formatted.", "A number or date format that does not apply to the value leaves the token unreadable.", { standard: SCHEMA }),
|
|
2299
2427
|
rule(
|
|
@@ -2377,11 +2505,11 @@ var dataRules = [
|
|
|
2377
2505
|
rule("chart-highlight-adapted", "content", "warning", "A chart highlight is adapted by the chart type.", "A highlight the chart type cannot show as authored (a category highlight on a chart without categories, for example) is adapted by every engine, so the emphasis is not exactly what was written.", { standard: SCHEMA }),
|
|
2378
2506
|
rule("code-highlight-out-of-range", "content", "warning", "A code highlight marks a line the code does not have.", "Marked lines count from 1 by line break in code.source; an entry past the last line marks nothing.", { standard: SCHEMA }),
|
|
2379
2507
|
rule("code-highlight-range-reversed", "content", "warning", "A code highlight range ends before it starts.", "A range written end before start marks nothing; write the smaller line number first.", { standard: SCHEMA }),
|
|
2380
|
-
rule("variable-builtin-missing", "content", "warning", "A built-in variable has no value in this document.", "A `{{deck.*}}`, `{{speaker.*}}` or `{{organization.*}}` token whose field the document does not set resolves to nothing, so the text shows a gap.", { standard: SCHEMA }),
|
|
2508
|
+
rule("variable-builtin-missing", "content", "warning", "A built-in variable has no value in this document.", "A `{{deck.*}}`, `{{speaker.*}}` or `{{organization.*}}` token whose field the document does not set resolves to nothing, so the text shows a gap. `{{slide.section}}` is checked per slide: a slide without a `section` that uses it, in its own text or in the header or footer it inherits, is reported at that slide.", { standard: SCHEMA }),
|
|
2381
2509
|
rule("duration-outside-narrative", "content", "warning", "The target duration is outside the range the narrative suits.", "A narrative records the talk lengths it suits. A deck whose duration is outside that range probably needs a different narrative or a different length; nothing is drawn or exported differently.", { standard: SCHEMA }),
|
|
2382
2510
|
rule("narrative-duration-range", "content", "warning", "An inline narrative duration range has min greater than max.", "A range with its bounds swapped matches no duration, so the duration check cannot help.", { standard: "spec/schemas/narrative.schema.json" })
|
|
2383
2511
|
];
|
|
2384
|
-
var implementations = [...accessibilityRules, ...layoutContentRules];
|
|
2512
|
+
var implementations = [...accessibilityRules, ...layoutContentRules, ...languageTagRules];
|
|
2385
2513
|
var ruleImplementations = implementations;
|
|
2386
2514
|
var infos = [
|
|
2387
2515
|
...formatRules,
|
|
@@ -3117,6 +3245,67 @@ function parseSource(source) {
|
|
|
3117
3245
|
};
|
|
3118
3246
|
}
|
|
3119
3247
|
|
|
3248
|
+
// src/script-measurement.ts
|
|
3249
|
+
function scriptProfile(presentation, slideIndex, options, language) {
|
|
3250
|
+
let resolved;
|
|
3251
|
+
try {
|
|
3252
|
+
resolved = resolveScriptFonts(presentation, { slideIndex, ...options.catalogs !== void 0 ? { catalogs: options.catalogs } : {}, ...language === void 0 ? {} : { language } });
|
|
3253
|
+
} catch {
|
|
3254
|
+
resolved = void 0;
|
|
3255
|
+
}
|
|
3256
|
+
const slots = (role) => {
|
|
3257
|
+
const latin = options.fontFamilies[role];
|
|
3258
|
+
const slot = (key) => !resolved || resolved.sources[key] === "latin" || resolved.sources[key] === "schemeFamily" ? latin : resolved[role][key] ?? latin;
|
|
3259
|
+
return { latin, eastAsian: slot("eastAsian"), complexScript: slot("complexScript") };
|
|
3260
|
+
};
|
|
3261
|
+
return {
|
|
3262
|
+
heading: slots("heading"),
|
|
3263
|
+
body: slots("body"),
|
|
3264
|
+
...resolved?.supplement ? { supplement: resolved.supplement } : {},
|
|
3265
|
+
script: resolved?.script ?? "Zzzz",
|
|
3266
|
+
...resolved ? { scriptRole: resolved.scriptRole, bcp47: resolved.bcp47, lang: resolved.lang, languageSource: resolved.languageSource, direction: resolved.direction } : {},
|
|
3267
|
+
rtl: resolved?.rtl === true,
|
|
3268
|
+
serif: options.serif
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
function slideScriptMeasurement(presentation, slideIndex, measurement, options) {
|
|
3272
|
+
const forScripts = measurement.forScripts;
|
|
3273
|
+
if (typeof forScripts !== "function") return measurement;
|
|
3274
|
+
const deckProfile = scriptProfile(presentation, slideIndex, options);
|
|
3275
|
+
const deck = forScripts.call(measurement, deckProfile);
|
|
3276
|
+
const languages = /* @__PURE__ */ new Map();
|
|
3277
|
+
const forLanguage = (lang) => {
|
|
3278
|
+
if (typeof lang !== "string" || !lang) return void 0;
|
|
3279
|
+
if (!languages.has(lang)) {
|
|
3280
|
+
let own2;
|
|
3281
|
+
try {
|
|
3282
|
+
const profile = scriptProfile(presentation, slideIndex, options, lang);
|
|
3283
|
+
own2 = profile.languageSource === "option" && profile.bcp47?.toLowerCase() !== deckProfile.bcp47?.toLowerCase() ? forScripts.call(measurement, profile) : void 0;
|
|
3284
|
+
} catch {
|
|
3285
|
+
own2 = void 0;
|
|
3286
|
+
}
|
|
3287
|
+
languages.set(lang, own2);
|
|
3288
|
+
}
|
|
3289
|
+
return languages.get(lang);
|
|
3290
|
+
};
|
|
3291
|
+
const pick = (style) => forLanguage(style?.lang) ?? deck;
|
|
3292
|
+
const scripted = { ...deck, measure: (text, size, style) => pick(style).measure(text, size, style) };
|
|
3293
|
+
if (typeof deck.resolveStyle === "function") scripted.resolveStyle = (style) => deck.resolveStyle.call(deck, style);
|
|
3294
|
+
if (typeof deck.outlineBounds === "function")
|
|
3295
|
+
scripted.outlineBounds = (text, size, style) => {
|
|
3296
|
+
const own2 = pick(style);
|
|
3297
|
+
return typeof own2.outlineBounds === "function" ? own2.outlineBounds(text, size, style) : deck.outlineBounds.call(deck, text, size, style);
|
|
3298
|
+
};
|
|
3299
|
+
return scripted;
|
|
3300
|
+
}
|
|
3301
|
+
function resolveSlideScriptMeasurement(presentation, slideIndex, measurement, options = {}) {
|
|
3302
|
+
if (typeof measurement.forScripts !== "function") return measurement;
|
|
3303
|
+
const { fontScheme } = resolveDesignRecords(presentation, slideIndex, options.catalogs !== void 0 ? { catalogs: options.catalogs } : {});
|
|
3304
|
+
const resolved = resolveFontFamilies(fontScheme);
|
|
3305
|
+
const fontFamilies = { heading: options.fontFamilies?.heading ?? resolved.heading, body: options.fontFamilies?.body ?? resolved.body };
|
|
3306
|
+
return slideScriptMeasurement(presentation, slideIndex, measurement, { ...options.catalogs !== void 0 ? { catalogs: options.catalogs } : {}, fontFamilies, serif: fontScheme.type === "serif" });
|
|
3307
|
+
}
|
|
3308
|
+
|
|
3120
3309
|
// src/slide-context.ts
|
|
3121
3310
|
var rec2 = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
3122
3311
|
function resolveSlideContext(presentation, index, options = {}) {
|
|
@@ -3152,7 +3341,8 @@ function resolveSlideContext(presentation, index, options = {}) {
|
|
|
3152
3341
|
...options.fonts?.textMeasurement ? { textMeasurement: options.fonts.textMeasurement } : {},
|
|
3153
3342
|
...options.date !== void 0 ? { date: options.date } : {}
|
|
3154
3343
|
};
|
|
3155
|
-
|
|
3344
|
+
const resolvedSlide = resolveSlideVariables(slide, { slideNumber: composeOptions.slideNumber, slideCount: composeOptions.slideCount });
|
|
3345
|
+
return { slide: resolvedSlide, options: composeOptions, diagnostics, resolved: { theme, colorScheme, fontScheme, fontSchemePath: records.fontSchemePath, ...layout ? { layout } : {}, provenance: { ...layoutProvenance ? { layout: layoutProvenance } : {}, ...records.provenance } } };
|
|
3156
3346
|
}
|
|
3157
3347
|
|
|
3158
3348
|
// src/rule-slides.ts
|
|
@@ -3188,10 +3378,17 @@ function buildSlides(document, options) {
|
|
|
3188
3378
|
const slides = Array.isArray(document.slides) ? document.slides : [];
|
|
3189
3379
|
return slides.map((slide, index) => {
|
|
3190
3380
|
const path = pointer2("slides", index);
|
|
3191
|
-
const
|
|
3381
|
+
const baseContext = lazy(() => resolveSlideContext(document, index, { catalogs: options.catalogs, date: "2000-01-01" }));
|
|
3382
|
+
const measurement = lazy(() => {
|
|
3383
|
+
const host = measurementFor(options, index);
|
|
3384
|
+
if (!host) return void 0;
|
|
3385
|
+
const { resolved: resolved2 } = baseContext();
|
|
3386
|
+
return slideScriptMeasurement(document, index, host, { catalogs: options.catalogs, fontFamilies: resolveFontFamilies(resolved2.fontScheme), serif: resolved2.fontScheme.type === "serif" });
|
|
3387
|
+
});
|
|
3192
3388
|
const resolved = lazy(() => {
|
|
3193
3389
|
const textMeasurement = measurement();
|
|
3194
|
-
|
|
3390
|
+
const slideContext = baseContext();
|
|
3391
|
+
return textMeasurement ? { ...slideContext, options: { ...slideContext.options, textMeasurement } } : slideContext;
|
|
3195
3392
|
});
|
|
3196
3393
|
const design = lazy(() => resolveDesign(document, index, resolved()));
|
|
3197
3394
|
const layout = lazy(() => resolved().options.layout);
|
|
@@ -3199,14 +3396,14 @@ function buildSlides(document, options) {
|
|
|
3199
3396
|
const result = { layoutDiagnostics: [] };
|
|
3200
3397
|
const compose = (input, withLayout) => composeSlide(input, { ...resolved().options, layout: withLayout });
|
|
3201
3398
|
try {
|
|
3202
|
-
result.composition = compose(slide, layout());
|
|
3399
|
+
result.composition = compose(resolved().slide, layout());
|
|
3203
3400
|
result.layoutDiagnostics.push(...result.composition.diagnostics.map((diagnostic) => ({ diagnostic, strict: false })));
|
|
3204
3401
|
} catch (error) {
|
|
3205
3402
|
if (error instanceof OPFCompositionError) {
|
|
3206
3403
|
result.layoutDiagnostics.push(...error.diagnostics.map((diagnostic) => ({ diagnostic, strict: true })));
|
|
3207
3404
|
try {
|
|
3208
3405
|
const relaxedLayout = layout();
|
|
3209
|
-
result.composition = compose(relax(slide), relaxedLayout ? relax(relaxedLayout) : void 0);
|
|
3406
|
+
result.composition = compose(relax(resolved().slide), relaxedLayout ? relax(relaxedLayout) : void 0);
|
|
3210
3407
|
} catch (second) {
|
|
3211
3408
|
result.layoutError = second instanceof Error ? second.message : String(second);
|
|
3212
3409
|
}
|
|
@@ -3248,12 +3445,12 @@ var OPFValidationError = class extends Error {
|
|
|
3248
3445
|
report;
|
|
3249
3446
|
/** The error-severity findings of the report. */
|
|
3250
3447
|
findings;
|
|
3251
|
-
constructor(
|
|
3252
|
-
const errors =
|
|
3448
|
+
constructor(report2) {
|
|
3449
|
+
const errors = report2.findings.filter((entry) => entry.severity === "error");
|
|
3253
3450
|
const first = errors[0];
|
|
3254
3451
|
super(first ? `OPF validation failed at ${first.path || "/"}: ${first.message}` : "OPF validation failed");
|
|
3255
3452
|
this.name = "OPFValidationError";
|
|
3256
|
-
this.report =
|
|
3453
|
+
this.report = report2;
|
|
3257
3454
|
this.findings = errors;
|
|
3258
3455
|
}
|
|
3259
3456
|
};
|
|
@@ -3486,8 +3683,8 @@ function validate(input, options = {}) {
|
|
|
3486
3683
|
}
|
|
3487
3684
|
function assertValid(value, options = {}) {
|
|
3488
3685
|
if (typeof value === "string") throw new TypeError("assertValid takes a parsed presentation. Use validate(text) for JSON text.");
|
|
3489
|
-
const
|
|
3490
|
-
if (!
|
|
3686
|
+
const report2 = validate(value, options);
|
|
3687
|
+
if (!report2.valid) throw new OPFValidationError(report2);
|
|
3491
3688
|
}
|
|
3492
3689
|
function validateCatalogRecord(kind, value) {
|
|
3493
3690
|
const kinds = Object.keys(catalogSchemaNames);
|
|
@@ -3508,8 +3705,8 @@ function validateCatalogRecord(kind, value) {
|
|
|
3508
3705
|
return { valid: tally.error === 0, schemaValid: engine.valid, findings, counts: tally, checks };
|
|
3509
3706
|
}
|
|
3510
3707
|
function assertValidCatalogRecord(kind, value) {
|
|
3511
|
-
const
|
|
3512
|
-
if (!
|
|
3708
|
+
const report2 = validateCatalogRecord(kind, value);
|
|
3709
|
+
if (!report2.valid) throw new OPFValidationError(report2);
|
|
3513
3710
|
}
|
|
3514
3711
|
|
|
3515
|
-
export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, resolveSlideContext, validate, validateCatalogRecord, validationCategories, validationRules };
|
|
3712
|
+
export { DEFAULT_CHART_PALETTE, DEFAULT_VALIDATION_THRESHOLDS, OPFValidationError, assertValid, assertValidCatalogRecord, findValidationRule, parseSource, resolveSlideContext, resolveSlideScriptMeasurement, validate, validateCatalogRecord, validationCategories, validationRules };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chartNumber } from './chunk-
|
|
1
|
+
import { chartNumber } from './chunk-CW7GEAV3.js';
|
|
2
2
|
|
|
3
3
|
// src/data.ts
|
|
4
4
|
var OPFDataImportError = class extends Error {
|
|
@@ -138,7 +138,10 @@ function importData(input, options) {
|
|
|
138
138
|
if (circular && numeric < 0) fail(`Row ${i + 1}: pie and donut values cannot be negative.`);
|
|
139
139
|
return numeric;
|
|
140
140
|
}));
|
|
141
|
-
return { chart: { type: options.chartType ?? "column", data: {
|
|
141
|
+
return { chart: { type: options.chartType ?? "column", data: {
|
|
142
|
+
columns: names,
|
|
143
|
+
rows
|
|
144
|
+
} } };
|
|
142
145
|
}
|
|
143
146
|
|
|
144
147
|
export { OPFDataImportError, importData, parseTabularData };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { assertValid, resolveSlideContext } from './chunk-
|
|
2
|
-
import { composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, sliceNumberedItems } from './chunk-
|
|
1
|
+
import { assertValid, resolveSlideScriptMeasurement, resolveSlideContext } from './chunk-NLNYNBTS.js';
|
|
2
|
+
import { resolveSlideVariables, usesSlideBuiltin, composeSlide, codeHighlightSlice, tableRowBoundaries, visitContentPayloads, slideTokenSpans, sliceNumberedItems } from './chunk-CW7GEAV3.js';
|
|
3
3
|
import { checkCatalogsOption } from './chunk-SPFV27O5.js';
|
|
4
4
|
|
|
5
5
|
// src/pagination.ts
|
|
@@ -39,6 +39,10 @@ var deckContext = (presentation) => {
|
|
|
39
39
|
const deck = isRecord(presentation) ? presentation : {};
|
|
40
40
|
return { ...Array.isArray(deck.references) ? { references: deck.references } : {}, ...isRecord(deck.datasets) ? { datasets: deck.datasets } : {} };
|
|
41
41
|
};
|
|
42
|
+
function keepTokensWhole(boundaries, text) {
|
|
43
|
+
const spans = slideTokenSpans(text);
|
|
44
|
+
return spans.length ? boundaries.filter((boundary) => !spans.some(([start, end]) => boundary > start && boundary < end)) : boundaries;
|
|
45
|
+
}
|
|
42
46
|
function leafFor(path, field, value) {
|
|
43
47
|
const leaf = { path, field, value };
|
|
44
48
|
let text;
|
|
@@ -62,7 +66,7 @@ function leafFor(path, field, value) {
|
|
|
62
66
|
}
|
|
63
67
|
if (text !== void 0) {
|
|
64
68
|
leaf.unit = "utf16";
|
|
65
|
-
leaf.boundaries = [0, ...Array.from(graphemes.segment(text), (segment) => segment.index + segment.segment.length)];
|
|
69
|
+
leaf.boundaries = keepTokensWhole([0, ...Array.from(graphemes.segment(text), (segment) => segment.index + segment.segment.length)], text);
|
|
66
70
|
} else if (field === "items" || field === "bullets") {
|
|
67
71
|
leaf.unit = "items";
|
|
68
72
|
leaf.boundaries = Array.from({ length: value.length + 1 }, (_, i) => i);
|
|
@@ -103,11 +107,25 @@ function paginateSlide(input, options = {}) {
|
|
|
103
107
|
};
|
|
104
108
|
source = withReadability(source);
|
|
105
109
|
let evaluations = 0;
|
|
106
|
-
const { fonts, minFontSize: _minFontSize, maxSlides: _maxSlides, reservedIds: _reservedIds, ...engineOptions } = options;
|
|
107
|
-
const
|
|
110
|
+
const { fonts, catalogs, minFontSize: _minFontSize, maxSlides: _maxSlides, reservedIds: _reservedIds, ...engineOptions } = options;
|
|
111
|
+
const textMeasurement = fonts?.textMeasurement && options.presentation !== void 0 ? resolveSlideScriptMeasurement(options.presentation, sourceIndex, fonts.textMeasurement, { ...catalogs !== void 0 ? { catalogs } : {}, ...options.fontFamilies ? { fontFamilies: options.fontFamilies } : {} }) : fonts?.textMeasurement;
|
|
112
|
+
const composeOptions = { ...engineOptions, ...textMeasurement ? { textMeasurement } : {} };
|
|
113
|
+
const readableLayout = (layout) => {
|
|
114
|
+
if (!isRecord(layout) || !Array.isArray(layout.placeholders) || !layout.placeholders.some((entry) => isRecord(entry) && entry.type === "group")) return layout;
|
|
115
|
+
const visit = (entries) => entries.map((entry) => {
|
|
116
|
+
if (!isRecord(entry) || entry.type !== "group") return entry;
|
|
117
|
+
const own = isRecord(entry.composition) ? entry.composition : {};
|
|
118
|
+
return { ...entry, composition: { ...own, ...typeof own.minFontSize === "number" ? { minFontSize: Math.max(minFontSize, own.minFontSize) } : {}, overflow: "warn" }, placeholders: Array.isArray(entry.placeholders) ? visit(entry.placeholders) : entry.placeholders };
|
|
119
|
+
});
|
|
120
|
+
return { ...layout, placeholders: visit(layout.placeholders) };
|
|
121
|
+
};
|
|
122
|
+
if (options.layout !== void 0) composeOptions.layout = readableLayout(options.layout);
|
|
123
|
+
const presentationSlides = options.presentation?.slides;
|
|
124
|
+
const slideCount = options.slideCount ?? (Array.isArray(presentationSlides) && presentationSlides.length ? presentationSlides.length : void 0);
|
|
108
125
|
const geometry = (slide, pageIndex = 0) => {
|
|
109
126
|
if (++evaluations > 2e4) throw new OPFPaginationError("Pagination exceeded its layout evaluation limit. Split the input into smaller sections.");
|
|
110
|
-
|
|
127
|
+
const slideNumber = (options.slideNumber ?? sourceIndex + 1) + pageIndex;
|
|
128
|
+
return composeSlide(resolveSlideVariables(withReadability(slide, true), { slideNumber, ...slideCount !== void 0 ? { slideCount } : {} }), { ...composeOptions, slideNumber });
|
|
111
129
|
};
|
|
112
130
|
const initial = geometry(source);
|
|
113
131
|
const fit = (diagnostics) => diagnostics.filter((issue) => issue.code === "text-overflow" || issue.code === "small-cell" || !!initial.furniture?.diagnostics.includes(issue));
|
|
@@ -123,7 +141,14 @@ function paginateSlide(input, options = {}) {
|
|
|
123
141
|
if (!fit(initial.diagnostics).length) return { slides: [source], pages: [{ slideIndex: sourceIndex, mappings: initial.items.map((item) => ({ sourcePath: item.path, outputPath: item.path })), ...initial.furniture ? { repeatedMappings: repeatedMappings(0) } : {} }] };
|
|
124
142
|
const headerIssues = initial.diagnostics.filter((issue) => headingFields.has(issue.path.slice(sourceBase.length + 1)) || initial.furniture?.diagnostics.includes(issue));
|
|
125
143
|
if (headerIssues.length) throw new OPFPaginationError("Repeated headings or header/footer content cannot fit or resolve. Change the repeated content or slide design before pagination.", headerIssues);
|
|
126
|
-
const
|
|
144
|
+
const tokens = resolveSlideVariables(source, { slideNumber: 1, slideCount: 1 }) !== source;
|
|
145
|
+
const sourceValue = (path, fallback) => {
|
|
146
|
+
if (!tokens || !path.startsWith(`${sourceBase}.`)) return fallback;
|
|
147
|
+
let node = source;
|
|
148
|
+
for (const key of path.slice(sourceBase.length + 1).split(".")) node = isRecord(node) || Array.isArray(node) ? node[key] : void 0;
|
|
149
|
+
return node === void 0 ? fallback : node;
|
|
150
|
+
};
|
|
151
|
+
const leaves = initial.items.filter((item) => !headingFields.has(item.field)).map((item) => leafFor(item.path, item.field, sourceValue(item.path, item.value)));
|
|
127
152
|
const leafPaths = new Set(leaves.map((leaf) => leaf.path));
|
|
128
153
|
const slides = [], pages = [];
|
|
129
154
|
let selected = /* @__PURE__ */ new Map();
|
|
@@ -246,10 +271,6 @@ function paginateSlide(input, options = {}) {
|
|
|
246
271
|
if (!slides.length) throw new OPFPaginationError("No body content can be paginated.", initial.diagnostics);
|
|
247
272
|
return { slides, pages };
|
|
248
273
|
}
|
|
249
|
-
var usesSlideTotal = (presentation) => [presentation, ...presentation.slides].some((owner) => ["header", "footer"].some((kind) => ["left", "center", "right"].some((zone) => {
|
|
250
|
-
const content = owner?.design?.[kind]?.[zone];
|
|
251
|
-
return content?.slideNumber === true && typeof content.slideNumberFormat === "string" && content.slideNumberFormat.includes("{total}");
|
|
252
|
-
})));
|
|
253
274
|
function paginate(input, options = {}) {
|
|
254
275
|
checkCatalogsOption(options.catalogs, "paginate");
|
|
255
276
|
assertValid(input, { only: ["format"] });
|
|
@@ -270,7 +291,7 @@ function paginate(input, options = {}) {
|
|
|
270
291
|
}
|
|
271
292
|
}
|
|
272
293
|
if (output2.length >= maxSlides) throw new OPFPaginationError(`Pagination needs more than ${maxSlides} slides. No partial result was returned.`);
|
|
273
|
-
const result2 = paginateSlide(slide, { ...context.options, textRasterPadding: options.textRasterPadding, fonts: options.fonts, presentation, slideIndex: index, slideNumber: output2.length + 1, slideCount: slideCount2, date: options.date, maxSlides: maxSlides - output2.length, minFontSize: options.minFontSize, reservedIds });
|
|
294
|
+
const result2 = paginateSlide(slide, { ...context.options, textRasterPadding: options.textRasterPadding, fonts: options.fonts, ...options.catalogs ? { catalogs: options.catalogs } : {}, presentation, slideIndex: index, slideNumber: output2.length + 1, slideCount: slideCount2, date: options.date, maxSlides: maxSlides - output2.length, minFontSize: options.minFontSize, reservedIds });
|
|
274
295
|
const outputStart = output2.length;
|
|
275
296
|
result2.pages.forEach((page, pageIndex) => {
|
|
276
297
|
const remap = (mapping) => ({ ...mapping, outputPath: mapping.outputPath.replace(/^slides\.\d+/, `slides.${outputStart + pageIndex}`) });
|
|
@@ -281,9 +302,10 @@ function paginate(input, options = {}) {
|
|
|
281
302
|
});
|
|
282
303
|
return { output: output2, pages: pages2 };
|
|
283
304
|
};
|
|
305
|
+
const usesSlideCount = usesSlideBuiltin(presentation, "deck.slideCount");
|
|
284
306
|
let slideCount = presentation.slides.length, result = run(slideCount);
|
|
285
|
-
for (let attempt = 0;
|
|
286
|
-
if (attempt >= 3) throw new OPFPaginationError("
|
|
307
|
+
for (let attempt = 0; usesSlideCount && result.output.length !== slideCount; attempt++) {
|
|
308
|
+
if (attempt >= 3) throw new OPFPaginationError("The slide count did not converge. Change the text around {{deck.slideCount}} or split the input.");
|
|
287
309
|
slideCount = result.output.length;
|
|
288
310
|
result = run(slideCount);
|
|
289
311
|
}
|