@openpresentation/opf-pptx 0.11.1 → 0.11.3
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/DEPENDENCY-NOTES.md +1 -1
- package/README.md +33 -8
- package/dist/chart-types.js +212 -0
- package/dist/chart-workbook.js +4 -2
- package/dist/chartex.js +375 -0
- package/dist/index.d.ts +18 -3
- package/dist/index.js +243 -101
- package/dist/script-fonts.js +1 -1
- package/dist/theme-colors.js +92 -6
- package/package.json +3 -3
package/dist/script-fonts.js
CHANGED
|
@@ -158,7 +158,7 @@ export function partScriptFonts(path, xml, plan, slideIndex) {
|
|
|
158
158
|
xml = xml.replace(/<p:(sp|graphicFrame)>[\s\S]*?<\/p:\1>/g, shape =>
|
|
159
159
|
runScriptFonts(shape, resolved, /<p:cNvPr\b[^>]*\bname="OPF heading /.test(shape)));
|
|
160
160
|
if (plan.rtl) xml = paragraphRtl(xml, plan.deck.direction);
|
|
161
|
-
} else if (/^ppt\/(?:charts\/chart
|
|
161
|
+
} else if (/^ppt\/(?:charts\/chart(?:Ex)?|notesSlides\/notesSlide)\d+\.xml$/.test(path)) {
|
|
162
162
|
xml = runScriptFonts(xml, resolved, false);
|
|
163
163
|
if (plan.rtl && path.startsWith("ppt/notesSlides/")) xml = paragraphRtl(xml, plan.deck.direction);
|
|
164
164
|
} else if (plan.rtl && /^ppt\/(?:slideMasters\/slideMaster\d+|slideLayouts\/slideLayout\d+|notesMasters\/notesMaster\d+|presentation)\.xml$/.test(path)) {
|
package/dist/theme-colors.js
CHANGED
|
@@ -102,7 +102,9 @@ function backgroundSlot(definition) {
|
|
|
102
102
|
* `vendor` limits the result to scheme values PptxGenJS 4.0.1 can emit.
|
|
103
103
|
*/
|
|
104
104
|
export function schemeColorValue(reference, hex, context, {vendor = false} = {}) {
|
|
105
|
-
|
|
105
|
+
// A slide with its own color scheme is pinned to literal colors: a theme edit
|
|
106
|
+
// in PowerPoint must not partially recolor it, even where a value coincides.
|
|
107
|
+
if (typeof reference !== 'string' || context.schemeOverride) return undefined;
|
|
106
108
|
const name = reference.trim();
|
|
107
109
|
const resolved = sixDigitHex(hex);
|
|
108
110
|
const deck = context.themeColors;
|
|
@@ -120,17 +122,73 @@ export function schemeColorValue(reference, hex, context, {vendor = false} = {})
|
|
|
120
122
|
return undefined;
|
|
121
123
|
}
|
|
122
124
|
|
|
125
|
+
function backgroundOpacity(definition) {
|
|
126
|
+
const opacity = typeof definition === 'object' && Number.isFinite(definition?.opacity) ? definition.opacity : 1;
|
|
127
|
+
return Math.max(0, Math.min(1, opacity));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Scheme value of a theme-slot slide background, or undefined when the background stays literal. */
|
|
131
|
+
export function schemeBackgroundValue(definition, context) {
|
|
132
|
+
const slot = backgroundSlot(definition);
|
|
133
|
+
return slot ? schemeColorValue(slot, context.colors?.background, context) : undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
123
136
|
/** Native slide background fill for a theme-slot background, or null when it must stay literal. */
|
|
124
137
|
export function schemeBackgroundFill(definition, context) {
|
|
125
|
-
const
|
|
126
|
-
if (!slot) return null;
|
|
127
|
-
const value = schemeColorValue(slot, context.colors?.background, context);
|
|
138
|
+
const value = schemeBackgroundValue(definition, context);
|
|
128
139
|
if (!value) return null;
|
|
129
|
-
const
|
|
130
|
-
const alpha = Math.round(Math.max(0, Math.min(1, opacity)) * 100000);
|
|
140
|
+
const alpha = Math.round(backgroundOpacity(definition) * 100000);
|
|
131
141
|
return `<a:solidFill>${alpha === 100000 ? `<a:schemeClr val="${value}"/>` : `<a:schemeClr val="${value}"><a:alpha val="${alpha}"/></a:schemeClr>`}</a:solidFill>`;
|
|
132
142
|
}
|
|
133
143
|
|
|
144
|
+
// The exporter picks default text for contrast with the slide background:
|
|
145
|
+
// light1 on a dark background, else the scheme text (dark1); muted text is
|
|
146
|
+
// dark2/light2. When the background itself is a theme reference, the matching
|
|
147
|
+
// text slot is the semantic pair, so a PowerPoint theme switch moves both.
|
|
148
|
+
const TEXT_PAIRS = Object.freeze({
|
|
149
|
+
light1: {text: 'dark1', muted: 'dark2'}, light2: {text: 'dark1', muted: 'dark2'},
|
|
150
|
+
dark1: {text: 'light1', muted: 'light2'}, dark2: {text: 'light1', muted: 'light2'},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Scheme values for the exporter's default text and muted text on this slide.
|
|
155
|
+
* A role gets a scheme value only when the slide background is an opaque theme
|
|
156
|
+
* reference to a light or dark slot, the slide has no scheme override, and the
|
|
157
|
+
* resolved default color is exactly the paired slot in the deck theme.
|
|
158
|
+
*/
|
|
159
|
+
export function defaultTextSchemeValues(context) {
|
|
160
|
+
const background = schemeBackgroundValue(context.backgroundDefinition, context);
|
|
161
|
+
if (!background || backgroundOpacity(context.backgroundDefinition) !== 1) return {};
|
|
162
|
+
const pair = TEXT_PAIRS[backgroundSlot(context.backgroundDefinition)];
|
|
163
|
+
if (!pair) return {};
|
|
164
|
+
const values = {};
|
|
165
|
+
for (const role of ['text', 'muted']) {
|
|
166
|
+
const hex = role === 'text' ? context.colors?.text : context.colors?.mutedText;
|
|
167
|
+
const value = schemeColorValue(pair[role], hex, context);
|
|
168
|
+
if (value) values[role] = value;
|
|
169
|
+
}
|
|
170
|
+
return values;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Scheme value for the exporter's default text in a table cell whose fill is a
|
|
175
|
+
* theme reference (`fillValue`, from schemeColorValue). A light or dark fill (bg1,
|
|
176
|
+
* bg2, tx1, tx2) pairs with the opposite text slot (dark1 on bg, light1 on tx).
|
|
177
|
+
* An accent fill has contrast-selected text, which stays a theme reference only
|
|
178
|
+
* when the selection is exactly the deck light1 or dark1. Anything else, and any
|
|
179
|
+
* fill that is not a theme reference, keeps literal text.
|
|
180
|
+
*/
|
|
181
|
+
export function tableTextSchemeValue(fillValue, hex, context) {
|
|
182
|
+
if (!fillValue) return undefined;
|
|
183
|
+
const light = /^bg[12]$/.test(fillValue), dark = /^tx[12]$/.test(fillValue);
|
|
184
|
+
const candidates = light ? ['dark1'] : dark ? ['light1'] : /^accent[1-6]$/.test(fillValue) ? ['light1', 'dark1'] : [];
|
|
185
|
+
for (const slot of candidates) {
|
|
186
|
+
const value = schemeColorValue(slot, hex, context, {vendor: true});
|
|
187
|
+
if (value) return value;
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
134
192
|
// ---------------------------------------------------------------------------
|
|
135
193
|
// Import
|
|
136
194
|
|
|
@@ -202,3 +260,31 @@ export function recoverTheme({themeName, colors, majorFont, minorFont}, {themes,
|
|
|
202
260
|
const fontMatch = !!fonts && fonts.heading === majorFont && fonts.body === minorFont;
|
|
203
261
|
return colorMatch || fontMatch ? {id: theme.id} : {unverified: theme.id};
|
|
204
262
|
}
|
|
263
|
+
|
|
264
|
+
// ---------------------------------------------------------------------------
|
|
265
|
+
// Slide master
|
|
266
|
+
|
|
267
|
+
/** a:solidFill child for a pptx color value: a scheme value or RRGGBB. */
|
|
268
|
+
export function solidColorXml(value) {
|
|
269
|
+
return /^(?:tx[12]|bg[12]|accent[1-6]|hlink|folHlink)$/.test(value) ? `<a:schemeClr val="${value}"/>` : `<a:srgbClr val="${value}"/>`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Give the slide master the deck's theme background and matching default text
|
|
274
|
+
* so slides added in PowerPoint follow the chosen theme. The vendored master
|
|
275
|
+
* has no background (its layout uses the bg1 background style) and tx1 text.
|
|
276
|
+
*/
|
|
277
|
+
export function writeMasterBackground(xml, {fill, text}) {
|
|
278
|
+
let result = xml.replace(/<p:bg>[\s\S]*?<\/p:bg>/, '');
|
|
279
|
+
result = result.replace(/<p:cSld\b([^>]*)>/, `<p:cSld$1><p:bg><p:bgPr>${fill}<a:effectLst/></p:bgPr></p:bg>`);
|
|
280
|
+
if (text && text !== 'tx1') {
|
|
281
|
+
result = result.replace(/<p:txStyles>[\s\S]*?<\/p:txStyles>/, styles =>
|
|
282
|
+
styles.split('<a:solidFill><a:schemeClr val="tx1"/></a:solidFill>').join(`<a:solidFill>${solidColorXml(text)}</a:solidFill>`));
|
|
283
|
+
}
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/** The vendored layout's own bg1 background would hide the master background. */
|
|
288
|
+
export function inheritLayoutBackground(xml) {
|
|
289
|
+
return xml.replace(/<p:bg>[\s\S]*?<\/p:bg>/, '');
|
|
290
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpresentation/opf-pptx",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "Pure local OPF to PPTX export and PPTX to OPF import tooling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"sideEffects": false,
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "node scripts/build.mjs",
|
|
40
|
-
"typecheck": "node --check src/index.js && node --check test/design-alignment.mjs && node --check src/color-ref.js && node --check src/theme-colors.js && node --check test/theme-colors.mjs && node --check scripts/build.mjs && node --check scripts/validate-package.mjs && node --check test/smoke.mjs && node --check src/image-geometry.js && node --check src/slide-image-provenance.js && node --check test/slide-image.mjs && node --check test/slide-image-treatments.mjs && node --check src/watermark-provenance.js && node --check src/media-dedupe.js && node --check test/media-dedupe.mjs && node --check test/watermark.mjs && node --check test/image-fit.mjs && node --check test/image-orientation.mjs && node --check test/table-layout.mjs && node --check test/table-import.mjs && node --check test/object-ids.mjs && node --check test/export-corpus.mjs && node --check test/image-media-type.mjs && node --check src/image-fallback-node.js && node --check src/image-fallback-browser.js && node --check test/webp-fallback.mjs && node --check scripts/build-browser-check.mjs && node --check test/webp-fallback-browser.js && node --check test/image-fallback-boundary.mjs && node --check src/background.js && node --check test/background.mjs && node --check src/image-import.js && node --check test/image-import.mjs && node --check test/native-background.mjs && node --check src/background-import.js && node --check test/background-inheritance.mjs && node --check test/rich-table.mjs && node --check src/table-import.js && node --check test/rich-table-import.mjs && node --check test/rich-table-import-browser.js && node --check test/table-row-sizing.mjs && node --check test/table-styles.mjs && node --check test/table-styles-browser.js && node --check test/styled-table.mjs && node --check src/table-cell-import.js && node --check test/styled-table-import.mjs && node --check test/styled-table-import-browser.js && node --check src/table-border-import.js && node --check test/table-border-styles.mjs && node --check src/card-provenance.js && node --check test/content-cards.mjs && node --check src/chart-workbook.js && node --check test/chart-colors.mjs && node --check test/chart-workbook.mjs && node --check test/single-column-chart.mjs && node --check test/native-chart-colors.mjs && node --check src/heading-provenance.js && node --check test/accepted-text.mjs && node --check test/heading-provenance.mjs && node --check src/text-provenance.js && node --check test/plain-whitespace.mjs && node --check src/timeline-provenance.js && node --check test/shared-timeline.mjs && node --check test/shared-furniture.mjs && node --check src/furniture-provenance.js && node --check src/document-provenance.js && node --check test/document-provenance.mjs && node --check test/furniture-provenance.mjs && node --check src/furniture-fields.js && node --check test/furniture-fields.mjs && node --check test/furniture-wrapped-date.mjs && node --check test/native-picture.mjs && node --check test/compare-native-picture.mjs && node --check test/compare-native-picture-edit.mjs && node --check test/compare-native-picture-edit-controls.mjs && node --check test/native-font-embed-audit.mjs && node --check test/native-font-embed-controls.mjs && node --check test/native-font-embed-fixture-source.mjs && node --check test/native-font-edit-fixture.mjs && node --check test/native-mixed-edit-audit.mjs && node --check test/native-mixed-edit-controls.mjs && node --check test/master-bullet-font.mjs && node --check test/native-font-inventory-audit.mjs && node --check test/native-font-inventory-controls.mjs && node --check test/default-font-scheme.mjs && node --check test/export-chosen-fonts.mjs && node --check test/background-fills.mjs && node --check src/typeface-inventory.js && node --check src/package-fonts.js && node --check test/typeface-inventory.mjs && node --check src/script-fonts.js && node --check test/script-fonts.mjs && node --check test/powershell-scan.mjs && node --check test/powershell-scan-probes.mjs && node --check test/socials-furniture.mjs && node --check src/media-provenance.js && node --check test/layout-parity.mjs && node --check test/cover-centering.mjs && node --check test/media-placeholder.mjs && node --check test/image-placeholder.mjs && node --check test/list-shape-names.mjs && node --check test/layout-intent.mjs && node --check test/chart-cache-import.mjs && node --check test/zip-date.mjs && node --check test/native-text-whitespace.mjs && node --check test/native-underline.mjs && node --check src/native-text-style.js && node --check src/font-weights.js && node --check test/font-weight-import.mjs && node --check src/body-text-import.js && node --check test/native-body-rich-text.mjs && node --check test/native-body-rich-text-browser.js && node --check test/native-quote-import-contract.mjs && node --check test/native-quote-import-controls.mjs && node --check test/native-quote.mjs && node --check test/determinism.mjs && node --check test/determinism-fixtures.mjs && node --check test/table-frame-furniture-order.mjs && node --check test/shape-border-widths.mjs",
|
|
41
|
-
"test": "npm run build && node test/zip-date.mjs && node test/background-fills.mjs && npm run test:furniture && node test/slide-image.mjs && node test/slide-image-treatments.mjs && node test/watermark.mjs && node test/media-dedupe.mjs && node test/document-provenance.mjs && node test/layout-intent.mjs && node test/master-bullet-font.mjs && node test/default-font-scheme.mjs && node test/script-fonts.mjs && node test/export-chosen-fonts.mjs && node test/typeface-inventory.mjs && node test/color-ref-export.mjs && node test/theme-colors.mjs && node test/plain-whitespace.mjs && node test/native-text-whitespace.mjs && node test/native-body-rich-text.mjs && node test/native-underline.mjs && node test/font-weight-import.mjs && node test/design-alignment.mjs && node test/accepted-text.mjs && node test/heading-provenance.mjs && node test/native-quote-import-controls.mjs && node test/chart-colors.mjs && node test/content-cards.mjs && node test/chart-workbook.mjs && node test/single-column-chart.mjs && node --test test/chart-cache-import.mjs && node test/dependency-boundary.mjs && npm run build:browser-check && npm run test:styled-table && node test/content-layout.mjs && node test/layout-parity.mjs && node test/cover-centering.mjs && node test/table-frame-furniture-order.mjs && node test/media-placeholder.mjs && node test/shape-border-widths.mjs && node test/image-placeholder.mjs && node test/list-shape-names.mjs && node test/shared-quote.mjs && npm run test:timeline && npm run test:determinism",
|
|
40
|
+
"typecheck": "node --check src/index.js && node --check src/chart-types.js && node --check src/chartex.js && node --check test/chartex.mjs && node --check test/chart-types.mjs && node --check test/design-alignment.mjs && node --check src/color-ref.js && node --check src/theme-colors.js && node --check test/theme-colors.mjs && node --check test/table-theme-colors.mjs && node --check scripts/build.mjs && node --check scripts/validate-package.mjs && node --check test/smoke.mjs && node --check src/image-geometry.js && node --check src/slide-image-provenance.js && node --check test/slide-image.mjs && node --check test/slide-image-treatments.mjs && node --check src/watermark-provenance.js && node --check src/media-dedupe.js && node --check test/media-dedupe.mjs && node --check test/watermark.mjs && node --check test/image-fit.mjs && node --check test/image-orientation.mjs && node --check test/table-layout.mjs && node --check test/table-import.mjs && node --check test/object-ids.mjs && node --check test/export-corpus.mjs && node --check test/image-media-type.mjs && node --check src/image-fallback-node.js && node --check src/image-fallback-browser.js && node --check test/webp-fallback.mjs && node --check scripts/build-browser-check.mjs && node --check test/webp-fallback-browser.js && node --check test/image-fallback-boundary.mjs && node --check src/background.js && node --check test/background.mjs && node --check src/image-import.js && node --check test/image-import.mjs && node --check test/native-background.mjs && node --check src/background-import.js && node --check test/background-inheritance.mjs && node --check test/rich-table.mjs && node --check src/table-import.js && node --check test/rich-table-import.mjs && node --check test/rich-table-import-browser.js && node --check test/table-row-sizing.mjs && node --check test/table-styles.mjs && node --check test/table-styles-browser.js && node --check test/styled-table.mjs && node --check src/table-cell-import.js && node --check test/styled-table-import.mjs && node --check test/styled-table-import-browser.js && node --check src/table-border-import.js && node --check test/table-border-styles.mjs && node --check src/card-provenance.js && node --check test/content-cards.mjs && node --check src/chart-workbook.js && node --check test/chart-colors.mjs && node --check test/chart-workbook.mjs && node --check test/single-column-chart.mjs && node --check test/native-chart-colors.mjs && node --check src/heading-provenance.js && node --check test/accepted-text.mjs && node --check test/heading-provenance.mjs && node --check src/text-provenance.js && node --check test/plain-whitespace.mjs && node --check src/timeline-provenance.js && node --check test/shared-timeline.mjs && node --check test/shared-furniture.mjs && node --check src/furniture-provenance.js && node --check src/document-provenance.js && node --check test/document-provenance.mjs && node --check test/furniture-provenance.mjs && node --check src/furniture-fields.js && node --check test/furniture-fields.mjs && node --check test/furniture-wrapped-date.mjs && node --check test/native-picture.mjs && node --check test/compare-native-picture.mjs && node --check test/compare-native-picture-edit.mjs && node --check test/compare-native-picture-edit-controls.mjs && node --check test/native-font-embed-audit.mjs && node --check test/native-font-embed-controls.mjs && node --check test/native-font-embed-fixture-source.mjs && node --check test/native-font-edit-fixture.mjs && node --check test/native-mixed-edit-audit.mjs && node --check test/native-mixed-edit-controls.mjs && node --check test/master-bullet-font.mjs && node --check test/native-font-inventory-audit.mjs && node --check test/native-font-inventory-controls.mjs && node --check test/default-font-scheme.mjs && node --check test/export-chosen-fonts.mjs && node --check test/background-fills.mjs && node --check src/typeface-inventory.js && node --check src/package-fonts.js && node --check test/typeface-inventory.mjs && node --check src/script-fonts.js && node --check test/script-fonts.mjs && node --check test/powershell-scan.mjs && node --check test/powershell-scan-probes.mjs && node --check test/socials-furniture.mjs && node --check src/media-provenance.js && node --check test/layout-parity.mjs && node --check test/cover-centering.mjs && node --check test/media-placeholder.mjs && node --check test/image-placeholder.mjs && node --check test/list-shape-names.mjs && node --check test/layout-intent.mjs && node --check test/chart-cache-import.mjs && node --check test/zip-date.mjs && node --check test/native-text-whitespace.mjs && node --check test/native-underline.mjs && node --check src/native-text-style.js && node --check src/font-weights.js && node --check test/font-weight-import.mjs && node --check src/body-text-import.js && node --check test/native-body-rich-text.mjs && node --check test/native-body-rich-text-browser.js && node --check test/native-quote-import-contract.mjs && node --check test/native-quote-import-controls.mjs && node --check test/native-quote.mjs && node --check test/determinism.mjs && node --check test/determinism-fixtures.mjs && node --check test/table-frame-furniture-order.mjs && node --check test/shape-border-widths.mjs",
|
|
41
|
+
"test": "npm run build && node test/chart-types.mjs && node test/chartex.mjs && node test/zip-date.mjs && node test/background-fills.mjs && npm run test:furniture && node test/slide-image.mjs && node test/slide-image-treatments.mjs && node test/watermark.mjs && node test/media-dedupe.mjs && node test/document-provenance.mjs && node test/layout-intent.mjs && node test/master-bullet-font.mjs && node test/default-font-scheme.mjs && node test/script-fonts.mjs && node test/export-chosen-fonts.mjs && node test/typeface-inventory.mjs && node test/color-ref-export.mjs && node test/theme-colors.mjs && node test/table-theme-colors.mjs && node test/plain-whitespace.mjs && node test/native-text-whitespace.mjs && node test/native-body-rich-text.mjs && node test/native-underline.mjs && node test/font-weight-import.mjs && node test/design-alignment.mjs && node test/accepted-text.mjs && node test/heading-provenance.mjs && node test/native-quote-import-controls.mjs && node test/chart-colors.mjs && node test/content-cards.mjs && node test/chart-workbook.mjs && node test/single-column-chart.mjs && node --test test/chart-cache-import.mjs && node test/dependency-boundary.mjs && npm run build:browser-check && npm run test:styled-table && node test/content-layout.mjs && node test/layout-parity.mjs && node test/cover-centering.mjs && node test/table-frame-furniture-order.mjs && node test/media-placeholder.mjs && node test/shape-border-widths.mjs && node test/image-placeholder.mjs && node test/list-shape-names.mjs && node test/shared-quote.mjs && npm run test:timeline && npm run test:determinism",
|
|
42
42
|
"validate": "node scripts/validate-package.mjs",
|
|
43
43
|
"test:packed": "node test/packed-install.mjs",
|
|
44
44
|
"test:compare-published": "node test/compare-published.mjs",
|