@shbernal/ts-pptx 3.4.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{autopage-BYlTzX-l.js → autopage-DkjYZUFD.js} +3 -3
- package/dist/{autopage-BYlTzX-l.js.map → autopage-DkjYZUFD.js.map} +1 -1
- package/dist/browser.d.ts +5 -5
- package/dist/browser.js +4 -4
- package/dist/browser.js.map +1 -1
- package/dist/{diagnostics-B0OmTOoz.d.ts → diagnostics-Du_s75MT.d.ts} +20 -5
- package/dist/diagnostics-Du_s75MT.d.ts.map +1 -0
- package/dist/{errors-fB1KDJKQ.d.ts → errors-PflMPWBn.d.ts} +3 -3
- package/dist/{errors-fB1KDJKQ.d.ts.map → errors-PflMPWBn.d.ts.map} +1 -1
- package/dist/{fit-TjpNbMru.js → fit-BKs0zxMI.js} +3 -3
- package/dist/{fit-TjpNbMru.js.map → fit-BKs0zxMI.js.map} +1 -1
- package/dist/{html-dom-yUSdCExA.js → html-dom-B0I0QSbU.js} +4 -4
- package/dist/{html-dom-yUSdCExA.js.map → html-dom-B0I0QSbU.js.map} +1 -1
- package/dist/html.d.ts +3 -3
- package/dist/html.js +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/inspect.d.ts +28 -4
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +42 -11
- package/dist/inspect.js.map +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/measure.d.ts +2 -2
- package/dist/measure.js +1 -1
- package/dist/{namespaces-Sy_m7Nhd.js → namespaces-BVnNu7sg.js} +77 -3
- package/dist/namespaces-BVnNu7sg.js.map +1 -0
- package/dist/node.d.ts +5 -5
- package/dist/node.js +3 -3
- package/dist/{notes-bWApshah.js → notes-RLCwdlTE.js} +289 -313
- package/dist/{notes-bWApshah.js.map → notes-RLCwdlTE.js.map} +1 -1
- package/dist/{pres-Dybf7ZUl.d.ts → pres-C5__xkDh.d.ts} +2 -2
- package/dist/{pres-Dybf7ZUl.d.ts.map → pres-C5__xkDh.d.ts.map} +1 -1
- package/dist/{presentation-3rAKKRfm.js → presentation-Cp7vzlAw.js} +566 -519
- package/dist/presentation-Cp7vzlAw.js.map +1 -0
- package/dist/{presentation-types-D7cgT-eG.d.ts → presentation-types-DP_8jog7.d.ts} +514 -276
- package/dist/presentation-types-DP_8jog7.d.ts.map +1 -0
- package/dist/read.d.ts +31 -48
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +3 -3
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +84 -75
- package/dist/script.js.map +1 -1
- package/dist/{shapes-Dyw5bxwO.js → shapes-BemroU-z.js} +1010 -306
- package/dist/shapes-BemroU-z.js.map +1 -0
- package/dist/{slide-B_aInw3N.d.ts → slide-js0QpRV-.d.ts} +1278 -1254
- package/dist/slide-js0QpRV-.d.ts.map +1 -0
- package/dist/{text-DSN3anEh.js → text-CyetbF6p.js} +2039 -1417
- package/dist/text-CyetbF6p.js.map +1 -0
- package/dist/units-1kRzYShk.js +231 -0
- package/dist/units-1kRzYShk.js.map +1 -0
- package/dist/units-internal-Dg95w_YD.js +753 -0
- package/dist/units-internal-Dg95w_YD.js.map +1 -0
- package/dist/zip.d.ts +1 -1
- package/package.json +2 -1
- package/dist/diagnostics-B0OmTOoz.d.ts.map +0 -1
- package/dist/diagnostics-CPVADSL1.js +0 -90
- package/dist/diagnostics-CPVADSL1.js.map +0 -1
- package/dist/namespaces-Sy_m7Nhd.js.map +0 -1
- package/dist/presentation-3rAKKRfm.js.map +0 -1
- package/dist/presentation-types-D7cgT-eG.d.ts.map +0 -1
- package/dist/shapes-Dyw5bxwO.js.map +0 -1
- package/dist/slide-B_aInw3N.d.ts.map +0 -1
- package/dist/text-DSN3anEh.js.map +0 -1
- package/dist/units-internal-DNvK2I6o.js +0 -896
- package/dist/units-internal-DNvK2I6o.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { d as emuToInches } from "./units-1kRzYShk.js";
|
|
1
2
|
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-Bvln0H-7.js";
|
|
2
|
-
import {
|
|
3
|
-
import { D as
|
|
4
|
-
import { _ as THEME_REL, a as NOTES_MASTER_REL, c as OFFICE_DOCUMENT_REL, d as SLIDE_LAYOUT_CONTENT_TYPE, f as SLIDE_LAYOUT_REL, g as THEME_CONTENT_TYPE, h as SLIDE_REL, i as NOTES_MASTER_CONTENT_TYPE, l as OFFICE_REL, m as SLIDE_MASTER_REL, o as NOTES_SLIDE_CONTENT_TYPE, p as SLIDE_MASTER_CONTENT_TYPE, r as IMAGE_REL, s as NOTES_SLIDE_REL, t as OOXML_NS } from "./namespaces-Sy_m7Nhd.js";
|
|
3
|
+
import { D as voidEl, E as raw, T as el, g as FONT_REL_TYPE, h as FONT_DATA_EXTENSION, m as FONT_DATA_CONTENT_TYPE, n as makeXmlNotesMaster, o as makeXmlNotesSlideSkeleton, p as EMBEDDED_FONT_SLOTS } from "./notes-RLCwdlTE.js";
|
|
4
|
+
import { D as COLOR_MAP_TOKENS, I as THEME_COLOR_SLOTS, _ as THEME_CONTENT_TYPE, a as NOTES_MASTER_REL, c as OFFICE_DOCUMENT_REL, d as SLIDE_CONTENT_TYPE, f as SLIDE_LAYOUT_CONTENT_TYPE, g as SLIDE_REL, h as SLIDE_MASTER_REL, i as NOTES_MASTER_CONTENT_TYPE, l as OFFICE_REL, m as SLIDE_MASTER_CONTENT_TYPE, o as NOTES_SLIDE_CONTENT_TYPE, p as SLIDE_LAYOUT_REL, r as IMAGE_REL, s as NOTES_SLIDE_REL, t as OOXML_NS, v as THEME_REL } from "./namespaces-BVnNu7sg.js";
|
|
5
5
|
import { ZipWriter, readZip } from "./zip.js";
|
|
6
|
-
import { $ as collectElements, A as
|
|
6
|
+
import { $ as collectElements, A as spPrXfrmEmu, At as resolveRelativePartName, B as LST_STYLE_LEVELS, Bt as firstChildElement, Ct as SPPR_LN_AFTER, Dt as partNameToZipPath, Et as partNameExtension, F as resolveLayoutColorContext, Ft as childElements, G as placeholderInheritedRunProps, Gt as ownerDocumentOf, H as placeholderInheritedAnchor, Ht as getOrAddChild, I as resolveMasterColorContext, It as createElement, J as slideDefinesColor, Jt as removeChildrenByQName, K as placeholderInheritedXfrm, Kt as parseXml, L as resolveNotesColorContext, Lt as descendantsByTag, M as readPictureFill, Mt as zipPathToPartName, N as readGradientFill, Nt as attr, Ot as relativePartName, P as resolveColorElement, Pt as boolValue, Q as carriedDecorations, R as resolveSlideColorContext, Rt as escapeXmlAttribute, St as SPPR_FILL_AFTER, T as AutoShape, Tt as SPPR_SP3D_AFTER, U as placeholderInheritedColor, Ut as insertInOrder, V as RUN_PROP_NAMES, Vt as getElements, W as placeholderInheritedListStyles, Wt as intValue, X as cSldName, Xt as serializeXml, Y as slideDefinesProp, Yt as replaceInParent, Z as cSldOf, Zt as setAttr, _t as FILL_CHOICES, at as fmtEntry, bt as SPPR_AFTER_XFRM, ct as parseClrMap, dt as resolveSchemeToken, et as firstShapeChild, ft as styleRefFill, it as SCHEME_SLOTS, j as readPatternFill, jt as resolveSingleRel, k as TextFrame, kt as relsPartNameFor, l as wrapShapeElement, lt as parseClrScheme, mt as substitutePhClr, n as buildShapes, nt as nvPrOf, ot as intAttr, pt as styleRefLine, q as placeholderOf, qt as removeAttr, r as findShapeByIdDeep, rt as spTreeOf, st as isA, t as GroupShape, tt as nthShapeChild, u as GraphicFrame, ut as resolveColor, vt as RPR_FILL_AFTER, w as Picture, wt as SPPR_SCENE3D_AFTER, xt as SPPR_EFFECT_AFTER, yt as SHAPE_AFTER_SPPR, z as resolveSlideThemeParts, zt as firstChild } from "./shapes-BemroU-z.js";
|
|
7
7
|
//#region src/read/opc/content-types.ts
|
|
8
8
|
/**
|
|
9
9
|
* Overlay over `[Content_Types].xml`, used to resolve part content types at
|
|
@@ -987,15 +987,108 @@ function readSlideBackground(bg, source, ctx, rels, themeRels) {
|
|
|
987
987
|
};
|
|
988
988
|
}
|
|
989
989
|
//#endregion
|
|
990
|
-
//#region src/read/api/
|
|
990
|
+
//#region src/read/api/chrome.ts
|
|
991
|
+
/** Read `<a:latin>`/`<a:ea>`/`<a:cs>` typefaces of a theme font family; empty string ⇒ `null`. */
|
|
992
|
+
function readThemeFontFace(font) {
|
|
993
|
+
const face = (child) => {
|
|
994
|
+
const el = font && firstChild(font, child);
|
|
995
|
+
return el && attr(el, "typeface") || null;
|
|
996
|
+
};
|
|
997
|
+
return {
|
|
998
|
+
latin: face("a:latin"),
|
|
999
|
+
ea: face("a:ea"),
|
|
1000
|
+
cs: face("a:cs")
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
991
1003
|
/**
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
*
|
|
995
|
-
*
|
|
996
|
-
*
|
|
1004
|
+
* A deck's theme part (`theme1.xml`) as a modeled object. Exposes the two property
|
|
1005
|
+
* tiers a slide resolves against: the {@link colorScheme} (the 12 `a:clrScheme`
|
|
1006
|
+
* slots as literal hex, `a:sysClr` resolved via its `lastClr`) and the
|
|
1007
|
+
* {@link fontScheme} (major/minor Latin/EA/CS faces). Reachable from
|
|
1008
|
+
* {@link SlideMaster.theme} and `Slide.theme`.
|
|
1009
|
+
*/
|
|
1010
|
+
var Theme = class {
|
|
1011
|
+
part;
|
|
1012
|
+
constructor(part) {
|
|
1013
|
+
this.part = part;
|
|
1014
|
+
}
|
|
1015
|
+
/** Partname of the theme part. */
|
|
1016
|
+
get partName() {
|
|
1017
|
+
return this.part.partName;
|
|
1018
|
+
}
|
|
1019
|
+
/** Theme name (`a:theme/@name`, e.g. `Office Theme`), or `null` when unnamed. */
|
|
1020
|
+
get name() {
|
|
1021
|
+
const root = this.part.dom.documentElement;
|
|
1022
|
+
return root ? attr(root, "name") : null;
|
|
1023
|
+
}
|
|
1024
|
+
/** Colour-scheme name (`a:clrScheme/@name`, e.g. `Office`), or `null`. */
|
|
1025
|
+
get colorSchemeName() {
|
|
1026
|
+
const clrScheme = this.#clrScheme();
|
|
1027
|
+
return clrScheme ? attr(clrScheme, "name") : null;
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* The theme colour scheme: the 12 {@link ThemeColorSlot}s resolved to a 6-hex RGB
|
|
1031
|
+
* (an `a:sysClr` slot resolves through its `lastClr`). A slot the theme does not
|
|
1032
|
+
* define reads `null`. Use {@link color} to look one slot up.
|
|
1033
|
+
*/
|
|
1034
|
+
get colorScheme() {
|
|
1035
|
+
const parsed = parseClrScheme(this.#clrScheme());
|
|
1036
|
+
const out = {};
|
|
1037
|
+
for (const slot of THEME_COLOR_SLOTS) out[slot] = parsed.get(slot) ?? null;
|
|
1038
|
+
return out;
|
|
1039
|
+
}
|
|
1040
|
+
/** The 6-hex RGB of one colour-scheme slot, or `null` when the theme does not define it. */
|
|
1041
|
+
color(slot) {
|
|
1042
|
+
return parseClrScheme(this.#clrScheme()).get(slot) ?? null;
|
|
1043
|
+
}
|
|
1044
|
+
/** The theme's `a:fontScheme` (major/minor font families), or `null` when the theme declares none. */
|
|
1045
|
+
get fontScheme() {
|
|
1046
|
+
const themeElements = this.#themeElements();
|
|
1047
|
+
const fontScheme = themeElements && firstChild(themeElements, "a:fontScheme");
|
|
1048
|
+
if (!fontScheme) return null;
|
|
1049
|
+
return {
|
|
1050
|
+
name: attr(fontScheme, "name"),
|
|
1051
|
+
major: readThemeFontFace(firstChild(fontScheme, "a:majorFont")),
|
|
1052
|
+
minor: readThemeFontFace(firstChild(fontScheme, "a:minorFont"))
|
|
1053
|
+
};
|
|
1054
|
+
}
|
|
1055
|
+
/** Escape hatch: the underlying `a:theme` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
1056
|
+
get element_() {
|
|
1057
|
+
return this.part.dom.documentElement;
|
|
1058
|
+
}
|
|
1059
|
+
/** Mark the theme part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
|
|
1060
|
+
markDirty() {
|
|
1061
|
+
this.part.markDirty();
|
|
1062
|
+
}
|
|
1063
|
+
#themeElements() {
|
|
1064
|
+
const root = this.part.dom.documentElement;
|
|
1065
|
+
return root ? firstChild(root, "a:themeElements") : null;
|
|
1066
|
+
}
|
|
1067
|
+
#clrScheme() {
|
|
1068
|
+
const themeElements = this.#themeElements();
|
|
1069
|
+
return themeElements ? firstChild(themeElements, "a:clrScheme") : null;
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
/** The placeholder shapes (`p:sp` carrying a `p:ph`) of a master/layout `p:spTree`, in document order. */
|
|
1073
|
+
function placeholderShapes(spTree) {
|
|
1074
|
+
if (!spTree) return [];
|
|
1075
|
+
return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
|
|
1076
|
+
}
|
|
1077
|
+
/**
|
|
1078
|
+
* One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout —
|
|
1079
|
+
* the definition a slide's placeholder of the same `type`/`idx` inherits geometry
|
|
1080
|
+
* and text style from. Geometry getters read the placeholder's *own* `a:xfrm`,
|
|
1081
|
+
* which a master/layout placeholder carries explicitly (so it round-trips, unlike a
|
|
1082
|
+
* notes placeholder's inherited geometry).
|
|
1083
|
+
*
|
|
1084
|
+
* This is the identity-and-geometry view. The same `p:sp` also appears in
|
|
1085
|
+
* {@link SlideMaster.shapes} / {@link SlideLayout.shapes} as an `AutoShape`, which
|
|
1086
|
+
* additionally carries the paint surface (`resolvedFill`, `resolvedLine`,
|
|
1087
|
+
* `presetGeometry`, `rotation`, `absoluteFrame`) and reports its `p:ph` through
|
|
1088
|
+
* `AutoShape.placeholder`. Read a placeholder here to *place* it; read it there to
|
|
1089
|
+
* *draw* it.
|
|
997
1090
|
*/
|
|
998
|
-
var
|
|
1091
|
+
var Placeholder = class {
|
|
999
1092
|
sp;
|
|
1000
1093
|
part;
|
|
1001
1094
|
themeContext;
|
|
@@ -1006,22 +1099,18 @@ var NotesPlaceholder = class {
|
|
|
1006
1099
|
this.themeContext = themeContext;
|
|
1007
1100
|
this.relationships = relationships;
|
|
1008
1101
|
}
|
|
1009
|
-
#ph() {
|
|
1010
|
-
const nvPr = nvPrOf(this.sp);
|
|
1011
|
-
return nvPr ? firstChild(nvPr, "p:ph") : null;
|
|
1012
|
-
}
|
|
1013
1102
|
#cNvPr() {
|
|
1014
1103
|
const nvSpPr = firstChild(this.sp, "p:nvSpPr");
|
|
1015
1104
|
return nvSpPr ? firstChild(nvSpPr, "p:cNvPr") : null;
|
|
1016
1105
|
}
|
|
1017
|
-
/** Placeholder type (`p:ph/@type`: `
|
|
1106
|
+
/** Placeholder type (`p:ph/@type`: `title` | `body` | `sldNum` | …), or `null` when absent (a body placeholder). */
|
|
1018
1107
|
get type() {
|
|
1019
|
-
const ph = this
|
|
1108
|
+
const ph = placeholderOf(this.sp);
|
|
1020
1109
|
return ph ? attr(ph, "type") : null;
|
|
1021
1110
|
}
|
|
1022
1111
|
/** Placeholder index (`p:ph/@idx`), or `null` when unset. */
|
|
1023
1112
|
get idx() {
|
|
1024
|
-
const ph = this
|
|
1113
|
+
const ph = placeholderOf(this.sp);
|
|
1025
1114
|
return ph ? attr(ph, "idx") : null;
|
|
1026
1115
|
}
|
|
1027
1116
|
/** Shape name (`p:cNvPr/@name`), or `''` when unnamed. */
|
|
@@ -1034,63 +1123,63 @@ var NotesPlaceholder = class {
|
|
|
1034
1123
|
const cNvPr = this.#cNvPr();
|
|
1035
1124
|
return cNvPr ? intValue(attr(cNvPr, "id")) : null;
|
|
1036
1125
|
}
|
|
1037
|
-
/** Left edge in EMU (`a:off/@x`), or `null` when
|
|
1126
|
+
/** Left edge in EMU (`a:off/@x`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1038
1127
|
get left() {
|
|
1039
1128
|
return spPrXfrmEmu(this.sp, "a:off", "x");
|
|
1040
1129
|
}
|
|
1041
|
-
/** Top edge in EMU (`a:off/@y`), or `null` when
|
|
1130
|
+
/** Top edge in EMU (`a:off/@y`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1042
1131
|
get top() {
|
|
1043
1132
|
return spPrXfrmEmu(this.sp, "a:off", "y");
|
|
1044
1133
|
}
|
|
1045
|
-
/** Width in EMU (`a:ext/@cx`), or `null` when
|
|
1134
|
+
/** Width in EMU (`a:ext/@cx`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1046
1135
|
get width() {
|
|
1047
1136
|
return spPrXfrmEmu(this.sp, "a:ext", "cx");
|
|
1048
1137
|
}
|
|
1049
|
-
/** Height in EMU (`a:ext/@cy`), or `null` when
|
|
1138
|
+
/** Height in EMU (`a:ext/@cy`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1050
1139
|
get height() {
|
|
1051
1140
|
return spPrXfrmEmu(this.sp, "a:ext", "cy");
|
|
1052
1141
|
}
|
|
1053
1142
|
/**
|
|
1054
1143
|
* The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
|
|
1055
|
-
* when it carries no text body
|
|
1056
|
-
*
|
|
1057
|
-
* holds the slide-number `a:fld`, whose value surfaces through `TextFrame.text`.
|
|
1144
|
+
* when it carries no text body. Threaded with the owning master/layout theme
|
|
1145
|
+
* context so a run's own `schemeClr` resolves through `Run.resolvedColor`.
|
|
1058
1146
|
*/
|
|
1059
1147
|
get textFrame() {
|
|
1060
1148
|
const txBody = firstChild(this.sp, "p:txBody");
|
|
1061
|
-
|
|
1062
|
-
const placeholder = this.type === "body" ? {
|
|
1063
|
-
ph: {
|
|
1064
|
-
type: this.type,
|
|
1065
|
-
idx: this.idx ?? "0"
|
|
1066
|
-
},
|
|
1067
|
-
flatten: this.themeContext
|
|
1068
|
-
} : void 0;
|
|
1069
|
-
return new TextFrame(txBody, this.part, this.themeContext, placeholder, this.relationships);
|
|
1070
|
-
}
|
|
1071
|
-
/** The placeholder's flattened text (paragraphs joined by `\n`), or `''` when it has no text body. */
|
|
1072
|
-
get text() {
|
|
1073
|
-
return this.textFrame?.text ?? "";
|
|
1149
|
+
return txBody ? new TextFrame(txBody, this.part, this.themeContext, void 0, this.relationships) : null;
|
|
1074
1150
|
}
|
|
1075
1151
|
/** Escape hatch: the underlying `p:sp` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
1076
1152
|
get element_() {
|
|
1077
1153
|
return this.sp;
|
|
1078
1154
|
}
|
|
1079
|
-
/** Mark the owning
|
|
1155
|
+
/** Mark the owning part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
|
|
1080
1156
|
markDirty() {
|
|
1081
1157
|
this.part.markDirty();
|
|
1082
1158
|
}
|
|
1083
1159
|
};
|
|
1084
1160
|
/**
|
|
1085
|
-
*
|
|
1086
|
-
*
|
|
1087
|
-
* shape tree as {@link NotesPlaceholder}s ({@link body}/{@link slideImage}/
|
|
1088
|
-
* {@link slideNumber}) plus the body {@link textFrame} the older getters flatten.
|
|
1161
|
+
* The shared body of a slide master and a slide layout — the two template tiers a slide
|
|
1162
|
+
* inherits from.
|
|
1089
1163
|
*
|
|
1090
|
-
*
|
|
1091
|
-
*
|
|
1164
|
+
* Both are an OPC part holding a `p:cSld` with a shape tree and a resolvable theme context, and
|
|
1165
|
+
* both were written out in full, member for member: `partName`, `relationships`, `name`,
|
|
1166
|
+
* `shapes`, `shapeByIdDeep`, `placeholders`, `themeContext` and the two DOM lookups behind them
|
|
1167
|
+
* were character-for-character identical in the two classes. Nothing about that was broken, but
|
|
1168
|
+
* it meant every accessor added to a template tier had to be added twice, and the ones meant to
|
|
1169
|
+
* agree could quietly stop agreeing.
|
|
1170
|
+
*
|
|
1171
|
+
* What genuinely differs stays on the subclasses. `theme` differs because a layout has to walk
|
|
1172
|
+
* to its master first. `background` differs in the tier label it decodes with and in where it
|
|
1173
|
+
* finds the theme part. And the context itself differs, which is why {@link resolveThemeContext}
|
|
1174
|
+
* is abstract: a master resolves its own `p:clrMap` + theme, a layout walks layout → master →
|
|
1175
|
+
* theme. The caching around it is shared, so a subclass supplies the walk and nothing else.
|
|
1176
|
+
*
|
|
1177
|
+
* `Slide` deliberately does not extend this. It is a shape host too, but it is constructed from
|
|
1178
|
+
* a `Presentation` rather than an `OpcPackage`, reaches its relationships through that, and
|
|
1179
|
+
* reports an unnamed slide as `null` where a template tier reports `''` — three differences in
|
|
1180
|
+
* the same handful of members, which is more contortion than the sharing is worth.
|
|
1092
1181
|
*/
|
|
1093
|
-
var
|
|
1182
|
+
var TemplatePart = class {
|
|
1094
1183
|
opc;
|
|
1095
1184
|
part;
|
|
1096
1185
|
#themeContext;
|
|
@@ -1098,72 +1187,303 @@ var NotesSlide = class {
|
|
|
1098
1187
|
this.opc = opc;
|
|
1099
1188
|
this.part = part;
|
|
1100
1189
|
}
|
|
1101
|
-
/** Partname of
|
|
1190
|
+
/** Partname of this part. */
|
|
1102
1191
|
get partName() {
|
|
1103
1192
|
return this.part.partName;
|
|
1104
1193
|
}
|
|
1194
|
+
/** This part's relationships (its theme or master, image embeds, hyperlinks, …). */
|
|
1195
|
+
get relationships() {
|
|
1196
|
+
return this.opc.relationshipsFor(this.partName);
|
|
1197
|
+
}
|
|
1198
|
+
/** The authoring name (`p:cSld/@name`), or `''` when unnamed. */
|
|
1199
|
+
get name() {
|
|
1200
|
+
const cSld = this.cSld();
|
|
1201
|
+
return (cSld && attr(cSld, "name")) ?? "";
|
|
1202
|
+
}
|
|
1105
1203
|
/**
|
|
1106
|
-
*
|
|
1107
|
-
*
|
|
1204
|
+
* Every shape in this tier's `p:cSld/p:spTree`, in document order — the same `AnyShape`
|
|
1205
|
+
* union `Slide.shapes` returns, so shape-walking code applies unchanged.
|
|
1206
|
+
*
|
|
1207
|
+
* {@link placeholders} is the filtered view of the same tree.
|
|
1108
1208
|
*/
|
|
1109
|
-
|
|
1110
|
-
|
|
1209
|
+
get shapes() {
|
|
1210
|
+
const spTree = this.spTree();
|
|
1211
|
+
return spTree ? buildShapes(spTree, this) : [];
|
|
1111
1212
|
}
|
|
1112
|
-
/** The
|
|
1213
|
+
/** The shape anywhere in this tier's tree with the given drawing id, or `undefined`. */
|
|
1214
|
+
shapeByIdDeep(id) {
|
|
1215
|
+
return findShapeByIdDeep(this.shapes, id);
|
|
1216
|
+
}
|
|
1217
|
+
/** This tier's placeholder shapes (`p:sp` carrying a `p:ph`), in document order. */
|
|
1113
1218
|
get placeholders() {
|
|
1114
|
-
const spTree = this.#spTree();
|
|
1115
|
-
if (!spTree) return [];
|
|
1116
1219
|
const ctx = this.themeContext();
|
|
1117
|
-
const rels = this.
|
|
1118
|
-
return
|
|
1220
|
+
const rels = this.relationships;
|
|
1221
|
+
return placeholderShapes(this.spTree()).map((sp) => new Placeholder(sp, this.part, ctx, rels));
|
|
1119
1222
|
}
|
|
1120
|
-
/**
|
|
1121
|
-
|
|
1122
|
-
return this.#
|
|
1223
|
+
/** This tier's colour/font context, resolved once and cached. Backs each {@link Placeholder}'s text frame. */
|
|
1224
|
+
themeContext() {
|
|
1225
|
+
return this.#themeContext ??= this.resolveThemeContext();
|
|
1123
1226
|
}
|
|
1124
|
-
/** The
|
|
1125
|
-
|
|
1126
|
-
return this
|
|
1227
|
+
/** The `p:cSld` of this part, or `null`. */
|
|
1228
|
+
cSld() {
|
|
1229
|
+
return cSldOf(this.part.dom.documentElement);
|
|
1127
1230
|
}
|
|
1128
|
-
/** The
|
|
1129
|
-
|
|
1130
|
-
return this
|
|
1231
|
+
/** The `p:cSld/p:spTree` of this part, or `null`. */
|
|
1232
|
+
spTree() {
|
|
1233
|
+
return spTreeOf(this.part.dom.documentElement);
|
|
1131
1234
|
}
|
|
1132
|
-
/** The
|
|
1133
|
-
|
|
1134
|
-
|
|
1235
|
+
/** The part this one's first relationship of `type` points at, or `null`. */
|
|
1236
|
+
relTarget(type) {
|
|
1237
|
+
const rels = this.relationships;
|
|
1238
|
+
const rel = rels.byType(type)[0];
|
|
1239
|
+
return rel ? this.opc.part(rels.resolveTarget(rel.id)) ?? null : null;
|
|
1135
1240
|
}
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1241
|
+
};
|
|
1242
|
+
/**
|
|
1243
|
+
* A deck's slide master (`slideMasterN.xml`) as a modeled object. Exposes the
|
|
1244
|
+
* property tiers a bound slide inherits: the {@link colorMap} (token → theme slot),
|
|
1245
|
+
* its {@link theme}, its `shapes` and the `placeholders` subset of them, its own
|
|
1246
|
+
* {@link background}, and the {@link layouts} that build on it. Reachable from
|
|
1247
|
+
* {@link SlideLayout.master} and `Slide.master`.
|
|
1248
|
+
*
|
|
1249
|
+
* `shapes` is where a template's non-placeholder furniture lives: the header band, the
|
|
1250
|
+
* rule under the title, the logo. Whether a given slide draws them is
|
|
1251
|
+
* `Slide.showMasterSp` (and, for a layout, {@link SlideLayout.showMasterSp}).
|
|
1252
|
+
*/
|
|
1253
|
+
var SlideMaster = class extends TemplatePart {
|
|
1254
|
+
/** The master's theme (via its `theme` relationship), or `null` when absent. */
|
|
1255
|
+
get theme() {
|
|
1256
|
+
const themePart = this.relTarget(THEME_REL);
|
|
1257
|
+
return themePart ? new Theme(themePart) : null;
|
|
1139
1258
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1259
|
+
/**
|
|
1260
|
+
* The master's colour map (`p:clrMap`): each of the 12 {@link ColorMapToken}s
|
|
1261
|
+
* mapped to the {@link ThemeColorSlot} it resolves through. This is the indirection
|
|
1262
|
+
* that lets a slide's `schemeClr val="tx1"` reach a theme slot (e.g. `tx1` → `dk1`).
|
|
1263
|
+
* A token the map omits reads `null`.
|
|
1264
|
+
*/
|
|
1265
|
+
get colorMap() {
|
|
1266
|
+
const root = this.part.dom.documentElement;
|
|
1267
|
+
const parsed = parseClrMap(root ? firstChild(root, "p:clrMap") : null);
|
|
1268
|
+
const out = {};
|
|
1269
|
+
for (const token of COLOR_MAP_TOKENS) out[token] = parsed.get(token) ?? null;
|
|
1270
|
+
return out;
|
|
1142
1271
|
}
|
|
1143
|
-
|
|
1144
|
-
|
|
1272
|
+
/** The layouts built on this master (via `p:sldLayoutIdLst` → its relationships), in list order. */
|
|
1273
|
+
get layouts() {
|
|
1274
|
+
const root = this.part.dom.documentElement;
|
|
1275
|
+
const lst = root && firstChild(root, "p:sldLayoutIdLst");
|
|
1276
|
+
if (!lst) return [];
|
|
1277
|
+
const rels = this.relationships;
|
|
1278
|
+
const out = [];
|
|
1279
|
+
for (const entry of getElements(lst, "p:sldLayoutId")) {
|
|
1280
|
+
const relId = attr(entry, "r:id");
|
|
1281
|
+
const part = relId ? this.opc.part(rels.resolveTarget(relId)) : null;
|
|
1282
|
+
if (part) out.push(new SlideLayout(this.opc, part));
|
|
1283
|
+
}
|
|
1284
|
+
return out;
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* The master's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
1288
|
+
* none. Scoped to the master's own element — a slide's *effective* background,
|
|
1289
|
+
* resolved through the slide → layout → master chain, is `Slide.background`.
|
|
1290
|
+
*/
|
|
1291
|
+
get background() {
|
|
1292
|
+
const bg = backgroundElementOf(this.part.dom.documentElement);
|
|
1293
|
+
if (!bg) return null;
|
|
1294
|
+
const themePart = this.relTarget(THEME_REL);
|
|
1295
|
+
return readSlideBackground(bg, "master", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
1296
|
+
}
|
|
1297
|
+
/** A master's context is its own `p:clrMap` plus its theme — no walk beyond this part. */
|
|
1298
|
+
resolveThemeContext() {
|
|
1299
|
+
return resolveMasterColorContext(this.opc, this.partName);
|
|
1145
1300
|
}
|
|
1146
1301
|
};
|
|
1147
|
-
//#endregion
|
|
1148
|
-
//#region src/read/api/ops/deck-target.ts
|
|
1149
1302
|
/**
|
|
1150
|
-
* `
|
|
1303
|
+
* A deck's slide layout (`slideLayoutN.xml`) as a modeled object — the gallery entry
|
|
1304
|
+
* a slide binds to. Exposes its {@link name}, its {@link type} (import-only; the
|
|
1305
|
+
* writer authors none), its {@link master} (and, through it, the {@link theme}), its
|
|
1306
|
+
* `shapes` and the `placeholders` subset of them, and its own {@link background}.
|
|
1307
|
+
* Reachable from `Slide.layout` and {@link SlideMaster.layouts}.
|
|
1151
1308
|
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
* `
|
|
1155
|
-
* those wiring sites. Naming it once means a call site says which rels it wants
|
|
1156
|
-
* rather than how to find them.
|
|
1157
|
-
* @param {DeckTarget} dest - the destination deck
|
|
1158
|
-
* @returns {Relationships} the presentation part's relationships
|
|
1309
|
+
* The write API authors non-placeholder shapes into a layout: every
|
|
1310
|
+
* `defineSlideMaster({ objects })` member that is not a `placeholder` — a `rect`, a
|
|
1311
|
+
* `line`, an `image`, a `chart`, a `text` box — lands in this tier's tree.
|
|
1159
1312
|
*/
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1313
|
+
var SlideLayout = class extends TemplatePart {
|
|
1314
|
+
/**
|
|
1315
|
+
* The layout type (`p:sldLayout/@type`, e.g. `title` | `obj` | `blank`), or `null`
|
|
1316
|
+
* when the layout declares none. Import-only: the writer authors no `@type`, so an
|
|
1317
|
+
* authored deck reads `null`; an imported deck carries PowerPoint's value.
|
|
1318
|
+
*/
|
|
1319
|
+
get type() {
|
|
1320
|
+
const root = this.part.dom.documentElement;
|
|
1321
|
+
return root ? attr(root, "type") : null;
|
|
1322
|
+
}
|
|
1323
|
+
/**
|
|
1324
|
+
* Whether this layout draws the master's non-placeholder shapes
|
|
1325
|
+
* (`p:sldLayout/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
|
|
1326
|
+
* attribute means shown — the layout-tier counterpart of `Slide.showMasterSp`,
|
|
1327
|
+
* and the one PowerPoint actually writes on a section-divider or full-bleed
|
|
1328
|
+
* layout. A renderer resolving what to paint under a slide's own shapes has to
|
|
1329
|
+
* consult both: the slide's flag, then its layout's.
|
|
1330
|
+
*/
|
|
1331
|
+
get showMasterSp() {
|
|
1332
|
+
const root = this.part.dom.documentElement;
|
|
1333
|
+
return boolValue(root && attr(root, "showMasterSp")) !== false;
|
|
1334
|
+
}
|
|
1335
|
+
/** The master this layout is built on (via its `slideMaster` relationship), or `null` when absent. */
|
|
1336
|
+
get master() {
|
|
1337
|
+
const rels = this.relationships;
|
|
1338
|
+
const rel = rels.byType(SLIDE_MASTER_REL)[0];
|
|
1339
|
+
const part = rel ? this.opc.part(rels.resolveTarget(rel.id)) : null;
|
|
1340
|
+
return part ? new SlideMaster(this.opc, part) : null;
|
|
1341
|
+
}
|
|
1342
|
+
/** The theme this layout resolves against, via its {@link master}, or `null`. */
|
|
1343
|
+
get theme() {
|
|
1344
|
+
return this.master?.theme ?? null;
|
|
1345
|
+
}
|
|
1346
|
+
/**
|
|
1347
|
+
* The layout's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
1348
|
+
* none. A layout usually authors one — an explicit fill (`p:bgPr`) or a
|
|
1349
|
+
* theme-indexed reference (`p:bgRef`). The slide's *effective* background is
|
|
1350
|
+
* `Slide.background`.
|
|
1351
|
+
*/
|
|
1352
|
+
get background() {
|
|
1353
|
+
const bg = backgroundElementOf(this.part.dom.documentElement);
|
|
1354
|
+
if (!bg) return null;
|
|
1355
|
+
const themePart = this.master?.theme?.part ?? null;
|
|
1356
|
+
return readSlideBackground(bg, "layout", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
1357
|
+
}
|
|
1358
|
+
/** A layout's context is walked layout → master → theme, which is why it is not the master's. */
|
|
1359
|
+
resolveThemeContext() {
|
|
1360
|
+
return resolveLayoutColorContext(this.opc, this.partName);
|
|
1361
|
+
}
|
|
1362
|
+
};
|
|
1363
|
+
//#endregion
|
|
1364
|
+
//#region src/read/api/notes.ts
|
|
1365
|
+
/**
|
|
1366
|
+
* One placeholder shape (`p:sp`) of a notes slide. A notes slide holds a fixed set:
|
|
1367
|
+
* the slide thumbnail (`type="sldImg"`), the notes body (`type="body"`), and the
|
|
1368
|
+
* slide number (`type="sldNum"`). Geometry getters read the placeholder's *own*
|
|
1369
|
+
* `a:xfrm` and are `null` when it inherits geometry from the notesMaster (the case
|
|
1370
|
+
* on an authored deck, whose `sldImg`/`sldNum` carry an empty `p:spPr`).
|
|
1371
|
+
*
|
|
1372
|
+
* A notes placeholder IS a {@link Placeholder} — identity, geometry and the escape hatch are
|
|
1373
|
+
* the same twelve members read off the same `p:sp`, down to `p:ph` being found the same way —
|
|
1374
|
+
* so it extends one rather than repeating it. Two things are its own: the flattened
|
|
1375
|
+
* {@link text} convenience, and a {@link textFrame} that threads the notesMaster inheritance
|
|
1376
|
+
* context onto the body frame.
|
|
1377
|
+
*/
|
|
1378
|
+
var NotesPlaceholder = class extends Placeholder {
|
|
1379
|
+
/**
|
|
1380
|
+
* The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
|
|
1381
|
+
* when it carries no text body (the `sldImg` thumbnail). The body placeholder's
|
|
1382
|
+
* runs round-trip a slide's authored notes; the `sldNum` placeholder's frame
|
|
1383
|
+
* holds the slide-number `a:fld`, whose value surfaces through `TextFrame.text`.
|
|
1384
|
+
*
|
|
1385
|
+
* Overrides {@link Placeholder.textFrame} for the inheritance context alone — see below.
|
|
1386
|
+
*/
|
|
1387
|
+
get textFrame() {
|
|
1388
|
+
const txBody = firstChild(this.sp, "p:txBody");
|
|
1389
|
+
if (!txBody) return null;
|
|
1390
|
+
const placeholder = this.type === "body" ? {
|
|
1391
|
+
ph: {
|
|
1392
|
+
type: this.type,
|
|
1393
|
+
idx: this.idx ?? "0"
|
|
1394
|
+
},
|
|
1395
|
+
flatten: this.themeContext
|
|
1396
|
+
} : void 0;
|
|
1397
|
+
return new TextFrame(txBody, this.part, this.themeContext, placeholder, this.relationships);
|
|
1398
|
+
}
|
|
1399
|
+
/** The placeholder's flattened text (paragraphs joined by `\n`), or `''` when it has no text body. */
|
|
1400
|
+
get text() {
|
|
1401
|
+
return this.textFrame?.text ?? "";
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1404
|
+
/**
|
|
1405
|
+
* A slide's speaker-notes slide (`notesSlideN.xml`) as a modeled object — the deep
|
|
1406
|
+
* companion to {@link import('./slide.js').Slide.notesText}. Exposes the notes
|
|
1407
|
+
* shape tree as {@link NotesPlaceholder}s ({@link body}/{@link slideImage}/
|
|
1408
|
+
* {@link slideNumber}) plus the body {@link textFrame} the older getters flatten.
|
|
1409
|
+
*
|
|
1410
|
+
* The theme context (notesMaster → `theme2.xml`) is resolved once and shared across
|
|
1411
|
+
* every placeholder's text frame.
|
|
1412
|
+
*/
|
|
1413
|
+
var NotesSlide = class {
|
|
1414
|
+
opc;
|
|
1415
|
+
part;
|
|
1416
|
+
#themeContext;
|
|
1417
|
+
constructor(opc, part) {
|
|
1418
|
+
this.opc = opc;
|
|
1419
|
+
this.part = part;
|
|
1420
|
+
}
|
|
1421
|
+
/** Partname of the notes slide part. */
|
|
1422
|
+
get partName() {
|
|
1423
|
+
return this.part.partName;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* The notes theme context, resolved through the notesMaster → `theme2.xml` chain
|
|
1427
|
+
* and cached. Backs {@link NotesPlaceholder.textFrame}'s `Run.resolvedColor`.
|
|
1428
|
+
*/
|
|
1429
|
+
themeContext() {
|
|
1430
|
+
return this.#themeContext ??= resolveNotesColorContext(this.opc, this.partName);
|
|
1431
|
+
}
|
|
1432
|
+
/** The placeholders (`p:sp`) in the notes shape tree, in document order. */
|
|
1433
|
+
get placeholders() {
|
|
1434
|
+
const spTree = this.#spTree();
|
|
1435
|
+
if (!spTree) return [];
|
|
1436
|
+
const ctx = this.themeContext();
|
|
1437
|
+
const rels = this.opc.relationshipsFor(this.partName);
|
|
1438
|
+
return getElements(spTree, "p:sp").map((sp) => new NotesPlaceholder(sp, this.part, ctx, rels));
|
|
1439
|
+
}
|
|
1440
|
+
/** The notes body placeholder (`p:ph type="body"`), or `null` when the notes slide has none. */
|
|
1441
|
+
get body() {
|
|
1442
|
+
return this.#byType("body");
|
|
1443
|
+
}
|
|
1444
|
+
/** The slide-thumbnail placeholder (`p:ph type="sldImg"`), or `null` when absent. */
|
|
1445
|
+
get slideImage() {
|
|
1446
|
+
return this.#byType("sldImg");
|
|
1447
|
+
}
|
|
1448
|
+
/** The slide-number placeholder (`p:ph type="sldNum"`, carrying the slide-number field), or `null`. */
|
|
1449
|
+
get slideNumber() {
|
|
1450
|
+
return this.#byType("sldNum");
|
|
1451
|
+
}
|
|
1452
|
+
/** The notes body as a navigable {@link TextFrame}, or `null` when there is no body text frame. */
|
|
1453
|
+
get textFrame() {
|
|
1454
|
+
return this.body?.textFrame ?? null;
|
|
1455
|
+
}
|
|
1456
|
+
/** The notes body text flattened (paragraphs joined by `\n`), or `''` when the body is empty/absent. */
|
|
1457
|
+
get text() {
|
|
1458
|
+
return this.body?.text ?? "";
|
|
1459
|
+
}
|
|
1460
|
+
#byType(type) {
|
|
1461
|
+
return this.placeholders.find((ph) => ph.type === type) ?? null;
|
|
1462
|
+
}
|
|
1463
|
+
#spTree() {
|
|
1464
|
+
return spTreeOf(this.part.dom.documentElement);
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
//#endregion
|
|
1468
|
+
//#region src/read/api/ops/deck-target.ts
|
|
1469
|
+
/**
|
|
1470
|
+
* `ppt/_rels/presentation.xml.rels` -- the deck's own relationship part.
|
|
1471
|
+
*
|
|
1472
|
+
* Every part that masters, layouts, the notes master and embedded fonts hang off
|
|
1473
|
+
* is reached through here, so the two-step `dest.presentationPart` ->
|
|
1474
|
+
* `dest.opc.relationshipsFor(part.partName)` was written out at every one of
|
|
1475
|
+
* those wiring sites. Naming it once means a call site says which rels it wants
|
|
1476
|
+
* rather than how to find them.
|
|
1477
|
+
* @param {DeckTarget} dest - the destination deck
|
|
1478
|
+
* @returns {Relationships} the presentation part's relationships
|
|
1479
|
+
*/
|
|
1480
|
+
function presentationRels(dest) {
|
|
1481
|
+
return dest.opc.relationshipsFor(dest.presentationPart.partName);
|
|
1482
|
+
}
|
|
1483
|
+
//#endregion
|
|
1484
|
+
//#region src/read/api/ops/master-registry.ts
|
|
1485
|
+
/**
|
|
1486
|
+
* Slide-master bookkeeping in `presentation.xml` and in a master's own
|
|
1167
1487
|
* `p:sldLayoutIdLst`: registering a master so renderers treat it as active,
|
|
1168
1488
|
* promoting masters to the front of the id list, allocating ids out of the shared
|
|
1169
1489
|
* master/layout id space, and emptying a freshly-copied master's layout list.
|
|
@@ -1391,6 +1711,148 @@ function duplicateSubtree(opc, partName, copies) {
|
|
|
1391
1711
|
return fresh;
|
|
1392
1712
|
}
|
|
1393
1713
|
//#endregion
|
|
1714
|
+
//#region src/read/api/ops/part-reuse.ts
|
|
1715
|
+
/**
|
|
1716
|
+
* Recognising a part the destination deck **already holds**, so an import binds to it
|
|
1717
|
+
* instead of copying it in again.
|
|
1718
|
+
*
|
|
1719
|
+
* The case this exists for is the deck templated from its own source. `fromTemplate`
|
|
1720
|
+
* keeps a package's masters, layouts and theme byte-identical and strips only its
|
|
1721
|
+
* slides, so a script that then imports a slide back out of that same file hands
|
|
1722
|
+
* `copyPart` a `slideLayout -> slideMaster -> theme` subgraph whose every part is
|
|
1723
|
+
* already in the destination under its own partname. The copy registry maps *source*
|
|
1724
|
+
* partnames to copies this import made and knows nothing about what the destination
|
|
1725
|
+
* arrived with, so each of those parts was copied again: the output grew a duplicate
|
|
1726
|
+
* layout and master per imported slide, visible in PowerPoint's layout picker and
|
|
1727
|
+
* enough to make a later `appendSlides({ layout: <name> })` ambiguous.
|
|
1728
|
+
*
|
|
1729
|
+
* The test is deliberately strict, because the cost of getting it wrong is a slide
|
|
1730
|
+
* bound to chrome that only resembles what it asked for. A part is reusable only when
|
|
1731
|
+
* the destination holds it at the *same partname*, with the same content type, the same
|
|
1732
|
+
* bytes (as they would be written, so an edited destination part disqualifies itself),
|
|
1733
|
+
* the same relationships, and the same rule applied recursively to everything those
|
|
1734
|
+
* relationships reach. Byte-equality all the way down means "reuse" and "copy" produce
|
|
1735
|
+
* the same rendering; anything less falls back to the copy, which is always correct and
|
|
1736
|
+
* merely wasteful.
|
|
1737
|
+
*
|
|
1738
|
+
* Two further conditions have nothing to do with identity and everything to do with the
|
|
1739
|
+
* destination being a real deck: a reused master must already be registered in
|
|
1740
|
+
* `presentation.xml`, and a reused layout must already sit in a registered master's
|
|
1741
|
+
* `p:sldLayoutIdLst`. An identical part that is an orphan in the destination is not
|
|
1742
|
+
* something to bind a slide to, and the copy path (which registers and links what it
|
|
1743
|
+
* copies) is the right answer there.
|
|
1744
|
+
*
|
|
1745
|
+
* Reuse mutates nothing, which is what lets it hold across repeated imports: the
|
|
1746
|
+
* destination's chrome stays byte-identical, so the second import recognises it exactly
|
|
1747
|
+
* as the first one did.
|
|
1748
|
+
*/
|
|
1749
|
+
/**
|
|
1750
|
+
* Whether the destination already holds `sourcePartName`'s subgraph, part for part and
|
|
1751
|
+
* byte for byte, in a state an imported slide can bind to.
|
|
1752
|
+
*
|
|
1753
|
+
* @param dest the deck being imported into
|
|
1754
|
+
* @param source the package being imported out of
|
|
1755
|
+
* @param sourcePartName the part the copy is about to make
|
|
1756
|
+
* @return `true` when the copy can be skipped and the existing part used
|
|
1757
|
+
*/
|
|
1758
|
+
function destinationAlreadyHolds(dest, source, sourcePartName) {
|
|
1759
|
+
const part = dest.opc.part(sourcePartName);
|
|
1760
|
+
if (!part || part.contentType === "application/vnd.openxmlformats-officedocument.presentationml.slide+xml") return false;
|
|
1761
|
+
if (!identicalSubgraph(dest.opc, source, sourcePartName, /* @__PURE__ */ new Set())) return false;
|
|
1762
|
+
return wiredIntoDeck(dest, sourcePartName);
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* `partName` and everything it reaches are the same in both packages: same content type,
|
|
1766
|
+
* same body bytes, same relationships, recursively.
|
|
1767
|
+
*
|
|
1768
|
+
* The destination side is compared as it would be **written** (`serialize()`), not as it
|
|
1769
|
+
* was read, so a part some earlier edit marked dirty is treated as different even when
|
|
1770
|
+
* the edit was a no-op. Under-reusing costs a duplicated part; over-reusing binds a slide
|
|
1771
|
+
* to something else's chrome.
|
|
1772
|
+
*
|
|
1773
|
+
* The traversal skips exactly what `copyPart` skips — the notes relationship, and a
|
|
1774
|
+
* master's relationships to its layouts — so it asks about the subgraph the copy would
|
|
1775
|
+
* actually have made. Skipping the master's layout list is what keeps one edited layout
|
|
1776
|
+
* from disqualifying every *other* layout that shares its master.
|
|
1777
|
+
*/
|
|
1778
|
+
function identicalSubgraph(dest, source, partName, visited) {
|
|
1779
|
+
if (visited.has(partName)) return true;
|
|
1780
|
+
visited.add(partName);
|
|
1781
|
+
const sourcePart = source.part(partName);
|
|
1782
|
+
const destPart = dest.part(partName);
|
|
1783
|
+
if (!sourcePart || !destPart) return false;
|
|
1784
|
+
if (sourcePart.contentType !== destPart.contentType) return false;
|
|
1785
|
+
if (!bytesEqual(sourcePart.bytes, destPart.serialize())) return false;
|
|
1786
|
+
const sourceRels = [...source.relationshipsFor(partName)];
|
|
1787
|
+
const destRels = [...dest.relationshipsFor(partName)];
|
|
1788
|
+
if (sourceRels.length !== destRels.length) return false;
|
|
1789
|
+
const isMaster = sourcePart.contentType === SLIDE_MASTER_CONTENT_TYPE;
|
|
1790
|
+
const destById = new Map(destRels.map((rel) => [rel.id, rel]));
|
|
1791
|
+
for (const rel of sourceRels) {
|
|
1792
|
+
const counterpart = destById.get(rel.id);
|
|
1793
|
+
if (!counterpart) return false;
|
|
1794
|
+
if (counterpart.type !== rel.type || counterpart.targetMode !== rel.targetMode) return false;
|
|
1795
|
+
if (rel.targetMode === "External") {
|
|
1796
|
+
if (counterpart.target !== rel.target) return false;
|
|
1797
|
+
continue;
|
|
1798
|
+
}
|
|
1799
|
+
const target = source.relationshipsFor(partName).resolveTarget(rel.id);
|
|
1800
|
+
if (dest.relationshipsFor(partName).resolveTarget(rel.id) !== target) return false;
|
|
1801
|
+
if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") continue;
|
|
1802
|
+
if (isMaster && rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") continue;
|
|
1803
|
+
if (!identicalSubgraph(dest, source, target, visited)) return false;
|
|
1804
|
+
}
|
|
1805
|
+
return true;
|
|
1806
|
+
}
|
|
1807
|
+
/**
|
|
1808
|
+
* Whether the destination's copy of this part is wired into the deck, for the two kinds
|
|
1809
|
+
* where "present in the package" is not the same as "usable".
|
|
1810
|
+
*
|
|
1811
|
+
* A master absent from `p:sldMasterIdLst` is inert: renderers ignore its background and
|
|
1812
|
+
* shape tree, so a slide bound through it renders blank. A layout absent from a
|
|
1813
|
+
* registered master's `p:sldLayoutIdLst` is not in the deck's gallery, so `layouts()`
|
|
1814
|
+
* cannot name it and `appendSlides` cannot bind to it. Every other kind (theme, media,
|
|
1815
|
+
* table styles) is reached by relationship alone and needs no registration.
|
|
1816
|
+
*/
|
|
1817
|
+
function wiredIntoDeck(dest, partName) {
|
|
1818
|
+
const contentType = dest.opc.part(partName)?.contentType;
|
|
1819
|
+
if (contentType === "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml") return registeredMasters(dest).includes(partName);
|
|
1820
|
+
if (contentType === "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml") return registeredMasters(dest).some((master) => galleryOf(dest, master).includes(partName));
|
|
1821
|
+
return true;
|
|
1822
|
+
}
|
|
1823
|
+
/** The destination's masters in `p:sldMasterIdLst` order — the ones renderers treat as active. */
|
|
1824
|
+
function registeredMasters(dest) {
|
|
1825
|
+
const root = dest.presentationPart.dom.documentElement;
|
|
1826
|
+
const lst = root && firstChild(root, "p:sldMasterIdLst");
|
|
1827
|
+
if (!lst) return [];
|
|
1828
|
+
const rels = presentationRels(dest);
|
|
1829
|
+
return idListTargets(lst, "p:sldMasterId", (relId) => rels.resolveTarget(relId));
|
|
1830
|
+
}
|
|
1831
|
+
/** One master's layouts in `p:sldLayoutIdLst` order — its slice of the deck's gallery. */
|
|
1832
|
+
function galleryOf(dest, masterPartName) {
|
|
1833
|
+
const root = dest.opc.part(masterPartName)?.dom.documentElement;
|
|
1834
|
+
const lst = root && firstChild(root, "p:sldLayoutIdLst");
|
|
1835
|
+
if (!lst) return [];
|
|
1836
|
+
const rels = dest.opc.relationshipsFor(masterPartName);
|
|
1837
|
+
return idListTargets(lst, "p:sldLayoutId", (relId) => rels.resolveTarget(relId));
|
|
1838
|
+
}
|
|
1839
|
+
/** Resolve every `@r:id` in an id list to its partname, skipping entries that carry none. */
|
|
1840
|
+
function idListTargets(lst, qname, resolve) {
|
|
1841
|
+
const out = [];
|
|
1842
|
+
for (const entry of getElements(lst, qname)) {
|
|
1843
|
+
const relId = attr(entry, "r:id");
|
|
1844
|
+
if (relId) out.push(resolve(relId));
|
|
1845
|
+
}
|
|
1846
|
+
return out;
|
|
1847
|
+
}
|
|
1848
|
+
/** Byte-for-byte equality of two part bodies. */
|
|
1849
|
+
function bytesEqual(a, b) {
|
|
1850
|
+
if (a === b) return true;
|
|
1851
|
+
if (a.length !== b.length) return false;
|
|
1852
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
1853
|
+
return true;
|
|
1854
|
+
}
|
|
1855
|
+
//#endregion
|
|
1394
1856
|
//#region src/read/api/ops/part-copy.ts
|
|
1395
1857
|
/** Open an ownership scope for one page copy. */
|
|
1396
1858
|
function newOwnedScope() {
|
|
@@ -1448,7 +1910,7 @@ function copySlidePart(ctx, sourcePartName) {
|
|
|
1448
1910
|
* from outside, as the rebinding import paths do for the page they build
|
|
1449
1911
|
* themselves.
|
|
1450
1912
|
*/
|
|
1451
|
-
function copyPart(ctx, sourcePartName, owned) {
|
|
1913
|
+
function copyPart(ctx, sourcePartName, owned, allowReuse = false) {
|
|
1452
1914
|
if (owned) {
|
|
1453
1915
|
const alreadyOwned = owned.get(sourcePartName);
|
|
1454
1916
|
if (alreadyOwned !== void 0) return alreadyOwned;
|
|
@@ -1457,6 +1919,12 @@ function copyPart(ctx, sourcePartName, owned) {
|
|
|
1457
1919
|
const selectedDest = ctx.selection?.destinations.get(sourcePartName);
|
|
1458
1920
|
if (existing && (selectedDest === void 0 || existing === selectedDest)) return existing;
|
|
1459
1921
|
}
|
|
1922
|
+
if (allowReuse && !owned && !ctx.selection?.destinations.has(sourcePartName)) {
|
|
1923
|
+
if (destinationAlreadyHolds(ctx.dest, ctx.source, sourcePartName)) {
|
|
1924
|
+
ctx.registry.set(sourcePartName, sourcePartName);
|
|
1925
|
+
return sourcePartName;
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1460
1928
|
const sourcePart = ctx.source.part(sourcePartName);
|
|
1461
1929
|
if (!sourcePart) throw new PackageReadError("package/part-missing", `importSlide: source package has no part ${sourcePartName}`);
|
|
1462
1930
|
const newPartName = owned ? ctx.dest.opc.reservePartNameLike(sourcePartName) : ctx.selection?.destinations.get(sourcePartName) ?? ctx.dest.opc.reservePartNameLike(sourcePartName);
|
|
@@ -1465,6 +1933,7 @@ function copyPart(ctx, sourcePartName, owned) {
|
|
|
1465
1933
|
else ctx.registry.set(sourcePartName, newPartName);
|
|
1466
1934
|
const scope = owned ?? (ctx.selection?.destinations.has(sourcePartName) ? newOwnedScope() : void 0);
|
|
1467
1935
|
const isMaster = sourcePart.contentType === SLIDE_MASTER_CONTENT_TYPE;
|
|
1936
|
+
const isPage = sourcePart.contentType === SLIDE_CONTENT_TYPE;
|
|
1468
1937
|
const sourceRels = ctx.source.relationshipsFor(sourcePartName);
|
|
1469
1938
|
const targetRels = ctx.dest.opc.relationshipsFor(newPartName);
|
|
1470
1939
|
for (const rel of sourceRels) {
|
|
@@ -1474,7 +1943,7 @@ function copyPart(ctx, sourcePartName, owned) {
|
|
|
1474
1943
|
targetRels.addWithId(rel.id, rel.type, rel.target, "External");
|
|
1475
1944
|
continue;
|
|
1476
1945
|
}
|
|
1477
|
-
const newTargetPartName = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : scope);
|
|
1946
|
+
const newTargetPartName = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : scope, isPage);
|
|
1478
1947
|
targetRels.addWithId(rel.id, rel.type, relativePartName(newPartName, newTargetPartName));
|
|
1479
1948
|
}
|
|
1480
1949
|
if (isMaster) {
|
|
@@ -2018,427 +2487,6 @@ function readTagsForPart(opc, ownerPartName) {
|
|
|
2018
2487
|
return out;
|
|
2019
2488
|
}
|
|
2020
2489
|
//#endregion
|
|
2021
|
-
//#region src/read/api/chrome.ts
|
|
2022
|
-
const THEME_COLOR_SLOTS = [
|
|
2023
|
-
"dk1",
|
|
2024
|
-
"lt1",
|
|
2025
|
-
"dk2",
|
|
2026
|
-
"lt2",
|
|
2027
|
-
"accent1",
|
|
2028
|
-
"accent2",
|
|
2029
|
-
"accent3",
|
|
2030
|
-
"accent4",
|
|
2031
|
-
"accent5",
|
|
2032
|
-
"accent6",
|
|
2033
|
-
"hlink",
|
|
2034
|
-
"folHlink"
|
|
2035
|
-
];
|
|
2036
|
-
const COLOR_MAP_TOKENS = [
|
|
2037
|
-
"bg1",
|
|
2038
|
-
"tx1",
|
|
2039
|
-
"bg2",
|
|
2040
|
-
"tx2",
|
|
2041
|
-
"accent1",
|
|
2042
|
-
"accent2",
|
|
2043
|
-
"accent3",
|
|
2044
|
-
"accent4",
|
|
2045
|
-
"accent5",
|
|
2046
|
-
"accent6",
|
|
2047
|
-
"hlink",
|
|
2048
|
-
"folHlink"
|
|
2049
|
-
];
|
|
2050
|
-
/** Read `<a:latin>`/`<a:ea>`/`<a:cs>` typefaces of a theme font family; empty string ⇒ `null`. */
|
|
2051
|
-
function readThemeFontFace(font) {
|
|
2052
|
-
const face = (child) => {
|
|
2053
|
-
const el = font && firstChild(font, child);
|
|
2054
|
-
return el && attr(el, "typeface") || null;
|
|
2055
|
-
};
|
|
2056
|
-
return {
|
|
2057
|
-
latin: face("a:latin"),
|
|
2058
|
-
ea: face("a:ea"),
|
|
2059
|
-
cs: face("a:cs")
|
|
2060
|
-
};
|
|
2061
|
-
}
|
|
2062
|
-
/**
|
|
2063
|
-
* A deck's theme part (`theme1.xml`) as a modeled object. Exposes the two property
|
|
2064
|
-
* tiers a slide resolves against: the {@link colorScheme} (the 12 `a:clrScheme`
|
|
2065
|
-
* slots as literal hex, `a:sysClr` resolved via its `lastClr`) and the
|
|
2066
|
-
* {@link fontScheme} (major/minor Latin/EA/CS faces). Reachable from
|
|
2067
|
-
* {@link SlideMaster.theme} and `Slide.theme`.
|
|
2068
|
-
*/
|
|
2069
|
-
var Theme = class {
|
|
2070
|
-
part;
|
|
2071
|
-
constructor(part) {
|
|
2072
|
-
this.part = part;
|
|
2073
|
-
}
|
|
2074
|
-
/** Partname of the theme part. */
|
|
2075
|
-
get partName() {
|
|
2076
|
-
return this.part.partName;
|
|
2077
|
-
}
|
|
2078
|
-
/** Theme name (`a:theme/@name`, e.g. `Office Theme`), or `null` when unnamed. */
|
|
2079
|
-
get name() {
|
|
2080
|
-
const root = this.part.dom.documentElement;
|
|
2081
|
-
return root ? attr(root, "name") : null;
|
|
2082
|
-
}
|
|
2083
|
-
/** Colour-scheme name (`a:clrScheme/@name`, e.g. `Office`), or `null`. */
|
|
2084
|
-
get colorSchemeName() {
|
|
2085
|
-
const clrScheme = this.#clrScheme();
|
|
2086
|
-
return clrScheme ? attr(clrScheme, "name") : null;
|
|
2087
|
-
}
|
|
2088
|
-
/**
|
|
2089
|
-
* The theme colour scheme: the 12 {@link ThemeColorSlot}s resolved to a 6-hex RGB
|
|
2090
|
-
* (an `a:sysClr` slot resolves through its `lastClr`). A slot the theme does not
|
|
2091
|
-
* define reads `null`. Use {@link color} to look one slot up.
|
|
2092
|
-
*/
|
|
2093
|
-
get colorScheme() {
|
|
2094
|
-
const parsed = parseClrScheme(this.#clrScheme());
|
|
2095
|
-
const out = {};
|
|
2096
|
-
for (const slot of THEME_COLOR_SLOTS) out[slot] = parsed.get(slot) ?? null;
|
|
2097
|
-
return out;
|
|
2098
|
-
}
|
|
2099
|
-
/** The 6-hex RGB of one colour-scheme slot, or `null` when the theme does not define it. */
|
|
2100
|
-
color(slot) {
|
|
2101
|
-
return parseClrScheme(this.#clrScheme()).get(slot) ?? null;
|
|
2102
|
-
}
|
|
2103
|
-
/** The theme's `a:fontScheme` (major/minor font families), or `null` when the theme declares none. */
|
|
2104
|
-
get fontScheme() {
|
|
2105
|
-
const themeElements = this.#themeElements();
|
|
2106
|
-
const fontScheme = themeElements && firstChild(themeElements, "a:fontScheme");
|
|
2107
|
-
if (!fontScheme) return null;
|
|
2108
|
-
return {
|
|
2109
|
-
name: attr(fontScheme, "name"),
|
|
2110
|
-
major: readThemeFontFace(firstChild(fontScheme, "a:majorFont")),
|
|
2111
|
-
minor: readThemeFontFace(firstChild(fontScheme, "a:minorFont"))
|
|
2112
|
-
};
|
|
2113
|
-
}
|
|
2114
|
-
/** Escape hatch: the underlying `a:theme` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
2115
|
-
get element_() {
|
|
2116
|
-
return this.part.dom.documentElement;
|
|
2117
|
-
}
|
|
2118
|
-
/** Mark the theme part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
|
|
2119
|
-
markDirty() {
|
|
2120
|
-
this.part.markDirty();
|
|
2121
|
-
}
|
|
2122
|
-
#themeElements() {
|
|
2123
|
-
const root = this.part.dom.documentElement;
|
|
2124
|
-
return root ? firstChild(root, "a:themeElements") : null;
|
|
2125
|
-
}
|
|
2126
|
-
#clrScheme() {
|
|
2127
|
-
const themeElements = this.#themeElements();
|
|
2128
|
-
return themeElements ? firstChild(themeElements, "a:clrScheme") : null;
|
|
2129
|
-
}
|
|
2130
|
-
};
|
|
2131
|
-
/** The placeholder shapes (`p:sp` carrying a `p:ph`) of a master/layout `p:spTree`, in document order. */
|
|
2132
|
-
function placeholderShapes(spTree) {
|
|
2133
|
-
if (!spTree) return [];
|
|
2134
|
-
return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
|
|
2135
|
-
}
|
|
2136
|
-
/**
|
|
2137
|
-
* One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout —
|
|
2138
|
-
* the definition a slide's placeholder of the same `type`/`idx` inherits geometry
|
|
2139
|
-
* and text style from. Geometry getters read the placeholder's *own* `a:xfrm`,
|
|
2140
|
-
* which a master/layout placeholder carries explicitly (so it round-trips, unlike a
|
|
2141
|
-
* notes placeholder's inherited geometry).
|
|
2142
|
-
*
|
|
2143
|
-
* This is the identity-and-geometry view. The same `p:sp` also appears in
|
|
2144
|
-
* {@link SlideMaster.shapes} / {@link SlideLayout.shapes} as an `AutoShape`, which
|
|
2145
|
-
* additionally carries the paint surface (`resolvedFill`, `resolvedLine`,
|
|
2146
|
-
* `presetGeometry`, `rotation`, `absoluteFrame`) and reports its `p:ph` through
|
|
2147
|
-
* `AutoShape.placeholder`. Read a placeholder here to *place* it; read it there to
|
|
2148
|
-
* *draw* it.
|
|
2149
|
-
*/
|
|
2150
|
-
var Placeholder = class {
|
|
2151
|
-
sp;
|
|
2152
|
-
part;
|
|
2153
|
-
themeContext;
|
|
2154
|
-
relationships;
|
|
2155
|
-
constructor(sp, part, themeContext, relationships) {
|
|
2156
|
-
this.sp = sp;
|
|
2157
|
-
this.part = part;
|
|
2158
|
-
this.themeContext = themeContext;
|
|
2159
|
-
this.relationships = relationships;
|
|
2160
|
-
}
|
|
2161
|
-
#cNvPr() {
|
|
2162
|
-
const nvSpPr = firstChild(this.sp, "p:nvSpPr");
|
|
2163
|
-
return nvSpPr ? firstChild(nvSpPr, "p:cNvPr") : null;
|
|
2164
|
-
}
|
|
2165
|
-
/** Placeholder type (`p:ph/@type`: `title` | `body` | `sldNum` | …), or `null` when absent (a body placeholder). */
|
|
2166
|
-
get type() {
|
|
2167
|
-
const ph = placeholderOf(this.sp);
|
|
2168
|
-
return ph ? attr(ph, "type") : null;
|
|
2169
|
-
}
|
|
2170
|
-
/** Placeholder index (`p:ph/@idx`), or `null` when unset. */
|
|
2171
|
-
get idx() {
|
|
2172
|
-
const ph = placeholderOf(this.sp);
|
|
2173
|
-
return ph ? attr(ph, "idx") : null;
|
|
2174
|
-
}
|
|
2175
|
-
/** Shape name (`p:cNvPr/@name`), or `''` when unnamed. */
|
|
2176
|
-
get name() {
|
|
2177
|
-
const cNvPr = this.#cNvPr();
|
|
2178
|
-
return (cNvPr && attr(cNvPr, "name")) ?? "";
|
|
2179
|
-
}
|
|
2180
|
-
/** Drawing id (`p:cNvPr/@id`), or `null` when absent. */
|
|
2181
|
-
get id() {
|
|
2182
|
-
const cNvPr = this.#cNvPr();
|
|
2183
|
-
return cNvPr ? intValue(attr(cNvPr, "id")) : null;
|
|
2184
|
-
}
|
|
2185
|
-
/** Left edge in EMU (`a:off/@x`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
2186
|
-
get left() {
|
|
2187
|
-
return spPrXfrmEmu(this.sp, "a:off", "x");
|
|
2188
|
-
}
|
|
2189
|
-
/** Top edge in EMU (`a:off/@y`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
2190
|
-
get top() {
|
|
2191
|
-
return spPrXfrmEmu(this.sp, "a:off", "y");
|
|
2192
|
-
}
|
|
2193
|
-
/** Width in EMU (`a:ext/@cx`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
2194
|
-
get width() {
|
|
2195
|
-
return spPrXfrmEmu(this.sp, "a:ext", "cx");
|
|
2196
|
-
}
|
|
2197
|
-
/** Height in EMU (`a:ext/@cy`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
2198
|
-
get height() {
|
|
2199
|
-
return spPrXfrmEmu(this.sp, "a:ext", "cy");
|
|
2200
|
-
}
|
|
2201
|
-
/**
|
|
2202
|
-
* The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
|
|
2203
|
-
* when it carries no text body. Threaded with the owning master/layout theme
|
|
2204
|
-
* context so a run's own `schemeClr` resolves through `Run.resolvedColor`.
|
|
2205
|
-
*/
|
|
2206
|
-
get textFrame() {
|
|
2207
|
-
const txBody = firstChild(this.sp, "p:txBody");
|
|
2208
|
-
return txBody ? new TextFrame(txBody, this.part, this.themeContext, void 0, this.relationships) : null;
|
|
2209
|
-
}
|
|
2210
|
-
/** Escape hatch: the underlying `p:sp` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
2211
|
-
get element_() {
|
|
2212
|
-
return this.sp;
|
|
2213
|
-
}
|
|
2214
|
-
/** Mark the owning master/layout part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
|
|
2215
|
-
markDirty() {
|
|
2216
|
-
this.part.markDirty();
|
|
2217
|
-
}
|
|
2218
|
-
};
|
|
2219
|
-
/**
|
|
2220
|
-
* A deck's slide master (`slideMasterN.xml`) as a modeled object. Exposes the
|
|
2221
|
-
* property tiers a bound slide inherits: the {@link colorMap} (token → theme slot),
|
|
2222
|
-
* its {@link theme}, its {@link shapes} and the {@link placeholders} subset of them,
|
|
2223
|
-
* its own {@link background}, and the {@link layouts} that build on it. Reachable
|
|
2224
|
-
* from {@link SlideLayout.master} and `Slide.master`.
|
|
2225
|
-
*/
|
|
2226
|
-
var SlideMaster = class {
|
|
2227
|
-
opc;
|
|
2228
|
-
part;
|
|
2229
|
-
#themeContext;
|
|
2230
|
-
constructor(opc, part) {
|
|
2231
|
-
this.opc = opc;
|
|
2232
|
-
this.part = part;
|
|
2233
|
-
}
|
|
2234
|
-
/** Partname of the master part. */
|
|
2235
|
-
get partName() {
|
|
2236
|
-
return this.part.partName;
|
|
2237
|
-
}
|
|
2238
|
-
/** This master part's relationships (its layouts, its theme, image embeds, …). */
|
|
2239
|
-
get relationships() {
|
|
2240
|
-
return this.opc.relationshipsFor(this.partName);
|
|
2241
|
-
}
|
|
2242
|
-
/** The master's authoring name (`p:cSld/@name`), or `''` when unnamed (the writer's default). */
|
|
2243
|
-
get name() {
|
|
2244
|
-
const cSld = this.#cSld();
|
|
2245
|
-
return (cSld && attr(cSld, "name")) ?? "";
|
|
2246
|
-
}
|
|
2247
|
-
/** The master's theme (via its `theme` relationship), or `null` when absent. */
|
|
2248
|
-
get theme() {
|
|
2249
|
-
const themePart = this.#relTarget(THEME_REL);
|
|
2250
|
-
return themePart ? new Theme(themePart) : null;
|
|
2251
|
-
}
|
|
2252
|
-
/**
|
|
2253
|
-
* The master's colour map (`p:clrMap`): each of the 12 {@link ColorMapToken}s
|
|
2254
|
-
* mapped to the {@link ThemeColorSlot} it resolves through. This is the indirection
|
|
2255
|
-
* that lets a slide's `schemeClr val="tx1"` reach a theme slot (e.g. `tx1` → `dk1`).
|
|
2256
|
-
* A token the map omits reads `null`.
|
|
2257
|
-
*/
|
|
2258
|
-
get colorMap() {
|
|
2259
|
-
const root = this.part.dom.documentElement;
|
|
2260
|
-
const parsed = parseClrMap(root ? firstChild(root, "p:clrMap") : null);
|
|
2261
|
-
const out = {};
|
|
2262
|
-
for (const token of COLOR_MAP_TOKENS) out[token] = parsed.get(token) ?? null;
|
|
2263
|
-
return out;
|
|
2264
|
-
}
|
|
2265
|
-
/**
|
|
2266
|
-
* Every shape in the master's `p:cSld/p:spTree`, in document order — the same
|
|
2267
|
-
* `AnyShape` union `Slide.shapes` returns, so shape-walking code applies
|
|
2268
|
-
* unchanged. This is where a template's non-placeholder furniture lives: the
|
|
2269
|
-
* header band, the rule under the title, the logo. Whether a given slide draws
|
|
2270
|
-
* them is `Slide.showMasterSp` (and, for a layout, {@link SlideLayout.showMasterSp}).
|
|
2271
|
-
*
|
|
2272
|
-
* {@link placeholders} is the filtered view of the same tree.
|
|
2273
|
-
*/
|
|
2274
|
-
get shapes() {
|
|
2275
|
-
const spTree = this.#spTree();
|
|
2276
|
-
return spTree ? buildShapes(spTree, this) : [];
|
|
2277
|
-
}
|
|
2278
|
-
/** The shape anywhere in the master's tree with the given drawing id, or `undefined`. */
|
|
2279
|
-
shapeByIdDeep(id) {
|
|
2280
|
-
return findShapeByIdDeep(this.shapes, id);
|
|
2281
|
-
}
|
|
2282
|
-
/** The master's placeholder shapes (`p:sp` carrying a `p:ph`), in document order. */
|
|
2283
|
-
get placeholders() {
|
|
2284
|
-
const ctx = this.themeContext();
|
|
2285
|
-
const rels = this.relationships;
|
|
2286
|
-
return placeholderShapes(this.#spTree()).map((sp) => new Placeholder(sp, this.part, ctx, rels));
|
|
2287
|
-
}
|
|
2288
|
-
/** The layouts built on this master (via `p:sldLayoutIdLst` → its relationships), in list order. */
|
|
2289
|
-
get layouts() {
|
|
2290
|
-
const root = this.part.dom.documentElement;
|
|
2291
|
-
const lst = root && firstChild(root, "p:sldLayoutIdLst");
|
|
2292
|
-
if (!lst) return [];
|
|
2293
|
-
const rels = this.relationships;
|
|
2294
|
-
const out = [];
|
|
2295
|
-
for (const entry of getElements(lst, "p:sldLayoutId")) {
|
|
2296
|
-
const relId = attr(entry, "r:id");
|
|
2297
|
-
const part = relId ? this.opc.part(rels.resolveTarget(relId)) : null;
|
|
2298
|
-
if (part) out.push(new SlideLayout(this.opc, part));
|
|
2299
|
-
}
|
|
2300
|
-
return out;
|
|
2301
|
-
}
|
|
2302
|
-
/**
|
|
2303
|
-
* The master's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
2304
|
-
* none. Scoped to the master's own element — a slide's *effective* background,
|
|
2305
|
-
* resolved through the slide → layout → master chain, is `Slide.background`.
|
|
2306
|
-
*/
|
|
2307
|
-
get background() {
|
|
2308
|
-
const bg = backgroundElementOf(this.part.dom.documentElement);
|
|
2309
|
-
if (!bg) return null;
|
|
2310
|
-
const themePart = this.#relTarget(THEME_REL);
|
|
2311
|
-
return readSlideBackground(bg, "master", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
2312
|
-
}
|
|
2313
|
-
/**
|
|
2314
|
-
* The master's colour/font context (its `p:clrMap` + theme), resolved once and
|
|
2315
|
-
* cached. Backs each {@link Placeholder}'s text frame.
|
|
2316
|
-
*/
|
|
2317
|
-
themeContext() {
|
|
2318
|
-
return this.#themeContext ??= resolveMasterColorContext(this.opc, this.partName);
|
|
2319
|
-
}
|
|
2320
|
-
#relTarget(type) {
|
|
2321
|
-
const rels = this.relationships;
|
|
2322
|
-
const rel = rels.byType(type)[0];
|
|
2323
|
-
return rel ? this.opc.part(rels.resolveTarget(rel.id)) ?? null : null;
|
|
2324
|
-
}
|
|
2325
|
-
#cSld() {
|
|
2326
|
-
return cSldOf(this.part.dom.documentElement);
|
|
2327
|
-
}
|
|
2328
|
-
#spTree() {
|
|
2329
|
-
return spTreeOf(this.part.dom.documentElement);
|
|
2330
|
-
}
|
|
2331
|
-
};
|
|
2332
|
-
/**
|
|
2333
|
-
* A deck's slide layout (`slideLayoutN.xml`) as a modeled object — the gallery entry
|
|
2334
|
-
* a slide binds to. Exposes its {@link name}, its {@link type} (import-only; the
|
|
2335
|
-
* writer authors none), its {@link master} (and, through it, the {@link theme}), its
|
|
2336
|
-
* {@link shapes} and the {@link placeholders} subset of them, and its own
|
|
2337
|
-
* {@link background}. Reachable from `Slide.layout` and {@link SlideMaster.layouts}.
|
|
2338
|
-
*/
|
|
2339
|
-
var SlideLayout = class {
|
|
2340
|
-
opc;
|
|
2341
|
-
part;
|
|
2342
|
-
#themeContext;
|
|
2343
|
-
constructor(opc, part) {
|
|
2344
|
-
this.opc = opc;
|
|
2345
|
-
this.part = part;
|
|
2346
|
-
}
|
|
2347
|
-
/** Partname of the layout part. */
|
|
2348
|
-
get partName() {
|
|
2349
|
-
return this.part.partName;
|
|
2350
|
-
}
|
|
2351
|
-
/** This layout part's relationships (its master, image embeds, hyperlinks, …). */
|
|
2352
|
-
get relationships() {
|
|
2353
|
-
return this.opc.relationshipsFor(this.partName);
|
|
2354
|
-
}
|
|
2355
|
-
/** The layout's authoring name (`p:cSld/@name`, e.g. `Title and Content`), or `''` when unnamed. */
|
|
2356
|
-
get name() {
|
|
2357
|
-
const cSld = this.#cSld();
|
|
2358
|
-
return (cSld && attr(cSld, "name")) ?? "";
|
|
2359
|
-
}
|
|
2360
|
-
/**
|
|
2361
|
-
* The layout type (`p:sldLayout/@type`, e.g. `title` | `obj` | `blank`), or `null`
|
|
2362
|
-
* when the layout declares none. Import-only: the writer authors no `@type`, so an
|
|
2363
|
-
* authored deck reads `null`; an imported deck carries PowerPoint's value.
|
|
2364
|
-
*/
|
|
2365
|
-
get type() {
|
|
2366
|
-
const root = this.part.dom.documentElement;
|
|
2367
|
-
return root ? attr(root, "type") : null;
|
|
2368
|
-
}
|
|
2369
|
-
/**
|
|
2370
|
-
* Whether this layout draws the master's non-placeholder shapes
|
|
2371
|
-
* (`p:sldLayout/@showMasterSp`). `xsd:boolean` defaulting to `true`, so an absent
|
|
2372
|
-
* attribute means shown — the layout-tier counterpart of `Slide.showMasterSp`,
|
|
2373
|
-
* and the one PowerPoint actually writes on a section-divider or full-bleed
|
|
2374
|
-
* layout. A renderer resolving what to paint under a slide's own shapes has to
|
|
2375
|
-
* consult both: the slide's flag, then its layout's.
|
|
2376
|
-
*/
|
|
2377
|
-
get showMasterSp() {
|
|
2378
|
-
const root = this.part.dom.documentElement;
|
|
2379
|
-
return boolValue(root && attr(root, "showMasterSp")) !== false;
|
|
2380
|
-
}
|
|
2381
|
-
/** The master this layout is built on (via its `slideMaster` relationship), or `null` when absent. */
|
|
2382
|
-
get master() {
|
|
2383
|
-
const rels = this.relationships;
|
|
2384
|
-
const rel = rels.byType(SLIDE_MASTER_REL)[0];
|
|
2385
|
-
const part = rel ? this.opc.part(rels.resolveTarget(rel.id)) : null;
|
|
2386
|
-
return part ? new SlideMaster(this.opc, part) : null;
|
|
2387
|
-
}
|
|
2388
|
-
/** The theme this layout resolves against, via its {@link master}, or `null`. */
|
|
2389
|
-
get theme() {
|
|
2390
|
-
return this.master?.theme ?? null;
|
|
2391
|
-
}
|
|
2392
|
-
/**
|
|
2393
|
-
* Every shape in the layout's `p:cSld/p:spTree`, in document order — the same
|
|
2394
|
-
* `AnyShape` union `Slide.shapes` returns, so shape-walking code applies
|
|
2395
|
-
* unchanged. The write API authors non-placeholder shapes here: every
|
|
2396
|
-
* `defineSlideMaster({ objects })` member that is not a `placeholder` (a `rect`,
|
|
2397
|
-
* a `line`, an `image`, a `chart`, a `text` box) lands in this tree.
|
|
2398
|
-
*
|
|
2399
|
-
* {@link placeholders} is the filtered view of the same tree.
|
|
2400
|
-
*/
|
|
2401
|
-
get shapes() {
|
|
2402
|
-
const spTree = this.#spTree();
|
|
2403
|
-
return spTree ? buildShapes(spTree, this) : [];
|
|
2404
|
-
}
|
|
2405
|
-
/** The shape anywhere in the layout's tree with the given drawing id, or `undefined`. */
|
|
2406
|
-
shapeByIdDeep(id) {
|
|
2407
|
-
return findShapeByIdDeep(this.shapes, id);
|
|
2408
|
-
}
|
|
2409
|
-
/** The layout's placeholder shapes (`p:sp` carrying a `p:ph`), in document order. */
|
|
2410
|
-
get placeholders() {
|
|
2411
|
-
const ctx = this.themeContext();
|
|
2412
|
-
const rels = this.relationships;
|
|
2413
|
-
return placeholderShapes(this.#spTree()).map((sp) => new Placeholder(sp, this.part, ctx, rels));
|
|
2414
|
-
}
|
|
2415
|
-
/**
|
|
2416
|
-
* The layout's *own* background (`p:cSld/p:bg`), decoded, or `null` when it defines
|
|
2417
|
-
* none. A layout usually authors one — an explicit fill (`p:bgPr`) or a
|
|
2418
|
-
* theme-indexed reference (`p:bgRef`). The slide's *effective* background is
|
|
2419
|
-
* `Slide.background`.
|
|
2420
|
-
*/
|
|
2421
|
-
get background() {
|
|
2422
|
-
const bg = backgroundElementOf(this.part.dom.documentElement);
|
|
2423
|
-
if (!bg) return null;
|
|
2424
|
-
const themePart = this.master?.theme?.part ?? null;
|
|
2425
|
-
return readSlideBackground(bg, "layout", this.themeContext(), this.relationships, themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
2426
|
-
}
|
|
2427
|
-
/**
|
|
2428
|
-
* The layout's colour/font context (walked layout → master → theme), resolved once
|
|
2429
|
-
* and cached. Backs each {@link Placeholder}'s text frame.
|
|
2430
|
-
*/
|
|
2431
|
-
themeContext() {
|
|
2432
|
-
return this.#themeContext ??= resolveLayoutColorContext(this.opc, this.partName);
|
|
2433
|
-
}
|
|
2434
|
-
#cSld() {
|
|
2435
|
-
return cSldOf(this.part.dom.documentElement);
|
|
2436
|
-
}
|
|
2437
|
-
#spTree() {
|
|
2438
|
-
return spTreeOf(this.part.dom.documentElement);
|
|
2439
|
-
}
|
|
2440
|
-
};
|
|
2441
|
-
//#endregion
|
|
2442
2490
|
//#region src/read/api/transition.ts
|
|
2443
2491
|
/**
|
|
2444
2492
|
* Typed read/edit model for a slide's `p:transition` (slide-show transition).
|
|
@@ -4299,7 +4347,7 @@ function importSlideRebind(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
|
4299
4347
|
targetRels.addWithId(rel.id, rel.type, rel.target, "External");
|
|
4300
4348
|
continue;
|
|
4301
4349
|
}
|
|
4302
|
-
const newTarget = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : owned);
|
|
4350
|
+
const newTarget = copyPart(ctx, sourceRels.resolveTarget(rel.id), isSharedByPageCopies(rel.type) ? void 0 : owned, true);
|
|
4303
4351
|
targetRels.addWithId(rel.id, rel.type, relativePartName(newPartName, newTarget));
|
|
4304
4352
|
}
|
|
4305
4353
|
if (carryGraphics) carryMasterGraphics(dest, ctx, slideRoot, newPartName, sourceSlide.partName, owned);
|
|
@@ -4487,7 +4535,6 @@ const PACKAGE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relat
|
|
|
4487
4535
|
const AUDIO_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio";
|
|
4488
4536
|
const VIDEO_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video";
|
|
4489
4537
|
const MS_MEDIA_REL = "http://schemas.microsoft.com/office/2007/relationships/media";
|
|
4490
|
-
const SLIDE_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slide+xml";
|
|
4491
4538
|
const CHART_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.drawingml.chart+xml";
|
|
4492
4539
|
const CHARTEX_CONTENT_TYPE = "application/vnd.ms-office.chartex+xml";
|
|
4493
4540
|
const CHART_STYLE_CONTENT_TYPE = "application/vnd.ms-office.chartstyle+xml";
|
|
@@ -5375,6 +5422,6 @@ var Presentation = class Presentation {
|
|
|
5375
5422
|
}
|
|
5376
5423
|
};
|
|
5377
5424
|
//#endregion
|
|
5378
|
-
export {
|
|
5425
|
+
export { readTagsForPart as a, Placeholder as c, Theme as d, OpcPackage as f, ContentTypes as h, Slide as i, SlideLayout as l, Part as m, readCoreProperties as n, NotesPlaceholder as o, Relationships as p, readCustomProperties as r, NotesSlide as s, Presentation as t, SlideMaster as u };
|
|
5379
5426
|
|
|
5380
|
-
//# sourceMappingURL=presentation-
|
|
5427
|
+
//# sourceMappingURL=presentation-Cp7vzlAw.js.map
|