@shbernal/ts-pptx 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/anim-presets-Qbgh8uut.js +55 -0
- package/dist/anim-presets-Qbgh8uut.js.map +1 -0
- package/dist/{autopage-D4kyVp11.js → autopage-DqZ37VpL.js} +11 -12
- package/dist/autopage-DqZ37VpL.js.map +1 -0
- package/dist/{binary-string-CKVAYI3M.js → binary-string-Ba8VM3b9.js} +3 -4
- package/dist/binary-string-Ba8VM3b9.js.map +1 -0
- package/dist/{body-insets-kVHmjGc6.js → body-insets-BDHymJvz.js} +7 -9
- package/dist/body-insets-BDHymJvz.js.map +1 -0
- package/dist/browser.d.ts +24 -16
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +19 -19
- package/dist/browser.js.map +1 -1
- package/dist/{image-size-DwDbakcl.js → chart-metadata-CwiTTcJq.js} +576 -538
- package/dist/chart-metadata-CwiTTcJq.js.map +1 -0
- package/dist/{clip-DHDlnHUq.d.ts → clip-ISfCMtWA.d.ts} +6 -10
- package/dist/clip-ISfCMtWA.d.ts.map +1 -0
- package/dist/{notes-BcT571R4.js → comments-CKIZYRbZ.js} +517 -389
- package/dist/comments-CKIZYRbZ.js.map +1 -0
- package/dist/{diagnostics-DiMmBU8P.d.ts → diagnostics-MoA4MBq5.d.ts} +52 -50
- package/dist/diagnostics-MoA4MBq5.d.ts.map +1 -0
- package/dist/{embedded-fonts-CI_YGW2d.d.ts → embedded-fonts-U7HIIhPO.d.ts} +4 -6
- package/dist/embedded-fonts-U7HIIhPO.d.ts.map +1 -0
- package/dist/{entry-compose-6hF9aTmT.d.ts → entry-compose-CiakSt-f.d.ts} +53 -74
- package/dist/entry-compose-CiakSt-f.d.ts.map +1 -0
- package/dist/{errors-Da2nXCJm.js → entry-errors-JMthgJ2g.js} +41 -25
- package/dist/entry-errors-JMthgJ2g.js.map +1 -0
- package/dist/{table-cell-margins-PDshga8T.js → enums-BP9wsb9Q.js} +42 -45
- package/dist/enums-BP9wsb9Q.js.map +1 -0
- package/dist/{enums-DQRpVOLh.d.ts → enums-PcXr3o3J.d.ts} +11 -12
- package/dist/{enums-DQRpVOLh.d.ts.map → enums-PcXr3o3J.d.ts.map} +1 -1
- package/dist/families.d.ts +24 -26
- package/dist/families.d.ts.map +1 -1
- package/dist/families.js +3 -4
- package/dist/{fit-CcXbg__l.js → fit-Bpmzep6O.js} +60 -52
- package/dist/fit-Bpmzep6O.js.map +1 -0
- package/dist/{font-metrics-CBGmhLo0.d.ts → font-metrics-DTppw4t3.d.ts} +61 -37
- package/dist/font-metrics-DTppw4t3.d.ts.map +1 -0
- package/dist/{grid-9B2w5EaY.js → grid-DUIMxjfl.js} +23 -26
- package/dist/grid-DUIMxjfl.js.map +1 -0
- package/dist/{html-dom-DvJmuHt2.js → html-dom-LZCgntnJ.js} +42 -43
- package/dist/html-dom-LZCgntnJ.js.map +1 -0
- package/dist/html.d.ts +53 -9
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +12 -13
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +21 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -15
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +14 -16
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +18 -19
- package/dist/inspect.js.map +1 -1
- package/dist/{text-anchor-BgLPbwbL.js → lock-attrs-CSQnFDHP.js} +62 -12
- package/dist/lock-attrs-CSQnFDHP.js.map +1 -0
- package/dist/math.d.ts +2 -4
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +12 -13
- package/dist/math.js.map +1 -1
- package/dist/measure.d.ts +23 -12
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +3 -3
- package/dist/node.d.ts +21 -13
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +12 -12
- package/dist/node.js.map +1 -1
- package/dist/{options-internal-WqgMf720.js → options-internal-BWgmLPdx.js} +4 -5
- package/dist/options-internal-BWgmLPdx.js.map +1 -0
- package/dist/{package-KoEgVwII.d.ts → package-CANBWuSm.d.ts} +4 -12
- package/dist/package-CANBWuSm.d.ts.map +1 -0
- package/dist/{playbtn-CUqQdoDf.js → playbtn-BQZFJir9.js} +4 -5
- package/dist/playbtn-BQZFJir9.js.map +1 -0
- package/dist/{pres-BA9_lJbY.d.ts → pres-BkEkUAEM.d.ts} +383 -230
- package/dist/pres-BkEkUAEM.d.ts.map +1 -0
- package/dist/{presentation-Cyl74rq2.js → presentation-4Ggq0_Xa.js} +408 -288
- package/dist/presentation-4Ggq0_Xa.js.map +1 -0
- package/dist/{presentation-types-B3VQH4Bz.d.ts → presentation-types-BgnZR-0a.d.ts} +1104 -457
- package/dist/presentation-types-BgnZR-0a.d.ts.map +1 -0
- package/dist/read.d.ts +18 -13
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +4 -4
- package/dist/script.d.ts +116 -65
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +2697 -777
- package/dist/script.js.map +1 -1
- package/dist/{shapes-BcyFdZpp.js → shapes-CAPOjU0Q.js} +1556 -487
- package/dist/shapes-CAPOjU0Q.js.map +1 -0
- package/dist/{slide-BqPtyKmb.d.ts → slide-BmT4kMoO.d.ts} +94 -86
- package/dist/slide-BmT4kMoO.d.ts.map +1 -0
- package/dist/{slide-size-BalTjFuv.js → slide-size-SkScz1ig.js} +2 -3
- package/dist/{slide-size-BalTjFuv.js.map → slide-size-SkScz1ig.js.map} +1 -1
- package/dist/{table-dom-CVbvUfgh.js → table-dom-BYga4FYY.js} +3 -4
- package/dist/{table-dom-CVbvUfgh.js.map → table-dom-BYga4FYY.js.map} +1 -1
- package/dist/{text-BvFFMMFQ.js → text-Dz0ArGIp.js} +365 -318
- package/dist/text-Dz0ArGIp.js.map +1 -0
- package/dist/{transition-speed-kUEWDWTx.js → transition-speed-CNJ0fSpQ.js} +2 -3
- package/dist/{transition-speed-kUEWDWTx.js.map → transition-speed-CNJ0fSpQ.js.map} +1 -1
- package/dist/{units-internal-DFcUYjh3.js → units-internal-BR3a4cIi.js} +26 -29
- package/dist/units-internal-BR3a4cIi.js.map +1 -0
- package/dist/{zip-CYZ0F6BV.js → zip-7JTh61LX.js} +8 -10
- package/dist/zip-7JTh61LX.js.map +1 -0
- package/dist/{zip-CTll_dr0.d.ts → zip-uUjs9Cqx.d.ts} +4 -6
- package/dist/zip-uUjs9Cqx.d.ts.map +1 -0
- package/dist/zip.d.ts +3 -3
- package/dist/zip.js +3 -3
- package/dist/{zoom-BoNyOlPm.js → zoom-CSSeOwM8.js} +1153 -583
- package/dist/zoom-CSSeOwM8.js.map +1 -0
- package/package.json +20 -21
- package/skills/{ts-pptx-upstream → pptx-ts-upstream}/SKILL.md +27 -74
- package/dist/autopage-D4kyVp11.js.map +0 -1
- package/dist/binary-string-CKVAYI3M.js.map +0 -1
- package/dist/body-insets-kVHmjGc6.js.map +0 -1
- package/dist/clip-DHDlnHUq.d.ts.map +0 -1
- package/dist/diagnostics-DiMmBU8P.d.ts.map +0 -1
- package/dist/embedded-fonts-CI_YGW2d.d.ts.map +0 -1
- package/dist/entry-compose-6hF9aTmT.d.ts.map +0 -1
- package/dist/errors-Da2nXCJm.js.map +0 -1
- package/dist/fit-CcXbg__l.js.map +0 -1
- package/dist/font-metrics-CBGmhLo0.d.ts.map +0 -1
- package/dist/grid-9B2w5EaY.js.map +0 -1
- package/dist/html-dom-DvJmuHt2.js.map +0 -1
- package/dist/image-size-DwDbakcl.js.map +0 -1
- package/dist/notes-BcT571R4.js.map +0 -1
- package/dist/options-internal-WqgMf720.js.map +0 -1
- package/dist/package-KoEgVwII.d.ts.map +0 -1
- package/dist/playbtn-CUqQdoDf.js.map +0 -1
- package/dist/pres-BA9_lJbY.d.ts.map +0 -1
- package/dist/presentation-Cyl74rq2.js.map +0 -1
- package/dist/presentation-types-B3VQH4Bz.d.ts.map +0 -1
- package/dist/shapes-BcyFdZpp.js.map +0 -1
- package/dist/slide-BqPtyKmb.d.ts.map +0 -1
- package/dist/table-cell-margins-PDshga8T.js.map +0 -1
- package/dist/text-BvFFMMFQ.js.map +0 -1
- package/dist/text-anchor-BgLPbwbL.js.map +0 -1
- package/dist/units-internal-DFcUYjh3.js.map +0 -1
- package/dist/zip-CTll_dr0.d.ts.map +0 -1
- package/dist/zip-CYZ0F6BV.js.map +0 -1
- package/dist/zoom-BoNyOlPm.js.map +0 -1
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { F as DEF_PRES_LAYOUT_NAME, J as SLDNUM_PLACEHOLDER_TEXT, P as DEF_PRES_LAYOUT, R as DEF_SLIDE_MARGIN_IN, U as LEVEL_MARGINS_EMU, V as LAYOUT_IDX_SERIES_BASE, X as MIN_SLIDE_MASTER_ID, a as convertRotationDegrees, n as clampRangedInput, q as SLDNUMFLDID, s as getSmartParseNumber, u as mapStated, v as resolveInsetsEmu } from "./units-internal-
|
|
3
|
-
import { A as inchesToEmu, D as emuToInches, F as EMU_PER_INCH, T as STANDARD_LAYOUTS } from "./
|
|
4
|
-
import { $
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { A as
|
|
8
|
-
import { n as setOrClear, t as pickDefined } from "./options-internal-
|
|
9
|
-
import { b as isFontCollection, g as FontMetricsRegistry, t as applyMeasuredFit, y as parseFontMetrics } from "./fit-
|
|
10
|
-
import { t as ZipWriter } from "./zip-
|
|
11
|
-
import { t as
|
|
12
|
-
import {
|
|
13
|
-
|
|
1
|
+
import { d as warnOnce, i as MediaError, n as InternalError, r as InvalidOptionError, u as warn } from "./entry-errors-JMthgJ2g.js";
|
|
2
|
+
import { F as DEF_PRES_LAYOUT_NAME, J as SLDNUM_PLACEHOLDER_TEXT, P as DEF_PRES_LAYOUT, R as DEF_SLIDE_MARGIN_IN, S as transparencyToAlpha, U as LEVEL_MARGINS_EMU, V as LAYOUT_IDX_SERIES_BASE, X as MIN_SLIDE_MASTER_ID, a as convertRotationDegrees, n as clampRangedInput, q as SLDNUMFLDID, s as getSmartParseNumber, u as mapStated, v as resolveInsetsEmu } from "./units-internal-BR3a4cIi.js";
|
|
3
|
+
import { A as inchesToEmu, D as emuToInches, F as EMU_PER_INCH, T as STANDARD_LAYOUTS, o as PlaceholderType } from "./enums-BP9wsb9Q.js";
|
|
4
|
+
import { $ as SLIDE_LAYOUT_CONTENT_TYPE, C as COMMENT_AUTHORS_REL, Ct as DEFAULT_COLOR_MAP, D as CUSTOM_PROPS_REL, E as CUSTOM_PROPS_CONTENT_TYPE, F as MS_MEDIA_REL, Gt as TEXT_CAPS_TYPES, J as PRES_PROPS_CONTENT_TYPE, M as IMAGE_REL, O as EXTENDED_PROPS_CONTENT_TYPE, Q as SLIDE_CONTENT_TYPE, T as CORE_PROPS_REL, Tt as EMBEDDED_FONT_SLOTS, U as OFFICE_DOCUMENT_REL, V as NOTES_SLIDE_REL, X as RELATIONSHIPS_CONTENT_TYPE, Xt as TEXT_UNDERLINE_TYPES, Y as PRES_PROPS_REL, Zt as TEXT_VERTICAL, _ as CHART_REL, an as checkEnumOrWarn, at as TABLE_STYLES_REL, cn as xsdBoolIfTrue, ct as VIDEO_REL, d as AUDIO_REL, dn as stripHash, et as SLIDE_LAYOUT_REL, fn as bytesToBase64, ft as OOXML_NS, gn as toMediaDataUri, in as checkEnumOrThrow, it as TABLE_STYLES_CONTENT_TYPE, k as EXTENDED_PROPS_REL, ln as isHexColor, lt as VIEW_PROPS_CONTENT_TYPE, nn as TRANSITION_TYPES, nt as SLIDE_MASTER_REL, ot as THEME_CONTENT_TYPE, p as CHARTEX_REL, pn as decodeBase64ToBytes, pt as PML_ROOT_NS, q as PRESENTATION_MAIN_CONTENT_TYPE, qt as TEXT_SHAPE_TYPES, rn as TRANSITION_VARIANT_ATTRIBUTES, rt as SLIDE_REL, s as textAlignToken, sn as xsdBool, st as THEME_REL, tt as SLIDE_MASTER_CONTENT_TYPE, ut as VIEW_PROPS_REL, w as CORE_PROPS_CONTENT_TYPE, x as COMMENTS_REL, z as NOTES_MASTER_REL } from "./chart-metadata-CwiTTcJq.js";
|
|
5
|
+
import { c as audioExtensionForSubtype, d as imageContentType, l as avContentType, n as GROUP_SHAPE_LOCK_ATTRS, u as dataUriMediaType } from "./lock-attrs-CSQnFDHP.js";
|
|
6
|
+
import { $ as buAutoNumEl, A as cNvPrEl, C as relsPath, Ct as resolveFillKind, D as targetFromPptSubpart, E as targetBetweenPptSubparts, Et as genXmlImageCropRect, Ft as voidEl, H as prstGeomRect, Ht as isPreencodedPath, It as XML_DECL, J as clampFontSizeSz, Kt as encodeXmlAttrValue, L as spTreeEl, Lt as bracedGuid, Mt as el, Nt as elOrVoid, O as targetFromPresentation, Ot as createColorElement, P as grpXfrmEl, Pt as raw, Q as clampParaMarginInchesEmu, Rt as getDuplicateObjectNames, S as overrideName, T as slidePath, Tt as STRETCH_FILL_RECT, Vt as isHyperlinkRel, Wt as preencodedPath, X as clampParaIndentInchesEmu, Y as clampParaIndentEmu, Z as clampParaMarginEmu, _ as PRESENTATION_PATH, _t as flattenEmbeddedFaces, a as makeXmlNotesMaster, b as fontPath, bt as genXmlColorSelection, d as themeFontDefRPr, dt as withLineDefaults, et as buCharEl, f as externalHyperlinkRel, g as NOTES_MASTER_PATH, gt as FONT_REL_TYPE, h as relationshipsPart, ht as FONT_DATA_EXTENSION, i as buildNotesSlideRels, jt as rejectEmptyColor, mt as FONT_DATA_CONTENT_TYPE, p as relationshipEl, q as underlineProps, s as makeXmlNotesSlide, u as lvlPPr, v as SLIDE_MASTER_PATH, vt as serializeEmbeddedFontLst, w as slideLayoutPath, wt as solidPaint, x as notesSlidePath, y as commentPath, yt as fillNamesPaint, zt as getNewRelId } from "./comments-CKIZYRbZ.js";
|
|
7
|
+
import { A as statedPlaceholderBinding, B as composeFamilies, C as msMediaRid, D as checkPlaceholderBinding, E as placeholderOptionsForSlide, F as shapeIdCount, H as requirePresentationAuthor, I as addChildDefinition, L as isGroupableObject, M as collectSlideShapeIds, N as renderedSlideObjects, O as findLayoutPlaceholder, P as resolveObjectNameToId, R as warnUnauthoredChild, S as alternateContentEl, T as addTextDefinition, U as pushMediaRel, V as familyMethodUnavailable, W as resolveImageSource, _ as makeXmlCore, a as mediaFamily, b as resolveImageExtent, c as commentsFamily, d as textFamily, f as shapeFamily, g as makeXmlTableStyles, h as groupFamily, i as model3dFamily, j as resolveTextAnchor, k as layoutPlaceholderName, l as chartFamily, m as imageFamily, n as tableFamily, o as measureFamily, p as notesFamily, r as oleFamily, s as connectorFamily, t as zoomFamily, u as animationsFamily, v as chartPartNames, w as previewRid, x as genXmlObjectLock, y as FMT_SCHEME_XML, z as SLIDE_METHOD_FAMILIES } from "./zoom-CSSeOwM8.js";
|
|
8
|
+
import { n as setOrClear, t as pickDefined } from "./options-internal-BWgmLPdx.js";
|
|
9
|
+
import { b as isFontCollection, g as FontMetricsRegistry, t as applyMeasuredFit, y as parseFontMetrics } from "./fit-Bpmzep6O.js";
|
|
10
|
+
import { t as ZipWriter } from "./zip-7JTh61LX.js";
|
|
11
|
+
import { t as ANIM_PRESET_IDENTITY } from "./anim-presets-Qbgh8uut.js";
|
|
12
|
+
import { t as transitionSpeedForDuration } from "./transition-speed-CNJ0fSpQ.js";
|
|
13
|
+
import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-SkScz1ig.js";
|
|
14
|
+
// src/gen/define/background.ts
|
|
14
15
|
/**
|
|
15
16
|
* pptx-ts: Background Definition
|
|
16
17
|
*
|
|
@@ -21,7 +22,7 @@ import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-B
|
|
|
21
22
|
* Adds a background image or color to a slide definition.
|
|
22
23
|
*
|
|
23
24
|
* Only an image background does anything here: it is the one that needs a media relationship.
|
|
24
|
-
* A colour
|
|
25
|
+
* A colour (object or the bare-string shorthand) carries no rel and is emitted straight from
|
|
25
26
|
* `slide.background` at serialize time.
|
|
26
27
|
*
|
|
27
28
|
* The rel id and the part name come from the shared allocators. This used to take the id as
|
|
@@ -49,11 +50,12 @@ import { n as presentationFormatLabel, r as slideSizeType } from "./slide-size-B
|
|
|
49
50
|
* @param {SlideLayoutInternal} target - slide or layout that the background is set on
|
|
50
51
|
*/
|
|
51
52
|
function addBackgroundDefinition(props, target) {
|
|
52
|
-
|
|
53
|
+
const image = backgroundImage(props);
|
|
54
|
+
if (!image) {
|
|
53
55
|
delete target._bkgdImgRid;
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
56
|
-
const source = resolveImageSource(
|
|
58
|
+
const source = resolveImageSource(image);
|
|
57
59
|
if (typeof source === "string") {
|
|
58
60
|
warn("background/missing-base64-header", "background `data` value lacks a base64 header (ex: 'image/png;base64,...'); ignoring the background image.");
|
|
59
61
|
delete target._bkgdImgRid;
|
|
@@ -87,8 +89,24 @@ function addBackgroundDefinition(props, target) {
|
|
|
87
89
|
});
|
|
88
90
|
target._bkgdImgRid = rId;
|
|
89
91
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
/**
|
|
93
|
+
* The picture a background names, in either of its two spellings: `{ data }` / `{ path }` from
|
|
94
|
+
* `DataOrPathProps`, or `{ image: { data, crop } }` from `ShapeFillProps`, which `BackgroundProps`
|
|
95
|
+
* also extends. The second used to type-check and register nothing, so the background was written
|
|
96
|
+
* as `<a:noFill/>` and its crop was lost with it.
|
|
97
|
+
*
|
|
98
|
+
* The top-level spelling wins, as it always has. The `image` one follows the fill rule that an
|
|
99
|
+
* explicit `type` other than `'image'` wins over a sub-object.
|
|
100
|
+
* @param props - the background option
|
|
101
|
+
* @returns the picture's source, or `null` when the background names none
|
|
102
|
+
*/
|
|
103
|
+
function backgroundImage(props) {
|
|
104
|
+
if (!props || typeof props !== "object") return null;
|
|
105
|
+
if (props.path || props.data) return props;
|
|
106
|
+
const image = props.image;
|
|
107
|
+
return resolveFillKind(props) === "image" && image && (image.path || image.data) ? image : null;
|
|
108
|
+
}
|
|
109
|
+
// src/slide.ts
|
|
92
110
|
/**
|
|
93
111
|
* Project one authored render-object onto the public {@link SlideObjectInfo}, recursing into a
|
|
94
112
|
* group's children.
|
|
@@ -139,7 +157,7 @@ var SlideBuilder = class {
|
|
|
139
157
|
constructor(params) {
|
|
140
158
|
this.addSlide = params.addSlide;
|
|
141
159
|
this.getSlide = params.getSlide;
|
|
142
|
-
this.getSections = params.getSections
|
|
160
|
+
this.getSections = params.getSections;
|
|
143
161
|
this._name = `Slide ${params.slideNumber}`;
|
|
144
162
|
this._presLayout = params.presLayout;
|
|
145
163
|
this._rId = params.slideRId;
|
|
@@ -148,7 +166,7 @@ var SlideBuilder = class {
|
|
|
148
166
|
this._relsMedia = [];
|
|
149
167
|
this._setSlideNum = params.setSlideNum;
|
|
150
168
|
this._slideId = params.slideId;
|
|
151
|
-
this._slideLayout = params.slideLayout
|
|
169
|
+
this._slideLayout = params.slideLayout;
|
|
152
170
|
this._slideNum = params.slideNumber;
|
|
153
171
|
this._slideObjects = [];
|
|
154
172
|
this._childAuthors = params.childAuthors;
|
|
@@ -157,7 +175,7 @@ var SlideBuilder = class {
|
|
|
157
175
|
* `defineSlideMaster` and `addNewSlide.slideNumber` will add {slideNumber} to `this.masterSlide` and `this.slideLayouts`
|
|
158
176
|
* so, lastly, add to the Slide now.
|
|
159
177
|
*/
|
|
160
|
-
this._slideNumberProps =
|
|
178
|
+
this._slideNumberProps = params.slideLayout._slideNumberProps ?? null;
|
|
161
179
|
}
|
|
162
180
|
/**
|
|
163
181
|
* Put this presentation's family methods on the slide.
|
|
@@ -269,8 +287,7 @@ var SlideBuilder = class {
|
|
|
269
287
|
return emuToInches(this._presLayout.height);
|
|
270
288
|
}
|
|
271
289
|
};
|
|
272
|
-
|
|
273
|
-
//#region src/font-source.ts
|
|
290
|
+
// src/font-source.ts
|
|
274
291
|
/**
|
|
275
292
|
* pptx-ts: getting font bytes out of whatever the caller handed over.
|
|
276
293
|
*
|
|
@@ -310,8 +327,11 @@ async function resolveFontBytes(source, runtime, opts, label) {
|
|
|
310
327
|
}
|
|
311
328
|
throw new InvalidOptionError("font/missing-source", `${label} must be a font path, raw bytes, or base64 data`);
|
|
312
329
|
}
|
|
313
|
-
|
|
314
|
-
|
|
330
|
+
// src/gen/define/master.ts
|
|
331
|
+
/** Whether a definition's `type` is one of the six friendly ones the `p:ph` writer maps itself. */
|
|
332
|
+
function isFriendlyPlaceholderType(type) {
|
|
333
|
+
return Object.hasOwn(PlaceholderType, type) || Object.values(PlaceholderType).includes(type);
|
|
334
|
+
}
|
|
315
335
|
/**
|
|
316
336
|
* Transforms a slide definition to a slide object that is then passed to the XML transformation process.
|
|
317
337
|
* @param {SlideMasterProps} props - slide definition
|
|
@@ -323,40 +343,59 @@ function createSlideMaster(props, target, children) {
|
|
|
323
343
|
const tgt = target;
|
|
324
344
|
if (addChildDefinition(tgt, object, children)) {} else if ("placeholder" in object) {
|
|
325
345
|
const placeholder = object.placeholder;
|
|
326
|
-
const { name, type, ...rawPlaceholderOptions } = placeholder.options;
|
|
346
|
+
const { name, type, idx: statedIdx, ...rawPlaceholderOptions } = placeholder.options;
|
|
347
|
+
checkPlaceholderBinding({
|
|
348
|
+
...statedIdx === void 0 ? {} : { idx: statedIdx },
|
|
349
|
+
...isFriendlyPlaceholderType(type) ? {} : { type }
|
|
350
|
+
}, "defineSlideMaster");
|
|
327
351
|
const placeholderOptions = rawPlaceholderOptions;
|
|
328
352
|
placeholderOptions.placeholder = name;
|
|
329
353
|
placeholderOptions._placeholderType = type;
|
|
330
|
-
placeholderOptions._placeholderIdx = 100 + idx;
|
|
354
|
+
placeholderOptions._placeholderIdx = statedIdx ?? 100 + idx;
|
|
331
355
|
const placeholderRun = placeholder.text === void 0 ? {} : { text: placeholder.text };
|
|
332
356
|
addTextDefinition(tgt, [placeholderRun], placeholderOptions, true);
|
|
333
357
|
} else warnUnauthoredChild(object, "defineSlideMaster()");
|
|
334
358
|
});
|
|
335
359
|
if (props.slideNumber && typeof props.slideNumber === "object") target._slideNumberProps = props.slideNumber;
|
|
336
360
|
}
|
|
337
|
-
|
|
338
|
-
//#region src/gen/define/placeholder.ts
|
|
361
|
+
// src/gen/define/placeholder.ts
|
|
339
362
|
/**
|
|
340
363
|
* Adds placeholder objects to slide
|
|
341
364
|
* @param {PresSlideInternal} slide - slide object containing layouts
|
|
342
365
|
*/
|
|
343
366
|
function addPlaceholdersToSlideLayouts(slide) {
|
|
344
367
|
if (!slide._slideLayout) return;
|
|
345
|
-
|
|
368
|
+
slide._slideLayout._slideObjects.forEach((slideLayoutObj) => {
|
|
346
369
|
if (slideLayoutObj._type === "placeholder") {
|
|
347
370
|
const slideLayoutOptions = slideLayoutObj.options || {};
|
|
348
|
-
|
|
371
|
+
const fills = (slideObj) => {
|
|
372
|
+
if (layoutPlaceholderName(slideObj.options?.placeholder) === slideLayoutOptions.placeholder) return true;
|
|
373
|
+
const binding = statedPlaceholderBinding(slideObj.options?.placeholder);
|
|
374
|
+
if (!binding || slideLayoutOptions._placeholderIdx === void 0) return false;
|
|
375
|
+
if (binding.idx !== void 0) return binding.idx === slideLayoutOptions._placeholderIdx;
|
|
376
|
+
return slideLayoutOptions._placeholderIdx === 0 && binding.type === slideLayoutOptions._placeholderType;
|
|
377
|
+
};
|
|
378
|
+
if (!slide._slideObjects.some(fills)) {
|
|
379
|
+
addTextDefinition(slide, [{ text: "" }], placeholderOptionsForSlide(slideLayoutOptions), true);
|
|
380
|
+
const seeded = slide._slideObjects.at(-1);
|
|
381
|
+
if (seeded) {
|
|
382
|
+
seeded._seeded = true;
|
|
383
|
+
seeded.text = [];
|
|
384
|
+
const options = seeded.options ?? (seeded.options = {});
|
|
385
|
+
const line = options.line;
|
|
386
|
+
options.line = line?.type === "none" ? { type: "none" } : line && fillNamesPaint(line) ? withLineDefaults(line, "line.color") : { type: "inherit" };
|
|
387
|
+
}
|
|
388
|
+
}
|
|
349
389
|
}
|
|
350
390
|
});
|
|
351
391
|
}
|
|
352
|
-
|
|
353
|
-
//#region src/media/placeholders.ts
|
|
392
|
+
// src/media/placeholders.ts
|
|
354
393
|
/**
|
|
355
394
|
* The base64 placeholder images the library falls back to when a real asset is unavailable.
|
|
356
395
|
*
|
|
357
396
|
* Their own module because of where the bundler puts them, not because they group naturally.
|
|
358
397
|
* The chunker splits by module, so a constant is pulled into every chunk that reaches the module
|
|
359
|
-
* declaring it
|
|
398
|
+
* declaring it, and these lived in `constants-internal.ts` beside `XML_DECL`, `DEF_FONT_SIZE`
|
|
360
399
|
* and the rest of the vocabulary all of `src/gen/` imports. The rule that follows: a payload
|
|
361
400
|
* measured in kilobytes does not share a module with vocabulary, however tidy that grouping reads.
|
|
362
401
|
*
|
|
@@ -368,8 +407,7 @@ function addPlaceholdersToSlideLayouts(slide) {
|
|
|
368
407
|
const IMG_BROKEN = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAB3CAYAAAD1oOVhAAAGAUlEQVR4Xu2dT0xcRRzHf7tAYSsc0EBSIq2xEg8mtTGebVzEqOVIolz0siRE4gGTStqKwdpWsXoyGhMuyAVJOHBgqyvLNgonDkabeCBYW/8kTUr0wsJC+Wfm0bfuvn37Znbem9mR9303mJnf/Pb7ed95M7PDI5JIJPYJV5EC7e3t1N/fT62trdqViQCIu+bVgpIHEo/Hqbe3V/sdYVKHyWSSZmZm8ilVA0oeyNjYmEnaVC2Xvr6+qg5fAOJAz4DU1dURGzFSqZRVqtMpAFIGyMjICC0vL9PExIRWKADiAYTNshYWFrRCARAOEFZcCKWtrY0GBgaUTYkBRACIE4rKZwqACALR5RQAqQCIDqcASIVAVDsFQCSAqHQKgEgCUeUUAPEBRIVTAMQnEBvK5OQkbW9vk991CoAEAMQJxc86BUACAhKUUwAkQCBBOAVAAgbi1ykAogCIH6cAiCIgsk4BEIVAZJwCIIqBVLqiBxANQFgXS0tLND4+zl08AogmIG5OSSQS1gGKwgtANAIRcQqAaAbCe6YASBWA2E6xDyeyDUl7+AKQMkDYYevm5mZHabA/Li4uUiaTsYLau8QA4gLE/hU7wajyYtv1hReDAiAOxQcHBymbzark4BkbQKom/X8dp9Npmpqasn4BIAYAYSnYp+4BBEAMUcCwNOCQsAKZnp62NtQOw8WmwT09PUo+ijaHsOMx7GppaaH6+nolH0Z10K2tLVpdXbW6UfV3mNqBdHd3U1NTk2rtlMRfW1uj2dlZAFGirkRQAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAFGprkRsAJEQTWUTAGHqrm8caPzQ0WC1logbeiC7X3xJm0PvUmRzh45cuki1588FAmVn9BO6P3yF9utrqGH0MtW82S8UN9RA9v/4k7InjhcJFTs/TLVXLwmJV67S7vD7tHF5pKi46fYdosdOcOOGG8j1OcqefbFEJD9Q3GCwDhqT31HklS4A8VRgfYM2Op6k3bt/BQJl58J7lPvwg5JYNccepaMry0LPqFA7hCm39+NNyp2J0172b19QysGINj5CsRtpij57musOViH0QPJQXn6J9u7dlYJSFkbrMYolrwvDAJAC+WWdEpQz7FTgECeUCpzi6YxvvqXoM6eEhqnCSgDikEzUKUE7Aw7xuHctKB5OYU3dZlNR9syQdAaAcAYTC0pXF+39c09o2Ik+3EqxVKqiB7hbYAxZkk4pbBaEM+AQofv+wTrFwylBOQNABIGwavdfe4O2pg5elO+86l99nY58/VUF0byrYsjiSFluNlXYrOHcBar7+EogUADEQ0YRGHbzoKAASBkg2+9cpM1rV0tK2QOcXW7bLEFAARAXIF4w2DrDWoeUWaf4hQIgDiA8GPZ2iNfi0Q8UACkAIgrDbrJ385eDxaPLLrEsFAB5oG6lMPJQPLZZZKAACBGVhcG2Q+bmuLu2nk55e4jqPv1IeEoceiBeX7s2zCa5MAqdstl91vfXwaEGsv/rb5TtOFk6tWXOuJGh6KmnhO9sayrMninPx103JBtXblHkice58cINZP4Hyr5wpkgkdiChEmc4FWazLzenNKa/p0jncwDiqcD6BuWePk07t1asatZGoYQzSqA4nFJ7soNiP/+EUyfc25GI2GG53dHPrKo1g/1Cw4pIXLrzO+1c+/wg7tBbFDle/EbQcjFCPWQJCau5EoBoFpzXHYDwFNJcDiCaBed1ByA8hTSXA4hmwXndAQhPIc3lAKJZcF53AMJTSHM5gGgWnNcdgPAU0lwOIJoF53UHIDyFNJcfSiCdnZ0Ui8U0SxlMd7lcjubn561gh+Y1scFIU/0o/3sgeLO12E2k7UXKYumgFoAYdg8ACIAYpoBh6cAhAGKYAoalA4cAiGEKGJYOHAIghilgWDpwCIAYpoBh6cAhAGKYAoalA4cAiGEKGJYOHAIghilgWDpwCIAYpoBh6ZQ4JB6PKzviYthnNy4d9h+1M5mMlVckkUjsG5dhiBMCEMPg/wuOfrZZ/RSywQAAAABJRU5ErkJggg==";
|
|
369
408
|
/** 1x1 transparent PNG, standing in for an SVG the runtime could not rasterize. */
|
|
370
409
|
const IMG_SVG_PLACEHOLDER = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==";
|
|
371
|
-
|
|
372
|
-
//#region src/gen/media.ts
|
|
410
|
+
// src/gen/media.ts
|
|
373
411
|
/**
|
|
374
412
|
* pptx-ts: Media Methods
|
|
375
413
|
*/
|
|
@@ -398,14 +436,14 @@ async function writeSvgPreview(rel, runtime, onMediaError) {
|
|
|
398
436
|
* Encode Image/Audio/Video into base64
|
|
399
437
|
* @param {PresSlideInternal | SlideLayoutInternal | SlideMasterInternal} layout - slide, layout or master
|
|
400
438
|
* @param {RuntimeAdapter} runtime - runtime adapter (Node/browser media loader)
|
|
401
|
-
* @param {'throw' | 'placeholder'} onMediaError - failure policy: reject the export
|
|
439
|
+
* @param {'throw' | 'placeholder'} onMediaError - failure policy: reject the export or substitute a placeholder and warn
|
|
402
440
|
* @return {Promise} promise
|
|
403
441
|
*/
|
|
404
|
-
function encodeSlideMediaRels(layout, runtime, onMediaError
|
|
442
|
+
function encodeSlideMediaRels(layout, runtime, onMediaError) {
|
|
405
443
|
const imageProms = [];
|
|
406
444
|
const defaultCoverRels = layout._relsMedia.filter((rel) => rel.isDefaultCover && !rel.data);
|
|
407
445
|
if (defaultCoverRels.length > 0) imageProms.push((async () => {
|
|
408
|
-
const { IMG_PLAYBTN } = await import("./playbtn-
|
|
446
|
+
const { IMG_PLAYBTN } = await import("./playbtn-BQZFJir9.js");
|
|
409
447
|
for (const rel of defaultCoverRels) rel.data = IMG_PLAYBTN;
|
|
410
448
|
return "done";
|
|
411
449
|
})());
|
|
@@ -445,24 +483,23 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
|
|
|
445
483
|
});
|
|
446
484
|
return imageProms;
|
|
447
485
|
}
|
|
448
|
-
|
|
449
|
-
//#region src/gen/prepare.ts
|
|
486
|
+
// src/gen/prepare.ts
|
|
450
487
|
/**
|
|
451
488
|
* pptx-ts: the pre-serialization pass
|
|
452
489
|
*
|
|
453
490
|
* Everything that must happen to authored slide state *before* any XML is built. Two callers
|
|
454
491
|
* reach serialization and both need it:
|
|
455
492
|
*
|
|
456
|
-
* - `package/assemble.ts
|
|
493
|
+
* - `package/assemble.ts`: the normal `write`/`stream`/`writeFile` path, building a
|
|
457
494
|
* complete `.pptx`.
|
|
458
|
-
* - `PresentationCore.extractSlides()
|
|
495
|
+
* - `PresentationCore.extractSlides()`: serializes slide bodies only, for splicing into
|
|
459
496
|
* an already-loaded deck via `Presentation.appendSlides()` (`pptx-ts/read`).
|
|
460
497
|
*
|
|
461
498
|
* The second used to reimplement the first inline, kept in step by a comment that said
|
|
462
499
|
* "exactly as ... does". Nothing failed when they drifted: `extractSlides` would simply emit
|
|
463
500
|
* slides that did not match what a normal write produces, and no test compares the two.
|
|
464
501
|
*
|
|
465
|
-
*
|
|
502
|
+
* Separate steps rather than one combined pass, because the package path has work of its own that
|
|
466
503
|
* must land between the last two: it de-duplicates identical media across the deck (which needs
|
|
467
504
|
* the encoded bytes {@link encodeMediaForTargets} produces) and assigns package-unique chart part
|
|
468
505
|
* filenames, both before any text is measured. Exposing the steps lets `assemble` keep that work
|
|
@@ -470,7 +507,9 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
|
|
|
470
507
|
*
|
|
471
508
|
* The order is load-bearing:
|
|
472
509
|
*
|
|
473
|
-
*
|
|
510
|
+
* 0. **Register transition sounds**, before media encoding loads their bytes and before
|
|
511
|
+
* backfill, so a slide's rel ids come out the same on both paths.
|
|
512
|
+
* 1. **Backfill placeholders**, so a slide carries its layout's placeholder objects before
|
|
474
513
|
* anything reads its media. A seeded placeholder can register media of its own, an image fill,
|
|
475
514
|
* and when backfill ran after encoding that image was never loaded and its rel named a part
|
|
476
515
|
* that was never written.
|
|
@@ -482,7 +521,7 @@ function encodeSlideMediaRels(layout, runtime, onMediaError = "throw") {
|
|
|
482
521
|
*
|
|
483
522
|
* The functions take different target sets on purpose: media lives on layouts and the master as
|
|
484
523
|
* well as on slides, while placeholder backfill and measured fit apply only to slides.
|
|
485
|
-
* `extractSlides` passes only its slides to
|
|
524
|
+
* `extractSlides` passes only its slides to every step, because it emits no layout or master
|
|
486
525
|
* parts.
|
|
487
526
|
*/
|
|
488
527
|
/**
|
|
@@ -518,10 +557,19 @@ function requireSlideLinksInDeck(slides, layouts = [], master) {
|
|
|
518
557
|
part: master
|
|
519
558
|
}] : []
|
|
520
559
|
];
|
|
560
|
+
const refuse = (label, target) => {
|
|
561
|
+
throw new InvalidOptionError("slide/link-past-last-slide", `${label} links to slide ${String(target)}, but the deck has ${slides.length} slide${slides.length === 1 ? "" : "s"}.`);
|
|
562
|
+
};
|
|
521
563
|
for (const { label, part } of sources) for (const rel of part._rels) {
|
|
522
564
|
if (!isHyperlinkRel(rel) || rel.data !== "slide") continue;
|
|
523
|
-
if (
|
|
524
|
-
|
|
565
|
+
if (Number(rel.Target) > slides.length) refuse(label, rel.Target);
|
|
566
|
+
}
|
|
567
|
+
for (const slide of slides) for (const obj of slide._slideObjects) {
|
|
568
|
+
if (obj._type !== "notes") continue;
|
|
569
|
+
for (const run of obj.text ?? []) {
|
|
570
|
+
const target = run.options?.hyperlink?.slide;
|
|
571
|
+
if (target !== void 0 && target > slides.length) refuse(`The notes of slide ${slide._slideNum}`, target);
|
|
572
|
+
}
|
|
525
573
|
}
|
|
526
574
|
}
|
|
527
575
|
/**
|
|
@@ -536,6 +584,61 @@ async function encodeMediaForTargets(targets, runtime, onMediaError) {
|
|
|
536
584
|
await Promise.all(promises);
|
|
537
585
|
}
|
|
538
586
|
/**
|
|
587
|
+
* Each slide's registered transition sound.
|
|
588
|
+
*
|
|
589
|
+
* Kept per slide, never on the transition: one transition object can be assigned to several slides,
|
|
590
|
+
* and a relationship id means something only inside the slide part that declares it. Kept across
|
|
591
|
+
* writes as well, so a deck written twice registers each sound once. An entry is reused only while
|
|
592
|
+
* the slide still holds its relationship and still has the same sound assigned.
|
|
593
|
+
*/
|
|
594
|
+
const registeredTransitionSounds = /* @__PURE__ */ new WeakMap();
|
|
595
|
+
/**
|
|
596
|
+
* Step 0: register an audio media part + relationship for each slide-transition start sound
|
|
597
|
+
* (`transition.sound` with `data`/`path`), and return the relationship id each slide's
|
|
598
|
+
* `p:sndAc/p:snd r:embed` refers to. Runs before media encoding so the bytes are loaded. Re-export
|
|
599
|
+
* is safe: a slide whose sound is already registered reuses that relationship. The stop-previous
|
|
600
|
+
* form (`sound.stopPrevious`) needs no part and is skipped.
|
|
601
|
+
*
|
|
602
|
+
* Registered here, at write time, rather than when `slide.transition` is assigned. A slide's rel
|
|
603
|
+
* ids and media part names are handed out in registration order, and a transition is often set
|
|
604
|
+
* before the slide's pictures and media are added; registering the sound then would move every
|
|
605
|
+
* later rel id and part name on the slide.
|
|
606
|
+
*/
|
|
607
|
+
function registerTransitionSounds(slides) {
|
|
608
|
+
const soundRIds = /* @__PURE__ */ new Map();
|
|
609
|
+
slides.forEach((slide) => {
|
|
610
|
+
const transition = slide.transition;
|
|
611
|
+
if (!transition || !TRANSITION_TYPES.includes(transition.type)) return;
|
|
612
|
+
const sound = transition.sound;
|
|
613
|
+
if (!sound || sound.stopPrevious) return;
|
|
614
|
+
if (!sound.data && !sound.path) return;
|
|
615
|
+
const registered = registeredTransitionSounds.get(slide);
|
|
616
|
+
if (registered && slide._relsMedia.includes(registered.rel) && registered.data === sound.data && registered.path === sound.path) {
|
|
617
|
+
soundRIds.set(slide, registered.rel.rId);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
620
|
+
const dataMime = dataUriMediaType(sound.data ?? "");
|
|
621
|
+
const pathFile = sound.path ? (sound.path.split("/").pop() ?? "").split("?")[0] ?? "" : "";
|
|
622
|
+
const extn = dataMime?.startsWith("audio/") ? audioExtensionForSubtype(dataMime.slice(6)) : (pathFile.split(".").pop() ?? "wav").toLowerCase();
|
|
623
|
+
const rId = getNewRelId(slide);
|
|
624
|
+
const rel = pushMediaRel(slide, {
|
|
625
|
+
kind: "audio",
|
|
626
|
+
extn,
|
|
627
|
+
type: `audio/${extn}`,
|
|
628
|
+
path: sound.path,
|
|
629
|
+
data: sound.data,
|
|
630
|
+
rId
|
|
631
|
+
});
|
|
632
|
+
registeredTransitionSounds.set(slide, {
|
|
633
|
+
rel,
|
|
634
|
+
data: sound.data,
|
|
635
|
+
path: sound.path
|
|
636
|
+
});
|
|
637
|
+
soundRIds.set(slide, rId);
|
|
638
|
+
});
|
|
639
|
+
return soundRIds;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
539
642
|
* Step 1: seed each slide with the layout placeholders it leaves empty.
|
|
540
643
|
* @param {PresSlideInternal[]} slides - the slides to prepare
|
|
541
644
|
*/
|
|
@@ -551,10 +654,9 @@ function backfillPlaceholders(slides) {
|
|
|
551
654
|
function bakeMeasuredFit(slides, fontMetrics) {
|
|
552
655
|
return applyMeasuredFit(slides, fontMetrics);
|
|
553
656
|
}
|
|
554
|
-
|
|
555
|
-
//#region src/gen/anim/animation.ts
|
|
657
|
+
// src/gen/anim/animation.ts
|
|
556
658
|
/**
|
|
557
|
-
* Resolved animation target's shape id (`spid`), or `null` when it cannot be resolved
|
|
659
|
+
* Resolved animation target's shape id (`spid`), or `null` when it cannot be resolved. In that
|
|
558
660
|
* case the effect is dropped, so every `null` warns rather than leaving the animation silently
|
|
559
661
|
* missing from the deck.
|
|
560
662
|
*
|
|
@@ -566,10 +668,10 @@ function bakeMeasuredFit(slides, fontMetrics) {
|
|
|
566
668
|
*
|
|
567
669
|
* `shapeIndex` is a 0-based index into the top-level objects that RENDER, which is the same
|
|
568
670
|
* sequence `collectSlideShapeIds` allocates along; group children take ids past that range. It
|
|
569
|
-
* counted `_slideObjects` itself, and four of that array's members draw nothing
|
|
671
|
+
* counted `_slideObjects` itself, and four of that array's members draw nothing, so `addNotes`
|
|
570
672
|
* before the first shape shifted every index by one and `shapeIndex: 0` addressed the notes.
|
|
571
673
|
* An index outside `[0, renderedCount)` would emit a `<p:spTgt spid>` naming no shape on the
|
|
572
|
-
* slide
|
|
674
|
+
* slide (a dangling spid PowerPoint reports as a repair, 0x80070570), so it warns and drops,
|
|
573
675
|
* exactly like an unresolvable `objectName` does.
|
|
574
676
|
* @param shapeIds - the slide's shape ids, from `collectSlideShapeIds`
|
|
575
677
|
* @param slideObjects - the slide's top-level objects, which a `shapeIndex` addresses
|
|
@@ -670,38 +772,23 @@ const ANIM_FLY_AXIS = (direction, axis, spid, dur, next) => {
|
|
|
670
772
|
};
|
|
671
773
|
const ANIM_PRESETS = {
|
|
672
774
|
fadeIn: {
|
|
673
|
-
|
|
674
|
-
presetClass: "entr",
|
|
675
|
-
presetSubtype: 0,
|
|
676
|
-
defaultDurationMs: 500,
|
|
775
|
+
...ANIM_PRESET_IDENTITY.fadeIn,
|
|
677
776
|
behaviors: (spid, dur, next) => ANIM_SET_VISIBLE(spid, next) + ANIM_FADE("in", spid, dur, next)
|
|
678
777
|
},
|
|
679
778
|
flyIn: {
|
|
680
|
-
|
|
681
|
-
presetClass: "entr",
|
|
682
|
-
presetSubtype: 4,
|
|
683
|
-
defaultDurationMs: 500,
|
|
779
|
+
...ANIM_PRESET_IDENTITY.flyIn,
|
|
684
780
|
behaviors: (spid, dur, next) => ANIM_SET_VISIBLE(spid, next) + ANIM_FLY_AXIS("in", "x", spid, dur, next) + ANIM_FLY_AXIS("in", "y", spid, dur, next)
|
|
685
781
|
},
|
|
686
782
|
appear: {
|
|
687
|
-
|
|
688
|
-
presetClass: "entr",
|
|
689
|
-
presetSubtype: 0,
|
|
690
|
-
defaultDurationMs: 500,
|
|
783
|
+
...ANIM_PRESET_IDENTITY.appear,
|
|
691
784
|
behaviors: (spid, _dur, next) => ANIM_SET_VISIBLE(spid, next)
|
|
692
785
|
},
|
|
693
786
|
wipe: {
|
|
694
|
-
|
|
695
|
-
presetClass: "entr",
|
|
696
|
-
presetSubtype: 4,
|
|
697
|
-
defaultDurationMs: 500,
|
|
787
|
+
...ANIM_PRESET_IDENTITY.wipe,
|
|
698
788
|
behaviors: (spid, dur, next) => ANIM_SET_VISIBLE(spid, next) + ANIM_EFFECT("in", "wipe(down)", spid, dur, next)
|
|
699
789
|
},
|
|
700
790
|
grow: {
|
|
701
|
-
|
|
702
|
-
presetClass: "emph",
|
|
703
|
-
presetSubtype: 0,
|
|
704
|
-
defaultDurationMs: 2e3,
|
|
791
|
+
...ANIM_PRESET_IDENTITY.grow,
|
|
705
792
|
behaviors: (spid, dur, next) => el("p:animScale", null, [raw(el("p:cBhvr", null, [raw(voidEl("p:cTn", {
|
|
706
793
|
id: next(),
|
|
707
794
|
dur,
|
|
@@ -712,10 +799,7 @@ const ANIM_PRESETS = {
|
|
|
712
799
|
}))])
|
|
713
800
|
},
|
|
714
801
|
spin: {
|
|
715
|
-
|
|
716
|
-
presetClass: "emph",
|
|
717
|
-
presetSubtype: 0,
|
|
718
|
-
defaultDurationMs: 2e3,
|
|
802
|
+
...ANIM_PRESET_IDENTITY.spin,
|
|
719
803
|
behaviors: (spid, dur, next) => el("p:animRot", { by: 216e5 }, [raw(el("p:cBhvr", null, [
|
|
720
804
|
raw(voidEl("p:cTn", {
|
|
721
805
|
id: next(),
|
|
@@ -727,17 +811,11 @@ const ANIM_PRESETS = {
|
|
|
727
811
|
]))])
|
|
728
812
|
},
|
|
729
813
|
fadeOut: {
|
|
730
|
-
|
|
731
|
-
presetClass: "exit",
|
|
732
|
-
presetSubtype: 0,
|
|
733
|
-
defaultDurationMs: 500,
|
|
814
|
+
...ANIM_PRESET_IDENTITY.fadeOut,
|
|
734
815
|
behaviors: (spid, dur, next) => ANIM_FADE("out", spid, dur, next) + ANIM_SET_HIDDEN(spid, dur, next)
|
|
735
816
|
},
|
|
736
817
|
flyOut: {
|
|
737
|
-
|
|
738
|
-
presetClass: "exit",
|
|
739
|
-
presetSubtype: 4,
|
|
740
|
-
defaultDurationMs: 500,
|
|
818
|
+
...ANIM_PRESET_IDENTITY.flyOut,
|
|
741
819
|
behaviors: (spid, dur, next) => ANIM_FLY_AXIS("out", "x", spid, dur, next) + ANIM_FLY_AXIS("out", "y", spid, dur, next) + ANIM_SET_HIDDEN(spid, dur, next)
|
|
742
820
|
}
|
|
743
821
|
};
|
|
@@ -847,8 +925,7 @@ function buildBldList(animations) {
|
|
|
847
925
|
}
|
|
848
926
|
return el("p:bldLst", null, raw(bldPs.join("")));
|
|
849
927
|
}
|
|
850
|
-
|
|
851
|
-
//#region src/gen/anim/timing.ts
|
|
928
|
+
// src/gen/anim/timing.ts
|
|
852
929
|
/**
|
|
853
930
|
* Build the slide-level `<p:timing>` tree that makes embedded media loop.
|
|
854
931
|
* - PowerPoint stores playback looping as `repeatCount` on the media node's `<p:cTn>`
|
|
@@ -863,7 +940,7 @@ function buildBldList(animations) {
|
|
|
863
940
|
function slideTimingToXml(slide) {
|
|
864
941
|
const loopMedia = slide._slideObjects.filter((obj) => obj._type === "media" && obj.mtype !== "online" && typeof obj.mediaRid === "number" && (obj.loop === true || typeof obj.loopCount === "number" && obj.loopCount > 0));
|
|
865
942
|
const shapeIds = collectSlideShapeIds(slide._slideObjects);
|
|
866
|
-
const animations =
|
|
943
|
+
const animations = slide._animations.filter((anim) => hasKnownAnimationPreset(anim)).map((anim) => ({
|
|
867
944
|
anim,
|
|
868
945
|
spid: resolveAnimationSpid(shapeIds, slide._slideObjects, anim)
|
|
869
946
|
})).filter((entry) => entry.spid !== null);
|
|
@@ -901,8 +978,7 @@ function slideTimingToXml(slide) {
|
|
|
901
978
|
nodeType: "tmRoot"
|
|
902
979
|
}, raw(el("p:childTnLst", null, raw(seq + mediaNodes))))))))), raw(bldLst)]);
|
|
903
980
|
}
|
|
904
|
-
|
|
905
|
-
//#region src/gen/anim/transition.ts
|
|
981
|
+
// src/gen/anim/transition.ts
|
|
906
982
|
/**
|
|
907
983
|
* The attributes of a transition's type element, from the caller's `variant`.
|
|
908
984
|
*
|
|
@@ -919,7 +995,7 @@ function transitionVariantAttrs(type, variant) {
|
|
|
919
995
|
attrs[name] = value;
|
|
920
996
|
continue;
|
|
921
997
|
}
|
|
922
|
-
warnOnce("transition/unknown-variant", `slide transition \`${type}\` variant \`${name}\` is not an attribute of <p:${type}> and is ignored
|
|
998
|
+
warnOnce("transition/unknown-variant", `slide transition \`${type}\` variant \`${name}\` is not an attribute of <p:${type}> and is ignored; ${allowed.length > 0 ? `use ${allowed.join(", ")}` : "it takes none"}.`, {
|
|
923
999
|
received: name,
|
|
924
1000
|
valid: allowed
|
|
925
1001
|
});
|
|
@@ -982,9 +1058,8 @@ function slideTransitionToXml(slide, soundRId) {
|
|
|
982
1058
|
fallback: el("p:transition", baseAttrs, [raw(typeEl), raw(sndAc)])
|
|
983
1059
|
});
|
|
984
1060
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
/** The four axes that make up an explicit group frame. All or nothing — see `givenGroupFrameAxes`. */
|
|
1061
|
+
// src/gen/slide/object.ts
|
|
1062
|
+
/** The four axes that make up an explicit group frame. All or nothing; see `givenGroupFrameAxes`. */
|
|
988
1063
|
const GROUP_FRAME_AXES = [
|
|
989
1064
|
"x",
|
|
990
1065
|
"y",
|
|
@@ -996,14 +1071,14 @@ const GROUP_FRAME_AXES = [
|
|
|
996
1071
|
*
|
|
997
1072
|
* A group frame is explicit only when **all four** are given; anything less falls back to
|
|
998
1073
|
* auto-bounds on every axis. Partial frames used to be honoured per-axis, which let the unset ones
|
|
999
|
-
* take the shared per-object defaults (`x=0`, `y=0`, `cx=75%` of the layout width, `cy=0`)
|
|
1074
|
+
* take the shared per-object defaults (`x=0`, `y=0`, `cx=75%` of the layout width, `cy=0`), so
|
|
1000
1075
|
* `addGroup([rect], { x: 5, y: 2 })` emitted a zero-height group whose width was a silent
|
|
1001
1076
|
* slide-width fraction, and every child of it then re-read as `null` through the read path's
|
|
1002
1077
|
* degenerate-`chExt` guard.
|
|
1003
1078
|
*
|
|
1004
1079
|
* Per-axis fallback was the other candidate, but it cannot mean what it reads like: the writer keeps
|
|
1005
1080
|
* an identity child space (`chOff/chExt == off/ext`), so a group's frame never moves or scales its
|
|
1006
|
-
* children
|
|
1081
|
+
* children; it only places the selection handle and the rotate pivot. `{ x: 5 }` would leave the
|
|
1007
1082
|
* children where they were and put the group's box somewhere they are not. Falling back whole, with
|
|
1008
1083
|
* a warning, keeps the group box around its content and says so out loud.
|
|
1009
1084
|
* @param options - the group object's options
|
|
@@ -1023,7 +1098,7 @@ const hasCompleteGroupFrame = (options) => givenGroupFrameAxes(options).length =
|
|
|
1023
1098
|
* `<a:ext cx>`/`<a:ext cy>` are `ST_PositiveCoordinate` (ECMA-376 Part 1, `CT_PositiveSize2D`), so a
|
|
1024
1099
|
* negative extent is schema-invalid. PowerPoint rejects the *whole* presentation with "The file or
|
|
1025
1100
|
* directory is corrupted and unreadable" (0x80070570) and names no shape, part, or slide, while
|
|
1026
|
-
* LibreOffice renders the same package happily
|
|
1101
|
+
* LibreOffice renders the same package happily, so the defect is invisible until the deck reaches
|
|
1027
1102
|
* PowerPoint.
|
|
1028
1103
|
*
|
|
1029
1104
|
* A signed delta is the natural way to write "draw from A to B" (`{ x: x0, y: y0, w: x1 - x0, h: y1 - y0 }`),
|
|
@@ -1075,7 +1150,7 @@ function resolveObjectFrame(obj, slide) {
|
|
|
1075
1150
|
const layout = slide._presLayout;
|
|
1076
1151
|
const placeholder = findLayoutPlaceholder(slide._slideLayout, itemOpts.placeholder);
|
|
1077
1152
|
const phOpts = placeholder?.options ?? {};
|
|
1078
|
-
const inherited = (own, ph) => own
|
|
1153
|
+
const inherited = (own, ph) => own ?? ph ?? void 0;
|
|
1079
1154
|
const ownX = inherited(itemOpts.x, phOpts.x);
|
|
1080
1155
|
const ownY = inherited(itemOpts.y, phOpts.y);
|
|
1081
1156
|
const ownW = inherited(itemOpts.w, phOpts.w);
|
|
@@ -1096,7 +1171,7 @@ function resolveObjectFrame(obj, slide) {
|
|
|
1096
1171
|
* Whether this part has a real slide number to cache in a `slidenum` field.
|
|
1097
1172
|
*
|
|
1098
1173
|
* Only a slide does. A master leaves `_slideNum` null and a layout carries the internal 1000+
|
|
1099
|
-
* counter that keeps layout media keys from colliding with slide media
|
|
1174
|
+
* counter that keeps layout media keys from colliding with slide media. Neither is a page
|
|
1100
1175
|
* number, and neither belongs in the field's cached text.
|
|
1101
1176
|
*/
|
|
1102
1177
|
function hasRealSlideNumber(slide) {
|
|
@@ -1107,7 +1182,7 @@ function hasRealSlideNumber(slide) {
|
|
|
1107
1182
|
*
|
|
1108
1183
|
* Unique `objectName` values are what consumers (e.g. semantic manifests) rely on, so
|
|
1109
1184
|
* collisions are flagged loudly. Deliberately NOT `warnOnce`: this runs per emit, and two
|
|
1110
|
-
* `write()` calls are two separate exports
|
|
1185
|
+
* `write()` calls are two separate exports, and a caller capturing diagnostics around the second
|
|
1111
1186
|
* would otherwise be told its deck is clean because the first one had already said otherwise. Groups are recursed into: a group's children are
|
|
1112
1187
|
* `<p:cNvPr>`-named on this same slide, so a child colliding with a top-level object (or with a
|
|
1113
1188
|
* child of another group) is a collision the Selection Pane shows, and checking only the top
|
|
@@ -1126,14 +1201,32 @@ function warnDuplicateObjectNames(slide) {
|
|
|
1126
1201
|
* mutually exclusive because a master carrying both a `color` and a `path` must still produce a
|
|
1127
1202
|
* single `<p:bg>`.
|
|
1128
1203
|
*/
|
|
1204
|
+
/**
|
|
1205
|
+
* The `<a:srcRect>` of a picture background: the crop an `image: { crop }` background states, else
|
|
1206
|
+
* the empty, uncropped form every picture background wrote before it could have one.
|
|
1207
|
+
*/
|
|
1208
|
+
function backgroundSrcRect(background) {
|
|
1209
|
+
const image = backgroundImage(background);
|
|
1210
|
+
const crop = image && "crop" in image ? image.crop : void 0;
|
|
1211
|
+
return crop ? genXmlImageCropRect(crop, "background image", "") : voidEl("a:srcRect");
|
|
1212
|
+
}
|
|
1213
|
+
/**
|
|
1214
|
+
* The children of a picture background's `<a:blip>`: its `transparency` as `a:alphaModFix`, as a
|
|
1215
|
+
* shape's picture fill writes it and PowerPoint writes Format Background's picture transparency
|
|
1216
|
+
* (#98), then the `a:lum` every picture background has always carried.
|
|
1217
|
+
*/
|
|
1218
|
+
function backgroundBlipEffects(background) {
|
|
1219
|
+
const transparency = typeof background === "object" ? background.transparency : void 0;
|
|
1220
|
+
return (transparency ? voidEl("a:alphaModFix", { amt: transparencyToAlpha(transparency) }) : "") + voidEl("a:lum");
|
|
1221
|
+
}
|
|
1129
1222
|
function slideBackgroundXml(slide) {
|
|
1130
1223
|
let strSlideXml = "";
|
|
1131
1224
|
if (slide._bkgdImgRid) strSlideXml += el("p:bg", null, raw(el("p:bgPr", null, [raw(el("a:blipFill", {
|
|
1132
1225
|
dpi: "0",
|
|
1133
1226
|
rotWithShape: "1"
|
|
1134
1227
|
}, [
|
|
1135
|
-
raw(el("a:blip", { "r:embed": `rId${slide._bkgdImgRid}` }, raw(
|
|
1136
|
-
raw(
|
|
1228
|
+
raw(el("a:blip", { "r:embed": `rId${slide._bkgdImgRid}` }, raw(backgroundBlipEffects(slide.background)))),
|
|
1229
|
+
raw(backgroundSrcRect(slide.background)),
|
|
1137
1230
|
raw(STRETCH_FILL_RECT)
|
|
1138
1231
|
])), raw(voidEl("a:effectLst"))])));
|
|
1139
1232
|
else if (fillNamesPaint(slide.background)) strSlideXml += el("p:bg", null, raw(el("p:bgPr", null, [raw(genXmlColorSelection(slide.background)), raw(voidEl("a:effectLst"))])));
|
|
@@ -1144,7 +1237,7 @@ function slideBackgroundXml(slide) {
|
|
|
1144
1237
|
* The slide-number placeholder shape.
|
|
1145
1238
|
*
|
|
1146
1239
|
* Emitted after every authored object, so its `<p:cNvPr>` id can come from the same monotonic
|
|
1147
|
-
* counter the walk has been advancing
|
|
1240
|
+
* counter the walk has been advancing; the caller allocates it and passes it in.
|
|
1148
1241
|
*
|
|
1149
1242
|
* @param slide - the slide, layout or master being emitted
|
|
1150
1243
|
* @param snProps - the part's slide-number properties, read but never written: a serializer
|
|
@@ -1178,19 +1271,21 @@ function slideNumberPlaceholderXml(slide, snProps, slideNumberId) {
|
|
|
1178
1271
|
})))), { openPrefix: " " }) + "</p:spPr>";
|
|
1179
1272
|
strSlideXml += "<p:txBody>";
|
|
1180
1273
|
const snInsets = resolveInsetsEmu(snProps.margin);
|
|
1181
|
-
|
|
1274
|
+
const snWarp = checkEnumOrWarn(snProps.textWarp, TEXT_SHAPE_TYPES, "text/invalid-warp", "slideNumber: textWarp");
|
|
1275
|
+
strSlideXml += elOrVoid("a:bodyPr", {
|
|
1182
1276
|
lIns: snInsets?.l ?? null,
|
|
1183
1277
|
tIns: snInsets?.t ?? null,
|
|
1184
1278
|
rIns: snInsets?.r ?? null,
|
|
1185
1279
|
bIns: snInsets?.b ?? null,
|
|
1186
|
-
anchor: resolveTextAnchor(snProps.valign)
|
|
1187
|
-
|
|
1280
|
+
anchor: resolveTextAnchor(snProps.valign),
|
|
1281
|
+
vert: checkEnumOrWarn(snProps.textDirection, TEXT_VERTICAL, "text/invalid-vertical", "slideNumber: textDirection")
|
|
1282
|
+
}, snWarp ? el("a:prstTxWarp", { prst: snWarp }, raw(voidEl("a:avLst"))) : "");
|
|
1188
1283
|
let defRPr = "";
|
|
1189
1284
|
rejectEmptyColor(snProps.color, "slideNumber color");
|
|
1190
1285
|
if (snProps.fontFace || snProps.fontSize || snProps.color) {
|
|
1191
1286
|
const face = snProps.fontFace;
|
|
1192
1287
|
defRPr = el("a:defRPr", { sz: clampFontSizeSz(snProps.fontSize || 12) }, [
|
|
1193
|
-
snProps.color ? raw(genXmlColorSelection(snProps.color)) : null,
|
|
1288
|
+
snProps.color ? raw(genXmlColorSelection(solidPaint(snProps.color, snProps.transparency))) : null,
|
|
1194
1289
|
face ? raw(voidEl("a:latin", { typeface: face })) : null,
|
|
1195
1290
|
face ? raw(voidEl("a:ea", { typeface: face })) : null,
|
|
1196
1291
|
face ? raw(voidEl("a:cs", { typeface: face })) : null
|
|
@@ -1200,20 +1295,47 @@ function slideNumberPlaceholderXml(slide, snProps, slideNumberId) {
|
|
|
1200
1295
|
const snAlignRaw = snProps.align ?? "left";
|
|
1201
1296
|
const snAlign = snAlignRaw.startsWith("c") ? "ctr" : snAlignRaw.startsWith("r") ? "r" : "l";
|
|
1202
1297
|
strSlideXml += el("a:p", null, [
|
|
1203
|
-
raw(voidEl("a:pPr", {
|
|
1298
|
+
raw(voidEl("a:pPr", {
|
|
1299
|
+
algn: snAlign,
|
|
1300
|
+
marL: paragraphMarginEmu(snProps.paraMarginLeft, clampParaMarginEmu),
|
|
1301
|
+
indent: paragraphMarginEmu(snProps.paraIndent, clampParaIndentEmu)
|
|
1302
|
+
})),
|
|
1204
1303
|
raw(el("a:fld", {
|
|
1205
1304
|
id: SLDNUMFLDID,
|
|
1206
1305
|
type: "slidenum"
|
|
1207
|
-
}, [raw(
|
|
1208
|
-
b: xsdBool(snProps.bold),
|
|
1209
|
-
lang: "en-US"
|
|
1210
|
-
})), raw(el("a:t", null, hasRealSlideNumber(slide) ? String(slide._slideNum) : SLDNUM_PLACEHOLDER_TEXT))])),
|
|
1306
|
+
}, [raw(slideNumberRunProperties(snProps)), raw(el("a:t", null, hasRealSlideNumber(slide) ? String(slide._slideNum) : SLDNUM_PLACEHOLDER_TEXT))])),
|
|
1211
1307
|
raw(voidEl("a:endParaRPr", { lang: "en-US" }))
|
|
1212
1308
|
]);
|
|
1213
1309
|
strSlideXml += "</p:txBody></p:sp>";
|
|
1214
1310
|
return strSlideXml;
|
|
1215
1311
|
}
|
|
1216
1312
|
/**
|
|
1313
|
+
* The slide-number field's own `a:rPr`: `b` and `lang` as it has always written them, then the
|
|
1314
|
+
* character options `addText` writes on a run's `a:rPr` (`genXmlTextRunProperties`). Colour, face
|
|
1315
|
+
* and size stay on the list style's `a:defRPr`, where they were written first.
|
|
1316
|
+
* @param snProps - the part's slide-number properties
|
|
1317
|
+
*/
|
|
1318
|
+
function slideNumberRunProperties(snProps) {
|
|
1319
|
+
const underline = underlineProps(snProps.underline);
|
|
1320
|
+
rejectEmptyColor(snProps.highlight, "slideNumber highlight");
|
|
1321
|
+
rejectEmptyColor(underline?.color, "slideNumber underline.color");
|
|
1322
|
+
return elOrVoid("a:rPr", {
|
|
1323
|
+
b: xsdBool(snProps.bold),
|
|
1324
|
+
lang: snProps.lang || "en-US",
|
|
1325
|
+
i: xsdBoolIfTrue(snProps.italic),
|
|
1326
|
+
cap: snProps.caps ? checkEnumOrWarn(snProps.caps, TEXT_CAPS_TYPES, "text/invalid-caps", "slideNumber `caps`") : null,
|
|
1327
|
+
u: underline?.style ? checkEnumOrWarn(underline.style, TEXT_UNDERLINE_TYPES, "text/invalid-underline", "slideNumber `underline.style`") : null
|
|
1328
|
+
}, [
|
|
1329
|
+
snProps.highlight ? el("a:highlight", null, raw(createColorElement(snProps.highlight))) : "",
|
|
1330
|
+
underline?.color ? el("a:uFill", null, raw(genXmlColorSelection(underline.color))) : "",
|
|
1331
|
+
snProps.fontFaceEA ? voidEl("a:ea", { typeface: snProps.fontFaceEA }) : ""
|
|
1332
|
+
].join(""));
|
|
1333
|
+
}
|
|
1334
|
+
/** `paraMarginLeft` / `paraIndent` in EMU through the attribute's clamp, or `null` to write none. */
|
|
1335
|
+
function paragraphMarginEmu(option, clamp) {
|
|
1336
|
+
return option === void 0 || option === "inherit" ? null : clamp(option);
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1217
1339
|
* Transforms a slide or slideLayout to resulting XML string - Creates `ppt/slide*.xml`
|
|
1218
1340
|
* @param slide - slide object created within createSlideObject
|
|
1219
1341
|
* @param renderers - which renderer emits each shape family; see {@link RendererTable} for why
|
|
@@ -1379,8 +1501,8 @@ function slideObjectToXml(slide, renderers) {
|
|
|
1379
1501
|
function defaultRelIdStart(slide) {
|
|
1380
1502
|
let lastRid = 0;
|
|
1381
1503
|
for (const rel of slide._rels) lastRid = Math.max(lastRid, rel.rId);
|
|
1382
|
-
for (const rel of slide._relsChart
|
|
1383
|
-
for (const rel of slide._relsMedia
|
|
1504
|
+
for (const rel of slide._relsChart) lastRid = Math.max(lastRid, rel.rId);
|
|
1505
|
+
for (const rel of slide._relsMedia) lastRid = Math.max(lastRid, rel.rId);
|
|
1384
1506
|
return lastRid + 1;
|
|
1385
1507
|
}
|
|
1386
1508
|
/**
|
|
@@ -1390,7 +1512,7 @@ function defaultRelIdStart(slide) {
|
|
|
1390
1512
|
* `selfPath` is the emitting part's own path, and it is required because a relationship target
|
|
1391
1513
|
* resolves against the part that states it. An internal slide link used to be written
|
|
1392
1514
|
* `slide${n}.xml` whatever the part, which is right from `ppt/slides/` and names nothing from
|
|
1393
|
-
* `ppt/slideLayouts/` or `ppt/slideMasters
|
|
1515
|
+
* `ppt/slideLayouts/` or `ppt/slideMasters/`, so a `hyperlink: { slide }` on an object given to
|
|
1394
1516
|
* `defineSlideMaster({ objects })` wrote a dangling relationship.
|
|
1395
1517
|
* @param {PresSlideInternal | SlideLayoutInternal | SlideMasterInternal} slide - slide, layout or master whose relations are being transformed
|
|
1396
1518
|
* @param {{ target: string; type: string }[]} defaultRels - array of default relations
|
|
@@ -1405,7 +1527,7 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
|
|
|
1405
1527
|
* told from the first by the first already being there.
|
|
1406
1528
|
*
|
|
1407
1529
|
* Two things about the shape. It holds raw Targets rather than escaped ones, because this
|
|
1408
|
-
* asks about the model and not about markup
|
|
1530
|
+
* asks about the model and not about markup. The probe used to substring-scan the emitted
|
|
1409
1531
|
* XML for ` Target="…"`, which forced the caller to escape by hand with the same escaper the
|
|
1410
1532
|
* builder uses or the two drifted apart, and cost O(rels squared) in string scanning.
|
|
1411
1533
|
*
|
|
@@ -1422,10 +1544,11 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
|
|
|
1422
1544
|
else rels.push(externalHyperlinkRel(rel.rId, rel.Target));
|
|
1423
1545
|
}
|
|
1424
1546
|
});
|
|
1425
|
-
|
|
1547
|
+
slide._relsChart.forEach((rel) => {
|
|
1426
1548
|
rels.push(relationshipEl(rel.rId, rel.isChartEx ? CHARTEX_REL : CHART_REL, rel.Target));
|
|
1427
1549
|
});
|
|
1428
|
-
|
|
1550
|
+
slide._relsMedia.forEach((rel) => {
|
|
1551
|
+
if (rel.chartOwned) return;
|
|
1429
1552
|
const relType = rel.type.toLowerCase();
|
|
1430
1553
|
const seen = mediaTargets.has(rel.Target);
|
|
1431
1554
|
mediaTargets.add(rel.Target);
|
|
@@ -1443,8 +1566,7 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
|
|
|
1443
1566
|
});
|
|
1444
1567
|
return relationshipsPart(rels);
|
|
1445
1568
|
}
|
|
1446
|
-
|
|
1447
|
-
//#region src/gen/slide/slide.ts
|
|
1569
|
+
// src/gen/slide/slide.ts
|
|
1448
1570
|
/**
|
|
1449
1571
|
* pptx-ts: slide part + slide/layout rels
|
|
1450
1572
|
*
|
|
@@ -1462,7 +1584,7 @@ function slideObjectRelationsToXml(slide, defaultRels, selfPath) {
|
|
|
1462
1584
|
function makeXmlSlide(slide, renderers, transitionSoundRId) {
|
|
1463
1585
|
return XML_DECL + "\r\n" + el("p:sld", {
|
|
1464
1586
|
...PML_ROOT_NS,
|
|
1465
|
-
show: slide
|
|
1587
|
+
show: slide.hidden ? "0" : null
|
|
1466
1588
|
}, [
|
|
1467
1589
|
raw(slideObjectToXml(slide, renderers)),
|
|
1468
1590
|
raw(el("p:clrMapOvr", null, raw(voidEl("a:masterClrMapping")))),
|
|
@@ -1501,7 +1623,7 @@ function makeXmlSlideRel(slides, slideLayouts, slideNumber) {
|
|
|
1501
1623
|
target: targetFromPptSubpart(notesSlidePath(slideNumber)),
|
|
1502
1624
|
type: NOTES_SLIDE_REL
|
|
1503
1625
|
}];
|
|
1504
|
-
if (
|
|
1626
|
+
if (slide._comments.length > 0) defaultRels.push({
|
|
1505
1627
|
target: targetFromPptSubpart(commentPath(slideNumber)),
|
|
1506
1628
|
type: COMMENTS_REL
|
|
1507
1629
|
});
|
|
@@ -1518,8 +1640,7 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
|
|
|
1518
1640
|
for (let i = 0; i < slideLayouts.length; i++) if (slideLayouts[i]?._name === slides[slideNumber - 1]?._slideLayout?._name) return i + 1;
|
|
1519
1641
|
return 1;
|
|
1520
1642
|
}
|
|
1521
|
-
|
|
1522
|
-
//#region src/gen/pres/theme.ts
|
|
1643
|
+
// src/gen/pres/theme.ts
|
|
1523
1644
|
/**
|
|
1524
1645
|
* pptx-ts: `ppt/theme/theme1.xml`
|
|
1525
1646
|
*
|
|
@@ -1572,13 +1693,13 @@ function buildThemeClrScheme(scheme) {
|
|
|
1572
1693
|
}
|
|
1573
1694
|
/**
|
|
1574
1695
|
* The per-script theme font fallback table (`a:fontScheme`'s CT_TextFont lists), in OOXML
|
|
1575
|
-
* document order. Static Office defaults
|
|
1696
|
+
* document order. Static Office defaults (no caller data, no escaping risk), unlike the
|
|
1576
1697
|
* `<a:latin>`/`<a:ea>`/`<a:cs>` slots above.
|
|
1577
1698
|
*
|
|
1578
1699
|
* Forty-one of the forty-seven scripts take the same face in the major and minor lists, so the
|
|
1579
1700
|
* shared table below is the whole thing and the two override maps hold the six-odd rows that
|
|
1580
1701
|
* actually differ. Written out twice, ninety-four lines said less than these do, and one row
|
|
1581
|
-
* mistyped in one copy produces a theme that opens fine and paints a script in the wrong face
|
|
1702
|
+
* mistyped in one copy produces a theme that opens fine and paints a script in the wrong face:
|
|
1582
1703
|
* a difference nobody reviewing a diff of two 47-row tables would see.
|
|
1583
1704
|
*/
|
|
1584
1705
|
const THEME_FONT_LIST = [
|
|
@@ -1703,15 +1824,14 @@ function makeXmlTheme(pres) {
|
|
|
1703
1824
|
raw(extLst)
|
|
1704
1825
|
]);
|
|
1705
1826
|
}
|
|
1706
|
-
|
|
1707
|
-
//#region src/gen/extract-slides.ts
|
|
1827
|
+
// src/gen/extract-slides.ts
|
|
1708
1828
|
/**
|
|
1709
1829
|
* Embedded audio/video. addMedia (`gen/define/media.ts`) pushes three consecutive
|
|
1710
1830
|
* _relsMedia entries per item off `mediaRid`: the ECMA audio/video rel
|
|
1711
1831
|
* (rId=mediaRid), the MS-2007 `media` rel (mediaRid+1, sharing one Target),
|
|
1712
1832
|
* and the preview image rel (mediaRid+2). The body references all three; we
|
|
1713
1833
|
* surface the descriptor so appendSlides can reproduce the rel graph. Online
|
|
1714
|
-
* media (external link) is excluded
|
|
1834
|
+
* media (external link) is excluded: it has a different rel shape and no part.
|
|
1715
1835
|
*/
|
|
1716
1836
|
function avMediaOf(slide, relByRid) {
|
|
1717
1837
|
return slide._slideObjects.filter((obj) => obj._type === "media" && obj.mtype !== "online" && typeof obj.mediaRid === "number").map((obj) => {
|
|
@@ -1720,8 +1840,8 @@ function avMediaOf(slide, relByRid) {
|
|
|
1720
1840
|
const mediaRel = relByRid.get(mediaRid);
|
|
1721
1841
|
const previewRel = relByRid.get(previewRid(mediaRid));
|
|
1722
1842
|
if (!mediaRel || !previewRel) return null;
|
|
1723
|
-
const mediaBytes = decodeBase64ToBytes(
|
|
1724
|
-
const previewBytes = decodeBase64ToBytes(
|
|
1843
|
+
const mediaBytes = decodeBase64ToBytes(mediaRel.data);
|
|
1844
|
+
const previewBytes = decodeBase64ToBytes(previewRel.data);
|
|
1725
1845
|
if (!mediaBytes || !previewBytes) return null;
|
|
1726
1846
|
const mediaExtn = relExtension(mediaRel, "mp4");
|
|
1727
1847
|
const previewExtn = relExtension(previewRel, "png");
|
|
@@ -1756,9 +1876,8 @@ function relExtension(rel, fallback) {
|
|
|
1756
1876
|
* excluded here rather than surfaced twice.
|
|
1757
1877
|
*/
|
|
1758
1878
|
function imageMediaOf(slide, avPreviewRids) {
|
|
1759
|
-
return slide._relsMedia.filter((rel) => rel.type.toLowerCase().includes("image") && !avPreviewRids.has(rel.rId)).map((rel) => {
|
|
1760
|
-
const
|
|
1761
|
-
const bytes = decodeBase64ToBytes(data);
|
|
1879
|
+
return slide._relsMedia.filter((rel) => rel.type.toLowerCase().includes("image") && !avPreviewRids.has(rel.rId) && !rel.chartOwned).map((rel) => {
|
|
1880
|
+
const bytes = decodeBase64ToBytes(rel.data);
|
|
1762
1881
|
const extn = relExtension(rel, "png");
|
|
1763
1882
|
return bytes ? {
|
|
1764
1883
|
rId: rel.rId,
|
|
@@ -1769,6 +1888,23 @@ function imageMediaOf(slide, avPreviewRids) {
|
|
|
1769
1888
|
}).filter((m) => m !== null);
|
|
1770
1889
|
}
|
|
1771
1890
|
/**
|
|
1891
|
+
* A transition's start sound: the audio part behind `p:sndAc/p:stSnd/p:snd r:embed`, under the
|
|
1892
|
+
* `rId` {@link registerTransitionSounds} gave it. `undefined` when the slide has no start sound.
|
|
1893
|
+
*/
|
|
1894
|
+
function transitionSoundOf(rId, relByRid) {
|
|
1895
|
+
const rel = rId === void 0 ? void 0 : relByRid.get(rId);
|
|
1896
|
+
if (!rel || rId === void 0) return void 0;
|
|
1897
|
+
const bytes = decodeBase64ToBytes(rel.data);
|
|
1898
|
+
if (!bytes) return void 0;
|
|
1899
|
+
const extn = relExtension(rel, "wav");
|
|
1900
|
+
return {
|
|
1901
|
+
rId,
|
|
1902
|
+
bytes,
|
|
1903
|
+
extn,
|
|
1904
|
+
contentType: avContentType(extn, "audio")
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1772
1908
|
* Online (external-link) video. addMedia type:'online' pushes three rels off
|
|
1773
1909
|
* mediaRid: the ECMA video rel (mediaRid, External), the MS-2007 media rel
|
|
1774
1910
|
* (mediaRid+1, External, sharing the link Target), and the poster image rel
|
|
@@ -1801,10 +1937,10 @@ function hyperlinksOf(slide) {
|
|
|
1801
1937
|
* A Target that does not read as a slide number throws rather than passing `NaN` on to the
|
|
1802
1938
|
* append path's rel-graph rebuild, where it surfaces as "links to source slide NaN" a whole
|
|
1803
1939
|
* subsystem later. `addText` writes this Target itself, so the condition is a defect on this
|
|
1804
|
-
* side rather than bad input
|
|
1940
|
+
* side rather than bad input: an {@link InternalError}, like the chart router's own.
|
|
1805
1941
|
*/
|
|
1806
|
-
function slideLinksOf(
|
|
1807
|
-
return
|
|
1942
|
+
function slideLinksOf(rels) {
|
|
1943
|
+
return rels.filter((rel) => isHyperlinkRel(rel) && rel.data === "slide").map((rel) => {
|
|
1808
1944
|
const sourceSlideNumber = Number(rel.Target);
|
|
1809
1945
|
if (!Number.isInteger(sourceSlideNumber) || sourceSlideNumber < 1) throw new InternalError("slide/link-target-not-a-number", `Internal slide link rId${rel.rId} targets "${String(rel.Target)}", which is not a slide number`);
|
|
1810
1946
|
return {
|
|
@@ -1823,10 +1959,11 @@ function notesOf(slide) {
|
|
|
1823
1959
|
const notes = buildNotesSlideRels(slide);
|
|
1824
1960
|
return {
|
|
1825
1961
|
xml: makeXmlNotesSlide(slide, notes),
|
|
1826
|
-
hyperlinks: notes.rels.filter((rel) => typeof rel.Target === "string" && rel.Target).map((rel) => ({
|
|
1962
|
+
hyperlinks: notes.rels.filter((rel) => rel.data !== "slide" && typeof rel.Target === "string" && rel.Target).map((rel) => ({
|
|
1827
1963
|
rId: rel.rId,
|
|
1828
1964
|
target: rel.Target
|
|
1829
|
-
}))
|
|
1965
|
+
})),
|
|
1966
|
+
slideLinks: slideLinksOf(notes.rels)
|
|
1830
1967
|
};
|
|
1831
1968
|
}
|
|
1832
1969
|
/**
|
|
@@ -1839,6 +1976,7 @@ async function extractSlides(source, opts = {}) {
|
|
|
1839
1976
|
const { presentation } = source;
|
|
1840
1977
|
const deckSlides = presentation.slides;
|
|
1841
1978
|
requireSlideLinksInDeck(deckSlides);
|
|
1979
|
+
const transitionSoundRIds = registerTransitionSounds(deckSlides);
|
|
1842
1980
|
backfillPlaceholders(deckSlides);
|
|
1843
1981
|
await encodeMediaForTargets(deckSlides, source.runtime, onMediaError);
|
|
1844
1982
|
const restoreAuthoredFit = bakeMeasuredFit(deckSlides, source.fontMetrics);
|
|
@@ -1848,15 +1986,19 @@ async function extractSlides(source, opts = {}) {
|
|
|
1848
1986
|
const relByRid = new Map(slide._relsMedia.map((rel) => [rel.rId, rel]));
|
|
1849
1987
|
const avMedia = avMediaOf(slide, relByRid);
|
|
1850
1988
|
const notes = notesOf(slide);
|
|
1989
|
+
const soundRId = transitionSoundRIds.get(slide);
|
|
1990
|
+
const transitionSound = transitionSoundOf(soundRId, relByRid);
|
|
1851
1991
|
return {
|
|
1852
|
-
xml: makeXmlSlide(slide, source.renderers),
|
|
1992
|
+
xml: makeXmlSlide(slide, source.renderers, soundRId),
|
|
1853
1993
|
media: imageMediaOf(slide, new Set(avMedia.map((item) => item.previewRid))),
|
|
1854
1994
|
hyperlinks: hyperlinksOf(slide),
|
|
1855
1995
|
charts: source.extract.charts?.(slide) ?? [],
|
|
1856
|
-
slideLinks: slideLinksOf(slide),
|
|
1996
|
+
slideLinks: slideLinksOf(slide._rels),
|
|
1857
1997
|
avMedia,
|
|
1858
1998
|
onlineMedia: onlineMediaOf(slide, relByRid),
|
|
1859
|
-
...notes ? { notes } : {}
|
|
1999
|
+
...notes ? { notes } : {},
|
|
2000
|
+
...transitionSound ? { transitionSound } : {},
|
|
2001
|
+
...slide._comments.length ? { comments: slide._comments } : {}
|
|
1860
2002
|
};
|
|
1861
2003
|
});
|
|
1862
2004
|
} finally {
|
|
@@ -1874,8 +2016,7 @@ async function extractSlides(source, opts = {}) {
|
|
|
1874
2016
|
...notesMaster ? { notesMaster } : {}
|
|
1875
2017
|
};
|
|
1876
2018
|
}
|
|
1877
|
-
|
|
1878
|
-
//#region src/gen/opc/app.ts
|
|
2019
|
+
// src/gen/opc/app.ts
|
|
1879
2020
|
/**
|
|
1880
2021
|
* pptx-ts: `docProps/app.xml`
|
|
1881
2022
|
*
|
|
@@ -1957,8 +2098,7 @@ function makeXmlApp(slides, company, presLayout) {
|
|
|
1957
2098
|
raw(el("AppVersion", null, "16.0000", PROP))
|
|
1958
2099
|
], { closePrefix: INDENT_1 });
|
|
1959
2100
|
}
|
|
1960
|
-
|
|
1961
|
-
//#region src/gen/opc/content-types.ts
|
|
2101
|
+
// src/gen/opc/content-types.ts
|
|
1962
2102
|
/**
|
|
1963
2103
|
* pptx-ts: `[Content_Types].xml`
|
|
1964
2104
|
*
|
|
@@ -1967,24 +2107,16 @@ function makeXmlApp(slides, company, presLayout) {
|
|
|
1967
2107
|
* Override entries for every part the package skeleton writes.
|
|
1968
2108
|
*
|
|
1969
2109
|
* Entries that exist only because a construct family put a part in the package arrive as
|
|
1970
|
-
* {@link ContentTypeContributions}
|
|
2110
|
+
* {@link ContentTypeContributions}, already resolved to data and grouped by where they land.
|
|
1971
2111
|
* The order below is a property of the format, not of whoever assembled that list, so a deck
|
|
1972
2112
|
* written by a program that links three families and one that links ten emit the same bytes.
|
|
1973
2113
|
*/
|
|
1974
2114
|
/**
|
|
1975
2115
|
* Some Override entries have always been emitted with a leading space. It is insignificant
|
|
1976
2116
|
* whitespace, but it is in the bytes this library has shipped for years, so it is reproduced
|
|
1977
|
-
* verbatim rather than normalized (see
|
|
2117
|
+
* verbatim rather than normalized (see "Byte identity" in `docs/contributing/testing.md`: whitespace diffs fail the gate).
|
|
1978
2118
|
*/
|
|
1979
2119
|
const LEADING_SPACE = { openPrefix: " " };
|
|
1980
|
-
/** Nothing beyond the skeleton — the shape a deck written with no construct families contributes. */
|
|
1981
|
-
const NO_CONTENT_TYPE_CONTRIBUTIONS = Object.freeze({
|
|
1982
|
-
defaults: [],
|
|
1983
|
-
presentation: [],
|
|
1984
|
-
perSlide: [],
|
|
1985
|
-
perLayout: [],
|
|
1986
|
-
trailing: []
|
|
1987
|
-
});
|
|
1988
2120
|
function contentDefault(extension, contentType) {
|
|
1989
2121
|
return voidEl("Default", {
|
|
1990
2122
|
Extension: extension,
|
|
@@ -2007,16 +2139,15 @@ function contributedOverride(entry) {
|
|
|
2007
2139
|
* @returns XML
|
|
2008
2140
|
*/
|
|
2009
2141
|
function makeXmlContTypes(opts) {
|
|
2010
|
-
const { slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts } = opts;
|
|
2011
|
-
const contributions = opts.contributions ?? NO_CONTENT_TYPE_CONTRIBUTIONS;
|
|
2142
|
+
const { slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts, contributions } = opts;
|
|
2012
2143
|
const parts = [contentDefault("xml", "application/xml"), contentDefault("rels", RELATIONSHIPS_CONTENT_TYPE)];
|
|
2013
2144
|
const extnTypeMap = /* @__PURE__ */ new Map();
|
|
2014
2145
|
const ctTargets = [];
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2146
|
+
slides.forEach((s) => ctTargets.push(s));
|
|
2147
|
+
slideLayouts.forEach((l) => ctTargets.push(l));
|
|
2148
|
+
ctTargets.push(masterSlide);
|
|
2018
2149
|
ctTargets.forEach((target) => {
|
|
2019
|
-
|
|
2150
|
+
target._relsMedia.forEach((rel) => {
|
|
2020
2151
|
if (rel.type === "online" || !rel.extn || !rel.type) return;
|
|
2021
2152
|
const contentType = rel.type.startsWith("audio/") ? avContentType(rel.extn, "audio") : rel.type.startsWith("video/") ? avContentType(rel.extn, "video") : rel.type;
|
|
2022
2153
|
if (!extnTypeMap.has(rel.extn)) extnTypeMap.set(rel.extn, contentType);
|
|
@@ -2030,7 +2161,7 @@ function makeXmlContTypes(opts) {
|
|
|
2030
2161
|
extnTypeMap.set(entry.extension, entry.contentType);
|
|
2031
2162
|
parts.push(contentDefault(entry.extension, entry.contentType));
|
|
2032
2163
|
});
|
|
2033
|
-
if (
|
|
2164
|
+
if (embeddedFonts.some((font) => font.faces.some((face) => face.bytes))) parts.push(contentDefault(FONT_DATA_EXTENSION, FONT_DATA_CONTENT_TYPE));
|
|
2034
2165
|
parts.push(override(overrideName(PRESENTATION_PATH), PRESENTATION_MAIN_CONTENT_TYPE));
|
|
2035
2166
|
contributions.presentation.forEach((entry) => parts.push(contributedOverride(entry)));
|
|
2036
2167
|
parts.push(override(overrideName(SLIDE_MASTER_PATH), SLIDE_MASTER_CONTENT_TYPE));
|
|
@@ -2053,8 +2184,7 @@ function makeXmlContTypes(opts) {
|
|
|
2053
2184
|
if (hasCustomProps) parts.push(override("/docProps/custom.xml", CUSTOM_PROPS_CONTENT_TYPE, LEADING_SPACE));
|
|
2054
2185
|
return XML_DECL + "\r\n" + el("Types", { xmlns: OOXML_NS.ct }, parts.map(raw));
|
|
2055
2186
|
}
|
|
2056
|
-
|
|
2057
|
-
//#region src/gen/opc/custom-props.ts
|
|
2187
|
+
// src/gen/opc/custom-props.ts
|
|
2058
2188
|
/**
|
|
2059
2189
|
* pptx-ts: `docProps/custom.xml`
|
|
2060
2190
|
*
|
|
@@ -2086,8 +2216,7 @@ function makeXmlCustomProperties(props) {
|
|
|
2086
2216
|
}, raw(valueEl(value)))));
|
|
2087
2217
|
return XML_DECL + "\r\n" + el("Properties", PROPS_NS, properties);
|
|
2088
2218
|
}
|
|
2089
|
-
|
|
2090
|
-
//#region src/gen/opc/root-rels.ts
|
|
2219
|
+
// src/gen/opc/root-rels.ts
|
|
2091
2220
|
/**
|
|
2092
2221
|
* pptx-ts: `_rels/.rels`
|
|
2093
2222
|
*
|
|
@@ -2109,8 +2238,7 @@ function makeXmlRootRels(hasCustomProps) {
|
|
|
2109
2238
|
if (hasCustomProps) rels.push(relationshipEl("rId4", CUSTOM_PROPS_REL, "docProps/custom.xml", INDENTED));
|
|
2110
2239
|
return relationshipsPart(rels, { closePrefix: "\n " });
|
|
2111
2240
|
}
|
|
2112
|
-
|
|
2113
|
-
//#region src/gen/pres/presentation-rels.ts
|
|
2241
|
+
// src/gen/pres/presentation-rels.ts
|
|
2114
2242
|
/**
|
|
2115
2243
|
* Creates `ppt/_rels/presentation.xml.rels`
|
|
2116
2244
|
* @param {PresSlideInternal[]} slides - Presenation Slides
|
|
@@ -2124,16 +2252,16 @@ function makeXmlRootRels(hasCustomProps) {
|
|
|
2124
2252
|
* theme1/tableStyles, then commentAuthors when the deck has any comment, then the embedded
|
|
2125
2253
|
* fonts.
|
|
2126
2254
|
*
|
|
2127
|
-
* The rels writer and `makeXmlPresentation` both need these numbers
|
|
2128
|
-
* names the notesMaster and each embedded face by `r:id`
|
|
2255
|
+
* The rels writer and `makeXmlPresentation` both need these numbers (the presentation part
|
|
2256
|
+
* names the notesMaster and each embedded face by `r:id`), and each derived them separately,
|
|
2129
2257
|
* one as `slides.length + 2` under a comment saying it copied the other's logic. Inserting a
|
|
2130
2258
|
* relationship before `notesMaster` then broke the presentation's own reference with no
|
|
2131
2259
|
* signal.
|
|
2132
2260
|
* @param slides - the deck's slides, whose count and comments decide the layout
|
|
2133
2261
|
*/
|
|
2134
2262
|
function presentationFixedRelIds(slides) {
|
|
2135
|
-
const first =
|
|
2136
|
-
const hasComments =
|
|
2263
|
+
const first = slides.length + 2;
|
|
2264
|
+
const hasComments = slides.some((slide) => slide._comments.length > 0);
|
|
2137
2265
|
return {
|
|
2138
2266
|
notesMaster: first,
|
|
2139
2267
|
presProps: first + 1,
|
|
@@ -2145,7 +2273,7 @@ function presentationFixedRelIds(slides) {
|
|
|
2145
2273
|
};
|
|
2146
2274
|
}
|
|
2147
2275
|
/**
|
|
2148
|
-
* The first relationship id free for embedded-font rels
|
|
2276
|
+
* The first relationship id free for embedded-font rels: {@link presentationFixedRelIds}'
|
|
2149
2277
|
* `fontStart`, kept as its own name because that is what the two font walks ask for.
|
|
2150
2278
|
*/
|
|
2151
2279
|
function presentationFontRelStart(slides) {
|
|
@@ -2157,11 +2285,10 @@ function makeXmlPresentationRels(slides, embeddedFonts) {
|
|
|
2157
2285
|
for (let idx = 1; idx <= slides.length; idx++) rels.push(relationshipEl(idx + 1, SLIDE_REL, targetFromPresentation(slidePath(idx))));
|
|
2158
2286
|
rels.push(relationshipEl(fixed.notesMaster, NOTES_MASTER_REL, targetFromPresentation(NOTES_MASTER_PATH)), relationshipEl(fixed.presProps, PRES_PROPS_REL, "presProps.xml"), relationshipEl(fixed.viewProps, VIEW_PROPS_REL, "viewProps.xml"), relationshipEl(fixed.theme, THEME_REL, "theme/theme1.xml"), relationshipEl(fixed.tableStyles, TABLE_STYLES_REL, "tableStyles.xml"));
|
|
2159
2287
|
if (fixed.commentAuthors !== null) rels.push(relationshipEl(fixed.commentAuthors, COMMENT_AUTHORS_REL, "commentAuthors.xml"));
|
|
2160
|
-
for (const face of flattenEmbeddedFaces(embeddedFonts
|
|
2288
|
+
for (const face of flattenEmbeddedFaces(embeddedFonts, presentationFontRelStart(slides))) rels.push(relationshipEl(face.rId, FONT_REL_TYPE, targetFromPresentation(fontPath(face.partIndex))));
|
|
2161
2289
|
return relationshipsPart(rels);
|
|
2162
2290
|
}
|
|
2163
|
-
|
|
2164
|
-
//#region src/gen/pres/presentation.ts
|
|
2291
|
+
// src/gen/pres/presentation.ts
|
|
2165
2292
|
/**
|
|
2166
2293
|
* pptx-ts: `ppt/presentation.xml` + presProps/viewProps
|
|
2167
2294
|
*
|
|
@@ -2191,7 +2318,7 @@ function sectionsExtLst(sections) {
|
|
|
2191
2318
|
* @return {string} XML
|
|
2192
2319
|
*/
|
|
2193
2320
|
function makeXmlPresentation(pres) {
|
|
2194
|
-
const openTag = `<p:presentation xmlns:a="${OOXML_NS.a}" xmlns:r="${OOXML_NS.r}" xmlns:p="${OOXML_NS.p}" ${pres.rtlMode ? "rtl=\"1\"" : ""} ${
|
|
2321
|
+
const openTag = `<p:presentation xmlns:a="${OOXML_NS.a}" xmlns:r="${OOXML_NS.r}" xmlns:p="${OOXML_NS.p}" ${pres.rtlMode ? "rtl=\"1\"" : ""} ${pres.embeddedFonts.some((font) => font.faces.some((face) => face.bytes)) ? "embedTrueTypeFonts=\"1\" saveSubsetFonts=\"0\"" : "saveSubsetFonts=\"1\""} autoCompressPictures="0"${pres.firstSlideNum !== 1 ? ` firstSlideNum="${pres.firstSlideNum}"` : ""}>`;
|
|
2195
2322
|
const sldMasterIdLst = el("p:sldMasterIdLst", null, raw(voidEl("p:sldMasterId", {
|
|
2196
2323
|
id: MIN_SLIDE_MASTER_ID,
|
|
2197
2324
|
"r:id": "rId1"
|
|
@@ -2211,12 +2338,12 @@ function makeXmlPresentation(pres) {
|
|
|
2211
2338
|
cx: pres.presLayout.height,
|
|
2212
2339
|
cy: pres.presLayout.width
|
|
2213
2340
|
});
|
|
2214
|
-
const fonts = pres.embeddedFonts
|
|
2341
|
+
const fonts = pres.embeddedFonts;
|
|
2215
2342
|
const flat = flattenEmbeddedFaces(fonts, presentationFontRelStart(pres.slides));
|
|
2216
2343
|
const rIdOf = new Map(flat.map((face) => [`${face.fontIndex}:${face.slot}`, face.rId]));
|
|
2217
2344
|
const embeddedFontLst = serializeEmbeddedFontLst(fonts, (fontIndex, slot) => rIdOf.get(`${fontIndex}:${slot}`));
|
|
2218
2345
|
const defaultTextStyle = el("p:defaultTextStyle", null, LEVEL_MARGINS_EMU.map((marL, i) => raw(defaultTextStyleLevel(i + 1, marL))));
|
|
2219
|
-
const extLst = pres.sections
|
|
2346
|
+
const extLst = pres.sections.length > 0 ? sectionsExtLst(pres.sections) : "";
|
|
2220
2347
|
return XML_DECL + "\r\n" + openTag + sldMasterIdLst + notesMasterIdLst + sldIdLst + sldSz + notesSz + embeddedFontLst + defaultTextStyle + extLst + "</p:presentation>";
|
|
2221
2348
|
}
|
|
2222
2349
|
/**
|
|
@@ -2262,21 +2389,7 @@ function makeXmlViewProps() {
|
|
|
2262
2389
|
}))
|
|
2263
2390
|
]);
|
|
2264
2391
|
}
|
|
2265
|
-
|
|
2266
|
-
//#region src/gen/pres/table-styles.ts
|
|
2267
|
-
/**
|
|
2268
|
-
* Create `ppt/tableStyles.xml`
|
|
2269
|
-
* @see: http://openxmldeveloper.org/discussions/formats/f/13/p/2398/8107.aspx
|
|
2270
|
-
* @return {string} XML
|
|
2271
|
-
*/
|
|
2272
|
-
function makeXmlTableStyles() {
|
|
2273
|
-
return XML_DECL + "\r\n" + voidEl("a:tblStyleLst", {
|
|
2274
|
-
"xmlns:a": OOXML_NS.a,
|
|
2275
|
-
def: "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}"
|
|
2276
|
-
});
|
|
2277
|
-
}
|
|
2278
|
-
//#endregion
|
|
2279
|
-
//#region src/gen/slide/layout.ts
|
|
2392
|
+
// src/gen/slide/layout.ts
|
|
2280
2393
|
/**
|
|
2281
2394
|
* pptx-ts: slide-layout part
|
|
2282
2395
|
*
|
|
@@ -2297,8 +2410,7 @@ function makeXmlLayout(layout, renderers) {
|
|
|
2297
2410
|
childPrefix: "\n "
|
|
2298
2411
|
});
|
|
2299
2412
|
}
|
|
2300
|
-
|
|
2301
|
-
//#region src/gen/slide/master.ts
|
|
2413
|
+
// src/gen/slide/master.ts
|
|
2302
2414
|
/**
|
|
2303
2415
|
* pptx-ts: slide-master parts
|
|
2304
2416
|
*
|
|
@@ -2436,6 +2548,7 @@ function masterAlignAttr(align) {
|
|
|
2436
2548
|
/** Build the bullet element for a master level: caller override wins over the level default. */
|
|
2437
2549
|
function masterBulletXml(bulletOverride, base) {
|
|
2438
2550
|
if (bulletOverride === false) return voidEl("a:buNone");
|
|
2551
|
+
if (bulletOverride === true && (base === "none" || base === void 0)) return masterBulletXml({}, base);
|
|
2439
2552
|
if (bulletOverride && typeof bulletOverride === "object") {
|
|
2440
2553
|
const font = bulletOverride.fontFace ? voidEl("a:buFont", { typeface: bulletOverride.fontFace }) : "";
|
|
2441
2554
|
if (bulletOverride.type === "number") {
|
|
@@ -2534,7 +2647,7 @@ function makeXmlMasterTxStyles(textStyles) {
|
|
|
2534
2647
|
* The built-in Office master's `<p:txStyles>`, used verbatim when `defineSlideMaster({ textStyles })`
|
|
2535
2648
|
* was never called. Deliberately NOT built via `masterLevelXml`: a configured title level always
|
|
2536
2649
|
* carries `marL="0"` (from `MASTER_TITLE_DEFAULT.marL`), but the true unconfigured default titleStyle
|
|
2537
|
-
* has no `marL`/`indent` attribute at all
|
|
2650
|
+
* has no `marL`/`indent` attribute at all: a pre-existing asymmetry between the two paths, preserved
|
|
2538
2651
|
* exactly rather than unified.
|
|
2539
2652
|
*/
|
|
2540
2653
|
function makeXmlMasterDefaultTxStyles() {
|
|
@@ -2624,8 +2737,7 @@ function makeXmlMasterRel(masterSlide, slideLayouts) {
|
|
|
2624
2737
|
});
|
|
2625
2738
|
return slideObjectRelationsToXml(masterSlide, defaultRels, SLIDE_MASTER_PATH);
|
|
2626
2739
|
}
|
|
2627
|
-
|
|
2628
|
-
//#region src/package/parts/shared.ts
|
|
2740
|
+
// src/package/parts/shared.ts
|
|
2629
2741
|
/**
|
|
2630
2742
|
* The contributors in emission order. Sorting here rather than trusting the caller's array is the
|
|
2631
2743
|
* whole point of {@link PartContributor.order}; the sort is stable, so two families that share a
|
|
@@ -2636,7 +2748,7 @@ function orderedContributors(contributors) {
|
|
|
2636
2748
|
}
|
|
2637
2749
|
/**
|
|
2638
2750
|
* Ask every contributor for its content-type entries and flatten them into the slot-keyed data
|
|
2639
|
-
* `makeXmlContTypes` emits. Takes the contributors already ordered
|
|
2751
|
+
* `makeXmlContTypes` emits. Takes the contributors already ordered; within a slot the entries
|
|
2640
2752
|
* come out in the order they are collected here.
|
|
2641
2753
|
*/
|
|
2642
2754
|
function collectContentTypes(contributors, pres) {
|
|
@@ -2668,15 +2780,14 @@ function collectContentTypes(contributors, pres) {
|
|
|
2668
2780
|
trailing
|
|
2669
2781
|
};
|
|
2670
2782
|
}
|
|
2671
|
-
|
|
2672
|
-
//#region src/package/assemble.ts
|
|
2783
|
+
// src/package/assemble.ts
|
|
2673
2784
|
/**
|
|
2674
2785
|
* Package assembly: turn an authored presentation's internal state into the full set of
|
|
2675
2786
|
* OOXML package parts and hand the bytes to the ZIP writer. This is the write-side
|
|
2676
|
-
* "packaging" layer
|
|
2677
|
-
* per-slide/layout/master parts, and media rels
|
|
2678
|
-
* (`
|
|
2679
|
-
* family adds on top of that skeleton
|
|
2787
|
+
* "packaging" layer (`[Content_Types].xml`, the `_rels` graph, docProps, theme, the
|
|
2788
|
+
* per-slide/layout/master parts, and media rels), split out of the authoring class
|
|
2789
|
+
* (`PptxTs`) so that class stays a façade over slide authoring. The parts a construct
|
|
2790
|
+
* family adds on top of that skeleton (charts, comments, notes) come from the part
|
|
2680
2791
|
* contributors it is handed, so nothing here names a family.
|
|
2681
2792
|
*
|
|
2682
2793
|
* The entry point `writePackage` takes a structural {@link PackageSource} that the
|
|
@@ -2717,7 +2828,7 @@ const ALREADY_COMPRESSED_MEDIA_EXTN = /* @__PURE__ */ new Set([
|
|
|
2717
2828
|
"oga"
|
|
2718
2829
|
]);
|
|
2719
2830
|
/**
|
|
2720
|
-
* Extensions whose payload is itself a ZIP archive
|
|
2831
|
+
* Extensions whose payload is itself a ZIP archive: the embedded OPC packages an OLE object can
|
|
2721
2832
|
* carry (`addOleObject`). Deflating a zip inside a zip buys nothing, so these are STOREd like the
|
|
2722
2833
|
* already-compressed media above. No media/image rel ever uses one of these extensions, so decks
|
|
2723
2834
|
* without an OLE object are unaffected.
|
|
@@ -2731,61 +2842,6 @@ const ZIP_CONTAINER_EXTN = /* @__PURE__ */ new Set([
|
|
|
2731
2842
|
"pptm"
|
|
2732
2843
|
]);
|
|
2733
2844
|
/**
|
|
2734
|
-
* Each slide's registered transition sound.
|
|
2735
|
-
*
|
|
2736
|
-
* Kept per slide, never on the transition: one transition object can be assigned to several slides,
|
|
2737
|
-
* and a relationship id means something only inside the slide part that declares it. Kept across
|
|
2738
|
-
* writes as well, so a deck written twice registers each sound once. An entry is reused only while
|
|
2739
|
-
* the slide still holds its relationship and still has the same sound assigned.
|
|
2740
|
-
*/
|
|
2741
|
-
const registeredTransitionSounds = /* @__PURE__ */ new WeakMap();
|
|
2742
|
-
/**
|
|
2743
|
-
* Register an audio media part + relationship for each slide-transition start sound
|
|
2744
|
-
* (`transition.sound` with `data`/`path`), and return the relationship id each slide's
|
|
2745
|
-
* `p:sndAc/p:snd r:embed` refers to. Runs before media encoding so the bytes are loaded. Re-export
|
|
2746
|
-
* is safe: a slide whose sound is already registered reuses that relationship. The stop-previous
|
|
2747
|
-
* form (`sound.stopPrevious`) needs no part and is skipped.
|
|
2748
|
-
*
|
|
2749
|
-
* Registered here, at write time, rather than when `slide.transition` is assigned. A slide's rel
|
|
2750
|
-
* ids and media part names are handed out in registration order, and a transition is often set
|
|
2751
|
-
* before the slide's pictures and media are added; registering the sound then would move every
|
|
2752
|
-
* later rel id and part name on the slide.
|
|
2753
|
-
*/
|
|
2754
|
-
function registerTransitionSounds(slides) {
|
|
2755
|
-
const soundRIds = /* @__PURE__ */ new Map();
|
|
2756
|
-
slides.forEach((slide) => {
|
|
2757
|
-
const transition = slide.transition;
|
|
2758
|
-
if (!transition || !TRANSITION_TYPES.includes(transition.type)) return;
|
|
2759
|
-
const sound = transition.sound;
|
|
2760
|
-
if (!sound || sound.stopPrevious) return;
|
|
2761
|
-
if (!sound.data && !sound.path) return;
|
|
2762
|
-
const registered = registeredTransitionSounds.get(slide);
|
|
2763
|
-
if (registered && slide._relsMedia.includes(registered.rel) && registered.data === sound.data && registered.path === sound.path) {
|
|
2764
|
-
soundRIds.set(slide, registered.rel.rId);
|
|
2765
|
-
return;
|
|
2766
|
-
}
|
|
2767
|
-
const dataMime = dataUriMediaType(sound.data ?? "");
|
|
2768
|
-
const pathFile = sound.path ? (sound.path.split("/").pop() ?? "").split("?")[0] ?? "" : "";
|
|
2769
|
-
const extn = dataMime?.startsWith("audio/") ? audioExtensionForSubtype(dataMime.slice(6)) : (pathFile.split(".").pop() ?? "wav").toLowerCase();
|
|
2770
|
-
const rId = getNewRelId(slide);
|
|
2771
|
-
const rel = pushMediaRel(slide, {
|
|
2772
|
-
kind: "audio",
|
|
2773
|
-
extn,
|
|
2774
|
-
type: `audio/${extn}`,
|
|
2775
|
-
path: sound.path,
|
|
2776
|
-
data: sound.data,
|
|
2777
|
-
rId
|
|
2778
|
-
});
|
|
2779
|
-
registeredTransitionSounds.set(slide, {
|
|
2780
|
-
rel,
|
|
2781
|
-
data: sound.data,
|
|
2782
|
-
path: sound.path
|
|
2783
|
-
});
|
|
2784
|
-
soundRIds.set(slide, rId);
|
|
2785
|
-
});
|
|
2786
|
-
return soundRIds;
|
|
2787
|
-
}
|
|
2788
|
-
/**
|
|
2789
2845
|
* Write the media parts one target's rels point at. Images are how every deck carries a picture,
|
|
2790
2846
|
* so this stays on the core path; the chart half of what used to be one function is a part
|
|
2791
2847
|
* contributor (`parts/chart.ts`) and runs just before it, per target, so part order is unchanged.
|
|
@@ -2804,8 +2860,7 @@ function createMediaParts(slide, zip, written) {
|
|
|
2804
2860
|
if (rel.type !== "online" && rel.type !== "hyperlink") {
|
|
2805
2861
|
const path = rel.Target.replace("..", "ppt");
|
|
2806
2862
|
if (written.has(path)) return;
|
|
2807
|
-
const
|
|
2808
|
-
const bytes = decodeBase64ToBytes(data);
|
|
2863
|
+
const bytes = decodeBase64ToBytes(rel.data);
|
|
2809
2864
|
if (!bytes) return;
|
|
2810
2865
|
const extn = (rel.extn || rel.Target.split(".").pop() || "").toLowerCase();
|
|
2811
2866
|
zip.add(path, bytes, { store: ALREADY_COMPRESSED_MEDIA_EXTN.has(extn) || ZIP_CONTAINER_EXTN.has(extn) });
|
|
@@ -2819,7 +2874,7 @@ function createMediaParts(slide, zip, written) {
|
|
|
2819
2874
|
* media de-dup, chart-part-id assignment, and measured-fit passes before the synchronous
|
|
2820
2875
|
* XML pass reads slide state. The bytes are the same the ZIP writer would compress; splitting the
|
|
2821
2876
|
* assembly from the zip lets the byte-identity harness (and a future parts API) read parts
|
|
2822
|
-
* directly. Only `onMediaError` is meaningful here
|
|
2877
|
+
* directly. Only `onMediaError` is meaningful here; compression/output shape are zip concerns
|
|
2823
2878
|
* handled by {@link zipPackageParts}.
|
|
2824
2879
|
*/
|
|
2825
2880
|
async function buildPackageParts(source, props) {
|
|
@@ -2838,16 +2893,16 @@ async function buildPackageParts(source, props) {
|
|
|
2838
2893
|
];
|
|
2839
2894
|
return await encodeMediaForTargets(mediaTargets, source.runtime, onMediaError).then(async () => {
|
|
2840
2895
|
const canonicalMediaTargets = /* @__PURE__ */ new Map();
|
|
2841
|
-
for (const target of mediaTargets) for (const rel of target._relsMedia
|
|
2842
|
-
if (rel.type === "online" || rel.type === "hyperlink" ||
|
|
2896
|
+
for (const target of mediaTargets) for (const rel of target._relsMedia) {
|
|
2897
|
+
if (rel.type === "online" || rel.type === "hyperlink" || !rel.data) continue;
|
|
2843
2898
|
if (rel.oleRelType) continue;
|
|
2844
|
-
const key =
|
|
2899
|
+
const key = rel.extn + "\0" + rel.data;
|
|
2845
2900
|
const canonical = canonicalMediaTargets.get(key);
|
|
2846
2901
|
if (canonical) rel.Target = canonical;
|
|
2847
2902
|
else canonicalMediaTargets.set(key, rel.Target);
|
|
2848
2903
|
}
|
|
2849
2904
|
let chartPartIdx = 0;
|
|
2850
|
-
for (const target of mediaTargets) for (const rel of target._relsChart
|
|
2905
|
+
for (const target of mediaTargets) for (const rel of target._relsChart) {
|
|
2851
2906
|
const chartId = ++chartPartIdx;
|
|
2852
2907
|
rel.globalId = chartId;
|
|
2853
2908
|
const { fileName, target } = chartPartNames(chartId, rel.isChartEx);
|
|
@@ -2876,7 +2931,7 @@ async function buildPackageParts(source, props) {
|
|
|
2876
2931
|
zip.add("ppt/theme/theme2.xml", themeXml);
|
|
2877
2932
|
zip.add(PRESENTATION_PATH, makeXmlPresentation(pres));
|
|
2878
2933
|
zip.add("ppt/presProps.xml", makeXmlPresProps());
|
|
2879
|
-
zip.add("ppt/tableStyles.xml", makeXmlTableStyles());
|
|
2934
|
+
zip.add("ppt/tableStyles.xml", makeXmlTableStyles(contributors.flatMap((contributor) => contributor.parts?.tableStyles?.(pres) ?? [])));
|
|
2880
2935
|
zip.add("ppt/viewProps.xml", makeXmlViewProps());
|
|
2881
2936
|
pres.slideLayouts.forEach((layout, idx) => {
|
|
2882
2937
|
zip.add(slideLayoutPath(idx + 1), makeXmlLayout(layout, source.renderers));
|
|
@@ -2909,7 +2964,7 @@ async function buildPackageParts(source, props) {
|
|
|
2909
2964
|
}
|
|
2910
2965
|
/**
|
|
2911
2966
|
* Zip an ordered list of package parts into the output shape `props.outputType` selects. Re-adds
|
|
2912
|
-
* each part to a fresh {@link ZipWriter} in order
|
|
2967
|
+
* each part to a fresh {@link ZipWriter} in order (preserving each part's `store` hint), so the
|
|
2913
2968
|
* archive is byte-identical to assembling straight into one writer. Compression and output type
|
|
2914
2969
|
* are the only zip-level knobs; part assembly happened in {@link buildPackageParts}.
|
|
2915
2970
|
*/
|
|
@@ -2928,8 +2983,7 @@ async function zipPackageParts(parts, props) {
|
|
|
2928
2983
|
async function writePackage(source, props) {
|
|
2929
2984
|
return await zipPackageParts(await buildPackageParts(source, props), props);
|
|
2930
2985
|
}
|
|
2931
|
-
|
|
2932
|
-
//#region src/presentation.ts
|
|
2986
|
+
// src/presentation.ts
|
|
2933
2987
|
/**
|
|
2934
2988
|
* pptx-ts: the presentation core
|
|
2935
2989
|
*
|
|
@@ -2937,14 +2991,14 @@ async function writePackage(source, props) {
|
|
|
2937
2991
|
* adapter (`index.ts`, `node.ts`, `browser.ts`). See the class doc below for the layout of
|
|
2938
2992
|
* its body and where package assembly actually happens.
|
|
2939
2993
|
*
|
|
2940
|
-
* Units: this library takes user coordinates in inches and converts to EMU on output
|
|
2994
|
+
* Units: this library takes user coordinates in inches and converts to EMU on output. See
|
|
2941
2995
|
* `units.ts` for the conversions and `STANDARD_LAYOUTS` for the built-in slide sizes.
|
|
2942
2996
|
*
|
|
2943
2997
|
* Derived from PptxGenJS (C) 2015-present Brent Ely (MIT), which in turn took some code from
|
|
2944
2998
|
* the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
|
|
2945
2999
|
* license text is in `LICENSE` at the repository root.
|
|
2946
3000
|
*/
|
|
2947
|
-
const VERSION = "
|
|
3001
|
+
const VERSION = "5.0.0";
|
|
2948
3002
|
function standardLayoutToPresLayout(layout) {
|
|
2949
3003
|
return {
|
|
2950
3004
|
name: layout.name,
|
|
@@ -2973,8 +3027,8 @@ function layoutSideInches(value, side) {
|
|
|
2973
3027
|
return clampRangedInput(Number(value), 1, 56, "layout/size-out-of-range", `defineLayout ${side} (inches)`, "coord/non-finite");
|
|
2974
3028
|
}
|
|
2975
3029
|
/**
|
|
2976
|
-
* Main presentation class and package export flow
|
|
2977
|
-
* instantiate (`new
|
|
3030
|
+
* Main presentation class and package export flow: the public entry point consumers
|
|
3031
|
+
* instantiate (`new PptxTs()`). Owns presentation-level state and metadata, collects
|
|
2978
3032
|
* slides, and drives `write`/`writeFile`/`stream`. The actual OOXML string building is
|
|
2979
3033
|
* delegated to the `gen-*` modules; runtime file/stream output goes through the injected
|
|
2980
3034
|
* `RuntimeAdapter`. Enums (`AlignH`, `ChartType`, `ShapeType`, …) are imported from the
|
|
@@ -2983,12 +3037,12 @@ function layoutSideInches(value, side) {
|
|
|
2983
3037
|
* Rough layout of the class body:
|
|
2984
3038
|
* - Metadata accessors layout / author / company / title / theme / sections getters+setters
|
|
2985
3039
|
* - Private slide helpers addNewSlide, getSlide, setSlideNumber
|
|
2986
|
-
* - Public export methods stream / write / writeFile
|
|
3040
|
+
* - Public export methods stream / write / writeFile; delegate packaging to `writePackage`
|
|
2987
3041
|
* - Public authoring methods addSlide / defineLayout / defineSlideMaster / defineTableStyle
|
|
2988
3042
|
*
|
|
2989
3043
|
* Package assembly (`[Content_Types].xml`, the rels graph, per-part XML, the ZIP) lives in
|
|
2990
3044
|
* `package/assemble.ts`; this class provides the deck state via {@link PresentationCore.packageSource}.
|
|
2991
|
-
* The live-DOM `tableToSlides()` is NOT here
|
|
3045
|
+
* The live-DOM `tableToSlides()` is NOT here; it is added by the browser entry subclass
|
|
2992
3046
|
* (`browser.ts`) so it stays out of the Node build and out of the core chunk.
|
|
2993
3047
|
*/
|
|
2994
3048
|
var PresentationCore = class {
|
|
@@ -3124,7 +3178,7 @@ var PresentationCore = class {
|
|
|
3124
3178
|
/**
|
|
3125
3179
|
* slide layout definition objects, used for generating slide layout files.
|
|
3126
3180
|
* `protected` (not `private`) so the browser entry subclass can resolve
|
|
3127
|
-
* `tableToSlides({ masterTitle })` against them
|
|
3181
|
+
* `tableToSlides({ masterTitle })` against them. That method lives on the
|
|
3128
3182
|
* browser build only (it reads a live DOM), see `browser.ts`.
|
|
3129
3183
|
*/
|
|
3130
3184
|
_slideLayouts;
|
|
@@ -3153,7 +3207,7 @@ var PresentationCore = class {
|
|
|
3153
3207
|
LAYOUTS;
|
|
3154
3208
|
/**
|
|
3155
3209
|
* The built-in default layout, or a throw. `LAYOUTS` is a string-keyed map, so the lookup is
|
|
3156
|
-
* typed as possibly-missing at every call site even though the constructor registers it
|
|
3210
|
+
* typed as possibly-missing at every call site even though the constructor registers it, and
|
|
3157
3211
|
* the two callers that need it were spelling the same guard and the same message.
|
|
3158
3212
|
*/
|
|
3159
3213
|
#requireDefaultLayout() {
|
|
@@ -3281,14 +3335,14 @@ var PresentationCore = class {
|
|
|
3281
3335
|
* Author + serialize this presentation's slides as injectable descriptors,
|
|
3282
3336
|
* WITHOUT producing a `.pptx` package. Runs the same media-encode, placeholder
|
|
3283
3337
|
* backfill, and measured-fit passes `write()` uses, then serializes each slide
|
|
3284
|
-
* body and resolves its image media to decoded bytes
|
|
3338
|
+
* body and resolves its image media to decoded bytes, so a loaded deck can
|
|
3285
3339
|
* splice the slides in via `Presentation.appendSlides()` (see `pptx-ts/read`)
|
|
3286
3340
|
* while keeping its own masters/layouts/theme byte-identical.
|
|
3287
3341
|
*
|
|
3288
3342
|
* Returns the deck's slide size (EMU, for the destination size check) and one
|
|
3289
3343
|
* descriptor per authored slide. Charts, audio/video media, online media, and
|
|
3290
3344
|
* internal slide-to-slide hyperlinks are surfaced as descriptors alongside the
|
|
3291
|
-
* serialized body
|
|
3345
|
+
* serialized body. This method does not resolve them into a package; the append
|
|
3292
3346
|
* path consumes each descriptor to reserve the parts and rebuild the rel graph.
|
|
3293
3347
|
*/
|
|
3294
3348
|
extractSlides = async (opts = {}) => extractSlides({
|
|
@@ -3391,11 +3445,11 @@ var PresentationCore = class {
|
|
|
3391
3445
|
}
|
|
3392
3446
|
/**
|
|
3393
3447
|
* Measure how tall text wraps at a given width, using the **same** calibrated wrap
|
|
3394
|
-
* model the export-time autofit bake uses
|
|
3448
|
+
* model the export-time autofit bake uses, so a layout-time prediction matches the
|
|
3395
3449
|
* value `fit:'shrink'`/`'resize'` would bake. Synchronous: register the face's metrics
|
|
3396
|
-
* first with {@link
|
|
3450
|
+
* first with {@link PptxTs.registerFontMetrics} (lookup is sync).
|
|
3397
3451
|
*
|
|
3398
|
-
* Lets a consumer size its own geometry before export
|
|
3452
|
+
* Lets a consumer size its own geometry before export: grow a card to fit its text,
|
|
3399
3453
|
* reflow a grid, or detect overflow at layout time. Heights err **tall** (conservative),
|
|
3400
3454
|
* so the returned height is ≥ what PowerPoint/LibreOffice render.
|
|
3401
3455
|
* @param {string | TextProps[]} text - a string or run array (per-run options override the defaults in `opts`)
|
|
@@ -3410,7 +3464,7 @@ var PresentationCore = class {
|
|
|
3410
3464
|
/**
|
|
3411
3465
|
* Convenience overflow check: `true` if `text` does not fit a box of inner size
|
|
3412
3466
|
* `wIn`×`hIn` (inches) at full size. Because the model errs tall, this is a
|
|
3413
|
-
* **conservative** (slightly over-reporting) check
|
|
3467
|
+
* **conservative** (slightly over-reporting) check, appropriate for a build-time
|
|
3414
3468
|
* warning, not a hard gate. An unmeasurable (unnamed theme-default) face reports
|
|
3415
3469
|
* `false` (no overflow) so the linter does not false-positive on faces it cannot measure.
|
|
3416
3470
|
* @param {string | TextProps[]} text - a string or run array
|
|
@@ -3422,13 +3476,13 @@ var PresentationCore = class {
|
|
|
3422
3476
|
}
|
|
3423
3477
|
/**
|
|
3424
3478
|
* Compute the per-cell geometry of a table laid out at `opts.x`/`y`/`w`, without
|
|
3425
|
-
* adding it to a slide
|
|
3479
|
+
* adding it to a slide, so a consumer can place images or shapes precisely over
|
|
3426
3480
|
* cells. Column widths (cell `x`/`w`) are exact, derived from the same logic the
|
|
3427
3481
|
* writer uses. Row heights (`y`/`h`) are exact when pinned by `rowH` (array or
|
|
3428
3482
|
* scalar) or table `h`; an auto-height row is estimated with the same conservative
|
|
3429
|
-
* (tall) text model as {@link
|
|
3430
|
-
* (register the cell font via {@link
|
|
3431
|
-
* estimate). Geometry is for a single, un-paginated table
|
|
3483
|
+
* (tall) text model as {@link PptxTs.measureText} and flagged `heightExact:false`
|
|
3484
|
+
* (register the cell font via {@link PptxTs.registerFontMetrics} for an exact
|
|
3485
|
+
* estimate). Geometry is for a single, un-paginated table; `autoPage` paging is
|
|
3432
3486
|
* not modeled.
|
|
3433
3487
|
* @param {TableRow[]} rows - the same `rows` passed to `slide.addTable`
|
|
3434
3488
|
* @param {TableProps} opts - the same table options (`x`, `y`, `w`, `colW`, `rowH`, `h`, …)
|
|
@@ -3470,7 +3524,7 @@ var PresentationCore = class {
|
|
|
3470
3524
|
]));
|
|
3471
3525
|
}
|
|
3472
3526
|
/**
|
|
3473
|
-
* Assemble the current Presentation into its OOXML package parts WITHOUT zipping
|
|
3527
|
+
* Assemble the current Presentation into its OOXML package parts WITHOUT zipping: the raw
|
|
3474
3528
|
* `path → bytes` contents `write()` would compress into the `.pptx`. Use this to stream parts
|
|
3475
3529
|
* into a custom container, inspect individual parts, or feed a pipeline that does its own
|
|
3476
3530
|
* archiving. To produce a real `.pptx` file, use {@link write} / {@link writeFile} instead.
|
|
@@ -3649,8 +3703,7 @@ var PresentationCore = class {
|
|
|
3649
3703
|
if (newLayout._slideNumberProps && !this._masterSlide._slideNumberProps) this._masterSlide._slideNumberProps = newLayout._slideNumberProps;
|
|
3650
3704
|
}
|
|
3651
3705
|
};
|
|
3652
|
-
|
|
3653
|
-
//#region src/families/core.ts
|
|
3706
|
+
// src/families/core.ts
|
|
3654
3707
|
/**
|
|
3655
3708
|
* pptx-ts: the construct families every presentation carries
|
|
3656
3709
|
*
|
|
@@ -3671,8 +3724,7 @@ const CORE_CONSTRUCT_FAMILIES = [
|
|
|
3671
3724
|
groupFamily,
|
|
3672
3725
|
notesFamily
|
|
3673
3726
|
];
|
|
3674
|
-
|
|
3675
|
-
//#region src/entry-compose.ts
|
|
3727
|
+
// src/entry-compose.ts
|
|
3676
3728
|
/**
|
|
3677
3729
|
* pptx-ts: composing a presentation from the construct families it actually needs
|
|
3678
3730
|
*
|
|
@@ -3685,7 +3737,7 @@ const CORE_CONSTRUCT_FAMILIES = [
|
|
|
3685
3737
|
* three-condition treatment `.` gets, so three more source entries, three more `dist` files, three
|
|
3686
3738
|
* more budget rows and a second place for the entries to diverge -- and it would buy nothing that
|
|
3687
3739
|
* export-level shaking does not already give. `pptx-ts` is an intermediate module in a consumer's
|
|
3688
|
-
* graph: a program that imports only `createPresentation` drops the `
|
|
3740
|
+
* graph: a program that imports only `createPresentation` drops the `PptxTs` class beside it and,
|
|
3689
3741
|
* with it, every family that class names.
|
|
3690
3742
|
*/
|
|
3691
3743
|
/**
|
|
@@ -3699,13 +3751,12 @@ const CORE_CONSTRUCT_FAMILIES = [
|
|
|
3699
3751
|
function composePresentation(runtime, opts) {
|
|
3700
3752
|
return new PresentationCore(runtime, [...CORE_CONSTRUCT_FAMILIES, ...opts?.use ?? []]);
|
|
3701
3753
|
}
|
|
3702
|
-
|
|
3703
|
-
//#region src/entry-families.ts
|
|
3754
|
+
// src/entry-families.ts
|
|
3704
3755
|
/**
|
|
3705
3756
|
* pptx-ts: the construct families the three main entries are composed with
|
|
3706
3757
|
*
|
|
3707
3758
|
* The full authoring surface, as a list: `index.ts`, `node.ts` and `browser.ts` each compose every
|
|
3708
|
-
* family, so `
|
|
3759
|
+
* family, so `PptxTs` authors everything it ever has; the browser entry adds the table family's
|
|
3709
3760
|
* live-DOM half on top, because that one needs a `document` to read. The families a *composed*
|
|
3710
3761
|
* presentation gets without asking are the shorter list in `families/core.ts`.
|
|
3711
3762
|
*
|
|
@@ -3739,14 +3790,13 @@ const ALL_CONSTRUCT_FAMILIES = /* @__PURE__ */ Object.freeze([
|
|
|
3739
3790
|
tableFamily,
|
|
3740
3791
|
zoomFamily
|
|
3741
3792
|
]);
|
|
3742
|
-
|
|
3743
|
-
//#region src/runtime/fetch-media.ts
|
|
3793
|
+
// src/runtime/fetch-media.ts
|
|
3744
3794
|
/**
|
|
3745
3795
|
* The parts of a runtime adapter that are not runtime-specific.
|
|
3746
3796
|
*
|
|
3747
3797
|
* `fetch`, `btoa` and `TextEncoder` exist in Node, in every browser, and in the edge/worker
|
|
3748
3798
|
* runtimes the neutral adapter serves, so the remote-load paths were the same code in all three
|
|
3749
|
-
* adapters
|
|
3799
|
+
* adapters: byte-identical in two of them and an inline branch in the third. What is genuinely
|
|
3750
3800
|
* per-runtime stays in each adapter: `node:fs`, `FileReader`, the canvas rasterizer, the
|
|
3751
3801
|
* `<a download>` writer.
|
|
3752
3802
|
*
|
|
@@ -3755,13 +3805,13 @@ const ALL_CONSTRUCT_FAMILIES = /* @__PURE__ */ Object.freeze([
|
|
|
3755
3805
|
/**
|
|
3756
3806
|
* Whether `source` names something to `fetch` rather than something to read from a filesystem.
|
|
3757
3807
|
*
|
|
3758
|
-
* A real scheme test, not `source.startsWith('http')`. That prefix matched a *relative path*
|
|
3759
|
-
* `httpdocs/logo.png`, `http-icons/x.svg`
|
|
3808
|
+
* A real scheme test, not `source.startsWith('http')`. That prefix matched a *relative path*
|
|
3809
|
+
* (`httpdocs/logo.png`, `http-icons/x.svg`) and sent it to `fetch`, which then failed with a
|
|
3760
3810
|
* fetch error naming a file that exists on disk. It was also case-sensitive, so
|
|
3761
3811
|
* `HTTPS://example.com/f.ttf` missed and went to `fs.readFile`.
|
|
3762
3812
|
*
|
|
3763
3813
|
* Only `http:` and `https:` count. A `file:` or `data:` URL falls through to the adapter's
|
|
3764
|
-
* local branch, where it fails
|
|
3814
|
+
* local branch, where it fails, the same as before this was tightened, and deliberately left
|
|
3765
3815
|
* that way: making either work is a feature with its own decisions (what a `file:` URL means
|
|
3766
3816
|
* off Node, whether a `data:` URL should bypass loading entirely), not a side effect of fixing
|
|
3767
3817
|
* a prefix test.
|
|
@@ -3777,7 +3827,7 @@ async function fetchFontBytes(source) {
|
|
|
3777
3827
|
return new Uint8Array(await response.arrayBuffer());
|
|
3778
3828
|
}
|
|
3779
3829
|
/**
|
|
3780
|
-
* Fetch a media file as raw base64 (no `data:` prefix)
|
|
3830
|
+
* Fetch a media file as raw base64 (no `data:` prefix). See {@link RuntimeAdapter.loadMedia}
|
|
3781
3831
|
* for why both encodings are accepted.
|
|
3782
3832
|
*
|
|
3783
3833
|
* Encodes through `bytesToBase64` rather than `Buffer`, so the same function serves a runtime
|
|
@@ -3802,16 +3852,15 @@ async function placeholderSvgPreview(rel) {
|
|
|
3802
3852
|
rel.data = IMG_SVG_PLACEHOLDER;
|
|
3803
3853
|
return null;
|
|
3804
3854
|
}
|
|
3805
|
-
|
|
3806
|
-
//#region src/clip.ts
|
|
3855
|
+
// src/clip.ts
|
|
3807
3856
|
/**
|
|
3808
3857
|
* pptx-ts: named clip silhouettes for images
|
|
3809
3858
|
*
|
|
3810
|
-
* A `ClipShape` is declarative data
|
|
3859
|
+
* A `ClipShape` is declarative data (a named silhouette plus its options), and {@link clipPath}
|
|
3811
3860
|
* resolves it to the freeform `points` path `addImage` emits as the `<a:custGeom>` clip mask on
|
|
3812
3861
|
* the `<p:pic>`. Paired with `sizing: { type: 'cover' }` (an `<a:srcRect>` crop) it reproduces
|
|
3813
|
-
* what a PowerPoint *picture placeholder* does
|
|
3814
|
-
* blipFill
|
|
3862
|
+
* what a PowerPoint *picture placeholder* does (a layout `custGeom` clipping an inherited
|
|
3863
|
+
* blipFill), but standalone, with no placeholder involved.
|
|
3815
3864
|
*
|
|
3816
3865
|
* These are silhouettes recurring often enough in real decks to be worth naming; hand-authored
|
|
3817
3866
|
* `points` remain available for anything else. Nothing here is theme- or brand-aware: which
|
|
@@ -3941,7 +3990,7 @@ function halfDisc(w, h, flat, preset) {
|
|
|
3941
3990
|
* to `addImage({ points })`.
|
|
3942
3991
|
*
|
|
3943
3992
|
* `w`/`h` are the image box's size **in inches** and must match the `w`/`h` the picture is drawn
|
|
3944
|
-
* at
|
|
3993
|
+
* at, because the path is emitted in the box's own coordinate space, not normalized (see the module
|
|
3945
3994
|
* note above).
|
|
3946
3995
|
* @param {ClipShape} shape - the named silhouette and its options
|
|
3947
3996
|
* @param {number} w - image box width (inches)
|
|
@@ -3963,8 +4012,7 @@ function clipPath(shape, w, h) {
|
|
|
3963
4012
|
case "half-disc": return halfDisc(w, h, checkEnumOrThrow(shape.flat, FLAT_SIDES, "clipPath `flat`", "clip/invalid-shape"), checkEnumOrThrow(shape.preset ?? "deep", HALF_DISC_PRESETS, "clipPath `preset`", "clip/invalid-shape"));
|
|
3964
4013
|
}
|
|
3965
4014
|
}
|
|
3966
|
-
|
|
3967
|
-
//#region src/types/text.ts
|
|
4015
|
+
// src/types/text.ts
|
|
3968
4016
|
/** Factory for a single inline text run. Prevents `as never` casts when building mixed-style run arrays. */
|
|
3969
4017
|
function textRun(text, options) {
|
|
3970
4018
|
return options !== void 0 ? {
|
|
@@ -3976,7 +4024,6 @@ function textRun(text, options) {
|
|
|
3976
4024
|
function textRuns(runs) {
|
|
3977
4025
|
return runs;
|
|
3978
4026
|
}
|
|
3979
|
-
//#endregion
|
|
3980
4027
|
export { fetchMediaBase64 as a, ALL_CONSTRUCT_FAMILIES as c, IMG_SVG_PLACEHOLDER as d, fetchFontBytes as i, composePresentation as l, textRuns as n, isRemote as o, clipPath as r, placeholderSvgPreview as s, textRun as t, PresentationCore as u };
|
|
3981
4028
|
|
|
3982
|
-
//# sourceMappingURL=text-
|
|
4029
|
+
//# sourceMappingURL=text-Dz0ArGIp.js.map
|