@shbernal/ts-pptx 4.1.0 → 4.2.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 +11 -11
- package/dist/anim-presets-Qbgh8uut.js +55 -0
- package/dist/anim-presets-Qbgh8uut.js.map +1 -0
- package/dist/{autopage-D4kyVp11.js → autopage-DqZ37VpL.js} +11 -12
- package/dist/autopage-DqZ37VpL.js.map +1 -0
- package/dist/{binary-string-CKVAYI3M.js → binary-string-Ba8VM3b9.js} +3 -4
- package/dist/binary-string-Ba8VM3b9.js.map +1 -0
- package/dist/{body-insets-kVHmjGc6.js → body-insets-BDHymJvz.js} +7 -9
- package/dist/body-insets-BDHymJvz.js.map +1 -0
- package/dist/browser.d.ts +24 -16
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +19 -19
- package/dist/browser.js.map +1 -1
- package/dist/{image-size-DwDbakcl.js → chart-metadata-CwiTTcJq.js} +576 -538
- package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
- package/dist/{clip-DHDlnHUq.d.ts → clip-ISfCMtWA.d.ts} +6 -10
- package/dist/clip-ISfCMtWA.d.ts.map +1 -0
- package/dist/{notes-BcT571R4.js → comments-CKIZYRbZ.js} +517 -389
- package/dist/comments-CKIZYRbZ.js.map +1 -0
- package/dist/{diagnostics-DiMmBU8P.d.ts → diagnostics-MoA4MBq5.d.ts} +52 -50
- package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
- package/dist/{embedded-fonts-CI_YGW2d.d.ts → embedded-fonts-U7HIIhPO.d.ts} +4 -6
- package/dist/embedded-fonts-U7HIIhPO.d.ts.map +1 -0
- package/dist/{entry-compose-6hF9aTmT.d.ts → entry-compose-CiakSt-f.d.ts} +53 -74
- package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
- package/dist/{errors-Da2nXCJm.js → entry-errors-JMthgJ2g.js} +41 -25
- package/dist/entry-errors-JMthgJ2g.js.map +1 -0
- package/dist/{table-cell-margins-PDshga8T.js → enums-BP9wsb9Q.js} +42 -45
- package/dist/enums-BP9wsb9Q.js.map +1 -0
- package/dist/{enums-DQRpVOLh.d.ts → enums-PcXr3o3J.d.ts} +11 -12
- package/dist/{enums-DQRpVOLh.d.ts.map → enums-PcXr3o3J.d.ts.map} +1 -1
- package/dist/families.d.ts +24 -26
- package/dist/families.d.ts.map +1 -1
- package/dist/families.js +3 -4
- package/dist/{fit-CcXbg__l.js → fit-Bpmzep6O.js} +60 -52
- package/dist/fit-Bpmzep6O.js.map +1 -0
- package/dist/{font-metrics-CBGmhLo0.d.ts → font-metrics-DTppw4t3.d.ts} +61 -37
- package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
- package/dist/{grid-9B2w5EaY.js → grid-DUIMxjfl.js} +23 -26
- package/dist/grid-DUIMxjfl.js.map +1 -0
- package/dist/{html-dom-DvJmuHt2.js → html-dom-LZCgntnJ.js} +42 -43
- package/dist/html-dom-LZCgntnJ.js.map +1 -0
- package/dist/html.d.ts +53 -9
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +12 -13
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +21 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -15
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +14 -16
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +18 -19
- package/dist/inspect.js.map +1 -1
- package/dist/{text-anchor-BgLPbwbL.js → lock-attrs-CSQnFDHP.js} +62 -12
- package/dist/lock-attrs-CSQnFDHP.js.map +1 -0
- package/dist/math.d.ts +2 -4
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +12 -13
- package/dist/math.js.map +1 -1
- package/dist/measure.d.ts +23 -12
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +3 -3
- package/dist/node.d.ts +21 -13
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +12 -12
- package/dist/node.js.map +1 -1
- package/dist/{options-internal-WqgMf720.js → options-internal-BWgmLPdx.js} +4 -5
- package/dist/options-internal-BWgmLPdx.js.map +1 -0
- package/dist/{package-KoEgVwII.d.ts → package-CANBWuSm.d.ts} +4 -12
- package/dist/package-CANBWuSm.d.ts.map +1 -0
- package/dist/{playbtn-CUqQdoDf.js → playbtn-BQZFJir9.js} +4 -5
- package/dist/playbtn-BQZFJir9.js.map +1 -0
- package/dist/{pres-BA9_lJbY.d.ts → pres-BkEkUAEM.d.ts} +383 -230
- package/dist/pres-BkEkUAEM.d.ts.map +1 -0
- package/dist/{presentation-Cyl74rq2.js → presentation-4Ggq0_Xa.js} +408 -288
- package/dist/presentation-4Ggq0_Xa.js.map +1 -0
- package/dist/{presentation-types-B3VQH4Bz.d.ts → presentation-types-BgnZR-0a.d.ts} +1104 -457
- package/dist/presentation-types-BgnZR-0a.d.ts.map +1 -0
- package/dist/read.d.ts +18 -13
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +4 -4
- package/dist/script.d.ts +116 -65
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +2697 -777
- package/dist/script.js.map +1 -1
- package/dist/{shapes-BcyFdZpp.js → shapes-CAPOjU0Q.js} +1556 -487
- package/dist/shapes-CAPOjU0Q.js.map +1 -0
- package/dist/{slide-BqPtyKmb.d.ts → slide-BmT4kMoO.d.ts} +94 -86
- package/dist/slide-BmT4kMoO.d.ts.map +1 -0
- package/dist/{slide-size-BalTjFuv.js → slide-size-SkScz1ig.js} +2 -3
- package/dist/{slide-size-BalTjFuv.js.map → slide-size-SkScz1ig.js.map} +1 -1
- package/dist/{table-dom-CVbvUfgh.js → table-dom-BYga4FYY.js} +3 -4
- package/dist/{table-dom-CVbvUfgh.js.map → table-dom-BYga4FYY.js.map} +1 -1
- package/dist/{text-BvFFMMFQ.js → text-CU08xJGs.js} +365 -318
- package/dist/text-CU08xJGs.js.map +1 -0
- package/dist/{transition-speed-kUEWDWTx.js → transition-speed-CNJ0fSpQ.js} +2 -3
- package/dist/{transition-speed-kUEWDWTx.js.map → transition-speed-CNJ0fSpQ.js.map} +1 -1
- package/dist/{units-internal-DFcUYjh3.js → units-internal-BR3a4cIi.js} +26 -29
- package/dist/units-internal-BR3a4cIi.js.map +1 -0
- package/dist/{zip-CYZ0F6BV.js → zip-7JTh61LX.js} +8 -10
- package/dist/zip-7JTh61LX.js.map +1 -0
- package/dist/{zip-CTll_dr0.d.ts → zip-uUjs9Cqx.d.ts} +4 -6
- package/dist/zip-uUjs9Cqx.d.ts.map +1 -0
- package/dist/zip.d.ts +3 -3
- package/dist/zip.js +3 -3
- package/dist/{zoom-BoNyOlPm.js → zoom-CSSeOwM8.js} +1153 -583
- package/dist/zoom-CSSeOwM8.js.map +1 -0
- package/package.json +20 -21
- package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +27 -74
- package/dist/autopage-D4kyVp11.js.map +0 -1
- package/dist/binary-string-CKVAYI3M.js.map +0 -1
- package/dist/body-insets-kVHmjGc6.js.map +0 -1
- package/dist/clip-DHDlnHUq.d.ts.map +0 -1
- package/dist/diagnostics-DiMmBU8P.d.ts.map +0 -1
- package/dist/embedded-fonts-CI_YGW2d.d.ts.map +0 -1
- package/dist/entry-compose-6hF9aTmT.d.ts.map +0 -1
- package/dist/errors-Da2nXCJm.js.map +0 -1
- package/dist/fit-CcXbg__l.js.map +0 -1
- package/dist/font-metrics-CBGmhLo0.d.ts.map +0 -1
- package/dist/grid-9B2w5EaY.js.map +0 -1
- package/dist/html-dom-DvJmuHt2.js.map +0 -1
- package/dist/image-size-DwDbakcl.js.map +0 -1
- package/dist/notes-BcT571R4.js.map +0 -1
- package/dist/options-internal-WqgMf720.js.map +0 -1
- package/dist/package-KoEgVwII.d.ts.map +0 -1
- package/dist/playbtn-CUqQdoDf.js.map +0 -1
- package/dist/pres-BA9_lJbY.d.ts.map +0 -1
- package/dist/presentation-Cyl74rq2.js.map +0 -1
- package/dist/presentation-types-B3VQH4Bz.d.ts.map +0 -1
- package/dist/shapes-BcyFdZpp.js.map +0 -1
- package/dist/slide-BqPtyKmb.d.ts.map +0 -1
- package/dist/table-cell-margins-PDshga8T.js.map +0 -1
- package/dist/text-BvFFMMFQ.js.map +0 -1
- package/dist/text-anchor-BgLPbwbL.js.map +0 -1
- package/dist/units-internal-DFcUYjh3.js.map +0 -1
- package/dist/zip-CTll_dr0.d.ts.map +0 -1
- package/dist/zip-CYZ0F6BV.js.map +0 -1
- package/dist/zoom-BoNyOlPm.js.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { D as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { t as transitionSpeedForDuration } from "./transition-speed-
|
|
9
|
-
import {
|
|
10
|
-
|
|
1
|
+
import { D as emuToInches } from "./enums-BP9wsb9Q.js";
|
|
2
|
+
import { n as readZip, t as ZipWriter } from "./zip-7JTh61LX.js";
|
|
3
|
+
import { $ as LST_STYLE_LEVELS, $t as TXBODY_AFTER_LSTSTYLE, A as enumerateSpids, At as themeElementsOfRoot, Bt as PRESENTATION_AFTER_SLD_MASTER_ID_LST, Cn as removeChildrenByQName, Ct as parseClrScheme, D as AutoShape, Dt as styleRefFill, E as Picture, Et as resolveSchemeToken, F as remapSpids, Ft as EMBEDDED_FONT_ENTRY_AFTER, Gt as SLIDE_MASTER_AFTER_SLD_LAYOUT_ID_LST, H as readGradientFill, Ht as RUN_AFTER_RPR, I as TextFrame, It as LN_SLOTS, J as resolveNotesColorContext, Jt as SPPR_FILL_AFTER, K as resolveLayoutColorContext, Kt as SPPR_AFTER_XFRM, Lt as PRESENTATION_AFTER_EMBEDDED_FONT_LST, M as hasAnimations, Mt as hasFillChoice, N as pruneSpids, Ot as styleRefLine, P as readBuildEffects, Pt as CSLD_AFTER_BG, Q as checkPositiveEmu, Qt as TXBODY_AFTER_BODYPR, Rt as PRESENTATION_AFTER_NOTES_MASTER_ID_LST, Sn as removeAttr, St as parseClrMap, Tn as setAttr, Tt as resolveColor, U as readPictureFill, Ut as SHAPE_AFTER_SPPR, V as readPatternFill, Vt as RPR_FILL_AFTER, W as readColorRef, Wt as SLIDE_AFTER_TRANSITION, X as resolveSlideThemeParts, Xt as SPPR_SCENE3D_AFTER, Y as resolveSlideColorContext, Yt as SPPR_LN_AFTER, Z as checkFiniteEmu, Zt as SPPR_SP3D_AFTER, _n as insertInOrder, _t as nvPrOf, an as resolveSingleRel, at as placeholderInheritedRunProps, bn as ownerDocumentOf, bt as fmtEntry, cn as attr, ct as slideDefinesColor, dn as createElement, dt as cSldOf, en as partNameExtension, et as RUN_PROP_NAMES, fn as descendantsByTag, ft as carriedDecorations, gn as getOrAddChild, gt as nthShapeChild, hn as getElements, ht as nextDrawingId, in as resolveRelativePartName, it as placeholderInheritedListStyles, j as flattenAnimations, k as carryShapeAnimations, kt as substitutePhClr, l as wrapShapeElement, ln as boolAttr, lt as slideDefinesProp, m as resolveTableCellTextStyle, mn as firstChildElement, mt as firstShapeChild, n as buildShapes, nn as relativePartName, nt as placeholderInheritedAnchor, on as singleRelPart, ot as placeholderInheritedXfrm, pn as firstChild, pt as collectElements, q as resolveMasterColorContext, qt as SPPR_EFFECT_AFTER, r as findShapeByIdDeep, rn as relsPartNameFor, rt as placeholderInheritedColor, sn as zipPathToPartName, st as placeholderOf, t as GroupShape, tn as partNameToZipPath, tt as lstStyleLevelDefRPr, u as GraphicFrame, un as childElements, ut as cSldName, vn as numberAttr, vt as reassignDrawingIds, wn as serializeXml, wt as replaceColorElement, xn as parseXml, xt as isA, yn as numberValue, yt as spTreeOf, zt as PRESENTATION_AFTER_SLD_ID_LST } from "./shapes-CAPOjU0Q.js";
|
|
4
|
+
import { Ft as voidEl, It as XML_DECL, Kt as encodeXmlAttrValue, Mt as el, Pt as raw, a as makeXmlNotesMaster, gt as FONT_REL_TYPE, ht as FONT_DATA_EXTENSION, l as makeXmlNotesSlideSkeleton, mt as FONT_DATA_CONTENT_TYPE, n as makeXmlComments, t as makeXmlCommentAuthors } from "./comments-CKIZYRbZ.js";
|
|
5
|
+
import { a as PackageReadError, n as InternalError, r as InvalidOptionError, s as UnsupportedFeatureError, u as warn } from "./entry-errors-JMthgJ2g.js";
|
|
6
|
+
import { B as NOTES_SLIDE_CONTENT_TYPE, C as COMMENT_AUTHORS_REL, F as MS_MEDIA_REL, G as PACKAGE_REL, I as MS_POWERPOINT_CONTENT, L as MS_REL, M as IMAGE_REL, P as MODERN_COMMENT_AUTHORS_REL, Q as SLIDE_CONTENT_TYPE, Qt as THEME_COLOR_SLOTS, R as NOTES_MASTER_CONTENT_TYPE, S as COMMENT_AUTHORS_CONTENT_TYPE, T as CORE_PROPS_REL, Tt as EMBEDDED_FONT_SLOTS, U as OFFICE_DOCUMENT_REL, V as NOTES_SLIDE_REL, W as OFFICE_REL, X as RELATIONSHIPS_CONTENT_TYPE, Z as SHARED_PARTS, _ as CHART_REL, _n as imageFormatForBytes, at as TABLE_STYLES_REL, b as COMMENTS_CONTENT_TYPE, ct as VIDEO_REL, d as AUDIO_REL, dt as XLSX_CONTENT_TYPE, et as SLIDE_LAYOUT_REL, f as CHARTEX_CONTENT_TYPE, ft as OOXML_NS, g as CHART_CONTENT_TYPE, h as CHART_COLOR_STYLE_REL, it as TABLE_STYLES_CONTENT_TYPE, j as HYPERLINK_REL, m as CHART_COLOR_STYLE_CONTENT_TYPE, nt as SLIDE_MASTER_REL, on as boolValue, ot as THEME_CONTENT_TYPE, p as CHARTEX_REL, q as PRESENTATION_MAIN_CONTENT_TYPE, rt as SLIDE_REL, st as THEME_REL, v as CHART_STYLE_CONTENT_TYPE, x as COMMENTS_REL, xt as COLOR_MAP_TOKENS, y as CHART_STYLE_REL, z as NOTES_MASTER_REL } from "./chart-metadata-CwiTTcJq.js";
|
|
7
|
+
import { X as MIN_SLIDE_MASTER_ID, Y as MAX_SLIDE_ID } from "./units-internal-BR3a4cIi.js";
|
|
8
|
+
import { t as transitionSpeedForDuration } from "./transition-speed-CNJ0fSpQ.js";
|
|
9
|
+
import { t as pickDefined } from "./options-internal-BWgmLPdx.js";
|
|
10
|
+
// src/read/opc/content-types.ts
|
|
11
11
|
/**
|
|
12
12
|
* Overlay over `[Content_Types].xml`, used to resolve part content types at
|
|
13
13
|
* load and to register new ones when parts are added. While clean, the original
|
|
@@ -57,7 +57,7 @@ var ContentTypes = class ContentTypes {
|
|
|
57
57
|
}
|
|
58
58
|
/**
|
|
59
59
|
* Drop the `Override` for `partName` if present (e.g. when a part is removed).
|
|
60
|
-
* `Default` rules by extension are left untouched
|
|
60
|
+
* `Default` rules by extension are left untouched; they may still serve other
|
|
61
61
|
* parts. Marks the overlay dirty when something was removed.
|
|
62
62
|
*/
|
|
63
63
|
removeOverride(partName) {
|
|
@@ -78,10 +78,9 @@ var ContentTypes = class ContentTypes {
|
|
|
78
78
|
return lines.join("");
|
|
79
79
|
}
|
|
80
80
|
};
|
|
81
|
-
|
|
82
|
-
//#region src/read/opc/part.ts
|
|
81
|
+
// src/read/opc/part.ts
|
|
83
82
|
const textDecoder$1 = new TextDecoder("utf-8");
|
|
84
|
-
const textEncoder$
|
|
83
|
+
const textEncoder$5 = new TextEncoder();
|
|
85
84
|
const XML_DECLARATION_RE = /^\uFEFF?<\?xml[^>]*\?>\s*/;
|
|
86
85
|
/**
|
|
87
86
|
* One OPC part. Holds the original bytes from the zip for its whole life;
|
|
@@ -147,11 +146,10 @@ var Part = class {
|
|
|
147
146
|
if (!this.#dirty) return this.#bytes;
|
|
148
147
|
let xml = serializeXml(this.dom);
|
|
149
148
|
if (!xml.startsWith("<?xml")) xml = (XML_DECLARATION_RE.exec(textDecoder$1.decode(this.#bytes))?.[0] ?? "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n") + xml;
|
|
150
|
-
return textEncoder$
|
|
149
|
+
return textEncoder$5.encode(xml);
|
|
151
150
|
}
|
|
152
151
|
};
|
|
153
|
-
|
|
154
|
-
//#region src/read/opc/relationships.ts
|
|
152
|
+
// src/read/opc/relationships.ts
|
|
155
153
|
/**
|
|
156
154
|
* Overlay over one `.rels` part, keyed by relationship id. While clean, the
|
|
157
155
|
* `.rels` bytes pass through verbatim on save (byte-identical); once `add()`
|
|
@@ -302,17 +300,16 @@ var Relationships = class Relationships {
|
|
|
302
300
|
return lines.join("");
|
|
303
301
|
}
|
|
304
302
|
};
|
|
305
|
-
|
|
306
|
-
//#region src/read/opc/package.ts
|
|
303
|
+
// src/read/opc/package.ts
|
|
307
304
|
const CONTENT_TYPES_ZIP_PATH = "[Content_Types].xml";
|
|
308
305
|
const textDecoder = new TextDecoder("utf-8");
|
|
309
|
-
const textEncoder$
|
|
306
|
+
const textEncoder$4 = new TextEncoder();
|
|
310
307
|
/**
|
|
311
308
|
* An OPC package loaded from `.pptx` bytes.
|
|
312
309
|
*
|
|
313
310
|
* Fidelity contract: parts that are never mutated (`Part.markDirty()`) are
|
|
314
311
|
* written back byte-identically by `save()`. Whole-zip byte-identity is not
|
|
315
|
-
* promised
|
|
312
|
+
* promised (zip metadata and compression may differ), but part bodies, the
|
|
316
313
|
* part-name set, and part order are preserved.
|
|
317
314
|
*/
|
|
318
315
|
var OpcPackage = class OpcPackage {
|
|
@@ -407,7 +404,7 @@ var OpcPackage = class OpcPackage {
|
|
|
407
404
|
* later `save()` neither re-emits it nor flushes a stale `.rels` for it. A relationship set
|
|
408
405
|
* belongs to its part: one left behind would come back as the relationships of the next part
|
|
409
406
|
* added under the same name. Low-level: it does not touch references *to* this part
|
|
410
|
-
* (dangling rels are the caller's concern) nor cascade to parts this one referenced
|
|
407
|
+
* (dangling rels are the caller's concern) nor cascade to parts this one referenced; see
|
|
411
408
|
* {@link Presentation.removeSlide} for the slide-aware variant that unwires the presentation
|
|
412
409
|
* and prunes orphans.
|
|
413
410
|
*/
|
|
@@ -481,21 +478,20 @@ var OpcPackage = class OpcPackage {
|
|
|
481
478
|
for (const relationships of this.#relationshipsCache.values()) {
|
|
482
479
|
if (!relationships.isDirty) continue;
|
|
483
480
|
const relsPartName = relsPartNameFor(relationships.sourcePartName);
|
|
484
|
-
const bytes = textEncoder$
|
|
481
|
+
const bytes = textEncoder$4.encode(relationships.serialize());
|
|
485
482
|
this.#parts.set(relsPartName, new Part(relsPartName, RELATIONSHIPS_CONTENT_TYPE, bytes));
|
|
486
483
|
this.#partsByContentType = null;
|
|
487
484
|
}
|
|
488
485
|
}
|
|
489
486
|
};
|
|
490
|
-
|
|
491
|
-
//#region src/read/api/slide-background.ts
|
|
487
|
+
// src/read/api/slide-background.ts
|
|
492
488
|
/**
|
|
493
489
|
* Read a slide's effective background (`p:cSld/p:bg`), decoded into a typed union.
|
|
494
490
|
*
|
|
495
491
|
* A background lives on the slide itself, or is inherited from its slideLayout,
|
|
496
|
-
* else its slideMaster
|
|
492
|
+
* else its slideMaster; the {@link SlideBackground.source} field records which.
|
|
497
493
|
* The writer authors solid-colour, gradient, pattern and image backgrounds (all FAITHFUL);
|
|
498
|
-
* theme-indexed (`p:bgRef`) backgrounds are read-only for imported decks
|
|
494
|
+
* theme-indexed (`p:bgRef`) backgrounds are read-only for imported decks. Surface
|
|
499
495
|
* them so a codegen pass can carry the part rather than fake it. A
|
|
500
496
|
* `themeRef` additionally resolves its `idx` through the slide theme's `fmtScheme`
|
|
501
497
|
* to a concrete {@link BackgroundFill} in `resolvedFill`, so a consumer can ask what
|
|
@@ -538,13 +534,13 @@ function decodeBackgroundFill(container, ctx, rels) {
|
|
|
538
534
|
/**
|
|
539
535
|
* Resolve a `p:bgRef`'s `idx` into the concrete {@link BackgroundFill} it renders
|
|
540
536
|
* as, through the slide theme's `fmtScheme`. A `p:bgRef` is a
|
|
541
|
-
* `CT_StyleMatrixReference`
|
|
537
|
+
* `CT_StyleMatrixReference` (the same shape as a shape's `a:fillRef`), so
|
|
542
538
|
* {@link styleRefFill} builds the indexed `bgFillStyleLst`/`fillStyleLst` entry with
|
|
543
539
|
* its `phClr` substituted by the bgRef's colour, and the resulting fill element is
|
|
544
540
|
* decoded like a `p:bgPr`. `null` when the theme has no `fmtScheme`, the entry is
|
|
545
541
|
* absent, or the colour cannot be resolved.
|
|
546
542
|
*
|
|
547
|
-
* `themeRels
|
|
543
|
+
* `themeRels`, not the owning slide/layout/master's rels, is what an `a:blipFill`
|
|
548
544
|
* entry's `r:embed` resolves against: the fill element is lifted out of the *theme*
|
|
549
545
|
* part, so its relationship id is scoped to that part. Several stock Office themes
|
|
550
546
|
* (Ion, Facet, …) put a picture in the third `bgFillStyleLst` slot, so a deck using
|
|
@@ -585,14 +581,13 @@ function readSlideBackground(bg, source, ctx, rels, themeRels) {
|
|
|
585
581
|
source
|
|
586
582
|
};
|
|
587
583
|
}
|
|
588
|
-
|
|
589
|
-
//#region src/read/api/ops/part-index.ts
|
|
584
|
+
// src/read/api/ops/part-index.ts
|
|
590
585
|
/**
|
|
591
586
|
* Walking a deck's master/layout spine by partname.
|
|
592
587
|
*
|
|
593
588
|
* The `p:sldMasterIdLst` / `p:sldLayoutIdLst` entries hold relationship ids, not partnames, so
|
|
594
589
|
* every traversal has to resolve through the owning part's relationships. These are the
|
|
595
|
-
* source-side reads that do it
|
|
590
|
+
* source-side reads that do it, used against a *foreign* presentation as often as against this
|
|
596
591
|
* one, which is why they take the deck rather than living on it.
|
|
597
592
|
*/
|
|
598
593
|
/**
|
|
@@ -632,8 +627,7 @@ function idListTargets(lst, qname, resolve) {
|
|
|
632
627
|
}
|
|
633
628
|
return out;
|
|
634
629
|
}
|
|
635
|
-
|
|
636
|
-
//#region src/read/api/chrome.ts
|
|
630
|
+
// src/read/api/chrome.ts
|
|
637
631
|
/** Read `<a:latin>`/`<a:ea>`/`<a:cs>` typefaces of a theme font family; empty string ⇒ `null`. */
|
|
638
632
|
function readThemeFontFace(font) {
|
|
639
633
|
const face = (child) => {
|
|
@@ -727,7 +721,7 @@ function placeholderShapes(spTree) {
|
|
|
727
721
|
return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
|
|
728
722
|
}
|
|
729
723
|
/**
|
|
730
|
-
* One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout
|
|
724
|
+
* One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout:
|
|
731
725
|
* the definition a slide's placeholder of the same `type`/`idx` inherits geometry
|
|
732
726
|
* and text style from. Geometry getters read the placeholder's *own* `a:xfrm`,
|
|
733
727
|
* which a master/layout placeholder carries explicitly (so it round-trips, unlike a
|
|
@@ -753,7 +747,7 @@ var Placeholder = class {
|
|
|
753
747
|
* `xfrm()` + `emuFrom`; and,
|
|
754
748
|
* the one that showed, the text frame was built with **no** inheritance context, so
|
|
755
749
|
* `Run.resolvedSizePt`, `resolvedFontFace`, `resolvedColor` and `resolvedBold` resolved
|
|
756
|
-
* through `SlideMaster.shapes` and came back `null` through `SlideMaster.placeholders
|
|
750
|
+
* through `SlideMaster.shapes` and came back `null` through `SlideMaster.placeholders`:
|
|
757
751
|
* two views of one element disagreeing about the same run.
|
|
758
752
|
*/
|
|
759
753
|
shape;
|
|
@@ -821,7 +815,7 @@ var Placeholder = class {
|
|
|
821
815
|
}
|
|
822
816
|
};
|
|
823
817
|
/**
|
|
824
|
-
* The shared body of a slide master and a slide layout
|
|
818
|
+
* The shared body of a slide master and a slide layout, the two template tiers a slide
|
|
825
819
|
* inherits from.
|
|
826
820
|
*
|
|
827
821
|
* Both are an OPC part holding a `p:cSld` with a shape tree and a resolvable theme context, and
|
|
@@ -839,7 +833,7 @@ var Placeholder = class {
|
|
|
839
833
|
*
|
|
840
834
|
* `Slide` deliberately does not extend this. It is a shape host too, but it is constructed from
|
|
841
835
|
* a `Presentation` rather than an `OpcPackage`, reaches its relationships through that, and
|
|
842
|
-
* reports an unnamed slide as `null` where a template tier reports `''
|
|
836
|
+
* reports an unnamed slide as `null` where a template tier reports `''`. That is three differences in
|
|
843
837
|
* the same handful of members, which is more contortion than the sharing is worth.
|
|
844
838
|
*/
|
|
845
839
|
var TemplatePart = class {
|
|
@@ -864,7 +858,7 @@ var TemplatePart = class {
|
|
|
864
858
|
return (cSld && attr(cSld, "name")) ?? "";
|
|
865
859
|
}
|
|
866
860
|
/**
|
|
867
|
-
* Every shape in this tier's `p:cSld/p:spTree`, in document order
|
|
861
|
+
* Every shape in this tier's `p:cSld/p:spTree`, in document order: the same `AnyShape`
|
|
868
862
|
* union `Slide.shapes` returns, so shape-walking code applies unchanged.
|
|
869
863
|
*
|
|
870
864
|
* {@link placeholders} is the filtered view of the same tree.
|
|
@@ -934,7 +928,7 @@ var SlideMaster = class extends TemplatePart {
|
|
|
934
928
|
}
|
|
935
929
|
/**
|
|
936
930
|
* The master's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
937
|
-
* none. Scoped to the master's own element
|
|
931
|
+
* none. Scoped to the master's own element. A slide's *effective* background,
|
|
938
932
|
* resolved through the slide → layout → master chain, is `Slide.background`.
|
|
939
933
|
*/
|
|
940
934
|
get background() {
|
|
@@ -943,21 +937,21 @@ var SlideMaster = class extends TemplatePart {
|
|
|
943
937
|
const themePart = this.relTarget(THEME_REL);
|
|
944
938
|
return readSlideBackground(bg, "master", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
945
939
|
}
|
|
946
|
-
/** A master's context is its own `p:clrMap` plus its theme
|
|
940
|
+
/** A master's context is its own `p:clrMap` plus its theme; no walk beyond this part. */
|
|
947
941
|
resolveThemeContext() {
|
|
948
942
|
return resolveMasterColorContext(this.opc, this.partName);
|
|
949
943
|
}
|
|
950
944
|
};
|
|
951
945
|
/**
|
|
952
|
-
* A deck's slide layout (`slideLayoutN.xml`) as a modeled object
|
|
946
|
+
* A deck's slide layout (`slideLayoutN.xml`) as a modeled object: the gallery entry
|
|
953
947
|
* a slide binds to. Exposes its {@link name}, its {@link type} (import-only; the
|
|
954
948
|
* writer authors none), its {@link master} (and, through it, the {@link theme}), its
|
|
955
949
|
* `shapes` and the `placeholders` subset of them, and its own {@link background}.
|
|
956
950
|
* Reachable from `Slide.layout` and {@link SlideMaster.layouts}.
|
|
957
951
|
*
|
|
958
952
|
* The write API authors non-placeholder shapes into a layout: every
|
|
959
|
-
* `defineSlideMaster({ objects })` member that is not a `placeholder`
|
|
960
|
-
* `line`, an `image`, a `chart`, a `text` box
|
|
953
|
+
* `defineSlideMaster({ objects })` member that is not a `placeholder` (a `rect`, a
|
|
954
|
+
* `line`, an `image`, a `chart`, a `text` box) lands in this tier's tree.
|
|
961
955
|
*/
|
|
962
956
|
var SlideLayout = class extends TemplatePart {
|
|
963
957
|
/**
|
|
@@ -972,7 +966,7 @@ var SlideLayout = class extends TemplatePart {
|
|
|
972
966
|
/**
|
|
973
967
|
* Whether this layout draws the master's non-placeholder shapes
|
|
974
968
|
* (`p:sldLayout/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
|
|
975
|
-
* attribute means shown
|
|
969
|
+
* attribute means shown. This is the layout-tier counterpart of `Slide.showMasterSp`,
|
|
976
970
|
* and the one PowerPoint actually writes on a section-divider or full-bleed
|
|
977
971
|
* layout. A renderer resolving what to paint under a slide's own shapes has to
|
|
978
972
|
* consult both: the slide's flag, then its layout's.
|
|
@@ -992,7 +986,7 @@ var SlideLayout = class extends TemplatePart {
|
|
|
992
986
|
}
|
|
993
987
|
/**
|
|
994
988
|
* The layout's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
995
|
-
* none. A layout usually authors one
|
|
989
|
+
* none. A layout usually authors one: an explicit fill (`p:bgPr`) or a
|
|
996
990
|
* theme-indexed reference (`p:bgRef`). The slide's *effective* background is
|
|
997
991
|
* `Slide.background`.
|
|
998
992
|
*/
|
|
@@ -1007,8 +1001,7 @@ var SlideLayout = class extends TemplatePart {
|
|
|
1007
1001
|
return resolveLayoutColorContext(this.opc, this.partName);
|
|
1008
1002
|
}
|
|
1009
1003
|
};
|
|
1010
|
-
|
|
1011
|
-
//#region src/read/api/notes.ts
|
|
1004
|
+
// src/read/api/notes.ts
|
|
1012
1005
|
/**
|
|
1013
1006
|
* One placeholder shape (`p:sp`) of a notes slide. A notes slide holds a fixed set:
|
|
1014
1007
|
* the slide thumbnail (`type="sldImg"`), the notes body (`type="body"`), and the
|
|
@@ -1016,8 +1009,8 @@ var SlideLayout = class extends TemplatePart {
|
|
|
1016
1009
|
* `a:xfrm` and are `null` when it inherits geometry from the notesMaster (the case
|
|
1017
1010
|
* on an authored deck, whose `sldImg`/`sldNum` carry an empty `p:spPr`).
|
|
1018
1011
|
*
|
|
1019
|
-
* A notes placeholder IS a {@link Placeholder}
|
|
1020
|
-
* the same twelve members read off the same `p:sp`, down to `p:ph` being found the same way
|
|
1012
|
+
* A notes placeholder IS a {@link Placeholder}. Identity, geometry and the escape hatch are
|
|
1013
|
+
* the same twelve members read off the same `p:sp`, down to `p:ph` being found the same way,
|
|
1021
1014
|
* so it extends one rather than repeating it. Two things are its own: the flattened
|
|
1022
1015
|
* {@link text} convenience, and a {@link textFrame} that threads the notesMaster inheritance
|
|
1023
1016
|
* context onto the body frame.
|
|
@@ -1029,7 +1022,7 @@ var NotesPlaceholder = class extends Placeholder {
|
|
|
1029
1022
|
* runs round-trip a slide's authored notes; the `sldNum` placeholder's frame
|
|
1030
1023
|
* holds the slide-number `a:fld`, whose value surfaces through `TextFrame.text`.
|
|
1031
1024
|
*
|
|
1032
|
-
* Overrides {@link Placeholder.textFrame} for the inheritance context alone
|
|
1025
|
+
* Overrides {@link Placeholder.textFrame} for the inheritance context alone (see below).
|
|
1033
1026
|
*/
|
|
1034
1027
|
get textFrame() {
|
|
1035
1028
|
const txBody = firstChild(this.sp, "p:txBody");
|
|
@@ -1055,7 +1048,7 @@ var NotesPlaceholder = class extends Placeholder {
|
|
|
1055
1048
|
}
|
|
1056
1049
|
};
|
|
1057
1050
|
/**
|
|
1058
|
-
* A slide's speaker-notes slide (`notesSlideN.xml`) as a modeled object
|
|
1051
|
+
* A slide's speaker-notes slide (`notesSlideN.xml`) as a modeled object: the deep
|
|
1059
1052
|
* companion to {@link Slide.notesText}. Exposes the notes
|
|
1060
1053
|
* shape tree as {@link NotesPlaceholder}s ({@link body}/{@link slideImage}/
|
|
1061
1054
|
* {@link slideNumber}) plus the body {@link textFrame} the older getters flatten.
|
|
@@ -1075,7 +1068,7 @@ var NotesSlide = class {
|
|
|
1075
1068
|
get partName() {
|
|
1076
1069
|
return this.part.partName;
|
|
1077
1070
|
}
|
|
1078
|
-
/** The notes part's own relationships
|
|
1071
|
+
/** The notes part's own relationships; a notes hyperlink resolves through these. */
|
|
1079
1072
|
get relationships() {
|
|
1080
1073
|
return this.opc.relationshipsFor(this.partName);
|
|
1081
1074
|
}
|
|
@@ -1083,7 +1076,7 @@ var NotesSlide = class {
|
|
|
1083
1076
|
* The notes shape tree as full shapes, in document order.
|
|
1084
1077
|
*
|
|
1085
1078
|
* A notes slide holds nothing but its three placeholders, so {@link placeholders} is the
|
|
1086
|
-
* view to read. This one exists because a notes slide is a {@link ShapeHost}
|
|
1079
|
+
* view to read. This one exists because a notes slide is a {@link ShapeHost}, which is
|
|
1087
1080
|
* what lets a {@link NotesPlaceholder} be the same {@link AutoShape} view of its `p:sp`
|
|
1088
1081
|
* that a master or layout placeholder is, rather than a second reading of one.
|
|
1089
1082
|
*/
|
|
@@ -1135,8 +1128,7 @@ var NotesSlide = class {
|
|
|
1135
1128
|
return spTreeOf(this.part.dom.documentElement);
|
|
1136
1129
|
}
|
|
1137
1130
|
};
|
|
1138
|
-
|
|
1139
|
-
//#region src/read/api/ops/deck-target.ts
|
|
1131
|
+
// src/read/api/ops/deck-target.ts
|
|
1140
1132
|
/**
|
|
1141
1133
|
* `ppt/_rels/presentation.xml.rels` -- the deck's own relationship part.
|
|
1142
1134
|
*
|
|
@@ -1151,8 +1143,7 @@ var NotesSlide = class {
|
|
|
1151
1143
|
function presentationRels(dest) {
|
|
1152
1144
|
return dest.opc.relationshipsFor(dest.presentationPart.partName);
|
|
1153
1145
|
}
|
|
1154
|
-
|
|
1155
|
-
//#region src/read/api/ops/master-registry.ts
|
|
1146
|
+
// src/read/api/ops/master-registry.ts
|
|
1156
1147
|
/**
|
|
1157
1148
|
* Slide-master bookkeeping in `presentation.xml` and in a master's own
|
|
1158
1149
|
* `p:sldLayoutIdLst`: registering a master so renderers treat it as active,
|
|
@@ -1189,7 +1180,7 @@ function registerMaster(dest, masterPartName) {
|
|
|
1189
1180
|
/**
|
|
1190
1181
|
* Move the `p:sldMasterId` entries for `masterPartNames` to the front of
|
|
1191
1182
|
* `p:sldMasterIdLst`, preserving their existing relative order, so the first of
|
|
1192
|
-
* them becomes the deck's `Designs(1)
|
|
1183
|
+
* them becomes the deck's `Designs(1)`, the theme PowerPoint's Design tab shows.
|
|
1193
1184
|
* Reorders the id list only; relationships, ids, and all other parts are left as
|
|
1194
1185
|
* they are. A no-op when the named masters already lead (idempotent re-call).
|
|
1195
1186
|
*/
|
|
@@ -1219,7 +1210,7 @@ function promoteMasters(dest, masterPartNames) {
|
|
|
1219
1210
|
* unique across the WHOLE presentation, not just within their own list. A duplicate
|
|
1220
1211
|
* anywhere in this space makes PowerPoint report the file as corrupt on open
|
|
1221
1212
|
* ("found a problem with content" / 0x80070570), even though LibreOffice silently
|
|
1222
|
-
* tolerates it
|
|
1213
|
+
* tolerates it. So an imported master AND each of its layouts must take an id past
|
|
1223
1214
|
* the max of both the master-id list and every layout-id list. Recomputed per
|
|
1224
1215
|
* allocation so ids appended earlier in the same import are counted.
|
|
1225
1216
|
*
|
|
@@ -1275,20 +1266,19 @@ function addLayoutToMaster(dest, masterPartName, layoutPartName) {
|
|
|
1275
1266
|
lst.appendChild(entry);
|
|
1276
1267
|
masterPart.markDirty();
|
|
1277
1268
|
}
|
|
1278
|
-
|
|
1279
|
-
//#region src/read/api/ops/copy-traversal.ts
|
|
1269
|
+
// src/read/api/ops/copy-traversal.ts
|
|
1280
1270
|
/**
|
|
1281
1271
|
* What a copy traversal does with one relationship: skip it, carry it as an external link, or
|
|
1282
1272
|
* follow it into the part it names.
|
|
1283
1273
|
*
|
|
1284
1274
|
* Notes pull in a notesMaster and its own theme, which an imported page does not need, so the
|
|
1285
|
-
* notes relationship is never followed
|
|
1275
|
+
* notes relationship is never followed; `carryNotes` picks it up afterwards for the pages
|
|
1286
1276
|
* that asked. A master's relationships to its layouts are skipped so the copy stays lean:
|
|
1287
1277
|
* copied layouts re-link themselves, and skipping the list is also what keeps one edited
|
|
1288
1278
|
* layout from disqualifying every other layout that shares its master.
|
|
1289
1279
|
*
|
|
1290
|
-
* Each caller still owns its own extra arms
|
|
1291
|
-
* check's byte comparison
|
|
1280
|
+
* Each caller still owns its own extra arms (a copy's overrides and its jump-link rule, the reuse
|
|
1281
|
+
* check's byte comparison), which is why this answers only the part they share.
|
|
1292
1282
|
* @param part - the part the relationship belongs to
|
|
1293
1283
|
* @param rel - the relationship being considered
|
|
1294
1284
|
*/
|
|
@@ -1297,13 +1287,12 @@ function copyTraversalStep(part, rel) {
|
|
|
1297
1287
|
if (part.contentType === "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml" && rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") return "skip";
|
|
1298
1288
|
return rel.targetMode === "External" ? "external" : "recurse";
|
|
1299
1289
|
}
|
|
1300
|
-
|
|
1301
|
-
//#region src/read/api/ops/page-owned.ts
|
|
1290
|
+
// src/read/api/ops/page-owned.ts
|
|
1302
1291
|
/**
|
|
1303
1292
|
* What a page copy may share with the page it was copied from, and what it must own.
|
|
1304
1293
|
*
|
|
1305
|
-
* Duplicating a page
|
|
1306
|
-
* `importSlides` naming it twice
|
|
1294
|
+
* Duplicating a page (`cloneSlide`, `importSlide` of the same page twice,
|
|
1295
|
+
* `importSlides` naming it twice) copies the page part and then has to decide,
|
|
1307
1296
|
* for every part hanging off it, whether the copy points at the same part or gets
|
|
1308
1297
|
* one of its own. Sharing is the cheap answer and is right for most of the graph:
|
|
1309
1298
|
* a layout, a master, a theme, an image are deck-wide assets PowerPoint itself
|
|
@@ -1312,7 +1301,7 @@ function copyTraversalStep(part, rel) {
|
|
|
1312
1301
|
* It is not right for the parts a page *owns*. A chart and its embedded workbook,
|
|
1313
1302
|
* a SmartArt diagram's five parts, an OLE embedding, a notes slide: PowerPoint
|
|
1314
1303
|
* treats each as belonging to exactly one page, and a package where two slides
|
|
1315
|
-
* resolve to one of them is one it refuses to open at all
|
|
1304
|
+
* resolve to one of them is one it refuses to open at all: the "the file or
|
|
1316
1305
|
* directory is corrupted and unreadable" (`0x80070570`) dialog, with the whole
|
|
1317
1306
|
* deck rejected rather than the duplicate page. The schema validator accepts such
|
|
1318
1307
|
* a package: nothing in ECMA-376 says a chart part has one referrer, so this rule
|
|
@@ -1342,14 +1331,13 @@ function copyTraversalStep(part, rel) {
|
|
|
1342
1331
|
const SHARED_BY_PAGE_COPIES = new Set(SHARED_PARTS.map((kind) => kind.relType));
|
|
1343
1332
|
/**
|
|
1344
1333
|
* Whether a page copy may point at this relationship's target rather than taking
|
|
1345
|
-
* a copy of it. Everything not listed is owned by the page and copied
|
|
1334
|
+
* a copy of it. Everything not listed is owned by the page and copied; see the
|
|
1346
1335
|
* module comment for why that is the safe default.
|
|
1347
1336
|
*/
|
|
1348
1337
|
function isSharedByPageCopies(relType) {
|
|
1349
1338
|
return SHARED_BY_PAGE_COPIES.has(relType);
|
|
1350
1339
|
}
|
|
1351
|
-
|
|
1352
|
-
//#region src/read/api/ops/part-reuse.ts
|
|
1340
|
+
// src/read/api/ops/part-reuse.ts
|
|
1353
1341
|
/**
|
|
1354
1342
|
* Whether the destination already holds `sourcePartName`'s subgraph, part for part and
|
|
1355
1343
|
* byte for byte, in a state an imported slide can bind to.
|
|
@@ -1430,8 +1418,7 @@ function bytesEqual(a, b) {
|
|
|
1430
1418
|
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1431
1419
|
return true;
|
|
1432
1420
|
}
|
|
1433
|
-
|
|
1434
|
-
//#region src/read/api/ops/part-copy.ts
|
|
1421
|
+
// src/read/api/ops/part-copy.ts
|
|
1435
1422
|
/** Open an ownership scope for one page copy. */
|
|
1436
1423
|
function newOwnedScope() {
|
|
1437
1424
|
return /* @__PURE__ */ new Map();
|
|
@@ -1445,7 +1432,7 @@ function copyTarget(ctx) {
|
|
|
1445
1432
|
* it would throw, found before anything in the destination moves.
|
|
1446
1433
|
*
|
|
1447
1434
|
* This is what lets a refused import leave the deck byte-identical. The copy changes the deck as it
|
|
1448
|
-
* goes
|
|
1435
|
+
* goes (parts added, a master registered in `presentation.xml`, a layout linked into its master),
|
|
1449
1436
|
* so a failure it met halfway would leave all of that behind. An import therefore runs the same
|
|
1450
1437
|
* traversal twice: first with a plan in its context, which throws what the copy would and writes
|
|
1451
1438
|
* nothing, then for real. The dry run used to be a separate walk of the source, transcribed from the
|
|
@@ -1456,8 +1443,8 @@ function copyTarget(ctx) {
|
|
|
1456
1443
|
* A plan stands in for the destination package as a {@link CopyTarget}. It hands out the partnames
|
|
1457
1444
|
* the copy will be given, keeps its own copy of each source's registry so a part it planned is found
|
|
1458
1445
|
* again as the copy would find it, and remembers the notes master it would install in a deck that has
|
|
1459
|
-
* none. Destination bookkeeping that adds no part
|
|
1460
|
-
* is not recorded.
|
|
1446
|
+
* none. Destination bookkeeping that adds no part, such as registering a master or linking a layout
|
|
1447
|
+
* into it, is not recorded.
|
|
1461
1448
|
*/
|
|
1462
1449
|
var CopyPlan = class {
|
|
1463
1450
|
api;
|
|
@@ -1551,7 +1538,7 @@ var CopyPlan = class {
|
|
|
1551
1538
|
* registry idempotence is right for a theme, master, layout or image, where a
|
|
1552
1539
|
* second copy is waste; applied to the slide it made a repeated import of the
|
|
1553
1540
|
* same source page return the first copy's partname, and the caller then wired a
|
|
1554
|
-
* second `p:sldId` to a part that already had one
|
|
1541
|
+
* second `p:sldId` to a part that already had one: a package PowerPoint refuses
|
|
1555
1542
|
* to open (0x80070570), with nothing in the read model to show for it. So the
|
|
1556
1543
|
* page goes into the selection plan `copyPart` already honours for a batch, which
|
|
1557
1544
|
* re-materializes exactly the named pages and leaves the dedup of their
|
|
@@ -1574,7 +1561,7 @@ function copySlidePart(ctx, sourcePartName) {
|
|
|
1574
1561
|
* per source package via the copy registry. Relationship ids are preserved so
|
|
1575
1562
|
* the copied part body's `r:id`/`r:embed` references stay valid; targets are
|
|
1576
1563
|
* rewritten to the freshly-allocated partnames. Notes relationships are
|
|
1577
|
-
* dropped. A copied `slideMaster` does not drag in all its sibling layouts
|
|
1564
|
+
* dropped. A copied `slideMaster` does not drag in all its sibling layouts;
|
|
1578
1565
|
* each imported `slideLayout` wires itself into the master instead (see
|
|
1579
1566
|
* {@link linkLayoutIntoMaster}).
|
|
1580
1567
|
*
|
|
@@ -1584,13 +1571,13 @@ function copySlidePart(ctx, sourcePartName) {
|
|
|
1584
1571
|
* a `slide → slide` link may not leave the selection is enforced as each link is
|
|
1585
1572
|
* followed (see {@link rebuildRels}). Imports run this traversal as a
|
|
1586
1573
|
* {@link CopyPlan} first, which throws that and every other failure the copy
|
|
1587
|
-
* could meet
|
|
1574
|
+
* could meet, so once copying starts there is nothing left to throw, and a
|
|
1588
1575
|
* rejected batch never leaves a half-copied deck behind.
|
|
1589
1576
|
*
|
|
1590
1577
|
* Idempotence stops at the page's own parts. Reaching a page opens an
|
|
1591
1578
|
* {@link OwnedScope}, and everything under it that {@link isSharedByPageCopies}
|
|
1592
|
-
* does not clear for sharing
|
|
1593
|
-
* its own subtree
|
|
1579
|
+
* does not clear for sharing (a chart, a diagram, an OLE embedding, each with
|
|
1580
|
+
* its own subtree) is copied into that scope instead of the registry, so the
|
|
1594
1581
|
* next copy of the page gets parts of its own. Pass `owned` to open the scope
|
|
1595
1582
|
* from outside, as the rebinding import paths do for the page they build
|
|
1596
1583
|
* themselves.
|
|
@@ -1698,7 +1685,7 @@ function rebuildRels(ctx, options) {
|
|
|
1698
1685
|
* The page's shared targets are carried as the source wrote them. Each owned one is repointed at a
|
|
1699
1686
|
* fresh copy, and the subtree under it is copied the same way, so a chart's workbook and
|
|
1700
1687
|
* user-shapes drawing come along while the image inside that drawing stays shared. A relationship
|
|
1701
|
-
* *back* to the source page
|
|
1688
|
+
* *back* to the source page (a notes slide names the slide it annotates) is repointed at the
|
|
1702
1689
|
* clone, which is what makes the copied notes belong to it.
|
|
1703
1690
|
*
|
|
1704
1691
|
* Unlike an import, a clone keeps its page's notes, and nothing here consults the copy registry or
|
|
@@ -1773,19 +1760,18 @@ function linkLayoutIntoMaster(ctx, layoutSourceRels, layoutPartName) {
|
|
|
1773
1760
|
if (!masterPartName) return;
|
|
1774
1761
|
addLayoutToMaster(ctx.dest, masterPartName, layoutPartName);
|
|
1775
1762
|
}
|
|
1776
|
-
|
|
1777
|
-
//#region src/read/api/ops/notes-master.ts
|
|
1763
|
+
// src/read/api/ops/notes-master.ts
|
|
1778
1764
|
/**
|
|
1779
1765
|
* Carrying speaker notes and the deck's single notes master across an import or an append.
|
|
1780
1766
|
*
|
|
1781
1767
|
* A `notesSlide` is not self-contained: it back-references the slide it annotates and forward-
|
|
1782
1768
|
* references the deck's notes master. `CT_NotesMasterIdList` allows at most one master per
|
|
1783
|
-
* presentation, so a deck that already has one keeps it
|
|
1769
|
+
* presentation, so a deck that already has one keeps it (the destination's notes styling wins),
|
|
1784
1770
|
* and a source master is copied only into a deck that has none. That single rule is why both the
|
|
1785
1771
|
* `Presentation`-to-`Presentation` path and the generator-XML path funnel through
|
|
1786
1772
|
* `registerNotesMaster`: they differ only in how the *part* comes into being.
|
|
1787
1773
|
*/
|
|
1788
|
-
const textEncoder$
|
|
1774
|
+
const textEncoder$3 = new TextEncoder();
|
|
1789
1775
|
/**
|
|
1790
1776
|
* Carry the source slide's speaker notes onto the just-imported slide (the
|
|
1791
1777
|
* `importNotes` option). The slide copy itself dropped the `notesSlide` rel, so
|
|
@@ -1793,7 +1779,7 @@ const textEncoder$2 = new TextEncoder();
|
|
|
1793
1779
|
* `slide → notesSlide` rel on the new slide, and rebuilds the copied notesSlide's
|
|
1794
1780
|
* own relationships:
|
|
1795
1781
|
*
|
|
1796
|
-
* - its `slide` back-rel is repointed at the new slide (`newSlidePartName`)
|
|
1782
|
+
* - its `slide` back-rel is repointed at the new slide (`newSlidePartName`); the
|
|
1797
1783
|
* source slide is *not* copied (that would be circular);
|
|
1798
1784
|
* - its `notesMaster` rel is resolved through {@link ensureNotesMaster}, which
|
|
1799
1785
|
* reuses this deck's notesMaster when it has one and copies the source's only
|
|
@@ -1842,7 +1828,7 @@ function carryNotes(dest, source, ctx, sourceSlidePartName, newSlidePartName) {
|
|
|
1842
1828
|
*/
|
|
1843
1829
|
function addNotesSlidePart(dest, xml, slidePartName, notesMasterPartName) {
|
|
1844
1830
|
const notesPartName = dest.opc.reservePartNameLike("/ppt/notesSlides/notesSlide1.xml");
|
|
1845
|
-
dest.opc.addPart(notesPartName, NOTES_SLIDE_CONTENT_TYPE, textEncoder$
|
|
1831
|
+
dest.opc.addPart(notesPartName, NOTES_SLIDE_CONTENT_TYPE, textEncoder$3.encode(xml));
|
|
1846
1832
|
const notesRels = dest.opc.relationshipsFor(notesPartName);
|
|
1847
1833
|
if (notesMasterPartName) notesRels.addWithId("rId1", NOTES_MASTER_REL, relativePartName(notesPartName, notesMasterPartName));
|
|
1848
1834
|
notesRels.addWithId("rId2", SLIDE_REL, relativePartName(notesPartName, slidePartName));
|
|
@@ -1880,8 +1866,8 @@ function presentationRoot(dest) {
|
|
|
1880
1866
|
* allows. Returns the partname, so callers can use it as a rel target.
|
|
1881
1867
|
*
|
|
1882
1868
|
* Split out of {@link ensureNotesMaster} because the two ways a notesMaster arrives
|
|
1883
|
-
*
|
|
1884
|
-
* {@link Presentation.appendSlides}
|
|
1869
|
+
* (copied from another `Presentation`, or authored by a generator and injected by
|
|
1870
|
+
* {@link Presentation.appendSlides}) differ only in how the *part* is created, not in how it is
|
|
1885
1871
|
* registered.
|
|
1886
1872
|
*/
|
|
1887
1873
|
function registerNotesMaster(dest, notesMasterPartName) {
|
|
@@ -1925,7 +1911,7 @@ function installNotesMaster(dest, masterBytes, themeBytes, themeContentType = TH
|
|
|
1925
1911
|
* theme its `.rels` requires (the normal write path emits that as `theme2.xml`).
|
|
1926
1912
|
*/
|
|
1927
1913
|
function ensureNotesMasterFromXml(dest, master) {
|
|
1928
|
-
return existingNotesMaster(dest) ?? installNotesMaster(dest, textEncoder$
|
|
1914
|
+
return existingNotesMaster(dest) ?? installNotesMaster(dest, textEncoder$3.encode(master.xml), textEncoder$3.encode(master.themeXml));
|
|
1929
1915
|
}
|
|
1930
1916
|
/**
|
|
1931
1917
|
* Resolve the notesMaster a *newly authored* notes slide should bind to
|
|
@@ -1945,7 +1931,7 @@ function ensureNotesMasterFromXml(dest, master) {
|
|
|
1945
1931
|
* arrangement PowerPoint writes (`theme1.xml` for the master, `theme2.xml` for the
|
|
1946
1932
|
* notes master).
|
|
1947
1933
|
*
|
|
1948
|
-
* Throws when the deck has no theme to clone
|
|
1934
|
+
* Throws when the deck has no theme to clone: a package that malformed cannot be
|
|
1949
1935
|
* given a valid notesMaster, and silently emitting one without a resolvable theme
|
|
1950
1936
|
* is what PowerPoint reports as a repair prompt rather than a bad edit.
|
|
1951
1937
|
* @param {Presentation} dest - the deck being authored onto
|
|
@@ -1958,10 +1944,9 @@ function ensureNotesMasterForAuthoring(dest, slidePartName) {
|
|
|
1958
1944
|
const themePartName = resolveSlideThemeParts(dest.opc, slidePartName).themePartName;
|
|
1959
1945
|
const themePart = themePartName ? dest.opc.part(themePartName) : void 0;
|
|
1960
1946
|
if (!themePart) throw new PackageReadError("package/part-missing", `addNotes: no theme reachable from ${slidePartName} to bind a new notes master to`);
|
|
1961
|
-
return installNotesMaster(dest, textEncoder$
|
|
1947
|
+
return installNotesMaster(dest, textEncoder$3.encode(makeXmlNotesMaster()), themePart.serialize(), themePart.contentType);
|
|
1962
1948
|
}
|
|
1963
|
-
|
|
1964
|
-
//#region src/read/api/ops/notes-author.ts
|
|
1949
|
+
// src/read/api/ops/notes-author.ts
|
|
1965
1950
|
/**
|
|
1966
1951
|
* Authoring speaker notes onto a slide of a *loaded* deck ({@link Slide.addNotes}).
|
|
1967
1952
|
*
|
|
@@ -1974,7 +1959,7 @@ function ensureNotesMasterForAuthoring(dest, slidePartName) {
|
|
|
1974
1959
|
* placeholder frame comes from {@link makeXmlNotesSlideSkeleton}, and the body
|
|
1975
1960
|
* paragraphs carry the same elements and attributes the write path emits for a plain
|
|
1976
1961
|
* `pptx.addSlide().addNotes(text)`. `test/read/add-notes.test.ts` asserts that
|
|
1977
|
-
* equivalence, so the two cannot drift apart unnoticed
|
|
1962
|
+
* equivalence, so the two cannot drift apart unnoticed: the same guarantee
|
|
1978
1963
|
* `src/ooxml/` gives the constants both halves share.
|
|
1979
1964
|
*
|
|
1980
1965
|
* The one difference is empty-element *spelling*: the write path emits `a:rPr` as an
|
|
@@ -2009,7 +1994,7 @@ function notesParagraphsXml(text) {
|
|
|
2009
1994
|
/**
|
|
2010
1995
|
* Replace a notes body's paragraphs with `text`, in the body's own document.
|
|
2011
1996
|
*
|
|
2012
|
-
* The path taken when the slide *already* has a notes part
|
|
1997
|
+
* The path taken when the slide *already* has a notes part: an imported one, or one
|
|
2013
1998
|
* an earlier `addNotes` wrote. Only the `a:p` children are replaced, so the part's
|
|
2014
1999
|
* geometry, its `a:bodyPr`/`a:lstStyle`, and its other two placeholders survive. The
|
|
2015
2000
|
* body's hyperlink relationships are deliberately *not* pruned: a caller replacing
|
|
@@ -2051,7 +2036,7 @@ function notesBodyTxBody(root) {
|
|
|
2051
2036
|
* When the slide already has a notes part, only the body paragraphs are rewritten
|
|
2052
2037
|
* (see {@link replaceNotesParagraphs}). When it has none, the part is built and
|
|
2053
2038
|
* wired: `slide → notesSlide`, and on the new part the two relationships a notes
|
|
2054
|
-
* slide always carries
|
|
2039
|
+
* slide always carries: `notesMaster` as rId1 and a `slide` back-reference as rId2,
|
|
2055
2040
|
* the ids and order the write path reserves.
|
|
2056
2041
|
*
|
|
2057
2042
|
* The notes master is resolved through {@link ensureNotesMasterForAuthoring}: this
|
|
@@ -2081,8 +2066,7 @@ function authorNotes(slide, text) {
|
|
|
2081
2066
|
slide.relationships.add(NOTES_SLIDE_REL, relativePartName(slide.partName, partName));
|
|
2082
2067
|
return partName;
|
|
2083
2068
|
}
|
|
2084
|
-
|
|
2085
|
-
//#region src/read/api/comments.ts
|
|
2069
|
+
// src/read/api/comments.ts
|
|
2086
2070
|
/** The slide → modern-comments-part relationship type (2018 schema). Only this module reads one. */
|
|
2087
2071
|
const MODERN_COMMENTS_REL_TYPE = MS_REL + "2018/10/relationships/comments";
|
|
2088
2072
|
/** Content type of the modern per-slide comments part (`ppt/comments/modernComment_*.xml`). */
|
|
@@ -2203,8 +2187,7 @@ function commentSchema(opc) {
|
|
|
2203
2187
|
if (opc.partsByContentType("application/vnd.openxmlformats-officedocument.presentationml.comments+xml").length > 0) return "legacy";
|
|
2204
2188
|
return "none";
|
|
2205
2189
|
}
|
|
2206
|
-
|
|
2207
|
-
//#region src/read/api/tags.ts
|
|
2190
|
+
// src/read/api/tags.ts
|
|
2208
2191
|
/** The owner → tags-part relationship type. Only this module follows one. */
|
|
2209
2192
|
const TAGS_REL_TYPE = OFFICE_REL + "tags";
|
|
2210
2193
|
/**
|
|
@@ -2227,8 +2210,7 @@ function readTagsForPart(opc, ownerPartName) {
|
|
|
2227
2210
|
}
|
|
2228
2211
|
return out;
|
|
2229
2212
|
}
|
|
2230
|
-
|
|
2231
|
-
//#region src/read/api/transition.ts
|
|
2213
|
+
// src/read/api/transition.ts
|
|
2232
2214
|
/**
|
|
2233
2215
|
* Typed read/edit model for a slide's `p:transition` (slide-show transition).
|
|
2234
2216
|
*
|
|
@@ -2239,7 +2221,7 @@ function readTagsForPart(opc, ownerPartName) {
|
|
|
2239
2221
|
* when the duration matches a coarse `spd` speed bucket; and
|
|
2240
2222
|
* - **wrapped** `<mc:AlternateContent>` carrying a `<mc:Choice Requires="p14">`
|
|
2241
2223
|
* whose `p:transition` adds the precise `p14:dur` (milliseconds), plus a base
|
|
2242
|
-
* `<mc:Fallback>` without it
|
|
2224
|
+
* `<mc:Fallback>` without it. This form is emitted for an off-bucket exact duration.
|
|
2243
2225
|
*
|
|
2244
2226
|
* The getter prefers the `p14` Choice (so `durationMs` is recovered); the setter
|
|
2245
2227
|
* emits the `mc:AlternateContent` form when `durationMs` is set and the bare form
|
|
@@ -2254,7 +2236,6 @@ function typeElement(transition) {
|
|
|
2254
2236
|
}
|
|
2255
2237
|
/** Namespace prefix for a URI, defaulting to `p` for the base presentationml namespace. */
|
|
2256
2238
|
function prefixFor(uri) {
|
|
2257
|
-
if (!uri) return "p";
|
|
2258
2239
|
for (const [prefix, ns] of Object.entries(OOXML_NS)) if (ns === uri) return prefix;
|
|
2259
2240
|
return "p";
|
|
2260
2241
|
}
|
|
@@ -2417,8 +2398,7 @@ function removeTransition(root) {
|
|
|
2417
2398
|
const found = findTransition(root);
|
|
2418
2399
|
if (found) root.removeChild(found.outer);
|
|
2419
2400
|
}
|
|
2420
|
-
|
|
2421
|
-
//#region src/read/api/slide-build.ts
|
|
2401
|
+
// src/read/api/slide-build.ts
|
|
2422
2402
|
/**
|
|
2423
2403
|
* Create `qname` in the parent's document and append it.
|
|
2424
2404
|
*
|
|
@@ -2488,8 +2468,7 @@ function buildPicture(doc, spec) {
|
|
|
2488
2468
|
appendSpPr(pic, spec);
|
|
2489
2469
|
return pic;
|
|
2490
2470
|
}
|
|
2491
|
-
|
|
2492
|
-
//#region src/read/api/slide.ts
|
|
2471
|
+
// src/read/api/slide.ts
|
|
2493
2472
|
/** Recognize a common raster format from its leading bytes, through the shared registry. */
|
|
2494
2473
|
function sniffImageType(bytes) {
|
|
2495
2474
|
const format = imageFormatForBytes(bytes);
|
|
@@ -2546,8 +2525,8 @@ var Slide = class {
|
|
|
2546
2525
|
* `fmtScheme` + `fontScheme`, plus the layout/master roots), walked once from
|
|
2547
2526
|
* slide → layout → master → theme and cached on this proxy. Backs the read-model
|
|
2548
2527
|
* resolved getters ({@link Shape.resolvedFill}, `Run.resolvedColor`,
|
|
2549
|
-
* `Run.resolvedSizePt`, `Run.resolvedFontFace`) so a `schemeClr` token
|
|
2550
|
-
* one delivered through a shape's `p:style` `fillRef`/`lnRef`
|
|
2528
|
+
* `Run.resolvedSizePt`, `Run.resolvedFontFace`) so a `schemeClr` token (including
|
|
2529
|
+
* one delivered through a shape's `p:style` `fillRef`/`lnRef`) resolves to a
|
|
2551
2530
|
* literal hex, and a placeholder run's inherited size/typeface (a `+mj-*`/`+mn-*`
|
|
2552
2531
|
* font token included) resolves to a literal value. The maps/roots are empty when
|
|
2553
2532
|
* the theme chain is incomplete, in which case tokens simply stay unresolved.
|
|
@@ -2597,7 +2576,7 @@ var Slide = class {
|
|
|
2597
2576
|
/**
|
|
2598
2577
|
* Whether this slide draws the master's non-placeholder shapes
|
|
2599
2578
|
* (`p:sld/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
|
|
2600
|
-
* attribute means shown
|
|
2579
|
+
* attribute means shown, the same shape as {@link hidden}.
|
|
2601
2580
|
*
|
|
2602
2581
|
* This is the switch that decides whether {@link SlideMaster.shapes} belongs on
|
|
2603
2582
|
* *this* slide. A slide the author gave a full-bleed image or a section divider
|
|
@@ -2624,7 +2603,7 @@ var Slide = class {
|
|
|
2624
2603
|
* Set or clear the slide's show transition. Assigning `null` removes it.
|
|
2625
2604
|
* Writing a transition with `durationMs` emits the `mc:AlternateContent` form
|
|
2626
2605
|
* (a `p14` Choice carrying `p14:dur` plus a base `mc:Fallback`); otherwise the
|
|
2627
|
-
* bare `<p:transition>` is written. The node is inserted at its schema slot
|
|
2606
|
+
* bare `<p:transition>` is written. The node is inserted at its schema slot,
|
|
2628
2607
|
* after `p:clrMapOvr`, before `p:timing`/`p:extLst`. Marks the slide part dirty.
|
|
2629
2608
|
*
|
|
2630
2609
|
* The new transition is built from the current one before that is removed: it keeps the sound
|
|
@@ -2650,6 +2629,15 @@ var Slide = class {
|
|
|
2650
2629
|
return root ? hasAnimations(root) : false;
|
|
2651
2630
|
}
|
|
2652
2631
|
/**
|
|
2632
|
+
* The slide's build effects: every time node in its `p:timing` with a `presetClass`, in document
|
|
2633
|
+
* order, with the sequence it plays in, its preset, trigger, duration, step delay and target shape
|
|
2634
|
+
* id. `[]` when the slide has no timing. Read-only: see {@link BuildEffect}.
|
|
2635
|
+
*/
|
|
2636
|
+
get animations() {
|
|
2637
|
+
const root = this.part.dom.documentElement;
|
|
2638
|
+
return root ? readBuildEffects(root) : [];
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2653
2641
|
* @internal The sorted, de-duplicated set of shape ids (`spid`) referenced by
|
|
2654
2642
|
* the slide's animations (`<p:spTgt spid>` and `<p:bldP spid>`). Exposed for the
|
|
2655
2643
|
* import paths' spid-coherence checks.
|
|
@@ -2679,14 +2667,14 @@ var Slide = class {
|
|
|
2679
2667
|
/**
|
|
2680
2668
|
* Flatten the slide's build animations: remove the `<p:timing>` block so the
|
|
2681
2669
|
* slide renders and edits as its final static state, with every shape shown at
|
|
2682
|
-
* once. Gated like {@link hasAnimations}
|
|
2670
|
+
* once. Gated like {@link hasAnimations}: a `<p:timing>` that is purely a media
|
|
2683
2671
|
* loop (no `<p:bldP>` or `presetID`) is preserved so media playback survives.
|
|
2684
2672
|
* Marks the slide part dirty and returns `true` only when a timing block was
|
|
2685
2673
|
* removed.
|
|
2686
2674
|
*
|
|
2687
2675
|
* This removes click-through staging only; it does not delete shapes. If a slide
|
|
2688
2676
|
* animated alternating states over the same region, the flattened render shows
|
|
2689
|
-
* them all at once
|
|
2677
|
+
* them all at once. Keep that distinct from removing staged/duplicate shapes.
|
|
2690
2678
|
*/
|
|
2691
2679
|
flattenAnimations() {
|
|
2692
2680
|
const root = this.part.dom.documentElement;
|
|
@@ -2702,7 +2690,7 @@ var Slide = class {
|
|
|
2702
2690
|
return spTree ? buildShapes(spTree, this) : [];
|
|
2703
2691
|
}
|
|
2704
2692
|
/**
|
|
2705
|
-
* All text on the slide, flattened in document order
|
|
2693
|
+
* All text on the slide, flattened in document order: the read-model
|
|
2706
2694
|
* counterpart to {@link TextFrame.text} one level up. Every text-bearing shape
|
|
2707
2695
|
* contributes its text, recursing into groups ({@link GroupShape}) and reading
|
|
2708
2696
|
* table cells ({@link GraphicFrame.table}) and SmartArt node text
|
|
@@ -2745,12 +2733,12 @@ var Slide = class {
|
|
|
2745
2733
|
return blocks.join("\n");
|
|
2746
2734
|
}
|
|
2747
2735
|
/**
|
|
2748
|
-
* The slide's speaker-note text
|
|
2736
|
+
* The slide's speaker-note text, the companion to {@link text} for the notes
|
|
2749
2737
|
* that {@link text} deliberately excludes. Delegates to {@link notesSlide}: finds
|
|
2750
2738
|
* the notes body placeholder (`p:ph` `type="body"`) and flattens its text frame
|
|
2751
2739
|
* the same way {@link TextFrame.text} does (paragraphs joined by `\n`).
|
|
2752
2740
|
*
|
|
2753
|
-
* Returns `null` when the slide has **no notes slide part at all
|
|
2741
|
+
* Returns `null` when the slide has **no notes slide part at all**, distinct
|
|
2754
2742
|
* from `''`, which means a notes slide exists but its body is empty (PowerPoint
|
|
2755
2743
|
* often attaches an empty notes slide to every slide). Only the body placeholder
|
|
2756
2744
|
* is read; the slide-thumbnail (`sldImg`) and slide-number (`sldNum`)
|
|
@@ -2761,14 +2749,14 @@ var Slide = class {
|
|
|
2761
2749
|
return notes ? notes.text : null;
|
|
2762
2750
|
}
|
|
2763
2751
|
/**
|
|
2764
|
-
* The slide's speaker-note body as a navigable {@link TextFrame}
|
|
2752
|
+
* The slide's speaker-note body as a navigable {@link TextFrame}: the rich
|
|
2765
2753
|
* companion to {@link notesText}, which flattens the same body to a plain string.
|
|
2766
2754
|
* Walk `paragraphs → runs` to recover per-run formatting (bold/italic/underline,
|
|
2767
2755
|
* colour, size, face) and any notes hyperlink that {@link notesText} discards.
|
|
2768
2756
|
*
|
|
2769
2757
|
* `null` when the slide has **no notes slide part** (the same boundary
|
|
2770
2758
|
* {@link notesText} returns `null` at), and also when a notes part exists but
|
|
2771
|
-
* carries no body-placeholder text frame
|
|
2759
|
+
* carries no body-placeholder text frame; there is then no frame to hand back
|
|
2772
2760
|
* (whereas {@link notesText} reports `''` for that empty-body case). Convenience
|
|
2773
2761
|
* for `notesSlide.textFrame`; see {@link notesSlide} for the whole modeled notes
|
|
2774
2762
|
* slide (its `sldImg`/`sldNum` placeholders and their geometry).
|
|
@@ -2780,9 +2768,9 @@ var Slide = class {
|
|
|
2780
2768
|
* The slide's speaker-notes slide (`notesSlideN.xml`) as a modeled
|
|
2781
2769
|
* {@link NotesSlide}, or `null` when the slide has no notes slide part. Beyond the
|
|
2782
2770
|
* body text {@link notesText}/{@link notesTextFrame} surface, this exposes the
|
|
2783
|
-
* notes slide's three placeholders
|
|
2784
|
-
*
|
|
2785
|
-
* (
|
|
2771
|
+
* notes slide's three placeholders, each with its geometry and (where present) a
|
|
2772
|
+
* navigable text frame: the slide thumbnail (`slideImage`), the notes body, and the
|
|
2773
|
+
* slide-number field (`slideNumber`).
|
|
2786
2774
|
*
|
|
2787
2775
|
* The writer authors all three placeholders but leaves `sldImg`/`sldNum` with an
|
|
2788
2776
|
* empty `p:spPr`, so on an authored deck their geometry reads `null` while an
|
|
@@ -2838,7 +2826,7 @@ var Slide = class {
|
|
|
2838
2826
|
* carries its body text, `created` timestamp, marker position (EMU), its author
|
|
2839
2827
|
* resolved through the deck-wide {@link Presentation.modernCommentAuthors}
|
|
2840
2828
|
* registry (`@authorId` GUID → name/initials), and its reply thread nested under
|
|
2841
|
-
* `replies`. Read-only
|
|
2829
|
+
* `replies`. Read-only: the writer authors the legacy schema; see
|
|
2842
2830
|
* {@link Presentation.commentSchema} to tell which schema a deck uses.
|
|
2843
2831
|
*/
|
|
2844
2832
|
get modernComments() {
|
|
@@ -2847,7 +2835,7 @@ var Slide = class {
|
|
|
2847
2835
|
/**
|
|
2848
2836
|
* The slide's programmatic tags (`p:custDataLst/p:tags` on the slide part,
|
|
2849
2837
|
* resolved to `ppt/tags/tagN.xml`) as `{ name, val }` string pairs, `[]` when it
|
|
2850
|
-
* has none. Host/add-in metadata with no visible rendering and no writer
|
|
2838
|
+
* has none. Host/add-in metadata with no visible rendering and no writer:
|
|
2851
2839
|
* read-only, preserved byte-for-byte on round-trip. Deck-level tags are
|
|
2852
2840
|
* {@link Presentation.tags}.
|
|
2853
2841
|
*/
|
|
@@ -2857,7 +2845,7 @@ var Slide = class {
|
|
|
2857
2845
|
/**
|
|
2858
2846
|
* The slide's effective background (`p:cSld/p:bg`), decoded into a typed
|
|
2859
2847
|
* {@link SlideBackground}. Resolved through the inheritance chain: the slide's
|
|
2860
|
-
* own `p:bg` wins; failing that the slideLayout's, then the slideMaster's
|
|
2848
|
+
* own `p:bg` wins; failing that the slideLayout's, then the slideMaster's. The
|
|
2861
2849
|
* result's `source` records which supplied it. `null` when nothing in the chain
|
|
2862
2850
|
* defines a background.
|
|
2863
2851
|
*
|
|
@@ -2903,7 +2891,7 @@ var Slide = class {
|
|
|
2903
2891
|
* `slide.slideNumber`, or added to a master defined with `slideNumber`; its geometry and run formatting
|
|
2904
2892
|
* read off the returned {@link AutoShape} the same as any placeholder.
|
|
2905
2893
|
*
|
|
2906
|
-
* Scoped to the slide's *own* shape tree
|
|
2894
|
+
* Scoped to the slide's *own* shape tree: a slide number inherited purely from
|
|
2907
2895
|
* the master's `p:hf`/placeholder (with no shape on the slide) is a master-level
|
|
2908
2896
|
* concern this getter does not resolve.
|
|
2909
2897
|
*/
|
|
@@ -2920,7 +2908,7 @@ var Slide = class {
|
|
|
2920
2908
|
}
|
|
2921
2909
|
/**
|
|
2922
2910
|
* The first shape *anywhere* in the slide's shape tree with the given drawing id
|
|
2923
|
-
* (`p:cNvPr/@id`), descending into groups
|
|
2911
|
+
* (`p:cNvPr/@id`), descending into groups, unlike {@link shapeById}, which scans
|
|
2924
2912
|
* only top-level shapes. Walked pre-order (a group is visited before its children),
|
|
2925
2913
|
* matching how the writer allocates ids. `undefined` when no shape carries that id.
|
|
2926
2914
|
*
|
|
@@ -2939,8 +2927,9 @@ var Slide = class {
|
|
|
2939
2927
|
/**
|
|
2940
2928
|
* The first placeholder of the given type (`p:ph/@type`, e.g. `title`,
|
|
2941
2929
|
* `ctrTitle`, `subTitle`, `body`), optionally narrowed by `idx`. Returns
|
|
2942
|
-
* `undefined` when none match. Only
|
|
2943
|
-
*
|
|
2930
|
+
* `undefined` when none match. Only {@link AutoShape}s are searched: a filled
|
|
2931
|
+
* picture, table or chart placeholder is not found here, though its own
|
|
2932
|
+
* `placeholder` getter still reads it.
|
|
2944
2933
|
*/
|
|
2945
2934
|
placeholder(type, idx) {
|
|
2946
2935
|
return this.shapes.find((shape) => {
|
|
@@ -3020,7 +3009,7 @@ var Slide = class {
|
|
|
3020
3009
|
* afterwards through {@link notesTextFrame}.
|
|
3021
3010
|
*
|
|
3022
3011
|
* The counterpart to the read side's `notesText`/`notesTextFrame`/`notesSlide`
|
|
3023
|
-
* getters, and the way to annotate a slide that has **no notes part at all
|
|
3012
|
+
* getters, and the way to annotate a slide that has **no notes part at all**:
|
|
3024
3013
|
* the state an `importSlide` without `{ importNotes: true }` leaves behind, and
|
|
3025
3014
|
* the one a `notesTextFrame` edit cannot reach because there is no frame to
|
|
3026
3015
|
* hand back. Calling it on a slide that already has notes replaces the body
|
|
@@ -3075,8 +3064,7 @@ var Slide = class {
|
|
|
3075
3064
|
return spTreeOf(this.part.dom.documentElement);
|
|
3076
3065
|
}
|
|
3077
3066
|
};
|
|
3078
|
-
|
|
3079
|
-
//#region src/read/api/document-properties.ts
|
|
3067
|
+
// src/read/api/document-properties.ts
|
|
3080
3068
|
/** `dc:title`-style core-property fields, keyed by output field → element qname. */
|
|
3081
3069
|
const CORE_FIELDS = [
|
|
3082
3070
|
["title", "dc:title"],
|
|
@@ -3146,7 +3134,7 @@ function readExtendedProperties(opc) {
|
|
|
3146
3134
|
* number; real types (`r4`/`r8`/`decimal`) → number; `bool` → boolean;
|
|
3147
3135
|
* `filetime`/`date` → the **raw string** (see {@link CoreProperties} on
|
|
3148
3136
|
* timestamps). Any unrecognized `vt:` element falls back to its text content as a
|
|
3149
|
-
* string
|
|
3137
|
+
* string, which is lossy but never throws.
|
|
3150
3138
|
*/
|
|
3151
3139
|
function decodeValue(vt) {
|
|
3152
3140
|
const text = vt.textContent ?? "";
|
|
@@ -3203,13 +3191,12 @@ function readCustomProperties(opc) {
|
|
|
3203
3191
|
}
|
|
3204
3192
|
return out;
|
|
3205
3193
|
}
|
|
3206
|
-
|
|
3207
|
-
//#region src/read/api/ops/embedded-fonts.ts
|
|
3194
|
+
// src/read/api/ops/embedded-fonts.ts
|
|
3208
3195
|
/**
|
|
3209
3196
|
* Merging embedded font faces into a deck's `p:embeddedFontLst`.
|
|
3210
3197
|
*
|
|
3211
|
-
* Fonts arrive two ways
|
|
3212
|
-
* generator during an append
|
|
3198
|
+
* Fonts arrive two ways (copied out of another `Presentation`, or handed over as raw bytes by a
|
|
3199
|
+
* generator during an append), and the two differ only in how the binary part comes into being.
|
|
3213
3200
|
* That difference is confined to a per-face thunk, so both paths share one merge core: entries
|
|
3214
3201
|
* join by typeface, faces de-dupe by slot, and the thunk runs only for a face actually being
|
|
3215
3202
|
* added, so a face the deck already embeds never mints an orphan part.
|
|
@@ -3271,7 +3258,7 @@ function readEmbeddedFontEntries(deck) {
|
|
|
3271
3258
|
* every face this deck would copy names a part that is actually there.
|
|
3272
3259
|
*
|
|
3273
3260
|
* Every import that carries fonts calls it before it changes anything, because the carry runs
|
|
3274
|
-
* last
|
|
3261
|
+
* last (after the page or the masters are already in the deck), and a face it cannot copy would
|
|
3275
3262
|
* throw with parts added and no way back. A face whose `r:id` names no relationship and a face
|
|
3276
3263
|
* whose relationship names no part are the same failure to a caller, the face's binary is not in
|
|
3277
3264
|
* the package, so both are `package/part-missing`.
|
|
@@ -3354,7 +3341,7 @@ function carryGeneratedEmbeddedFonts(dest, fonts) {
|
|
|
3354
3341
|
}
|
|
3355
3342
|
/**
|
|
3356
3343
|
* Merge normalized {@link IncomingEmbeddedFont} entries into this deck's
|
|
3357
|
-
* `p:embeddedFontLst
|
|
3344
|
+
* `p:embeddedFontLst`: the shared core of {@link carryEmbeddedFonts} (import-side)
|
|
3358
3345
|
* and {@link carryGeneratedEmbeddedFonts} (append-side). Entries merge by `typeface`,
|
|
3359
3346
|
* faces de-dupe by slot (a face this deck already embeds is left as is). For each newly
|
|
3360
3347
|
* added face the `fntdata` Default is ensured, the binary part is created via the face's
|
|
@@ -3404,8 +3391,126 @@ function mergeEmbeddedFontEntries(dest, entries) {
|
|
|
3404
3391
|
presPart.markDirty();
|
|
3405
3392
|
}
|
|
3406
3393
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3394
|
+
// src/read/api/ops/comments.ts
|
|
3395
|
+
/**
|
|
3396
|
+
* Carrying legacy review comments onto a deck: a comments part per slide, and the deck-wide
|
|
3397
|
+
* author registry (`ppt/commentAuthors.xml`) every comment names its author in by `@authorId`.
|
|
3398
|
+
* `appendSlides` builds the comments part from a generator's comments; `importSlide` and
|
|
3399
|
+
* `importSlides` copy the source's part with the page and then renumber it here.
|
|
3400
|
+
*
|
|
3401
|
+
* The registry is the part that makes this more than a copy. The destination may already have
|
|
3402
|
+
* one, with its own ids and its own per-author numbering, so an incoming comment's author is
|
|
3403
|
+
* looked up there by name and initials (the key the writer resolves authors by) and its `idx`
|
|
3404
|
+
* continues that author's `lastIdx`. An author the destination does not have is added under the
|
|
3405
|
+
* next free id.
|
|
3406
|
+
*/
|
|
3407
|
+
const textEncoder$2 = new TextEncoder();
|
|
3408
|
+
/** The deck's comment-author registry part, created and related from the presentation when it has none. */
|
|
3409
|
+
function commentAuthorsPart(dest) {
|
|
3410
|
+
const existing = singleRelPart(dest.opc, dest.presentationPart.partName, COMMENT_AUTHORS_REL);
|
|
3411
|
+
if (existing) return existing;
|
|
3412
|
+
const partName = dest.opc.reservePartNameLike("/ppt/commentAuthors.xml");
|
|
3413
|
+
const part = dest.opc.addPart(partName, COMMENT_AUTHORS_CONTENT_TYPE, textEncoder$2.encode(makeXmlCommentAuthors([])));
|
|
3414
|
+
presentationRels(dest).add(COMMENT_AUTHORS_REL, relativePartName(dest.presentationPart.partName, partName));
|
|
3415
|
+
return part;
|
|
3416
|
+
}
|
|
3417
|
+
/**
|
|
3418
|
+
* The deck's author registry, open for registering authors: each call to `register` finds or adds
|
|
3419
|
+
* the author by name and initials, takes that author's next `idx`, and marks the part dirty.
|
|
3420
|
+
*/
|
|
3421
|
+
function authorRegistry(dest) {
|
|
3422
|
+
const part = commentAuthorsPart(dest);
|
|
3423
|
+
const root = part.dom.documentElement;
|
|
3424
|
+
if (!root) throw new PackageReadError("package/part-has-no-root", `${part.partName} has no document element to register comment authors in`);
|
|
3425
|
+
const authors = getElements(root, "p:cmAuthor");
|
|
3426
|
+
return (name, initials) => {
|
|
3427
|
+
let author = authors.find((a) => attr(a, "name") === name && attr(a, "initials") === initials);
|
|
3428
|
+
if (!author) {
|
|
3429
|
+
const id = Math.max(-1, ...authors.map((a) => numberValue(attr(a, "id")) ?? -1)) + 1;
|
|
3430
|
+
author = createElement(part.dom, "p:cmAuthor");
|
|
3431
|
+
setAttr(author, "id", String(id));
|
|
3432
|
+
setAttr(author, "name", name);
|
|
3433
|
+
setAttr(author, "initials", initials);
|
|
3434
|
+
setAttr(author, "lastIdx", "0");
|
|
3435
|
+
setAttr(author, "clrIdx", String(id));
|
|
3436
|
+
root.appendChild(author);
|
|
3437
|
+
authors.push(author);
|
|
3438
|
+
}
|
|
3439
|
+
const idx = (numberValue(attr(author, "lastIdx")) ?? 0) + 1;
|
|
3440
|
+
setAttr(author, "lastIdx", String(idx));
|
|
3441
|
+
part.markDirty();
|
|
3442
|
+
return {
|
|
3443
|
+
authorId: numberValue(attr(author, "id")) ?? 0,
|
|
3444
|
+
idx
|
|
3445
|
+
};
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
/**
|
|
3449
|
+
* Add a comments part holding `comments`, registering each author in the deck's
|
|
3450
|
+
* `commentAuthors.xml`. The slide's own relationship *to* the part is left to the caller, as
|
|
3451
|
+
* `addNotesSlidePart` leaves the notes one, because the append path adds it after every id
|
|
3452
|
+
* the slide body names.
|
|
3453
|
+
* @param dest - the deck the part is added to
|
|
3454
|
+
* @param comments - the slide's comments, in order
|
|
3455
|
+
* @returns the comments part's name
|
|
3456
|
+
*/
|
|
3457
|
+
function addCommentsPart(dest, comments) {
|
|
3458
|
+
const register = authorRegistry(dest);
|
|
3459
|
+
const meta = new Map(comments.map((comment) => [comment, register(comment.author, comment.initials)]));
|
|
3460
|
+
const partName = dest.opc.reservePartNameLike("/ppt/comments/comment1.xml");
|
|
3461
|
+
dest.opc.addPart(partName, COMMENTS_CONTENT_TYPE, textEncoder$2.encode(makeXmlComments(comments, meta)));
|
|
3462
|
+
return partName;
|
|
3463
|
+
}
|
|
3464
|
+
/**
|
|
3465
|
+
* Renumber the comments part a page copy brought from `source` against this deck's registry
|
|
3466
|
+
* (#235). The copy keeps each `@authorId` and `@idx` as the source numbered them, which on this
|
|
3467
|
+
* deck names no author, or the wrong one. Each comment's author is resolved in the source's
|
|
3468
|
+
* registry and registered in this deck's, and the comment takes the id and the next `idx` it gets
|
|
3469
|
+
* there. A comment whose author the source itself does not register is left as it is.
|
|
3470
|
+
*
|
|
3471
|
+
* Runs once per page copy, after the copy: a comments part is owned by its page (see
|
|
3472
|
+
* `page-owned.ts`), so every copy has a part of its own to renumber.
|
|
3473
|
+
* @param dest - the deck the page was copied into
|
|
3474
|
+
* @param source - the deck it was copied from
|
|
3475
|
+
* @param slidePartName - the copied page in `dest`
|
|
3476
|
+
*/
|
|
3477
|
+
function carryCommentAuthors(dest, source, slidePartName) {
|
|
3478
|
+
const part = singleRelPart(dest.opc, slidePartName, COMMENTS_REL);
|
|
3479
|
+
const root = part?.dom.documentElement;
|
|
3480
|
+
if (!part || !root) return;
|
|
3481
|
+
const comments = getElements(root, "p:cm");
|
|
3482
|
+
if (comments.length === 0) return;
|
|
3483
|
+
const sourceAuthors = new Map(readCommentAuthors(source.opc, source.presentationPart.partName).map((a) => [a.id, a]));
|
|
3484
|
+
const register = authorRegistry(dest);
|
|
3485
|
+
const renumber = (cm) => {
|
|
3486
|
+
const author = sourceAuthors.get(numberValue(attr(cm, "authorId")));
|
|
3487
|
+
if (!author) return;
|
|
3488
|
+
const { authorId, idx } = register(author.name, author.initials);
|
|
3489
|
+
setAttr(cm, "authorId", String(authorId));
|
|
3490
|
+
setAttr(cm, "idx", String(idx));
|
|
3491
|
+
};
|
|
3492
|
+
comments.forEach(renumber);
|
|
3493
|
+
part.markDirty();
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Reset every author's `lastIdx` in the deck's registry to 0, for a deck with no comments left
|
|
3497
|
+
* (#244). `fromTemplate` strips the template's slides, and their comments with them, but the
|
|
3498
|
+
* registry keeps counting from the comments it removed, so the next comment would be numbered
|
|
3499
|
+
* after one the deck no longer has. `lastIdx` is the author's highest used index (ECMA-376
|
|
3500
|
+
* §19.4.2), and in an empty deck that is none. The authors themselves stay, with their ids and
|
|
3501
|
+
* colours, so a comment re-authored under the same name lands on the same entry.
|
|
3502
|
+
* @param dest - the deck whose slides were all removed
|
|
3503
|
+
*/
|
|
3504
|
+
function resetCommentAuthorIndexes(dest) {
|
|
3505
|
+
const part = singleRelPart(dest.opc, dest.presentationPart.partName, COMMENT_AUTHORS_REL);
|
|
3506
|
+
const root = part?.dom.documentElement;
|
|
3507
|
+
if (!part || !root) return;
|
|
3508
|
+
const authors = getElements(root, "p:cmAuthor").filter((author) => attr(author, "lastIdx") !== "0");
|
|
3509
|
+
if (authors.length === 0) return;
|
|
3510
|
+
for (const author of authors) setAttr(author, "lastIdx", "0");
|
|
3511
|
+
part.markDirty();
|
|
3512
|
+
}
|
|
3513
|
+
// src/read/api/ops/slide-size.ts
|
|
3409
3514
|
/**
|
|
3410
3515
|
* pptx-ts: slide-size preconditions for the import surface
|
|
3411
3516
|
*
|
|
@@ -3417,9 +3522,9 @@ function mergeEmbeddedFontEntries(dest, entries) {
|
|
|
3417
3522
|
* except `importSlide`, and only when a rescale had been asked for.
|
|
3418
3523
|
*
|
|
3419
3524
|
* The two conditions are separated here, and the codes now mean what their names say:
|
|
3420
|
-
* - `import/slide-size-unknown
|
|
3525
|
+
* - `import/slide-size-unknown`: a deck does not declare `p:sldSz`, so there is no size to
|
|
3421
3526
|
* compare and no rescale to compute. Nothing the caller passes can make it comparable.
|
|
3422
|
-
* - `import/slide-size-mismatch
|
|
3527
|
+
* - `import/slide-size-mismatch`: both sizes are known and they differ. This one *is*
|
|
3423
3528
|
* answerable, which is why each caller supplies the escape hatch it offers.
|
|
3424
3529
|
*/
|
|
3425
3530
|
/** A size as the error messages print it. */
|
|
@@ -3430,7 +3535,7 @@ function fmt(size) {
|
|
|
3430
3535
|
* Both decks' sizes, or a throw when either deck does not declare one.
|
|
3431
3536
|
*
|
|
3432
3537
|
* Returned rather than asserted because every caller that needs both sizes known needs the
|
|
3433
|
-
* values too
|
|
3538
|
+
* values too, to compute a rescale, and TypeScript cannot narrow two parameters from one
|
|
3434
3539
|
* assertion signature.
|
|
3435
3540
|
*
|
|
3436
3541
|
* @param target - this deck's slide size
|
|
@@ -3455,19 +3560,18 @@ function requireEqualSlideSize(target, incoming, api, hint = "") {
|
|
|
3455
3560
|
if (known.widthEmu === source.widthEmu && known.heightEmu === source.heightEmu) return;
|
|
3456
3561
|
throw new InvalidOptionError("import/slide-size-mismatch", `${api} requires equal slide sizes${hint ? ` (${hint})` : ""}; target is ${fmt(target)}, source is ${fmt(incoming)}`);
|
|
3457
3562
|
}
|
|
3458
|
-
/** Whether both sizes are known and equal
|
|
3563
|
+
/** Whether both sizes are known and equal: the question a caller with a rescale asks first. */
|
|
3459
3564
|
function slideSizesMatch(target, incoming) {
|
|
3460
3565
|
return !!target && !!incoming && target.widthEmu === incoming.widthEmu && target.heightEmu === incoming.heightEmu;
|
|
3461
3566
|
}
|
|
3462
|
-
|
|
3463
|
-
//#region src/read/api/ops/append-slides.ts
|
|
3567
|
+
// src/read/api/ops/append-slides.ts
|
|
3464
3568
|
/**
|
|
3465
3569
|
* pptx-ts: the body of {@link Presentation.appendSlides}
|
|
3466
3570
|
*
|
|
3467
3571
|
* The hybrid "generate onto an existing deck" path: a slide producer authors slides, they are
|
|
3468
|
-
* spliced into this package under fresh partnames, and every dependency each one carries
|
|
3469
|
-
* images, embedded and online media, hyperlinks, speaker notes, charts,
|
|
3470
|
-
*
|
|
3572
|
+
* spliced into this package under fresh partnames, and every dependency each one carries
|
|
3573
|
+
* (images, embedded and online media, hyperlinks, speaker notes, review comments, charts,
|
|
3574
|
+
* intra-batch slide links) is wired into its `.rels`.
|
|
3471
3575
|
*
|
|
3472
3576
|
* Here rather than on the class for the reason `presentation.ts` states about the four import
|
|
3473
3577
|
* bodies: the *contract* is the doc comment a caller reads and stays on the public method, while
|
|
@@ -3480,12 +3584,12 @@ function slideSizesMatch(target, incoming) {
|
|
|
3480
3584
|
*/
|
|
3481
3585
|
const textEncoder$1 = new TextEncoder();
|
|
3482
3586
|
/**
|
|
3483
|
-
* Resolve `options.layout`
|
|
3484
|
-
* gallery, with no silent fallback: an unusable value is one of
|
|
3587
|
+
* Resolve `options.layout` (a layout *name* or a {@link LayoutHandle}) against this deck's
|
|
3588
|
+
* gallery, with no silent fallback: an unusable value is one of four named errors rather than a
|
|
3485
3589
|
* default layout, because a slide bound to the wrong layout is a deck that looks authored.
|
|
3486
3590
|
*
|
|
3487
3591
|
* Split out because it is the part of the append most likely to be wanted by a second caller, and
|
|
3488
|
-
* because it is the only place `layout/ambiguous-name`, `layout/not-found` and
|
|
3592
|
+
* because it is the only place `layout/missing`, `layout/ambiguous-name`, `layout/not-found` and
|
|
3489
3593
|
* `layout/foreign-handle` are raised.
|
|
3490
3594
|
*
|
|
3491
3595
|
* @param deck - the destination deck, whose `layouts()` gallery is searched
|
|
@@ -3494,6 +3598,8 @@ const textEncoder$1 = new TextEncoder();
|
|
|
3494
3598
|
*/
|
|
3495
3599
|
function resolveLayoutTarget(deck, layout, api = "appendSlides") {
|
|
3496
3600
|
const gallery = deck.layouts();
|
|
3601
|
+
const names = () => gallery.map((l) => JSON.stringify(l.name)).join(", ") || "(none)";
|
|
3602
|
+
if (layout == null) throw new InvalidOptionError("layout/missing", `${api}: options.layout is required, a layout name or a LayoutHandle from layouts(); available: ${names()}`);
|
|
3497
3603
|
if (typeof layout !== "string") {
|
|
3498
3604
|
if (!gallery.some((l) => l.partName === layout.partName)) throw new InvalidOptionError("layout/foreign-handle", `${api}: layout ${layout.partName} does not belong to this presentation`);
|
|
3499
3605
|
return layout;
|
|
@@ -3501,10 +3607,7 @@ function resolveLayoutTarget(deck, layout, api = "appendSlides") {
|
|
|
3501
3607
|
const matches = gallery.filter((l) => l.name === layout);
|
|
3502
3608
|
if (matches.length > 1) throw new InvalidOptionError("layout/ambiguous-name", `${api}: layout name ${JSON.stringify(layout)} is ambiguous (${matches.length} layouts share it); pass a LayoutHandle from layouts() instead`);
|
|
3503
3609
|
const [only] = matches;
|
|
3504
|
-
if (!only) {
|
|
3505
|
-
const names = gallery.map((l) => JSON.stringify(l.name)).join(", ");
|
|
3506
|
-
throw new InvalidOptionError("layout/not-found", `${api}: no layout named ${JSON.stringify(layout)}; available: ${names || "(none)"}`);
|
|
3507
|
-
}
|
|
3610
|
+
if (!only) throw new InvalidOptionError("layout/not-found", `${api}: no layout named ${JSON.stringify(layout)}; available: ${names()}`);
|
|
3508
3611
|
return only;
|
|
3509
3612
|
}
|
|
3510
3613
|
/** Image parts behind the slide body's `image` rels, each keeping the id the body was built with. */
|
|
@@ -3536,8 +3639,20 @@ function wireAvMedia(deck, rels, partName, slide) {
|
|
|
3536
3639
|
}
|
|
3537
3640
|
}
|
|
3538
3641
|
/**
|
|
3539
|
-
*
|
|
3540
|
-
*
|
|
3642
|
+
* A transition's start sound: one audio part behind the `audio` rel the body's `p:snd r:embed`
|
|
3643
|
+
* names. Registered as a Default extension entry, as {@link wireAvMedia} does.
|
|
3644
|
+
*/
|
|
3645
|
+
function wireTransitionSound(deck, rels, partName, slide) {
|
|
3646
|
+
const sound = slide.transitionSound;
|
|
3647
|
+
if (!sound) return;
|
|
3648
|
+
const mediaPartName = deck.opc.reserveMediaPartName(sound.extn, "media");
|
|
3649
|
+
deck.opc.contentTypes.ensureDefault(sound.extn, sound.contentType);
|
|
3650
|
+
deck.opc.addPart(mediaPartName, sound.contentType, sound.bytes);
|
|
3651
|
+
rels.addWithId(`rId${sound.rId}`, AUDIO_REL, relativePartName(partName, mediaPartName));
|
|
3652
|
+
}
|
|
3653
|
+
/**
|
|
3654
|
+
* Online (external-link) video: two External rels share the link Target (the ECMA video rel and
|
|
3655
|
+
* the MS-2007 media rel), with no media binary part and no content-type entry. The poster image is
|
|
3541
3656
|
* wired by {@link wireMedia}, which runs first.
|
|
3542
3657
|
*/
|
|
3543
3658
|
function wireOnlineMedia(rels, slide) {
|
|
@@ -3552,7 +3667,7 @@ function wireHyperlinks(rels, slide) {
|
|
|
3552
3667
|
}
|
|
3553
3668
|
/**
|
|
3554
3669
|
* Speaker notes. The notes part carries its own rel namespace, independent of the slide's:
|
|
3555
|
-
* rId1 = notesMaster, rId2 = the slide it annotates, hyperlinks from rId3
|
|
3670
|
+
* rId1 = notesMaster, rId2 = the slide it annotates, hyperlinks from rId3. That is the order the
|
|
3556
3671
|
* generator's body was serialized against, so these are added by explicit id rather than left to
|
|
3557
3672
|
* auto-numbering.
|
|
3558
3673
|
*
|
|
@@ -3560,12 +3675,13 @@ function wireHyperlinks(rels, slide) {
|
|
|
3560
3675
|
* so {@link wireAutoIdRels} adds it once every id the body does name is claimed.
|
|
3561
3676
|
* @returns the notes part's name, or `null` when the slide has no notes
|
|
3562
3677
|
*/
|
|
3563
|
-
function wireNotes(deck, partName, slide, sourceNotesMaster) {
|
|
3678
|
+
function wireNotes(deck, partName, slide, sourceNotesMaster, partBySourceNumber, index) {
|
|
3564
3679
|
if (!slide.notes) return null;
|
|
3565
3680
|
const notesMasterPartName = sourceNotesMaster ? ensureNotesMasterFromXml(deck, sourceNotesMaster) : null;
|
|
3566
3681
|
const notesPartName = addNotesSlidePart(deck, slide.notes.xml, partName, notesMasterPartName);
|
|
3567
3682
|
const notesRels = deck.opc.relationshipsFor(notesPartName);
|
|
3568
3683
|
for (const h of slide.notes.hyperlinks) notesRels.addWithId(`rId${h.rId}`, HYPERLINK_REL, h.target, "External");
|
|
3684
|
+
wireSlideLinks(notesRels, notesPartName, slide.notes.slideLinks ?? [], partBySourceNumber, index);
|
|
3569
3685
|
return notesPartName;
|
|
3570
3686
|
}
|
|
3571
3687
|
/**
|
|
@@ -3575,7 +3691,7 @@ function wireNotes(deck, partName, slide, sourceNotesMaster) {
|
|
|
3575
3691
|
*
|
|
3576
3692
|
* A chartEx chart differs in every other coordinate: the `chartEx{N}.xml` name family, the MS
|
|
3577
3693
|
* chartex content type, the MS `chartEx` rel off the slide, and two mandatory sidecars at
|
|
3578
|
-
* rId2/rId3
|
|
3694
|
+
* rId2/rId3, without which PowerPoint reports the deck as corrupt (schema-valid but unopenable).
|
|
3579
3695
|
* `c.chartEx` carries both sidecar bodies; the generator built them, so nothing here reaches into
|
|
3580
3696
|
* the emitters.
|
|
3581
3697
|
*/
|
|
@@ -3589,6 +3705,11 @@ function wireCharts(deck, rels, partName, slide) {
|
|
|
3589
3705
|
deck.opc.addPart(embeddingPartName, XLSX_CONTENT_TYPE, c.embeddingBytes);
|
|
3590
3706
|
const chartRels = deck.opc.relationshipsFor(chartPartName);
|
|
3591
3707
|
chartRels.addWithId("rId1", PACKAGE_REL, relativePartName(chartPartName, embeddingPartName));
|
|
3708
|
+
for (const picture of c.pictures ?? []) {
|
|
3709
|
+
const mediaPartName = deck.opc.reserveMediaPartName(picture.extn);
|
|
3710
|
+
deck.opc.addPart(mediaPartName, picture.contentType, picture.bytes);
|
|
3711
|
+
chartRels.addWithId(`rId${picture.rId}`, IMAGE_REL, relativePartName(chartPartName, mediaPartName));
|
|
3712
|
+
}
|
|
3592
3713
|
if (c.chartEx) {
|
|
3593
3714
|
const colorsPartName = deck.opc.reservePartNameLike("/ppt/charts/colors1.xml");
|
|
3594
3715
|
deck.opc.addPart(colorsPartName, CHART_COLOR_STYLE_CONTENT_TYPE, textEncoder$1.encode(c.chartEx.colorsXml));
|
|
@@ -3601,12 +3722,12 @@ function wireCharts(deck, rels, partName, slide) {
|
|
|
3601
3722
|
}
|
|
3602
3723
|
}
|
|
3603
3724
|
/**
|
|
3604
|
-
* Intra-batch slide-to-slide links: a `slide:N` in the source is
|
|
3605
|
-
* slide's new partname, which is why pass 1 places every slide before any of this runs. A link out
|
|
3725
|
+
* Intra-batch slide-to-slide links, from a slide or from its notes: a `slide:N` in the source is
|
|
3726
|
+
* repointed at the Nth appended slide's new partname, which is why pass 1 places every slide before any of this runs. A link out
|
|
3606
3727
|
* of the batch was refused before pass 1 by {@link requireLinksInBatch}.
|
|
3607
3728
|
*/
|
|
3608
|
-
function wireSlideLinks(rels, partName,
|
|
3609
|
-
for (const link of
|
|
3729
|
+
function wireSlideLinks(rels, partName, links, partBySourceNumber, index) {
|
|
3730
|
+
for (const link of links) {
|
|
3610
3731
|
const targetPartName = partBySourceNumber.get(link.sourceSlideNumber);
|
|
3611
3732
|
if (!targetPartName) throw new InternalError("import/slide-link-not-placed", `appendSlides: slide ${index} links to source slide ${link.sourceSlideNumber}, which was checked against the batch but has no appended slide`);
|
|
3612
3733
|
rels.addWithId(`rId${link.rId}`, SLIDE_REL, relativePartName(partName, targetPartName));
|
|
@@ -3619,12 +3740,13 @@ function wireSlideLinks(rels, partName, slide, partBySourceNumber, index) {
|
|
|
3619
3740
|
*/
|
|
3620
3741
|
function requireLinksInBatch(slides) {
|
|
3621
3742
|
slides.forEach((slide, index) => {
|
|
3622
|
-
for (const link of slide.slideLinks) if (link.sourceSlideNumber > slides.length) throw new InvalidOptionError("import/unresolved-slide-link", `appendSlides: slide ${index} links to source slide ${link.sourceSlideNumber}, which is not among the appended slides`);
|
|
3743
|
+
for (const link of [...slide.slideLinks, ...slide.notes?.slideLinks ?? []]) if (link.sourceSlideNumber > slides.length) throw new InvalidOptionError("import/unresolved-slide-link", `appendSlides: slide ${index} links to source slide ${link.sourceSlideNumber}, which is not among the appended slides`);
|
|
3623
3744
|
});
|
|
3624
3745
|
}
|
|
3625
3746
|
/**
|
|
3626
|
-
* The first of pass 2's two phases: every rel the slide body names by id
|
|
3627
|
-
* online media, hyperlinks, charts, slide links
|
|
3747
|
+
* The first of pass 2's two phases: every rel the slide body names by id (media, embedded and
|
|
3748
|
+
* online media, a transition sound, hyperlinks, charts, slide links), each added with the rId the
|
|
3749
|
+
* generator wrote.
|
|
3628
3750
|
* Also creates the notes part, whose own rels are fixed ids too.
|
|
3629
3751
|
*
|
|
3630
3752
|
* A new rel the body references by id belongs here, and nowhere after {@link wireAutoIdRels}.
|
|
@@ -3634,24 +3756,26 @@ function wireFixedIdRels(deck, rels, placed, sourceNotesMaster, partBySourceNumb
|
|
|
3634
3756
|
const { slide, partName } = placed;
|
|
3635
3757
|
wireMedia(deck, rels, partName, slide);
|
|
3636
3758
|
wireAvMedia(deck, rels, partName, slide);
|
|
3759
|
+
wireTransitionSound(deck, rels, partName, slide);
|
|
3637
3760
|
wireOnlineMedia(rels, slide);
|
|
3638
3761
|
wireHyperlinks(rels, slide);
|
|
3639
|
-
const notesPartName = wireNotes(deck, partName, slide, sourceNotesMaster);
|
|
3762
|
+
const notesPartName = wireNotes(deck, partName, slide, sourceNotesMaster, partBySourceNumber, index);
|
|
3640
3763
|
wireCharts(deck, rels, partName, slide);
|
|
3641
|
-
wireSlideLinks(rels, partName, slide, partBySourceNumber, index);
|
|
3764
|
+
wireSlideLinks(rels, partName, slide.slideLinks, partBySourceNumber, index);
|
|
3642
3765
|
return notesPartName;
|
|
3643
3766
|
}
|
|
3644
3767
|
/**
|
|
3645
|
-
* The second phase: the rels the slide body names by no id
|
|
3646
|
-
* by `add()`, which takes one past the highest id present.
|
|
3768
|
+
* The second phase: the rels the slide body names by no id (its notes, its comments and its
|
|
3769
|
+
* layout), numbered by `add()`, which takes one past the highest id present.
|
|
3647
3770
|
*
|
|
3648
3771
|
* Strictly after {@link wireFixedIdRels}. The generator numbers a slide's rels from rId1, so an
|
|
3649
3772
|
* auto id taken before a fixed one is claimed is routinely that fixed one: the notes rel used to
|
|
3650
3773
|
* be added in the middle of the first phase, and a slide carrying notes and a chart or a slide
|
|
3651
3774
|
* link threw `relationship/duplicate-id`.
|
|
3652
3775
|
*/
|
|
3653
|
-
function wireAutoIdRels(rels, partName, notesPartName, layoutPartName) {
|
|
3776
|
+
function wireAutoIdRels(rels, partName, notesPartName, commentsPartName, layoutPartName) {
|
|
3654
3777
|
if (notesPartName) rels.add(NOTES_SLIDE_REL, relativePartName(partName, notesPartName));
|
|
3778
|
+
if (commentsPartName) rels.add(COMMENTS_REL, relativePartName(partName, commentsPartName));
|
|
3655
3779
|
rels.add(SLIDE_LAYOUT_REL, relativePartName(partName, layoutPartName));
|
|
3656
3780
|
}
|
|
3657
3781
|
/**
|
|
@@ -3659,7 +3783,7 @@ function wireAutoIdRels(rels, partName, notesPartName, layoutPartName) {
|
|
|
3659
3783
|
* {@link Presentation.appendSlides}.
|
|
3660
3784
|
*/
|
|
3661
3785
|
async function appendSlides(deck, source, options) {
|
|
3662
|
-
const target = resolveLayoutTarget(deck, options
|
|
3786
|
+
const target = resolveLayoutTarget(deck, options?.layout);
|
|
3663
3787
|
const extracted = await source.extractSlides(pickDefined(options, ["onMediaError"]));
|
|
3664
3788
|
requireEqualSlideSize(deck.slideSize, {
|
|
3665
3789
|
widthEmu: extracted.widthEmu,
|
|
@@ -3683,15 +3807,16 @@ async function appendSlides(deck, source, options) {
|
|
|
3683
3807
|
placed.forEach((placedSlide, i) => {
|
|
3684
3808
|
const rels = deck.opc.relationshipsFor(placedSlide.partName);
|
|
3685
3809
|
const notesPartName = wireFixedIdRels(deck, rels, placedSlide, extracted.notesMaster, partBySourceNumber, i);
|
|
3686
|
-
|
|
3810
|
+
const comments = placedSlide.slide.comments;
|
|
3811
|
+
const commentsPartName = comments?.length ? addCommentsPart(deck, comments) : null;
|
|
3812
|
+
wireAutoIdRels(rels, placedSlide.partName, notesPartName, commentsPartName, target.partName);
|
|
3687
3813
|
const at = options.at === void 0 ? void 0 : options.at + i;
|
|
3688
3814
|
added.push(deck.insertSlidePart(placedSlide.part, at));
|
|
3689
3815
|
});
|
|
3690
3816
|
carryGeneratedEmbeddedFonts(deck, extracted.embeddedFonts || []);
|
|
3691
3817
|
return added;
|
|
3692
3818
|
}
|
|
3693
|
-
|
|
3694
|
-
//#region src/read/api/ops/prune.ts
|
|
3819
|
+
// src/read/api/ops/prune.ts
|
|
3695
3820
|
/**
|
|
3696
3821
|
* Content types that are shared deck chrome, not owned by any one slide: the `deck` rows of
|
|
3697
3822
|
* {@link SHARED_PARTS}, reached through the presentation → master → layout → theme graph or named
|
|
@@ -3823,8 +3948,7 @@ function isReferenced(pres, partName) {
|
|
|
3823
3948
|
function relationshipOwners(pres) {
|
|
3824
3949
|
return [...[...pres.opc.parts.keys()].filter((partName) => !partName.endsWith(".rels")), "/"];
|
|
3825
3950
|
}
|
|
3826
|
-
|
|
3827
|
-
//#region src/read/api/ops/import-memo.ts
|
|
3951
|
+
// src/read/api/ops/import-memo.ts
|
|
3828
3952
|
var ImportMemo = class {
|
|
3829
3953
|
#registries = /* @__PURE__ */ new Map();
|
|
3830
3954
|
#rescaled = /* @__PURE__ */ new Map();
|
|
@@ -3848,14 +3972,13 @@ var ImportMemo = class {
|
|
|
3848
3972
|
for (const partName of gone) this.#rescaled.delete(partName);
|
|
3849
3973
|
}
|
|
3850
3974
|
};
|
|
3851
|
-
|
|
3852
|
-
//#region src/read/api/ops/flatten.ts
|
|
3975
|
+
// src/read/api/ops/flatten.ts
|
|
3853
3976
|
/**
|
|
3854
3977
|
* Baking a slide's theme dependencies into the slide itself, for `Presentation.importSlide` /
|
|
3855
3978
|
* `importShape`.
|
|
3856
3979
|
*
|
|
3857
3980
|
* A slide imported into another deck is rebound to the *destination* master, so everything it
|
|
3858
|
-
* resolved symbolically against its own theme silently re-resolves against a different one
|
|
3981
|
+
* resolved symbolically against its own theme silently re-resolves against a different one: its
|
|
3859
3982
|
* scheme colours change, its style-matrix fills change, its placeholders snap to different
|
|
3860
3983
|
* geometry and type sizes. The `theme: 'preserve'` mode exists to stop that: each pass here
|
|
3861
3984
|
* resolves a dependency against the **source** theme (via `read/oxml/theme.ts` and
|
|
@@ -3864,25 +3987,25 @@ var ImportMemo = class {
|
|
|
3864
3987
|
*
|
|
3865
3988
|
* What gets flattened, and why each one matters:
|
|
3866
3989
|
*
|
|
3867
|
-
* 1. **`a:schemeClr` tokens
|
|
3990
|
+
* 1. **`a:schemeClr` tokens**: rewritten to the literal `a:srgbClr` the source `clrMap` +
|
|
3868
3991
|
* `clrScheme` produce. Child transforms (`lumMod`/`shade`/`alpha`/…) are carried through
|
|
3869
3992
|
* untouched, so tints and shades render identically; only the *base* reference is swapped,
|
|
3870
3993
|
* never the transform math.
|
|
3871
|
-
* 2. **Style-matrix refs
|
|
3994
|
+
* 2. **Style-matrix refs**: a shape's `p:style` (`lnRef`/`fillRef`/`effectRef`) indexes the
|
|
3872
3995
|
* theme `fmtScheme`; the indexed entry is resolved into an explicit `spPr` fill/line/effect
|
|
3873
3996
|
* (with its `phClr` substituted) and the ref neutralized so it cannot re-resolve. The
|
|
3874
|
-
* `fontRef` is deliberately left intact so its font can re-bind to the destination theme
|
|
3875
|
-
* the "normalize fonts on attach" bonus.
|
|
3876
|
-
* 3. **Placeholder-inherited run colour, size and weight
|
|
3997
|
+
* `fontRef` is deliberately left intact so its font can re-bind to the destination theme
|
|
3998
|
+
* (the "normalize fonts on attach" bonus).
|
|
3999
|
+
* 3. **Placeholder-inherited run colour, size and weight**: a run that sets none of its own
|
|
3877
4000
|
* takes them from the source placeholder → layout → master text-style chain. Rebinding
|
|
3878
4001
|
* replaces that chain, so each is resolved from the source styles and written onto the run.
|
|
3879
|
-
* Typeface (`a:latin`) is deliberately *not* baked
|
|
4002
|
+
* Typeface (`a:latin`) is deliberately *not* baked; it re-binds along with `fontRef`.
|
|
3880
4003
|
* A shape that is not a placeholder takes the same properties from the presentation's
|
|
3881
4004
|
* `p:defaultTextStyle` instead, which the rebind swaps for the destination's, so those
|
|
3882
4005
|
* are baked from the source one. A table cell's text takes its size from the master's
|
|
3883
4006
|
* `p:otherStyle`, and its weight too where the table style states none, so those are
|
|
3884
4007
|
* baked from the source master.
|
|
3885
|
-
* 4. **Placeholder-inherited geometry
|
|
4008
|
+
* 4. **Placeholder-inherited geometry**: a placeholder with no own `a:xfrm` takes position and
|
|
3886
4009
|
* size from the matching source layout/master placeholder; without baking it, a title snaps
|
|
3887
4010
|
* to the destination default and often clips off-canvas.
|
|
3888
4011
|
*
|
|
@@ -3925,7 +4048,7 @@ function flattenSlide(slideRoot, ctx) {
|
|
|
3925
4048
|
resolveSchemeColors(slideRoot, ctx);
|
|
3926
4049
|
}
|
|
3927
4050
|
/**
|
|
3928
|
-
* Flatten a single lifted shape's theme dependencies in place
|
|
4051
|
+
* Flatten a single lifted shape's theme dependencies in place: the shape-scoped
|
|
3929
4052
|
* subset of {@link flattenSlide} used by `Presentation.importShape` `preserve`
|
|
3930
4053
|
* mode. It runs every pass that resolves a *shape's* theme references against the
|
|
3931
4054
|
* source theme (style-matrix refs, placeholder-inherited geometry/colour/size/
|
|
@@ -3946,7 +4069,7 @@ function flattenSlide(slideRoot, ctx) {
|
|
|
3946
4069
|
*
|
|
3947
4070
|
* `shapeRoot` must be an element whose *descendants* include the lifted shape
|
|
3948
4071
|
* (the passes match via `getElementsByTagNameNS`, which excludes the root element
|
|
3949
|
-
* itself)
|
|
4072
|
+
* itself), so the caller wraps the imported `p:sp`/`p:pic`/`p:graphicFrame`/`p:grpSp`
|
|
3950
4073
|
* in a throwaway container before calling. The caller marks the part dirty.
|
|
3951
4074
|
*/
|
|
3952
4075
|
function flattenShape(shapeRoot, ctx) {
|
|
@@ -3963,7 +4086,7 @@ function flattenShape(shapeRoot, ctx) {
|
|
|
3963
4086
|
}
|
|
3964
4087
|
/**
|
|
3965
4088
|
* Restyle a rebound slide in place (the `theme: 'restyle'` mode): the exact
|
|
3966
|
-
* inverse of {@link flattenSlide}. It bakes *nothing
|
|
4089
|
+
* inverse of {@link flattenSlide}. It bakes *nothing*: leaving every
|
|
3967
4090
|
* `a:schemeClr`/style-matrix ref and `p:bg` `bgRef` symbolic is the whole point,
|
|
3968
4091
|
* so they re-resolve against the *destination* master's `clrMap` + theme once the
|
|
3969
4092
|
* slide is rebound. The single mutation is dropping the slide's own colour-map
|
|
@@ -3986,14 +4109,14 @@ function restyleSlide(slideRoot) {
|
|
|
3986
4109
|
* destination theme; a literal matching no slot (the common case) is left exactly
|
|
3987
4110
|
* as authored. Transform children (`lumMod`/`shade`/`alpha`/…) are carried across.
|
|
3988
4111
|
*
|
|
3989
|
-
* Plain `restyle` cannot re-brand a literal
|
|
4112
|
+
* Plain `restyle` cannot re-brand a literal, since it carries no theme reference. This
|
|
3990
4113
|
* re-introduces one by matching the literal's RGB against the slots the *source*
|
|
3991
4114
|
* theme defined, emitting the scheme token that routes through the source `clrMap`
|
|
3992
4115
|
* (so a literal equal to the source `accent1` slot becomes `schemeClr accent1`,
|
|
3993
4116
|
* which the destination `clrMap`/theme then re-resolve). Slots are matched in
|
|
3994
4117
|
* `clrScheme` order, so the first slot defining a given RGB wins when several share
|
|
3995
4118
|
* it. Opt-in because it deliberately reinterprets authored literals as theme
|
|
3996
|
-
* colours
|
|
4119
|
+
* colours: visual QA territory, the same caveat as the rest of `restyle`.
|
|
3997
4120
|
*/
|
|
3998
4121
|
function remapLiteralColors(slideRoot, ctx) {
|
|
3999
4122
|
const slotByHex = reverseClrScheme(ctx.clrScheme);
|
|
@@ -4082,7 +4205,7 @@ function resolvePlaceholderRunColors(slideRoot, ctx) {
|
|
|
4082
4205
|
* `resolve` is asked once per *list level* per shape and memoized, because walking the
|
|
4083
4206
|
* layout/master style chain is the expensive half and every paragraph at the same level gets
|
|
4084
4207
|
* the same answer. `write` then runs per run, and owns the decision to skip a run the slide
|
|
4085
|
-
* already fixes
|
|
4208
|
+
* already fixes. The two passes ask that question differently (one colour check versus three
|
|
4086
4209
|
* independent property checks), so it stays on their side of the boundary rather than becoming
|
|
4087
4210
|
* a predicate parameter only one caller could use.
|
|
4088
4211
|
*
|
|
@@ -4177,7 +4300,7 @@ function resolvePlaceholderGeometry(slideRoot, ctx) {
|
|
|
4177
4300
|
* We resolve the effective anchor from the source chain and write it explicitly.
|
|
4178
4301
|
* Shapes whose `a:bodyPr` already fixes `@anchor`, and placeholders with no
|
|
4179
4302
|
* resolvable inherited anchor, are left untouched. Other `a:bodyPr` knobs (insets,
|
|
4180
|
-
* autofit) are left as authored
|
|
4303
|
+
* autofit) are left as authored; they have sane defaults a plain shape keeps.
|
|
4181
4304
|
*/
|
|
4182
4305
|
function resolvePlaceholderBodyPr(shapeRoot, ctx) {
|
|
4183
4306
|
forEachPlaceholderTextBody(shapeRoot, ctx, (txBody, type, idx) => {
|
|
@@ -4190,21 +4313,20 @@ function resolvePlaceholderBodyPr(shapeRoot, ctx) {
|
|
|
4190
4313
|
}
|
|
4191
4314
|
/**
|
|
4192
4315
|
* Bake the placeholder-inherited list style onto a lifted shape's text body (the
|
|
4193
|
-
* {@link flattenShape} placeholder-demotion path). A placeholder
|
|
4194
|
-
* *paragraph* formatting
|
|
4195
|
-
* `
|
|
4196
|
-
*
|
|
4197
|
-
*
|
|
4198
|
-
*
|
|
4199
|
-
*
|
|
4200
|
-
*
|
|
4201
|
-
* the shape's `p:txBody/a:lstStyle`.
|
|
4316
|
+
* {@link flattenShape} placeholder-demotion path). A placeholder inherits its per-level
|
|
4317
|
+
* *paragraph* formatting from the source layout/master placeholder `a:lstStyle` and
|
|
4318
|
+
* the master `p:txStyles` category style: indent (`marL`/`indent`), bullets
|
|
4319
|
+
* (`a:buChar`/`a:buAutoNum`/`a:buNone`), alignment, and the level `a:defRPr`. Run
|
|
4320
|
+
* colour/size are baked onto each run by the sibling passes, but paragraph-level
|
|
4321
|
+
* defaults live here; once {@link demotePlaceholders} strips the `p:ph` the shape
|
|
4322
|
+
* stops inheriting them, so bullets and indents would vanish. We materialize the
|
|
4323
|
+
* effective list style onto the shape's `p:txBody/a:lstStyle`.
|
|
4202
4324
|
*
|
|
4203
4325
|
* Resolution is per *level*, most specific tier wins whole: the slide's own level
|
|
4204
4326
|
* (if it defines one) is kept verbatim, else the first source tier that defines it
|
|
4205
4327
|
* (layout placeholder, then master placeholder, then master category style) is
|
|
4206
4328
|
* cloned. This is a whole-element overlay, not the per-attribute merge PowerPoint
|
|
4207
|
-
* does
|
|
4329
|
+
* does, but explicit paragraph `a:pPr` on the slide's own runs travels with the
|
|
4208
4330
|
* shape and still wins, so this only supplies defaults for the inherited case
|
|
4209
4331
|
* (paragraphs that set no `a:pPr` of their own). Scheme colours in the cloned
|
|
4210
4332
|
* levels are resolved by the later `resolveSchemeColors` pass.
|
|
@@ -4231,7 +4353,7 @@ function resolvePlaceholderListStyle(shapeRoot, ctx) {
|
|
|
4231
4353
|
* Strip the `p:ph` marker from every placeholder shape in a lifted subtree so it
|
|
4232
4354
|
* becomes a self-contained ordinary shape (the {@link flattenShape} path only). By
|
|
4233
4355
|
* the time this runs, the shape's inherited geometry, colour, run size, anchor, and
|
|
4234
|
-
* list style are all baked explicitly, so it no longer needs
|
|
4356
|
+
* list style are all baked explicitly, so it no longer needs (and must not keep)
|
|
4235
4357
|
* its placeholder identity: a surviving `p:ph` would re-resolve against the *host*
|
|
4236
4358
|
* deck's layout/master placeholder of the same type/idx (wrong inheritance, or a
|
|
4237
4359
|
* fallback when the host has none) and could collide with the host slide's own
|
|
@@ -4249,7 +4371,7 @@ function demotePlaceholders(shapeRoot) {
|
|
|
4249
4371
|
* {@link resolvePlaceholderRunColors}: for each placeholder run that sets no
|
|
4250
4372
|
* `sz`/`b`/`i` of its own (nor at paragraph/text-body level on the *slide*),
|
|
4251
4373
|
* resolve the value it would inherit from the source `slideLayout`/`slideMaster`
|
|
4252
|
-
* text styles
|
|
4374
|
+
* text styles, per paragraph list level, and write it explicitly onto the run's
|
|
4253
4375
|
* `a:rPr`. Each property resolves independently up the chain. Typeface (`a:latin`)
|
|
4254
4376
|
* is left to re-bind to the destination theme, as the colour pass does for the `fontRef`.
|
|
4255
4377
|
*/
|
|
@@ -4483,15 +4605,14 @@ function neutralizeRef(ref) {
|
|
|
4483
4605
|
setAttr(ref, "idx", "0");
|
|
4484
4606
|
for (const child of childElements(ref)) ref.removeChild(child);
|
|
4485
4607
|
}
|
|
4486
|
-
|
|
4487
|
-
//#region src/read/api/ops/carried-rels.ts
|
|
4608
|
+
// src/read/api/ops/carried-rels.ts
|
|
4488
4609
|
/**
|
|
4489
4610
|
* Relationship rewriting for content carried between packages.
|
|
4490
4611
|
*
|
|
4491
4612
|
* When a shape or decoration is deep-copied out of a source deck, every relationship
|
|
4492
4613
|
* reference it carries (`r:embed` on a picture fill, `r:id` on a chart frame, `r:link` on a
|
|
4493
4614
|
* linked image, …) still names an id in the *source* part's relationship list. Left alone
|
|
4494
|
-
* those ids either dangle or
|
|
4615
|
+
* those ids either dangle or, worse, silently resolve to whatever the destination part
|
|
4495
4616
|
* happens to have under the same id. This rewrites each one to a fresh relationship on the
|
|
4496
4617
|
* destination part, copying the referenced part into the destination package on first sight.
|
|
4497
4618
|
*
|
|
@@ -4551,8 +4672,7 @@ function carryRel(ctx, sourceRels, id, destPartName, destRels, relIdMap, owned)
|
|
|
4551
4672
|
if (!ownsIt) relIdMap.set(key, newId);
|
|
4552
4673
|
return newId;
|
|
4553
4674
|
}
|
|
4554
|
-
|
|
4555
|
-
//#region src/read/api/ops/flatten-context.ts
|
|
4675
|
+
// src/read/api/ops/flatten-context.ts
|
|
4556
4676
|
/**
|
|
4557
4677
|
* The background the slide effectively inherits from its source subgraph: the layout's
|
|
4558
4678
|
* `p:bg`, else the master's. Returns `null` when the slide carries its own `p:bg` (it stays
|
|
@@ -4585,8 +4705,7 @@ function sourceFlattenContext(sourceOpc, slidePartName) {
|
|
|
4585
4705
|
tableStyles: sourceOpc.partsByContentType("application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml")[0]?.dom.documentElement ?? null
|
|
4586
4706
|
};
|
|
4587
4707
|
}
|
|
4588
|
-
|
|
4589
|
-
//#region src/read/api/ops/table-styles.ts
|
|
4708
|
+
// src/read/api/ops/table-styles.ts
|
|
4590
4709
|
const TABLE_STYLES_DEFAULT_GUID = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
4591
4710
|
const textEncoder = new TextEncoder();
|
|
4592
4711
|
/**
|
|
@@ -4595,7 +4714,7 @@ const textEncoder = new TextEncoder();
|
|
|
4595
4714
|
* table resolves its `@tableStyleId` against the *destination* `tableStyles` and
|
|
4596
4715
|
* silently falls back when that id is absent; the source `<a:tblStyle>` is itself
|
|
4597
4716
|
* symbolic (scheme colours), so copying it verbatim under the **same** id lets the
|
|
4598
|
-
* table keep its structure while re-branding to this deck's theme
|
|
4717
|
+
* table keep its structure while re-branding to this deck's theme, with no table-XML
|
|
4599
4718
|
* rewrite needed. Idempotent: an id this deck already defines is left as-is, and a
|
|
4600
4719
|
* referenced id with no source definition is skipped. Creates and wires a
|
|
4601
4720
|
* `tableStyles.xml` part if this deck has none.
|
|
@@ -4622,7 +4741,7 @@ function copySourceTableStyles(dest, sourceOpc, slideRoot) {
|
|
|
4622
4741
|
}
|
|
4623
4742
|
/**
|
|
4624
4743
|
* Append each source `<a:tblStyle>` this deck does not already define, keyed by
|
|
4625
|
-
* `styleId
|
|
4744
|
+
* `styleId`: the shared core of {@link copySourceTableStyles} (which offers only
|
|
4626
4745
|
* the styles a restyled slide references) and {@link carryTableStyles} (which
|
|
4627
4746
|
* offers the source's whole list). Destination-wins per id: a style this deck
|
|
4628
4747
|
* already defines is left untouched, so a re-call adds nothing. Returns whether
|
|
@@ -4644,14 +4763,14 @@ function addTableStyles(destList, styles) {
|
|
|
4644
4763
|
* Copy the source deck's **whole** `tableStyles.xml` into this deck: every
|
|
4645
4764
|
* `<a:tblStyle>` it defines, plus its `a:tblStyleLst@def` (the default table
|
|
4646
4765
|
* style). Presentation-level, so a separate traversal from the master/layout copy
|
|
4647
|
-
* chain
|
|
4766
|
+
* chain, and whole-deck, since `tableStyles.xml` does not record which style
|
|
4648
4767
|
* belongs to which master. See `ImportSlideMastersOptions.tableStyles`.
|
|
4649
4768
|
*
|
|
4650
4769
|
* Styles union by `styleId` (destination-wins per id, via {@link addTableStyles}),
|
|
4651
4770
|
* but `def` is **source-wins**: a caller asking for the source's table styling
|
|
4652
4771
|
* wants its default too, and the destination's `def` is typically a generator stub
|
|
4653
4772
|
* rather than a deliberate choice. Carrying the styles without the `def` would be
|
|
4654
|
-
* the worse half-measure
|
|
4773
|
+
* the worse half-measure: the standard default GUID is one most templates also
|
|
4655
4774
|
* define, so a new table would silently resolve to the wrong style rather than
|
|
4656
4775
|
* visibly to none. Idempotent: a re-call re-adds nothing and re-sets `def` to the
|
|
4657
4776
|
* same value.
|
|
@@ -4688,26 +4807,25 @@ function ensureTableStylesPart(dest) {
|
|
|
4688
4807
|
dest.opc.relationshipsFor(dest.presentationPart.partName).add(TABLE_STYLES_REL, relativePartName(dest.presentationPart.partName, partName));
|
|
4689
4808
|
return part;
|
|
4690
4809
|
}
|
|
4691
|
-
|
|
4692
|
-
//#region src/read/api/ops/import-slide.ts
|
|
4810
|
+
// src/read/api/ops/import-slide.ts
|
|
4693
4811
|
/**
|
|
4694
4812
|
* The three ways a slide can be brought across from another deck.
|
|
4695
4813
|
*
|
|
4696
4814
|
* {@link Presentation.importSlide} picks one by `options.theme`, and the difference between
|
|
4697
4815
|
* them is entirely about what happens to the slide's *look*:
|
|
4698
4816
|
*
|
|
4699
|
-
* - **`copy
|
|
4817
|
+
* - **`copy`**: not here. It copies the source theme subgraph wholesale via `copyPart`, so
|
|
4700
4818
|
* the slide keeps its look by keeping its own theme; nothing needs rewriting.
|
|
4701
|
-
* - **`preserve`** ({@link importSlidePreserve})
|
|
4819
|
+
* - **`preserve`** ({@link importSlidePreserve}): rebind to this deck's master, then bake
|
|
4702
4820
|
* the source theme into the slide XML. The slide looks the same as it did; it just no
|
|
4703
4821
|
* longer depends on the theme it came from.
|
|
4704
|
-
* - **`restyle`** ({@link importSlideRestyle})
|
|
4822
|
+
* - **`restyle`** ({@link importSlideRestyle}): rebind and bake *nothing*, so the slide's
|
|
4705
4823
|
* symbolic theme references re-resolve against the destination theme and it re-brands.
|
|
4706
4824
|
*
|
|
4707
4825
|
* The two share {@link importSlideRebind} and are exact inverses on top of it: preserve
|
|
4708
4826
|
* resolves every reference it can, restyle resolves none. Any baking added to the shared
|
|
4709
4827
|
* rebind would break restyle by pinning the slide to its source look, which is why the
|
|
4710
|
-
* rebind carries no theme handling of its own
|
|
4828
|
+
* rebind carries no theme handling of its own, not even the inherited background.
|
|
4711
4829
|
*
|
|
4712
4830
|
* These were private methods on `Presentation`, where they made the read model's own surface
|
|
4713
4831
|
* (open a deck, walk slides, read shapes) hard to see: the import machinery was roughly two
|
|
@@ -4738,17 +4856,17 @@ function importSlidePreserve(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
|
4738
4856
|
}
|
|
4739
4857
|
/**
|
|
4740
4858
|
* Import a slide in `restyle` mode: rebind it to this deck's master/layout and then
|
|
4741
|
-
* {@link restyleSlide} it
|
|
4859
|
+
* {@link restyleSlide} it: drop its colour map override but bake *nothing*, so its symbolic
|
|
4742
4860
|
* theme references re-resolve against the destination theme and the slide re-brands.
|
|
4743
4861
|
*
|
|
4744
4862
|
* The deliberate inverse of `preserve`: no flatten, no inherited-background bake, no
|
|
4745
|
-
* placeholder colour/size/geometry bake
|
|
4863
|
+
* placeholder colour/size/geometry bake. Every one of those would pin the slide to its
|
|
4746
4864
|
* source look, the opposite of re-branding. Carried decorations are left symbolic too, so
|
|
4747
4865
|
* they re-brand along with the slide.
|
|
4748
4866
|
*
|
|
4749
4867
|
* With `remapLiterals` it additionally force-remaps the slide's source-theme literal colours
|
|
4750
|
-
* back to symbolic scheme colours and copies any referenced source table style into this deck
|
|
4751
|
-
*
|
|
4868
|
+
* back to symbolic scheme colours and copies any referenced source table style into this deck:
|
|
4869
|
+
* the two things plain `restyle` cannot re-brand.
|
|
4752
4870
|
* @param {Presentation} dest - the destination deck
|
|
4753
4871
|
* @param {ImportContext} ctx - the open import out of the source package
|
|
4754
4872
|
* @param {Presentation} source - the source deck
|
|
@@ -4774,11 +4892,11 @@ function importSlideRestyle(dest, ctx, source, sourceSlide, carryGraphics, remap
|
|
|
4774
4892
|
/**
|
|
4775
4893
|
* The rebind shared by `preserve` and `restyle`: copy the slide bytes into a fresh part,
|
|
4776
4894
|
* rebuild its relationships (drop notes, repoint the `slideLayout` rel at this deck's
|
|
4777
|
-
* existing layout, copy every other internal target
|
|
4895
|
+
* existing layout, copy every other internal target (media/charts), and pass externals
|
|
4778
4896
|
* through), and optionally bake the source master/layout decorations onto the slide. Returns
|
|
4779
4897
|
* the new part, its name, and its live root element for the caller's mode-specific pass.
|
|
4780
4898
|
*
|
|
4781
|
-
* This carries *no* theme baking of its own
|
|
4899
|
+
* This carries *no* theme baking of its own, not even the inherited background.
|
|
4782
4900
|
* `preserve` adds that via {@link flattenSlide}'s context; `restyle` must not, so the
|
|
4783
4901
|
* background stays symbolic and re-brands.
|
|
4784
4902
|
* @param {Presentation} dest - the destination deck
|
|
@@ -4839,8 +4957,8 @@ function rebind(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
|
4839
4957
|
/**
|
|
4840
4958
|
* Bake the source `slideLayout`/`slideMaster` shape-tree decorations onto the imported slide
|
|
4841
4959
|
* (the `carryMasterGraphics` path). Every shape on those trees *except* placeholders is
|
|
4842
|
-
* deep-copied into the slide's `p:spTree` ahead of its own content
|
|
4843
|
-
* first, then layout, then the slide's shapes
|
|
4960
|
+
* deep-copied into the slide's `p:spTree` ahead of its own content (master decorations
|
|
4961
|
+
* first, then layout, then the slide's shapes), so document (z-)order keeps the master
|
|
4844
4962
|
* furthest back. Each decoration's media and other relationship targets are copied into this
|
|
4845
4963
|
* package and its `r:embed`/`r:id`/… references rewritten to fresh slide-local ids. The
|
|
4846
4964
|
* injected shapes are left for the caller's {@link flattenSlide} pass to resolve any theme
|
|
@@ -4893,8 +5011,7 @@ function destinationLayoutPartName(dest) {
|
|
|
4893
5011
|
if (!layoutPartName) throw new InvalidOptionError("import/destination-missing-layout", "importSlide with theme 'preserve' or 'restyle' requires a slide layout in the destination deck");
|
|
4894
5012
|
return layoutPartName;
|
|
4895
5013
|
}
|
|
4896
|
-
|
|
4897
|
-
//#region src/read/api/ops/rescale.ts
|
|
5014
|
+
// src/read/api/ops/rescale.ts
|
|
4898
5015
|
/** Build the EMU transform mapping source-canvas coordinates onto the target canvas (see {@link ImportSlideOptions.rescale}). */
|
|
4899
5016
|
function computeRescale(source, target, mode) {
|
|
4900
5017
|
if (mode === "stretch") return {
|
|
@@ -4913,7 +5030,7 @@ function computeRescale(source, target, mode) {
|
|
|
4913
5030
|
}
|
|
4914
5031
|
/**
|
|
4915
5032
|
* Rescale every top-level transform on a `p:spTree`: shapes/pictures/connectors
|
|
4916
|
-
* (`p:spPr/a:xfrm`), groups (`p:grpSpPr/a:xfrm
|
|
5033
|
+
* (`p:spPr/a:xfrm`), groups (`p:grpSpPr/a:xfrm`, only the group's own off/ext, so
|
|
4917
5034
|
* its children remap via the unchanged `chOff`/`chExt` rather than being recursed
|
|
4918
5035
|
* into), and graphic frames (`p:graphicFrame/p:xfrm`, plus any inner table grid).
|
|
4919
5036
|
* Placeholders that inherit geometry carry no `a:xfrm` and are left to inherit from
|
|
@@ -4962,8 +5079,8 @@ function rescaleXfrm(xfrm, t) {
|
|
|
4962
5079
|
}
|
|
4963
5080
|
}
|
|
4964
5081
|
/**
|
|
4965
|
-
* Scale a graphic-frame table's intrinsic dimensions
|
|
4966
|
-
* heights by `sy`
|
|
5082
|
+
* Scale a graphic-frame table's intrinsic dimensions (column widths by `sx`, row
|
|
5083
|
+
* heights by `sy`) so the table resizes with the slide (PowerPoint derives table
|
|
4967
5084
|
* geometry from the grid, not the frame `a:ext`). No-op for non-table frames.
|
|
4968
5085
|
*/
|
|
4969
5086
|
function rescaleTableGrid(graphicFrame, t) {
|
|
@@ -4981,8 +5098,7 @@ function rescaleTableGrid(graphicFrame, t) {
|
|
|
4981
5098
|
if (h !== null) setAttr(tr, "h", String(Math.max(0, Math.round(h * t.sy))));
|
|
4982
5099
|
}
|
|
4983
5100
|
}
|
|
4984
|
-
|
|
4985
|
-
//#region src/read/api/ops/rescale-import.ts
|
|
5101
|
+
// src/read/api/ops/rescale-import.ts
|
|
4986
5102
|
/**
|
|
4987
5103
|
* Rescaling an imported slide's geometry onto the destination canvas.
|
|
4988
5104
|
*
|
|
@@ -4999,8 +5115,8 @@ function rescaleTableGrid(graphicFrame, t) {
|
|
|
4999
5115
|
* graphicFrame transform and table grid on the slide; in `copy` mode also
|
|
5000
5116
|
* rescales the imported layout and master shape trees (resolved via the
|
|
5001
5117
|
* slide → layout → master rel chain) so inherited placeholder/background geometry
|
|
5002
|
-
* stays aligned. `preserve`/`restyle` rebind to this deck's own master/layout
|
|
5003
|
-
* already the destination size
|
|
5118
|
+
* stays aligned. `preserve`/`restyle` rebind to this deck's own master/layout,
|
|
5119
|
+
* already the destination size, so only the slide is touched. Geometry only:
|
|
5004
5120
|
* font sizes and line widths are left as authored.
|
|
5005
5121
|
*/
|
|
5006
5122
|
function rescaleImportedGeometry(dest, rescaledParts, slidePartName, theme, source, target, mode) {
|
|
@@ -5052,8 +5168,7 @@ function rescalePartGeometry(dest, rescaledParts, partName, transform, mode) {
|
|
|
5052
5168
|
rescaleSpTree(spTree, transform);
|
|
5053
5169
|
part.markDirty();
|
|
5054
5170
|
}
|
|
5055
|
-
|
|
5056
|
-
//#region src/read/api/presentation-imports.ts
|
|
5171
|
+
// src/read/api/presentation-imports.ts
|
|
5057
5172
|
/** Body of {@link Presentation.importSlide}, whose doc comment carries the contract. */
|
|
5058
5173
|
function importSlide(deck, source, index, options = {}) {
|
|
5059
5174
|
const sourceSlide = source.slides[index];
|
|
@@ -5071,6 +5186,7 @@ function importSlide(deck, source, index, options = {}) {
|
|
|
5071
5186
|
const newPart = deck.opc.part(newPartName);
|
|
5072
5187
|
if (!newPart) throw new InternalError("import/part-went-missing", `Imported slide part went missing: ${newPartName}`);
|
|
5073
5188
|
if (sizesDiffer && options.rescale && target && incoming) rescaleImportedGeometry(deck, deck.rescaledParts, newPartName, options.theme, incoming, target, options.rescale === true ? "fit" : options.rescale);
|
|
5189
|
+
carryCommentAuthors(deck, source, newPartName);
|
|
5074
5190
|
const slide = deck.insertSlidePart(newPart, options.at);
|
|
5075
5191
|
if (options.importNotes) carryNotes(deck, source, importCtx, sourceSlide.partName, newPartName);
|
|
5076
5192
|
if (options.embedFonts) carryEmbeddedFonts(deck, source, importCtx);
|
|
@@ -5125,6 +5241,7 @@ function importSlides(deck, requests) {
|
|
|
5125
5241
|
destPart
|
|
5126
5242
|
};
|
|
5127
5243
|
});
|
|
5244
|
+
for (const request of planned) carryCommentAuthors(deck, request.source, request.destPartName);
|
|
5128
5245
|
for (const request of planned) {
|
|
5129
5246
|
const mode = rescaleBySource.get(request.source.opc);
|
|
5130
5247
|
if (!mode) continue;
|
|
@@ -5318,14 +5435,13 @@ function importShapes(deck, target, source, shapeIndices, options = {}) {
|
|
|
5318
5435
|
}
|
|
5319
5436
|
return result;
|
|
5320
5437
|
}
|
|
5321
|
-
|
|
5322
|
-
//#region src/read/api/presentation.ts
|
|
5438
|
+
// src/read/api/presentation.ts
|
|
5323
5439
|
/**
|
|
5324
5440
|
* Read-model entry point: `Presentation` wraps an `OpcPackage` and exposes a
|
|
5325
5441
|
* navigable, typed view of the deck (slides → shapes → text), backed by the
|
|
5326
5442
|
* live DOM so the same nodes can later be mutated.
|
|
5327
5443
|
*
|
|
5328
|
-
* The class holds the model and the deck-level plumbing every operation needs
|
|
5444
|
+
* The class holds the model and the deck-level plumbing every operation needs: the part
|
|
5329
5445
|
* index, the slide list, the copy registry that lets repeated imports from one source share
|
|
5330
5446
|
* what they already copied, and the `p:sldIdLst` wiring. The operations themselves live in
|
|
5331
5447
|
* `ops/`, one job per module, taking this deck as an argument. The slide-import machinery
|
|
@@ -5335,11 +5451,11 @@ function importShapes(deck, target, source, shapeIndices, options = {}) {
|
|
|
5335
5451
|
* The four import entry points went the same way, and {@link appendSlides} after them: their
|
|
5336
5452
|
* contracts stay here as the doc comments a caller reads, and their bodies live in
|
|
5337
5453
|
* `presentation-imports.ts` and `ops/append-slides.ts`. Those bodies are the one thing in this
|
|
5338
|
-
* directory that is not independent of this class
|
|
5454
|
+
* directory that is not independent of this class. They reach back for {@link importContext},
|
|
5339
5455
|
* {@link insertSlidePart} and {@link rescaledParts}, which {@link cloneSlide} reads too and so
|
|
5340
5456
|
* could not travel with them. Those three are `@internal` for that and nothing else.
|
|
5341
5457
|
*/
|
|
5342
|
-
/** Content type of the main part in a `.potx` template package
|
|
5458
|
+
/** Content type of the main part in a `.potx` template package, flipped to {@link PRESENTATION_MAIN_CONTENT_TYPE} by {@link Presentation.fromTemplate}. */
|
|
5343
5459
|
const PRESENTATION_TEMPLATE_MAIN_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml";
|
|
5344
5460
|
var Presentation = class Presentation {
|
|
5345
5461
|
opc;
|
|
@@ -5393,6 +5509,7 @@ var Presentation = class Presentation {
|
|
|
5393
5509
|
if (mainPart.contentType === PRESENTATION_TEMPLATE_MAIN_CONTENT_TYPE) pres.opc.contentTypes.ensureRegistered(mainPart.partName, PRESENTATION_MAIN_CONTENT_TYPE);
|
|
5394
5510
|
}
|
|
5395
5511
|
while (pres.slides.length > 0) pres.removeSlide(0);
|
|
5512
|
+
resetCommentAuthorIndexes(pres);
|
|
5396
5513
|
return pres;
|
|
5397
5514
|
}
|
|
5398
5515
|
/** The main presentation part (`/ppt/presentation.xml`), resolved via the package `officeDocument` relationship. */
|
|
@@ -5444,7 +5561,7 @@ var Presentation = class Presentation {
|
|
|
5444
5561
|
/**
|
|
5445
5562
|
* The deck's embedded font families (`p:embeddedFontLst` in `presentation.xml`),
|
|
5446
5563
|
* `[]` when it embeds none. Each entry names the typeface and resolves every
|
|
5447
|
-
* embedded face's `r:id` to the absolute partname of its `.fntdata` binary
|
|
5564
|
+
* embedded face's `r:id` to the absolute partname of its `.fntdata` binary: the
|
|
5448
5565
|
* read counterpart to the write-side `pptx.embedFont` / `importSlide({ embedFonts })`
|
|
5449
5566
|
* carry. An entry whose `p:font` has no `@typeface`, or a face whose `r:id` is
|
|
5450
5567
|
* missing or dangling, is skipped (faithful degradation, no throw). Read-only.
|
|
@@ -5474,7 +5591,7 @@ var Presentation = class Presentation {
|
|
|
5474
5591
|
* `ppt/authors.xml`), `[]` when the deck carries no modern comments. Unlike the
|
|
5475
5592
|
* legacy {@link commentAuthors}, each entry is keyed by a GUID `id` (plus
|
|
5476
5593
|
* `userId`/`providerId`); each slide's {@link Slide.modernComments} resolves its
|
|
5477
|
-
* `@authorId` against this list. Read-only
|
|
5594
|
+
* `@authorId` against this list. Read-only: the writer emits legacy comments.
|
|
5478
5595
|
*/
|
|
5479
5596
|
get modernCommentAuthors() {
|
|
5480
5597
|
return readModernCommentAuthors(this.opc, this.presentationPart.partName);
|
|
@@ -5504,7 +5621,7 @@ var Presentation = class Presentation {
|
|
|
5504
5621
|
* The deck's extended document properties (`docProps/app.xml`): the producing
|
|
5505
5622
|
* application and its version, the company, and the flat `TitlesOfParts` vector.
|
|
5506
5623
|
* `{}` when the deck carries no extended-properties part. The read counterpart of
|
|
5507
|
-
* the write-side `pptx.company` setter
|
|
5624
|
+
* the write-side `pptx.company` setter, the only one of the four this library
|
|
5508
5625
|
* writes from a caller's value; the other three it states about itself.
|
|
5509
5626
|
*
|
|
5510
5627
|
* A deliberate subset of the part: see {@link ExtendedProperties} for why the
|
|
@@ -5526,7 +5643,7 @@ var Presentation = class Presentation {
|
|
|
5526
5643
|
* The deck-level programmatic tags (`p:custDataLst/p:tags` on `presentation.xml`,
|
|
5527
5644
|
* resolved to `ppt/tags/tagN.xml`) as `{ name, val }` string pairs, `[]` when the
|
|
5528
5645
|
* deck carries none. These are add-in/host metadata with no visible rendering and
|
|
5529
|
-
* no writer
|
|
5646
|
+
* no writer. They are read-only, preserved byte-for-byte on round-trip. Per-slide tags are
|
|
5530
5647
|
* {@link Slide.tags}.
|
|
5531
5648
|
*/
|
|
5532
5649
|
get tags() {
|
|
@@ -5545,7 +5662,7 @@ var Presentation = class Presentation {
|
|
|
5545
5662
|
* charts, SmartArt diagrams and OLE embeddings, each with the subtree under it
|
|
5546
5663
|
* (a chart's embedded workbook comes along; the image inside its user-shapes
|
|
5547
5664
|
* drawing stays shared). PowerPoint refuses to open a deck where two slides
|
|
5548
|
-
* resolve to one chart or diagram, so this is not tidiness
|
|
5665
|
+
* resolve to one chart or diagram, so this is not tidiness; see
|
|
5549
5666
|
* `page-owned.ts` for the rule and the evidence behind it.
|
|
5550
5667
|
*/
|
|
5551
5668
|
cloneSlide(index, options = {}) {
|
|
@@ -5563,7 +5680,7 @@ var Presentation = class Presentation {
|
|
|
5563
5680
|
* `p:sldId` entry and the presentation→slide relationship are dropped, the slide
|
|
5564
5681
|
* part and its `.rels` are deleted, and any part the slide *privately* owned
|
|
5565
5682
|
* (its notes slide, slide-only media, charts/embeddings) that no remaining part
|
|
5566
|
-
* references is pruned too
|
|
5683
|
+
* references is pruned too, recursively. Shared deck chrome (layout, master,
|
|
5567
5684
|
* theme, …) is never pruned, so the deck stays renderable; removing every slide
|
|
5568
5685
|
* leaves a valid master/layout-only package (a template shell). The slide also
|
|
5569
5686
|
* leaves every custom show and section it was in, which stay even when that
|
|
@@ -5603,9 +5720,9 @@ var Presentation = class Presentation {
|
|
|
5603
5720
|
* Append a copy of `source.slides[index]` to this presentation and return it.
|
|
5604
5721
|
*
|
|
5605
5722
|
* Unlike {@link cloneSlide} (same-deck duplicate), this copies a slide across
|
|
5606
|
-
* a package boundary: it brings the connected sub-graph the slide depends on
|
|
5607
|
-
* its `slideLayout` → `slideMaster` → `theme`, plus any media, charts, and
|
|
5608
|
-
* embeddings
|
|
5723
|
+
* a package boundary: it brings the connected sub-graph the slide depends on
|
|
5724
|
+
* (its `slideLayout` → `slideMaster` → `theme`, plus any media, charts, and
|
|
5725
|
+
* embeddings) into this package under fresh partnames, rewriting every
|
|
5609
5726
|
* partname, relationship id, and content-type registration so the result is a
|
|
5610
5727
|
* self-consistent OPC package. Parts of this (target) package that are not
|
|
5611
5728
|
* touched stay byte-identical, matching `cloneSlide`'s fidelity contract.
|
|
@@ -5613,7 +5730,7 @@ var Presentation = class Presentation {
|
|
|
5613
5730
|
* Only the layout(s) actually used by imported slides are copied; the imported
|
|
5614
5731
|
* master's `p:sldLayoutIdLst` is pruned to exactly those, mirroring how
|
|
5615
5732
|
* PowerPoint's "Reuse Slides" brings a slide across. Parts shared by repeated
|
|
5616
|
-
* imports from the same source deck are copied once and reused
|
|
5733
|
+
* imports from the same source deck are copied once and reused: every part
|
|
5617
5734
|
* except the page itself. Importing the same source slide twice yields two
|
|
5618
5735
|
* independent copies over one shared layout/master/theme, which is what lets a
|
|
5619
5736
|
* deck show one page twice (verbatim, then edited).
|
|
@@ -5622,7 +5739,7 @@ var Presentation = class Presentation {
|
|
|
5622
5739
|
* into the slide XML and the slide is bound to this deck's existing
|
|
5623
5740
|
* master/layout; with `{ theme: 'restyle' }` the slide is bound to this deck's
|
|
5624
5741
|
* master/layout with its theme references left symbolic, so it re-brands to the
|
|
5625
|
-
* destination palette
|
|
5742
|
+
* destination palette. See {@link ImportSlideOptions}.
|
|
5626
5743
|
*
|
|
5627
5744
|
* By default the source slide size must equal this presentation's. Pass
|
|
5628
5745
|
* `{ rescale: 'fit' | 'stretch' }` to rescale the imported geometry onto this
|
|
@@ -5641,7 +5758,7 @@ var Presentation = class Presentation {
|
|
|
5641
5758
|
* Import selected pages from one or more loaded source presentations as one
|
|
5642
5759
|
* batch. Each imported page lands at its `outputIndex` in the complete
|
|
5643
5760
|
* destination slide list after the batch, and the returned array is parallel
|
|
5644
|
-
* to `requests
|
|
5761
|
+
* to `requests`: `result[i]` is the page `requests[i]` asked for, whatever
|
|
5645
5762
|
* order the output positions were given in.
|
|
5646
5763
|
*
|
|
5647
5764
|
* Everything is checked before a single byte of this deck moves: every request
|
|
@@ -5654,13 +5771,13 @@ var Presentation = class Presentation {
|
|
|
5654
5771
|
* The batch also decides what a `slide → slide` link means: an internal link
|
|
5655
5772
|
* on a selected page must target another **selected** page (or one this deck
|
|
5656
5773
|
* already contains via an earlier import from that source), and is rewritten
|
|
5657
|
-
* to the fresh partname
|
|
5774
|
+
* to the fresh partname. Importing page 3 of 10 does not drag pages 1–2
|
|
5658
5775
|
* across as dependencies, and never strands the link. This mirrors
|
|
5659
5776
|
* `appendSlides`' `import/unresolved-slide-link`, which the write side already
|
|
5660
5777
|
* enforces for generator decks.
|
|
5661
5778
|
*
|
|
5662
5779
|
* One request is one output page, so naming the same source page in several
|
|
5663
|
-
* requests is how you ask for several independent copies of it
|
|
5780
|
+
* requests is how you ask for several independent copies of it; the page part
|
|
5664
5781
|
* is the one thing an import never shares, exactly as in {@link importSlide}.
|
|
5665
5782
|
* Everything under it (layout, master, theme, media) is still copied once and
|
|
5666
5783
|
* shared. Where such a page is the target of a `slide → slide` link, the link
|
|
@@ -5671,7 +5788,7 @@ var Presentation = class Presentation {
|
|
|
5671
5788
|
* Speaker notes travel per request: `{ importNotes: true }` carries that page's
|
|
5672
5789
|
* `notesSlide` part across, wired to the new page and bound to a single
|
|
5673
5790
|
* `notesMaster` under the same 0..1 rule {@link importSlide} and
|
|
5674
|
-
* {@link appendSlides} follow
|
|
5791
|
+
* {@link appendSlides} follow: the destination's own master wins when it has
|
|
5675
5792
|
* one. Notes are part of the up-front dry run too, so a batch that would fail
|
|
5676
5793
|
* carrying them is refused with the deck still byte-identical. A page named in
|
|
5677
5794
|
* several requests gets its own copy of its notes each time, as of everything
|
|
@@ -5698,9 +5815,9 @@ var Presentation = class Presentation {
|
|
|
5698
5815
|
}
|
|
5699
5816
|
/**
|
|
5700
5817
|
* Graft slide master(s) from another open package into this one and return what
|
|
5701
|
-
* was copied. Unlike {@link importSlide}
|
|
5818
|
+
* was copied. Unlike {@link importSlide}, which brings a master across only as
|
|
5702
5819
|
* the dependency of an imported *slide* and prunes it to the one layout that
|
|
5703
|
-
* slide uses
|
|
5820
|
+
* slide uses, this copies a master together with its **whole** layout family
|
|
5704
5821
|
* and attaches it to no slide: the master and its layouts land in this deck's
|
|
5705
5822
|
* layout gallery (PowerPoint's *Insert ▸ New Slide* / *Layout* picker) without
|
|
5706
5823
|
* changing any existing slide.
|
|
@@ -5732,7 +5849,7 @@ var Presentation = class Presentation {
|
|
|
5732
5849
|
}
|
|
5733
5850
|
/**
|
|
5734
5851
|
* The deck's slide masters, in `p:sldMasterIdLst` order, as modeled
|
|
5735
|
-
* {@link SlideMaster}s
|
|
5852
|
+
* {@link SlideMaster}s: the typed read model over the shared chrome (each
|
|
5736
5853
|
* master's colour map, theme, placeholders, and the layouts built on it). Walk
|
|
5737
5854
|
* `pres.masters()[i].layouts` for the rich layout model, or use {@link layouts}
|
|
5738
5855
|
* for the flat {@link LayoutHandle} gallery {@link appendSlides} binds to.
|
|
@@ -5742,7 +5859,7 @@ var Presentation = class Presentation {
|
|
|
5742
5859
|
return slideMasterPartNames(this).map((partName) => this.opc.part(partName)).filter((part) => part !== void 0).map((part) => new SlideMaster(this.opc, part));
|
|
5743
5860
|
}
|
|
5744
5861
|
/**
|
|
5745
|
-
* The deck's slide layouts, in master then layout order
|
|
5862
|
+
* The deck's slide layouts, in master then layout order: the gallery a new
|
|
5746
5863
|
* slide can bind to. Each {@link LayoutHandle} addresses one layout for
|
|
5747
5864
|
* {@link appendSlides}; the `name` is its `p:cSld@name`. Read-only enumeration:
|
|
5748
5865
|
* it copies nothing and leaves the package byte-identical.
|
|
@@ -5765,12 +5882,12 @@ var Presentation = class Presentation {
|
|
|
5765
5882
|
/**
|
|
5766
5883
|
* Append generator-produced slides onto this deck, binding each to an existing
|
|
5767
5884
|
* layout, and return the new {@link Slide}s. This is the hybrid
|
|
5768
|
-
* "generate-onto-existing" path: the deck's masters, layouts, theme
|
|
5769
|
-
* other untouched part
|
|
5885
|
+
* "generate-onto-existing" path: the deck's masters, layouts, theme, and every
|
|
5886
|
+
* other untouched part stay **byte-identical** (only `presentation.xml`, its
|
|
5770
5887
|
* `.rels`, `[Content_Types].xml`, and the freshly-added slide/media parts
|
|
5771
5888
|
* change), because the existing chrome is never regenerated.
|
|
5772
5889
|
*
|
|
5773
|
-
* `source` is any slide producer (a `
|
|
5890
|
+
* `source` is any slide producer (a `PptxTs` instance); its authored slides
|
|
5774
5891
|
* are serialized via {@link SlideSource.extractSlides} and spliced in under
|
|
5775
5892
|
* fresh partnames, with each slide's `slideLayout` relationship pointed at the
|
|
5776
5893
|
* layout named by `options.layout` and its image/hyperlink relationships rebuilt
|
|
@@ -5780,13 +5897,13 @@ var Presentation = class Presentation {
|
|
|
5780
5897
|
* Charts and internal slide-to-slide hyperlinks are carried across: chart parts
|
|
5781
5898
|
* (chart XML + `.rels` + embedded workbook) are injected under fresh names, and a
|
|
5782
5899
|
* `slide:N` link is repointed at the Nth appended slide's new partname. A chartEx
|
|
5783
|
-
* (Office 2016
|
|
5900
|
+
* (Office 2016: waterfall, funnel, treemap, ...) chart carries too, as its own
|
|
5784
5901
|
* `chartEx{N}.xml` part behind the MS chartEx rel, with the style and color-style
|
|
5785
5902
|
* sidecars PowerPoint requires alongside it.
|
|
5786
5903
|
*
|
|
5787
5904
|
* The generator's presentation-level embedded fonts (`pptx.embedFont`) are also
|
|
5788
5905
|
* carried into this deck and merged into its `p:embeddedFontLst`, de-duped by
|
|
5789
|
-
* typeface + face slot
|
|
5906
|
+
* typeface + face slot, so author-side embedded fonts survive the append onto a
|
|
5790
5907
|
* template that may itself already embed fonts.
|
|
5791
5908
|
*
|
|
5792
5909
|
* Embedded audio/video is carried too: the media part backs the ECMA audio/video rel
|
|
@@ -5797,24 +5914,28 @@ var Presentation = class Presentation {
|
|
|
5797
5914
|
* Speaker notes ride across too: a slide authored with `addNotes` gets a `notesSlide`
|
|
5798
5915
|
* part wired back to it, with its hyperlink rels preserved. A notes slide must bind to
|
|
5799
5916
|
* a notes master, and a template commonly has none, so the generator's is installed
|
|
5800
|
-
* (with the theme its `.rels` needs)
|
|
5917
|
+
* (with the theme its `.rels` needs), but only when this deck has none of its own, so
|
|
5801
5918
|
* an existing notes master and its styling always win.
|
|
5802
5919
|
*
|
|
5920
|
+
* Review comments (`addComment`) ride across as a comments part per slide. Their
|
|
5921
|
+
* authors merge into this deck's `commentAuthors.xml` by name and initials: an author
|
|
5922
|
+
* the deck already has keeps its id and continues its numbering, and a new one is added.
|
|
5923
|
+
*
|
|
5803
5924
|
* Limitations:
|
|
5804
5925
|
* - An internal link to a source slide outside the appended batch throws (its
|
|
5805
5926
|
* target has no counterpart in the destination).
|
|
5806
5927
|
* - Appended slides are concrete absolute-positioned content with no placeholder
|
|
5807
5928
|
* inheritance from the bound layout; the binding governs theme/`clrMap`
|
|
5808
5929
|
* resolution and the "based on" link, not placeholder geometry. Author with
|
|
5809
|
-
* concrete colours
|
|
5930
|
+
* concrete colours: any `schemeClr` re-resolves against the destination theme.
|
|
5810
5931
|
* - Source and destination slide sizes must match (no geometry rescale).
|
|
5811
5932
|
*/
|
|
5812
5933
|
async appendSlides(source, options) {
|
|
5813
5934
|
return appendSlides(this, source, options);
|
|
5814
5935
|
}
|
|
5815
5936
|
/**
|
|
5816
|
-
* Copy one shape
|
|
5817
|
-
* or group
|
|
5937
|
+
* Copy one shape (an autoshape, picture, table/chart graphic frame, connector,
|
|
5938
|
+
* or group) from `source.shapes[shapeIndex]` onto `target`, returning the new
|
|
5818
5939
|
* {@link Shape}. `target` must be a slide of *this* presentation; `source` may
|
|
5819
5940
|
* belong to any open presentation.
|
|
5820
5941
|
*
|
|
@@ -5825,7 +5946,7 @@ var Presentation = class Presentation {
|
|
|
5825
5946
|
* so they cannot collide with the host. With `theme: 'preserve'` (default) the
|
|
5826
5947
|
* shape's theme references are baked to literals against the *source* theme so it
|
|
5827
5948
|
* renders the same on a foreign host; `restyle` leaves them symbolic to re-brand;
|
|
5828
|
-
* `copy` brings the XML across untouched
|
|
5949
|
+
* `copy` brings the XML across untouched. See {@link ImportShapeOptions}.
|
|
5829
5950
|
*
|
|
5830
5951
|
* Differing slide sizes need `{ rescale }` (see {@link ImportShapeOptions.rescale});
|
|
5831
5952
|
* a lifted `preserve` placeholder is baked self-contained and demoted to a plain
|
|
@@ -5857,7 +5978,7 @@ var Presentation = class Presentation {
|
|
|
5857
5978
|
}
|
|
5858
5979
|
/**
|
|
5859
5980
|
* @internal The parts whose geometry a rescale has already rewritten, and in which mode.
|
|
5860
|
-
* Exposed for `presentation-imports.ts` as a live map the callee adds to
|
|
5981
|
+
* Exposed for `presentation-imports.ts` as a live map the callee adds to. The memo is what
|
|
5861
5982
|
* makes a layout or master shared across repeated imports from one source scale exactly
|
|
5862
5983
|
* once, so like the copy registry it has to outlive the call that fills it.
|
|
5863
5984
|
*/
|
|
@@ -5867,7 +5988,7 @@ var Presentation = class Presentation {
|
|
|
5867
5988
|
/**
|
|
5868
5989
|
* @internal Open an import out of `source`: this deck as the destination, paired with
|
|
5869
5990
|
* the copy registry for that package (created on first use). The registry is held on
|
|
5870
|
-
* the class rather than in the context because it must outlive any one call
|
|
5991
|
+
* the class rather than in the context because it must outlive any one call: that is
|
|
5871
5992
|
* what makes a second import from the same source reuse the layout, master, theme, and
|
|
5872
5993
|
* media it already copied instead of duplicating them, and it is why the import bodies
|
|
5873
5994
|
* in `presentation-imports.ts` call back here rather than carrying it with them.
|
|
@@ -5914,7 +6035,7 @@ var Presentation = class Presentation {
|
|
|
5914
6035
|
* A slide id one past the highest existing, but at least ST_SlideId's minimum.
|
|
5915
6036
|
*
|
|
5916
6037
|
* The module named and enforced that minimum and not the maximum, so a deck near the ceiling
|
|
5917
|
-
* got an out-of-range `p:sldId/@id` written with no diagnostic
|
|
6038
|
+
* got an out-of-range `p:sldId/@id` written with no diagnostic. The package then needs
|
|
5918
6039
|
* repair, for a reason nothing reported. Past the ceiling there is no "next" id to hand out,
|
|
5919
6040
|
* so the allocation falls back to the lowest id in range that the deck is not already using;
|
|
5920
6041
|
* a deck holding every one of the two billion has no legal answer and says so.
|
|
@@ -5937,7 +6058,6 @@ var Presentation = class Presentation {
|
|
|
5937
6058
|
return this.opc.save();
|
|
5938
6059
|
}
|
|
5939
6060
|
};
|
|
5940
|
-
//#endregion
|
|
5941
6061
|
export { Slide as a, NotesSlide as c, SlideMaster as d, Theme as f, ContentTypes as g, Part as h, readExtendedProperties as i, Placeholder as l, Relationships as m, readCoreProperties as n, readTagsForPart as o, OpcPackage as p, readCustomProperties as r, NotesPlaceholder as s, Presentation as t, SlideLayout as u };
|
|
5942
6062
|
|
|
5943
|
-
//# sourceMappingURL=presentation-
|
|
6063
|
+
//# sourceMappingURL=presentation-4Ggq0_Xa.js.map
|