@shbernal/ts-pptx 4.0.1 → 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 +147 -114
- package/dist/anim-presets-Qbgh8uut.js +55 -0
- package/dist/anim-presets-Qbgh8uut.js.map +1 -0
- package/dist/{autopage-odPz7zZO.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-Dw_BeWD_.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-CvN2iCA4.js → chart-metadata-CwiTTcJq.js} +634 -537
- package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
- package/dist/{clip-Bl01BPcN.d.ts → clip-ISfCMtWA.d.ts} +6 -10
- package/dist/clip-ISfCMtWA.d.ts.map +1 -0
- package/dist/{notes-Dh12JAbn.js → comments-CKIZYRbZ.js} +530 -401
- package/dist/comments-CKIZYRbZ.js.map +1 -0
- package/dist/{diagnostics-D8ot4Ei7.d.ts → diagnostics-MoA4MBq5.d.ts} +55 -53
- package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
- package/dist/{embedded-fonts-DarT9aoa.d.ts → embedded-fonts-U7HIIhPO.d.ts} +4 -6
- package/dist/embedded-fonts-U7HIIhPO.d.ts.map +1 -0
- package/dist/{entry-compose-DCnvBZz2.d.ts → entry-compose-CiakSt-f.d.ts} +62 -81
- package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
- package/dist/{errors-C4oENPcu.js → entry-errors-JMthgJ2g.js} +45 -29
- package/dist/entry-errors-JMthgJ2g.js.map +1 -0
- package/dist/{table-cell-margins-EVg0EwxT.js → enums-BP9wsb9Q.js} +44 -47
- package/dist/enums-BP9wsb9Q.js.map +1 -0
- package/dist/{enums-BUKTUB6V.d.ts → enums-PcXr3o3J.d.ts} +13 -14
- package/dist/{enums-BUKTUB6V.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-Cb7naFj6.js → fit-Bpmzep6O.js} +64 -56
- package/dist/fit-Bpmzep6O.js.map +1 -0
- package/dist/{font-metrics-v5fn7s7e.d.ts → font-metrics-DTppw4t3.d.ts} +62 -38
- package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
- package/dist/{grid-DikTjFey.js → grid-DUIMxjfl.js} +23 -26
- package/dist/grid-DUIMxjfl.js.map +1 -0
- package/dist/{html-dom-Iuog2wdK.js → html-dom-LZCgntnJ.js} +46 -47
- 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 +16 -18
- 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-CrYXAfUu.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-J49Ck6S2.d.ts → package-CANBWuSm.d.ts} +9 -13
- 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-C6qgOFAa.d.ts → pres-BkEkUAEM.d.ts} +410 -237
- package/dist/pres-BkEkUAEM.d.ts.map +1 -0
- package/dist/{presentation-DGYlAWhl.js → presentation-4Ggq0_Xa.js} +470 -315
- package/dist/presentation-4Ggq0_Xa.js.map +1 -0
- package/dist/{presentation-types-8jLQHI4M.d.ts → presentation-types-BgnZR-0a.d.ts} +1119 -468
- 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 +2700 -780
- package/dist/script.js.map +1 -1
- package/dist/{shapes-re0KyQk_.js → shapes-CAPOjU0Q.js} +1564 -491
- package/dist/shapes-CAPOjU0Q.js.map +1 -0
- package/dist/{slide-Dx2ZeVZ6.d.ts → slide-BmT4kMoO.d.ts} +102 -89
- 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-BTDP3lEP.js → table-dom-BYga4FYY.js} +4 -5
- package/dist/{table-dom-BTDP3lEP.js.map → table-dom-BYga4FYY.js.map} +1 -1
- package/dist/{text-sN64Otf_.js → text-CU08xJGs.js} +393 -344
- 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-XJlf-hOl.js → units-internal-BR3a4cIi.js} +34 -32
- package/dist/units-internal-BR3a4cIi.js.map +1 -0
- package/dist/{zip-CKd8ihcf.js → zip-7JTh61LX.js} +8 -10
- package/dist/zip-7JTh61LX.js.map +1 -0
- package/dist/{zip-C0leZZMj.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-DPUHh85d.js → zoom-CSSeOwM8.js} +1204 -620
- package/dist/zoom-CSSeOwM8.js.map +1 -0
- package/package.json +27 -28
- package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +37 -84
- package/dist/autopage-odPz7zZO.js.map +0 -1
- package/dist/binary-string-CKVAYI3M.js.map +0 -1
- package/dist/body-insets-Dw_BeWD_.js.map +0 -1
- package/dist/clip-Bl01BPcN.d.ts.map +0 -1
- package/dist/diagnostics-D8ot4Ei7.d.ts.map +0 -1
- package/dist/embedded-fonts-DarT9aoa.d.ts.map +0 -1
- package/dist/entry-compose-DCnvBZz2.d.ts.map +0 -1
- package/dist/errors-C4oENPcu.js.map +0 -1
- package/dist/fit-Cb7naFj6.js.map +0 -1
- package/dist/font-metrics-v5fn7s7e.d.ts.map +0 -1
- package/dist/grid-DikTjFey.js.map +0 -1
- package/dist/html-dom-Iuog2wdK.js.map +0 -1
- package/dist/image-size-CvN2iCA4.js.map +0 -1
- package/dist/notes-Dh12JAbn.js.map +0 -1
- package/dist/options-internal-WqgMf720.js.map +0 -1
- package/dist/package-J49Ck6S2.d.ts.map +0 -1
- package/dist/playbtn-CUqQdoDf.js.map +0 -1
- package/dist/pres-C6qgOFAa.d.ts.map +0 -1
- package/dist/presentation-DGYlAWhl.js.map +0 -1
- package/dist/presentation-types-8jLQHI4M.d.ts.map +0 -1
- package/dist/shapes-re0KyQk_.js.map +0 -1
- package/dist/slide-Dx2ZeVZ6.d.ts.map +0 -1
- package/dist/table-cell-margins-EVg0EwxT.js.map +0 -1
- package/dist/text-anchor-CrYXAfUu.js.map +0 -1
- package/dist/text-sN64Otf_.js.map +0 -1
- package/dist/units-internal-XJlf-hOl.js.map +0 -1
- package/dist/zip-C0leZZMj.d.ts.map +0 -1
- package/dist/zip-CKd8ihcf.js.map +0 -1
- package/dist/zoom-DPUHh85d.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 {
|
|
@@ -357,8 +354,12 @@ var OpcPackage = class OpcPackage {
|
|
|
357
354
|
}
|
|
358
355
|
return new OpcPackage(parts, contentTypes, contentTypesBytes);
|
|
359
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* The part at `partName`, or `undefined`. Takes `null`, as a part-name getter such as
|
|
359
|
+
* `Picture.imagePartName` reads when there is no part to name, and misses on it.
|
|
360
|
+
*/
|
|
360
361
|
part(partName) {
|
|
361
|
-
return this.parts.get(partName);
|
|
362
|
+
return partName === null ? void 0 : this.parts.get(partName);
|
|
362
363
|
}
|
|
363
364
|
partsByContentType(contentType) {
|
|
364
365
|
if (!this.#partsByContentType) {
|
|
@@ -403,7 +404,7 @@ var OpcPackage = class OpcPackage {
|
|
|
403
404
|
* later `save()` neither re-emits it nor flushes a stale `.rels` for it. A relationship set
|
|
404
405
|
* belongs to its part: one left behind would come back as the relationships of the next part
|
|
405
406
|
* added under the same name. Low-level: it does not touch references *to* this part
|
|
406
|
-
* (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
|
|
407
408
|
* {@link Presentation.removeSlide} for the slide-aware variant that unwires the presentation
|
|
408
409
|
* and prunes orphans.
|
|
409
410
|
*/
|
|
@@ -477,21 +478,20 @@ var OpcPackage = class OpcPackage {
|
|
|
477
478
|
for (const relationships of this.#relationshipsCache.values()) {
|
|
478
479
|
if (!relationships.isDirty) continue;
|
|
479
480
|
const relsPartName = relsPartNameFor(relationships.sourcePartName);
|
|
480
|
-
const bytes = textEncoder$
|
|
481
|
+
const bytes = textEncoder$4.encode(relationships.serialize());
|
|
481
482
|
this.#parts.set(relsPartName, new Part(relsPartName, RELATIONSHIPS_CONTENT_TYPE, bytes));
|
|
482
483
|
this.#partsByContentType = null;
|
|
483
484
|
}
|
|
484
485
|
}
|
|
485
486
|
};
|
|
486
|
-
|
|
487
|
-
//#region src/read/api/slide-background.ts
|
|
487
|
+
// src/read/api/slide-background.ts
|
|
488
488
|
/**
|
|
489
489
|
* Read a slide's effective background (`p:cSld/p:bg`), decoded into a typed union.
|
|
490
490
|
*
|
|
491
491
|
* A background lives on the slide itself, or is inherited from its slideLayout,
|
|
492
|
-
* else its slideMaster
|
|
492
|
+
* else its slideMaster; the {@link SlideBackground.source} field records which.
|
|
493
493
|
* The writer authors solid-colour, gradient, pattern and image backgrounds (all FAITHFUL);
|
|
494
|
-
* theme-indexed (`p:bgRef`) backgrounds are read-only for imported decks
|
|
494
|
+
* theme-indexed (`p:bgRef`) backgrounds are read-only for imported decks. Surface
|
|
495
495
|
* them so a codegen pass can carry the part rather than fake it. A
|
|
496
496
|
* `themeRef` additionally resolves its `idx` through the slide theme's `fmtScheme`
|
|
497
497
|
* to a concrete {@link BackgroundFill} in `resolvedFill`, so a consumer can ask what
|
|
@@ -534,13 +534,13 @@ function decodeBackgroundFill(container, ctx, rels) {
|
|
|
534
534
|
/**
|
|
535
535
|
* Resolve a `p:bgRef`'s `idx` into the concrete {@link BackgroundFill} it renders
|
|
536
536
|
* as, through the slide theme's `fmtScheme`. A `p:bgRef` is a
|
|
537
|
-
* `CT_StyleMatrixReference`
|
|
537
|
+
* `CT_StyleMatrixReference` (the same shape as a shape's `a:fillRef`), so
|
|
538
538
|
* {@link styleRefFill} builds the indexed `bgFillStyleLst`/`fillStyleLst` entry with
|
|
539
539
|
* its `phClr` substituted by the bgRef's colour, and the resulting fill element is
|
|
540
540
|
* decoded like a `p:bgPr`. `null` when the theme has no `fmtScheme`, the entry is
|
|
541
541
|
* absent, or the colour cannot be resolved.
|
|
542
542
|
*
|
|
543
|
-
* `themeRels
|
|
543
|
+
* `themeRels`, not the owning slide/layout/master's rels, is what an `a:blipFill`
|
|
544
544
|
* entry's `r:embed` resolves against: the fill element is lifted out of the *theme*
|
|
545
545
|
* part, so its relationship id is scoped to that part. Several stock Office themes
|
|
546
546
|
* (Ion, Facet, …) put a picture in the third `bgFillStyleLst` slot, so a deck using
|
|
@@ -581,14 +581,13 @@ function readSlideBackground(bg, source, ctx, rels, themeRels) {
|
|
|
581
581
|
source
|
|
582
582
|
};
|
|
583
583
|
}
|
|
584
|
-
|
|
585
|
-
//#region src/read/api/ops/part-index.ts
|
|
584
|
+
// src/read/api/ops/part-index.ts
|
|
586
585
|
/**
|
|
587
586
|
* Walking a deck's master/layout spine by partname.
|
|
588
587
|
*
|
|
589
588
|
* The `p:sldMasterIdLst` / `p:sldLayoutIdLst` entries hold relationship ids, not partnames, so
|
|
590
589
|
* every traversal has to resolve through the owning part's relationships. These are the
|
|
591
|
-
* source-side reads that do it
|
|
590
|
+
* source-side reads that do it, used against a *foreign* presentation as often as against this
|
|
592
591
|
* one, which is why they take the deck rather than living on it.
|
|
593
592
|
*/
|
|
594
593
|
/**
|
|
@@ -628,8 +627,7 @@ function idListTargets(lst, qname, resolve) {
|
|
|
628
627
|
}
|
|
629
628
|
return out;
|
|
630
629
|
}
|
|
631
|
-
|
|
632
|
-
//#region src/read/api/chrome.ts
|
|
630
|
+
// src/read/api/chrome.ts
|
|
633
631
|
/** Read `<a:latin>`/`<a:ea>`/`<a:cs>` typefaces of a theme font family; empty string ⇒ `null`. */
|
|
634
632
|
function readThemeFontFace(font) {
|
|
635
633
|
const face = (child) => {
|
|
@@ -723,7 +721,7 @@ function placeholderShapes(spTree) {
|
|
|
723
721
|
return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
|
|
724
722
|
}
|
|
725
723
|
/**
|
|
726
|
-
* 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:
|
|
727
725
|
* the definition a slide's placeholder of the same `type`/`idx` inherits geometry
|
|
728
726
|
* and text style from. Geometry getters read the placeholder's *own* `a:xfrm`,
|
|
729
727
|
* which a master/layout placeholder carries explicitly (so it round-trips, unlike a
|
|
@@ -749,7 +747,7 @@ var Placeholder = class {
|
|
|
749
747
|
* `xfrm()` + `emuFrom`; and,
|
|
750
748
|
* the one that showed, the text frame was built with **no** inheritance context, so
|
|
751
749
|
* `Run.resolvedSizePt`, `resolvedFontFace`, `resolvedColor` and `resolvedBold` resolved
|
|
752
|
-
* through `SlideMaster.shapes` and came back `null` through `SlideMaster.placeholders
|
|
750
|
+
* through `SlideMaster.shapes` and came back `null` through `SlideMaster.placeholders`:
|
|
753
751
|
* two views of one element disagreeing about the same run.
|
|
754
752
|
*/
|
|
755
753
|
shape;
|
|
@@ -817,7 +815,7 @@ var Placeholder = class {
|
|
|
817
815
|
}
|
|
818
816
|
};
|
|
819
817
|
/**
|
|
820
|
-
* 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
|
|
821
819
|
* inherits from.
|
|
822
820
|
*
|
|
823
821
|
* Both are an OPC part holding a `p:cSld` with a shape tree and a resolvable theme context, and
|
|
@@ -835,7 +833,7 @@ var Placeholder = class {
|
|
|
835
833
|
*
|
|
836
834
|
* `Slide` deliberately does not extend this. It is a shape host too, but it is constructed from
|
|
837
835
|
* a `Presentation` rather than an `OpcPackage`, reaches its relationships through that, and
|
|
838
|
-
* 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
|
|
839
837
|
* the same handful of members, which is more contortion than the sharing is worth.
|
|
840
838
|
*/
|
|
841
839
|
var TemplatePart = class {
|
|
@@ -860,7 +858,7 @@ var TemplatePart = class {
|
|
|
860
858
|
return (cSld && attr(cSld, "name")) ?? "";
|
|
861
859
|
}
|
|
862
860
|
/**
|
|
863
|
-
* 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`
|
|
864
862
|
* union `Slide.shapes` returns, so shape-walking code applies unchanged.
|
|
865
863
|
*
|
|
866
864
|
* {@link placeholders} is the filtered view of the same tree.
|
|
@@ -869,7 +867,7 @@ var TemplatePart = class {
|
|
|
869
867
|
const spTree = this.spTree();
|
|
870
868
|
return spTree ? buildShapes(spTree, this) : [];
|
|
871
869
|
}
|
|
872
|
-
/** The shape anywhere in this tier's tree with the given drawing id, or `undefined
|
|
870
|
+
/** The shape anywhere in this tier's tree with the given drawing id, or `undefined` (always, for `null`). */
|
|
873
871
|
shapeByIdDeep(id) {
|
|
874
872
|
return findShapeByIdDeep(this.shapes, id);
|
|
875
873
|
}
|
|
@@ -930,7 +928,7 @@ var SlideMaster = class extends TemplatePart {
|
|
|
930
928
|
}
|
|
931
929
|
/**
|
|
932
930
|
* The master's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
933
|
-
* none. Scoped to the master's own element
|
|
931
|
+
* none. Scoped to the master's own element. A slide's *effective* background,
|
|
934
932
|
* resolved through the slide → layout → master chain, is `Slide.background`.
|
|
935
933
|
*/
|
|
936
934
|
get background() {
|
|
@@ -939,21 +937,21 @@ var SlideMaster = class extends TemplatePart {
|
|
|
939
937
|
const themePart = this.relTarget(THEME_REL);
|
|
940
938
|
return readSlideBackground(bg, "master", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
941
939
|
}
|
|
942
|
-
/** 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. */
|
|
943
941
|
resolveThemeContext() {
|
|
944
942
|
return resolveMasterColorContext(this.opc, this.partName);
|
|
945
943
|
}
|
|
946
944
|
};
|
|
947
945
|
/**
|
|
948
|
-
* 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
|
|
949
947
|
* a slide binds to. Exposes its {@link name}, its {@link type} (import-only; the
|
|
950
948
|
* writer authors none), its {@link master} (and, through it, the {@link theme}), its
|
|
951
949
|
* `shapes` and the `placeholders` subset of them, and its own {@link background}.
|
|
952
950
|
* Reachable from `Slide.layout` and {@link SlideMaster.layouts}.
|
|
953
951
|
*
|
|
954
952
|
* The write API authors non-placeholder shapes into a layout: every
|
|
955
|
-
* `defineSlideMaster({ objects })` member that is not a `placeholder`
|
|
956
|
-
* `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.
|
|
957
955
|
*/
|
|
958
956
|
var SlideLayout = class extends TemplatePart {
|
|
959
957
|
/**
|
|
@@ -968,7 +966,7 @@ var SlideLayout = class extends TemplatePart {
|
|
|
968
966
|
/**
|
|
969
967
|
* Whether this layout draws the master's non-placeholder shapes
|
|
970
968
|
* (`p:sldLayout/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
|
|
971
|
-
* attribute means shown
|
|
969
|
+
* attribute means shown. This is the layout-tier counterpart of `Slide.showMasterSp`,
|
|
972
970
|
* and the one PowerPoint actually writes on a section-divider or full-bleed
|
|
973
971
|
* layout. A renderer resolving what to paint under a slide's own shapes has to
|
|
974
972
|
* consult both: the slide's flag, then its layout's.
|
|
@@ -988,7 +986,7 @@ var SlideLayout = class extends TemplatePart {
|
|
|
988
986
|
}
|
|
989
987
|
/**
|
|
990
988
|
* The layout's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
991
|
-
* none. A layout usually authors one
|
|
989
|
+
* none. A layout usually authors one: an explicit fill (`p:bgPr`) or a
|
|
992
990
|
* theme-indexed reference (`p:bgRef`). The slide's *effective* background is
|
|
993
991
|
* `Slide.background`.
|
|
994
992
|
*/
|
|
@@ -1003,8 +1001,7 @@ var SlideLayout = class extends TemplatePart {
|
|
|
1003
1001
|
return resolveLayoutColorContext(this.opc, this.partName);
|
|
1004
1002
|
}
|
|
1005
1003
|
};
|
|
1006
|
-
|
|
1007
|
-
//#region src/read/api/notes.ts
|
|
1004
|
+
// src/read/api/notes.ts
|
|
1008
1005
|
/**
|
|
1009
1006
|
* One placeholder shape (`p:sp`) of a notes slide. A notes slide holds a fixed set:
|
|
1010
1007
|
* the slide thumbnail (`type="sldImg"`), the notes body (`type="body"`), and the
|
|
@@ -1012,8 +1009,8 @@ var SlideLayout = class extends TemplatePart {
|
|
|
1012
1009
|
* `a:xfrm` and are `null` when it inherits geometry from the notesMaster (the case
|
|
1013
1010
|
* on an authored deck, whose `sldImg`/`sldNum` carry an empty `p:spPr`).
|
|
1014
1011
|
*
|
|
1015
|
-
* A notes placeholder IS a {@link Placeholder}
|
|
1016
|
-
* 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,
|
|
1017
1014
|
* so it extends one rather than repeating it. Two things are its own: the flattened
|
|
1018
1015
|
* {@link text} convenience, and a {@link textFrame} that threads the notesMaster inheritance
|
|
1019
1016
|
* context onto the body frame.
|
|
@@ -1025,7 +1022,7 @@ var NotesPlaceholder = class extends Placeholder {
|
|
|
1025
1022
|
* runs round-trip a slide's authored notes; the `sldNum` placeholder's frame
|
|
1026
1023
|
* holds the slide-number `a:fld`, whose value surfaces through `TextFrame.text`.
|
|
1027
1024
|
*
|
|
1028
|
-
* Overrides {@link Placeholder.textFrame} for the inheritance context alone
|
|
1025
|
+
* Overrides {@link Placeholder.textFrame} for the inheritance context alone (see below).
|
|
1029
1026
|
*/
|
|
1030
1027
|
get textFrame() {
|
|
1031
1028
|
const txBody = firstChild(this.sp, "p:txBody");
|
|
@@ -1051,7 +1048,7 @@ var NotesPlaceholder = class extends Placeholder {
|
|
|
1051
1048
|
}
|
|
1052
1049
|
};
|
|
1053
1050
|
/**
|
|
1054
|
-
* 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
|
|
1055
1052
|
* companion to {@link Slide.notesText}. Exposes the notes
|
|
1056
1053
|
* shape tree as {@link NotesPlaceholder}s ({@link body}/{@link slideImage}/
|
|
1057
1054
|
* {@link slideNumber}) plus the body {@link textFrame} the older getters flatten.
|
|
@@ -1071,7 +1068,7 @@ var NotesSlide = class {
|
|
|
1071
1068
|
get partName() {
|
|
1072
1069
|
return this.part.partName;
|
|
1073
1070
|
}
|
|
1074
|
-
/** The notes part's own relationships
|
|
1071
|
+
/** The notes part's own relationships; a notes hyperlink resolves through these. */
|
|
1075
1072
|
get relationships() {
|
|
1076
1073
|
return this.opc.relationshipsFor(this.partName);
|
|
1077
1074
|
}
|
|
@@ -1079,7 +1076,7 @@ var NotesSlide = class {
|
|
|
1079
1076
|
* The notes shape tree as full shapes, in document order.
|
|
1080
1077
|
*
|
|
1081
1078
|
* A notes slide holds nothing but its three placeholders, so {@link placeholders} is the
|
|
1082
|
-
* 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
|
|
1083
1080
|
* what lets a {@link NotesPlaceholder} be the same {@link AutoShape} view of its `p:sp`
|
|
1084
1081
|
* that a master or layout placeholder is, rather than a second reading of one.
|
|
1085
1082
|
*/
|
|
@@ -1087,7 +1084,7 @@ var NotesSlide = class {
|
|
|
1087
1084
|
const spTree = this.#spTree();
|
|
1088
1085
|
return spTree ? buildShapes(spTree, this) : [];
|
|
1089
1086
|
}
|
|
1090
|
-
/** The shape anywhere in the notes tree with the given drawing id, or `undefined
|
|
1087
|
+
/** The shape anywhere in the notes tree with the given drawing id, or `undefined` (always, for `null`). */
|
|
1091
1088
|
shapeByIdDeep(id) {
|
|
1092
1089
|
return findShapeByIdDeep(this.shapes, id);
|
|
1093
1090
|
}
|
|
@@ -1131,8 +1128,7 @@ var NotesSlide = class {
|
|
|
1131
1128
|
return spTreeOf(this.part.dom.documentElement);
|
|
1132
1129
|
}
|
|
1133
1130
|
};
|
|
1134
|
-
|
|
1135
|
-
//#region src/read/api/ops/deck-target.ts
|
|
1131
|
+
// src/read/api/ops/deck-target.ts
|
|
1136
1132
|
/**
|
|
1137
1133
|
* `ppt/_rels/presentation.xml.rels` -- the deck's own relationship part.
|
|
1138
1134
|
*
|
|
@@ -1147,8 +1143,7 @@ var NotesSlide = class {
|
|
|
1147
1143
|
function presentationRels(dest) {
|
|
1148
1144
|
return dest.opc.relationshipsFor(dest.presentationPart.partName);
|
|
1149
1145
|
}
|
|
1150
|
-
|
|
1151
|
-
//#region src/read/api/ops/master-registry.ts
|
|
1146
|
+
// src/read/api/ops/master-registry.ts
|
|
1152
1147
|
/**
|
|
1153
1148
|
* Slide-master bookkeeping in `presentation.xml` and in a master's own
|
|
1154
1149
|
* `p:sldLayoutIdLst`: registering a master so renderers treat it as active,
|
|
@@ -1185,7 +1180,7 @@ function registerMaster(dest, masterPartName) {
|
|
|
1185
1180
|
/**
|
|
1186
1181
|
* Move the `p:sldMasterId` entries for `masterPartNames` to the front of
|
|
1187
1182
|
* `p:sldMasterIdLst`, preserving their existing relative order, so the first of
|
|
1188
|
-
* them becomes the deck's `Designs(1)
|
|
1183
|
+
* them becomes the deck's `Designs(1)`, the theme PowerPoint's Design tab shows.
|
|
1189
1184
|
* Reorders the id list only; relationships, ids, and all other parts are left as
|
|
1190
1185
|
* they are. A no-op when the named masters already lead (idempotent re-call).
|
|
1191
1186
|
*/
|
|
@@ -1215,7 +1210,7 @@ function promoteMasters(dest, masterPartNames) {
|
|
|
1215
1210
|
* unique across the WHOLE presentation, not just within their own list. A duplicate
|
|
1216
1211
|
* anywhere in this space makes PowerPoint report the file as corrupt on open
|
|
1217
1212
|
* ("found a problem with content" / 0x80070570), even though LibreOffice silently
|
|
1218
|
-
* tolerates it
|
|
1213
|
+
* tolerates it. So an imported master AND each of its layouts must take an id past
|
|
1219
1214
|
* the max of both the master-id list and every layout-id list. Recomputed per
|
|
1220
1215
|
* allocation so ids appended earlier in the same import are counted.
|
|
1221
1216
|
*
|
|
@@ -1271,20 +1266,19 @@ function addLayoutToMaster(dest, masterPartName, layoutPartName) {
|
|
|
1271
1266
|
lst.appendChild(entry);
|
|
1272
1267
|
masterPart.markDirty();
|
|
1273
1268
|
}
|
|
1274
|
-
|
|
1275
|
-
//#region src/read/api/ops/copy-traversal.ts
|
|
1269
|
+
// src/read/api/ops/copy-traversal.ts
|
|
1276
1270
|
/**
|
|
1277
1271
|
* What a copy traversal does with one relationship: skip it, carry it as an external link, or
|
|
1278
1272
|
* follow it into the part it names.
|
|
1279
1273
|
*
|
|
1280
1274
|
* Notes pull in a notesMaster and its own theme, which an imported page does not need, so the
|
|
1281
|
-
* notes relationship is never followed
|
|
1275
|
+
* notes relationship is never followed; `carryNotes` picks it up afterwards for the pages
|
|
1282
1276
|
* that asked. A master's relationships to its layouts are skipped so the copy stays lean:
|
|
1283
1277
|
* copied layouts re-link themselves, and skipping the list is also what keeps one edited
|
|
1284
1278
|
* layout from disqualifying every other layout that shares its master.
|
|
1285
1279
|
*
|
|
1286
|
-
* Each caller still owns its own extra arms
|
|
1287
|
-
* 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.
|
|
1288
1282
|
* @param part - the part the relationship belongs to
|
|
1289
1283
|
* @param rel - the relationship being considered
|
|
1290
1284
|
*/
|
|
@@ -1293,13 +1287,12 @@ function copyTraversalStep(part, rel) {
|
|
|
1293
1287
|
if (part.contentType === "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml" && rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") return "skip";
|
|
1294
1288
|
return rel.targetMode === "External" ? "external" : "recurse";
|
|
1295
1289
|
}
|
|
1296
|
-
|
|
1297
|
-
//#region src/read/api/ops/page-owned.ts
|
|
1290
|
+
// src/read/api/ops/page-owned.ts
|
|
1298
1291
|
/**
|
|
1299
1292
|
* What a page copy may share with the page it was copied from, and what it must own.
|
|
1300
1293
|
*
|
|
1301
|
-
* Duplicating a page
|
|
1302
|
-
* `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,
|
|
1303
1296
|
* for every part hanging off it, whether the copy points at the same part or gets
|
|
1304
1297
|
* one of its own. Sharing is the cheap answer and is right for most of the graph:
|
|
1305
1298
|
* a layout, a master, a theme, an image are deck-wide assets PowerPoint itself
|
|
@@ -1308,7 +1301,7 @@ function copyTraversalStep(part, rel) {
|
|
|
1308
1301
|
* It is not right for the parts a page *owns*. A chart and its embedded workbook,
|
|
1309
1302
|
* a SmartArt diagram's five parts, an OLE embedding, a notes slide: PowerPoint
|
|
1310
1303
|
* treats each as belonging to exactly one page, and a package where two slides
|
|
1311
|
-
* 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
|
|
1312
1305
|
* directory is corrupted and unreadable" (`0x80070570`) dialog, with the whole
|
|
1313
1306
|
* deck rejected rather than the duplicate page. The schema validator accepts such
|
|
1314
1307
|
* a package: nothing in ECMA-376 says a chart part has one referrer, so this rule
|
|
@@ -1338,14 +1331,13 @@ function copyTraversalStep(part, rel) {
|
|
|
1338
1331
|
const SHARED_BY_PAGE_COPIES = new Set(SHARED_PARTS.map((kind) => kind.relType));
|
|
1339
1332
|
/**
|
|
1340
1333
|
* Whether a page copy may point at this relationship's target rather than taking
|
|
1341
|
-
* 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
|
|
1342
1335
|
* module comment for why that is the safe default.
|
|
1343
1336
|
*/
|
|
1344
1337
|
function isSharedByPageCopies(relType) {
|
|
1345
1338
|
return SHARED_BY_PAGE_COPIES.has(relType);
|
|
1346
1339
|
}
|
|
1347
|
-
|
|
1348
|
-
//#region src/read/api/ops/part-reuse.ts
|
|
1340
|
+
// src/read/api/ops/part-reuse.ts
|
|
1349
1341
|
/**
|
|
1350
1342
|
* Whether the destination already holds `sourcePartName`'s subgraph, part for part and
|
|
1351
1343
|
* byte for byte, in a state an imported slide can bind to.
|
|
@@ -1426,8 +1418,7 @@ function bytesEqual(a, b) {
|
|
|
1426
1418
|
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1427
1419
|
return true;
|
|
1428
1420
|
}
|
|
1429
|
-
|
|
1430
|
-
//#region src/read/api/ops/part-copy.ts
|
|
1421
|
+
// src/read/api/ops/part-copy.ts
|
|
1431
1422
|
/** Open an ownership scope for one page copy. */
|
|
1432
1423
|
function newOwnedScope() {
|
|
1433
1424
|
return /* @__PURE__ */ new Map();
|
|
@@ -1441,7 +1432,7 @@ function copyTarget(ctx) {
|
|
|
1441
1432
|
* it would throw, found before anything in the destination moves.
|
|
1442
1433
|
*
|
|
1443
1434
|
* This is what lets a refused import leave the deck byte-identical. The copy changes the deck as it
|
|
1444
|
-
* goes
|
|
1435
|
+
* goes (parts added, a master registered in `presentation.xml`, a layout linked into its master),
|
|
1445
1436
|
* so a failure it met halfway would leave all of that behind. An import therefore runs the same
|
|
1446
1437
|
* traversal twice: first with a plan in its context, which throws what the copy would and writes
|
|
1447
1438
|
* nothing, then for real. The dry run used to be a separate walk of the source, transcribed from the
|
|
@@ -1452,8 +1443,8 @@ function copyTarget(ctx) {
|
|
|
1452
1443
|
* A plan stands in for the destination package as a {@link CopyTarget}. It hands out the partnames
|
|
1453
1444
|
* the copy will be given, keeps its own copy of each source's registry so a part it planned is found
|
|
1454
1445
|
* again as the copy would find it, and remembers the notes master it would install in a deck that has
|
|
1455
|
-
* none. Destination bookkeeping that adds no part
|
|
1456
|
-
* 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.
|
|
1457
1448
|
*/
|
|
1458
1449
|
var CopyPlan = class {
|
|
1459
1450
|
api;
|
|
@@ -1547,7 +1538,7 @@ var CopyPlan = class {
|
|
|
1547
1538
|
* registry idempotence is right for a theme, master, layout or image, where a
|
|
1548
1539
|
* second copy is waste; applied to the slide it made a repeated import of the
|
|
1549
1540
|
* same source page return the first copy's partname, and the caller then wired a
|
|
1550
|
-
* 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
|
|
1551
1542
|
* to open (0x80070570), with nothing in the read model to show for it. So the
|
|
1552
1543
|
* page goes into the selection plan `copyPart` already honours for a batch, which
|
|
1553
1544
|
* re-materializes exactly the named pages and leaves the dedup of their
|
|
@@ -1570,7 +1561,7 @@ function copySlidePart(ctx, sourcePartName) {
|
|
|
1570
1561
|
* per source package via the copy registry. Relationship ids are preserved so
|
|
1571
1562
|
* the copied part body's `r:id`/`r:embed` references stay valid; targets are
|
|
1572
1563
|
* rewritten to the freshly-allocated partnames. Notes relationships are
|
|
1573
|
-
* 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;
|
|
1574
1565
|
* each imported `slideLayout` wires itself into the master instead (see
|
|
1575
1566
|
* {@link linkLayoutIntoMaster}).
|
|
1576
1567
|
*
|
|
@@ -1580,13 +1571,13 @@ function copySlidePart(ctx, sourcePartName) {
|
|
|
1580
1571
|
* a `slide → slide` link may not leave the selection is enforced as each link is
|
|
1581
1572
|
* followed (see {@link rebuildRels}). Imports run this traversal as a
|
|
1582
1573
|
* {@link CopyPlan} first, which throws that and every other failure the copy
|
|
1583
|
-
* could meet
|
|
1574
|
+
* could meet, so once copying starts there is nothing left to throw, and a
|
|
1584
1575
|
* rejected batch never leaves a half-copied deck behind.
|
|
1585
1576
|
*
|
|
1586
1577
|
* Idempotence stops at the page's own parts. Reaching a page opens an
|
|
1587
1578
|
* {@link OwnedScope}, and everything under it that {@link isSharedByPageCopies}
|
|
1588
|
-
* does not clear for sharing
|
|
1589
|
-
* 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
|
|
1590
1581
|
* next copy of the page gets parts of its own. Pass `owned` to open the scope
|
|
1591
1582
|
* from outside, as the rebinding import paths do for the page they build
|
|
1592
1583
|
* themselves.
|
|
@@ -1694,7 +1685,7 @@ function rebuildRels(ctx, options) {
|
|
|
1694
1685
|
* The page's shared targets are carried as the source wrote them. Each owned one is repointed at a
|
|
1695
1686
|
* fresh copy, and the subtree under it is copied the same way, so a chart's workbook and
|
|
1696
1687
|
* user-shapes drawing come along while the image inside that drawing stays shared. A relationship
|
|
1697
|
-
* *back* to the source page
|
|
1688
|
+
* *back* to the source page (a notes slide names the slide it annotates) is repointed at the
|
|
1698
1689
|
* clone, which is what makes the copied notes belong to it.
|
|
1699
1690
|
*
|
|
1700
1691
|
* Unlike an import, a clone keeps its page's notes, and nothing here consults the copy registry or
|
|
@@ -1769,19 +1760,18 @@ function linkLayoutIntoMaster(ctx, layoutSourceRels, layoutPartName) {
|
|
|
1769
1760
|
if (!masterPartName) return;
|
|
1770
1761
|
addLayoutToMaster(ctx.dest, masterPartName, layoutPartName);
|
|
1771
1762
|
}
|
|
1772
|
-
|
|
1773
|
-
//#region src/read/api/ops/notes-master.ts
|
|
1763
|
+
// src/read/api/ops/notes-master.ts
|
|
1774
1764
|
/**
|
|
1775
1765
|
* Carrying speaker notes and the deck's single notes master across an import or an append.
|
|
1776
1766
|
*
|
|
1777
1767
|
* A `notesSlide` is not self-contained: it back-references the slide it annotates and forward-
|
|
1778
1768
|
* references the deck's notes master. `CT_NotesMasterIdList` allows at most one master per
|
|
1779
|
-
* 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),
|
|
1780
1770
|
* and a source master is copied only into a deck that has none. That single rule is why both the
|
|
1781
1771
|
* `Presentation`-to-`Presentation` path and the generator-XML path funnel through
|
|
1782
1772
|
* `registerNotesMaster`: they differ only in how the *part* comes into being.
|
|
1783
1773
|
*/
|
|
1784
|
-
const textEncoder$
|
|
1774
|
+
const textEncoder$3 = new TextEncoder();
|
|
1785
1775
|
/**
|
|
1786
1776
|
* Carry the source slide's speaker notes onto the just-imported slide (the
|
|
1787
1777
|
* `importNotes` option). The slide copy itself dropped the `notesSlide` rel, so
|
|
@@ -1789,7 +1779,7 @@ const textEncoder$2 = new TextEncoder();
|
|
|
1789
1779
|
* `slide → notesSlide` rel on the new slide, and rebuilds the copied notesSlide's
|
|
1790
1780
|
* own relationships:
|
|
1791
1781
|
*
|
|
1792
|
-
* - its `slide` back-rel is repointed at the new slide (`newSlidePartName`)
|
|
1782
|
+
* - its `slide` back-rel is repointed at the new slide (`newSlidePartName`); the
|
|
1793
1783
|
* source slide is *not* copied (that would be circular);
|
|
1794
1784
|
* - its `notesMaster` rel is resolved through {@link ensureNotesMaster}, which
|
|
1795
1785
|
* reuses this deck's notesMaster when it has one and copies the source's only
|
|
@@ -1838,7 +1828,7 @@ function carryNotes(dest, source, ctx, sourceSlidePartName, newSlidePartName) {
|
|
|
1838
1828
|
*/
|
|
1839
1829
|
function addNotesSlidePart(dest, xml, slidePartName, notesMasterPartName) {
|
|
1840
1830
|
const notesPartName = dest.opc.reservePartNameLike("/ppt/notesSlides/notesSlide1.xml");
|
|
1841
|
-
dest.opc.addPart(notesPartName, NOTES_SLIDE_CONTENT_TYPE, textEncoder$
|
|
1831
|
+
dest.opc.addPart(notesPartName, NOTES_SLIDE_CONTENT_TYPE, textEncoder$3.encode(xml));
|
|
1842
1832
|
const notesRels = dest.opc.relationshipsFor(notesPartName);
|
|
1843
1833
|
if (notesMasterPartName) notesRels.addWithId("rId1", NOTES_MASTER_REL, relativePartName(notesPartName, notesMasterPartName));
|
|
1844
1834
|
notesRels.addWithId("rId2", SLIDE_REL, relativePartName(notesPartName, slidePartName));
|
|
@@ -1876,8 +1866,8 @@ function presentationRoot(dest) {
|
|
|
1876
1866
|
* allows. Returns the partname, so callers can use it as a rel target.
|
|
1877
1867
|
*
|
|
1878
1868
|
* Split out of {@link ensureNotesMaster} because the two ways a notesMaster arrives
|
|
1879
|
-
*
|
|
1880
|
-
* {@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
|
|
1881
1871
|
* registered.
|
|
1882
1872
|
*/
|
|
1883
1873
|
function registerNotesMaster(dest, notesMasterPartName) {
|
|
@@ -1921,7 +1911,7 @@ function installNotesMaster(dest, masterBytes, themeBytes, themeContentType = TH
|
|
|
1921
1911
|
* theme its `.rels` requires (the normal write path emits that as `theme2.xml`).
|
|
1922
1912
|
*/
|
|
1923
1913
|
function ensureNotesMasterFromXml(dest, master) {
|
|
1924
|
-
return existingNotesMaster(dest) ?? installNotesMaster(dest, textEncoder$
|
|
1914
|
+
return existingNotesMaster(dest) ?? installNotesMaster(dest, textEncoder$3.encode(master.xml), textEncoder$3.encode(master.themeXml));
|
|
1925
1915
|
}
|
|
1926
1916
|
/**
|
|
1927
1917
|
* Resolve the notesMaster a *newly authored* notes slide should bind to
|
|
@@ -1941,7 +1931,7 @@ function ensureNotesMasterFromXml(dest, master) {
|
|
|
1941
1931
|
* arrangement PowerPoint writes (`theme1.xml` for the master, `theme2.xml` for the
|
|
1942
1932
|
* notes master).
|
|
1943
1933
|
*
|
|
1944
|
-
* 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
|
|
1945
1935
|
* given a valid notesMaster, and silently emitting one without a resolvable theme
|
|
1946
1936
|
* is what PowerPoint reports as a repair prompt rather than a bad edit.
|
|
1947
1937
|
* @param {Presentation} dest - the deck being authored onto
|
|
@@ -1954,10 +1944,9 @@ function ensureNotesMasterForAuthoring(dest, slidePartName) {
|
|
|
1954
1944
|
const themePartName = resolveSlideThemeParts(dest.opc, slidePartName).themePartName;
|
|
1955
1945
|
const themePart = themePartName ? dest.opc.part(themePartName) : void 0;
|
|
1956
1946
|
if (!themePart) throw new PackageReadError("package/part-missing", `addNotes: no theme reachable from ${slidePartName} to bind a new notes master to`);
|
|
1957
|
-
return installNotesMaster(dest, textEncoder$
|
|
1947
|
+
return installNotesMaster(dest, textEncoder$3.encode(makeXmlNotesMaster()), themePart.serialize(), themePart.contentType);
|
|
1958
1948
|
}
|
|
1959
|
-
|
|
1960
|
-
//#region src/read/api/ops/notes-author.ts
|
|
1949
|
+
// src/read/api/ops/notes-author.ts
|
|
1961
1950
|
/**
|
|
1962
1951
|
* Authoring speaker notes onto a slide of a *loaded* deck ({@link Slide.addNotes}).
|
|
1963
1952
|
*
|
|
@@ -1969,8 +1958,8 @@ function ensureNotesMasterForAuthoring(dest, slidePartName) {
|
|
|
1969
1958
|
* The part it builds is the generator's, not a second design: the fixed three-
|
|
1970
1959
|
* placeholder frame comes from {@link makeXmlNotesSlideSkeleton}, and the body
|
|
1971
1960
|
* paragraphs carry the same elements and attributes the write path emits for a plain
|
|
1972
|
-
* `pptx.addSlide().addNotes(text)`. `test/read/add-notes.test.
|
|
1973
|
-
* equivalence, so the two cannot drift apart unnoticed
|
|
1961
|
+
* `pptx.addSlide().addNotes(text)`. `test/read/add-notes.test.ts` asserts that
|
|
1962
|
+
* equivalence, so the two cannot drift apart unnoticed: the same guarantee
|
|
1974
1963
|
* `src/ooxml/` gives the constants both halves share.
|
|
1975
1964
|
*
|
|
1976
1965
|
* The one difference is empty-element *spelling*: the write path emits `a:rPr` as an
|
|
@@ -2005,7 +1994,7 @@ function notesParagraphsXml(text) {
|
|
|
2005
1994
|
/**
|
|
2006
1995
|
* Replace a notes body's paragraphs with `text`, in the body's own document.
|
|
2007
1996
|
*
|
|
2008
|
-
* 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
|
|
2009
1998
|
* an earlier `addNotes` wrote. Only the `a:p` children are replaced, so the part's
|
|
2010
1999
|
* geometry, its `a:bodyPr`/`a:lstStyle`, and its other two placeholders survive. The
|
|
2011
2000
|
* body's hyperlink relationships are deliberately *not* pruned: a caller replacing
|
|
@@ -2047,7 +2036,7 @@ function notesBodyTxBody(root) {
|
|
|
2047
2036
|
* When the slide already has a notes part, only the body paragraphs are rewritten
|
|
2048
2037
|
* (see {@link replaceNotesParagraphs}). When it has none, the part is built and
|
|
2049
2038
|
* wired: `slide → notesSlide`, and on the new part the two relationships a notes
|
|
2050
|
-
* slide always carries
|
|
2039
|
+
* slide always carries: `notesMaster` as rId1 and a `slide` back-reference as rId2,
|
|
2051
2040
|
* the ids and order the write path reserves.
|
|
2052
2041
|
*
|
|
2053
2042
|
* The notes master is resolved through {@link ensureNotesMasterForAuthoring}: this
|
|
@@ -2077,8 +2066,7 @@ function authorNotes(slide, text) {
|
|
|
2077
2066
|
slide.relationships.add(NOTES_SLIDE_REL, relativePartName(slide.partName, partName));
|
|
2078
2067
|
return partName;
|
|
2079
2068
|
}
|
|
2080
|
-
|
|
2081
|
-
//#region src/read/api/comments.ts
|
|
2069
|
+
// src/read/api/comments.ts
|
|
2082
2070
|
/** The slide → modern-comments-part relationship type (2018 schema). Only this module reads one. */
|
|
2083
2071
|
const MODERN_COMMENTS_REL_TYPE = MS_REL + "2018/10/relationships/comments";
|
|
2084
2072
|
/** Content type of the modern per-slide comments part (`ppt/comments/modernComment_*.xml`). */
|
|
@@ -2199,8 +2187,7 @@ function commentSchema(opc) {
|
|
|
2199
2187
|
if (opc.partsByContentType("application/vnd.openxmlformats-officedocument.presentationml.comments+xml").length > 0) return "legacy";
|
|
2200
2188
|
return "none";
|
|
2201
2189
|
}
|
|
2202
|
-
|
|
2203
|
-
//#region src/read/api/tags.ts
|
|
2190
|
+
// src/read/api/tags.ts
|
|
2204
2191
|
/** The owner → tags-part relationship type. Only this module follows one. */
|
|
2205
2192
|
const TAGS_REL_TYPE = OFFICE_REL + "tags";
|
|
2206
2193
|
/**
|
|
@@ -2223,8 +2210,7 @@ function readTagsForPart(opc, ownerPartName) {
|
|
|
2223
2210
|
}
|
|
2224
2211
|
return out;
|
|
2225
2212
|
}
|
|
2226
|
-
|
|
2227
|
-
//#region src/read/api/transition.ts
|
|
2213
|
+
// src/read/api/transition.ts
|
|
2228
2214
|
/**
|
|
2229
2215
|
* Typed read/edit model for a slide's `p:transition` (slide-show transition).
|
|
2230
2216
|
*
|
|
@@ -2235,7 +2221,7 @@ function readTagsForPart(opc, ownerPartName) {
|
|
|
2235
2221
|
* when the duration matches a coarse `spd` speed bucket; and
|
|
2236
2222
|
* - **wrapped** `<mc:AlternateContent>` carrying a `<mc:Choice Requires="p14">`
|
|
2237
2223
|
* whose `p:transition` adds the precise `p14:dur` (milliseconds), plus a base
|
|
2238
|
-
* `<mc:Fallback>` without it
|
|
2224
|
+
* `<mc:Fallback>` without it. This form is emitted for an off-bucket exact duration.
|
|
2239
2225
|
*
|
|
2240
2226
|
* The getter prefers the `p14` Choice (so `durationMs` is recovered); the setter
|
|
2241
2227
|
* emits the `mc:AlternateContent` form when `durationMs` is set and the bare form
|
|
@@ -2250,7 +2236,6 @@ function typeElement(transition) {
|
|
|
2250
2236
|
}
|
|
2251
2237
|
/** Namespace prefix for a URI, defaulting to `p` for the base presentationml namespace. */
|
|
2252
2238
|
function prefixFor(uri) {
|
|
2253
|
-
if (!uri) return "p";
|
|
2254
2239
|
for (const [prefix, ns] of Object.entries(OOXML_NS)) if (ns === uri) return prefix;
|
|
2255
2240
|
return "p";
|
|
2256
2241
|
}
|
|
@@ -2413,8 +2398,7 @@ function removeTransition(root) {
|
|
|
2413
2398
|
const found = findTransition(root);
|
|
2414
2399
|
if (found) root.removeChild(found.outer);
|
|
2415
2400
|
}
|
|
2416
|
-
|
|
2417
|
-
//#region src/read/api/slide-build.ts
|
|
2401
|
+
// src/read/api/slide-build.ts
|
|
2418
2402
|
/**
|
|
2419
2403
|
* Create `qname` in the parent's document and append it.
|
|
2420
2404
|
*
|
|
@@ -2484,8 +2468,7 @@ function buildPicture(doc, spec) {
|
|
|
2484
2468
|
appendSpPr(pic, spec);
|
|
2485
2469
|
return pic;
|
|
2486
2470
|
}
|
|
2487
|
-
|
|
2488
|
-
//#region src/read/api/slide.ts
|
|
2471
|
+
// src/read/api/slide.ts
|
|
2489
2472
|
/** Recognize a common raster format from its leading bytes, through the shared registry. */
|
|
2490
2473
|
function sniffImageType(bytes) {
|
|
2491
2474
|
const format = imageFormatForBytes(bytes);
|
|
@@ -2542,8 +2525,8 @@ var Slide = class {
|
|
|
2542
2525
|
* `fmtScheme` + `fontScheme`, plus the layout/master roots), walked once from
|
|
2543
2526
|
* slide → layout → master → theme and cached on this proxy. Backs the read-model
|
|
2544
2527
|
* resolved getters ({@link Shape.resolvedFill}, `Run.resolvedColor`,
|
|
2545
|
-
* `Run.resolvedSizePt`, `Run.resolvedFontFace`) so a `schemeClr` token
|
|
2546
|
-
* 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
|
|
2547
2530
|
* literal hex, and a placeholder run's inherited size/typeface (a `+mj-*`/`+mn-*`
|
|
2548
2531
|
* font token included) resolves to a literal value. The maps/roots are empty when
|
|
2549
2532
|
* the theme chain is incomplete, in which case tokens simply stay unresolved.
|
|
@@ -2593,7 +2576,7 @@ var Slide = class {
|
|
|
2593
2576
|
/**
|
|
2594
2577
|
* Whether this slide draws the master's non-placeholder shapes
|
|
2595
2578
|
* (`p:sld/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
|
|
2596
|
-
* attribute means shown
|
|
2579
|
+
* attribute means shown, the same shape as {@link hidden}.
|
|
2597
2580
|
*
|
|
2598
2581
|
* This is the switch that decides whether {@link SlideMaster.shapes} belongs on
|
|
2599
2582
|
* *this* slide. A slide the author gave a full-bleed image or a section divider
|
|
@@ -2620,7 +2603,7 @@ var Slide = class {
|
|
|
2620
2603
|
* Set or clear the slide's show transition. Assigning `null` removes it.
|
|
2621
2604
|
* Writing a transition with `durationMs` emits the `mc:AlternateContent` form
|
|
2622
2605
|
* (a `p14` Choice carrying `p14:dur` plus a base `mc:Fallback`); otherwise the
|
|
2623
|
-
* 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,
|
|
2624
2607
|
* after `p:clrMapOvr`, before `p:timing`/`p:extLst`. Marks the slide part dirty.
|
|
2625
2608
|
*
|
|
2626
2609
|
* The new transition is built from the current one before that is removed: it keeps the sound
|
|
@@ -2646,6 +2629,15 @@ var Slide = class {
|
|
|
2646
2629
|
return root ? hasAnimations(root) : false;
|
|
2647
2630
|
}
|
|
2648
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
|
+
/**
|
|
2649
2641
|
* @internal The sorted, de-duplicated set of shape ids (`spid`) referenced by
|
|
2650
2642
|
* the slide's animations (`<p:spTgt spid>` and `<p:bldP spid>`). Exposed for the
|
|
2651
2643
|
* import paths' spid-coherence checks.
|
|
@@ -2675,14 +2667,14 @@ var Slide = class {
|
|
|
2675
2667
|
/**
|
|
2676
2668
|
* Flatten the slide's build animations: remove the `<p:timing>` block so the
|
|
2677
2669
|
* slide renders and edits as its final static state, with every shape shown at
|
|
2678
|
-
* once. Gated like {@link hasAnimations}
|
|
2670
|
+
* once. Gated like {@link hasAnimations}: a `<p:timing>` that is purely a media
|
|
2679
2671
|
* loop (no `<p:bldP>` or `presetID`) is preserved so media playback survives.
|
|
2680
2672
|
* Marks the slide part dirty and returns `true` only when a timing block was
|
|
2681
2673
|
* removed.
|
|
2682
2674
|
*
|
|
2683
2675
|
* This removes click-through staging only; it does not delete shapes. If a slide
|
|
2684
2676
|
* animated alternating states over the same region, the flattened render shows
|
|
2685
|
-
* them all at once
|
|
2677
|
+
* them all at once. Keep that distinct from removing staged/duplicate shapes.
|
|
2686
2678
|
*/
|
|
2687
2679
|
flattenAnimations() {
|
|
2688
2680
|
const root = this.part.dom.documentElement;
|
|
@@ -2698,7 +2690,7 @@ var Slide = class {
|
|
|
2698
2690
|
return spTree ? buildShapes(spTree, this) : [];
|
|
2699
2691
|
}
|
|
2700
2692
|
/**
|
|
2701
|
-
* All text on the slide, flattened in document order
|
|
2693
|
+
* All text on the slide, flattened in document order: the read-model
|
|
2702
2694
|
* counterpart to {@link TextFrame.text} one level up. Every text-bearing shape
|
|
2703
2695
|
* contributes its text, recursing into groups ({@link GroupShape}) and reading
|
|
2704
2696
|
* table cells ({@link GraphicFrame.table}) and SmartArt node text
|
|
@@ -2741,12 +2733,12 @@ var Slide = class {
|
|
|
2741
2733
|
return blocks.join("\n");
|
|
2742
2734
|
}
|
|
2743
2735
|
/**
|
|
2744
|
-
* The slide's speaker-note text
|
|
2736
|
+
* The slide's speaker-note text, the companion to {@link text} for the notes
|
|
2745
2737
|
* that {@link text} deliberately excludes. Delegates to {@link notesSlide}: finds
|
|
2746
2738
|
* the notes body placeholder (`p:ph` `type="body"`) and flattens its text frame
|
|
2747
2739
|
* the same way {@link TextFrame.text} does (paragraphs joined by `\n`).
|
|
2748
2740
|
*
|
|
2749
|
-
* 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
|
|
2750
2742
|
* from `''`, which means a notes slide exists but its body is empty (PowerPoint
|
|
2751
2743
|
* often attaches an empty notes slide to every slide). Only the body placeholder
|
|
2752
2744
|
* is read; the slide-thumbnail (`sldImg`) and slide-number (`sldNum`)
|
|
@@ -2757,14 +2749,14 @@ var Slide = class {
|
|
|
2757
2749
|
return notes ? notes.text : null;
|
|
2758
2750
|
}
|
|
2759
2751
|
/**
|
|
2760
|
-
* 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
|
|
2761
2753
|
* companion to {@link notesText}, which flattens the same body to a plain string.
|
|
2762
2754
|
* Walk `paragraphs → runs` to recover per-run formatting (bold/italic/underline,
|
|
2763
2755
|
* colour, size, face) and any notes hyperlink that {@link notesText} discards.
|
|
2764
2756
|
*
|
|
2765
2757
|
* `null` when the slide has **no notes slide part** (the same boundary
|
|
2766
2758
|
* {@link notesText} returns `null` at), and also when a notes part exists but
|
|
2767
|
-
* carries no body-placeholder text frame
|
|
2759
|
+
* carries no body-placeholder text frame; there is then no frame to hand back
|
|
2768
2760
|
* (whereas {@link notesText} reports `''` for that empty-body case). Convenience
|
|
2769
2761
|
* for `notesSlide.textFrame`; see {@link notesSlide} for the whole modeled notes
|
|
2770
2762
|
* slide (its `sldImg`/`sldNum` placeholders and their geometry).
|
|
@@ -2776,9 +2768,9 @@ var Slide = class {
|
|
|
2776
2768
|
* The slide's speaker-notes slide (`notesSlideN.xml`) as a modeled
|
|
2777
2769
|
* {@link NotesSlide}, or `null` when the slide has no notes slide part. Beyond the
|
|
2778
2770
|
* body text {@link notesText}/{@link notesTextFrame} surface, this exposes the
|
|
2779
|
-
* notes slide's three placeholders
|
|
2780
|
-
*
|
|
2781
|
-
* (
|
|
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`).
|
|
2782
2774
|
*
|
|
2783
2775
|
* The writer authors all three placeholders but leaves `sldImg`/`sldNum` with an
|
|
2784
2776
|
* empty `p:spPr`, so on an authored deck their geometry reads `null` while an
|
|
@@ -2834,7 +2826,7 @@ var Slide = class {
|
|
|
2834
2826
|
* carries its body text, `created` timestamp, marker position (EMU), its author
|
|
2835
2827
|
* resolved through the deck-wide {@link Presentation.modernCommentAuthors}
|
|
2836
2828
|
* registry (`@authorId` GUID → name/initials), and its reply thread nested under
|
|
2837
|
-
* `replies`. Read-only
|
|
2829
|
+
* `replies`. Read-only: the writer authors the legacy schema; see
|
|
2838
2830
|
* {@link Presentation.commentSchema} to tell which schema a deck uses.
|
|
2839
2831
|
*/
|
|
2840
2832
|
get modernComments() {
|
|
@@ -2843,7 +2835,7 @@ var Slide = class {
|
|
|
2843
2835
|
/**
|
|
2844
2836
|
* The slide's programmatic tags (`p:custDataLst/p:tags` on the slide part,
|
|
2845
2837
|
* resolved to `ppt/tags/tagN.xml`) as `{ name, val }` string pairs, `[]` when it
|
|
2846
|
-
* 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:
|
|
2847
2839
|
* read-only, preserved byte-for-byte on round-trip. Deck-level tags are
|
|
2848
2840
|
* {@link Presentation.tags}.
|
|
2849
2841
|
*/
|
|
@@ -2853,7 +2845,7 @@ var Slide = class {
|
|
|
2853
2845
|
/**
|
|
2854
2846
|
* The slide's effective background (`p:cSld/p:bg`), decoded into a typed
|
|
2855
2847
|
* {@link SlideBackground}. Resolved through the inheritance chain: the slide's
|
|
2856
|
-
* 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
|
|
2857
2849
|
* result's `source` records which supplied it. `null` when nothing in the chain
|
|
2858
2850
|
* defines a background.
|
|
2859
2851
|
*
|
|
@@ -2899,20 +2891,24 @@ var Slide = class {
|
|
|
2899
2891
|
* `slide.slideNumber`, or added to a master defined with `slideNumber`; its geometry and run formatting
|
|
2900
2892
|
* read off the returned {@link AutoShape} the same as any placeholder.
|
|
2901
2893
|
*
|
|
2902
|
-
* Scoped to the slide's *own* shape tree
|
|
2894
|
+
* Scoped to the slide's *own* shape tree: a slide number inherited purely from
|
|
2903
2895
|
* the master's `p:hf`/placeholder (with no shape on the slide) is a master-level
|
|
2904
2896
|
* concern this getter does not resolve.
|
|
2905
2897
|
*/
|
|
2906
2898
|
get slideNumberPlaceholder() {
|
|
2907
2899
|
return this.placeholder("sldNum") ?? null;
|
|
2908
2900
|
}
|
|
2909
|
-
/**
|
|
2901
|
+
/**
|
|
2902
|
+
* The first top-level shape with the given drawing id (`p:cNvPr/@id`), or `undefined`. Takes
|
|
2903
|
+
* `null`, as a shape's `id` reads when its `p:cNvPr` has no `@id`, and misses on it.
|
|
2904
|
+
*/
|
|
2910
2905
|
shapeById(id) {
|
|
2906
|
+
if (id === null) return void 0;
|
|
2911
2907
|
return this.shapes.find((shape) => shape.id === id);
|
|
2912
2908
|
}
|
|
2913
2909
|
/**
|
|
2914
2910
|
* The first shape *anywhere* in the slide's shape tree with the given drawing id
|
|
2915
|
-
* (`p:cNvPr/@id`), descending into groups
|
|
2911
|
+
* (`p:cNvPr/@id`), descending into groups, unlike {@link shapeById}, which scans
|
|
2916
2912
|
* only top-level shapes. Walked pre-order (a group is visited before its children),
|
|
2917
2913
|
* matching how the writer allocates ids. `undefined` when no shape carries that id.
|
|
2918
2914
|
*
|
|
@@ -2931,8 +2927,9 @@ var Slide = class {
|
|
|
2931
2927
|
/**
|
|
2932
2928
|
* The first placeholder of the given type (`p:ph/@type`, e.g. `title`,
|
|
2933
2929
|
* `ctrTitle`, `subTitle`, `body`), optionally narrowed by `idx`. Returns
|
|
2934
|
-
* `undefined` when none match. Only
|
|
2935
|
-
*
|
|
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.
|
|
2936
2933
|
*/
|
|
2937
2934
|
placeholder(type, idx) {
|
|
2938
2935
|
return this.shapes.find((shape) => {
|
|
@@ -3012,7 +3009,7 @@ var Slide = class {
|
|
|
3012
3009
|
* afterwards through {@link notesTextFrame}.
|
|
3013
3010
|
*
|
|
3014
3011
|
* The counterpart to the read side's `notesText`/`notesTextFrame`/`notesSlide`
|
|
3015
|
-
* 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**:
|
|
3016
3013
|
* the state an `importSlide` without `{ importNotes: true }` leaves behind, and
|
|
3017
3014
|
* the one a `notesTextFrame` edit cannot reach because there is no frame to
|
|
3018
3015
|
* hand back. Calling it on a slide that already has notes replaces the body
|
|
@@ -3067,8 +3064,7 @@ var Slide = class {
|
|
|
3067
3064
|
return spTreeOf(this.part.dom.documentElement);
|
|
3068
3065
|
}
|
|
3069
3066
|
};
|
|
3070
|
-
|
|
3071
|
-
//#region src/read/api/document-properties.ts
|
|
3067
|
+
// src/read/api/document-properties.ts
|
|
3072
3068
|
/** `dc:title`-style core-property fields, keyed by output field → element qname. */
|
|
3073
3069
|
const CORE_FIELDS = [
|
|
3074
3070
|
["title", "dc:title"],
|
|
@@ -3138,7 +3134,7 @@ function readExtendedProperties(opc) {
|
|
|
3138
3134
|
* number; real types (`r4`/`r8`/`decimal`) → number; `bool` → boolean;
|
|
3139
3135
|
* `filetime`/`date` → the **raw string** (see {@link CoreProperties} on
|
|
3140
3136
|
* timestamps). Any unrecognized `vt:` element falls back to its text content as a
|
|
3141
|
-
* string
|
|
3137
|
+
* string, which is lossy but never throws.
|
|
3142
3138
|
*/
|
|
3143
3139
|
function decodeValue(vt) {
|
|
3144
3140
|
const text = vt.textContent ?? "";
|
|
@@ -3195,13 +3191,12 @@ function readCustomProperties(opc) {
|
|
|
3195
3191
|
}
|
|
3196
3192
|
return out;
|
|
3197
3193
|
}
|
|
3198
|
-
|
|
3199
|
-
//#region src/read/api/ops/embedded-fonts.ts
|
|
3194
|
+
// src/read/api/ops/embedded-fonts.ts
|
|
3200
3195
|
/**
|
|
3201
3196
|
* Merging embedded font faces into a deck's `p:embeddedFontLst`.
|
|
3202
3197
|
*
|
|
3203
|
-
* Fonts arrive two ways
|
|
3204
|
-
* 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.
|
|
3205
3200
|
* That difference is confined to a per-face thunk, so both paths share one merge core: entries
|
|
3206
3201
|
* join by typeface, faces de-dupe by slot, and the thunk runs only for a face actually being
|
|
3207
3202
|
* added, so a face the deck already embeds never mints an orphan part.
|
|
@@ -3263,7 +3258,7 @@ function readEmbeddedFontEntries(deck) {
|
|
|
3263
3258
|
* every face this deck would copy names a part that is actually there.
|
|
3264
3259
|
*
|
|
3265
3260
|
* Every import that carries fonts calls it before it changes anything, because the carry runs
|
|
3266
|
-
* last
|
|
3261
|
+
* last (after the page or the masters are already in the deck), and a face it cannot copy would
|
|
3267
3262
|
* throw with parts added and no way back. A face whose `r:id` names no relationship and a face
|
|
3268
3263
|
* whose relationship names no part are the same failure to a caller, the face's binary is not in
|
|
3269
3264
|
* the package, so both are `package/part-missing`.
|
|
@@ -3346,7 +3341,7 @@ function carryGeneratedEmbeddedFonts(dest, fonts) {
|
|
|
3346
3341
|
}
|
|
3347
3342
|
/**
|
|
3348
3343
|
* Merge normalized {@link IncomingEmbeddedFont} entries into this deck's
|
|
3349
|
-
* `p:embeddedFontLst
|
|
3344
|
+
* `p:embeddedFontLst`: the shared core of {@link carryEmbeddedFonts} (import-side)
|
|
3350
3345
|
* and {@link carryGeneratedEmbeddedFonts} (append-side). Entries merge by `typeface`,
|
|
3351
3346
|
* faces de-dupe by slot (a face this deck already embeds is left as is). For each newly
|
|
3352
3347
|
* added face the `fntdata` Default is ensured, the binary part is created via the face's
|
|
@@ -3396,10 +3391,128 @@ function mergeEmbeddedFontEntries(dest, entries) {
|
|
|
3396
3391
|
presPart.markDirty();
|
|
3397
3392
|
}
|
|
3398
3393
|
}
|
|
3399
|
-
|
|
3400
|
-
|
|
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
|
+
}
|
|
3401
3448
|
/**
|
|
3402
|
-
*
|
|
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
|
|
3514
|
+
/**
|
|
3515
|
+
* pptx-ts: slide-size preconditions for the import surface
|
|
3403
3516
|
*
|
|
3404
3517
|
* Every import entry point starts by asking whether the two decks agree on canvas size, and
|
|
3405
3518
|
* the five that asked wrote out the same comparison, the same `unknown`-printing formatter
|
|
@@ -3409,9 +3522,9 @@ function mergeEmbeddedFontEntries(dest, entries) {
|
|
|
3409
3522
|
* except `importSlide`, and only when a rescale had been asked for.
|
|
3410
3523
|
*
|
|
3411
3524
|
* The two conditions are separated here, and the codes now mean what their names say:
|
|
3412
|
-
* - `import/slide-size-unknown
|
|
3525
|
+
* - `import/slide-size-unknown`: a deck does not declare `p:sldSz`, so there is no size to
|
|
3413
3526
|
* compare and no rescale to compute. Nothing the caller passes can make it comparable.
|
|
3414
|
-
* - `import/slide-size-mismatch
|
|
3527
|
+
* - `import/slide-size-mismatch`: both sizes are known and they differ. This one *is*
|
|
3415
3528
|
* answerable, which is why each caller supplies the escape hatch it offers.
|
|
3416
3529
|
*/
|
|
3417
3530
|
/** A size as the error messages print it. */
|
|
@@ -3422,7 +3535,7 @@ function fmt(size) {
|
|
|
3422
3535
|
* Both decks' sizes, or a throw when either deck does not declare one.
|
|
3423
3536
|
*
|
|
3424
3537
|
* Returned rather than asserted because every caller that needs both sizes known needs the
|
|
3425
|
-
* values too
|
|
3538
|
+
* values too, to compute a rescale, and TypeScript cannot narrow two parameters from one
|
|
3426
3539
|
* assertion signature.
|
|
3427
3540
|
*
|
|
3428
3541
|
* @param target - this deck's slide size
|
|
@@ -3447,19 +3560,18 @@ function requireEqualSlideSize(target, incoming, api, hint = "") {
|
|
|
3447
3560
|
if (known.widthEmu === source.widthEmu && known.heightEmu === source.heightEmu) return;
|
|
3448
3561
|
throw new InvalidOptionError("import/slide-size-mismatch", `${api} requires equal slide sizes${hint ? ` (${hint})` : ""}; target is ${fmt(target)}, source is ${fmt(incoming)}`);
|
|
3449
3562
|
}
|
|
3450
|
-
/** Whether both sizes are known and equal
|
|
3563
|
+
/** Whether both sizes are known and equal: the question a caller with a rescale asks first. */
|
|
3451
3564
|
function slideSizesMatch(target, incoming) {
|
|
3452
3565
|
return !!target && !!incoming && target.widthEmu === incoming.widthEmu && target.heightEmu === incoming.heightEmu;
|
|
3453
3566
|
}
|
|
3454
|
-
|
|
3455
|
-
//#region src/read/api/ops/append-slides.ts
|
|
3567
|
+
// src/read/api/ops/append-slides.ts
|
|
3456
3568
|
/**
|
|
3457
|
-
* ts
|
|
3569
|
+
* pptx-ts: the body of {@link Presentation.appendSlides}
|
|
3458
3570
|
*
|
|
3459
3571
|
* The hybrid "generate onto an existing deck" path: a slide producer authors slides, they are
|
|
3460
|
-
* spliced into this package under fresh partnames, and every dependency each one carries
|
|
3461
|
-
* images, embedded and online media, hyperlinks, speaker notes, charts,
|
|
3462
|
-
*
|
|
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`.
|
|
3463
3575
|
*
|
|
3464
3576
|
* Here rather than on the class for the reason `presentation.ts` states about the four import
|
|
3465
3577
|
* bodies: the *contract* is the doc comment a caller reads and stays on the public method, while
|
|
@@ -3472,12 +3584,12 @@ function slideSizesMatch(target, incoming) {
|
|
|
3472
3584
|
*/
|
|
3473
3585
|
const textEncoder$1 = new TextEncoder();
|
|
3474
3586
|
/**
|
|
3475
|
-
* Resolve `options.layout`
|
|
3476
|
-
* 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
|
|
3477
3589
|
* default layout, because a slide bound to the wrong layout is a deck that looks authored.
|
|
3478
3590
|
*
|
|
3479
3591
|
* Split out because it is the part of the append most likely to be wanted by a second caller, and
|
|
3480
|
-
* 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
|
|
3481
3593
|
* `layout/foreign-handle` are raised.
|
|
3482
3594
|
*
|
|
3483
3595
|
* @param deck - the destination deck, whose `layouts()` gallery is searched
|
|
@@ -3486,6 +3598,8 @@ const textEncoder$1 = new TextEncoder();
|
|
|
3486
3598
|
*/
|
|
3487
3599
|
function resolveLayoutTarget(deck, layout, api = "appendSlides") {
|
|
3488
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()}`);
|
|
3489
3603
|
if (typeof layout !== "string") {
|
|
3490
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`);
|
|
3491
3605
|
return layout;
|
|
@@ -3493,10 +3607,7 @@ function resolveLayoutTarget(deck, layout, api = "appendSlides") {
|
|
|
3493
3607
|
const matches = gallery.filter((l) => l.name === layout);
|
|
3494
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`);
|
|
3495
3609
|
const [only] = matches;
|
|
3496
|
-
if (!only) {
|
|
3497
|
-
const names = gallery.map((l) => JSON.stringify(l.name)).join(", ");
|
|
3498
|
-
throw new InvalidOptionError("layout/not-found", `${api}: no layout named ${JSON.stringify(layout)}; available: ${names || "(none)"}`);
|
|
3499
|
-
}
|
|
3610
|
+
if (!only) throw new InvalidOptionError("layout/not-found", `${api}: no layout named ${JSON.stringify(layout)}; available: ${names()}`);
|
|
3500
3611
|
return only;
|
|
3501
3612
|
}
|
|
3502
3613
|
/** Image parts behind the slide body's `image` rels, each keeping the id the body was built with. */
|
|
@@ -3528,8 +3639,20 @@ function wireAvMedia(deck, rels, partName, slide) {
|
|
|
3528
3639
|
}
|
|
3529
3640
|
}
|
|
3530
3641
|
/**
|
|
3531
|
-
*
|
|
3532
|
-
*
|
|
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
|
|
3533
3656
|
* wired by {@link wireMedia}, which runs first.
|
|
3534
3657
|
*/
|
|
3535
3658
|
function wireOnlineMedia(rels, slide) {
|
|
@@ -3544,7 +3667,7 @@ function wireHyperlinks(rels, slide) {
|
|
|
3544
3667
|
}
|
|
3545
3668
|
/**
|
|
3546
3669
|
* Speaker notes. The notes part carries its own rel namespace, independent of the slide's:
|
|
3547
|
-
* 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
|
|
3548
3671
|
* generator's body was serialized against, so these are added by explicit id rather than left to
|
|
3549
3672
|
* auto-numbering.
|
|
3550
3673
|
*
|
|
@@ -3552,12 +3675,13 @@ function wireHyperlinks(rels, slide) {
|
|
|
3552
3675
|
* so {@link wireAutoIdRels} adds it once every id the body does name is claimed.
|
|
3553
3676
|
* @returns the notes part's name, or `null` when the slide has no notes
|
|
3554
3677
|
*/
|
|
3555
|
-
function wireNotes(deck, partName, slide, sourceNotesMaster) {
|
|
3678
|
+
function wireNotes(deck, partName, slide, sourceNotesMaster, partBySourceNumber, index) {
|
|
3556
3679
|
if (!slide.notes) return null;
|
|
3557
3680
|
const notesMasterPartName = sourceNotesMaster ? ensureNotesMasterFromXml(deck, sourceNotesMaster) : null;
|
|
3558
3681
|
const notesPartName = addNotesSlidePart(deck, slide.notes.xml, partName, notesMasterPartName);
|
|
3559
3682
|
const notesRels = deck.opc.relationshipsFor(notesPartName);
|
|
3560
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);
|
|
3561
3685
|
return notesPartName;
|
|
3562
3686
|
}
|
|
3563
3687
|
/**
|
|
@@ -3567,7 +3691,7 @@ function wireNotes(deck, partName, slide, sourceNotesMaster) {
|
|
|
3567
3691
|
*
|
|
3568
3692
|
* A chartEx chart differs in every other coordinate: the `chartEx{N}.xml` name family, the MS
|
|
3569
3693
|
* chartex content type, the MS `chartEx` rel off the slide, and two mandatory sidecars at
|
|
3570
|
-
* rId2/rId3
|
|
3694
|
+
* rId2/rId3, without which PowerPoint reports the deck as corrupt (schema-valid but unopenable).
|
|
3571
3695
|
* `c.chartEx` carries both sidecar bodies; the generator built them, so nothing here reaches into
|
|
3572
3696
|
* the emitters.
|
|
3573
3697
|
*/
|
|
@@ -3581,6 +3705,11 @@ function wireCharts(deck, rels, partName, slide) {
|
|
|
3581
3705
|
deck.opc.addPart(embeddingPartName, XLSX_CONTENT_TYPE, c.embeddingBytes);
|
|
3582
3706
|
const chartRels = deck.opc.relationshipsFor(chartPartName);
|
|
3583
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
|
+
}
|
|
3584
3713
|
if (c.chartEx) {
|
|
3585
3714
|
const colorsPartName = deck.opc.reservePartNameLike("/ppt/charts/colors1.xml");
|
|
3586
3715
|
deck.opc.addPart(colorsPartName, CHART_COLOR_STYLE_CONTENT_TYPE, textEncoder$1.encode(c.chartEx.colorsXml));
|
|
@@ -3593,12 +3722,12 @@ function wireCharts(deck, rels, partName, slide) {
|
|
|
3593
3722
|
}
|
|
3594
3723
|
}
|
|
3595
3724
|
/**
|
|
3596
|
-
* Intra-batch slide-to-slide links: a `slide:N` in the source is
|
|
3597
|
-
* 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
|
|
3598
3727
|
* of the batch was refused before pass 1 by {@link requireLinksInBatch}.
|
|
3599
3728
|
*/
|
|
3600
|
-
function wireSlideLinks(rels, partName,
|
|
3601
|
-
for (const link of
|
|
3729
|
+
function wireSlideLinks(rels, partName, links, partBySourceNumber, index) {
|
|
3730
|
+
for (const link of links) {
|
|
3602
3731
|
const targetPartName = partBySourceNumber.get(link.sourceSlideNumber);
|
|
3603
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`);
|
|
3604
3733
|
rels.addWithId(`rId${link.rId}`, SLIDE_REL, relativePartName(partName, targetPartName));
|
|
@@ -3611,12 +3740,13 @@ function wireSlideLinks(rels, partName, slide, partBySourceNumber, index) {
|
|
|
3611
3740
|
*/
|
|
3612
3741
|
function requireLinksInBatch(slides) {
|
|
3613
3742
|
slides.forEach((slide, index) => {
|
|
3614
|
-
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`);
|
|
3615
3744
|
});
|
|
3616
3745
|
}
|
|
3617
3746
|
/**
|
|
3618
|
-
* The first of pass 2's two phases: every rel the slide body names by id
|
|
3619
|
-
* 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.
|
|
3620
3750
|
* Also creates the notes part, whose own rels are fixed ids too.
|
|
3621
3751
|
*
|
|
3622
3752
|
* A new rel the body references by id belongs here, and nowhere after {@link wireAutoIdRels}.
|
|
@@ -3626,24 +3756,26 @@ function wireFixedIdRels(deck, rels, placed, sourceNotesMaster, partBySourceNumb
|
|
|
3626
3756
|
const { slide, partName } = placed;
|
|
3627
3757
|
wireMedia(deck, rels, partName, slide);
|
|
3628
3758
|
wireAvMedia(deck, rels, partName, slide);
|
|
3759
|
+
wireTransitionSound(deck, rels, partName, slide);
|
|
3629
3760
|
wireOnlineMedia(rels, slide);
|
|
3630
3761
|
wireHyperlinks(rels, slide);
|
|
3631
|
-
const notesPartName = wireNotes(deck, partName, slide, sourceNotesMaster);
|
|
3762
|
+
const notesPartName = wireNotes(deck, partName, slide, sourceNotesMaster, partBySourceNumber, index);
|
|
3632
3763
|
wireCharts(deck, rels, partName, slide);
|
|
3633
|
-
wireSlideLinks(rels, partName, slide, partBySourceNumber, index);
|
|
3764
|
+
wireSlideLinks(rels, partName, slide.slideLinks, partBySourceNumber, index);
|
|
3634
3765
|
return notesPartName;
|
|
3635
3766
|
}
|
|
3636
3767
|
/**
|
|
3637
|
-
* The second phase: the rels the slide body names by no id
|
|
3638
|
-
* 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.
|
|
3639
3770
|
*
|
|
3640
3771
|
* Strictly after {@link wireFixedIdRels}. The generator numbers a slide's rels from rId1, so an
|
|
3641
3772
|
* auto id taken before a fixed one is claimed is routinely that fixed one: the notes rel used to
|
|
3642
3773
|
* be added in the middle of the first phase, and a slide carrying notes and a chart or a slide
|
|
3643
3774
|
* link threw `relationship/duplicate-id`.
|
|
3644
3775
|
*/
|
|
3645
|
-
function wireAutoIdRels(rels, partName, notesPartName, layoutPartName) {
|
|
3776
|
+
function wireAutoIdRels(rels, partName, notesPartName, commentsPartName, layoutPartName) {
|
|
3646
3777
|
if (notesPartName) rels.add(NOTES_SLIDE_REL, relativePartName(partName, notesPartName));
|
|
3778
|
+
if (commentsPartName) rels.add(COMMENTS_REL, relativePartName(partName, commentsPartName));
|
|
3647
3779
|
rels.add(SLIDE_LAYOUT_REL, relativePartName(partName, layoutPartName));
|
|
3648
3780
|
}
|
|
3649
3781
|
/**
|
|
@@ -3651,7 +3783,7 @@ function wireAutoIdRels(rels, partName, notesPartName, layoutPartName) {
|
|
|
3651
3783
|
* {@link Presentation.appendSlides}.
|
|
3652
3784
|
*/
|
|
3653
3785
|
async function appendSlides(deck, source, options) {
|
|
3654
|
-
const target = resolveLayoutTarget(deck, options
|
|
3786
|
+
const target = resolveLayoutTarget(deck, options?.layout);
|
|
3655
3787
|
const extracted = await source.extractSlides(pickDefined(options, ["onMediaError"]));
|
|
3656
3788
|
requireEqualSlideSize(deck.slideSize, {
|
|
3657
3789
|
widthEmu: extracted.widthEmu,
|
|
@@ -3675,15 +3807,16 @@ async function appendSlides(deck, source, options) {
|
|
|
3675
3807
|
placed.forEach((placedSlide, i) => {
|
|
3676
3808
|
const rels = deck.opc.relationshipsFor(placedSlide.partName);
|
|
3677
3809
|
const notesPartName = wireFixedIdRels(deck, rels, placedSlide, extracted.notesMaster, partBySourceNumber, i);
|
|
3678
|
-
|
|
3810
|
+
const comments = placedSlide.slide.comments;
|
|
3811
|
+
const commentsPartName = comments?.length ? addCommentsPart(deck, comments) : null;
|
|
3812
|
+
wireAutoIdRels(rels, placedSlide.partName, notesPartName, commentsPartName, target.partName);
|
|
3679
3813
|
const at = options.at === void 0 ? void 0 : options.at + i;
|
|
3680
3814
|
added.push(deck.insertSlidePart(placedSlide.part, at));
|
|
3681
3815
|
});
|
|
3682
3816
|
carryGeneratedEmbeddedFonts(deck, extracted.embeddedFonts || []);
|
|
3683
3817
|
return added;
|
|
3684
3818
|
}
|
|
3685
|
-
|
|
3686
|
-
//#region src/read/api/ops/prune.ts
|
|
3819
|
+
// src/read/api/ops/prune.ts
|
|
3687
3820
|
/**
|
|
3688
3821
|
* Content types that are shared deck chrome, not owned by any one slide: the `deck` rows of
|
|
3689
3822
|
* {@link SHARED_PARTS}, reached through the presentation → master → layout → theme graph or named
|
|
@@ -3815,8 +3948,7 @@ function isReferenced(pres, partName) {
|
|
|
3815
3948
|
function relationshipOwners(pres) {
|
|
3816
3949
|
return [...[...pres.opc.parts.keys()].filter((partName) => !partName.endsWith(".rels")), "/"];
|
|
3817
3950
|
}
|
|
3818
|
-
|
|
3819
|
-
//#region src/read/api/ops/import-memo.ts
|
|
3951
|
+
// src/read/api/ops/import-memo.ts
|
|
3820
3952
|
var ImportMemo = class {
|
|
3821
3953
|
#registries = /* @__PURE__ */ new Map();
|
|
3822
3954
|
#rescaled = /* @__PURE__ */ new Map();
|
|
@@ -3840,14 +3972,13 @@ var ImportMemo = class {
|
|
|
3840
3972
|
for (const partName of gone) this.#rescaled.delete(partName);
|
|
3841
3973
|
}
|
|
3842
3974
|
};
|
|
3843
|
-
|
|
3844
|
-
//#region src/read/api/ops/flatten.ts
|
|
3975
|
+
// src/read/api/ops/flatten.ts
|
|
3845
3976
|
/**
|
|
3846
3977
|
* Baking a slide's theme dependencies into the slide itself, for `Presentation.importSlide` /
|
|
3847
3978
|
* `importShape`.
|
|
3848
3979
|
*
|
|
3849
3980
|
* A slide imported into another deck is rebound to the *destination* master, so everything it
|
|
3850
|
-
* 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
|
|
3851
3982
|
* scheme colours change, its style-matrix fills change, its placeholders snap to different
|
|
3852
3983
|
* geometry and type sizes. The `theme: 'preserve'` mode exists to stop that: each pass here
|
|
3853
3984
|
* resolves a dependency against the **source** theme (via `read/oxml/theme.ts` and
|
|
@@ -3856,25 +3987,25 @@ var ImportMemo = class {
|
|
|
3856
3987
|
*
|
|
3857
3988
|
* What gets flattened, and why each one matters:
|
|
3858
3989
|
*
|
|
3859
|
-
* 1. **`a:schemeClr` tokens
|
|
3990
|
+
* 1. **`a:schemeClr` tokens**: rewritten to the literal `a:srgbClr` the source `clrMap` +
|
|
3860
3991
|
* `clrScheme` produce. Child transforms (`lumMod`/`shade`/`alpha`/…) are carried through
|
|
3861
3992
|
* untouched, so tints and shades render identically; only the *base* reference is swapped,
|
|
3862
3993
|
* never the transform math.
|
|
3863
|
-
* 2. **Style-matrix refs
|
|
3994
|
+
* 2. **Style-matrix refs**: a shape's `p:style` (`lnRef`/`fillRef`/`effectRef`) indexes the
|
|
3864
3995
|
* theme `fmtScheme`; the indexed entry is resolved into an explicit `spPr` fill/line/effect
|
|
3865
3996
|
* (with its `phClr` substituted) and the ref neutralized so it cannot re-resolve. The
|
|
3866
|
-
* `fontRef` is deliberately left intact so its font can re-bind to the destination theme
|
|
3867
|
-
* the "normalize fonts on attach" bonus.
|
|
3868
|
-
* 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
|
|
3869
4000
|
* takes them from the source placeholder → layout → master text-style chain. Rebinding
|
|
3870
4001
|
* replaces that chain, so each is resolved from the source styles and written onto the run.
|
|
3871
|
-
* Typeface (`a:latin`) is deliberately *not* baked
|
|
4002
|
+
* Typeface (`a:latin`) is deliberately *not* baked; it re-binds along with `fontRef`.
|
|
3872
4003
|
* A shape that is not a placeholder takes the same properties from the presentation's
|
|
3873
4004
|
* `p:defaultTextStyle` instead, which the rebind swaps for the destination's, so those
|
|
3874
4005
|
* are baked from the source one. A table cell's text takes its size from the master's
|
|
3875
4006
|
* `p:otherStyle`, and its weight too where the table style states none, so those are
|
|
3876
4007
|
* baked from the source master.
|
|
3877
|
-
* 4. **Placeholder-inherited geometry
|
|
4008
|
+
* 4. **Placeholder-inherited geometry**: a placeholder with no own `a:xfrm` takes position and
|
|
3878
4009
|
* size from the matching source layout/master placeholder; without baking it, a title snaps
|
|
3879
4010
|
* to the destination default and often clips off-canvas.
|
|
3880
4011
|
*
|
|
@@ -3897,7 +4028,7 @@ var ImportMemo = class {
|
|
|
3897
4028
|
* 4. bake each placeholder run's effective colour and size/weight (inherited from
|
|
3898
4029
|
* the source layout/master text styles) explicitly onto the run, each other
|
|
3899
4030
|
* shape's run the ones it takes from the source `p:defaultTextStyle`, and each
|
|
3900
|
-
* table cell's run the ones it takes from the source `p:otherStyle
|
|
4031
|
+
* table cell's run the ones it takes from the source `p:otherStyle` and theme;
|
|
3901
4032
|
* 5. rewrite every remaining `a:schemeClr` to its literal `a:srgbClr`.
|
|
3902
4033
|
*
|
|
3903
4034
|
* Steps run in this order so the inherited/materialized backgrounds are present
|
|
@@ -3917,7 +4048,7 @@ function flattenSlide(slideRoot, ctx) {
|
|
|
3917
4048
|
resolveSchemeColors(slideRoot, ctx);
|
|
3918
4049
|
}
|
|
3919
4050
|
/**
|
|
3920
|
-
* Flatten a single lifted shape's theme dependencies in place
|
|
4051
|
+
* Flatten a single lifted shape's theme dependencies in place: the shape-scoped
|
|
3921
4052
|
* subset of {@link flattenSlide} used by `Presentation.importShape` `preserve`
|
|
3922
4053
|
* mode. It runs every pass that resolves a *shape's* theme references against the
|
|
3923
4054
|
* source theme (style-matrix refs, placeholder-inherited geometry/colour/size/
|
|
@@ -3938,7 +4069,7 @@ function flattenSlide(slideRoot, ctx) {
|
|
|
3938
4069
|
*
|
|
3939
4070
|
* `shapeRoot` must be an element whose *descendants* include the lifted shape
|
|
3940
4071
|
* (the passes match via `getElementsByTagNameNS`, which excludes the root element
|
|
3941
|
-
* itself)
|
|
4072
|
+
* itself), so the caller wraps the imported `p:sp`/`p:pic`/`p:graphicFrame`/`p:grpSp`
|
|
3942
4073
|
* in a throwaway container before calling. The caller marks the part dirty.
|
|
3943
4074
|
*/
|
|
3944
4075
|
function flattenShape(shapeRoot, ctx) {
|
|
@@ -3955,7 +4086,7 @@ function flattenShape(shapeRoot, ctx) {
|
|
|
3955
4086
|
}
|
|
3956
4087
|
/**
|
|
3957
4088
|
* Restyle a rebound slide in place (the `theme: 'restyle'` mode): the exact
|
|
3958
|
-
* inverse of {@link flattenSlide}. It bakes *nothing
|
|
4089
|
+
* inverse of {@link flattenSlide}. It bakes *nothing*: leaving every
|
|
3959
4090
|
* `a:schemeClr`/style-matrix ref and `p:bg` `bgRef` symbolic is the whole point,
|
|
3960
4091
|
* so they re-resolve against the *destination* master's `clrMap` + theme once the
|
|
3961
4092
|
* slide is rebound. The single mutation is dropping the slide's own colour-map
|
|
@@ -3978,14 +4109,14 @@ function restyleSlide(slideRoot) {
|
|
|
3978
4109
|
* destination theme; a literal matching no slot (the common case) is left exactly
|
|
3979
4110
|
* as authored. Transform children (`lumMod`/`shade`/`alpha`/…) are carried across.
|
|
3980
4111
|
*
|
|
3981
|
-
* Plain `restyle` cannot re-brand a literal
|
|
4112
|
+
* Plain `restyle` cannot re-brand a literal, since it carries no theme reference. This
|
|
3982
4113
|
* re-introduces one by matching the literal's RGB against the slots the *source*
|
|
3983
4114
|
* theme defined, emitting the scheme token that routes through the source `clrMap`
|
|
3984
4115
|
* (so a literal equal to the source `accent1` slot becomes `schemeClr accent1`,
|
|
3985
4116
|
* which the destination `clrMap`/theme then re-resolve). Slots are matched in
|
|
3986
4117
|
* `clrScheme` order, so the first slot defining a given RGB wins when several share
|
|
3987
4118
|
* it. Opt-in because it deliberately reinterprets authored literals as theme
|
|
3988
|
-
* colours
|
|
4119
|
+
* colours: visual QA territory, the same caveat as the rest of `restyle`.
|
|
3989
4120
|
*/
|
|
3990
4121
|
function remapLiteralColors(slideRoot, ctx) {
|
|
3991
4122
|
const slotByHex = reverseClrScheme(ctx.clrScheme);
|
|
@@ -4074,7 +4205,7 @@ function resolvePlaceholderRunColors(slideRoot, ctx) {
|
|
|
4074
4205
|
* `resolve` is asked once per *list level* per shape and memoized, because walking the
|
|
4075
4206
|
* layout/master style chain is the expensive half and every paragraph at the same level gets
|
|
4076
4207
|
* the same answer. `write` then runs per run, and owns the decision to skip a run the slide
|
|
4077
|
-
* already fixes
|
|
4208
|
+
* already fixes. The two passes ask that question differently (one colour check versus three
|
|
4078
4209
|
* independent property checks), so it stays on their side of the boundary rather than becoming
|
|
4079
4210
|
* a predicate parameter only one caller could use.
|
|
4080
4211
|
*
|
|
@@ -4169,7 +4300,7 @@ function resolvePlaceholderGeometry(slideRoot, ctx) {
|
|
|
4169
4300
|
* We resolve the effective anchor from the source chain and write it explicitly.
|
|
4170
4301
|
* Shapes whose `a:bodyPr` already fixes `@anchor`, and placeholders with no
|
|
4171
4302
|
* resolvable inherited anchor, are left untouched. Other `a:bodyPr` knobs (insets,
|
|
4172
|
-
* autofit) are left as authored
|
|
4303
|
+
* autofit) are left as authored; they have sane defaults a plain shape keeps.
|
|
4173
4304
|
*/
|
|
4174
4305
|
function resolvePlaceholderBodyPr(shapeRoot, ctx) {
|
|
4175
4306
|
forEachPlaceholderTextBody(shapeRoot, ctx, (txBody, type, idx) => {
|
|
@@ -4182,21 +4313,20 @@ function resolvePlaceholderBodyPr(shapeRoot, ctx) {
|
|
|
4182
4313
|
}
|
|
4183
4314
|
/**
|
|
4184
4315
|
* Bake the placeholder-inherited list style onto a lifted shape's text body (the
|
|
4185
|
-
* {@link flattenShape} placeholder-demotion path). A placeholder
|
|
4186
|
-
* *paragraph* formatting
|
|
4187
|
-
* `
|
|
4188
|
-
*
|
|
4189
|
-
*
|
|
4190
|
-
*
|
|
4191
|
-
*
|
|
4192
|
-
*
|
|
4193
|
-
* 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`.
|
|
4194
4324
|
*
|
|
4195
4325
|
* Resolution is per *level*, most specific tier wins whole: the slide's own level
|
|
4196
4326
|
* (if it defines one) is kept verbatim, else the first source tier that defines it
|
|
4197
4327
|
* (layout placeholder, then master placeholder, then master category style) is
|
|
4198
4328
|
* cloned. This is a whole-element overlay, not the per-attribute merge PowerPoint
|
|
4199
|
-
* does
|
|
4329
|
+
* does, but explicit paragraph `a:pPr` on the slide's own runs travels with the
|
|
4200
4330
|
* shape and still wins, so this only supplies defaults for the inherited case
|
|
4201
4331
|
* (paragraphs that set no `a:pPr` of their own). Scheme colours in the cloned
|
|
4202
4332
|
* levels are resolved by the later `resolveSchemeColors` pass.
|
|
@@ -4223,7 +4353,7 @@ function resolvePlaceholderListStyle(shapeRoot, ctx) {
|
|
|
4223
4353
|
* Strip the `p:ph` marker from every placeholder shape in a lifted subtree so it
|
|
4224
4354
|
* becomes a self-contained ordinary shape (the {@link flattenShape} path only). By
|
|
4225
4355
|
* the time this runs, the shape's inherited geometry, colour, run size, anchor, and
|
|
4226
|
-
* 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)
|
|
4227
4357
|
* its placeholder identity: a surviving `p:ph` would re-resolve against the *host*
|
|
4228
4358
|
* deck's layout/master placeholder of the same type/idx (wrong inheritance, or a
|
|
4229
4359
|
* fallback when the host has none) and could collide with the host slide's own
|
|
@@ -4241,7 +4371,7 @@ function demotePlaceholders(shapeRoot) {
|
|
|
4241
4371
|
* {@link resolvePlaceholderRunColors}: for each placeholder run that sets no
|
|
4242
4372
|
* `sz`/`b`/`i` of its own (nor at paragraph/text-body level on the *slide*),
|
|
4243
4373
|
* resolve the value it would inherit from the source `slideLayout`/`slideMaster`
|
|
4244
|
-
* text styles
|
|
4374
|
+
* text styles, per paragraph list level, and write it explicitly onto the run's
|
|
4245
4375
|
* `a:rPr`. Each property resolves independently up the chain. Typeface (`a:latin`)
|
|
4246
4376
|
* is left to re-bind to the destination theme, as the colour pass does for the `fontRef`.
|
|
4247
4377
|
*/
|
|
@@ -4294,25 +4424,26 @@ function resolveDefaultTextStyleRuns(root, ctx) {
|
|
|
4294
4424
|
}
|
|
4295
4425
|
}
|
|
4296
4426
|
/**
|
|
4297
|
-
* Bake what a table cell's runs take from the source
|
|
4298
|
-
*
|
|
4299
|
-
*
|
|
4427
|
+
* Bake what a table cell's runs take from the source deck: size, and bold and italic where the
|
|
4428
|
+
* cell's table style states neither, from the master's `p:otherStyle` per paragraph level, and the
|
|
4429
|
+
* colour from the table style's text style, else `tx1`, resolved against the source theme. Each is
|
|
4430
|
+
* written wherever the run, its paragraph and the cell's own list style state none of it.
|
|
4300
4431
|
*
|
|
4301
4432
|
* Cell text resolves through `p:otherStyle`, not `p:defaultTextStyle` (`TableCell.textFrame`), and
|
|
4302
|
-
* the rebind swaps the source master for the destination's, so without this a cell's text
|
|
4303
|
-
* size between two decks whose masters differ
|
|
4304
|
-
* writes nothing onto the cells; it keeps them on a copy of the source master
|
|
4305
|
-
* (`test/read/fixtures/authoring/probe-table-text-paste.ps1`), which is the look this pins.
|
|
4433
|
+
* the rebind swaps the source master and theme for the destination's, so without this a cell's text
|
|
4434
|
+
* changes size and colour between two decks whose masters or themes differ. PowerPoint's Keep Source
|
|
4435
|
+
* Formatting paste writes nothing onto the cells; it keeps them on a copy of the source master
|
|
4436
|
+
* instead (`test/read/fixtures/authoring/probe-table-text-paste.ps1`), which is the look this pins.
|
|
4306
4437
|
*
|
|
4307
|
-
*
|
|
4308
|
-
*
|
|
4309
|
-
*
|
|
4310
|
-
*
|
|
4438
|
+
* Only the table's style id travels with the slide, and PowerPoint resolves that id against its own
|
|
4439
|
+
* built-in gallery, never a definition in the package. The source's `tableStyles.xml` says what the
|
|
4440
|
+
* style states, so bold and italic it states are left to it. Where the source does not define the
|
|
4441
|
+
* style, bold, italic and colour are left alone because what the style states is unknown. The face
|
|
4442
|
+
* re-binds to the destination theme, as it does for every other text tier.
|
|
4311
4443
|
*/
|
|
4312
4444
|
function resolveTableCellRuns(root, ctx) {
|
|
4313
4445
|
const txStyles = ctx.masterRoot ? firstChild(ctx.masterRoot, "p:txStyles") : null;
|
|
4314
4446
|
const otherStyle = txStyles && firstChild(txStyles, "p:otherStyle");
|
|
4315
|
-
if (!otherStyle) return;
|
|
4316
4447
|
for (const tbl of descendantsByTag(root, OOXML_NS.a, "tbl")) {
|
|
4317
4448
|
const tblPr = firstChild(tbl, "a:tblPr");
|
|
4318
4449
|
const styleId = (tblPr && firstChild(tblPr, "a:tableStyleId"))?.textContent?.trim() || null;
|
|
@@ -4335,25 +4466,51 @@ function resolveTableCellRuns(root, ctx) {
|
|
|
4335
4466
|
const txBody = firstChild(tc, "a:txBody");
|
|
4336
4467
|
if (!txBody) return;
|
|
4337
4468
|
const styled = style ? resolveTableCellTextStyle(style, flags, rowIndex, colIndex, rows.length, colCount, ctx) : null;
|
|
4469
|
+
const color = styleUnknown ? null : tableCellTextColor(styled, ownerDocumentOf(tbl), ctx);
|
|
4338
4470
|
const cellLst = firstChild(txBody, "a:lstStyle");
|
|
4339
4471
|
for (const p of getElements(txBody, "a:p")) {
|
|
4340
4472
|
const runs = [...getElements(p, "a:r"), ...getElements(p, "a:fld")];
|
|
4341
4473
|
if (runs.length === 0) continue;
|
|
4342
4474
|
const pPr = firstChild(p, "a:pPr");
|
|
4343
4475
|
const level = (pPr && numberValue(attr(pPr, "lvl"))) ?? 0;
|
|
4344
|
-
const defRPr = lstStyleLevelDefRPr(otherStyle, level);
|
|
4345
|
-
if (!defRPr) continue;
|
|
4476
|
+
const defRPr = otherStyle && lstStyleLevelDefRPr(otherStyle, level);
|
|
4346
4477
|
const props = {
|
|
4347
|
-
sz: attr(defRPr, "sz"),
|
|
4348
|
-
b: styleUnknown || styled?.bold != null ? null : attr(defRPr, "b"),
|
|
4349
|
-
i: styleUnknown || styled?.italic != null ? null : attr(defRPr, "i")
|
|
4478
|
+
sz: defRPr ? attr(defRPr, "sz") : null,
|
|
4479
|
+
b: !defRPr || styleUnknown || styled?.bold != null ? null : attr(defRPr, "b"),
|
|
4480
|
+
i: !defRPr || styleUnknown || styled?.italic != null ? null : attr(defRPr, "i")
|
|
4350
4481
|
};
|
|
4351
|
-
for (const run of runs)
|
|
4482
|
+
for (const run of runs) {
|
|
4483
|
+
writeRunProps(run, props, pPr, cellLst, level);
|
|
4484
|
+
if (color && !slideDefinesColor(run, pPr, cellLst, level)) writeRunColor(run, color);
|
|
4485
|
+
}
|
|
4352
4486
|
}
|
|
4353
4487
|
});
|
|
4354
4488
|
});
|
|
4355
4489
|
}
|
|
4356
4490
|
}
|
|
4491
|
+
/**
|
|
4492
|
+
* The colour a cell's text paints on the source deck: what its table style's text style names,
|
|
4493
|
+
* else `tx1`, as `TableCell.textFrame` resolves it. The transforms are rebuilt as elements so
|
|
4494
|
+
* {@link writeRunColor} re-emits them.
|
|
4495
|
+
*/
|
|
4496
|
+
function tableCellTextColor(styled, doc, ctx) {
|
|
4497
|
+
if (styled?.color) {
|
|
4498
|
+
const transforms = styled.color.transforms.map(({ name, value }) => {
|
|
4499
|
+
const t = createElement(doc, `a:${name}`);
|
|
4500
|
+
if (value !== null) setAttr(t, "val", value);
|
|
4501
|
+
return t;
|
|
4502
|
+
});
|
|
4503
|
+
return {
|
|
4504
|
+
hex: styled.color.hex,
|
|
4505
|
+
transforms
|
|
4506
|
+
};
|
|
4507
|
+
}
|
|
4508
|
+
const tx1 = resolveSchemeToken("tx1", ctx);
|
|
4509
|
+
return tx1 ? {
|
|
4510
|
+
hex: tx1,
|
|
4511
|
+
transforms: []
|
|
4512
|
+
} : null;
|
|
4513
|
+
}
|
|
4357
4514
|
/** The source deck's `a:tblStyle` with this id, or `null` when the source defines none. */
|
|
4358
4515
|
function sourceTableStyle(ctx, styleId) {
|
|
4359
4516
|
if (!ctx.tableStyles) return null;
|
|
@@ -4448,15 +4605,14 @@ function neutralizeRef(ref) {
|
|
|
4448
4605
|
setAttr(ref, "idx", "0");
|
|
4449
4606
|
for (const child of childElements(ref)) ref.removeChild(child);
|
|
4450
4607
|
}
|
|
4451
|
-
|
|
4452
|
-
//#region src/read/api/ops/carried-rels.ts
|
|
4608
|
+
// src/read/api/ops/carried-rels.ts
|
|
4453
4609
|
/**
|
|
4454
4610
|
* Relationship rewriting for content carried between packages.
|
|
4455
4611
|
*
|
|
4456
4612
|
* When a shape or decoration is deep-copied out of a source deck, every relationship
|
|
4457
4613
|
* reference it carries (`r:embed` on a picture fill, `r:id` on a chart frame, `r:link` on a
|
|
4458
4614
|
* linked image, …) still names an id in the *source* part's relationship list. Left alone
|
|
4459
|
-
* those ids either dangle or
|
|
4615
|
+
* those ids either dangle or, worse, silently resolve to whatever the destination part
|
|
4460
4616
|
* happens to have under the same id. This rewrites each one to a fresh relationship on the
|
|
4461
4617
|
* destination part, copying the referenced part into the destination package on first sight.
|
|
4462
4618
|
*
|
|
@@ -4516,8 +4672,7 @@ function carryRel(ctx, sourceRels, id, destPartName, destRels, relIdMap, owned)
|
|
|
4516
4672
|
if (!ownsIt) relIdMap.set(key, newId);
|
|
4517
4673
|
return newId;
|
|
4518
4674
|
}
|
|
4519
|
-
|
|
4520
|
-
//#region src/read/api/ops/flatten-context.ts
|
|
4675
|
+
// src/read/api/ops/flatten-context.ts
|
|
4521
4676
|
/**
|
|
4522
4677
|
* The background the slide effectively inherits from its source subgraph: the layout's
|
|
4523
4678
|
* `p:bg`, else the master's. Returns `null` when the slide carries its own `p:bg` (it stays
|
|
@@ -4550,8 +4705,7 @@ function sourceFlattenContext(sourceOpc, slidePartName) {
|
|
|
4550
4705
|
tableStyles: sourceOpc.partsByContentType("application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml")[0]?.dom.documentElement ?? null
|
|
4551
4706
|
};
|
|
4552
4707
|
}
|
|
4553
|
-
|
|
4554
|
-
//#region src/read/api/ops/table-styles.ts
|
|
4708
|
+
// src/read/api/ops/table-styles.ts
|
|
4555
4709
|
const TABLE_STYLES_DEFAULT_GUID = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
4556
4710
|
const textEncoder = new TextEncoder();
|
|
4557
4711
|
/**
|
|
@@ -4560,7 +4714,7 @@ const textEncoder = new TextEncoder();
|
|
|
4560
4714
|
* table resolves its `@tableStyleId` against the *destination* `tableStyles` and
|
|
4561
4715
|
* silently falls back when that id is absent; the source `<a:tblStyle>` is itself
|
|
4562
4716
|
* symbolic (scheme colours), so copying it verbatim under the **same** id lets the
|
|
4563
|
-
* 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
|
|
4564
4718
|
* rewrite needed. Idempotent: an id this deck already defines is left as-is, and a
|
|
4565
4719
|
* referenced id with no source definition is skipped. Creates and wires a
|
|
4566
4720
|
* `tableStyles.xml` part if this deck has none.
|
|
@@ -4587,7 +4741,7 @@ function copySourceTableStyles(dest, sourceOpc, slideRoot) {
|
|
|
4587
4741
|
}
|
|
4588
4742
|
/**
|
|
4589
4743
|
* Append each source `<a:tblStyle>` this deck does not already define, keyed by
|
|
4590
|
-
* `styleId
|
|
4744
|
+
* `styleId`: the shared core of {@link copySourceTableStyles} (which offers only
|
|
4591
4745
|
* the styles a restyled slide references) and {@link carryTableStyles} (which
|
|
4592
4746
|
* offers the source's whole list). Destination-wins per id: a style this deck
|
|
4593
4747
|
* already defines is left untouched, so a re-call adds nothing. Returns whether
|
|
@@ -4609,14 +4763,14 @@ function addTableStyles(destList, styles) {
|
|
|
4609
4763
|
* Copy the source deck's **whole** `tableStyles.xml` into this deck: every
|
|
4610
4764
|
* `<a:tblStyle>` it defines, plus its `a:tblStyleLst@def` (the default table
|
|
4611
4765
|
* style). Presentation-level, so a separate traversal from the master/layout copy
|
|
4612
|
-
* chain
|
|
4766
|
+
* chain, and whole-deck, since `tableStyles.xml` does not record which style
|
|
4613
4767
|
* belongs to which master. See `ImportSlideMastersOptions.tableStyles`.
|
|
4614
4768
|
*
|
|
4615
4769
|
* Styles union by `styleId` (destination-wins per id, via {@link addTableStyles}),
|
|
4616
4770
|
* but `def` is **source-wins**: a caller asking for the source's table styling
|
|
4617
4771
|
* wants its default too, and the destination's `def` is typically a generator stub
|
|
4618
4772
|
* rather than a deliberate choice. Carrying the styles without the `def` would be
|
|
4619
|
-
* the worse half-measure
|
|
4773
|
+
* the worse half-measure: the standard default GUID is one most templates also
|
|
4620
4774
|
* define, so a new table would silently resolve to the wrong style rather than
|
|
4621
4775
|
* visibly to none. Idempotent: a re-call re-adds nothing and re-sets `def` to the
|
|
4622
4776
|
* same value.
|
|
@@ -4653,26 +4807,25 @@ function ensureTableStylesPart(dest) {
|
|
|
4653
4807
|
dest.opc.relationshipsFor(dest.presentationPart.partName).add(TABLE_STYLES_REL, relativePartName(dest.presentationPart.partName, partName));
|
|
4654
4808
|
return part;
|
|
4655
4809
|
}
|
|
4656
|
-
|
|
4657
|
-
//#region src/read/api/ops/import-slide.ts
|
|
4810
|
+
// src/read/api/ops/import-slide.ts
|
|
4658
4811
|
/**
|
|
4659
4812
|
* The three ways a slide can be brought across from another deck.
|
|
4660
4813
|
*
|
|
4661
4814
|
* {@link Presentation.importSlide} picks one by `options.theme`, and the difference between
|
|
4662
4815
|
* them is entirely about what happens to the slide's *look*:
|
|
4663
4816
|
*
|
|
4664
|
-
* - **`copy
|
|
4817
|
+
* - **`copy`**: not here. It copies the source theme subgraph wholesale via `copyPart`, so
|
|
4665
4818
|
* the slide keeps its look by keeping its own theme; nothing needs rewriting.
|
|
4666
|
-
* - **`preserve`** ({@link importSlidePreserve})
|
|
4819
|
+
* - **`preserve`** ({@link importSlidePreserve}): rebind to this deck's master, then bake
|
|
4667
4820
|
* the source theme into the slide XML. The slide looks the same as it did; it just no
|
|
4668
4821
|
* longer depends on the theme it came from.
|
|
4669
|
-
* - **`restyle`** ({@link importSlideRestyle})
|
|
4822
|
+
* - **`restyle`** ({@link importSlideRestyle}): rebind and bake *nothing*, so the slide's
|
|
4670
4823
|
* symbolic theme references re-resolve against the destination theme and it re-brands.
|
|
4671
4824
|
*
|
|
4672
4825
|
* The two share {@link importSlideRebind} and are exact inverses on top of it: preserve
|
|
4673
4826
|
* resolves every reference it can, restyle resolves none. Any baking added to the shared
|
|
4674
4827
|
* rebind would break restyle by pinning the slide to its source look, which is why the
|
|
4675
|
-
* rebind carries no theme handling of its own
|
|
4828
|
+
* rebind carries no theme handling of its own, not even the inherited background.
|
|
4676
4829
|
*
|
|
4677
4830
|
* These were private methods on `Presentation`, where they made the read model's own surface
|
|
4678
4831
|
* (open a deck, walk slides, read shapes) hard to see: the import machinery was roughly two
|
|
@@ -4703,17 +4856,17 @@ function importSlidePreserve(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
|
4703
4856
|
}
|
|
4704
4857
|
/**
|
|
4705
4858
|
* Import a slide in `restyle` mode: rebind it to this deck's master/layout and then
|
|
4706
|
-
* {@link restyleSlide} it
|
|
4859
|
+
* {@link restyleSlide} it: drop its colour map override but bake *nothing*, so its symbolic
|
|
4707
4860
|
* theme references re-resolve against the destination theme and the slide re-brands.
|
|
4708
4861
|
*
|
|
4709
4862
|
* The deliberate inverse of `preserve`: no flatten, no inherited-background bake, no
|
|
4710
|
-
* placeholder colour/size/geometry bake
|
|
4863
|
+
* placeholder colour/size/geometry bake. Every one of those would pin the slide to its
|
|
4711
4864
|
* source look, the opposite of re-branding. Carried decorations are left symbolic too, so
|
|
4712
4865
|
* they re-brand along with the slide.
|
|
4713
4866
|
*
|
|
4714
4867
|
* With `remapLiterals` it additionally force-remaps the slide's source-theme literal colours
|
|
4715
|
-
* back to symbolic scheme colours and copies any referenced source table style into this deck
|
|
4716
|
-
*
|
|
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.
|
|
4717
4870
|
* @param {Presentation} dest - the destination deck
|
|
4718
4871
|
* @param {ImportContext} ctx - the open import out of the source package
|
|
4719
4872
|
* @param {Presentation} source - the source deck
|
|
@@ -4739,11 +4892,11 @@ function importSlideRestyle(dest, ctx, source, sourceSlide, carryGraphics, remap
|
|
|
4739
4892
|
/**
|
|
4740
4893
|
* The rebind shared by `preserve` and `restyle`: copy the slide bytes into a fresh part,
|
|
4741
4894
|
* rebuild its relationships (drop notes, repoint the `slideLayout` rel at this deck's
|
|
4742
|
-
* existing layout, copy every other internal target
|
|
4895
|
+
* existing layout, copy every other internal target (media/charts), and pass externals
|
|
4743
4896
|
* through), and optionally bake the source master/layout decorations onto the slide. Returns
|
|
4744
4897
|
* the new part, its name, and its live root element for the caller's mode-specific pass.
|
|
4745
4898
|
*
|
|
4746
|
-
* This carries *no* theme baking of its own
|
|
4899
|
+
* This carries *no* theme baking of its own, not even the inherited background.
|
|
4747
4900
|
* `preserve` adds that via {@link flattenSlide}'s context; `restyle` must not, so the
|
|
4748
4901
|
* background stays symbolic and re-brands.
|
|
4749
4902
|
* @param {Presentation} dest - the destination deck
|
|
@@ -4804,8 +4957,8 @@ function rebind(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
|
4804
4957
|
/**
|
|
4805
4958
|
* Bake the source `slideLayout`/`slideMaster` shape-tree decorations onto the imported slide
|
|
4806
4959
|
* (the `carryMasterGraphics` path). Every shape on those trees *except* placeholders is
|
|
4807
|
-
* deep-copied into the slide's `p:spTree` ahead of its own content
|
|
4808
|
-
* 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
|
|
4809
4962
|
* furthest back. Each decoration's media and other relationship targets are copied into this
|
|
4810
4963
|
* package and its `r:embed`/`r:id`/… references rewritten to fresh slide-local ids. The
|
|
4811
4964
|
* injected shapes are left for the caller's {@link flattenSlide} pass to resolve any theme
|
|
@@ -4847,7 +5000,7 @@ function carryMasterGraphics(ctx, slideRoot, newPartName, slidePartName, owned)
|
|
|
4847
5000
|
/**
|
|
4848
5001
|
* The partname of the layout this deck's slides should attach to in `preserve` mode: the
|
|
4849
5002
|
* first layout of the first slide master. Throws when the deck has no master/layout to attach
|
|
4850
|
-
* to (a deck ts
|
|
5003
|
+
* to (a deck pptx-ts always provides).
|
|
4851
5004
|
* @param {Presentation} dest - the destination deck
|
|
4852
5005
|
* @return {string} partname of the destination layout
|
|
4853
5006
|
*/
|
|
@@ -4858,8 +5011,7 @@ function destinationLayoutPartName(dest) {
|
|
|
4858
5011
|
if (!layoutPartName) throw new InvalidOptionError("import/destination-missing-layout", "importSlide with theme 'preserve' or 'restyle' requires a slide layout in the destination deck");
|
|
4859
5012
|
return layoutPartName;
|
|
4860
5013
|
}
|
|
4861
|
-
|
|
4862
|
-
//#region src/read/api/ops/rescale.ts
|
|
5014
|
+
// src/read/api/ops/rescale.ts
|
|
4863
5015
|
/** Build the EMU transform mapping source-canvas coordinates onto the target canvas (see {@link ImportSlideOptions.rescale}). */
|
|
4864
5016
|
function computeRescale(source, target, mode) {
|
|
4865
5017
|
if (mode === "stretch") return {
|
|
@@ -4878,7 +5030,7 @@ function computeRescale(source, target, mode) {
|
|
|
4878
5030
|
}
|
|
4879
5031
|
/**
|
|
4880
5032
|
* Rescale every top-level transform on a `p:spTree`: shapes/pictures/connectors
|
|
4881
|
-
* (`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
|
|
4882
5034
|
* its children remap via the unchanged `chOff`/`chExt` rather than being recursed
|
|
4883
5035
|
* into), and graphic frames (`p:graphicFrame/p:xfrm`, plus any inner table grid).
|
|
4884
5036
|
* Placeholders that inherit geometry carry no `a:xfrm` and are left to inherit from
|
|
@@ -4927,8 +5079,8 @@ function rescaleXfrm(xfrm, t) {
|
|
|
4927
5079
|
}
|
|
4928
5080
|
}
|
|
4929
5081
|
/**
|
|
4930
|
-
* Scale a graphic-frame table's intrinsic dimensions
|
|
4931
|
-
* 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
|
|
4932
5084
|
* geometry from the grid, not the frame `a:ext`). No-op for non-table frames.
|
|
4933
5085
|
*/
|
|
4934
5086
|
function rescaleTableGrid(graphicFrame, t) {
|
|
@@ -4946,8 +5098,7 @@ function rescaleTableGrid(graphicFrame, t) {
|
|
|
4946
5098
|
if (h !== null) setAttr(tr, "h", String(Math.max(0, Math.round(h * t.sy))));
|
|
4947
5099
|
}
|
|
4948
5100
|
}
|
|
4949
|
-
|
|
4950
|
-
//#region src/read/api/ops/rescale-import.ts
|
|
5101
|
+
// src/read/api/ops/rescale-import.ts
|
|
4951
5102
|
/**
|
|
4952
5103
|
* Rescaling an imported slide's geometry onto the destination canvas.
|
|
4953
5104
|
*
|
|
@@ -4964,8 +5115,8 @@ function rescaleTableGrid(graphicFrame, t) {
|
|
|
4964
5115
|
* graphicFrame transform and table grid on the slide; in `copy` mode also
|
|
4965
5116
|
* rescales the imported layout and master shape trees (resolved via the
|
|
4966
5117
|
* slide → layout → master rel chain) so inherited placeholder/background geometry
|
|
4967
|
-
* stays aligned. `preserve`/`restyle` rebind to this deck's own master/layout
|
|
4968
|
-
* 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:
|
|
4969
5120
|
* font sizes and line widths are left as authored.
|
|
4970
5121
|
*/
|
|
4971
5122
|
function rescaleImportedGeometry(dest, rescaledParts, slidePartName, theme, source, target, mode) {
|
|
@@ -5017,8 +5168,7 @@ function rescalePartGeometry(dest, rescaledParts, partName, transform, mode) {
|
|
|
5017
5168
|
rescaleSpTree(spTree, transform);
|
|
5018
5169
|
part.markDirty();
|
|
5019
5170
|
}
|
|
5020
|
-
|
|
5021
|
-
//#region src/read/api/presentation-imports.ts
|
|
5171
|
+
// src/read/api/presentation-imports.ts
|
|
5022
5172
|
/** Body of {@link Presentation.importSlide}, whose doc comment carries the contract. */
|
|
5023
5173
|
function importSlide(deck, source, index, options = {}) {
|
|
5024
5174
|
const sourceSlide = source.slides[index];
|
|
@@ -5036,6 +5186,7 @@ function importSlide(deck, source, index, options = {}) {
|
|
|
5036
5186
|
const newPart = deck.opc.part(newPartName);
|
|
5037
5187
|
if (!newPart) throw new InternalError("import/part-went-missing", `Imported slide part went missing: ${newPartName}`);
|
|
5038
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);
|
|
5039
5190
|
const slide = deck.insertSlidePart(newPart, options.at);
|
|
5040
5191
|
if (options.importNotes) carryNotes(deck, source, importCtx, sourceSlide.partName, newPartName);
|
|
5041
5192
|
if (options.embedFonts) carryEmbeddedFonts(deck, source, importCtx);
|
|
@@ -5090,6 +5241,7 @@ function importSlides(deck, requests) {
|
|
|
5090
5241
|
destPart
|
|
5091
5242
|
};
|
|
5092
5243
|
});
|
|
5244
|
+
for (const request of planned) carryCommentAuthors(deck, request.source, request.destPartName);
|
|
5093
5245
|
for (const request of planned) {
|
|
5094
5246
|
const mode = rescaleBySource.get(request.source.opc);
|
|
5095
5247
|
if (!mode) continue;
|
|
@@ -5283,14 +5435,13 @@ function importShapes(deck, target, source, shapeIndices, options = {}) {
|
|
|
5283
5435
|
}
|
|
5284
5436
|
return result;
|
|
5285
5437
|
}
|
|
5286
|
-
|
|
5287
|
-
//#region src/read/api/presentation.ts
|
|
5438
|
+
// src/read/api/presentation.ts
|
|
5288
5439
|
/**
|
|
5289
5440
|
* Read-model entry point: `Presentation` wraps an `OpcPackage` and exposes a
|
|
5290
5441
|
* navigable, typed view of the deck (slides → shapes → text), backed by the
|
|
5291
5442
|
* live DOM so the same nodes can later be mutated.
|
|
5292
5443
|
*
|
|
5293
|
-
* 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
|
|
5294
5445
|
* index, the slide list, the copy registry that lets repeated imports from one source share
|
|
5295
5446
|
* what they already copied, and the `p:sldIdLst` wiring. The operations themselves live in
|
|
5296
5447
|
* `ops/`, one job per module, taking this deck as an argument. The slide-import machinery
|
|
@@ -5300,11 +5451,11 @@ function importShapes(deck, target, source, shapeIndices, options = {}) {
|
|
|
5300
5451
|
* The four import entry points went the same way, and {@link appendSlides} after them: their
|
|
5301
5452
|
* contracts stay here as the doc comments a caller reads, and their bodies live in
|
|
5302
5453
|
* `presentation-imports.ts` and `ops/append-slides.ts`. Those bodies are the one thing in this
|
|
5303
|
-
* directory that is not independent of this class
|
|
5454
|
+
* directory that is not independent of this class. They reach back for {@link importContext},
|
|
5304
5455
|
* {@link insertSlidePart} and {@link rescaledParts}, which {@link cloneSlide} reads too and so
|
|
5305
5456
|
* could not travel with them. Those three are `@internal` for that and nothing else.
|
|
5306
5457
|
*/
|
|
5307
|
-
/** 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}. */
|
|
5308
5459
|
const PRESENTATION_TEMPLATE_MAIN_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml";
|
|
5309
5460
|
var Presentation = class Presentation {
|
|
5310
5461
|
opc;
|
|
@@ -5358,6 +5509,7 @@ var Presentation = class Presentation {
|
|
|
5358
5509
|
if (mainPart.contentType === PRESENTATION_TEMPLATE_MAIN_CONTENT_TYPE) pres.opc.contentTypes.ensureRegistered(mainPart.partName, PRESENTATION_MAIN_CONTENT_TYPE);
|
|
5359
5510
|
}
|
|
5360
5511
|
while (pres.slides.length > 0) pres.removeSlide(0);
|
|
5512
|
+
resetCommentAuthorIndexes(pres);
|
|
5361
5513
|
return pres;
|
|
5362
5514
|
}
|
|
5363
5515
|
/** The main presentation part (`/ppt/presentation.xml`), resolved via the package `officeDocument` relationship. */
|
|
@@ -5409,7 +5561,7 @@ var Presentation = class Presentation {
|
|
|
5409
5561
|
/**
|
|
5410
5562
|
* The deck's embedded font families (`p:embeddedFontLst` in `presentation.xml`),
|
|
5411
5563
|
* `[]` when it embeds none. Each entry names the typeface and resolves every
|
|
5412
|
-
* 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
|
|
5413
5565
|
* read counterpart to the write-side `pptx.embedFont` / `importSlide({ embedFonts })`
|
|
5414
5566
|
* carry. An entry whose `p:font` has no `@typeface`, or a face whose `r:id` is
|
|
5415
5567
|
* missing or dangling, is skipped (faithful degradation, no throw). Read-only.
|
|
@@ -5439,7 +5591,7 @@ var Presentation = class Presentation {
|
|
|
5439
5591
|
* `ppt/authors.xml`), `[]` when the deck carries no modern comments. Unlike the
|
|
5440
5592
|
* legacy {@link commentAuthors}, each entry is keyed by a GUID `id` (plus
|
|
5441
5593
|
* `userId`/`providerId`); each slide's {@link Slide.modernComments} resolves its
|
|
5442
|
-
* `@authorId` against this list. Read-only
|
|
5594
|
+
* `@authorId` against this list. Read-only: the writer emits legacy comments.
|
|
5443
5595
|
*/
|
|
5444
5596
|
get modernCommentAuthors() {
|
|
5445
5597
|
return readModernCommentAuthors(this.opc, this.presentationPart.partName);
|
|
@@ -5469,7 +5621,7 @@ var Presentation = class Presentation {
|
|
|
5469
5621
|
* The deck's extended document properties (`docProps/app.xml`): the producing
|
|
5470
5622
|
* application and its version, the company, and the flat `TitlesOfParts` vector.
|
|
5471
5623
|
* `{}` when the deck carries no extended-properties part. The read counterpart of
|
|
5472
|
-
* the write-side `pptx.company` setter
|
|
5624
|
+
* the write-side `pptx.company` setter, the only one of the four this library
|
|
5473
5625
|
* writes from a caller's value; the other three it states about itself.
|
|
5474
5626
|
*
|
|
5475
5627
|
* A deliberate subset of the part: see {@link ExtendedProperties} for why the
|
|
@@ -5491,7 +5643,7 @@ var Presentation = class Presentation {
|
|
|
5491
5643
|
* The deck-level programmatic tags (`p:custDataLst/p:tags` on `presentation.xml`,
|
|
5492
5644
|
* resolved to `ppt/tags/tagN.xml`) as `{ name, val }` string pairs, `[]` when the
|
|
5493
5645
|
* deck carries none. These are add-in/host metadata with no visible rendering and
|
|
5494
|
-
* no writer
|
|
5646
|
+
* no writer. They are read-only, preserved byte-for-byte on round-trip. Per-slide tags are
|
|
5495
5647
|
* {@link Slide.tags}.
|
|
5496
5648
|
*/
|
|
5497
5649
|
get tags() {
|
|
@@ -5510,7 +5662,7 @@ var Presentation = class Presentation {
|
|
|
5510
5662
|
* charts, SmartArt diagrams and OLE embeddings, each with the subtree under it
|
|
5511
5663
|
* (a chart's embedded workbook comes along; the image inside its user-shapes
|
|
5512
5664
|
* drawing stays shared). PowerPoint refuses to open a deck where two slides
|
|
5513
|
-
* resolve to one chart or diagram, so this is not tidiness
|
|
5665
|
+
* resolve to one chart or diagram, so this is not tidiness; see
|
|
5514
5666
|
* `page-owned.ts` for the rule and the evidence behind it.
|
|
5515
5667
|
*/
|
|
5516
5668
|
cloneSlide(index, options = {}) {
|
|
@@ -5528,7 +5680,7 @@ var Presentation = class Presentation {
|
|
|
5528
5680
|
* `p:sldId` entry and the presentation→slide relationship are dropped, the slide
|
|
5529
5681
|
* part and its `.rels` are deleted, and any part the slide *privately* owned
|
|
5530
5682
|
* (its notes slide, slide-only media, charts/embeddings) that no remaining part
|
|
5531
|
-
* references is pruned too
|
|
5683
|
+
* references is pruned too, recursively. Shared deck chrome (layout, master,
|
|
5532
5684
|
* theme, …) is never pruned, so the deck stays renderable; removing every slide
|
|
5533
5685
|
* leaves a valid master/layout-only package (a template shell). The slide also
|
|
5534
5686
|
* leaves every custom show and section it was in, which stay even when that
|
|
@@ -5568,9 +5720,9 @@ var Presentation = class Presentation {
|
|
|
5568
5720
|
* Append a copy of `source.slides[index]` to this presentation and return it.
|
|
5569
5721
|
*
|
|
5570
5722
|
* Unlike {@link cloneSlide} (same-deck duplicate), this copies a slide across
|
|
5571
|
-
* a package boundary: it brings the connected sub-graph the slide depends on
|
|
5572
|
-
* its `slideLayout` → `slideMaster` → `theme`, plus any media, charts, and
|
|
5573
|
-
* 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
|
|
5574
5726
|
* partname, relationship id, and content-type registration so the result is a
|
|
5575
5727
|
* self-consistent OPC package. Parts of this (target) package that are not
|
|
5576
5728
|
* touched stay byte-identical, matching `cloneSlide`'s fidelity contract.
|
|
@@ -5578,7 +5730,7 @@ var Presentation = class Presentation {
|
|
|
5578
5730
|
* Only the layout(s) actually used by imported slides are copied; the imported
|
|
5579
5731
|
* master's `p:sldLayoutIdLst` is pruned to exactly those, mirroring how
|
|
5580
5732
|
* PowerPoint's "Reuse Slides" brings a slide across. Parts shared by repeated
|
|
5581
|
-
* 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
|
|
5582
5734
|
* except the page itself. Importing the same source slide twice yields two
|
|
5583
5735
|
* independent copies over one shared layout/master/theme, which is what lets a
|
|
5584
5736
|
* deck show one page twice (verbatim, then edited).
|
|
@@ -5587,7 +5739,7 @@ var Presentation = class Presentation {
|
|
|
5587
5739
|
* into the slide XML and the slide is bound to this deck's existing
|
|
5588
5740
|
* master/layout; with `{ theme: 'restyle' }` the slide is bound to this deck's
|
|
5589
5741
|
* master/layout with its theme references left symbolic, so it re-brands to the
|
|
5590
|
-
* destination palette
|
|
5742
|
+
* destination palette. See {@link ImportSlideOptions}.
|
|
5591
5743
|
*
|
|
5592
5744
|
* By default the source slide size must equal this presentation's. Pass
|
|
5593
5745
|
* `{ rescale: 'fit' | 'stretch' }` to rescale the imported geometry onto this
|
|
@@ -5606,7 +5758,7 @@ var Presentation = class Presentation {
|
|
|
5606
5758
|
* Import selected pages from one or more loaded source presentations as one
|
|
5607
5759
|
* batch. Each imported page lands at its `outputIndex` in the complete
|
|
5608
5760
|
* destination slide list after the batch, and the returned array is parallel
|
|
5609
|
-
* to `requests
|
|
5761
|
+
* to `requests`: `result[i]` is the page `requests[i]` asked for, whatever
|
|
5610
5762
|
* order the output positions were given in.
|
|
5611
5763
|
*
|
|
5612
5764
|
* Everything is checked before a single byte of this deck moves: every request
|
|
@@ -5619,13 +5771,13 @@ var Presentation = class Presentation {
|
|
|
5619
5771
|
* The batch also decides what a `slide → slide` link means: an internal link
|
|
5620
5772
|
* on a selected page must target another **selected** page (or one this deck
|
|
5621
5773
|
* already contains via an earlier import from that source), and is rewritten
|
|
5622
|
-
* to the fresh partname
|
|
5774
|
+
* to the fresh partname. Importing page 3 of 10 does not drag pages 1–2
|
|
5623
5775
|
* across as dependencies, and never strands the link. This mirrors
|
|
5624
5776
|
* `appendSlides`' `import/unresolved-slide-link`, which the write side already
|
|
5625
5777
|
* enforces for generator decks.
|
|
5626
5778
|
*
|
|
5627
5779
|
* One request is one output page, so naming the same source page in several
|
|
5628
|
-
* 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
|
|
5629
5781
|
* is the one thing an import never shares, exactly as in {@link importSlide}.
|
|
5630
5782
|
* Everything under it (layout, master, theme, media) is still copied once and
|
|
5631
5783
|
* shared. Where such a page is the target of a `slide → slide` link, the link
|
|
@@ -5636,7 +5788,7 @@ var Presentation = class Presentation {
|
|
|
5636
5788
|
* Speaker notes travel per request: `{ importNotes: true }` carries that page's
|
|
5637
5789
|
* `notesSlide` part across, wired to the new page and bound to a single
|
|
5638
5790
|
* `notesMaster` under the same 0..1 rule {@link importSlide} and
|
|
5639
|
-
* {@link appendSlides} follow
|
|
5791
|
+
* {@link appendSlides} follow: the destination's own master wins when it has
|
|
5640
5792
|
* one. Notes are part of the up-front dry run too, so a batch that would fail
|
|
5641
5793
|
* carrying them is refused with the deck still byte-identical. A page named in
|
|
5642
5794
|
* several requests gets its own copy of its notes each time, as of everything
|
|
@@ -5663,9 +5815,9 @@ var Presentation = class Presentation {
|
|
|
5663
5815
|
}
|
|
5664
5816
|
/**
|
|
5665
5817
|
* Graft slide master(s) from another open package into this one and return what
|
|
5666
|
-
* was copied. Unlike {@link importSlide}
|
|
5818
|
+
* was copied. Unlike {@link importSlide}, which brings a master across only as
|
|
5667
5819
|
* the dependency of an imported *slide* and prunes it to the one layout that
|
|
5668
|
-
* slide uses
|
|
5820
|
+
* slide uses, this copies a master together with its **whole** layout family
|
|
5669
5821
|
* and attaches it to no slide: the master and its layouts land in this deck's
|
|
5670
5822
|
* layout gallery (PowerPoint's *Insert ▸ New Slide* / *Layout* picker) without
|
|
5671
5823
|
* changing any existing slide.
|
|
@@ -5697,7 +5849,7 @@ var Presentation = class Presentation {
|
|
|
5697
5849
|
}
|
|
5698
5850
|
/**
|
|
5699
5851
|
* The deck's slide masters, in `p:sldMasterIdLst` order, as modeled
|
|
5700
|
-
* {@link SlideMaster}s
|
|
5852
|
+
* {@link SlideMaster}s: the typed read model over the shared chrome (each
|
|
5701
5853
|
* master's colour map, theme, placeholders, and the layouts built on it). Walk
|
|
5702
5854
|
* `pres.masters()[i].layouts` for the rich layout model, or use {@link layouts}
|
|
5703
5855
|
* for the flat {@link LayoutHandle} gallery {@link appendSlides} binds to.
|
|
@@ -5707,7 +5859,7 @@ var Presentation = class Presentation {
|
|
|
5707
5859
|
return slideMasterPartNames(this).map((partName) => this.opc.part(partName)).filter((part) => part !== void 0).map((part) => new SlideMaster(this.opc, part));
|
|
5708
5860
|
}
|
|
5709
5861
|
/**
|
|
5710
|
-
* 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
|
|
5711
5863
|
* slide can bind to. Each {@link LayoutHandle} addresses one layout for
|
|
5712
5864
|
* {@link appendSlides}; the `name` is its `p:cSld@name`. Read-only enumeration:
|
|
5713
5865
|
* it copies nothing and leaves the package byte-identical.
|
|
@@ -5730,12 +5882,12 @@ var Presentation = class Presentation {
|
|
|
5730
5882
|
/**
|
|
5731
5883
|
* Append generator-produced slides onto this deck, binding each to an existing
|
|
5732
5884
|
* layout, and return the new {@link Slide}s. This is the hybrid
|
|
5733
|
-
* "generate-onto-existing" path: the deck's masters, layouts, theme
|
|
5734
|
-
* 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
|
|
5735
5887
|
* `.rels`, `[Content_Types].xml`, and the freshly-added slide/media parts
|
|
5736
5888
|
* change), because the existing chrome is never regenerated.
|
|
5737
5889
|
*
|
|
5738
|
-
* `source` is any slide producer (a `
|
|
5890
|
+
* `source` is any slide producer (a `PptxTs` instance); its authored slides
|
|
5739
5891
|
* are serialized via {@link SlideSource.extractSlides} and spliced in under
|
|
5740
5892
|
* fresh partnames, with each slide's `slideLayout` relationship pointed at the
|
|
5741
5893
|
* layout named by `options.layout` and its image/hyperlink relationships rebuilt
|
|
@@ -5745,13 +5897,13 @@ var Presentation = class Presentation {
|
|
|
5745
5897
|
* Charts and internal slide-to-slide hyperlinks are carried across: chart parts
|
|
5746
5898
|
* (chart XML + `.rels` + embedded workbook) are injected under fresh names, and a
|
|
5747
5899
|
* `slide:N` link is repointed at the Nth appended slide's new partname. A chartEx
|
|
5748
|
-
* (Office 2016
|
|
5900
|
+
* (Office 2016: waterfall, funnel, treemap, ...) chart carries too, as its own
|
|
5749
5901
|
* `chartEx{N}.xml` part behind the MS chartEx rel, with the style and color-style
|
|
5750
5902
|
* sidecars PowerPoint requires alongside it.
|
|
5751
5903
|
*
|
|
5752
5904
|
* The generator's presentation-level embedded fonts (`pptx.embedFont`) are also
|
|
5753
5905
|
* carried into this deck and merged into its `p:embeddedFontLst`, de-duped by
|
|
5754
|
-
* typeface + face slot
|
|
5906
|
+
* typeface + face slot, so author-side embedded fonts survive the append onto a
|
|
5755
5907
|
* template that may itself already embed fonts.
|
|
5756
5908
|
*
|
|
5757
5909
|
* Embedded audio/video is carried too: the media part backs the ECMA audio/video rel
|
|
@@ -5762,24 +5914,28 @@ var Presentation = class Presentation {
|
|
|
5762
5914
|
* Speaker notes ride across too: a slide authored with `addNotes` gets a `notesSlide`
|
|
5763
5915
|
* part wired back to it, with its hyperlink rels preserved. A notes slide must bind to
|
|
5764
5916
|
* a notes master, and a template commonly has none, so the generator's is installed
|
|
5765
|
-
* (with the theme its `.rels` needs)
|
|
5917
|
+
* (with the theme its `.rels` needs), but only when this deck has none of its own, so
|
|
5766
5918
|
* an existing notes master and its styling always win.
|
|
5767
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
|
+
*
|
|
5768
5924
|
* Limitations:
|
|
5769
5925
|
* - An internal link to a source slide outside the appended batch throws (its
|
|
5770
5926
|
* target has no counterpart in the destination).
|
|
5771
5927
|
* - Appended slides are concrete absolute-positioned content with no placeholder
|
|
5772
5928
|
* inheritance from the bound layout; the binding governs theme/`clrMap`
|
|
5773
5929
|
* resolution and the "based on" link, not placeholder geometry. Author with
|
|
5774
|
-
* concrete colours
|
|
5930
|
+
* concrete colours: any `schemeClr` re-resolves against the destination theme.
|
|
5775
5931
|
* - Source and destination slide sizes must match (no geometry rescale).
|
|
5776
5932
|
*/
|
|
5777
5933
|
async appendSlides(source, options) {
|
|
5778
5934
|
return appendSlides(this, source, options);
|
|
5779
5935
|
}
|
|
5780
5936
|
/**
|
|
5781
|
-
* Copy one shape
|
|
5782
|
-
* 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
|
|
5783
5939
|
* {@link Shape}. `target` must be a slide of *this* presentation; `source` may
|
|
5784
5940
|
* belong to any open presentation.
|
|
5785
5941
|
*
|
|
@@ -5790,7 +5946,7 @@ var Presentation = class Presentation {
|
|
|
5790
5946
|
* so they cannot collide with the host. With `theme: 'preserve'` (default) the
|
|
5791
5947
|
* shape's theme references are baked to literals against the *source* theme so it
|
|
5792
5948
|
* renders the same on a foreign host; `restyle` leaves them symbolic to re-brand;
|
|
5793
|
-
* `copy` brings the XML across untouched
|
|
5949
|
+
* `copy` brings the XML across untouched. See {@link ImportShapeOptions}.
|
|
5794
5950
|
*
|
|
5795
5951
|
* Differing slide sizes need `{ rescale }` (see {@link ImportShapeOptions.rescale});
|
|
5796
5952
|
* a lifted `preserve` placeholder is baked self-contained and demoted to a plain
|
|
@@ -5822,7 +5978,7 @@ var Presentation = class Presentation {
|
|
|
5822
5978
|
}
|
|
5823
5979
|
/**
|
|
5824
5980
|
* @internal The parts whose geometry a rescale has already rewritten, and in which mode.
|
|
5825
|
-
* 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
|
|
5826
5982
|
* makes a layout or master shared across repeated imports from one source scale exactly
|
|
5827
5983
|
* once, so like the copy registry it has to outlive the call that fills it.
|
|
5828
5984
|
*/
|
|
@@ -5832,7 +5988,7 @@ var Presentation = class Presentation {
|
|
|
5832
5988
|
/**
|
|
5833
5989
|
* @internal Open an import out of `source`: this deck as the destination, paired with
|
|
5834
5990
|
* the copy registry for that package (created on first use). The registry is held on
|
|
5835
|
-
* 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
|
|
5836
5992
|
* what makes a second import from the same source reuse the layout, master, theme, and
|
|
5837
5993
|
* media it already copied instead of duplicating them, and it is why the import bodies
|
|
5838
5994
|
* in `presentation-imports.ts` call back here rather than carrying it with them.
|
|
@@ -5879,7 +6035,7 @@ var Presentation = class Presentation {
|
|
|
5879
6035
|
* A slide id one past the highest existing, but at least ST_SlideId's minimum.
|
|
5880
6036
|
*
|
|
5881
6037
|
* The module named and enforced that minimum and not the maximum, so a deck near the ceiling
|
|
5882
|
-
* 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
|
|
5883
6039
|
* repair, for a reason nothing reported. Past the ceiling there is no "next" id to hand out,
|
|
5884
6040
|
* so the allocation falls back to the lowest id in range that the deck is not already using;
|
|
5885
6041
|
* a deck holding every one of the two billion has no legal answer and says so.
|
|
@@ -5902,7 +6058,6 @@ var Presentation = class Presentation {
|
|
|
5902
6058
|
return this.opc.save();
|
|
5903
6059
|
}
|
|
5904
6060
|
};
|
|
5905
|
-
//#endregion
|
|
5906
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 };
|
|
5907
6062
|
|
|
5908
|
-
//# sourceMappingURL=presentation-
|
|
6063
|
+
//# sourceMappingURL=presentation-4Ggq0_Xa.js.map
|