@shbernal/ts-pptx 3.2.0 → 3.3.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 +19 -20
- package/dist/{autopage-QQyfRtoK.js → autopage-D1sqK6j7.js} +16 -15
- package/dist/{autopage-QQyfRtoK.js.map → autopage-D1sqK6j7.js.map} +1 -1
- package/dist/browser.d.ts +6 -6
- package/dist/browser.js +5 -6
- package/dist/browser.js.map +1 -1
- package/dist/{diagnostics-Chjz_4_Q.d.ts → diagnostics-B2yWpcjB.d.ts} +31 -14
- package/dist/{diagnostics-Chjz_4_Q.d.ts.map → diagnostics-B2yWpcjB.d.ts.map} +1 -1
- package/dist/{diagnostics-DYQC7xLV.js → diagnostics-H1QzgRSw.js} +0 -0
- package/dist/{diagnostics-DYQC7xLV.js.map → diagnostics-H1QzgRSw.js.map} +1 -1
- package/dist/{enums-CoXnxiCI.d.ts → enums-CvRYniUA.d.ts} +13 -3
- package/dist/{enums-CoXnxiCI.d.ts.map → enums-CvRYniUA.d.ts.map} +1 -1
- package/dist/{errors-BVLCFr-t.d.ts → errors-fB1KDJKQ.d.ts} +5 -5
- package/dist/{errors-BVLCFr-t.d.ts.map → errors-fB1KDJKQ.d.ts.map} +1 -1
- package/dist/{fit-DhMbQG5O.js → fit-CYJ1jkQI.js} +0 -0
- package/dist/fit-CYJ1jkQI.js.map +1 -0
- package/dist/{html-dom-DajBuobe.js → html-dom-CzewEdE1.js} +4 -5
- package/dist/{html-dom-DajBuobe.js.map → html-dom-CzewEdE1.js.map} +1 -1
- package/dist/html.d.ts +3 -3
- package/dist/html.js +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +1 -1
- package/dist/inspect.js +4 -4
- package/dist/math.d.ts +1 -1
- package/dist/measure.d.ts +96 -6
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +2 -2
- package/dist/node.d.ts +6 -6
- package/dist/node.js +4 -5
- package/dist/node.js.map +1 -1
- package/dist/notes-SCvpJz0l.js +1646 -0
- package/dist/notes-SCvpJz0l.js.map +1 -0
- package/dist/{pres-C-EU3SkN.d.ts → pres-Cb-1xDne.d.ts} +4 -4
- package/dist/{pres-C-EU3SkN.d.ts.map → pres-Cb-1xDne.d.ts.map} +1 -1
- package/dist/{presentation-CxnUVhK9.js → presentation-DeKTJGHf.js} +1002 -422
- package/dist/presentation-DeKTJGHf.js.map +1 -0
- package/dist/{presentation-Osx1cDdJ.d.ts → presentation-types-B50aOUFI.d.ts} +679 -11
- package/dist/presentation-types-B50aOUFI.d.ts.map +1 -0
- package/dist/read.d.ts +4 -5
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +2 -2
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +0 -0
- package/dist/script.js.map +1 -1
- package/dist/{shapes-DoJBPrwN.js → shapes-BXVgsk0F.js} +3 -3
- package/dist/{shapes-DoJBPrwN.js.map → shapes-BXVgsk0F.js.map} +1 -1
- package/dist/{slide-BTfn1Pyp.d.ts → slide-DWw9UoI6.d.ts} +39 -3
- package/dist/slide-DWw9UoI6.d.ts.map +1 -0
- package/dist/{st-enums-evQG2A7Z.js → st-enums-bVnezkMJ.js} +33 -12
- package/dist/{st-enums-evQG2A7Z.js.map → st-enums-bVnezkMJ.js.map} +1 -1
- package/dist/{text-C4P9MNdf.js → text-uM-zGton.js} +219 -1668
- package/dist/text-uM-zGton.js.map +1 -0
- package/dist/{units-internal-CR8s8YQa.js → units-internal-CGf4VrXy.js} +156 -4
- package/dist/units-internal-CGf4VrXy.js.map +1 -0
- package/dist/zip.d.ts +2 -2
- package/package.json +36 -27
- package/dist/embedded-fonts-MaynAHa9.js +0 -94
- package/dist/embedded-fonts-MaynAHa9.js.map +0 -1
- package/dist/fit-DhMbQG5O.js.map +0 -1
- package/dist/presentation-CxnUVhK9.js.map +0 -1
- package/dist/presentation-Osx1cDdJ.d.ts.map +0 -1
- package/dist/presentation-types-Dj4riuDs.d.ts +0 -538
- package/dist/presentation-types-Dj4riuDs.d.ts.map +0 -1
- package/dist/slide-BTfn1Pyp.d.ts.map +0 -1
- package/dist/text-C4P9MNdf.js.map +0 -1
- package/dist/units-C7QM1KgH.js +0 -144
- package/dist/units-C7QM1KgH.js.map +0 -1
- package/dist/units-internal-CR8s8YQa.js.map +0 -1
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { n as warn, r as warnOnce } from "./diagnostics-
|
|
1
|
+
import { n as warn, r as warnOnce } from "./diagnostics-H1QzgRSw.js";
|
|
2
2
|
import { n as InvalidOptionError, o as UnsupportedFeatureError, r as MediaError, t as InternalError } from "./errors-Bvln0H-7.js";
|
|
3
|
-
import { a as
|
|
4
|
-
import {
|
|
5
|
-
import { E as svgMarkupToDataUri, S as getImageSizeFromBase64, T as decodeBase64ToBytes, a as PRESET_MATERIALS, g as PACKAGE_REL_NS, h as OFFICE_REL, i as PRESET_LINE_DASHES, n as LIGHT_RIGS, r as LIGHT_RIG_DIRECTIONS, s as TEXT_HORZ_OVERFLOW, t as BEVEL_PRESETS, u as checkEnumOrWarn, x as fitSrcRectPercents } from "./st-enums-
|
|
6
|
-
import { t as getSlidesForTableRows } from "./autopage-
|
|
7
|
-
import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit } from "./fit-
|
|
8
|
-
import { a as flattenEmbeddedFaces, i as FONT_REL_TYPE, n as FONT_DATA_CONTENT_TYPE, o as serializeEmbeddedFontLst, r as FONT_DATA_EXTENSION, t as EMBEDDED_FONT_SLOTS } from "./embedded-fonts-MaynAHa9.js";
|
|
3
|
+
import { $ as connectorPresetFor, C as DEF_PRES_LAYOUT_NAME, E as DEF_SLIDE_MARGIN_IN, F as PIECHART_COLORS, G as PlaceholderType, I as REGEX_HEX_COLOR, K as SchemeColor, L as SLDNUMFLDID, M as LAYOUT_IDX_SERIES_BASE, N as LETTERS, O as DEF_TEXT_SHADOW, Q as asChartType, R as XML_DECL, S as DEF_PRES_LAYOUT, T as DEF_SHAPE_SHADOW, U as ChartType, Z as VALID_SHAPE_PRESETS, _ as DEF_CELL_BORDER, _t as pixelsToEmu, a as lineWidthToEmu, at as EMU_PER_POINT, b as DEF_CHART_GRIDLINE, c as resolveTableColWidthsEmu, ct as PERCENT_SCALE, d as AXIS_ID_CATEGORY_PRIMARY, et as isChartExType, f as AXIS_ID_CATEGORY_SECONDARY, ft as emuToInches, g as BARCHART_COLORS, h as AXIS_ID_VALUE_SECONDARY, ht as inchesToEmu, i as inch2Emu, it as EMU_PER_INCH, k as IMG_BROKEN, m as AXIS_ID_VALUE_PRIMARY, n as convertRotationDegrees, o as marginToEmu, ot as FIXED_PCT_PER_PERCENT, p as AXIS_ID_SERIES_PRIMARY, r as getSmartParseNumber, t as convertArcAngle, tt as isChartType, u as valToPts, ut as STANDARD_LAYOUTS, v as DEF_CELL_MARGIN_IN, x as DEF_FONT_COLOR, y as DEF_CHART_BORDER, yt as ptToHundredths } from "./units-internal-CGf4VrXy.js";
|
|
4
|
+
import { A as getNewRelId, C as createColorElement, D as encodeXmlAttrValue, E as voidEl, M as isHyperlinkRel, N as validateObjectName, O as encodeXmlEntities, S as createShadowEffectLst, T as raw, _ as FONT_DATA_EXTENSION, a as makeXmlNotesSlideRel, b as serializeEmbeddedFontLst, c as genXmlParagraphProperties, d as clampFontSizeSz, f as genXmlColorSelection, g as FONT_DATA_CONTENT_TYPE, h as EMBEDDED_FONT_SLOTS, i as makeXmlNotesSlide, j as getUuid, k as getDuplicateObjectNames, l as groupRunsIntoLines, m as genXmlPatternFill, n as makeXmlNotesMaster, p as genXmlGradientFill, r as makeXmlNotesMasterRel, s as genXmlNormAutofit, t as buildNotesSlideRels, u as renderTextParagraphsXml, v as FONT_REL_TYPE, w as el, x as correctShadowOptions, y as flattenEmbeddedFaces } from "./notes-SCvpJz0l.js";
|
|
5
|
+
import { E as svgMarkupToDataUri, S as getImageSizeFromBase64, T as decodeBase64ToBytes, a as PRESET_MATERIALS, g as PACKAGE_REL_NS, h as OFFICE_REL, i as PRESET_LINE_DASHES, n as LIGHT_RIGS, r as LIGHT_RIG_DIRECTIONS, s as TEXT_HORZ_OVERFLOW, t as BEVEL_PRESETS, u as checkEnumOrWarn, x as fitSrcRectPercents } from "./st-enums-bVnezkMJ.js";
|
|
6
|
+
import { t as getSlidesForTableRows } from "./autopage-D1sqK6j7.js";
|
|
7
|
+
import { _ as parseFontMetrics, m as FontMetricsRegistry, n as measureText, r as computeTableLayout, t as applyMeasuredFit, v as isFontCollection } from "./fit-CYJ1jkQI.js";
|
|
9
8
|
import { ZipWriter } from "./zip.js";
|
|
10
9
|
//#region src/media/content-type.ts
|
|
11
10
|
/**
|
|
@@ -164,400 +163,6 @@ function addBackgroundDefinition(props, target) {
|
|
|
164
163
|
}
|
|
165
164
|
}
|
|
166
165
|
//#endregion
|
|
167
|
-
//#region src/gen/utils.ts
|
|
168
|
-
/**
|
|
169
|
-
* ts-pptx: shared generator helpers.
|
|
170
|
-
*
|
|
171
|
-
* The small cross-cutting pieces the OOXML writers all need and that belong to
|
|
172
|
-
* no single part:
|
|
173
|
-
* - XML text encodeXmlEntities
|
|
174
|
-
* - Identifiers & naming getUuid, validateObjectName, getDuplicateObjectNames
|
|
175
|
-
* - Slide relationships getNewRelId, isHyperlinkRel
|
|
176
|
-
*
|
|
177
|
-
* DrawingML fragment builders moved to `gen/drawingml/{color,effect,fill,line}.ts`;
|
|
178
|
-
* unit conversion to `units-internal.ts` (over the public primitives in `units.ts`);
|
|
179
|
-
* base64/image-header decoding and media content types to `media/`.
|
|
180
|
-
*/
|
|
181
|
-
/**
|
|
182
|
-
* Basic UUID Generator Adapted
|
|
183
|
-
* @link https://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript#answer-2117523
|
|
184
|
-
* @param {string} uuidFormat - UUID format
|
|
185
|
-
* @returns {string} UUID
|
|
186
|
-
*/
|
|
187
|
-
function getUuid(uuidFormat) {
|
|
188
|
-
return uuidFormat.replace(/[xy]/g, function(c) {
|
|
189
|
-
const r = Math.random() * 16 | 0;
|
|
190
|
-
return (c === "x" ? r : r & 3 | 8).toString(16);
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Replace special XML characters with HTML-encoded strings.
|
|
195
|
-
*
|
|
196
|
-
* ELEMENT TEXT ONLY. A literal tab, carriage return or line feed is left alone here because in
|
|
197
|
-
* character data it *is* the content (`<a:t>` line breaks depend on it). Inside an attribute value
|
|
198
|
-
* the same three characters are destroyed by the parser rather than preserved — see
|
|
199
|
-
* {@link encodeXmlAttrValue}, which every attribute-emitting path must use instead.
|
|
200
|
-
* @param {string | number} xml - value to encode (numbers are stringified, as callers pass counts/sizes)
|
|
201
|
-
* @returns {string} escaped XML
|
|
202
|
-
*/
|
|
203
|
-
function encodeXmlEntities(xml) {
|
|
204
|
-
if (typeof xml === "undefined" || xml == null) return "";
|
|
205
|
-
const cc = String.fromCharCode;
|
|
206
|
-
const illegalXmlCharsRe = new RegExp(`[${cc(0)}-${cc(8)}${cc(11)}${cc(12)}${cc(14)}-${cc(31)}${cc(127)}]`, "g");
|
|
207
|
-
return xml.toString().replace(illegalXmlCharsRe, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Escape a value destined for an XML **attribute**.
|
|
211
|
-
*
|
|
212
|
-
* Everything {@link encodeXmlEntities} does, plus the three whitespace characters that survive in
|
|
213
|
-
* element text but not in an attribute: XML 1.0 section 3.3.3 requires a parser to normalise a
|
|
214
|
-
* literal tab, carriage return or line feed inside an attribute value to a single space *before any
|
|
215
|
-
* consumer sees it*. Carrying one across therefore requires a character reference, so a caller's
|
|
216
|
-
* `objectName: 'Abschnitts-\nuberschrift'` reads back with its line break rather than a space.
|
|
217
|
-
*
|
|
218
|
-
* Deliberately a separate function rather than a widening of `encodeXmlEntities`: that helper also
|
|
219
|
-
* escapes element text, where a raw newline is meaningful content emitted as-is, and escaping it
|
|
220
|
-
* there would change bytes across every text-bearing part in the package.
|
|
221
|
-
* @param {string | number} xml - value to encode (numbers are stringified, as callers pass ids/sizes)
|
|
222
|
-
* @returns {string} escaped XML, safe to place between attribute quotes
|
|
223
|
-
*/
|
|
224
|
-
function encodeXmlAttrValue(xml) {
|
|
225
|
-
return encodeXmlEntities(xml).replace(/\t/g, "	").replace(/\n/g, " ").replace(/\r/g, " ");
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Practical maximum length for a `p:cNvPr` object name. PowerPoint does not
|
|
229
|
-
* enforce a hard spec limit, but very long names are a strong signal of a bug
|
|
230
|
-
* and are unwieldy in the Selection Pane.
|
|
231
|
-
*/
|
|
232
|
-
const MAX_OBJECT_NAME_LENGTH = 255;
|
|
233
|
-
/**
|
|
234
|
-
* Validate a user-supplied object name and warn (does not throw) when the value
|
|
235
|
-
* cannot be preserved as a stable PowerPoint Selection Pane identity. This keeps
|
|
236
|
-
* semantic-identity bugs visible at generation time without breaking existing
|
|
237
|
-
* decks that pass loose names.
|
|
238
|
-
* - Empty/whitespace-only names provide no usable identity.
|
|
239
|
-
* - Control characters are stripped by `encodeXmlEntities`, silently changing
|
|
240
|
-
* the stored name.
|
|
241
|
-
* - Excessively long names may not round-trip through PowerPoint/consumers.
|
|
242
|
-
* @param {string} name - the raw (pre-encoding) object name
|
|
243
|
-
* @param {string} kind - object kind for the warning message (e.g. 'text')
|
|
244
|
-
* @returns {string} the name unchanged (validation only)
|
|
245
|
-
*/
|
|
246
|
-
function validateObjectName(name, kind) {
|
|
247
|
-
if (typeof name !== "string") return name;
|
|
248
|
-
if (name.trim().length === 0) {
|
|
249
|
-
warn("object-name/empty", `${kind} objectName is empty or whitespace-only; it will not provide a stable Selection Pane identity.`);
|
|
250
|
-
return name;
|
|
251
|
-
}
|
|
252
|
-
const cc = String.fromCharCode;
|
|
253
|
-
if (new RegExp(`[${cc(0)}-${cc(8)}${cc(11)}${cc(12)}${cc(14)}-${cc(31)}${cc(127)}]`).test(name)) warn("object-name/control-characters", `${kind} objectName "${name}" contains control characters that will be stripped, changing the stored name.`);
|
|
254
|
-
if (name.length > MAX_OBJECT_NAME_LENGTH) warn("object-name/too-long", `${kind} objectName exceeds ${MAX_OBJECT_NAME_LENGTH} characters and may not be preserved by PowerPoint.`);
|
|
255
|
-
return name;
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Return object names that appear more than once in the given list. Used to warn
|
|
259
|
-
* when duplicate Selection Pane identities would be emitted on a single slide,
|
|
260
|
-
* which breaks consumers (e.g. semantic manifests) that rely on unique names.
|
|
261
|
-
* @param {string[]} names - object names emitted on one slide
|
|
262
|
-
* @returns {string[]} the duplicated names (each listed once)
|
|
263
|
-
*/
|
|
264
|
-
function getDuplicateObjectNames(names) {
|
|
265
|
-
const seen = /* @__PURE__ */ new Set();
|
|
266
|
-
const dupes = /* @__PURE__ */ new Set();
|
|
267
|
-
names.forEach((name) => {
|
|
268
|
-
if (typeof name !== "string" || name.length === 0) return;
|
|
269
|
-
if (seen.has(name)) dupes.add(name);
|
|
270
|
-
else seen.add(name);
|
|
271
|
-
});
|
|
272
|
-
return Array.from(dupes);
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Get a new rel ID (rId) for charts, media, etc.
|
|
276
|
-
*
|
|
277
|
-
* Counting is the normal case: a slide's rels are minted in order, so one past the
|
|
278
|
-
* total is free. It is not enough on its own, because a rel can arrive on a slide
|
|
279
|
-
* carrying an id minted somewhere else — auto-paging re-registers a repeated header
|
|
280
|
-
* row's hyperlink on each overflow slide under the id stamped on the shared
|
|
281
|
-
* hyperlink object (`gen/define/hyperlinks.ts`). That id is then held but not
|
|
282
|
-
* counted-to, so the next mint could hand out the same number and the part would
|
|
283
|
-
* declare two `Relationship` elements sharing an `Id` — invalid OPC, and PowerPoint
|
|
284
|
-
* resolves such a reference to whichever comes first. Hence the scan: start where
|
|
285
|
-
* the count says, then step over anything the slide already holds.
|
|
286
|
-
* @param {PresSlideInternal} target - the slide to use
|
|
287
|
-
* @returns {number} the lowest free rId at or above the current rel count plus 1
|
|
288
|
-
*/
|
|
289
|
-
function getNewRelId(target) {
|
|
290
|
-
const held = /* @__PURE__ */ new Set();
|
|
291
|
-
target._rels.forEach((rel) => held.add(rel.rId));
|
|
292
|
-
target._relsChart.forEach((rel) => held.add(rel.rId));
|
|
293
|
-
target._relsMedia.forEach((rel) => held.add(rel.rId));
|
|
294
|
-
let rId = target._rels.length + target._relsChart.length + target._relsMedia.length + 1;
|
|
295
|
-
while (held.has(rId)) rId++;
|
|
296
|
-
return rId;
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Whether a slide relationship is a hyperlink (external URL or internal slide
|
|
300
|
-
* link). The relationship `type` is stringly-typed (`'hyperlink'`, `'online'`,
|
|
301
|
-
* mixed-case variants), so this centralizes the case-insensitive predicate that
|
|
302
|
-
* was duplicated across the slide-rels writer (`gen/slide/slide.ts`) and the inspect path
|
|
303
|
-
* (presentation.ts). For an internal slide-to-slide link, `rel.data === 'slide'` and
|
|
304
|
-
* `rel.Target` is the 1-based target slide number.
|
|
305
|
-
* @param {{ type: string }} rel - a slide relationship
|
|
306
|
-
* @returns {boolean} true if the rel is any kind of hyperlink
|
|
307
|
-
*/
|
|
308
|
-
function isHyperlinkRel(rel) {
|
|
309
|
-
return rel.type.toLowerCase().includes("hyperlink");
|
|
310
|
-
}
|
|
311
|
-
//#endregion
|
|
312
|
-
//#region src/gen/oxml/el.ts
|
|
313
|
-
/**
|
|
314
|
-
* ts-pptx: XML element builder (write-side substrate)
|
|
315
|
-
*
|
|
316
|
-
* The write path historically concatenated template strings and called
|
|
317
|
-
* `encodeXmlEntities` by hand at every interpolation — the source of escaping,
|
|
318
|
-
* attribute-order and child-sequence bugs. This builder centralizes escaping
|
|
319
|
-
* while reproducing today's exact byte layout, so a migration onto it can be
|
|
320
|
-
* gated on strict byte-identity (`pnpm run byte-identity:check`) rather than on
|
|
321
|
-
* looser schema equivalence.
|
|
322
|
-
*
|
|
323
|
-
* Mirrors `src/read/oxml/dom.ts` on the write side.
|
|
324
|
-
*
|
|
325
|
-
* Two rules make byte-identity achievable:
|
|
326
|
-
*
|
|
327
|
-
* 1. Self-closing is decided by ARITY, never by child value. `el()` always emits
|
|
328
|
-
* a paired tag; `voidEl()` always self-closes. This matters because
|
|
329
|
-
* `encodeXmlEntities(undefined)` returns `''`, so a value-based rule would
|
|
330
|
-
* turn today's `<dc:title></dc:title>` into `<dc:title/>` — a silent byte
|
|
331
|
-
* regression on every optional-text element in the tree.
|
|
332
|
-
*
|
|
333
|
-
* Escaping is split to match: attribute values go through `encodeXmlAttrValue`
|
|
334
|
-
* (which adds the `	`/` `/` ` character references an XML parser
|
|
335
|
-
* would otherwise normalise to a space), text children through
|
|
336
|
-
* `encodeXmlEntities`, where those same characters are content.
|
|
337
|
-
*
|
|
338
|
-
* 2. Whitespace is explicit, per element, via `fmt`. Most emitted parts are flat
|
|
339
|
-
* (no `fmt` needed at all); the pretty-printed ones carry indentation that is
|
|
340
|
-
* not always depth-regular, so it is described rather than derived. Quirks
|
|
341
|
-
* like a misindented closing tag stay visible in the call site instead of
|
|
342
|
-
* being hidden inside a template literal.
|
|
343
|
-
*/
|
|
344
|
-
/** Wrap already-serialized XML so it is interpolated verbatim. */
|
|
345
|
-
function raw(xml) {
|
|
346
|
-
return { raw: xml };
|
|
347
|
-
}
|
|
348
|
-
function isRaw(child) {
|
|
349
|
-
return typeof child === "object" && child !== null && typeof child.raw === "string";
|
|
350
|
-
}
|
|
351
|
-
function openTag(name, attrs, fmt) {
|
|
352
|
-
let out = (fmt?.openPrefix ?? "") + "<" + name;
|
|
353
|
-
for (const [key, value] of Object.entries(attrs ?? {})) {
|
|
354
|
-
if (value === void 0 || value === null) continue;
|
|
355
|
-
out += " " + key + "=\"" + encodeXmlAttrValue(value) + "\"";
|
|
356
|
-
}
|
|
357
|
-
return out;
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Paired element: `<name …>children</name>` — emitted even when `children` is
|
|
361
|
-
* empty or `undefined`. Text children are escaped; `raw()` children are not.
|
|
362
|
-
*/
|
|
363
|
-
function el(name, attrs, children, fmt) {
|
|
364
|
-
let out = openTag(name, attrs, fmt) + ">";
|
|
365
|
-
const childPrefix = fmt?.childPrefix ?? "";
|
|
366
|
-
for (const child of Array.isArray(children) ? children : [children]) {
|
|
367
|
-
if (child === void 0 || child === null) continue;
|
|
368
|
-
out += childPrefix + (isRaw(child) ? child.raw : encodeXmlEntities(child));
|
|
369
|
-
}
|
|
370
|
-
return out + (fmt?.closePrefix ?? "") + "</" + name + ">";
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Self-closing element: `<name …/>`. `fmt.closePrefix` goes before the `/>` — a
|
|
374
|
-
* handful of emitters write `<a:avLst />` with a space there, and that space is
|
|
375
|
-
* byte-significant like any other.
|
|
376
|
-
*/
|
|
377
|
-
function voidEl(name, attrs, fmt) {
|
|
378
|
-
return openTag(name, attrs, fmt) + (fmt?.closePrefix ?? "") + "/>";
|
|
379
|
-
}
|
|
380
|
-
//#endregion
|
|
381
|
-
//#region src/gen/drawingml/color.ts
|
|
382
|
-
/**
|
|
383
|
-
* ts-pptx: DrawingML color elements
|
|
384
|
-
*
|
|
385
|
-
* Emit the bare `<a:srgbClr>` / `<a:schemeClr>` color element that every other
|
|
386
|
-
* color context builds on (solid fills, gradient stops, effect alphas, line
|
|
387
|
-
* fills, highlights, patterns).
|
|
388
|
-
*
|
|
389
|
-
* The RGB->hex helpers this module used to carry now live in
|
|
390
|
-
* `gen/table/html-dom.ts`, their only caller: they are reachable exclusively from
|
|
391
|
-
* the browser-only `tableToSlides` path, so keeping them here made them permanent
|
|
392
|
-
* dead weight in the Node chunk's coverage report (33% functions on a file that is
|
|
393
|
-
* otherwise fully exercised) while html-dom.ts is coverage-excluded wholesale.
|
|
394
|
-
*/
|
|
395
|
-
/**
|
|
396
|
-
* Opening delimiter of an `<a:alpha>` child, for detecting one a caller already
|
|
397
|
-
* supplied. A prefix rather than a whole element: the value is the caller's, and
|
|
398
|
-
* this asks only whether the element is present.
|
|
399
|
-
*/
|
|
400
|
-
const ALPHA_TAG = "<a:alpha";
|
|
401
|
-
/**
|
|
402
|
-
* Emit a bare DrawingML color element — either `<a:schemeClr>` (scheme color) or `<a:srgbClr>`
|
|
403
|
-
* (hex RGB). This is a low-level primitive: it is the shared building block for every color
|
|
404
|
-
* context (solid fills, gradient stops, `<a:alpha>` on shadows/glow, line fills, highlight,
|
|
405
|
-
* underline, patterns, …), so it is intentionally NOT limited to solid fills and cannot be
|
|
406
|
-
* folded into `genXmlColorSelection`.
|
|
407
|
-
*
|
|
408
|
-
* When you specifically want a *solid fill*, prefer `genXmlColorSelection` (`fill.ts`), which
|
|
409
|
-
* wraps this in `<a:solidFill>…</a:solidFill>` and also handles `alpha`/`transparency` and the
|
|
410
|
-
* gradient/pattern/image fill types — reach for `createColorElement` directly only when you
|
|
411
|
-
* need the raw color element (e.g. inside `<a:ln>`, `<a:gs>`, an effect, or a highlight).
|
|
412
|
-
* @param {string|SCHEME_COLORS} colorStr - hex RGB (e.g. "FFFF00") or a scheme color constant (e.g. SchemeColor.ACCENT1)
|
|
413
|
-
* @param {string} [innerElements] - additional elements that adjust the color, nested inside the color element
|
|
414
|
-
* @returns {string} XML string
|
|
415
|
-
*/
|
|
416
|
-
function createColorElement(colorStr, innerElements) {
|
|
417
|
-
if (typeof colorStr !== "string") {
|
|
418
|
-
warn("color/not-a-string", `createColorElement: expected a string color value, got ${typeof colorStr}. "${DEF_FONT_COLOR}" used instead.`);
|
|
419
|
-
colorStr = DEF_FONT_COLOR;
|
|
420
|
-
}
|
|
421
|
-
let colorVal = (colorStr || "").replace("#", "");
|
|
422
|
-
if (/^[0-9a-fA-F]{8}$/.test(colorVal)) {
|
|
423
|
-
if (!innerElements?.includes(ALPHA_TAG)) {
|
|
424
|
-
const alphaHex = colorVal.slice(6, 8);
|
|
425
|
-
innerElements = voidEl("a:alpha", { val: Math.round(parseInt(alphaHex, 16) / 255 * PERCENT_SCALE) }) + (innerElements || "");
|
|
426
|
-
}
|
|
427
|
-
colorVal = colorVal.slice(0, 6);
|
|
428
|
-
}
|
|
429
|
-
if (!REGEX_HEX_COLOR.test(colorVal) && !Object.values(SchemeColor).includes(colorVal)) {
|
|
430
|
-
warn("color/invalid-value", `"${colorVal}" is not a valid scheme color or hex RGB! "${DEF_FONT_COLOR}" used instead. Only provide 6-digit RGB or 'SchemeColor' values!`);
|
|
431
|
-
colorVal = DEF_FONT_COLOR;
|
|
432
|
-
}
|
|
433
|
-
const isHex = REGEX_HEX_COLOR.test(colorVal);
|
|
434
|
-
const name = isHex ? "a:srgbClr" : "a:schemeClr";
|
|
435
|
-
const attrs = { val: isHex ? colorVal.toUpperCase() : colorVal };
|
|
436
|
-
return innerElements ? el(name, attrs, raw(innerElements)) : voidEl(name, attrs);
|
|
437
|
-
}
|
|
438
|
-
//#endregion
|
|
439
|
-
//#region src/gen/drawingml/effect.ts
|
|
440
|
-
/**
|
|
441
|
-
* ts-pptx: DrawingML effects (glow and shadow)
|
|
442
|
-
*
|
|
443
|
-
* Emit the `<a:glow>` / `<a:outerShdw>` / `<a:innerShdw>` children of an
|
|
444
|
-
* `<a:effectLst>`, and normalize the user-facing `shadow` options into the
|
|
445
|
-
* internal shape every emit site reads.
|
|
446
|
-
* @see http://officeopenxml.com/drwSp-effects.php
|
|
447
|
-
*/
|
|
448
|
-
/**
|
|
449
|
-
* Creates `a:glow` element
|
|
450
|
-
* @param {TextGlowProps} options glow properties
|
|
451
|
-
* @param {TextGlowProps} defaults defaults for unspecified properties in `opts`
|
|
452
|
-
* @see http://officeopenxml.com/drwSp-effects.php
|
|
453
|
-
* { size: 8, color: 'FFFFFF', opacity: 0.75 };
|
|
454
|
-
*/
|
|
455
|
-
function createGlowElement(options, defaults) {
|
|
456
|
-
const opts = {
|
|
457
|
-
...defaults,
|
|
458
|
-
...options
|
|
459
|
-
};
|
|
460
|
-
const size = Math.round(opts.size * EMU_PER_POINT);
|
|
461
|
-
const color = opts.color || "000000";
|
|
462
|
-
const opacity = opacityToAlpha(opts.opacity ?? 0);
|
|
463
|
-
return el("a:glow", { rad: size }, raw(createColorElement(color, alpha(opacity))));
|
|
464
|
-
}
|
|
465
|
-
/** The `<a:alpha>` child both effects hang off their color element. */
|
|
466
|
-
function alpha(value) {
|
|
467
|
-
return voidEl("a:alpha", { val: value });
|
|
468
|
-
}
|
|
469
|
-
/**
|
|
470
|
-
* Creates an `a:outerShdw`/`a:innerShdw` element for a text run, shape, image, or chart.
|
|
471
|
-
* Returns the shadow element only (no wrapping `a:effectLst`) so callers can either combine
|
|
472
|
-
* it with other effects (e.g. glow) inside one `a:effectLst`, or wrap a lone shadow via
|
|
473
|
-
* {@link createShadowEffectLst}.
|
|
474
|
-
*
|
|
475
|
-
* Colors go through {@link createColorElement}, so scheme colors (e.g. `accent1`) are honored
|
|
476
|
-
* — earlier per-site copies hardcoded `a:srgbClr` and silently emitted invalid OOXML for them.
|
|
477
|
-
* @param {ShadowPropsInternal} options shadow properties
|
|
478
|
-
* @param {ShadowPropsInternal} defaults defaults for unspecified properties in `options`
|
|
479
|
-
* @see http://officeopenxml.com/drwSp-effects.php
|
|
480
|
-
* @returns {string} XML string, or '' when type is 'none'
|
|
481
|
-
*/
|
|
482
|
-
function createShadowElement(options, defaults) {
|
|
483
|
-
const opts = {
|
|
484
|
-
...defaults,
|
|
485
|
-
...options
|
|
486
|
-
};
|
|
487
|
-
if (opts.type === "none") return "";
|
|
488
|
-
const type = opts.type || "outer";
|
|
489
|
-
const blur = valToPts(opts.blur ?? 0);
|
|
490
|
-
const offset = valToPts(opts.offset ?? 0);
|
|
491
|
-
const angle = Math.round((opts.angle ?? 0) * ANGLE_UNITS_PER_DEGREE);
|
|
492
|
-
const opacity = Math.round((opts._alpha ?? .75) * PERCENT_SCALE);
|
|
493
|
-
const color = opts.color || "000000";
|
|
494
|
-
const attrs = {
|
|
495
|
-
...type === "outer" ? {
|
|
496
|
-
sx: 1e5,
|
|
497
|
-
sy: 1e5,
|
|
498
|
-
kx: 0,
|
|
499
|
-
ky: 0,
|
|
500
|
-
algn: "bl",
|
|
501
|
-
rotWithShape: opts.rotateWithShape ? 1 : 0
|
|
502
|
-
} : {},
|
|
503
|
-
blurRad: blur,
|
|
504
|
-
dist: offset,
|
|
505
|
-
dir: angle
|
|
506
|
-
};
|
|
507
|
-
return el(`a:${type}Shdw`, attrs, raw(createColorElement(color, alpha(opacity))));
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Wraps a lone shadow in the `a:effectLst` that CT_ShapeProperties (shapes/images) and chart
|
|
511
|
-
* marker/data-point properties require. Returns a self-closing `<a:effectLst/>` when there is
|
|
512
|
-
* no shadow (missing/non-object options, or `type: 'none'`), matching the "no effects" element
|
|
513
|
-
* PowerPoint emits. Use this instead of {@link createShadowElement} when the shadow is the only
|
|
514
|
-
* effect; use `createShadowElement` directly when combining with other effects (e.g. glow).
|
|
515
|
-
* @param {ShadowPropsInternal} options shadow properties
|
|
516
|
-
* @param {ShadowPropsInternal} defaults defaults for unspecified properties in `options`
|
|
517
|
-
* @returns {string} `<a:effectLst>…</a:effectLst>` or `<a:effectLst/>`
|
|
518
|
-
*/
|
|
519
|
-
function createShadowEffectLst(options, defaults) {
|
|
520
|
-
if (!options || typeof options !== "object") return voidEl("a:effectLst");
|
|
521
|
-
const inner = createShadowElement(options, defaults);
|
|
522
|
-
return inner ? el("a:effectLst", null, raw(inner)) : voidEl("a:effectLst");
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Checks shadow options passed by user and performs corrections if needed.
|
|
526
|
-
* @param {ShadowProps} ShadowProps - shadow options
|
|
527
|
-
*/
|
|
528
|
-
function correctShadowOptions(ShadowProps) {
|
|
529
|
-
if (!ShadowProps || typeof ShadowProps !== "object") return;
|
|
530
|
-
const corrected = ShadowProps;
|
|
531
|
-
if (corrected.type !== "outer" && corrected.type !== "inner" && corrected.type !== "none") {
|
|
532
|
-
warn("shadow/invalid-type", "shadow.type options are `outer`, `inner` or `none`.");
|
|
533
|
-
corrected.type = "outer";
|
|
534
|
-
}
|
|
535
|
-
if (corrected.angle) {
|
|
536
|
-
if (isNaN(Number(corrected.angle)) || corrected.angle < 0 || corrected.angle > 359) {
|
|
537
|
-
warn("shadow/angle-out-of-range", "shadow.angle can only be 0-359");
|
|
538
|
-
corrected.angle = 270;
|
|
539
|
-
}
|
|
540
|
-
corrected.angle = Math.round(Number(corrected.angle));
|
|
541
|
-
}
|
|
542
|
-
if (corrected.transparency !== void 0) {
|
|
543
|
-
const pct = Number(corrected.transparency);
|
|
544
|
-
if (isNaN(pct) || pct < 0 || pct > 100) warn("shadow/transparency-out-of-range", "shadow.transparency can only be 0-100");
|
|
545
|
-
else corrected._alpha = 1 - pct / 100;
|
|
546
|
-
}
|
|
547
|
-
if (corrected.color) {
|
|
548
|
-
if (corrected.color.startsWith("#")) {
|
|
549
|
-
warn("shadow/color-has-hash", "shadow.color should not include hash (#) character, , e.g. \"FF0000\"");
|
|
550
|
-
corrected.color = corrected.color.replace("#", "");
|
|
551
|
-
}
|
|
552
|
-
if (/^[0-9a-fA-F]{8}$/.test(corrected.color)) {
|
|
553
|
-
const alphaHex = corrected.color.slice(6, 8);
|
|
554
|
-
if (corrected._alpha === void 0) corrected._alpha = parseInt(alphaHex, 16) / 255;
|
|
555
|
-
corrected.color = corrected.color.slice(0, 6);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
return corrected;
|
|
559
|
-
}
|
|
560
|
-
//#endregion
|
|
561
166
|
//#region src/gen/define/chart.ts
|
|
562
167
|
/**
|
|
563
168
|
* ts-pptx: Chart Definition
|
|
@@ -937,6 +542,7 @@ function addChartDefinition(target, type, data, opt) {
|
|
|
937
542
|
}
|
|
938
543
|
const options = copyChartOptions(tmpOpt && typeof tmpOpt === "object" ? tmpOpt : {});
|
|
939
544
|
const callerSetBarGapWidthPct = typeof options.barGapWidthPct === "number" && !isNaN(options.barGapWidthPct);
|
|
545
|
+
for (const name of tmpTypes ? tmpTypes.map((sub) => sub.type) : [type]) if (!isChartType(name)) throw new InvalidOptionError("chart/unknown-type", `addChart: "${String(name)}" is not a chart type. Valid types are: ${Object.values(ChartType).join(", ")}.`, { detail: { type: name } });
|
|
940
546
|
options._type = tmpTypes ?? asChartType(type);
|
|
941
547
|
options.x = typeof options.x !== "undefined" && options.x != null && !isNaN(Number(options.x)) ? options.x : 1;
|
|
942
548
|
options.y = typeof options.y !== "undefined" && options.y != null && !isNaN(Number(options.y)) ? options.y : 1;
|
|
@@ -1409,17 +1015,19 @@ function addImageDefinition(target, opt) {
|
|
|
1409
1015
|
});
|
|
1410
1016
|
newObject.imageRid = imageRelId;
|
|
1411
1017
|
}
|
|
1412
|
-
if (typeof objHyperlink === "object")
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1018
|
+
if (typeof objHyperlink === "object") {
|
|
1019
|
+
if (!objHyperlink.url && !objHyperlink.slide) throw new InvalidOptionError("hyperlink/missing-target", "addImage: `hyperlink` requires either `url` or `slide`");
|
|
1020
|
+
else {
|
|
1021
|
+
imageRelId++;
|
|
1022
|
+
target._rels.push({
|
|
1023
|
+
type: "hyperlink",
|
|
1024
|
+
data: objHyperlink.slide ? "slide" : "dummy",
|
|
1025
|
+
rId: imageRelId,
|
|
1026
|
+
Target: objHyperlink.url ? objHyperlink.url : String(objHyperlink.slide)
|
|
1027
|
+
});
|
|
1028
|
+
objHyperlink._rId = imageRelId;
|
|
1029
|
+
newObject.hyperlink = objHyperlink;
|
|
1030
|
+
}
|
|
1423
1031
|
}
|
|
1424
1032
|
target._slideObjects.push(newObject);
|
|
1425
1033
|
}
|
|
@@ -1580,10 +1188,14 @@ function addTextDefinition(target, text, opts, isPlaceholder) {
|
|
|
1580
1188
|
itemOpts._bodyProp.vert = itemOpts.textDirection ?? itemOpts.vert;
|
|
1581
1189
|
itemOpts._bodyProp.wrap = typeof itemOpts.wrap === "boolean" ? itemOpts.wrap : true;
|
|
1582
1190
|
itemOpts._bodyProp.prstTxWarp = itemOpts.textWarp;
|
|
1583
|
-
if (itemOpts.columns !== void 0)
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1191
|
+
if (itemOpts.columns !== void 0) {
|
|
1192
|
+
if (typeof itemOpts.columns !== "number" || isNaN(itemOpts.columns) || itemOpts.columns < 1 || itemOpts.columns > 16) warn("text/invalid-columns", "text `columns` must be a number 1-16 (ignoring value)");
|
|
1193
|
+
else itemOpts._bodyProp.numCol = Math.round(itemOpts.columns);
|
|
1194
|
+
}
|
|
1195
|
+
if (itemOpts.columnSpacing !== void 0) {
|
|
1196
|
+
if (typeof itemOpts.columnSpacing !== "number" || isNaN(itemOpts.columnSpacing) || itemOpts.columnSpacing < 0) warn("text/invalid-column-spacing", "text `columnSpacing` must be a number >= 0 (ignoring value)");
|
|
1197
|
+
else itemOpts._bodyProp.spcCol = valToPts(itemOpts.columnSpacing);
|
|
1198
|
+
}
|
|
1587
1199
|
if (typeof itemOpts.underline === "boolean" && itemOpts.underline === true) itemOpts.underline = { style: "sng" };
|
|
1588
1200
|
{
|
|
1589
1201
|
const align = (itemOpts.align || "").toLowerCase();
|
|
@@ -2123,6 +1735,7 @@ function addNotesDefinition(target, notes) {
|
|
|
2123
1735
|
}
|
|
2124
1736
|
//#endregion
|
|
2125
1737
|
//#region src/gen/define/ole.ts
|
|
1738
|
+
const OD$1 = "application/vnd.openxmlformats-officedocument.";
|
|
2126
1739
|
/** Rel type for an embedded OPC package (an Office file, itself a zip). */
|
|
2127
1740
|
const PACKAGE_REL = OFFICE_REL + "package";
|
|
2128
1741
|
/** Rel type for a generic OLE-server blob — a compound-file `.bin` (ECMA-376 Part 1 §15.2.10). */
|
|
@@ -2134,7 +1747,7 @@ const OLE_OBJECT_REL = OFFICE_REL + "oleObject";
|
|
|
2134
1747
|
*/
|
|
2135
1748
|
const OLE_FORMATS = {
|
|
2136
1749
|
xlsx: {
|
|
2137
|
-
contentType: "
|
|
1750
|
+
contentType: OD$1 + "spreadsheetml.sheet",
|
|
2138
1751
|
relType: PACKAGE_REL,
|
|
2139
1752
|
progId: "Excel.Sheet.12",
|
|
2140
1753
|
name: "Worksheet"
|
|
@@ -2146,7 +1759,7 @@ const OLE_FORMATS = {
|
|
|
2146
1759
|
name: "Worksheet"
|
|
2147
1760
|
},
|
|
2148
1761
|
docx: {
|
|
2149
|
-
contentType: "
|
|
1762
|
+
contentType: OD$1 + "wordprocessingml.document",
|
|
2150
1763
|
relType: PACKAGE_REL,
|
|
2151
1764
|
progId: "Word.Document.12",
|
|
2152
1765
|
name: "Document"
|
|
@@ -2158,7 +1771,7 @@ const OLE_FORMATS = {
|
|
|
2158
1771
|
name: "Document"
|
|
2159
1772
|
},
|
|
2160
1773
|
pptx: {
|
|
2161
|
-
contentType: "
|
|
1774
|
+
contentType: OD$1 + "presentationml.presentation",
|
|
2162
1775
|
relType: PACKAGE_REL,
|
|
2163
1776
|
progId: "PowerPoint.Show.12",
|
|
2164
1777
|
name: "Presentation"
|
|
@@ -2172,7 +1785,7 @@ const OLE_FORMATS = {
|
|
|
2172
1785
|
};
|
|
2173
1786
|
/** Fallback for any payload that is not one of the {@link OLE_FORMATS} Office packages. */
|
|
2174
1787
|
const BIN_FORMAT = {
|
|
2175
|
-
contentType: "
|
|
1788
|
+
contentType: OD$1 + "oleObject",
|
|
2176
1789
|
relType: OLE_OBJECT_REL,
|
|
2177
1790
|
progId: "Package",
|
|
2178
1791
|
name: "Object"
|
|
@@ -3095,140 +2708,6 @@ var SlideBuilder = class {
|
|
|
3095
2708
|
}
|
|
3096
2709
|
};
|
|
3097
2710
|
//#endregion
|
|
3098
|
-
//#region src/gen/drawingml/fill.ts
|
|
3099
|
-
/**
|
|
3100
|
-
* ts-pptx: DrawingML fills
|
|
3101
|
-
*
|
|
3102
|
-
* Emit the fill group a shape, cell, chart element, or line paint can carry:
|
|
3103
|
-
* `<a:solidFill>`, `<a:gradFill>`, `<a:pattFill>`, `<a:blipFill>`.
|
|
3104
|
-
* {@link genXmlColorSelection} is the dispatch every caller should reach for;
|
|
3105
|
-
* the per-kind builders are exported for the few sites that know their fill kind.
|
|
3106
|
-
*/
|
|
3107
|
-
function boolToXml(value) {
|
|
3108
|
-
return value ? "1" : "0";
|
|
3109
|
-
}
|
|
3110
|
-
function normalizeGradientAngle(angle) {
|
|
3111
|
-
const degrees = angle ?? 0;
|
|
3112
|
-
if (typeof degrees !== "number" || !Number.isFinite(degrees)) throw new InvalidOptionError("gradient/angle-non-finite", "Gradient angle must be a finite number.");
|
|
3113
|
-
return convertRotationDegrees((degrees % 360 + 360) % 360);
|
|
3114
|
-
}
|
|
3115
|
-
function alphaFromTransparency(transparency) {
|
|
3116
|
-
return transparency ? voidEl("a:alpha", { val: transparencyToAlpha(transparency) }) : "";
|
|
3117
|
-
}
|
|
3118
|
-
function normalizeGradientStops(stops) {
|
|
3119
|
-
if (!Array.isArray(stops) || stops.length < 2) throw new InvalidOptionError("gradient/too-few-stops", "Gradient fill requires at least two stops.");
|
|
3120
|
-
return stops.map((stop) => {
|
|
3121
|
-
if (!stop || typeof stop.position !== "number" || !Number.isFinite(stop.position)) throw new InvalidOptionError("gradient/stop-position-non-finite", "Gradient stop position must be a finite number from 0 to 100.");
|
|
3122
|
-
if (stop.position < 0 || stop.position > 100) throw new InvalidOptionError("gradient/stop-position-out-of-range", "Gradient stop position must be from 0 to 100.");
|
|
3123
|
-
return stop;
|
|
3124
|
-
}).sort((a, b) => a.position - b.position);
|
|
3125
|
-
}
|
|
3126
|
-
/**
|
|
3127
|
-
* Create a native DrawingML gradient fill.
|
|
3128
|
-
* @param {GradientFillProps} gradient gradient fill options
|
|
3129
|
-
* @returns XML string
|
|
3130
|
-
*/
|
|
3131
|
-
function genXmlGradientFill(gradient) {
|
|
3132
|
-
if (!gradient || gradient.kind !== "linear" && gradient.kind !== "radial") throw new UnsupportedFeatureError("gradient/type-unsupported", "Gradient fill currently supports only linear and radial gradients.");
|
|
3133
|
-
if (typeof gradient.rotateWithShape !== "undefined" && typeof gradient.rotateWithShape !== "boolean") throw new InvalidOptionError("gradient/rotate-with-shape-not-boolean", "Gradient rotateWithShape must be a boolean.");
|
|
3134
|
-
const stops = normalizeGradientStops(gradient.stops);
|
|
3135
|
-
const rotWithShape = gradient.rotateWithShape ?? true;
|
|
3136
|
-
const gsLst = el("a:gsLst", null, stops.map((stop) => raw(el("a:gs", { pos: Math.round(stop.position * FIXED_PCT_PER_PERCENT) }, raw(createColorElement(stop.color, alphaFromTransparency(stop.transparency)))))));
|
|
3137
|
-
let shape;
|
|
3138
|
-
if (gradient.kind === "radial") {
|
|
3139
|
-
const cx = Math.max(0, Math.min(100, gradient.center?.x ?? 50));
|
|
3140
|
-
const cy = Math.max(0, Math.min(100, gradient.center?.y ?? 50));
|
|
3141
|
-
shape = el("a:path", { path: "circle" }, raw(voidEl("a:fillToRect", {
|
|
3142
|
-
l: Math.round(cx * FIXED_PCT_PER_PERCENT),
|
|
3143
|
-
t: Math.round(cy * FIXED_PCT_PER_PERCENT),
|
|
3144
|
-
r: Math.round((100 - cx) * FIXED_PCT_PER_PERCENT),
|
|
3145
|
-
b: Math.round((100 - cy) * FIXED_PCT_PER_PERCENT)
|
|
3146
|
-
})));
|
|
3147
|
-
} else {
|
|
3148
|
-
if (typeof gradient.scaled !== "undefined" && typeof gradient.scaled !== "boolean") throw new InvalidOptionError("gradient/scaled-not-boolean", "Gradient scaled must be a boolean.");
|
|
3149
|
-
shape = voidEl("a:lin", {
|
|
3150
|
-
ang: normalizeGradientAngle(gradient.angle),
|
|
3151
|
-
scaled: typeof gradient.scaled === "boolean" ? boolToXml(gradient.scaled) : void 0
|
|
3152
|
-
});
|
|
3153
|
-
}
|
|
3154
|
-
return el("a:gradFill", { rotWithShape: boolToXml(rotWithShape) }, [raw(gsLst), raw(shape)]);
|
|
3155
|
-
}
|
|
3156
|
-
/**
|
|
3157
|
-
* Create a native DrawingML pattern fill.
|
|
3158
|
-
* @param {PatternFillProps} pattern pattern fill options
|
|
3159
|
-
* @returns XML string
|
|
3160
|
-
*/
|
|
3161
|
-
function genXmlPatternFill(pattern) {
|
|
3162
|
-
if (!pattern) throw new InvalidOptionError("pattern-fill/missing-pattern", "Pattern fill requires a pattern object.");
|
|
3163
|
-
const fgColor = pattern.fgColor ?? "000000";
|
|
3164
|
-
const bgColor = pattern.bgColor ?? "FFFFFF";
|
|
3165
|
-
return el("a:pattFill", { prst: pattern.preset }, [raw(el("a:fgClr", null, raw(createColorElement(fgColor)))), raw(el("a:bgClr", null, raw(createColorElement(bgColor))))]);
|
|
3166
|
-
}
|
|
3167
|
-
/**
|
|
3168
|
-
* Create a native DrawingML picture (image) fill.
|
|
3169
|
-
* The media relationship is registered when the object is added; this only emits
|
|
3170
|
-
* the `<a:blipFill>` referencing the pre-resolved rId.
|
|
3171
|
-
* @param {ShapeFillProps} props fill props (must carry a resolved `_imgRid`)
|
|
3172
|
-
* @returns XML string
|
|
3173
|
-
*/
|
|
3174
|
-
function genXmlImageFill(props) {
|
|
3175
|
-
if (!props || typeof props._imgRid !== "number") {
|
|
3176
|
-
warn("image-fill/unresolved-media", "image fill is missing its resolved media reference; falling back to no fill. Provide `image: { path }` or `image: { data }`.");
|
|
3177
|
-
return voidEl("a:noFill");
|
|
3178
|
-
}
|
|
3179
|
-
const alpha = props.transparency;
|
|
3180
|
-
const blipInner = alpha ? voidEl("a:alphaModFix", { amt: Math.round((100 - alpha) * FIXED_PCT_PER_PERCENT) }) : "";
|
|
3181
|
-
return el("a:blipFill", {
|
|
3182
|
-
dpi: 0,
|
|
3183
|
-
rotWithShape: 1
|
|
3184
|
-
}, [
|
|
3185
|
-
raw(el("a:blip", { "r:embed": `rId${props._imgRid}` }, raw(blipInner))),
|
|
3186
|
-
raw(voidEl("a:srcRect")),
|
|
3187
|
-
raw(el("a:stretch", null, raw(voidEl("a:fillRect"))))
|
|
3188
|
-
]);
|
|
3189
|
-
}
|
|
3190
|
-
/**
|
|
3191
|
-
* Create color selection
|
|
3192
|
-
* @param {Color | ShapeFillProps | ShapeLineProps} props fill props
|
|
3193
|
-
* @returns XML string
|
|
3194
|
-
*/
|
|
3195
|
-
function genXmlColorSelection(props) {
|
|
3196
|
-
let fillType = "solid";
|
|
3197
|
-
let colorVal = "";
|
|
3198
|
-
let internalElements = "";
|
|
3199
|
-
let outText = "";
|
|
3200
|
-
if (props) {
|
|
3201
|
-
if (typeof props === "string") colorVal = props;
|
|
3202
|
-
else {
|
|
3203
|
-
if (props.type) fillType = props.type;
|
|
3204
|
-
if (props.color) colorVal = props.color;
|
|
3205
|
-
internalElements += alphaFromTransparency(props.transparency);
|
|
3206
|
-
}
|
|
3207
|
-
switch (fillType) {
|
|
3208
|
-
case "none":
|
|
3209
|
-
outText += voidEl("a:noFill");
|
|
3210
|
-
break;
|
|
3211
|
-
case "inherit":
|
|
3212
|
-
outText += "";
|
|
3213
|
-
break;
|
|
3214
|
-
case "solid":
|
|
3215
|
-
outText += el("a:solidFill", null, raw(createColorElement(colorVal, internalElements)));
|
|
3216
|
-
break;
|
|
3217
|
-
case "gradient":
|
|
3218
|
-
outText += genXmlGradientFill(typeof props === "string" ? void 0 : props.gradient);
|
|
3219
|
-
break;
|
|
3220
|
-
case "pattern":
|
|
3221
|
-
outText += genXmlPatternFill(typeof props === "string" ? void 0 : props.pattern);
|
|
3222
|
-
break;
|
|
3223
|
-
case "image":
|
|
3224
|
-
outText += genXmlImageFill(typeof props === "string" ? void 0 : props);
|
|
3225
|
-
break;
|
|
3226
|
-
default: outText += "";
|
|
3227
|
-
}
|
|
3228
|
-
}
|
|
3229
|
-
return outText;
|
|
3230
|
-
}
|
|
3231
|
-
//#endregion
|
|
3232
2711
|
//#region src/gen/drawingml/line.ts
|
|
3233
2712
|
/** Every key `BorderProps` defines. Keep in step with the interface in `types/style.ts`. */
|
|
3234
2713
|
const BORDER_KEYS = [
|
|
@@ -3480,13 +2959,14 @@ function genXmlTitle(opts, chartX, chartY) {
|
|
|
3480
2959
|
* @return {string} XML
|
|
3481
2960
|
*/
|
|
3482
2961
|
function createGridLineElement(glOpts) {
|
|
3483
|
-
|
|
2962
|
+
const line = el("a:ln", {
|
|
3484
2963
|
w: valToPts(glOpts.size || DEF_CHART_GRIDLINE.size || 1),
|
|
3485
2964
|
cap: createLineCap(glOpts.cap || DEF_CHART_GRIDLINE.cap)
|
|
3486
2965
|
}, [raw(" " + el("a:solidFill", null, raw(createColorElement(glOpts.color || DEF_GRIDLINE_COLOR)))), raw(" " + voidEl("a:prstDash", { val: glOpts.style || DEF_CHART_GRIDLINE.style }) + voidEl("a:round"))], {
|
|
3487
2966
|
openPrefix: " ",
|
|
3488
2967
|
closePrefix: " "
|
|
3489
|
-
})
|
|
2968
|
+
});
|
|
2969
|
+
return el("c:majorGridlines", null, raw(el("c:spPr", null, raw(line), {
|
|
3490
2970
|
openPrefix: " ",
|
|
3491
2971
|
closePrefix: " "
|
|
3492
2972
|
})));
|
|
@@ -3580,10 +3060,11 @@ function createSerLinesElement(opt) {
|
|
|
3580
3060
|
if (!opt) return "";
|
|
3581
3061
|
if (opt === true) return "<c:serLines/>";
|
|
3582
3062
|
if (opt.style === "none") return "";
|
|
3583
|
-
|
|
3063
|
+
const line = el("a:ln", {
|
|
3584
3064
|
w: valToPts(opt.size || DEF_CHART_GRIDLINE.size || 1),
|
|
3585
3065
|
cap: createLineCap(opt.cap || DEF_CHART_GRIDLINE.cap)
|
|
3586
|
-
}, [raw(el("a:solidFill", null, raw(createColorElement(opt.color || DEF_GRIDLINE_COLOR)))), raw(voidEl("a:prstDash", { val: opt.style || DEF_CHART_GRIDLINE.style }) + voidEl("a:round"))])
|
|
3066
|
+
}, [raw(el("a:solidFill", null, raw(createColorElement(opt.color || DEF_GRIDLINE_COLOR)))), raw(voidEl("a:prstDash", { val: opt.style || DEF_CHART_GRIDLINE.style }) + voidEl("a:round"))]);
|
|
3067
|
+
return el("c:serLines", null, raw(el("c:spPr", null, raw(line))));
|
|
3587
3068
|
}
|
|
3588
3069
|
/**
|
|
3589
3070
|
* Build the `<c:leaderLines>` element for pie/doughnut data labels.
|
|
@@ -3601,7 +3082,9 @@ function createSerLinesElement(opt) {
|
|
|
3601
3082
|
function createLeaderLinesElement(opts) {
|
|
3602
3083
|
if (!opts.showLeaderLines) return "";
|
|
3603
3084
|
if (!opts.leaderLineColor && opts.leaderLineSize == null) return "";
|
|
3604
|
-
|
|
3085
|
+
const w = valToPts(opts.leaderLineSize ?? .75);
|
|
3086
|
+
const color = opts.leaderLineColor || "808080";
|
|
3087
|
+
return `<c:leaderLines><c:spPr><a:ln w="${w}" cap="flat">${genXmlColorSelection(color)}<a:prstDash val="solid"/><a:round/></a:ln><a:effectLst/></c:spPr></c:leaderLines>`;
|
|
3605
3088
|
}
|
|
3606
3089
|
/**
|
|
3607
3090
|
* Build a single custom `<c:dLbl>` (rich-text data label) overriding one data point's label.
|
|
@@ -3677,8 +3160,10 @@ function makeSeriesDataPointsXml(chartType, obj, opts, varyColors) {
|
|
|
3677
3160
|
...pattern,
|
|
3678
3161
|
fgColor: fillColor
|
|
3679
3162
|
} : pattern);
|
|
3680
|
-
else if (fillColor)
|
|
3681
|
-
|
|
3163
|
+
else if (fillColor) {
|
|
3164
|
+
if (chartType === "bar3D") xml += `<a:ln>${genXmlColorSelection(fillColor)}</a:ln>`;
|
|
3165
|
+
else xml += genXmlColorSelection(fillColor);
|
|
3166
|
+
}
|
|
3682
3167
|
if (border) xml += createChartBorderLine(border);
|
|
3683
3168
|
}
|
|
3684
3169
|
xml += createShadowEffectLst(opts.shadow, DEF_SHAPE_SHADOW);
|
|
@@ -5089,9 +4574,10 @@ function makeXmlCharts(rel) {
|
|
|
5089
4574
|
usesSecondaryCatAxis = usesSecondaryCatAxis || (options.secondaryCatAxis ?? false);
|
|
5090
4575
|
const subType = asChartType(type.type);
|
|
5091
4576
|
const usesValueXAxis = subType === "scatter" || subType === "bubble" || subType === "bubble3D";
|
|
5092
|
-
if (options.secondaryCatAxis)
|
|
5093
|
-
|
|
5094
|
-
|
|
4577
|
+
if (options.secondaryCatAxis) {
|
|
4578
|
+
if (usesValueXAxis) secondaryCatAxisValType = subType;
|
|
4579
|
+
else secondaryCatAxisHasCategoryChart = true;
|
|
4580
|
+
} else if (usesValueXAxis) primaryCatAxisValType = subType;
|
|
5095
4581
|
else primaryCatAxisHasCategoryChart = true;
|
|
5096
4582
|
strXml += makeChartType(subType, type.data, options, valAxisId, catAxisId);
|
|
5097
4583
|
});
|
|
@@ -5182,7 +4668,7 @@ function makeChartType(chartType, data, opts, valAxisId, catAxisId) {
|
|
|
5182
4668
|
case "surface": return makeSurfacePlot(chartType, data, opts, valAxisId, catAxisId, valFmtCode);
|
|
5183
4669
|
case "doughnut":
|
|
5184
4670
|
case "pie": return makePiePlot(chartType, data, opts, valFmtCode);
|
|
5185
|
-
default:
|
|
4671
|
+
default: throw new InternalError("chart/type-not-routed", `makeChartType: "${String(chartType)}" has no <c:...Chart> plot — the chartEx catalog is built by makeXmlChartEx, and a newly added classic type needs an arm here`, { detail: { chartType } });
|
|
5186
4672
|
}
|
|
5187
4673
|
}
|
|
5188
4674
|
//#endregion
|
|
@@ -5245,7 +4731,8 @@ function makeChartExData(rel) {
|
|
|
5245
4731
|
formatCode: "General"
|
|
5246
4732
|
}, raw(valPts)))]);
|
|
5247
4733
|
const data = el("cx:data", { id: 0 }, [raw(catDim), raw(valDim)]);
|
|
5248
|
-
|
|
4734
|
+
const externalData = voidEl("cx:externalData", { "r:id": "rId1" });
|
|
4735
|
+
return el("cx:chartData", null, [raw(externalData), raw(data)]);
|
|
5249
4736
|
}
|
|
5250
4737
|
/**
|
|
5251
4738
|
* The `Sheet1` cell holding the series name (the numeric dimension's header) — column immediately
|
|
@@ -5264,7 +4751,8 @@ const A_NS = "http://schemas.openxmlformats.org/drawingml/2006/main";
|
|
|
5264
4751
|
const R_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
5265
4752
|
/**
|
|
5266
4753
|
* Map a chartEx {@link ChartType} to its `<cx:series layoutId>` token (the CT_SeriesLayout value
|
|
5267
|
-
* PowerPoint keys the chart geometry on).
|
|
4754
|
+
* PowerPoint keys the chart geometry on). Throws on anything it does not own — see the `default`
|
|
4755
|
+
* arm for which members those are and why reaching it is a routing bug.
|
|
5268
4756
|
*/
|
|
5269
4757
|
function chartExLayoutId(type) {
|
|
5270
4758
|
switch (type) {
|
|
@@ -5275,7 +4763,7 @@ function chartExLayoutId(type) {
|
|
|
5275
4763
|
case "histogram": return "clusteredColumn";
|
|
5276
4764
|
case "boxWhisker": return "boxWhisker";
|
|
5277
4765
|
case "regionMap": return "regionMap";
|
|
5278
|
-
default:
|
|
4766
|
+
default: throw new InternalError("chart/type-not-routed", `chartExLayoutId: "${String(type)}" has no <cx:series> layoutId — classic chart types belong to makeXmlCharts, pareto is built by makeParetoSeries, and a newly added chartEx type needs an arm here`, { detail: { chartType: type } });
|
|
5279
4767
|
}
|
|
5280
4768
|
}
|
|
5281
4769
|
/**
|
|
@@ -5353,11 +4841,12 @@ function makeBoxWhiskerLayoutPr(stats) {
|
|
|
5353
4841
|
* renders blank if the names don't match PowerPoint's geography database.
|
|
5354
4842
|
*/
|
|
5355
4843
|
function makeGeographyLayoutPr(geo) {
|
|
5356
|
-
|
|
4844
|
+
const geography = voidEl("cx:geography", {
|
|
5357
4845
|
cultureLanguage: geo?.cultureLanguage || "en-US",
|
|
5358
4846
|
cultureRegion: geo?.cultureRegion || "US",
|
|
5359
4847
|
attribution: "Powered by Bing"
|
|
5360
|
-
})
|
|
4848
|
+
});
|
|
4849
|
+
return el("cx:layoutPr", null, raw(geography));
|
|
5361
4850
|
}
|
|
5362
4851
|
/** The `<cx:tx>` series-name cell/value block (shared by every layout that names its series). */
|
|
5363
4852
|
function makeChartExSeriesName(rel) {
|
|
@@ -5403,8 +4892,10 @@ function makeChartExSeries(rel) {
|
|
|
5403
4892
|
const type = opts._type;
|
|
5404
4893
|
if (type === "pareto") return makeParetoSeries(rel);
|
|
5405
4894
|
let layoutPr = "";
|
|
5406
|
-
if (type === "waterfall" && Array.isArray(opts.subtotals) && opts.subtotals.length > 0)
|
|
5407
|
-
|
|
4895
|
+
if (type === "waterfall" && Array.isArray(opts.subtotals) && opts.subtotals.length > 0) {
|
|
4896
|
+
const idxs = opts.subtotals.map((idx) => voidEl("cx:idx", { val: idx })).join("");
|
|
4897
|
+
layoutPr = el("cx:layoutPr", null, raw(el("cx:subtotals", null, raw(idxs))));
|
|
4898
|
+
} else if (type === "treemap") layoutPr = el("cx:layoutPr", null, raw(voidEl("cx:parentLabelLayout", { val: "overlapping" })));
|
|
5408
4899
|
else if (type === "histogram") layoutPr = el("cx:layoutPr", null, raw(makeChartExBinning(opts.binning)));
|
|
5409
4900
|
else if (type === "boxWhisker") layoutPr = makeBoxWhiskerLayoutPr(opts.statistics);
|
|
5410
4901
|
else if (type === "regionMap") layoutPr = makeGeographyLayoutPr(opts.geography);
|
|
@@ -5473,15 +4964,16 @@ function makeChartExTitle(rel) {
|
|
|
5473
4964
|
const color = rel.opts.titleColor || "000000";
|
|
5474
4965
|
const face = rel.opts.titleFontFace || "Calibri";
|
|
5475
4966
|
const rPr = el("a:defRPr", null, [raw(genXmlColorSelection(color)), raw(createChartTextFonts(face))]);
|
|
4967
|
+
const rich = el("cx:rich", null, [
|
|
4968
|
+
raw(voidEl("a:bodyPr")),
|
|
4969
|
+
raw(voidEl("a:lstStyle")),
|
|
4970
|
+
raw(el("a:p", null, [raw(el("a:pPr", null, raw(rPr))), raw(el("a:r", null, [raw(voidEl("a:rPr", { lang: rel.opts.lang || "en-US" })), raw(el("a:t", null, rel.opts.title || "Chart Title"))]))]))
|
|
4971
|
+
]);
|
|
5476
4972
|
return el("cx:title", {
|
|
5477
4973
|
pos: "t",
|
|
5478
4974
|
align: "ctr",
|
|
5479
4975
|
overlay: 0
|
|
5480
|
-
}, raw(el("cx:tx", null, raw(
|
|
5481
|
-
raw(voidEl("a:bodyPr")),
|
|
5482
|
-
raw(voidEl("a:lstStyle")),
|
|
5483
|
-
raw(el("a:p", null, [raw(el("a:pPr", null, raw(rPr))), raw(el("a:r", null, [raw(voidEl("a:rPr", { lang: rel.opts.lang || "en-US" })), raw(el("a:t", null, rel.opts.title || "Chart Title"))]))]))
|
|
5484
|
-
])))));
|
|
4976
|
+
}, raw(el("cx:tx", null, raw(rich))));
|
|
5485
4977
|
}
|
|
5486
4978
|
/**
|
|
5487
4979
|
* Assemble the full `<cx:chartSpace>` for a chartEx chart part.
|
|
@@ -5492,7 +4984,8 @@ function makeChartExTitle(rel) {
|
|
|
5492
4984
|
*/
|
|
5493
4985
|
function makeXmlChartEx(rel) {
|
|
5494
4986
|
const type = rel.opts._type;
|
|
5495
|
-
const
|
|
4987
|
+
const plotAreaRegion = el("cx:plotAreaRegion", null, raw(makeChartExSeries(rel)));
|
|
4988
|
+
const plotArea = el("cx:plotArea", null, [raw(plotAreaRegion), raw(makeChartExAxes(type))]);
|
|
5496
4989
|
const legend = rel.opts.showLegend ? voidEl("cx:legend", {
|
|
5497
4990
|
pos: chartExLegendPos(rel.opts.legendPos),
|
|
5498
4991
|
align: "ctr",
|
|
@@ -5546,6 +5039,8 @@ function makeChartExStyleXml() {
|
|
|
5546
5039
|
}
|
|
5547
5040
|
//#endregion
|
|
5548
5041
|
//#region src/gen/chart/embed-xlsx.ts
|
|
5042
|
+
/** MS chart-extension relationship types (chartEx style + color-style sidecar parts). */
|
|
5043
|
+
const MS_CHART_REL = "http://schemas.microsoft.com/office/2011/relationships/";
|
|
5549
5044
|
function relationship$2(id, type, target) {
|
|
5550
5045
|
return voidEl("Relationship", {
|
|
5551
5046
|
Id: id,
|
|
@@ -5783,8 +5278,8 @@ function buildChartRelsXml(embeddingTarget) {
|
|
|
5783
5278
|
function buildChartExRelsXml(embeddingTarget, colorsTarget, styleTarget) {
|
|
5784
5279
|
return XML_DECL + relationships([
|
|
5785
5280
|
relationship$2("rId1", OFFICE_REL + "package", embeddingTarget),
|
|
5786
|
-
relationship$2("rId2", "
|
|
5787
|
-
relationship$2("rId3", "
|
|
5281
|
+
relationship$2("rId2", MS_CHART_REL + "chartColorStyle", colorsTarget),
|
|
5282
|
+
relationship$2("rId3", MS_CHART_REL + "chartStyle", styleTarget)
|
|
5788
5283
|
]);
|
|
5789
5284
|
}
|
|
5790
5285
|
/**
|
|
@@ -6254,6 +5749,7 @@ function buildAnimationSeq(animations, next) {
|
|
|
6254
5749
|
fill: "hold"
|
|
6255
5750
|
}, [raw(el("p:stCondLst", null, raw(voidEl("p:cond", { delay: "indefinite" })))), raw(el("p:childTnLst", null, raw(subs)))])));
|
|
6256
5751
|
};
|
|
5752
|
+
const clickGroups = groups.map(emitGroup).join("");
|
|
6257
5753
|
return el("p:seq", {
|
|
6258
5754
|
concurrent: 1,
|
|
6259
5755
|
nextAc: "seek"
|
|
@@ -6262,7 +5758,7 @@ function buildAnimationSeq(animations, next) {
|
|
|
6262
5758
|
id: 2,
|
|
6263
5759
|
dur: "indefinite",
|
|
6264
5760
|
nodeType: "mainSeq"
|
|
6265
|
-
}, raw(el("p:childTnLst", null, raw(
|
|
5761
|
+
}, raw(el("p:childTnLst", null, raw(clickGroups))))),
|
|
6266
5762
|
raw(el("p:prevCondLst", null, raw(el("p:cond", {
|
|
6267
5763
|
evt: "onPrev",
|
|
6268
5764
|
delay: 0
|
|
@@ -6310,7 +5806,8 @@ function slideTimingToXml(slide) {
|
|
|
6310
5806
|
const mediaNode = (obj, nodeId) => {
|
|
6311
5807
|
const spid = slide._slideObjects.indexOf(obj) + 2;
|
|
6312
5808
|
const repeatCount = obj.loop === true ? "indefinite" : String(Math.round(obj.loopCount * 1e3));
|
|
6313
|
-
|
|
5809
|
+
const mediaTag = obj.mtype === "audio" ? "p:audio" : "p:video";
|
|
5810
|
+
return el(mediaTag, null, raw(el("p:cMediaNode", null, [raw(el("p:cTn", {
|
|
6314
5811
|
id: nodeId,
|
|
6315
5812
|
repeatCount,
|
|
6316
5813
|
fill: "hold",
|
|
@@ -6320,12 +5817,13 @@ function slideTimingToXml(slide) {
|
|
|
6320
5817
|
if (animations.length === 0) {
|
|
6321
5818
|
if (loopMedia.length === 0) return "";
|
|
6322
5819
|
let nodeId = 1;
|
|
5820
|
+
const mediaNodes = loopMedia.map((obj) => mediaNode(obj, nodeId += 1)).join("");
|
|
6323
5821
|
return el("p:timing", null, raw(el("p:tnLst", null, raw(el("p:par", null, raw(el("p:cTn", {
|
|
6324
5822
|
id: 1,
|
|
6325
5823
|
dur: "indefinite",
|
|
6326
5824
|
restart: "never",
|
|
6327
5825
|
nodeType: "tmRoot"
|
|
6328
|
-
}, raw(el("p:childTnLst", null, raw(
|
|
5826
|
+
}, raw(el("p:childTnLst", null, raw(mediaNodes))))))))));
|
|
6329
5827
|
}
|
|
6330
5828
|
let id = 2;
|
|
6331
5829
|
const next = () => id += 1;
|
|
@@ -6394,62 +5892,6 @@ function slideTransitionToXml(slide) {
|
|
|
6394
5892
|
}, [raw(typeEl), raw(sndAc)])))), raw(el("mc:Fallback", null, raw(el("p:transition", baseAttrs, [raw(typeEl), raw(sndAc)]))))]);
|
|
6395
5893
|
}
|
|
6396
5894
|
//#endregion
|
|
6397
|
-
//#region src/gen/drawingml/clamp.ts
|
|
6398
|
-
/**
|
|
6399
|
-
* ts-pptx: DrawingML value clamps
|
|
6400
|
-
*
|
|
6401
|
-
* Clamp font/character/line spacing values (points) into their ST_* schema
|
|
6402
|
-
* ranges and convert to the hundredths-of-a-point units the attributes expect.
|
|
6403
|
-
* Out-of-range values make PowerPoint report the package as needing repair.
|
|
6404
|
-
*/
|
|
6405
|
-
/**
|
|
6406
|
-
* Clamp a font size (points) into ST_TextFontSize (1-4000pt) and return it in
|
|
6407
|
-
* hundredths of a point for the `sz` attribute. Out-of-range sizes make
|
|
6408
|
-
* PowerPoint report the package as needing repair (e.g. `sz` > 400000 or < 100).
|
|
6409
|
-
*/
|
|
6410
|
-
function clampFontSizeSz(fontSizePts) {
|
|
6411
|
-
const raw = ptToHundredths(fontSizePts);
|
|
6412
|
-
const clamped = Math.min(4e5, Math.max(100, raw));
|
|
6413
|
-
if (clamped !== raw) warnOnce("font/size-out-of-range", `fontSize ${fontSizePts} is outside the valid range 1-4000pt; using ${clamped / 100}.`);
|
|
6414
|
-
return clamped;
|
|
6415
|
-
}
|
|
6416
|
-
/** Clamp character spacing (points) into ST_TextPoint (-4000..4000pt); returns hundredths for the `spc` attribute. */
|
|
6417
|
-
function clampCharSpacingSpc(charSpacingPts) {
|
|
6418
|
-
const raw = ptToHundredths(charSpacingPts);
|
|
6419
|
-
const clamped = Math.min(4e5, Math.max(-4e5, raw));
|
|
6420
|
-
if (clamped !== raw) warnOnce("text/char-spacing-out-of-range", `charSpacing ${charSpacingPts} is outside the valid range -4000..4000pt; using ${clamped / 100}.`);
|
|
6421
|
-
return clamped;
|
|
6422
|
-
}
|
|
6423
|
-
/**
|
|
6424
|
-
* Clamp a paragraph's left margin (points) into ST_TextMargin (0..4032pt, i.e. 0..51206400 EMU);
|
|
6425
|
-
* returns EMU for `a:pPr/@marL`. The type is unsigned — a negative margin is not a narrower one,
|
|
6426
|
-
* it is a value PowerPoint reports as needing repair.
|
|
6427
|
-
*/
|
|
6428
|
-
function clampParaMarginEmu(marginPts) {
|
|
6429
|
-
const raw = valToPts(marginPts);
|
|
6430
|
-
const clamped = Math.min(51206400, Math.max(0, raw));
|
|
6431
|
-
if (clamped !== raw) warnOnce("text/paragraph-margin-out-of-range", `paraMarginLeft ${marginPts} is outside the valid range 0-4032pt; using ${clamped / EMU_PER_POINT}.`);
|
|
6432
|
-
return clamped;
|
|
6433
|
-
}
|
|
6434
|
-
/**
|
|
6435
|
-
* Clamp a first-line indent (points) into ST_TextIndent (-4032..4032pt); returns EMU for
|
|
6436
|
-
* `a:pPr/@indent`. Unlike the margin this one is signed: a negative indent is the hanging
|
|
6437
|
-
* indent every bulleted paragraph uses.
|
|
6438
|
-
*/
|
|
6439
|
-
function clampParaIndentEmu(indentPts) {
|
|
6440
|
-
const raw = valToPts(indentPts);
|
|
6441
|
-
const clamped = Math.min(51206400, Math.max(-51206400, raw));
|
|
6442
|
-
if (clamped !== raw) warnOnce("text/paragraph-indent-out-of-range", `paraIndent ${indentPts} is outside the valid range -4032..4032pt; using ${clamped / EMU_PER_POINT}.`);
|
|
6443
|
-
return clamped;
|
|
6444
|
-
}
|
|
6445
|
-
/** Clamp line spacing (points) into ST_TextSpacingPoint (0..1584pt); returns hundredths for `<a:spcPts val>`. */
|
|
6446
|
-
function clampLineSpacingPts(lineSpacingPts) {
|
|
6447
|
-
const raw = ptToHundredths(lineSpacingPts);
|
|
6448
|
-
const clamped = Math.min(158400, Math.max(0, raw));
|
|
6449
|
-
if (clamped !== raw) warnOnce("text/line-spacing-out-of-range", `lineSpacing ${lineSpacingPts} is outside the valid range 0-1584pt; using ${clamped / 100}.`);
|
|
6450
|
-
return clamped;
|
|
6451
|
-
}
|
|
6452
|
-
//#endregion
|
|
6453
5895
|
//#region src/gen/drawingml/locks.ts
|
|
6454
5896
|
const SHAPE_LOCK_ATTRS = [
|
|
6455
5897
|
"noGrp",
|
|
@@ -6585,430 +6027,6 @@ function genXmlShapeLine(ln) {
|
|
|
6585
6027
|
]);
|
|
6586
6028
|
}
|
|
6587
6029
|
//#endregion
|
|
6588
|
-
//#region src/gen/drawingml/math.ts
|
|
6589
|
-
/**
|
|
6590
|
-
* ts-pptx: DrawingML native equations (OMML)
|
|
6591
|
-
*
|
|
6592
|
-
* Wrap raw OMML markup into the `<a14:m>` marker PowerPoint uses for editable
|
|
6593
|
-
* equations — as a standalone display paragraph (`genXmlMathParagraph`) or as an
|
|
6594
|
-
* inline run that flows between the surrounding `<a:r>` runs (`genXmlInlineMath`).
|
|
6595
|
-
*/
|
|
6596
|
-
const M_NS = "http://schemas.openxmlformats.org/officeDocument/2006/math";
|
|
6597
|
-
const A14_NS = "http://schemas.microsoft.com/office/drawing/2010/main";
|
|
6598
|
-
/** The `<a14:m>` marker, carrying both namespace declarations so the supplied OMML needs none. */
|
|
6599
|
-
function a14Math(mathXml) {
|
|
6600
|
-
return el("a14:m", {
|
|
6601
|
-
"xmlns:a14": A14_NS,
|
|
6602
|
-
"xmlns:m": M_NS
|
|
6603
|
-
}, raw(mathXml));
|
|
6604
|
-
}
|
|
6605
|
-
/**
|
|
6606
|
-
* Build a native-equation paragraph (`<a:p>`) from raw OMML.
|
|
6607
|
-
*
|
|
6608
|
-
* PowerPoint stores an editable equation inside a text body as an `<a14:m>` marker wrapping
|
|
6609
|
-
* `<m:oMathPara><m:oMath>…`. We declare both the `a14` (drawing-2010) and `m` (math) namespaces
|
|
6610
|
-
* on the `<a14:m>` element so the supplied OMML needs no namespace declarations of its own, then
|
|
6611
|
-
* accept three input shapes: a full `<m:oMathPara>`, a full `<m:oMath>`, or the inner OMML
|
|
6612
|
-
* (children of `<m:oMath>`). A trailing `<a:endParaRPr>` matches what PowerPoint authors.
|
|
6613
|
-
*
|
|
6614
|
-
* @param {string} omml - raw OMML markup for the equation
|
|
6615
|
-
* @returns {string} an `<a:p>` math paragraph
|
|
6616
|
-
*/
|
|
6617
|
-
function genXmlMathParagraph(omml) {
|
|
6618
|
-
const trimmed = (omml || "").trim();
|
|
6619
|
-
const paraPr = el("m:oMathParaPr", null, raw(voidEl("m:jc", { "m:val": "centerGroup" })));
|
|
6620
|
-
return el("a:p", null, [a14Math(trimmed.startsWith("<m:oMathPara") ? trimmed : trimmed.includes("<m:oMath") ? el("m:oMathPara", null, [paraPr, trimmed].map(raw)) : el("m:oMathPara", null, [paraPr, el("m:oMath", null, raw(trimmed))].map(raw))), voidEl("a:endParaRPr", { lang: "en-US" })].map(raw));
|
|
6621
|
-
}
|
|
6622
|
-
/**
|
|
6623
|
-
* Build an INLINE native-equation run (dn-inline-math) from raw OMML.
|
|
6624
|
-
*
|
|
6625
|
-
* Unlike the display form ({@link genXmlMathParagraph}), an inline equation is *not* its own
|
|
6626
|
-
* paragraph: PowerPoint authors an `<a14:m>` marker wrapping a bare `<m:oMath>` (no `<m:oMathPara>`,
|
|
6627
|
-
* no `<m:oMathParaPr>`/`<m:jc>`) that flows between the surrounding `<a:r>` runs in one `<a:p>` —
|
|
6628
|
-
* pinned by the `math-omml-inline.pptx` oracle. The `mc:AlternateContent` envelope stays at the
|
|
6629
|
-
* shape level (see `objectHasMath`), so nothing wraps the run itself. We declare the `a14` and `m`
|
|
6630
|
-
* namespaces on the `<a14:m>` element so the supplied OMML needs none of its own, and accept the
|
|
6631
|
-
* same three input shapes as the display helper: a full `<m:oMathPara>` (its inner `<m:oMath>` is
|
|
6632
|
-
* unwrapped, since a paragraph block cannot flow inline), a full `<m:oMath>`, or the inner OMML.
|
|
6633
|
-
*
|
|
6634
|
-
* @param {string} omml - raw OMML markup for the equation
|
|
6635
|
-
* @returns {string} an `<a14:m>` inline equation run
|
|
6636
|
-
*/
|
|
6637
|
-
function genXmlInlineMath(omml) {
|
|
6638
|
-
const trimmed = (omml || "").trim();
|
|
6639
|
-
const oMathMatch = trimmed.match(/<m:oMath[\s>][\s\S]*<\/m:oMath>/);
|
|
6640
|
-
return a14Math(oMathMatch ? oMathMatch[0] : el("m:oMath", null, raw(trimmed)));
|
|
6641
|
-
}
|
|
6642
|
-
//#endregion
|
|
6643
|
-
//#region src/gen/drawingml/text-run.ts
|
|
6644
|
-
/** The 2018 hyperlink-color extension namespace, written on the `<ahyp:hlinkClr>` element itself. */
|
|
6645
|
-
const AHYP_NS = "http://schemas.microsoft.com/office/drawing/2018/hyperlinkcolor";
|
|
6646
|
-
/**
|
|
6647
|
-
* `<a:buChar>` cannot go through the element builder. Its `char` attribute carries a *pre-escaped*
|
|
6648
|
-
* numeric character reference (`•` — see `BulletType`), and the builder escapes every
|
|
6649
|
-
* attribute value, which would emit `&#x2022;` and render that text literally as the bullet.
|
|
6650
|
-
* There is no raw-attribute escape hatch, so this one stays a template.
|
|
6651
|
-
*/
|
|
6652
|
-
function buChar(char) {
|
|
6653
|
-
return `<a:buChar char="${char}"/>`;
|
|
6654
|
-
}
|
|
6655
|
-
/**
|
|
6656
|
-
* `<a:extLst>` marking a hyperlink whose color should follow the text color rather than the
|
|
6657
|
-
* theme's hyperlink color. The leading spaces are byte-significant: this block was authored as
|
|
6658
|
-
* indented string concatenation and that indentation reaches the file, so it is described here
|
|
6659
|
-
* with `openPrefix`/`closePrefix` rather than being tidied away.
|
|
6660
|
-
*/
|
|
6661
|
-
const HLINK_TEXT_COLOR_EXT = el("a:extLst", null, raw(el("a:ext", { uri: "{A12FA001-AC4F-418D-AE19-62706E023703}" }, raw(voidEl("ahyp:hlinkClr", {
|
|
6662
|
-
"xmlns:ahyp": AHYP_NS,
|
|
6663
|
-
val: "tx"
|
|
6664
|
-
}, { openPrefix: " " })), {
|
|
6665
|
-
openPrefix: " ",
|
|
6666
|
-
closePrefix: " "
|
|
6667
|
-
})), {
|
|
6668
|
-
openPrefix: " ",
|
|
6669
|
-
closePrefix: " "
|
|
6670
|
-
});
|
|
6671
|
-
/**
|
|
6672
|
-
* Whether a `bullet` value makes the paragraph DRAW a bullet (a glyph, a number or a picture).
|
|
6673
|
-
*
|
|
6674
|
-
* Two call sites downstream of the emitter act on "this paragraph has a bullet" rather than on
|
|
6675
|
-
* "this paragraph says something about its bullet", and both were plain truthiness tests until
|
|
6676
|
-
* `'inherit'` — a truthy string that draws nothing — joined the union. Left as truthiness they
|
|
6677
|
-
* would have broken the line grouping (every run starting its own paragraph) and eaten a real
|
|
6678
|
-
* leading `•` from text that emits no `a:buChar` to duplicate it.
|
|
6679
|
-
* @param {TextPropsOptions['bullet']} bullet - the option value
|
|
6680
|
-
* @return {boolean} true when bullet markup is emitted
|
|
6681
|
-
*/
|
|
6682
|
-
function emitsBulletMarkup(bullet) {
|
|
6683
|
-
return !!bullet && bullet !== "inherit";
|
|
6684
|
-
}
|
|
6685
|
-
/**
|
|
6686
|
-
* One paragraph margin attribute's value in EMU, or `null` when the attribute is not written.
|
|
6687
|
-
*
|
|
6688
|
-
* Three inputs, because the option has three states and so does the attribute: an explicit
|
|
6689
|
-
* number is clamped and written, `'inherit'` writes nothing, and an absent option falls back to
|
|
6690
|
-
* whatever the `bullet` arm decided — which is also `null` for `bullet: 'inherit'`, the one arm
|
|
6691
|
-
* that states nothing on its own.
|
|
6692
|
-
* @param {number|'inherit'|undefined} option - `paraMarginLeft` / `paraIndent` as given
|
|
6693
|
-
* @param {number|null} fallbackEmu - the bullet arm's default, `null` for no attribute
|
|
6694
|
-
* @param {Function} clamp - the attribute's ST_* range clamp (points in, EMU out)
|
|
6695
|
-
* @return {number|null} the EMU value to write, or null to omit the attribute
|
|
6696
|
-
*/
|
|
6697
|
-
function resolveParagraphMargin(option, fallbackEmu, clamp) {
|
|
6698
|
-
if (option === void 0) return fallbackEmu;
|
|
6699
|
-
if (option === "inherit") return null;
|
|
6700
|
-
return clamp(option);
|
|
6701
|
-
}
|
|
6702
|
-
/**
|
|
6703
|
-
* Generate XML Paragraph Properties
|
|
6704
|
-
* @param {SlideObject|TextProps} textObj - text object
|
|
6705
|
-
* @param {boolean} isDefault - array of default relations
|
|
6706
|
-
* @return {string} XML
|
|
6707
|
-
*/
|
|
6708
|
-
function genXmlParagraphProperties(textObj, isDefault) {
|
|
6709
|
-
const opts = textObj.options ?? {};
|
|
6710
|
-
let strXmlBullet = "";
|
|
6711
|
-
let strXmlBulletColor = "";
|
|
6712
|
-
let strXmlLnSpc = "";
|
|
6713
|
-
let strXmlParaSpc = "";
|
|
6714
|
-
let strXmlTabStops = "";
|
|
6715
|
-
const tag = isDefault ? "a:lvl1pPr" : "a:pPr";
|
|
6716
|
-
let bulletMarL = valToPts(27);
|
|
6717
|
-
let defaultMarL = null;
|
|
6718
|
-
let defaultIndent = null;
|
|
6719
|
-
let indentBeforeMarL = false;
|
|
6720
|
-
let paragraphPropXml = `<${tag}${opts.rtlMode ? " rtl=\"1\" " : ""}`;
|
|
6721
|
-
{
|
|
6722
|
-
if (opts.align) switch (opts.align) {
|
|
6723
|
-
case "left":
|
|
6724
|
-
paragraphPropXml += " algn=\"l\"";
|
|
6725
|
-
break;
|
|
6726
|
-
case "right":
|
|
6727
|
-
paragraphPropXml += " algn=\"r\"";
|
|
6728
|
-
break;
|
|
6729
|
-
case "center":
|
|
6730
|
-
paragraphPropXml += " algn=\"ctr\"";
|
|
6731
|
-
break;
|
|
6732
|
-
case "justify":
|
|
6733
|
-
paragraphPropXml += " algn=\"just\"";
|
|
6734
|
-
break;
|
|
6735
|
-
default: paragraphPropXml += "";
|
|
6736
|
-
}
|
|
6737
|
-
if (opts.lineSpacing) strXmlLnSpc = el("a:lnSpc", null, raw(voidEl("a:spcPts", { val: clampLineSpacingPts(opts.lineSpacing) })));
|
|
6738
|
-
else if (opts.lineSpacingMultiple) strXmlLnSpc = el("a:lnSpc", null, raw(voidEl("a:spcPct", { val: Math.round(opts.lineSpacingMultiple * PERCENT_SCALE) })));
|
|
6739
|
-
if (opts.indentLevel && !isNaN(Number(opts.indentLevel)) && opts.indentLevel > 0) paragraphPropXml += ` lvl="${opts.indentLevel}"`;
|
|
6740
|
-
if (opts.paraSpaceBefore && !isNaN(Number(opts.paraSpaceBefore)) && opts.paraSpaceBefore > 0) strXmlParaSpc += el("a:spcBef", null, raw(voidEl("a:spcPts", { val: ptToHundredths(opts.paraSpaceBefore) })));
|
|
6741
|
-
if (opts.paraSpaceAfter && !isNaN(Number(opts.paraSpaceAfter)) && opts.paraSpaceAfter > 0) strXmlParaSpc += el("a:spcAft", null, raw(voidEl("a:spcPts", { val: ptToHundredths(opts.paraSpaceAfter) })));
|
|
6742
|
-
if (typeof opts.bullet === "object") {
|
|
6743
|
-
const bulletImage = opts.bullet.image;
|
|
6744
|
-
const isPictureBullet = !!(bulletImage && (bulletImage.path || bulletImage.data));
|
|
6745
|
-
if (opts.bullet?.indent) bulletMarL = valToPts(opts.bullet.indent);
|
|
6746
|
-
defaultMarL = opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL;
|
|
6747
|
-
defaultIndent = -bulletMarL;
|
|
6748
|
-
if (opts.bullet.color && !isPictureBullet) strXmlBulletColor = el("a:buClr", null, raw(createColorElement(opts.bullet.color)));
|
|
6749
|
-
let bulletSizePct;
|
|
6750
|
-
if (opts.bullet.size !== void 0) {
|
|
6751
|
-
const bulletSize = Number(opts.bullet.size);
|
|
6752
|
-
if (isNaN(bulletSize) || bulletSize < 25 || bulletSize > 400) warn("bullet/size-out-of-range", "`bullet.size` must be a percentage between 25 and 400!");
|
|
6753
|
-
else bulletSizePct = Math.round(bulletSize * FIXED_PCT_PER_PERCENT);
|
|
6754
|
-
}
|
|
6755
|
-
const strXmlBulletSize = bulletSizePct === void 0 ? "" : voidEl("a:buSzPct", { val: bulletSizePct });
|
|
6756
|
-
const strXmlBulletFont = opts.bullet.fontFace ? voidEl("a:buFont", { typeface: opts.bullet.fontFace }) : "";
|
|
6757
|
-
if (isPictureBullet) if (opts.bullet._rId) {
|
|
6758
|
-
const svgExt = opts.bullet._rIdSvg ? el("a:extLst", null, raw(el("a:ext", { uri: "{96DAC541-7B7A-43D3-8B79-37D633B846F1}" }, raw(voidEl("asvg:svgBlip", {
|
|
6759
|
-
"xmlns:asvg": "http://schemas.microsoft.com/office/drawing/2016/SVG/main",
|
|
6760
|
-
"r:embed": `rId${opts.bullet._rIdSvg}`
|
|
6761
|
-
}))))) : "";
|
|
6762
|
-
strXmlBullet = strXmlBulletSize + el("a:buBlip", null, raw(svgExt ? el("a:blip", { "r:embed": `rId${opts.bullet._rId}` }, raw(svgExt)) : voidEl("a:blip", { "r:embed": `rId${opts.bullet._rId}` })));
|
|
6763
|
-
} else {
|
|
6764
|
-
warn("bullet/image-embed-failed", "picture `bullet.image` could not be embedded; using a default bullet glyph");
|
|
6765
|
-
strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar("•");
|
|
6766
|
-
}
|
|
6767
|
-
else if (opts.bullet.type && opts.bullet.type.toString().toLowerCase() === "number") strXmlBullet = strXmlBulletSize + (strXmlBulletFont || voidEl("a:buFont", { typeface: "+mj-lt" })) + voidEl("a:buAutoNum", {
|
|
6768
|
-
type: opts.bullet.numberType || "arabicPeriod",
|
|
6769
|
-
startAt: opts.bullet.numberStartAt || "1"
|
|
6770
|
-
});
|
|
6771
|
-
else if (opts.bullet.characterCode) {
|
|
6772
|
-
let bulletCode = `&#x${opts.bullet.characterCode};`;
|
|
6773
|
-
if (!/^[0-9A-Fa-f]{4}$/.test(opts.bullet.characterCode)) {
|
|
6774
|
-
warn("bullet/invalid-character-code", "`bullet.characterCode` should be a 4-digit unicode character (ex: 22AB)!");
|
|
6775
|
-
bulletCode = "•";
|
|
6776
|
-
}
|
|
6777
|
-
strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar(bulletCode);
|
|
6778
|
-
} else strXmlBullet = strXmlBulletSize + strXmlBulletFont + buChar("•");
|
|
6779
|
-
} else if (opts.bullet === "inherit") {} else if (opts.bullet) {
|
|
6780
|
-
defaultMarL = opts.indentLevel && opts.indentLevel > 0 ? bulletMarL + bulletMarL * opts.indentLevel : bulletMarL;
|
|
6781
|
-
defaultIndent = -bulletMarL;
|
|
6782
|
-
strXmlBullet = buChar("•");
|
|
6783
|
-
} else if (!opts.bullet) {
|
|
6784
|
-
defaultMarL = 0;
|
|
6785
|
-
defaultIndent = 0;
|
|
6786
|
-
indentBeforeMarL = true;
|
|
6787
|
-
strXmlBullet = voidEl("a:buNone");
|
|
6788
|
-
}
|
|
6789
|
-
const marLEmu = resolveParagraphMargin(opts.paraMarginLeft, defaultMarL, clampParaMarginEmu);
|
|
6790
|
-
const indentEmu = resolveParagraphMargin(opts.paraIndent, defaultIndent, clampParaIndentEmu);
|
|
6791
|
-
const marLAttr = marLEmu === null ? "" : ` marL="${marLEmu}"`;
|
|
6792
|
-
const indentAttr = indentEmu === null ? "" : ` indent="${indentEmu}"`;
|
|
6793
|
-
paragraphPropXml += indentBeforeMarL ? indentAttr + marLAttr : marLAttr + indentAttr;
|
|
6794
|
-
if (opts.tabStops && Array.isArray(opts.tabStops)) strXmlTabStops = el("a:tabLst", null, raw(opts.tabStops.map((stop) => voidEl("a:tab", {
|
|
6795
|
-
pos: inch2Emu(stop.position || 1),
|
|
6796
|
-
algn: stop.alignment || "l"
|
|
6797
|
-
})).join("")));
|
|
6798
|
-
paragraphPropXml += ">" + strXmlLnSpc + strXmlParaSpc + strXmlBulletColor + strXmlBullet + strXmlTabStops;
|
|
6799
|
-
if (isDefault) paragraphPropXml += genXmlTextRunProperties(opts, true);
|
|
6800
|
-
paragraphPropXml += "</" + tag + ">";
|
|
6801
|
-
}
|
|
6802
|
-
return paragraphPropXml;
|
|
6803
|
-
}
|
|
6804
|
-
/**
|
|
6805
|
-
* Generate XML Text Run Properties (`a:rPr`)
|
|
6806
|
-
* @param {ObjectOptions|TextPropsOptions} opts - text options
|
|
6807
|
-
* @param {boolean} isDefault - whether these are the default text run properties
|
|
6808
|
-
* @return {string} XML
|
|
6809
|
-
*/
|
|
6810
|
-
function genXmlTextRunProperties(opts, isDefault) {
|
|
6811
|
-
let runProps = "";
|
|
6812
|
-
const runPropsTag = isDefault ? "a:defRPr" : "a:rPr";
|
|
6813
|
-
const underline = typeof opts.underline === "object" && opts.underline?.style ? opts.underline.style : opts.hyperlink ? "sng" : null;
|
|
6814
|
-
const baseline = opts.baseline ? Math.round(opts.baseline * 50) : opts.subscript ? -4e4 : opts.superscript ? 3e4 : null;
|
|
6815
|
-
const attrs = {
|
|
6816
|
-
lang: opts.lang ? opts.lang : "en-US",
|
|
6817
|
-
altLang: opts.lang ? "en-US" : null,
|
|
6818
|
-
sz: opts.fontSize ? clampFontSizeSz(opts.fontSize) : null,
|
|
6819
|
-
b: opts?.bold ? "1" : null,
|
|
6820
|
-
i: opts?.italic ? "1" : null,
|
|
6821
|
-
strike: opts?.strike ? typeof opts.strike === "string" ? opts.strike : "sngStrike" : null,
|
|
6822
|
-
cap: opts?.caps ? opts.caps : null,
|
|
6823
|
-
u: underline,
|
|
6824
|
-
baseline,
|
|
6825
|
-
spc: opts.charSpacing ? clampCharSpacingSpc(opts.charSpacing) : null,
|
|
6826
|
-
kern: opts.charSpacing ? 0 : null,
|
|
6827
|
-
dirty: "0"
|
|
6828
|
-
};
|
|
6829
|
-
const hasShadow = !!opts.shadow && opts.shadow.type !== "none";
|
|
6830
|
-
if (opts.color || opts.fontFace || opts.outline || opts.glow || hasShadow || typeof opts.underline === "object" && opts.underline.color) {
|
|
6831
|
-
if (opts.outline && typeof opts.outline === "object") runProps += el("a:ln", { w: lineWidthToEmu(opts.outline.size || .75) }, raw(genXmlColorSelection(opts.outline.color || "FFFFFF")));
|
|
6832
|
-
if (opts.color) runProps += genXmlColorSelection({
|
|
6833
|
-
color: opts.color,
|
|
6834
|
-
transparency: opts.transparency
|
|
6835
|
-
});
|
|
6836
|
-
if (opts.glow || hasShadow) runProps += el("a:effectLst", null, [opts.glow ? raw(createGlowElement(opts.glow, DEF_TEXT_GLOW)) : null, hasShadow ? raw(createShadowElement(opts.shadow, DEF_TEXT_SHADOW)) : null]);
|
|
6837
|
-
if (opts.highlight) runProps += el("a:highlight", null, raw(createColorElement(opts.highlight)));
|
|
6838
|
-
if (typeof opts.underline === "object" && opts.underline.color) runProps += el("a:uFill", null, raw(genXmlColorSelection(opts.underline.color)));
|
|
6839
|
-
if (opts.fontFace) {
|
|
6840
|
-
runProps += voidEl("a:latin", {
|
|
6841
|
-
typeface: opts.fontFace,
|
|
6842
|
-
pitchFamily: "34",
|
|
6843
|
-
charset: "0"
|
|
6844
|
-
});
|
|
6845
|
-
if (opts.fontFaceEA) runProps += voidEl("a:ea", { typeface: opts.fontFaceEA });
|
|
6846
|
-
runProps += voidEl("a:cs", { typeface: opts.fontFace });
|
|
6847
|
-
}
|
|
6848
|
-
}
|
|
6849
|
-
if (opts.hyperlink) {
|
|
6850
|
-
if (typeof opts.hyperlink !== "object") throw new InvalidOptionError("hyperlink/not-an-object", "text `hyperlink` option should be an object. Ex: `hyperlink:{url:'https://github.com'}` ");
|
|
6851
|
-
else if (!opts.hyperlink.url && !opts.hyperlink.slide && !opts.hyperlink.action) throw new InvalidOptionError("hyperlink/missing-target", "text `hyperlink` requires either `url`, `slide`, or `action`");
|
|
6852
|
-
else if (opts.hyperlink.url || opts.hyperlink.slide) {
|
|
6853
|
-
const linkAttrs = opts.hyperlink.url ? {
|
|
6854
|
-
"r:id": `rId${opts.hyperlink._rId}`,
|
|
6855
|
-
invalidUrl: "",
|
|
6856
|
-
action: "",
|
|
6857
|
-
tgtFrame: "",
|
|
6858
|
-
tooltip: opts.hyperlink.tooltip ?? "",
|
|
6859
|
-
history: "1",
|
|
6860
|
-
highlightClick: "0",
|
|
6861
|
-
endSnd: "0"
|
|
6862
|
-
} : {
|
|
6863
|
-
"r:id": `rId${opts.hyperlink._rId}`,
|
|
6864
|
-
action: "ppaction://hlinksldjump",
|
|
6865
|
-
tooltip: opts.hyperlink.tooltip ?? ""
|
|
6866
|
-
};
|
|
6867
|
-
runProps += opts.color ? el("a:hlinkClick", linkAttrs, raw(HLINK_TEXT_COLOR_EXT)) : voidEl("a:hlinkClick", linkAttrs);
|
|
6868
|
-
}
|
|
6869
|
-
}
|
|
6870
|
-
return el(runPropsTag, attrs, raw(runProps));
|
|
6871
|
-
}
|
|
6872
|
-
/**
|
|
6873
|
-
* Build textBody text runs [`<a:r></a:r>`] for paragraphs [`<a:p>`]
|
|
6874
|
-
* @param {TextProps} textObj - Text object
|
|
6875
|
-
* @return {string} XML string
|
|
6876
|
-
*/
|
|
6877
|
-
function genXmlTextRun(textObj) {
|
|
6878
|
-
if (textObj.text === void 0 || textObj.text === null) return "";
|
|
6879
|
-
return el("a:r", null, [raw(genXmlTextRunProperties(textObj.options ?? {}, false)), raw(el("a:t", null, String(textObj.text)))]);
|
|
6880
|
-
}
|
|
6881
|
-
/**
|
|
6882
|
-
* Builds `<a:normAutofit>` with explicit fontScale/lnSpcReduction for "shrink text on overflow"
|
|
6883
|
-
* @param {TextFitShrinkProps} fit - shrink fit options
|
|
6884
|
-
* @return {string} XML string (`<a:normAutofit .../>`)
|
|
6885
|
-
* @see ECMA-376 CT_TextNormAutofit (attributes in 1000ths of a percent)
|
|
6886
|
-
*/
|
|
6887
|
-
function genXmlNormAutofit(fit) {
|
|
6888
|
-
const pct = (val, name) => {
|
|
6889
|
-
if (val === void 0 || val === null) return null;
|
|
6890
|
-
if (typeof val !== "number" || isNaN(val) || val < 0 || val > 100) {
|
|
6891
|
-
warn("text/invalid-fit-percentage", `fit.${name} must be a number between 0 and 100 (percent); received ${String(val)} - attribute ignored.`);
|
|
6892
|
-
return null;
|
|
6893
|
-
}
|
|
6894
|
-
return Math.round(val * FIXED_PCT_PER_PERCENT);
|
|
6895
|
-
};
|
|
6896
|
-
return voidEl("a:normAutofit", {
|
|
6897
|
-
fontScale: pct(fit.fontScale, "fontScale"),
|
|
6898
|
-
lnSpcReduction: pct(fit.lnSpcReduction, "lnSpcReduction")
|
|
6899
|
-
});
|
|
6900
|
-
}
|
|
6901
|
-
/**
|
|
6902
|
-
* Group the flat run list into paragraphs (lines): a new line starts on a display-math run,
|
|
6903
|
-
* an alignment change, or a bullet, and closes after a breakLine. Returns the per-line run arrays.
|
|
6904
|
-
*/
|
|
6905
|
-
function groupRunsIntoLines(arrTextObjects, opts) {
|
|
6906
|
-
const arrLines = [];
|
|
6907
|
-
let arrTexts = [];
|
|
6908
|
-
arrTextObjects.forEach((textObj, idx) => {
|
|
6909
|
-
if (textObj.math && !textObj.inline) {
|
|
6910
|
-
if (arrTexts.length > 0) {
|
|
6911
|
-
arrLines.push(arrTexts);
|
|
6912
|
-
arrTexts = [];
|
|
6913
|
-
}
|
|
6914
|
-
arrLines.push([textObj]);
|
|
6915
|
-
return;
|
|
6916
|
-
}
|
|
6917
|
-
if (arrTexts.length > 0 && (textObj.options.align || opts.align)) {
|
|
6918
|
-
if (textObj.options.align !== arrTextObjects[idx - 1]?.options.align) {
|
|
6919
|
-
arrLines.push(arrTexts);
|
|
6920
|
-
arrTexts = [];
|
|
6921
|
-
}
|
|
6922
|
-
} else if (arrTexts.length > 0 && emitsBulletMarkup(textObj.options.bullet)) {
|
|
6923
|
-
arrLines.push(arrTexts);
|
|
6924
|
-
arrTexts = [];
|
|
6925
|
-
textObj.options.breakLine = false;
|
|
6926
|
-
}
|
|
6927
|
-
arrTexts.push(textObj);
|
|
6928
|
-
if (arrTexts.length > 0 && textObj.options.breakLine) {
|
|
6929
|
-
if (idx + 1 < arrTextObjects.length) {
|
|
6930
|
-
arrLines.push(arrTexts);
|
|
6931
|
-
arrTexts = [];
|
|
6932
|
-
}
|
|
6933
|
-
}
|
|
6934
|
-
if (idx + 1 === arrTextObjects.length) arrLines.push(arrTexts);
|
|
6935
|
-
});
|
|
6936
|
-
return arrLines;
|
|
6937
|
-
}
|
|
6938
|
-
/**
|
|
6939
|
-
* Render each grouped line to an `<a:p>` paragraph: paragraph props, inherited run options,
|
|
6940
|
-
* text runs (and inline/display math), and the closing endParaRPr.
|
|
6941
|
-
*/
|
|
6942
|
-
function renderTextParagraphsXml(arrLines, slideObj, opts) {
|
|
6943
|
-
let strSlideXml = "";
|
|
6944
|
-
arrLines.forEach((line) => {
|
|
6945
|
-
const firstRun = line[0];
|
|
6946
|
-
if (line.length === 1 && firstRun?.math && !firstRun.inline) {
|
|
6947
|
-
strSlideXml += genXmlMathParagraph(firstRun.math);
|
|
6948
|
-
return;
|
|
6949
|
-
}
|
|
6950
|
-
let reqsClosingFontSize = false;
|
|
6951
|
-
let paraXml = "";
|
|
6952
|
-
let paragraphPropEmitted = false;
|
|
6953
|
-
line.forEach((textObj, idx) => {
|
|
6954
|
-
textObj.options._lineIdx = idx;
|
|
6955
|
-
if (idx > 0 && textObj.options.softBreakBefore) paraXml += voidEl("a:br");
|
|
6956
|
-
textObj.options.align = textObj.options.align || opts.align;
|
|
6957
|
-
textObj.options.lineSpacing = textObj.options.lineSpacing || opts.lineSpacing;
|
|
6958
|
-
textObj.options.lineSpacingMultiple = textObj.options.lineSpacingMultiple || opts.lineSpacingMultiple;
|
|
6959
|
-
textObj.options.indentLevel = textObj.options.indentLevel || opts.indentLevel;
|
|
6960
|
-
textObj.options.paraSpaceBefore = textObj.options.paraSpaceBefore || opts.paraSpaceBefore;
|
|
6961
|
-
textObj.options.paraSpaceAfter = textObj.options.paraSpaceAfter || opts.paraSpaceAfter;
|
|
6962
|
-
textObj.options.paraMarginLeft = textObj.options.paraMarginLeft ?? opts.paraMarginLeft;
|
|
6963
|
-
textObj.options.paraIndent = textObj.options.paraIndent ?? opts.paraIndent;
|
|
6964
|
-
if (!paragraphPropEmitted) {
|
|
6965
|
-
paragraphPropEmitted = true;
|
|
6966
|
-
paraXml += genXmlParagraphProperties(textObj, false).replace("<a:pPr></a:pPr>", "");
|
|
6967
|
-
}
|
|
6968
|
-
const textOptions = textObj.options;
|
|
6969
|
-
Object.entries(opts).filter(([key]) => !(textObj.options.hyperlink && key === "color")).forEach(([key, val]) => {
|
|
6970
|
-
if (key !== "bullet" && !textOptions[key]) textOptions[key] = val;
|
|
6971
|
-
});
|
|
6972
|
-
let _textRunObj = textObj;
|
|
6973
|
-
if (idx === 0 && emitsBulletMarkup(line[0]?.options.bullet) && typeof textObj.text === "string") {
|
|
6974
|
-
const _stripped = textObj.text.replace(/^[\u2022\u25E6\u25AA\u25AB\u25CF\u25CB\u2023\u2043\u2219]\s*/, "");
|
|
6975
|
-
if (_stripped !== textObj.text) _textRunObj = {
|
|
6976
|
-
text: _stripped,
|
|
6977
|
-
options: textObj.options
|
|
6978
|
-
};
|
|
6979
|
-
}
|
|
6980
|
-
const isEmptyBreakArtifact = _textRunObj.text === "" && line.length > 1;
|
|
6981
|
-
if (_textRunObj.math) paraXml += genXmlInlineMath(_textRunObj.math);
|
|
6982
|
-
else if (!isEmptyBreakArtifact) paraXml += genXmlTextRun(_textRunObj);
|
|
6983
|
-
if (!textObj.text && opts.fontSize || textObj.options.fontSize) {
|
|
6984
|
-
reqsClosingFontSize = true;
|
|
6985
|
-
opts.fontSize = opts.fontSize || textObj.options.fontSize;
|
|
6986
|
-
}
|
|
6987
|
-
});
|
|
6988
|
-
const sizedAttrs = () => ({
|
|
6989
|
-
lang: opts.lang || "en-US",
|
|
6990
|
-
sz: opts.fontSize ? clampFontSizeSz(opts.fontSize) : null,
|
|
6991
|
-
dirty: "0"
|
|
6992
|
-
});
|
|
6993
|
-
if (slideObj._type === "tablecell" && (opts.fontSize || opts.fontFace)) if (opts.fontFace) paraXml += el("a:endParaRPr", sizedAttrs(), [
|
|
6994
|
-
raw(voidEl("a:latin", {
|
|
6995
|
-
typeface: opts.fontFace,
|
|
6996
|
-
charset: "0"
|
|
6997
|
-
})),
|
|
6998
|
-
opts.fontFaceEA ? raw(voidEl("a:ea", { typeface: opts.fontFaceEA })) : null,
|
|
6999
|
-
raw(voidEl("a:cs", { typeface: opts.fontFace }))
|
|
7000
|
-
]);
|
|
7001
|
-
else paraXml += voidEl("a:endParaRPr", sizedAttrs());
|
|
7002
|
-
else if (reqsClosingFontSize) paraXml += voidEl("a:endParaRPr", sizedAttrs());
|
|
7003
|
-
else paraXml += voidEl("a:endParaRPr", {
|
|
7004
|
-
lang: opts.lang || "en-US",
|
|
7005
|
-
dirty: "0"
|
|
7006
|
-
});
|
|
7007
|
-
strSlideXml += el("a:p", null, raw(paraXml));
|
|
7008
|
-
});
|
|
7009
|
-
return strSlideXml;
|
|
7010
|
-
}
|
|
7011
|
-
//#endregion
|
|
7012
6030
|
//#region src/gen/drawingml/text-body.ts
|
|
7013
6031
|
/**
|
|
7014
6032
|
* ts-pptx: DrawingML text body
|
|
@@ -7514,29 +6532,32 @@ function genXmlCustGeom(options, cx, cy, layout) {
|
|
|
7514
6532
|
if (cxnEls.length) cxnLst = el("a:cxnLst", null, cxnEls.map(raw));
|
|
7515
6533
|
}
|
|
7516
6534
|
let ahLst = voidEl("a:ahLst", null, SPACE_BEFORE_SLASH);
|
|
7517
|
-
if (options.adjustHandles?.length)
|
|
7518
|
-
const
|
|
7519
|
-
|
|
7520
|
-
|
|
7521
|
-
|
|
7522
|
-
|
|
7523
|
-
gdRefR
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
|
|
7532
|
-
|
|
7533
|
-
|
|
7534
|
-
|
|
7535
|
-
|
|
7536
|
-
|
|
7537
|
-
|
|
7538
|
-
|
|
7539
|
-
|
|
6535
|
+
if (options.adjustHandles?.length) {
|
|
6536
|
+
const ahEls = options.adjustHandles.map((h) => {
|
|
6537
|
+
const pos = raw(voidEl("a:pos", {
|
|
6538
|
+
x: adjCoord(h.x, "X"),
|
|
6539
|
+
y: adjCoord(h.y, "Y")
|
|
6540
|
+
}));
|
|
6541
|
+
if ("gdRefR" in h || "minR" in h || "maxR" in h || "gdRefAng" in h || "minAng" in h || "maxAng" in h) return el("a:ahPolar", {
|
|
6542
|
+
gdRefR: h.gdRefR,
|
|
6543
|
+
minR: adjCoord(h.minR, "X"),
|
|
6544
|
+
maxR: adjCoord(h.maxR, "X"),
|
|
6545
|
+
gdRefAng: h.gdRefAng,
|
|
6546
|
+
minAng: h.minAng == null ? void 0 : convertRotationDegrees(h.minAng),
|
|
6547
|
+
maxAng: h.maxAng == null ? void 0 : convertRotationDegrees(h.maxAng)
|
|
6548
|
+
}, pos);
|
|
6549
|
+
const xy = h;
|
|
6550
|
+
return el("a:ahXY", {
|
|
6551
|
+
gdRefX: xy.gdRefX,
|
|
6552
|
+
minX: adjCoord(xy.minX, "X"),
|
|
6553
|
+
maxX: adjCoord(xy.maxX, "X"),
|
|
6554
|
+
gdRefY: xy.gdRefY,
|
|
6555
|
+
minY: adjCoord(xy.minY, "Y"),
|
|
6556
|
+
maxY: adjCoord(xy.maxY, "Y")
|
|
6557
|
+
}, pos);
|
|
6558
|
+
});
|
|
6559
|
+
ahLst = el("a:ahLst", null, ahEls.map(raw));
|
|
6560
|
+
}
|
|
7540
6561
|
return el("a:custGeom", null, [
|
|
7541
6562
|
voidEl("a:avLst", null, SPACE_BEFORE_SLASH),
|
|
7542
6563
|
gdLst,
|
|
@@ -7600,11 +6621,15 @@ const ImageSizingXml = {
|
|
|
7600
6621
|
].filter(Boolean).join(", ");
|
|
7601
6622
|
throw new InvalidOptionError("image/crop-window-overflows", `addImage sizing.type 'crop': crop window overflows image bounds — ${over}. Ensure x≥0, y≥0, x+w≤w, y+h≤h.`);
|
|
7602
6623
|
}
|
|
6624
|
+
const lPerc = Math.round(1e5 * (l / imgSize.w));
|
|
6625
|
+
const rPerc = Math.round(1e5 * (r / imgSize.w));
|
|
6626
|
+
const tPerc = Math.round(1e5 * (t / imgSize.h));
|
|
6627
|
+
const bPerc = Math.round(1e5 * (b / imgSize.h));
|
|
7603
6628
|
return voidEl("a:srcRect", {
|
|
7604
|
-
l:
|
|
7605
|
-
r:
|
|
7606
|
-
t:
|
|
7607
|
-
b:
|
|
6629
|
+
l: lPerc,
|
|
6630
|
+
r: rPerc,
|
|
6631
|
+
t: tPerc,
|
|
6632
|
+
b: bPerc
|
|
7608
6633
|
}) + STRETCH;
|
|
7609
6634
|
}
|
|
7610
6635
|
};
|
|
@@ -8966,7 +7991,11 @@ function slideObjectToXml(slide) {
|
|
|
8966
7991
|
strSlideXml += "</p:grpSp>";
|
|
8967
7992
|
break;
|
|
8968
7993
|
}
|
|
8969
|
-
|
|
7994
|
+
case "notes": break;
|
|
7995
|
+
case "tablecell": break;
|
|
7996
|
+
case "hyperlink": break;
|
|
7997
|
+
case "online": break;
|
|
7998
|
+
default: slideItemObj._type;
|
|
8970
7999
|
}
|
|
8971
8000
|
return strSlideXml;
|
|
8972
8001
|
};
|
|
@@ -9061,17 +8090,19 @@ function slideObjectRelationsToXml(slide, defaultRels) {
|
|
|
9061
8090
|
const hasTarget = (target) => rels.some((xml) => xml.includes(` Target="${target}"`));
|
|
9062
8091
|
slide._rels.forEach((rel) => {
|
|
9063
8092
|
lastRid = Math.max(lastRid, rel.rId);
|
|
9064
|
-
if (isHyperlinkRel(rel))
|
|
9065
|
-
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9073
|
-
|
|
9074
|
-
|
|
8093
|
+
if (isHyperlinkRel(rel)) {
|
|
8094
|
+
if (rel.data === "slide") rels.push(voidEl("Relationship", {
|
|
8095
|
+
Id: `rId${rel.rId}`,
|
|
8096
|
+
Type: OFFICE_REL + "slide",
|
|
8097
|
+
Target: `slide${rel.Target}.xml`
|
|
8098
|
+
}));
|
|
8099
|
+
else rels.push(voidEl("Relationship", {
|
|
8100
|
+
Id: `rId${rel.rId}`,
|
|
8101
|
+
Type: OFFICE_REL + "hyperlink",
|
|
8102
|
+
Target: rel.Target,
|
|
8103
|
+
TargetMode: "External"
|
|
8104
|
+
}));
|
|
8105
|
+
}
|
|
9075
8106
|
});
|
|
9076
8107
|
(slide._relsChart || []).forEach((rel) => {
|
|
9077
8108
|
lastRid = Math.max(lastRid, rel.rId);
|
|
@@ -9182,527 +8213,6 @@ function getLayoutIdxForSlide(slides, slideLayouts, slideNumber) {
|
|
|
9182
8213
|
return 1;
|
|
9183
8214
|
}
|
|
9184
8215
|
//#endregion
|
|
9185
|
-
//#region src/gen/slide/notes.ts
|
|
9186
|
-
/**
|
|
9187
|
-
* Collect the speaker-notes runs for a slide (flattened across any number of `addNotes()` calls).
|
|
9188
|
-
* @param {PresSlideInternal} slide - the slide object
|
|
9189
|
-
* @return {TextProps[]} notes text runs in document order
|
|
9190
|
-
*/
|
|
9191
|
-
function getNotesRuns(slide) {
|
|
9192
|
-
const runs = [];
|
|
9193
|
-
slide._slideObjects.forEach((obj) => {
|
|
9194
|
-
if (obj._type === "notes" && obj.text) runs.push(...obj.text);
|
|
9195
|
-
});
|
|
9196
|
-
return runs;
|
|
9197
|
-
}
|
|
9198
|
-
/**
|
|
9199
|
-
* Build (and cache) the hyperlink relationships for a slide's notes part (`notesSlideN.xml.rels`).
|
|
9200
|
-
*
|
|
9201
|
-
* Notes rels use their own namespace, independent of `slide._rels` (which serialize to
|
|
9202
|
-
* `slideN.xml.rels`). The notes part always reserves rId1=notesMaster and rId2=slide, so
|
|
9203
|
-
* dynamic hyperlink rels are allocated starting at rId3. Each notes hyperlink run is tagged
|
|
9204
|
-
* with its `_rId` so the body serializer and the rels file agree.
|
|
9205
|
-
*
|
|
9206
|
-
* Idempotent: the result is cached on `slide._relsNotes` and reused by both callers.
|
|
9207
|
-
* Only external `url` hyperlinks are supported; `slide` targets are ignored with a warning.
|
|
9208
|
-
* @param {PresSlideInternal} slide - the slide object
|
|
9209
|
-
* @return {SlideRel[]} notes hyperlink relationships
|
|
9210
|
-
*/
|
|
9211
|
-
function buildNotesSlideRels(slide) {
|
|
9212
|
-
if (slide._relsNotes) return slide._relsNotes;
|
|
9213
|
-
const NOTES_REL_RESERVED = 2;
|
|
9214
|
-
const rels = [];
|
|
9215
|
-
let lastRid = NOTES_REL_RESERVED;
|
|
9216
|
-
getNotesRuns(slide).forEach((run) => {
|
|
9217
|
-
const hyperlink = run.options?.hyperlink;
|
|
9218
|
-
if (!hyperlink) return;
|
|
9219
|
-
if (!hyperlink.url) {
|
|
9220
|
-
if (hyperlink.slide) warn("notes/hyperlink-slide-unsupported", "notes hyperlinks support `url` only (ignoring `slide` target)");
|
|
9221
|
-
if (run.options) delete run.options.hyperlink;
|
|
9222
|
-
return;
|
|
9223
|
-
}
|
|
9224
|
-
lastRid++;
|
|
9225
|
-
hyperlink._rId = lastRid;
|
|
9226
|
-
rels.push({
|
|
9227
|
-
type: "hyperlink",
|
|
9228
|
-
data: "dummy",
|
|
9229
|
-
rId: lastRid,
|
|
9230
|
-
Target: hyperlink.url
|
|
9231
|
-
});
|
|
9232
|
-
});
|
|
9233
|
-
slide._relsNotes = rels;
|
|
9234
|
-
return rels;
|
|
9235
|
-
}
|
|
9236
|
-
/**
|
|
9237
|
-
* Build the `<p:txBody>` paragraphs for the notes placeholder.
|
|
9238
|
-
* Runs are split into `<a:p>` paragraphs on newlines; each run is serialized with the standard
|
|
9239
|
-
* text-run generator so inline formatting and `<a:hlinkClick>` markup are emitted consistently.
|
|
9240
|
-
* @param {PresSlideInternal} slide - the slide object
|
|
9241
|
-
* @return {string} XML string of `<a:p>` paragraphs
|
|
9242
|
-
*/
|
|
9243
|
-
function genXmlNotesParagraphs(slide) {
|
|
9244
|
-
const paragraphs = [[]];
|
|
9245
|
-
getNotesRuns(slide).forEach((run) => {
|
|
9246
|
-
String(run.text ?? "").split("\n").forEach((segment, idx) => {
|
|
9247
|
-
if (idx > 0) paragraphs.push([]);
|
|
9248
|
-
const text = segment.replace(/\r/g, "");
|
|
9249
|
-
if (text !== "") paragraphs[paragraphs.length - 1]?.push({
|
|
9250
|
-
text,
|
|
9251
|
-
options: run.options || {}
|
|
9252
|
-
});
|
|
9253
|
-
});
|
|
9254
|
-
});
|
|
9255
|
-
return paragraphs.map((runs) => el("a:p", null, [raw(runs.map((run) => genXmlTextRun(run)).join("")), raw(voidEl("a:endParaRPr", {
|
|
9256
|
-
lang: "en-US",
|
|
9257
|
-
dirty: 0
|
|
9258
|
-
}))])).join("");
|
|
9259
|
-
}
|
|
9260
|
-
/**
|
|
9261
|
-
* Generate XML for Notes Master (notesMaster1.xml)
|
|
9262
|
-
* @returns {string} XML
|
|
9263
|
-
*/
|
|
9264
|
-
function makeXmlNotesMaster() {
|
|
9265
|
-
const xfrmOffExt = (x, y, cx, cy) => el("a:xfrm", null, [raw(voidEl("a:off", {
|
|
9266
|
-
x,
|
|
9267
|
-
y
|
|
9268
|
-
})), raw(voidEl("a:ext", {
|
|
9269
|
-
cx,
|
|
9270
|
-
cy
|
|
9271
|
-
}))]);
|
|
9272
|
-
const prstGeomRect = el("a:prstGeom", { prst: "rect" }, raw(voidEl("a:avLst")));
|
|
9273
|
-
const spLocks = (attrs) => el("p:cNvSpPr", null, raw(voidEl("a:spLocks", attrs)));
|
|
9274
|
-
const nvPr = (phAttrs) => el("p:nvPr", null, raw(voidEl("p:ph", phAttrs)));
|
|
9275
|
-
const bodyPr = (attrs) => voidEl("a:bodyPr", attrs);
|
|
9276
|
-
const lstStyleLvl1 = (algn) => el("a:lstStyle", null, raw(el("a:lvl1pPr", { algn }, raw(voidEl("a:defRPr", { sz: 1200 })))));
|
|
9277
|
-
const emptyPara = el("a:p", null, raw(voidEl("a:endParaRPr", { lang: "en-US" })));
|
|
9278
|
-
const header = el("p:sp", null, [
|
|
9279
|
-
raw(el("p:nvSpPr", null, [
|
|
9280
|
-
raw(voidEl("p:cNvPr", {
|
|
9281
|
-
id: 2,
|
|
9282
|
-
name: "Header Placeholder 1"
|
|
9283
|
-
})),
|
|
9284
|
-
raw(spLocks({ noGrp: 1 })),
|
|
9285
|
-
raw(nvPr({
|
|
9286
|
-
type: "hdr",
|
|
9287
|
-
sz: "quarter"
|
|
9288
|
-
}))
|
|
9289
|
-
])),
|
|
9290
|
-
raw(el("p:spPr", null, [raw(xfrmOffExt(0, 0, 2971800, 458788)), raw(prstGeomRect)])),
|
|
9291
|
-
raw(el("p:txBody", null, [
|
|
9292
|
-
raw(bodyPr({
|
|
9293
|
-
vert: "horz",
|
|
9294
|
-
lIns: 91440,
|
|
9295
|
-
tIns: 45720,
|
|
9296
|
-
rIns: 91440,
|
|
9297
|
-
bIns: 45720,
|
|
9298
|
-
rtlCol: 0
|
|
9299
|
-
})),
|
|
9300
|
-
raw(lstStyleLvl1("l")),
|
|
9301
|
-
raw(emptyPara)
|
|
9302
|
-
]))
|
|
9303
|
-
]);
|
|
9304
|
-
const date = el("p:sp", null, [
|
|
9305
|
-
raw(el("p:nvSpPr", null, [
|
|
9306
|
-
raw(voidEl("p:cNvPr", {
|
|
9307
|
-
id: 3,
|
|
9308
|
-
name: "Date Placeholder 2"
|
|
9309
|
-
})),
|
|
9310
|
-
raw(spLocks({ noGrp: 1 })),
|
|
9311
|
-
raw(nvPr({
|
|
9312
|
-
type: "dt",
|
|
9313
|
-
idx: 1
|
|
9314
|
-
}))
|
|
9315
|
-
])),
|
|
9316
|
-
raw(el("p:spPr", null, [raw(xfrmOffExt(3884613, 0, 2971800, 458788)), raw(prstGeomRect)])),
|
|
9317
|
-
raw(el("p:txBody", null, [
|
|
9318
|
-
raw(bodyPr({
|
|
9319
|
-
vert: "horz",
|
|
9320
|
-
lIns: 91440,
|
|
9321
|
-
tIns: 45720,
|
|
9322
|
-
rIns: 91440,
|
|
9323
|
-
bIns: 45720,
|
|
9324
|
-
rtlCol: 0
|
|
9325
|
-
})),
|
|
9326
|
-
raw(lstStyleLvl1("r")),
|
|
9327
|
-
raw(el("a:p", null, [raw(el("a:fld", {
|
|
9328
|
-
id: "{5282F153-3F37-0F45-9E97-73ACFA13230C}",
|
|
9329
|
-
type: "datetimeFigureOut"
|
|
9330
|
-
}, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, "7/23/19"))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
|
|
9331
|
-
]))
|
|
9332
|
-
]);
|
|
9333
|
-
const slideImg = el("p:sp", null, [
|
|
9334
|
-
raw(el("p:nvSpPr", null, [
|
|
9335
|
-
raw(voidEl("p:cNvPr", {
|
|
9336
|
-
id: 4,
|
|
9337
|
-
name: "Slide Image Placeholder 3"
|
|
9338
|
-
})),
|
|
9339
|
-
raw(spLocks({
|
|
9340
|
-
noGrp: 1,
|
|
9341
|
-
noRot: 1,
|
|
9342
|
-
noChangeAspect: 1
|
|
9343
|
-
})),
|
|
9344
|
-
raw(nvPr({
|
|
9345
|
-
type: "sldImg",
|
|
9346
|
-
idx: 2
|
|
9347
|
-
}))
|
|
9348
|
-
])),
|
|
9349
|
-
raw(el("p:spPr", null, [
|
|
9350
|
-
raw(xfrmOffExt(685800, 1143e3, 5486400, 3086100)),
|
|
9351
|
-
raw(prstGeomRect),
|
|
9352
|
-
raw(voidEl("a:noFill")),
|
|
9353
|
-
raw(el("a:ln", { w: 12700 }, raw(el("a:solidFill", null, raw(voidEl("a:prstClr", { val: "black" }))))))
|
|
9354
|
-
])),
|
|
9355
|
-
raw(el("p:txBody", null, [
|
|
9356
|
-
raw(bodyPr({
|
|
9357
|
-
vert: "horz",
|
|
9358
|
-
lIns: 91440,
|
|
9359
|
-
tIns: 45720,
|
|
9360
|
-
rIns: 91440,
|
|
9361
|
-
bIns: 45720,
|
|
9362
|
-
rtlCol: 0,
|
|
9363
|
-
anchor: "ctr"
|
|
9364
|
-
})),
|
|
9365
|
-
raw(voidEl("a:lstStyle")),
|
|
9366
|
-
raw(emptyPara)
|
|
9367
|
-
]))
|
|
9368
|
-
]);
|
|
9369
|
-
const notesPlaceholder = el("p:sp", null, [
|
|
9370
|
-
raw(el("p:nvSpPr", null, [
|
|
9371
|
-
raw(voidEl("p:cNvPr", {
|
|
9372
|
-
id: 5,
|
|
9373
|
-
name: "Notes Placeholder 4"
|
|
9374
|
-
})),
|
|
9375
|
-
raw(spLocks({ noGrp: 1 })),
|
|
9376
|
-
raw(nvPr({
|
|
9377
|
-
type: "body",
|
|
9378
|
-
sz: "quarter",
|
|
9379
|
-
idx: 3
|
|
9380
|
-
}))
|
|
9381
|
-
])),
|
|
9382
|
-
raw(el("p:spPr", null, [raw(xfrmOffExt(685800, 4400550, 5486400, 3600450)), raw(prstGeomRect)])),
|
|
9383
|
-
raw(el("p:txBody", null, [
|
|
9384
|
-
raw(bodyPr({
|
|
9385
|
-
vert: "horz",
|
|
9386
|
-
lIns: 91440,
|
|
9387
|
-
tIns: 45720,
|
|
9388
|
-
rIns: 91440,
|
|
9389
|
-
bIns: 45720,
|
|
9390
|
-
rtlCol: 0
|
|
9391
|
-
})),
|
|
9392
|
-
raw(voidEl("a:lstStyle")),
|
|
9393
|
-
raw([
|
|
9394
|
-
"Click to edit Master text styles",
|
|
9395
|
-
"Second level",
|
|
9396
|
-
"Third level",
|
|
9397
|
-
"Fourth level",
|
|
9398
|
-
"Fifth level"
|
|
9399
|
-
].map((text, lvl) => el("a:p", null, [raw(voidEl("a:pPr", { lvl })), raw(el("a:r", null, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, text))]))])).join(""))
|
|
9400
|
-
]))
|
|
9401
|
-
]);
|
|
9402
|
-
const footer = el("p:sp", null, [
|
|
9403
|
-
raw(el("p:nvSpPr", null, [
|
|
9404
|
-
raw(voidEl("p:cNvPr", {
|
|
9405
|
-
id: 6,
|
|
9406
|
-
name: "Footer Placeholder 5"
|
|
9407
|
-
})),
|
|
9408
|
-
raw(spLocks({ noGrp: 1 })),
|
|
9409
|
-
raw(nvPr({
|
|
9410
|
-
type: "ftr",
|
|
9411
|
-
sz: "quarter",
|
|
9412
|
-
idx: 4
|
|
9413
|
-
}))
|
|
9414
|
-
])),
|
|
9415
|
-
raw(el("p:spPr", null, [raw(xfrmOffExt(0, 8685213, 2971800, 458787)), raw(prstGeomRect)])),
|
|
9416
|
-
raw(el("p:txBody", null, [
|
|
9417
|
-
raw(bodyPr({
|
|
9418
|
-
vert: "horz",
|
|
9419
|
-
lIns: 91440,
|
|
9420
|
-
tIns: 45720,
|
|
9421
|
-
rIns: 91440,
|
|
9422
|
-
bIns: 45720,
|
|
9423
|
-
rtlCol: 0,
|
|
9424
|
-
anchor: "b"
|
|
9425
|
-
})),
|
|
9426
|
-
raw(lstStyleLvl1("l")),
|
|
9427
|
-
raw(emptyPara)
|
|
9428
|
-
]))
|
|
9429
|
-
]);
|
|
9430
|
-
const slideNum = el("p:sp", null, [
|
|
9431
|
-
raw(el("p:nvSpPr", null, [
|
|
9432
|
-
raw(voidEl("p:cNvPr", {
|
|
9433
|
-
id: 7,
|
|
9434
|
-
name: "Slide Number Placeholder 6"
|
|
9435
|
-
})),
|
|
9436
|
-
raw(spLocks({ noGrp: 1 })),
|
|
9437
|
-
raw(nvPr({
|
|
9438
|
-
type: "sldNum",
|
|
9439
|
-
sz: "quarter",
|
|
9440
|
-
idx: 5
|
|
9441
|
-
}))
|
|
9442
|
-
])),
|
|
9443
|
-
raw(el("p:spPr", null, [raw(xfrmOffExt(3884613, 8685213, 2971800, 458787)), raw(prstGeomRect)])),
|
|
9444
|
-
raw(el("p:txBody", null, [
|
|
9445
|
-
raw(bodyPr({
|
|
9446
|
-
vert: "horz",
|
|
9447
|
-
lIns: 91440,
|
|
9448
|
-
tIns: 45720,
|
|
9449
|
-
rIns: 91440,
|
|
9450
|
-
bIns: 45720,
|
|
9451
|
-
rtlCol: 0,
|
|
9452
|
-
anchor: "b"
|
|
9453
|
-
})),
|
|
9454
|
-
raw(lstStyleLvl1("r")),
|
|
9455
|
-
raw(el("a:p", null, [raw(el("a:fld", {
|
|
9456
|
-
id: "{CE5E9CC1-C706-0F49-92D6-E571CC5EEA8F}",
|
|
9457
|
-
type: "slidenum"
|
|
9458
|
-
}, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, "‹#›"))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
|
|
9459
|
-
]))
|
|
9460
|
-
]);
|
|
9461
|
-
const spTree = el("p:spTree", null, [
|
|
9462
|
-
raw(el("p:nvGrpSpPr", null, [
|
|
9463
|
-
raw(voidEl("p:cNvPr", {
|
|
9464
|
-
id: 1,
|
|
9465
|
-
name: ""
|
|
9466
|
-
})),
|
|
9467
|
-
raw(voidEl("p:cNvGrpSpPr")),
|
|
9468
|
-
raw(voidEl("p:nvPr"))
|
|
9469
|
-
])),
|
|
9470
|
-
raw(el("p:grpSpPr", null, raw(el("a:xfrm", null, [
|
|
9471
|
-
raw(voidEl("a:off", {
|
|
9472
|
-
x: 0,
|
|
9473
|
-
y: 0
|
|
9474
|
-
})),
|
|
9475
|
-
raw(voidEl("a:ext", {
|
|
9476
|
-
cx: 0,
|
|
9477
|
-
cy: 0
|
|
9478
|
-
})),
|
|
9479
|
-
raw(voidEl("a:chOff", {
|
|
9480
|
-
x: 0,
|
|
9481
|
-
y: 0
|
|
9482
|
-
})),
|
|
9483
|
-
raw(voidEl("a:chExt", {
|
|
9484
|
-
cx: 0,
|
|
9485
|
-
cy: 0
|
|
9486
|
-
}))
|
|
9487
|
-
])))),
|
|
9488
|
-
raw(header),
|
|
9489
|
-
raw(date),
|
|
9490
|
-
raw(slideImg),
|
|
9491
|
-
raw(notesPlaceholder),
|
|
9492
|
-
raw(footer),
|
|
9493
|
-
raw(slideNum)
|
|
9494
|
-
]);
|
|
9495
|
-
const extLst = el("p:extLst", null, raw(el("p:ext", { uri: "{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}" }, raw(voidEl("p14:creationId", {
|
|
9496
|
-
"xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
|
|
9497
|
-
val: 1024086991
|
|
9498
|
-
})))));
|
|
9499
|
-
const cSld = el("p:cSld", null, [
|
|
9500
|
-
raw(el("p:bg", null, raw(el("p:bgRef", { idx: 1001 }, raw(voidEl("a:schemeClr", { val: "bg1" })))))),
|
|
9501
|
-
raw(spTree),
|
|
9502
|
-
raw(extLst)
|
|
9503
|
-
]);
|
|
9504
|
-
const clrMap = voidEl("p:clrMap", {
|
|
9505
|
-
bg1: "lt1",
|
|
9506
|
-
tx1: "dk1",
|
|
9507
|
-
bg2: "lt2",
|
|
9508
|
-
tx2: "dk2",
|
|
9509
|
-
accent1: "accent1",
|
|
9510
|
-
accent2: "accent2",
|
|
9511
|
-
accent3: "accent3",
|
|
9512
|
-
accent4: "accent4",
|
|
9513
|
-
accent5: "accent5",
|
|
9514
|
-
accent6: "accent6",
|
|
9515
|
-
hlink: "hlink",
|
|
9516
|
-
folHlink: "folHlink"
|
|
9517
|
-
});
|
|
9518
|
-
const notesStyleLevel = (n, marL) => el(`a:lvl${n}pPr`, {
|
|
9519
|
-
marL,
|
|
9520
|
-
algn: "l",
|
|
9521
|
-
defTabSz: 914400,
|
|
9522
|
-
rtl: 0,
|
|
9523
|
-
eaLnBrk: 1,
|
|
9524
|
-
latinLnBrk: 0,
|
|
9525
|
-
hangingPunct: 1
|
|
9526
|
-
}, raw(el("a:defRPr", {
|
|
9527
|
-
sz: 1200,
|
|
9528
|
-
kern: 1200
|
|
9529
|
-
}, [
|
|
9530
|
-
raw(el("a:solidFill", null, raw(voidEl("a:schemeClr", { val: "tx1" })))),
|
|
9531
|
-
raw(voidEl("a:latin", { typeface: "+mn-lt" })),
|
|
9532
|
-
raw(voidEl("a:ea", { typeface: "+mn-ea" })),
|
|
9533
|
-
raw(voidEl("a:cs", { typeface: "+mn-cs" }))
|
|
9534
|
-
])));
|
|
9535
|
-
const notesStyle = el("p:notesStyle", null, raw([
|
|
9536
|
-
0,
|
|
9537
|
-
457200,
|
|
9538
|
-
914400,
|
|
9539
|
-
1371600,
|
|
9540
|
-
1828800,
|
|
9541
|
-
2286e3,
|
|
9542
|
-
2743200,
|
|
9543
|
-
3200400,
|
|
9544
|
-
3657600
|
|
9545
|
-
].map((marL, i) => notesStyleLevel(i + 1, marL)).join("")));
|
|
9546
|
-
return XML_DECL + "\r\n" + el("p:notesMaster", {
|
|
9547
|
-
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
9548
|
-
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
9549
|
-
"xmlns:p": "http://schemas.openxmlformats.org/presentationml/2006/main"
|
|
9550
|
-
}, [
|
|
9551
|
-
raw(cSld),
|
|
9552
|
-
raw(clrMap),
|
|
9553
|
-
raw(notesStyle)
|
|
9554
|
-
]);
|
|
9555
|
-
}
|
|
9556
|
-
/**
|
|
9557
|
-
* Creates Notes Slide (`ppt/notesSlides/notesSlide1.xml`)
|
|
9558
|
-
* @param {PresSlideInternal} slide - the slide object to transform into XML
|
|
9559
|
-
* @return {string} XML
|
|
9560
|
-
*/
|
|
9561
|
-
function makeXmlNotesSlide(slide) {
|
|
9562
|
-
buildNotesSlideRels(slide);
|
|
9563
|
-
const slideImgPh = el("p:sp", null, [raw(el("p:nvSpPr", null, [
|
|
9564
|
-
raw(voidEl("p:cNvPr", {
|
|
9565
|
-
id: 2,
|
|
9566
|
-
name: "Slide Image Placeholder 1"
|
|
9567
|
-
})),
|
|
9568
|
-
raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", {
|
|
9569
|
-
noGrp: 1,
|
|
9570
|
-
noRot: 1,
|
|
9571
|
-
noChangeAspect: 1
|
|
9572
|
-
})))),
|
|
9573
|
-
raw(el("p:nvPr", null, raw(voidEl("p:ph", { type: "sldImg" }))))
|
|
9574
|
-
])), raw(voidEl("p:spPr"))]);
|
|
9575
|
-
const notesPh = el("p:sp", null, [
|
|
9576
|
-
raw(el("p:nvSpPr", null, [
|
|
9577
|
-
raw(voidEl("p:cNvPr", {
|
|
9578
|
-
id: 3,
|
|
9579
|
-
name: "Notes Placeholder 2"
|
|
9580
|
-
})),
|
|
9581
|
-
raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", { noGrp: 1 })))),
|
|
9582
|
-
raw(el("p:nvPr", null, raw(voidEl("p:ph", {
|
|
9583
|
-
type: "body",
|
|
9584
|
-
idx: 1
|
|
9585
|
-
}))))
|
|
9586
|
-
])),
|
|
9587
|
-
raw(voidEl("p:spPr")),
|
|
9588
|
-
raw(el("p:txBody", null, [
|
|
9589
|
-
raw(voidEl("a:bodyPr")),
|
|
9590
|
-
raw(voidEl("a:lstStyle")),
|
|
9591
|
-
raw(genXmlNotesParagraphs(slide))
|
|
9592
|
-
]))
|
|
9593
|
-
]);
|
|
9594
|
-
const slideNumPh = el("p:sp", null, [
|
|
9595
|
-
raw(el("p:nvSpPr", null, [
|
|
9596
|
-
raw(voidEl("p:cNvPr", {
|
|
9597
|
-
id: 4,
|
|
9598
|
-
name: "Slide Number Placeholder 3"
|
|
9599
|
-
})),
|
|
9600
|
-
raw(el("p:cNvSpPr", null, raw(voidEl("a:spLocks", { noGrp: 1 })))),
|
|
9601
|
-
raw(el("p:nvPr", null, raw(voidEl("p:ph", {
|
|
9602
|
-
type: "sldNum",
|
|
9603
|
-
sz: "quarter",
|
|
9604
|
-
idx: 10
|
|
9605
|
-
}))))
|
|
9606
|
-
])),
|
|
9607
|
-
raw(voidEl("p:spPr")),
|
|
9608
|
-
raw(el("p:txBody", null, [
|
|
9609
|
-
raw(voidEl("a:bodyPr")),
|
|
9610
|
-
raw(voidEl("a:lstStyle")),
|
|
9611
|
-
raw(el("a:p", null, [raw(el("a:fld", {
|
|
9612
|
-
id: SLDNUMFLDID,
|
|
9613
|
-
type: "slidenum"
|
|
9614
|
-
}, [raw(voidEl("a:rPr", { lang: "en-US" })), raw(el("a:t", null, slide._slideNum))])), raw(voidEl("a:endParaRPr", { lang: "en-US" }))]))
|
|
9615
|
-
]))
|
|
9616
|
-
]);
|
|
9617
|
-
const spTree = el("p:spTree", null, [
|
|
9618
|
-
raw(el("p:nvGrpSpPr", null, [
|
|
9619
|
-
raw(voidEl("p:cNvPr", {
|
|
9620
|
-
id: 1,
|
|
9621
|
-
name: ""
|
|
9622
|
-
})),
|
|
9623
|
-
raw(voidEl("p:cNvGrpSpPr")),
|
|
9624
|
-
raw(voidEl("p:nvPr"))
|
|
9625
|
-
])),
|
|
9626
|
-
raw(el("p:grpSpPr", null, raw(el("a:xfrm", null, [
|
|
9627
|
-
raw(voidEl("a:off", {
|
|
9628
|
-
x: 0,
|
|
9629
|
-
y: 0
|
|
9630
|
-
})),
|
|
9631
|
-
raw(voidEl("a:ext", {
|
|
9632
|
-
cx: 0,
|
|
9633
|
-
cy: 0
|
|
9634
|
-
})),
|
|
9635
|
-
raw(voidEl("a:chOff", {
|
|
9636
|
-
x: 0,
|
|
9637
|
-
y: 0
|
|
9638
|
-
})),
|
|
9639
|
-
raw(voidEl("a:chExt", {
|
|
9640
|
-
cx: 0,
|
|
9641
|
-
cy: 0
|
|
9642
|
-
}))
|
|
9643
|
-
])))),
|
|
9644
|
-
raw(slideImgPh),
|
|
9645
|
-
raw(notesPh),
|
|
9646
|
-
raw(slideNumPh)
|
|
9647
|
-
]);
|
|
9648
|
-
const extLst = el("p:extLst", null, raw(el("p:ext", { uri: "{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}" }, raw(voidEl("p14:creationId", {
|
|
9649
|
-
"xmlns:p14": "http://schemas.microsoft.com/office/powerpoint/2010/main",
|
|
9650
|
-
val: 1024086991
|
|
9651
|
-
})))));
|
|
9652
|
-
const cSld = el("p:cSld", null, [raw(spTree), raw(extLst)]);
|
|
9653
|
-
return XML_DECL + "\r\n" + el("p:notes", {
|
|
9654
|
-
"xmlns:a": "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
9655
|
-
"xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
|
|
9656
|
-
"xmlns:p": "http://schemas.openxmlformats.org/presentationml/2006/main"
|
|
9657
|
-
}, [raw(cSld), raw(el("p:clrMapOvr", null, raw(voidEl("a:masterClrMapping"))))]);
|
|
9658
|
-
}
|
|
9659
|
-
/**
|
|
9660
|
-
* Generates XML string for a notes-slide relation file (`ppt/notesSlides/_rels/notesSlideN.xml.rels`).
|
|
9661
|
-
* rId1=notesMaster and rId2=slide are always reserved; any notes hyperlink rels follow (rId3+).
|
|
9662
|
-
* @param {PresSlideInternal} slide - the slide whose notes part is being related
|
|
9663
|
-
* @param {number} slideNumber - 1-indexed slide number the notes part belongs to
|
|
9664
|
-
* @return {string} XML
|
|
9665
|
-
*/
|
|
9666
|
-
function makeXmlNotesSlideRel(slide, slideNumber) {
|
|
9667
|
-
const hlinkRels = buildNotesSlideRels(slide).map((rel) => voidEl("Relationship", {
|
|
9668
|
-
Id: `rId${rel.rId}`,
|
|
9669
|
-
Type: OFFICE_REL + "hyperlink",
|
|
9670
|
-
Target: rel.Target,
|
|
9671
|
-
TargetMode: "External"
|
|
9672
|
-
})).join("");
|
|
9673
|
-
return XML_DECL + el("Relationships", { xmlns: PACKAGE_REL_NS }, [
|
|
9674
|
-
raw(voidEl("Relationship", {
|
|
9675
|
-
Id: "rId1",
|
|
9676
|
-
Type: OFFICE_REL + "notesMaster",
|
|
9677
|
-
Target: "../notesMasters/notesMaster1.xml"
|
|
9678
|
-
})),
|
|
9679
|
-
raw(voidEl("Relationship", {
|
|
9680
|
-
Id: "rId2",
|
|
9681
|
-
Type: OFFICE_REL + "slide",
|
|
9682
|
-
Target: `../slides/slide${slideNumber}.xml`
|
|
9683
|
-
})),
|
|
9684
|
-
raw(hlinkRels)
|
|
9685
|
-
], {
|
|
9686
|
-
openPrefix: "\n ",
|
|
9687
|
-
childPrefix: "\n ",
|
|
9688
|
-
closePrefix: ""
|
|
9689
|
-
});
|
|
9690
|
-
}
|
|
9691
|
-
/**
|
|
9692
|
-
* Creates `ppt/notesMasters/_rels/notesMaster1.xml.rels`
|
|
9693
|
-
* @return {string} XML
|
|
9694
|
-
*/
|
|
9695
|
-
function makeXmlNotesMasterRel() {
|
|
9696
|
-
return XML_DECL + "\r\n" + el("Relationships", { xmlns: PACKAGE_REL_NS }, raw(voidEl("Relationship", {
|
|
9697
|
-
Id: "rId1",
|
|
9698
|
-
Type: OFFICE_REL + "theme",
|
|
9699
|
-
Target: "../theme/theme2.xml"
|
|
9700
|
-
})), {
|
|
9701
|
-
childPrefix: "\n ",
|
|
9702
|
-
closePrefix: "\n "
|
|
9703
|
-
});
|
|
9704
|
-
}
|
|
9705
|
-
//#endregion
|
|
9706
8216
|
//#region src/gen/pres/theme.ts
|
|
9707
8217
|
/**
|
|
9708
8218
|
* ts-pptx: `ppt/theme/theme1.xml`
|
|
@@ -9994,11 +8504,14 @@ function makeXmlApp(slides, company) {
|
|
|
9994
8504
|
* actually used by the deck (plus xlsx/font defaults when present) and Override
|
|
9995
8505
|
* entries for every written part.
|
|
9996
8506
|
*/
|
|
9997
|
-
|
|
8507
|
+
/** Content-type prefixes; spelled out per part below so each entry stays greppable by its suffix. */
|
|
8508
|
+
const OD = "application/vnd.openxmlformats-officedocument.";
|
|
8509
|
+
const PKG = "application/vnd.openxmlformats-package.";
|
|
8510
|
+
const CT_CHART = OD + "drawingml.chart+xml";
|
|
9998
8511
|
const CT_CHARTEX = "application/vnd.ms-office.chartex+xml";
|
|
9999
8512
|
const CT_CHARTEX_STYLE = "application/vnd.ms-office.chartstyle+xml";
|
|
10000
8513
|
const CT_CHARTEX_COLORS = "application/vnd.ms-office.chartcolorstyle+xml";
|
|
10001
|
-
const CT_THEME = "
|
|
8514
|
+
const CT_THEME = OD + "theme+xml";
|
|
10002
8515
|
/**
|
|
10003
8516
|
* Some Override entries have always been emitted with a leading space. It is insignificant
|
|
10004
8517
|
* whitespace, but it is in the bytes this library has shipped for years, so it is reproduced
|
|
@@ -10038,7 +8551,7 @@ function chartOverrides(rel, fmt) {
|
|
|
10038
8551
|
* @returns XML
|
|
10039
8552
|
*/
|
|
10040
8553
|
function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps, embeddedFonts) {
|
|
10041
|
-
const parts = [contentDefault("xml", "application/xml"), contentDefault("rels", "
|
|
8554
|
+
const parts = [contentDefault("xml", "application/xml"), contentDefault("rels", PKG + "relationships+xml")];
|
|
10042
8555
|
const extnTypeMap = /* @__PURE__ */ new Map();
|
|
10043
8556
|
const ctTargets = [];
|
|
10044
8557
|
(slides || []).forEach((s) => ctTargets.push(s));
|
|
@@ -10056,45 +8569,45 @@ function makeXmlContTypes(slides, slideLayouts, masterSlide, hasCustomProps, emb
|
|
|
10056
8569
|
extnTypeMap.forEach((type, extn) => {
|
|
10057
8570
|
parts.push(contentDefault(extn, type));
|
|
10058
8571
|
});
|
|
10059
|
-
if (ctHasChart && !extnTypeMap.has("xlsx")) parts.push(contentDefault("xlsx", "
|
|
8572
|
+
if (ctHasChart && !extnTypeMap.has("xlsx")) parts.push(contentDefault("xlsx", OD + "spreadsheetml.sheet"));
|
|
10060
8573
|
if ((embeddedFonts || []).some((font) => font.faces.some((face) => face.bytes))) parts.push(contentDefault(FONT_DATA_EXTENSION, FONT_DATA_CONTENT_TYPE));
|
|
10061
|
-
parts.push(override("/ppt/presentation.xml", "
|
|
10062
|
-
parts.push(override("/ppt/notesMasters/notesMaster1.xml", "
|
|
10063
|
-
parts.push(override("/ppt/slideMasters/slideMaster1.xml", "
|
|
8574
|
+
parts.push(override("/ppt/presentation.xml", OD + "presentationml.presentation.main+xml"));
|
|
8575
|
+
parts.push(override("/ppt/notesMasters/notesMaster1.xml", OD + "presentationml.notesMaster+xml"));
|
|
8576
|
+
parts.push(override("/ppt/slideMasters/slideMaster1.xml", OD + "presentationml.slideMaster+xml"));
|
|
10064
8577
|
slides.forEach((slide, idx) => {
|
|
10065
|
-
parts.push(override(`/ppt/slides/slide${idx + 1}.xml`, "
|
|
8578
|
+
parts.push(override(`/ppt/slides/slide${idx + 1}.xml`, OD + "presentationml.slide+xml"));
|
|
10066
8579
|
slide._relsChart.forEach((rel) => {
|
|
10067
8580
|
parts.push(...chartOverrides(rel));
|
|
10068
8581
|
});
|
|
10069
8582
|
});
|
|
10070
|
-
parts.push(override("/ppt/presProps.xml", "
|
|
10071
|
-
parts.push(override("/ppt/viewProps.xml", "
|
|
8583
|
+
parts.push(override("/ppt/presProps.xml", OD + "presentationml.presProps+xml"));
|
|
8584
|
+
parts.push(override("/ppt/viewProps.xml", OD + "presentationml.viewProps+xml"));
|
|
10072
8585
|
parts.push(override("/ppt/theme/theme1.xml", CT_THEME));
|
|
10073
8586
|
parts.push(override("/ppt/theme/theme2.xml", CT_THEME));
|
|
10074
|
-
parts.push(override("/ppt/tableStyles.xml", "
|
|
8587
|
+
parts.push(override("/ppt/tableStyles.xml", OD + "presentationml.tableStyles+xml"));
|
|
10075
8588
|
slideLayouts.forEach((layout, idx) => {
|
|
10076
|
-
parts.push(override(`/ppt/slideLayouts/slideLayout${idx + 1}.xml`, "
|
|
8589
|
+
parts.push(override(`/ppt/slideLayouts/slideLayout${idx + 1}.xml`, OD + "presentationml.slideLayout+xml"));
|
|
10077
8590
|
(layout._relsChart || []).forEach((rel) => {
|
|
10078
8591
|
parts.push(...chartOverrides(rel, LEADING_SPACE));
|
|
10079
8592
|
});
|
|
10080
8593
|
});
|
|
10081
8594
|
slides.forEach((_slide, idx) => {
|
|
10082
|
-
parts.push(override(`/ppt/notesSlides/notesSlide${idx + 1}.xml`, "
|
|
8595
|
+
parts.push(override(`/ppt/notesSlides/notesSlide${idx + 1}.xml`, OD + "presentationml.notesSlide+xml"));
|
|
10083
8596
|
});
|
|
10084
8597
|
let hasAnyComment = false;
|
|
10085
8598
|
slides.forEach((slide, idx) => {
|
|
10086
8599
|
if ((slide._comments || []).length > 0) {
|
|
10087
8600
|
hasAnyComment = true;
|
|
10088
|
-
parts.push(override(`/ppt/comments/comment${idx + 1}.xml`, "
|
|
8601
|
+
parts.push(override(`/ppt/comments/comment${idx + 1}.xml`, OD + "presentationml.comments+xml"));
|
|
10089
8602
|
}
|
|
10090
8603
|
});
|
|
10091
|
-
if (hasAnyComment) parts.push(override("/ppt/commentAuthors.xml", "
|
|
8604
|
+
if (hasAnyComment) parts.push(override("/ppt/commentAuthors.xml", OD + "presentationml.commentAuthors+xml"));
|
|
10092
8605
|
masterSlide?._relsChart.forEach((rel) => {
|
|
10093
8606
|
parts.push(...chartOverrides(rel, LEADING_SPACE));
|
|
10094
8607
|
});
|
|
10095
|
-
parts.push(override("/docProps/core.xml", "
|
|
10096
|
-
parts.push(override("/docProps/app.xml", "
|
|
10097
|
-
if (hasCustomProps) parts.push(override("/docProps/custom.xml", "
|
|
8608
|
+
parts.push(override("/docProps/core.xml", PKG + "core-properties+xml", LEADING_SPACE));
|
|
8609
|
+
parts.push(override("/docProps/app.xml", OD + "extended-properties+xml", LEADING_SPACE));
|
|
8610
|
+
if (hasCustomProps) parts.push(override("/docProps/custom.xml", OD + "custom-properties+xml", LEADING_SPACE));
|
|
10098
8611
|
return XML_DECL + "\r\n" + el("Types", { xmlns: "http://schemas.openxmlformats.org/package/2006/content-types" }, parts.map(raw));
|
|
10099
8612
|
}
|
|
10100
8613
|
//#endregion
|
|
@@ -10643,7 +9156,7 @@ function masterAlignAttr(align) {
|
|
|
10643
9156
|
case "right": return "r";
|
|
10644
9157
|
case "center": return "ctr";
|
|
10645
9158
|
case "justify": return "just";
|
|
10646
|
-
|
|
9159
|
+
case void 0: return "";
|
|
10647
9160
|
}
|
|
10648
9161
|
}
|
|
10649
9162
|
/** Build the bullet element for a master level: caller override wins over the level default. */
|
|
@@ -10651,10 +9164,14 @@ function masterBulletXml(bulletOverride, base) {
|
|
|
10651
9164
|
if (bulletOverride === false) return voidEl("a:buNone");
|
|
10652
9165
|
if (bulletOverride && typeof bulletOverride === "object") {
|
|
10653
9166
|
const font = bulletOverride.fontFace ? voidEl("a:buFont", { typeface: bulletOverride.fontFace }) : "";
|
|
10654
|
-
if (bulletOverride.type === "number")
|
|
10655
|
-
type
|
|
10656
|
-
startAt
|
|
10657
|
-
|
|
9167
|
+
if (bulletOverride.type === "number") {
|
|
9168
|
+
const type = bulletOverride.numberType || "arabicPeriod";
|
|
9169
|
+
const startAt = typeof bulletOverride.numberStartAt === "number" ? Math.round(bulletOverride.numberStartAt) : null;
|
|
9170
|
+
return font + voidEl("a:buAutoNum", {
|
|
9171
|
+
type,
|
|
9172
|
+
startAt
|
|
9173
|
+
});
|
|
9174
|
+
}
|
|
10658
9175
|
const char = bulletOverride.characterCode ? `&#x${bulletOverride.characterCode};` : "•";
|
|
10659
9176
|
return `${bulletOverride.fontFace ? font : voidEl("a:buFont", {
|
|
10660
9177
|
typeface: "Arial",
|
|
@@ -10676,8 +9193,10 @@ function masterLevelXml(levelNum, base, levelOverride = {}) {
|
|
|
10676
9193
|
const indentEmu = typeof levelOverride.indent === "number" && !isNaN(levelOverride.indent) ? inch2Emu(levelOverride.indent) : base.indent;
|
|
10677
9194
|
const algn = levelOverride.align && masterAlignAttr(levelOverride.align) || base.algn;
|
|
10678
9195
|
let sz = base.sz;
|
|
10679
|
-
if (typeof levelOverride.fontSize === "number")
|
|
10680
|
-
|
|
9196
|
+
if (typeof levelOverride.fontSize === "number") {
|
|
9197
|
+
if (isNaN(levelOverride.fontSize) || levelOverride.fontSize <= 0) warn("master/invalid-text-style-font-size", `master textStyles fontSize "${levelOverride.fontSize}" is invalid; keeping default ${base.sz / 100}pt.`);
|
|
9198
|
+
else sz = ptToHundredths(levelOverride.fontSize);
|
|
9199
|
+
}
|
|
10681
9200
|
const colorXml = levelOverride.color ? createColorElement(levelOverride.color) : voidEl("a:schemeClr", { val: "tx1" });
|
|
10682
9201
|
const latinXml = levelOverride.fontFace ? voidEl("a:latin", { typeface: levelOverride.fontFace }) : voidEl("a:latin", { typeface: `+${base.font}-lt` });
|
|
10683
9202
|
return el(`a:lvl${levelNum}pPr`, {
|
|
@@ -11060,8 +9579,7 @@ async function zipPackageParts(parts, props) {
|
|
|
11060
9579
|
const zip = new ZipWriter();
|
|
11061
9580
|
for (const part of parts) zip.add(part.path, part.data, { store: part.store });
|
|
11062
9581
|
const compression = props.compression !== false;
|
|
11063
|
-
if (props.outputType
|
|
11064
|
-
else if (props.outputType) return await zip.generate(props.outputType, { compression });
|
|
9582
|
+
if (props.outputType) return await zip.generate(props.outputType, { compression });
|
|
11065
9583
|
else return await zip.generate("blob", { compression });
|
|
11066
9584
|
}
|
|
11067
9585
|
/**
|
|
@@ -11088,7 +9606,7 @@ async function writePackage(source, props) {
|
|
|
11088
9606
|
* the OfficeGen project (Copyright 2013 Ziv Barber). This project is likewise MIT; the full
|
|
11089
9607
|
* license text is in `LICENSE` at the repository root.
|
|
11090
9608
|
*/
|
|
11091
|
-
const VERSION = "3.
|
|
9609
|
+
const VERSION = "3.3.0";
|
|
11092
9610
|
function standardLayoutToPresLayout(layout) {
|
|
11093
9611
|
return {
|
|
11094
9612
|
name: layout.name,
|
|
@@ -11469,11 +9987,23 @@ var PresentationCore = class {
|
|
|
11469
9987
|
rId: rel.rId,
|
|
11470
9988
|
sourceSlideNumber: Number(rel.Target)
|
|
11471
9989
|
}));
|
|
11472
|
-
const charts = (slide._relsChart || []).map((rel) =>
|
|
11473
|
-
|
|
11474
|
-
|
|
11475
|
-
|
|
11476
|
-
|
|
9990
|
+
const charts = (slide._relsChart || []).map((rel) => {
|
|
9991
|
+
const base = {
|
|
9992
|
+
rId: rel.rId,
|
|
9993
|
+
embeddingBytes: buildEmbeddedWorksheet(rel)
|
|
9994
|
+
};
|
|
9995
|
+
return rel.isChartEx ? {
|
|
9996
|
+
...base,
|
|
9997
|
+
chartXml: makeXmlChartEx(rel),
|
|
9998
|
+
chartEx: {
|
|
9999
|
+
styleXml: makeChartExStyleXml(),
|
|
10000
|
+
colorsXml: makeChartExColorsXml()
|
|
10001
|
+
}
|
|
10002
|
+
} : {
|
|
10003
|
+
...base,
|
|
10004
|
+
chartXml: makeXmlCharts(rel)
|
|
10005
|
+
};
|
|
10006
|
+
});
|
|
11477
10007
|
const notes = slide._slideObjects.some((obj) => obj._type === "notes") ? {
|
|
11478
10008
|
xml: makeXmlNotesSlide(slide),
|
|
11479
10009
|
hyperlinks: buildNotesSlideRels(slide).filter((rel) => typeof rel.Target === "string" && rel.Target).map((rel) => ({
|
|
@@ -11512,19 +10042,36 @@ var PresentationCore = class {
|
|
|
11512
10042
|
* Without registered metrics, `fit:'shrink'` keeps its current behavior (a bare
|
|
11513
10043
|
* `<a:normAutofit/>` that only PowerPoint recomputes on edit). Register the same
|
|
11514
10044
|
* face once per weight/style you use; bold/italic advances differ.
|
|
10045
|
+
*
|
|
10046
|
+
* **Font collections** (`.ttc`/`.otc`, how MS Gothic, Yu Gothic, SimSun, Microsoft
|
|
10047
|
+
* YaHei and Cambria ship on Windows) hold several fonts in one file, so one has to be
|
|
10048
|
+
* chosen. With no `font` option, `face` is used as the selector, which is usually what
|
|
10049
|
+
* you meant; a `face` that names no font in the collection throws rather than falling
|
|
10050
|
+
* back to the first, since measuring the wrong member is invisible downstream. Pass
|
|
10051
|
+
* `font` when the deck-side name differs from the name inside the file, and use
|
|
10052
|
+
* `listFontFaces` from `ts-pptx/measure` to see what a file holds.
|
|
11515
10053
|
* @param {string} face - font family name as used in `fontFace` (e.g. 'Aptos')
|
|
11516
|
-
* @param {string | Uint8Array | ArrayBuffer} source - font file path/URL (Node/web) or raw TTF/OTF bytes
|
|
11517
|
-
* @param {object} [opts] - variant flags
|
|
10054
|
+
* @param {string | Uint8Array | ArrayBuffer} source - font file path/URL (Node/web) or raw TTF/OTF/TTC bytes
|
|
10055
|
+
* @param {object} [opts] - variant flags (advances differ per weight/style) and collection selection
|
|
10056
|
+
* @param {boolean} [opts.bold] - these are the bold advances
|
|
10057
|
+
* @param {boolean} [opts.italic] - these are the italic advances
|
|
10058
|
+
* @param {number | string} [opts.font] - which font inside a collection: 0-based index, or a name
|
|
11518
10059
|
* @example await pptx.registerFontMetrics('Aptos', '/usr/share/fonts/Aptos.ttf')
|
|
11519
10060
|
* @example await pptx.registerFontMetrics('Aptos', aptosBoldBytes, { bold: true })
|
|
10061
|
+
* @example await pptx.registerFontMetrics('MS PGothic', 'C:/Windows/Fonts/msgothic.ttc')
|
|
10062
|
+
* @example await pptx.registerFontMetrics('Cambria Math', 'C:/Windows/Fonts/cambria.ttc', { font: 1 })
|
|
11520
10063
|
*/
|
|
11521
10064
|
async registerFontMetrics(face, source, opts) {
|
|
11522
10065
|
let bytes;
|
|
11523
10066
|
if (typeof source === "string") bytes = await this._runtime.loadFontData(source);
|
|
11524
10067
|
else if (source instanceof Uint8Array) bytes = source;
|
|
11525
10068
|
else bytes = new Uint8Array(source);
|
|
11526
|
-
const
|
|
11527
|
-
|
|
10069
|
+
const font = opts?.font ?? (isFontCollection(bytes) ? face : void 0);
|
|
10070
|
+
const metrics = await parseFontMetrics(bytes, font === void 0 ? void 0 : { font });
|
|
10071
|
+
this._fontMetrics.set(face, metrics, {
|
|
10072
|
+
bold: opts?.bold,
|
|
10073
|
+
italic: opts?.italic
|
|
10074
|
+
});
|
|
11528
10075
|
}
|
|
11529
10076
|
/**
|
|
11530
10077
|
* Embed a font face so the deck renders with it even on machines that do not
|
|
@@ -11580,7 +10127,7 @@ var PresentationCore = class {
|
|
|
11580
10127
|
* Measure how tall text wraps at a given width, using the **same** calibrated wrap
|
|
11581
10128
|
* model the export-time autofit bake uses — so a layout-time prediction matches the
|
|
11582
10129
|
* value `fit:'shrink'`/`'resize'` would bake. Synchronous: register the face's metrics
|
|
11583
|
-
* first with {@link
|
|
10130
|
+
* first with {@link TsPptx.registerFontMetrics} (lookup is sync).
|
|
11584
10131
|
*
|
|
11585
10132
|
* Lets a consumer size its own geometry before export — grow a card to fit its text,
|
|
11586
10133
|
* reflow a grid, or detect overflow at layout time. Heights err **tall** (conservative),
|
|
@@ -11614,8 +10161,8 @@ var PresentationCore = class {
|
|
|
11614
10161
|
* cells. Column widths (cell `x`/`w`) are exact, derived from the same logic the
|
|
11615
10162
|
* writer uses. Row heights (`y`/`h`) are exact when pinned by `rowH` (array or
|
|
11616
10163
|
* scalar) or table `h`; an auto-height row is estimated with the same conservative
|
|
11617
|
-
* (tall) text model as {@link
|
|
11618
|
-
* (register the cell font via {@link
|
|
10164
|
+
* (tall) text model as {@link TsPptx.measureText} and flagged `heightExact:false`
|
|
10165
|
+
* (register the cell font via {@link TsPptx.registerFontMetrics} for an exact
|
|
11619
10166
|
* estimate). Geometry is for a single, un-paginated table — `autoPage` paging is
|
|
11620
10167
|
* not modeled.
|
|
11621
10168
|
* @param {TableRow[]} rows - the same `rows` passed to `slide.addTable`
|
|
@@ -11630,14 +10177,18 @@ var PresentationCore = class {
|
|
|
11630
10177
|
return computeTableLayout(rows, opts, this._presLayout, this._fontMetrics);
|
|
11631
10178
|
}
|
|
11632
10179
|
/**
|
|
11633
|
-
* Export the current
|
|
10180
|
+
* Export the current presentation as portable ZIP bytes. Unlike the former
|
|
10181
|
+
* `stream()` method, this does not imply incremental output or convert the
|
|
10182
|
+
* archive to a Node `Buffer`: package assembly is synchronous and the returned
|
|
10183
|
+
* `Uint8Array` works unchanged in Node, browsers, and workers.
|
|
11634
10184
|
* @param {WriteBaseProps} props - output properties
|
|
11635
|
-
* @returns {Promise<
|
|
10185
|
+
* @returns {Promise<Uint8Array>} complete `.pptx` archive bytes
|
|
11636
10186
|
*/
|
|
11637
|
-
async
|
|
10187
|
+
async toBytes(props) {
|
|
11638
10188
|
return await writePackage(this.packageSource(), {
|
|
11639
10189
|
compression: props?.compression,
|
|
11640
|
-
outputType: "
|
|
10190
|
+
outputType: "uint8array",
|
|
10191
|
+
onMediaError: props?.onMediaError
|
|
11641
10192
|
});
|
|
11642
10193
|
}
|
|
11643
10194
|
/**
|
|
@@ -11976,4 +10527,4 @@ function textRuns(runs) {
|
|
|
11976
10527
|
//#endregion
|
|
11977
10528
|
export { PresentationCore as i, textRuns as n, clipPath as r, textRun as t };
|
|
11978
10529
|
|
|
11979
|
-
//# sourceMappingURL=text-
|
|
10530
|
+
//# sourceMappingURL=text-uM-zGton.js.map
|