@shbernal/ts-pptx 2.0.0 → 3.1.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 +31 -2
- package/dist/{autopage-tmfjRzL4.js → autopage-QQyfRtoK.js} +4 -5
- package/dist/{autopage-tmfjRzL4.js.map → autopage-QQyfRtoK.js.map} +1 -1
- package/dist/browser.d.ts +14 -6
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +14 -6
- package/dist/browser.js.map +1 -1
- package/dist/{diagnostics-CQ0JWc9m.d.ts → diagnostics-PMINsVP6.d.ts} +97 -8
- package/dist/diagnostics-PMINsVP6.d.ts.map +1 -0
- package/dist/{enums-D3aF4QGb.d.ts → enums-CoXnxiCI.d.ts} +6 -5
- package/dist/{enums-D3aF4QGb.d.ts.map → enums-CoXnxiCI.d.ts.map} +1 -1
- package/dist/{errors-B22h8Qd0.d.ts → errors-BeUEkNWL.d.ts} +26 -4
- package/dist/errors-BeUEkNWL.d.ts.map +1 -0
- package/dist/{errors-DGE9zh3U.js → errors-Bvln0H-7.js} +21 -2
- package/dist/errors-Bvln0H-7.js.map +1 -0
- package/dist/{fit-BSWY0AYt.js → fit-DhMbQG5O.js} +0 -0
- package/dist/fit-DhMbQG5O.js.map +1 -0
- package/dist/{html-dom-BQLUntho.js → html-dom-DajBuobe.js} +20 -6
- package/dist/html-dom-DajBuobe.js.map +1 -0
- package/dist/html.d.ts +3 -3
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +7 -4
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +1 -1
- package/dist/inspect.js +4 -4
- package/dist/inspect.js.map +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.d.ts.map +1 -1
- package/dist/math.js +98 -2
- package/dist/math.js.map +1 -1
- package/dist/measure.d.ts +3 -3
- package/dist/measure.js +2 -2
- package/dist/node.d.ts +14 -6
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +13 -5
- package/dist/node.js.map +1 -1
- package/dist/package-CaOLXSNi.d.ts.map +1 -1
- package/dist/{pres-GU-jjBEx.d.ts → pres-BgvxhQ_m.d.ts} +3 -3
- package/dist/{pres-GU-jjBEx.d.ts.map → pres-BgvxhQ_m.d.ts.map} +1 -1
- package/dist/{presentation-MJe9WDi4.js → presentation-BP1B8PCF.js} +863 -343
- package/dist/presentation-BP1B8PCF.js.map +1 -0
- package/dist/{presentation-CVaTZw1V.d.ts → presentation-DyIRDKkp.d.ts} +742 -595
- package/dist/presentation-DyIRDKkp.d.ts.map +1 -0
- package/dist/{presentation-types-BVw0EknS.d.ts → presentation-types-Dj4riuDs.d.ts} +2 -2
- package/dist/{presentation-types-BVw0EknS.d.ts.map → presentation-types-Dj4riuDs.d.ts.map} +1 -1
- package/dist/read.d.ts +5 -5
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +3 -3
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +0 -0
- package/dist/script.js.map +1 -1
- package/dist/{shapes-CaOgR0i1.js → shapes-DMThLsZN.js} +689 -1032
- package/dist/shapes-DMThLsZN.js.map +1 -0
- package/dist/{slide-BBlElWr9.d.ts → slide-BRAtT8Dl.d.ts} +197 -22
- package/dist/slide-BRAtT8Dl.d.ts.map +1 -0
- package/dist/{image-size-C8uYE8GJ.js → st-enums-evQG2A7Z.js} +233 -2
- package/dist/st-enums-evQG2A7Z.js.map +1 -0
- package/dist/{text-B1ZrPEeK.js → text-B6PbMFih.js} +725 -319
- package/dist/text-B6PbMFih.js.map +1 -0
- package/dist/{units-B-JUKwCS.js → units-C7QM1KgH.js} +2 -2
- package/dist/{units-B-JUKwCS.js.map → units-C7QM1KgH.js.map} +1 -1
- package/dist/{units-internal-7JcMfYti.js → units-internal-CR8s8YQa.js} +4 -3
- package/dist/units-internal-CR8s8YQa.js.map +1 -0
- package/dist/zip.d.ts +2 -2
- package/dist/zip.js +1 -1
- package/dist/zip.js.map +1 -1
- package/package.json +40 -29
- package/skills/ts-pptx-upstream/SKILL.md +247 -0
- package/dist/diagnostics-CQ0JWc9m.d.ts.map +0 -1
- package/dist/errors-B22h8Qd0.d.ts.map +0 -1
- package/dist/errors-DGE9zh3U.js.map +0 -1
- package/dist/fit-BSWY0AYt.js.map +0 -1
- package/dist/html-dom-BQLUntho.js.map +0 -1
- package/dist/image-size-C8uYE8GJ.js.map +0 -1
- package/dist/presentation-CVaTZw1V.d.ts.map +0 -1
- package/dist/presentation-MJe9WDi4.js.map +0 -1
- package/dist/shapes-CaOgR0i1.js.map +0 -1
- package/dist/slide-BBlElWr9.d.ts.map +0 -1
- package/dist/text-B1ZrPEeK.js.map +0 -1
- package/dist/units-internal-7JcMfYti.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as warn } from "./diagnostics-DYQC7xLV.js";
|
|
2
|
-
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-
|
|
3
|
-
import {
|
|
2
|
+
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-Bvln0H-7.js";
|
|
3
|
+
import { C as getImageSizeFromBytes, _ as SLIDE_LAYOUT_REL, b as THEME_REL, c as TEXT_VERTICAL, d as IMAGE_REL, f as NOTES_MASTER_REL, l as checkEnumOrThrow, m as OFFICE_DOCUMENT_REL, o as TEXT_ANCHORS, s as TEXT_HORZ_OVERFLOW, v as SLIDE_MASTER_REL, x as fitSrcRectPercents } from "./st-enums-evQG2A7Z.js";
|
|
4
4
|
import { DOMParser, MIME_TYPE, XMLSerializer, onErrorStopParsing } from "@xmldom/xmldom";
|
|
5
5
|
/** Parse an XML string strictly: any well-formedness error throws. */
|
|
6
6
|
function parseXml(xml) {
|
|
@@ -58,11 +58,25 @@ function getElements(parent, qname) {
|
|
|
58
58
|
return out;
|
|
59
59
|
}
|
|
60
60
|
/** All direct child elements in document order, regardless of qname. */
|
|
61
|
-
function childElements
|
|
61
|
+
function childElements(parent) {
|
|
62
62
|
const out = [];
|
|
63
63
|
for (let node = parent.firstChild; node; node = node.nextSibling) if (node.nodeType === 1) out.push(node);
|
|
64
64
|
return out;
|
|
65
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* All *descendant* elements of `root` matching a namespace + local name, snapshotted into an
|
|
68
|
+
* array. Unlike {@link getElements} (direct children only) this searches the whole subtree, and
|
|
69
|
+
* unlike the live `HTMLCollection` that `getElementsByTagNameNS` returns, the result does not
|
|
70
|
+
* change as the caller mutates the tree — which is what makes it safe to iterate while replacing
|
|
71
|
+
* the very elements being iterated.
|
|
72
|
+
*
|
|
73
|
+
* Note the DOM's own exclusion: `root` itself is never a candidate, only its descendants.
|
|
74
|
+
*/
|
|
75
|
+
function descendantsByTag(root, ns, local) {
|
|
76
|
+
const out = [];
|
|
77
|
+
for (const element of root.getElementsByTagNameNS(ns, local)) out.push(element);
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
66
80
|
/** First direct child element matching a qname, or `null`. */
|
|
67
81
|
function firstChild(parent, qname) {
|
|
68
82
|
const { uri, local } = splitQName(qname);
|
|
@@ -216,6 +230,20 @@ function escapeXmlAttribute(value) {
|
|
|
216
230
|
* A partname is an absolute, `/`-separated path inside the package, e.g.
|
|
217
231
|
* `/ppt/slides/slide1.xml`. Zip entry paths omit the leading slash.
|
|
218
232
|
*/
|
|
233
|
+
/**
|
|
234
|
+
* Resolve the single relationship of `type` owned by `partName` to its target partname, or
|
|
235
|
+
* `null` when the part declares none. For the one-of-a-kind links in a deck's spine — a slide to
|
|
236
|
+
* its layout, a layout to its master, a master to its theme — where a second relationship of the
|
|
237
|
+
* same type would itself be malformed.
|
|
238
|
+
*
|
|
239
|
+
* The `OpcPackage` import is type-only, so this does not create a cycle with `package.ts` (which
|
|
240
|
+
* imports this module at run time).
|
|
241
|
+
*/
|
|
242
|
+
function resolveSingleRel(opc, partName, type) {
|
|
243
|
+
const rels = opc.relationshipsFor(partName);
|
|
244
|
+
const rel = rels.byType(type)[0];
|
|
245
|
+
return rel ? rels.resolveTarget(rel.id) : null;
|
|
246
|
+
}
|
|
219
247
|
function zipPathToPartName(zipPath) {
|
|
220
248
|
return zipPath.startsWith("/") ? zipPath : `/${zipPath}`;
|
|
221
249
|
}
|
|
@@ -272,16 +300,11 @@ function resolveRelativePartName(sourcePartName, target) {
|
|
|
272
300
|
return `/${segments.join("/")}`;
|
|
273
301
|
}
|
|
274
302
|
//#endregion
|
|
275
|
-
//#region src/
|
|
303
|
+
//#region src/ooxml/sequence.ts
|
|
276
304
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* A parent carries at most one `EG_FillProperties` choice, so setting a solid
|
|
281
|
-
* fill first clears any competing choice, then inserts `a:solidFill` in document
|
|
282
|
-
* order. These helpers never mark a part dirty — callers own that.
|
|
305
|
+
* `EG_FillProperties` — the mutually exclusive fill choices, in schema order.
|
|
306
|
+
* @see ECMA-376 Part 1 §20.1.8 (DrawingML fill group)
|
|
283
307
|
*/
|
|
284
|
-
/** The mutually-exclusive fill choices (`EG_FillProperties`); a parent has at most one. */
|
|
285
308
|
const FILL_CHOICES = [
|
|
286
309
|
"a:noFill",
|
|
287
310
|
"a:solidFill",
|
|
@@ -290,6 +313,143 @@ const FILL_CHOICES = [
|
|
|
290
313
|
"a:pattFill",
|
|
291
314
|
"a:grpFill"
|
|
292
315
|
];
|
|
316
|
+
/** `EG_EffectProperties` — the two mutually exclusive effect containers. */
|
|
317
|
+
const EFFECT_CHOICES = ["a:effectLst", "a:effectDag"];
|
|
318
|
+
/** `EG_Geometry` — the two mutually exclusive geometry choices. */
|
|
319
|
+
const GEOMETRY_CHOICES = ["a:custGeom", "a:prstGeom"];
|
|
320
|
+
/**
|
|
321
|
+
* Everything that legally follows `member` in `sequence`, flattened.
|
|
322
|
+
*
|
|
323
|
+
* `member` may name a single element or any member of a choice group; either way the result
|
|
324
|
+
* starts after the step it occupies, so a choice member never lists its own siblings. Throws on
|
|
325
|
+
* an unknown name rather than returning `[]` — an empty successor list silently *appends*, which
|
|
326
|
+
* is exactly the corruption these lists exist to prevent, so a typo must fail loudly and at
|
|
327
|
+
* module-load time rather than produce a subtly out-of-order part at run time.
|
|
328
|
+
*/
|
|
329
|
+
function successorsOf(sequence, member) {
|
|
330
|
+
const index = sequence.findIndex((step) => typeof step === "string" ? step === member : step.includes(member));
|
|
331
|
+
if (index < 0) throw new Error(`successorsOf: ${member} is not part of the given sequence`);
|
|
332
|
+
return sequence.slice(index + 1).flat();
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* `CT_ShapeProperties` (`p:spPr`), in declaration order.
|
|
336
|
+
* @see ECMA-376 Part 1 §20.1.2.2.35
|
|
337
|
+
*/
|
|
338
|
+
const SPPR_SEQUENCE = [
|
|
339
|
+
"a:xfrm",
|
|
340
|
+
GEOMETRY_CHOICES,
|
|
341
|
+
FILL_CHOICES,
|
|
342
|
+
"a:ln",
|
|
343
|
+
EFFECT_CHOICES,
|
|
344
|
+
"a:scene3d",
|
|
345
|
+
"a:sp3d",
|
|
346
|
+
"a:extLst"
|
|
347
|
+
];
|
|
348
|
+
/**
|
|
349
|
+
* `CT_GroupShapeProperties` (`p:grpSpPr`), in declaration order. A group carries no `a:ln` and
|
|
350
|
+
* no `a:sp3d` — which is exactly why it needs its own successor lists rather than borrowing the
|
|
351
|
+
* shape ones.
|
|
352
|
+
* @see ECMA-376 Part 1 §20.1.2.2.19
|
|
353
|
+
*/
|
|
354
|
+
const GRPSPPR_SEQUENCE = [
|
|
355
|
+
"a:xfrm",
|
|
356
|
+
FILL_CHOICES,
|
|
357
|
+
EFFECT_CHOICES,
|
|
358
|
+
"a:scene3d",
|
|
359
|
+
"a:extLst"
|
|
360
|
+
];
|
|
361
|
+
/**
|
|
362
|
+
* `CT_LineProperties` (`a:ln`), in declaration order.
|
|
363
|
+
* @see ECMA-376 Part 1 §20.1.2.2.24
|
|
364
|
+
*/
|
|
365
|
+
const LN_SEQUENCE = [
|
|
366
|
+
FILL_CHOICES,
|
|
367
|
+
["a:prstDash", "a:custDash"],
|
|
368
|
+
[
|
|
369
|
+
"a:round",
|
|
370
|
+
"a:bevel",
|
|
371
|
+
"a:miter"
|
|
372
|
+
],
|
|
373
|
+
"a:headEnd",
|
|
374
|
+
"a:tailEnd",
|
|
375
|
+
"a:extLst"
|
|
376
|
+
];
|
|
377
|
+
/**
|
|
378
|
+
* `CT_TextCharacterProperties` (`a:rPr`/`a:defRPr`/`a:endParaRPr`), in declaration order.
|
|
379
|
+
* @see ECMA-376 Part 1 §21.1.2.3.9
|
|
380
|
+
*/
|
|
381
|
+
const RPR_SEQUENCE = [
|
|
382
|
+
"a:ln",
|
|
383
|
+
FILL_CHOICES,
|
|
384
|
+
EFFECT_CHOICES,
|
|
385
|
+
"a:highlight",
|
|
386
|
+
["a:uLnTx", "a:uLn"],
|
|
387
|
+
["a:uFillTx", "a:uFill"],
|
|
388
|
+
"a:latin",
|
|
389
|
+
"a:ea",
|
|
390
|
+
"a:cs",
|
|
391
|
+
"a:sym",
|
|
392
|
+
"a:hlinkClick",
|
|
393
|
+
"a:hlinkMouseOver",
|
|
394
|
+
"a:rtl",
|
|
395
|
+
"a:extLst"
|
|
396
|
+
];
|
|
397
|
+
/**
|
|
398
|
+
* `CT_TableCellProperties` (`a:tcPr`), in declaration order.
|
|
399
|
+
* @see ECMA-376 Part 1 §21.1.3.17
|
|
400
|
+
*/
|
|
401
|
+
const TCPR_SEQUENCE = [
|
|
402
|
+
"a:lnL",
|
|
403
|
+
"a:lnR",
|
|
404
|
+
"a:lnT",
|
|
405
|
+
"a:lnB",
|
|
406
|
+
"a:lnTlToBr",
|
|
407
|
+
"a:lnBlToTr",
|
|
408
|
+
"a:cell3D",
|
|
409
|
+
...FILL_CHOICES,
|
|
410
|
+
"a:headers",
|
|
411
|
+
"a:extLst"
|
|
412
|
+
];
|
|
413
|
+
/**
|
|
414
|
+
* For each `a:tcPr` child, the children that must follow it — i.e. what a new one is inserted
|
|
415
|
+
* *before*. Every child gets an entry, so a caller can look one up by qname.
|
|
416
|
+
*/
|
|
417
|
+
const TCPR_AFTER = Object.fromEntries(TCPR_SEQUENCE.map((qname, index) => [qname, TCPR_SEQUENCE.slice(index + 1)]));
|
|
418
|
+
/** Successors of `a:xfrm` inside `p:spPr`. */
|
|
419
|
+
const SPPR_AFTER_XFRM = successorsOf(SPPR_SEQUENCE, "a:xfrm");
|
|
420
|
+
/** Successors of a fill choice inside `p:spPr`. */
|
|
421
|
+
const SPPR_FILL_AFTER = successorsOf(SPPR_SEQUENCE, "a:solidFill");
|
|
422
|
+
/** Successors of `a:ln` inside `p:spPr`. */
|
|
423
|
+
const SPPR_LN_AFTER = successorsOf(SPPR_SEQUENCE, "a:ln");
|
|
424
|
+
/** Successors of an effect container inside `p:spPr`. */
|
|
425
|
+
const SPPR_EFFECT_AFTER = successorsOf(SPPR_SEQUENCE, "a:effectLst");
|
|
426
|
+
/** Successors of `a:scene3d` inside `p:spPr`. */
|
|
427
|
+
const SPPR_SCENE3D_AFTER = successorsOf(SPPR_SEQUENCE, "a:scene3d");
|
|
428
|
+
/** Successors of `a:sp3d` inside `p:spPr`. */
|
|
429
|
+
const SPPR_SP3D_AFTER = successorsOf(SPPR_SEQUENCE, "a:sp3d");
|
|
430
|
+
/** Successors of `a:xfrm` inside `p:grpSpPr`. */
|
|
431
|
+
const GRPSPPR_AFTER_XFRM = successorsOf(GRPSPPR_SEQUENCE, "a:xfrm");
|
|
432
|
+
/** Successors of a fill choice inside `p:grpSpPr`. */
|
|
433
|
+
const GRPSPPR_FILL_AFTER = successorsOf(GRPSPPR_SEQUENCE, "a:solidFill");
|
|
434
|
+
/**
|
|
435
|
+
* Successors of `p:spPr` within `p:sp` (and before `p:style` within `p:pic` / `p:cxnSp`).
|
|
436
|
+
* `p:blipFill` and the `p:nv*Pr` wrapper precede `p:spPr` and so are deliberately absent.
|
|
437
|
+
*/
|
|
438
|
+
const SHAPE_AFTER_SPPR = ["p:style", "p:txBody"];
|
|
439
|
+
/** Successors of a fill choice inside `a:ln`. */
|
|
440
|
+
const LN_FILL_AFTER = successorsOf(LN_SEQUENCE, "a:solidFill");
|
|
441
|
+
/** Successors of a fill choice inside `a:rPr` (and `a:defRPr`/`a:endParaRPr`). */
|
|
442
|
+
const RPR_FILL_AFTER = successorsOf(RPR_SEQUENCE, "a:solidFill");
|
|
443
|
+
//#endregion
|
|
444
|
+
//#region src/read/oxml/fill.ts
|
|
445
|
+
/**
|
|
446
|
+
* Shared solid-fill read/mutation helpers for DrawingML colour-bearing parents:
|
|
447
|
+
* a run's `a:rPr`, a shape's `p:spPr` / `p:grpSpPr`, and a line's `a:ln`.
|
|
448
|
+
*
|
|
449
|
+
* A parent carries at most one `EG_FillProperties` choice, so setting a solid
|
|
450
|
+
* fill first clears any competing choice, then inserts `a:solidFill` in document
|
|
451
|
+
* order. These helpers never mark a part dirty — callers own that.
|
|
452
|
+
*/
|
|
293
453
|
/** Normalize a 6-hex RGB string (optional leading `#`) to upper-case, or throw. */
|
|
294
454
|
function normalizeHex(value) {
|
|
295
455
|
const hex = value.startsWith("#") ? value.slice(1) : value;
|
|
@@ -318,41 +478,27 @@ function setSolidFill(parent, afterOrder, color) {
|
|
|
318
478
|
//#endregion
|
|
319
479
|
//#region src/read/oxml/theme.ts
|
|
320
480
|
/**
|
|
321
|
-
* Theme resolution
|
|
322
|
-
*
|
|
323
|
-
* slide XML, so the slide no longer depends on which theme it resolves against.
|
|
481
|
+
* Theme colour resolution: turning a DrawingML colour reference or a style-matrix reference into
|
|
482
|
+
* something literal.
|
|
324
483
|
*
|
|
325
|
-
* Two
|
|
484
|
+
* Two maps do the colour work. A scheme token (`accent1`, `bg1`, …) routes through the master's
|
|
485
|
+
* `clrMap` to a `clrScheme` slot, whose literal RGB is the answer; child colour transforms
|
|
486
|
+
* (`lumMod`/`shade`/`alpha`/…) are carried through untouched rather than computed, so a caller
|
|
487
|
+
* that re-emits them renders identically. A style-matrix reference (`a:fillRef`/`a:lnRef`) instead
|
|
488
|
+
* indexes the theme's `fmtScheme`, and resolves to a deep-cloned entry with its `phClr`
|
|
489
|
+
* placeholder substituted.
|
|
326
490
|
*
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
* emit as `a:srgbClr`. Child colour transforms (`lumMod`/`shade`/`alpha`/…)
|
|
330
|
-
* are carried through untouched, so tints/shades render identically — we swap
|
|
331
|
-
* only the *base* colour reference, never compute the transform math.
|
|
332
|
-
* 2. **Style-matrix refs** — a shape's `p:style` (`lnRef`/`fillRef`/`effectRef`)
|
|
333
|
-
* indexes the theme's `fmtScheme`; we resolve the indexed entry into an
|
|
334
|
-
* explicit `spPr` fill/line/effect (substituting its `phClr` placeholder with
|
|
335
|
-
* the ref's own colour) and neutralize the ref so it cannot re-resolve. The
|
|
336
|
-
* `fontRef` is left intact so its font (and resolved colour) can re-bind to
|
|
337
|
-
* the destination theme — the deliberate "normalize fonts on attach" bonus.
|
|
338
|
-
* 3. **Placeholder-inherited run colour** — a run whose `a:rPr` carries no own
|
|
339
|
-
* fill takes its colour from the source placeholder → layout → master text
|
|
340
|
-
* style chain (`p:txStyles` / placeholder `a:lstStyle`). Rebinding to the
|
|
341
|
-
* destination master would replace that chain and flip the colour, so we
|
|
342
|
-
* resolve each such run's effective colour from the *source* styles and write
|
|
343
|
-
* it explicitly onto the run.
|
|
344
|
-
* 4. **Placeholder-inherited geometry & run size** — a placeholder shape with no
|
|
345
|
-
* own `a:xfrm` takes its position/size from the matching source layout/master
|
|
346
|
-
* placeholder, and a run with no own `sz`/`b`/`i` takes them from the same text
|
|
347
|
-
* style chain. Rebinding to the destination master replaces both inheritances,
|
|
348
|
-
* so a title clips off-canvas and type comes out at the wrong size. We bake the
|
|
349
|
-
* effective `a:xfrm` onto the shape and the effective `sz`/`b`/`i` onto each run.
|
|
350
|
-
* Typeface (`a:latin`) is deliberately *not* baked — it re-binds to the
|
|
351
|
-
* destination theme along with `fontRef`.
|
|
491
|
+
* Everything here is **pure**: it reads a DOM and builds detached elements, and mutates nothing
|
|
492
|
+
* the caller passes in. Two very different consumers depend on that:
|
|
352
493
|
*
|
|
353
|
-
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
494
|
+
* - the read model's colour getters, which resolve a colour to report it;
|
|
495
|
+
* - the import-time flatten passes (`read/api/ops/flatten.ts`), which resolve the same colour in
|
|
496
|
+
* order to *bake* it into a slide.
|
|
497
|
+
*
|
|
498
|
+
* They must agree, so they share this. The passes that write results back into a part live with
|
|
499
|
+
* the other import operations, not here — this module deliberately has no mutating exports.
|
|
500
|
+
* Placeholder-inheritance resolution (what a placeholder gets from its layout/master chain) is
|
|
501
|
+
* its own concern and lives in `placeholder-inherit.ts`.
|
|
356
502
|
*/
|
|
357
503
|
/** The 12 `a:clrScheme` slot names, in schema order. */
|
|
358
504
|
const SCHEME_SLOTS = [
|
|
@@ -376,39 +522,6 @@ const DIRECT_SLOT_TOKENS = /* @__PURE__ */ new Set([
|
|
|
376
522
|
"dk2",
|
|
377
523
|
"lt2"
|
|
378
524
|
]);
|
|
379
|
-
/** Schema successors for ordered insertion into `p:spPr` (CT_ShapeProperties). */
|
|
380
|
-
const SPPR_XFRM_AFTER = [
|
|
381
|
-
"a:custGeom",
|
|
382
|
-
"a:prstGeom",
|
|
383
|
-
...FILL_CHOICES,
|
|
384
|
-
"a:ln",
|
|
385
|
-
"a:effectLst",
|
|
386
|
-
"a:effectDag",
|
|
387
|
-
"a:scene3d",
|
|
388
|
-
"a:sp3d",
|
|
389
|
-
"a:extLst"
|
|
390
|
-
];
|
|
391
|
-
const SPPR_FILL_AFTER$1 = [
|
|
392
|
-
"a:ln",
|
|
393
|
-
"a:effectLst",
|
|
394
|
-
"a:effectDag",
|
|
395
|
-
"a:scene3d",
|
|
396
|
-
"a:sp3d",
|
|
397
|
-
"a:extLst"
|
|
398
|
-
];
|
|
399
|
-
const SPPR_LN_AFTER$1 = [
|
|
400
|
-
"a:effectLst",
|
|
401
|
-
"a:effectDag",
|
|
402
|
-
"a:scene3d",
|
|
403
|
-
"a:sp3d",
|
|
404
|
-
"a:extLst"
|
|
405
|
-
];
|
|
406
|
-
const SPPR_EFFECT_AFTER = [
|
|
407
|
-
"a:scene3d",
|
|
408
|
-
"a:sp3d",
|
|
409
|
-
"a:extLst"
|
|
410
|
-
];
|
|
411
|
-
const SHAPE_AFTER_SPPR$1 = ["p:style", "p:txBody"];
|
|
412
525
|
/** Parse an `a:clrScheme` into slot → 6-hex RGB, reading `srgbClr`/`sysClr`. */
|
|
413
526
|
function parseClrScheme(clrScheme) {
|
|
414
527
|
const out = /* @__PURE__ */ new Map();
|
|
@@ -452,12 +565,6 @@ function resolveSchemeToken(token, ctx) {
|
|
|
452
565
|
const slot = DIRECT_SLOT_TOKENS.has(token) ? token : ctx.clrMap.get(token);
|
|
453
566
|
return slot ? ctx.clrScheme.get(slot) ?? null : null;
|
|
454
567
|
}
|
|
455
|
-
/** Direct child *elements* of `parent`, in order. */
|
|
456
|
-
function childElements$1(parent) {
|
|
457
|
-
const out = [];
|
|
458
|
-
for (let node = parent.firstChild; node; node = node.nextSibling) if (node.nodeType === 1) out.push(node);
|
|
459
|
-
return out;
|
|
460
|
-
}
|
|
461
568
|
/** Whether `el` is a DrawingML element with the given local name. */
|
|
462
569
|
function isA(el, local) {
|
|
463
570
|
return !!el && el.namespaceURI === OOXML_NS.a && el.localName === local;
|
|
@@ -476,7 +583,7 @@ function colorElementHex(color) {
|
|
|
476
583
|
*/
|
|
477
584
|
function resolveColor(color, ctx) {
|
|
478
585
|
if (!color) return null;
|
|
479
|
-
const transforms = childElements
|
|
586
|
+
const transforms = childElements(color);
|
|
480
587
|
if (isA(color, "srgbClr")) {
|
|
481
588
|
const hex = attr(color, "val");
|
|
482
589
|
return hex ? {
|
|
@@ -502,239 +609,128 @@ function resolveColor(color, ctx) {
|
|
|
502
609
|
return null;
|
|
503
610
|
}
|
|
504
611
|
/**
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
511
|
-
* shape so a rebind cannot move or resize it;
|
|
512
|
-
* 4. bake each placeholder run's effective colour and size/weight (inherited from
|
|
513
|
-
* the source layout/master text styles) explicitly onto the run;
|
|
514
|
-
* 5. rewrite every remaining `a:schemeClr` to its literal `a:srgbClr`.
|
|
515
|
-
*
|
|
516
|
-
* Steps run in this order so the inherited/materialized backgrounds are present
|
|
517
|
-
* before the final scheme-colour sweep resolves the colours they carry. The
|
|
518
|
-
* placeholder geometry/colour/size passes have no data dependency on the others.
|
|
519
|
-
* The caller marks the part dirty.
|
|
612
|
+
* Resolve a `+mj-*`/`+mn-*` major/minor font token against a theme `a:fontScheme`
|
|
613
|
+
* to a literal typeface name. A non-token `typeface` (an already-literal face) is
|
|
614
|
+
* returned verbatim. `null` when `typeface` is absent, or when a token cannot be
|
|
615
|
+
* resolved (no `fontScheme`, or the indexed major/minor script slot is empty).
|
|
616
|
+
* The script suffix selects the scheme child: `lt`→`a:latin`, `ea`→`a:ea`,
|
|
617
|
+
* `cs`→`a:cs`.
|
|
520
618
|
*/
|
|
521
|
-
function
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
619
|
+
function resolveThemeFont(typeface, fontScheme) {
|
|
620
|
+
if (!typeface) return null;
|
|
621
|
+
const match = /^\+(mj|mn)-(lt|ea|cs)$/.exec(typeface);
|
|
622
|
+
if (!match) return typeface;
|
|
623
|
+
if (!fontScheme) return null;
|
|
624
|
+
const font = firstChild(fontScheme, match[1] === "mj" ? "a:majorFont" : "a:minorFont");
|
|
625
|
+
const childName = match[2] === "lt" ? "a:latin" : match[2] === "ea" ? "a:ea" : "a:cs";
|
|
626
|
+
const child = font && firstChild(font, childName);
|
|
627
|
+
return child && attr(child, "typeface") || null;
|
|
529
628
|
}
|
|
530
|
-
/**
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
* A placeholder makes sense on its own slide, where it resolves against that deck's
|
|
542
|
-
* master/layout; lifted onto a foreign host its surviving `p:ph` would re-resolve
|
|
543
|
-
* against the *host* placeholder of the same type/idx (wrong inheritance, or a
|
|
544
|
-
* fallback when absent) and could collide with the host's own placeholder. The
|
|
545
|
-
* extra bakes here (anchor + list style, on top of the geometry/colour/size the
|
|
546
|
-
* shared passes cover) make the shape self-contained so demotion loses nothing
|
|
547
|
-
* visible. This is why the demotion is scoped to `flattenShape`: `flattenSlide`
|
|
548
|
-
* keeps placeholders as placeholders by design.
|
|
549
|
-
*
|
|
550
|
-
* `shapeRoot` must be an element whose *descendants* include the lifted shape
|
|
551
|
-
* (the passes match via `getElementsByTagNameNS`, which excludes the root element
|
|
552
|
-
* itself) — the caller wraps the imported `p:sp`/`p:pic`/`p:graphicFrame`/`p:grpSp`
|
|
553
|
-
* in a throwaway container before calling. The caller marks the part dirty.
|
|
554
|
-
*/
|
|
555
|
-
function flattenShape(shapeRoot, ctx) {
|
|
556
|
-
materializeStyleRefs(shapeRoot, ctx);
|
|
557
|
-
resolvePlaceholderGeometry(shapeRoot, ctx);
|
|
558
|
-
resolvePlaceholderRunColors(shapeRoot, ctx);
|
|
559
|
-
resolvePlaceholderRunSizes(shapeRoot, ctx);
|
|
560
|
-
resolvePlaceholderBodyPr(shapeRoot, ctx);
|
|
561
|
-
resolvePlaceholderListStyle(shapeRoot, ctx);
|
|
562
|
-
resolveSchemeColors(shapeRoot, ctx);
|
|
563
|
-
demotePlaceholders(shapeRoot);
|
|
629
|
+
/** Replace every `phClr` under `el` with the ref colour (ref transforms first, then the `phClr`'s own). */
|
|
630
|
+
function substitutePhClr(el, ref) {
|
|
631
|
+
const doc = ownerDocumentOf(el);
|
|
632
|
+
for (const phClr of descendantsByTag(el, OOXML_NS.a, "schemeClr")) {
|
|
633
|
+
if (attr(phClr, "val") !== "phClr") continue;
|
|
634
|
+
const srgb = createElement(doc, "a:srgbClr");
|
|
635
|
+
setAttr(srgb, "val", ref.hex);
|
|
636
|
+
for (const t of ref.transforms) srgb.appendChild(t.cloneNode(true));
|
|
637
|
+
while (phClr.firstChild) srgb.appendChild(phClr.firstChild);
|
|
638
|
+
replaceInParent(phClr, srgb);
|
|
639
|
+
}
|
|
564
640
|
}
|
|
565
|
-
/**
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
* override (`p:clrMapOvr/a:overrideClrMapping`): a source override would keep
|
|
572
|
-
* mapping the slide's scheme-token names the source way and defeat the re-brand,
|
|
573
|
-
* so it must yield to the destination master's `p:clrMap`.
|
|
574
|
-
*
|
|
575
|
-
* Caveat carried from the plan: only *symbolic* colours re-brand. Anything the
|
|
576
|
-
* source authored as a literal `a:srgbClr` has no theme reference to re-resolve
|
|
577
|
-
* and stays exactly that colour. The caller marks the part dirty.
|
|
578
|
-
*/
|
|
579
|
-
function restyleSlide(slideRoot) {
|
|
580
|
-
removeChildrenByQName(slideRoot, ["p:clrMapOvr"]);
|
|
641
|
+
/** The `idx`-th entry (1-based) of a `fmtScheme` style list, deep-cloned, or `null`. */
|
|
642
|
+
function fmtEntry(ctx, listName, idx) {
|
|
643
|
+
const list = ctx.fmtScheme && firstChild(ctx.fmtScheme, listName);
|
|
644
|
+
if (!list || idx < 1) return null;
|
|
645
|
+
const entry = childElements(list)[idx - 1];
|
|
646
|
+
return entry ? entry.cloneNode(true) : null;
|
|
581
647
|
}
|
|
582
648
|
/**
|
|
583
|
-
*
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
*
|
|
587
|
-
*
|
|
588
|
-
*
|
|
589
|
-
*
|
|
590
|
-
* Plain `restyle` cannot re-brand a literal — it carries no theme reference. This
|
|
591
|
-
* re-introduces one by matching the literal's RGB against the slots the *source*
|
|
592
|
-
* theme defined, emitting the scheme token that routes through the source `clrMap`
|
|
593
|
-
* (so a literal equal to the source `accent1` slot becomes `schemeClr accent1`,
|
|
594
|
-
* which the destination `clrMap`/theme then re-resolve). Slots are matched in
|
|
595
|
-
* `clrScheme` order, so the first slot defining a given RGB wins when several share
|
|
596
|
-
* it. Opt-in because it deliberately reinterprets authored literals as theme
|
|
597
|
-
* colours — visual QA territory, the same caveat as the rest of `restyle`.
|
|
649
|
+
* Build the explicit fill element a `p:style` `a:fillRef` resolves to — the indexed
|
|
650
|
+
* `fmtScheme` `fillStyleLst`/`bgFillStyleLst` entry (deep-cloned) with its `phClr`
|
|
651
|
+
* replaced by the ref's resolved colour. Pure: mutates neither the ref nor the
|
|
652
|
+
* theme. `null` when the ref is absent, `idx` is 0/unset, or the entry or its
|
|
653
|
+
* colour cannot be resolved. Shared by the flatten path and the read-model
|
|
654
|
+
* `resolveStyleFillColor` getter so both see the same fill.
|
|
598
655
|
*/
|
|
599
|
-
function
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
const
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const scheme = createElement(doc, "a:schemeClr");
|
|
609
|
-
setAttr(scheme, "val", tokenBySlot.get(slot) ?? slot);
|
|
610
|
-
while (srgb.firstChild) scheme.appendChild(srgb.firstChild);
|
|
611
|
-
replaceInParent(srgb, scheme);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
/** Reverse a slot → RGB `clrScheme` into RGB → slot, in `SCHEME_SLOTS` order (first slot wins on a shared RGB). */
|
|
615
|
-
function reverseClrScheme(clrScheme) {
|
|
616
|
-
const out = /* @__PURE__ */ new Map();
|
|
617
|
-
for (const slot of SCHEME_SLOTS) {
|
|
618
|
-
const hex = clrScheme.get(slot);
|
|
619
|
-
if (hex && !out.has(hex.toUpperCase())) out.set(hex.toUpperCase(), slot);
|
|
620
|
-
}
|
|
621
|
-
return out;
|
|
622
|
-
}
|
|
623
|
-
/** Reverse a token → slot `clrMap` into slot → token (first token wins on a shared slot). */
|
|
624
|
-
function reverseClrMap(clrMap) {
|
|
625
|
-
const out = /* @__PURE__ */ new Map();
|
|
626
|
-
for (const [token, slot] of clrMap) if (!out.has(slot)) out.set(slot, token);
|
|
627
|
-
return out;
|
|
656
|
+
function styleRefFill(fillRef, ctx) {
|
|
657
|
+
if (!fillRef) return null;
|
|
658
|
+
const idx = intAttr(fillRef, "idx");
|
|
659
|
+
if (idx === null || idx <= 0) return null;
|
|
660
|
+
const fill = idx >= 1e3 ? fmtEntry(ctx, "a:bgFillStyleLst", idx - 1e3) : fmtEntry(ctx, "a:fillStyleLst", idx);
|
|
661
|
+
const ref = resolveColor(firstChildElement(fillRef), ctx);
|
|
662
|
+
if (!fill || !ref) return null;
|
|
663
|
+
substitutePhClr(fill, ref);
|
|
664
|
+
return fill;
|
|
628
665
|
}
|
|
629
666
|
/**
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
667
|
+
* Build the explicit `a:ln` element a `p:style` `a:lnRef` resolves to — the indexed
|
|
668
|
+
* `fmtScheme` `lnStyleLst` entry (deep-cloned) with its `phClr` replaced by the
|
|
669
|
+
* ref's resolved colour. Pure; the line/read counterpart of {@link styleRefFill}.
|
|
633
670
|
*/
|
|
634
|
-
function
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
671
|
+
function styleRefLine(lnRef, ctx) {
|
|
672
|
+
if (!lnRef) return null;
|
|
673
|
+
const idx = intAttr(lnRef, "idx");
|
|
674
|
+
if (idx === null || idx <= 0) return null;
|
|
675
|
+
const ln = fmtEntry(ctx, "a:lnStyleLst", idx);
|
|
676
|
+
const ref = resolveColor(firstChildElement(lnRef), ctx);
|
|
677
|
+
if (!ln || !ref) return null;
|
|
678
|
+
substitutePhClr(ln, ref);
|
|
679
|
+
return ln;
|
|
680
|
+
}
|
|
681
|
+
/** Read an integer attribute; `null`/empty/non-finite → `null`. */
|
|
682
|
+
function intAttr(el, name) {
|
|
683
|
+
const value = attr(el, name);
|
|
684
|
+
if (value === null || value === "") return null;
|
|
685
|
+
const n = Number(value);
|
|
686
|
+
return Number.isFinite(n) ? n : null;
|
|
645
687
|
}
|
|
688
|
+
//#endregion
|
|
689
|
+
//#region src/read/oxml/placeholder-inherit.ts
|
|
646
690
|
/**
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
691
|
+
* Resolving what a placeholder shape *inherits* from its source layout/master chain.
|
|
692
|
+
*
|
|
693
|
+
* A placeholder that sets none of its own colour, size, geometry, anchor or list style takes
|
|
694
|
+
* each from a chain: the layout placeholder's `a:lstStyle`, then the master placeholder's, then
|
|
695
|
+
* the master's `p:txStyles` category style (and, on a notes context, the notesMaster
|
|
696
|
+
* `p:notesStyle`). Every function here answers "what would this placeholder get?" — none of them
|
|
697
|
+
* writes anything.
|
|
698
|
+
*
|
|
699
|
+
* **Two callers, one answer.** The read model's getters (`Run.resolvedColor`,
|
|
700
|
+
* `TextFrame.resolvedAnchor`, the font getters) and the import-time flatten passes
|
|
701
|
+
* (`read/api/ops/flatten.ts`, which bakes the inherited values onto a slide so they survive a
|
|
702
|
+
* rebind to a different master) both ask the same questions. They must not answer them
|
|
703
|
+
* differently: if the getter and the bake disagreed, a caller would see one colour before export
|
|
704
|
+
* and another in the file. Sharing this module is what makes that structurally impossible.
|
|
705
|
+
*
|
|
706
|
+
* This module is pure — mutation lives in the flatten pass, which is also why it sits in
|
|
707
|
+
* `read/oxml/` (substrate) rather than `read/api/ops/`.
|
|
650
708
|
*/
|
|
651
|
-
function materializeBackground(slideRoot, ctx) {
|
|
652
|
-
const doc = ownerDocumentOf(slideRoot);
|
|
653
|
-
for (const bg of elementsByTag(slideRoot, OOXML_NS.p, "bg")) {
|
|
654
|
-
const bgRef = firstChild(bg, "p:bgRef");
|
|
655
|
-
if (!bgRef) continue;
|
|
656
|
-
const fill = styleRefFill(bgRef, ctx);
|
|
657
|
-
const bgPr = createElement(doc, "p:bgPr");
|
|
658
|
-
bgPr.appendChild(fill ?? createElement(doc, "a:noFill"));
|
|
659
|
-
bg.replaceChild(bgPr, bgRef);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
/** Rewrite every `a:schemeClr` under `root` to a literal `a:srgbClr` when resolvable. */
|
|
663
|
-
function resolveSchemeColors(root, ctx) {
|
|
664
|
-
const doc = ownerDocumentOf(root);
|
|
665
|
-
for (const schemeClr of elementsByTag(root, OOXML_NS.a, "schemeClr")) {
|
|
666
|
-
const token = attr(schemeClr, "val");
|
|
667
|
-
const hex = token ? resolveSchemeToken(token, ctx) : null;
|
|
668
|
-
if (!hex) continue;
|
|
669
|
-
const srgb = createElement(doc, "a:srgbClr");
|
|
670
|
-
setAttr(srgb, "val", hex);
|
|
671
|
-
while (schemeClr.firstChild) srgb.appendChild(schemeClr.firstChild);
|
|
672
|
-
replaceInParent(schemeClr, srgb);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
/** Schema successors of `a:solidFill` inside `a:rPr` (CT_TextCharacterProperties sequence). */
|
|
676
|
-
const RPR_FILL_AFTER = [
|
|
677
|
-
"a:effectLst",
|
|
678
|
-
"a:effectDag",
|
|
679
|
-
"a:highlight",
|
|
680
|
-
"a:uLnTx",
|
|
681
|
-
"a:uLn",
|
|
682
|
-
"a:uFillTx",
|
|
683
|
-
"a:uFill",
|
|
684
|
-
"a:latin",
|
|
685
|
-
"a:ea",
|
|
686
|
-
"a:cs",
|
|
687
|
-
"a:sym",
|
|
688
|
-
"a:hlinkClick",
|
|
689
|
-
"a:hlinkMouseOver",
|
|
690
|
-
"a:rtl",
|
|
691
|
-
"a:extLst"
|
|
692
|
-
];
|
|
693
709
|
/** The master `p:txStyles` style element name for a placeholder category. */
|
|
694
710
|
const TX_STYLE_NAME = {
|
|
695
711
|
title: "p:titleStyle",
|
|
696
712
|
body: "p:bodyStyle",
|
|
697
713
|
other: "p:otherStyle"
|
|
698
714
|
};
|
|
699
|
-
/**
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const slideLst = firstChild(txBody, "a:lstStyle");
|
|
719
|
-
const byLevel = /* @__PURE__ */ new Map();
|
|
720
|
-
for (const p of getElements(txBody, "a:p")) {
|
|
721
|
-
const pPr = firstChild(p, "a:pPr");
|
|
722
|
-
const level = (pPr && intValue(attr(pPr, "lvl"))) ?? 0;
|
|
723
|
-
const runs = [...getElements(p, "a:r"), ...getElements(p, "a:fld")];
|
|
724
|
-
if (runs.length === 0) continue;
|
|
725
|
-
let color = byLevel.get(level);
|
|
726
|
-
if (color === void 0) {
|
|
727
|
-
color = placeholderInheritedColor(type, idx, level, ctx);
|
|
728
|
-
byLevel.set(level, color);
|
|
729
|
-
}
|
|
730
|
-
if (!color) continue;
|
|
731
|
-
for (const run of runs) {
|
|
732
|
-
if (slideDefinesColor(run, pPr, slideLst, level)) continue;
|
|
733
|
-
writeRunColor(run, color);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
}
|
|
715
|
+
/** The ordered children of a `CT_TextListStyle` (`a:lstStyle` / a `p:txStyles` style). */
|
|
716
|
+
const LST_STYLE_LEVELS = [
|
|
717
|
+
"a:defPPr",
|
|
718
|
+
"a:lvl1pPr",
|
|
719
|
+
"a:lvl2pPr",
|
|
720
|
+
"a:lvl3pPr",
|
|
721
|
+
"a:lvl4pPr",
|
|
722
|
+
"a:lvl5pPr",
|
|
723
|
+
"a:lvl6pPr",
|
|
724
|
+
"a:lvl7pPr",
|
|
725
|
+
"a:lvl8pPr",
|
|
726
|
+
"a:lvl9pPr"
|
|
727
|
+
];
|
|
728
|
+
/** Inheritable run properties baked under `preserve`: size and weight/slant (not typeface). */
|
|
729
|
+
const RUN_PROP_NAMES = [
|
|
730
|
+
"sz",
|
|
731
|
+
"b",
|
|
732
|
+
"i"
|
|
733
|
+
];
|
|
738
734
|
/** The `p:ph` element of a shape (`p:sp/p:nvSpPr/p:nvPr/p:ph`), or `null`. */
|
|
739
735
|
function placeholderOf(sp) {
|
|
740
736
|
const nvSpPr = firstChild(sp, "p:nvSpPr");
|
|
@@ -784,7 +780,7 @@ const SINGLETON_PH = /* @__PURE__ */ new Set([
|
|
|
784
780
|
*/
|
|
785
781
|
function findPlaceholder(root, slideType, slideIdx) {
|
|
786
782
|
if (slideType && SINGLETON_PH.has(slideType)) {
|
|
787
|
-
for (const sp of
|
|
783
|
+
for (const sp of descendantsByTag(root, OOXML_NS.p, "sp")) {
|
|
788
784
|
const ph = placeholderOf(sp);
|
|
789
785
|
if (ph && attr(ph, "type") === slideType) return sp;
|
|
790
786
|
}
|
|
@@ -793,7 +789,7 @@ function findPlaceholder(root, slideType, slideIdx) {
|
|
|
793
789
|
const cat = phCategory(slideType);
|
|
794
790
|
let idxMatch = null;
|
|
795
791
|
let catMatch = null;
|
|
796
|
-
for (const sp of
|
|
792
|
+
for (const sp of descendantsByTag(root, OOXML_NS.p, "sp")) {
|
|
797
793
|
const ph = placeholderOf(sp);
|
|
798
794
|
if (!ph) continue;
|
|
799
795
|
const type = attr(ph, "type");
|
|
@@ -827,259 +823,14 @@ function lstStyleLevelFill(listStyle, level) {
|
|
|
827
823
|
return defRPr ? firstChild(defRPr, "a:solidFill") : null;
|
|
828
824
|
}
|
|
829
825
|
/**
|
|
830
|
-
* The
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
* resolves against `ctx` (the `a:srgbClr`/`a:schemeClr`/… inside its
|
|
834
|
-
* `a:solidFill`), or `null` when nothing in the chain defines a resolvable colour.
|
|
835
|
-
* The read-model `Run.resolvedColor` getter feeds this element to
|
|
836
|
-
* `resolveColorElement` for the `effectiveHex`; the flatten path resolves it
|
|
837
|
-
* directly via {@link placeholderInheritedColor}.
|
|
838
|
-
*/
|
|
839
|
-
function placeholderInheritedFill(type, idx, level, ctx) {
|
|
840
|
-
const tiers = [];
|
|
841
|
-
if (ctx.layoutRoot) {
|
|
842
|
-
const layoutPh = findPlaceholder(ctx.layoutRoot, type, idx);
|
|
843
|
-
tiers.push(layoutPh && lstStyleLevelFill(placeholderLstStyle(layoutPh), level));
|
|
844
|
-
}
|
|
845
|
-
if (ctx.masterRoot) {
|
|
846
|
-
const masterPh = findPlaceholder(ctx.masterRoot, type, idx);
|
|
847
|
-
tiers.push(masterPh && lstStyleLevelFill(placeholderLstStyle(masterPh), level));
|
|
848
|
-
const txStyles = firstChild(ctx.masterRoot, "p:txStyles");
|
|
849
|
-
const styleEl = txStyles && firstChild(txStyles, TX_STYLE_NAME[phCategory(type)]);
|
|
850
|
-
tiers.push(styleEl && lstStyleLevelFill(styleEl, level));
|
|
851
|
-
}
|
|
852
|
-
if (ctx.notesStyle) tiers.push(lstStyleLevelFill(ctx.notesStyle, level));
|
|
853
|
-
for (const fill of tiers) {
|
|
854
|
-
const colorEl = fill && firstChildElement(fill);
|
|
855
|
-
if (colorEl && resolveColor(colorEl, ctx)) return colorEl;
|
|
856
|
-
}
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
859
|
-
/**
|
|
860
|
-
* The level `a:defRPr` elements a placeholder inherits from the source style
|
|
861
|
-
* chain, in resolution order: the layout placeholder's `a:lstStyle`, then the
|
|
862
|
-
* master placeholder's, then the master `p:txStyles` category style — and, on a
|
|
863
|
-
* notes context only, the notesMaster `p:notesStyle` (see `ctx.notesStyle`). Tiers
|
|
864
|
-
* with no `a:defRPr` for `level` are dropped. The shared root for inherited run
|
|
865
|
-
* *size* and *typeface* resolution (the size/face sibling of
|
|
866
|
-
* {@link placeholderInheritedFill}), read directly by the flatten path and the
|
|
867
|
-
* read-model font getters.
|
|
868
|
-
*/
|
|
869
|
-
function placeholderInheritedDefRPrs(type, idx, level, ctx) {
|
|
870
|
-
const tiers = [];
|
|
871
|
-
if (ctx.layoutRoot) {
|
|
872
|
-
const layoutPh = findPlaceholder(ctx.layoutRoot, type, idx);
|
|
873
|
-
tiers.push(layoutPh && lstStyleLevelDefRPr(placeholderLstStyle(layoutPh), level));
|
|
874
|
-
}
|
|
875
|
-
if (ctx.masterRoot) {
|
|
876
|
-
const masterPh = findPlaceholder(ctx.masterRoot, type, idx);
|
|
877
|
-
tiers.push(masterPh && lstStyleLevelDefRPr(placeholderLstStyle(masterPh), level));
|
|
878
|
-
const txStyles = firstChild(ctx.masterRoot, "p:txStyles");
|
|
879
|
-
const styleEl = txStyles && firstChild(txStyles, TX_STYLE_NAME[phCategory(type)]);
|
|
880
|
-
tiers.push(styleEl && lstStyleLevelDefRPr(styleEl, level));
|
|
881
|
-
}
|
|
882
|
-
if (ctx.notesStyle) tiers.push(lstStyleLevelDefRPr(ctx.notesStyle, level));
|
|
883
|
-
return tiers.filter((t) => t !== null);
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* Resolve a `+mj-*`/`+mn-*` major/minor font token against a theme `a:fontScheme`
|
|
887
|
-
* to a literal typeface name. A non-token `typeface` (an already-literal face) is
|
|
888
|
-
* returned verbatim. `null` when `typeface` is absent, or when a token cannot be
|
|
889
|
-
* resolved (no `fontScheme`, or the indexed major/minor script slot is empty).
|
|
890
|
-
* The script suffix selects the scheme child: `lt`→`a:latin`, `ea`→`a:ea`,
|
|
891
|
-
* `cs`→`a:cs`.
|
|
892
|
-
*/
|
|
893
|
-
function resolveThemeFont(typeface, fontScheme) {
|
|
894
|
-
if (!typeface) return null;
|
|
895
|
-
const match = /^\+(mj|mn)-(lt|ea|cs)$/.exec(typeface);
|
|
896
|
-
if (!match) return typeface;
|
|
897
|
-
if (!fontScheme) return null;
|
|
898
|
-
const font = firstChild(fontScheme, match[1] === "mj" ? "a:majorFont" : "a:minorFont");
|
|
899
|
-
const childName = match[2] === "lt" ? "a:latin" : match[2] === "ea" ? "a:ea" : "a:cs";
|
|
900
|
-
const child = font && firstChild(font, childName);
|
|
901
|
-
return child && attr(child, "typeface") || null;
|
|
902
|
-
}
|
|
903
|
-
/**
|
|
904
|
-
* The colour a placeholder run inherits from the source style chain, resolved to a
|
|
905
|
-
* literal `{ hex, transforms }`. Thin wrapper over {@link placeholderInheritedFill}
|
|
906
|
-
* for the flatten path, which re-emits the transforms verbatim. Returns `null`
|
|
907
|
-
* when nothing in the chain defines a resolvable colour (the run then re-binds to
|
|
908
|
-
* the destination).
|
|
909
|
-
*/
|
|
910
|
-
function placeholderInheritedColor(type, idx, level, ctx) {
|
|
911
|
-
const colorEl = placeholderInheritedFill(type, idx, level, ctx);
|
|
912
|
-
return colorEl ? resolveColor(colorEl, ctx) : null;
|
|
913
|
-
}
|
|
914
|
-
/** Whether the *slide itself* already fixes this run's colour (so a rebind cannot change it). */
|
|
915
|
-
function slideDefinesColor(run, pPr, slideLst, level) {
|
|
916
|
-
const rPr = firstChild(run, "a:rPr");
|
|
917
|
-
if (rPr && FILL_CHOICES.some((q) => firstChild(rPr, q))) return true;
|
|
918
|
-
const defRPr = pPr && firstChild(pPr, "a:defRPr");
|
|
919
|
-
if (defRPr && firstChild(defRPr, "a:solidFill")) return true;
|
|
920
|
-
return !!lstStyleLevelFill(slideLst, level);
|
|
921
|
-
}
|
|
922
|
-
/** Write a resolved colour as an explicit `a:solidFill` (with carried transforms) onto a run's `a:rPr`. */
|
|
923
|
-
function writeRunColor(run, color) {
|
|
924
|
-
const doc = ownerDocumentOf(run);
|
|
925
|
-
const rPr = getOrAddChild(run, "a:rPr", ["a:t"]);
|
|
926
|
-
const fill = createElement(doc, "a:solidFill");
|
|
927
|
-
const srgb = createElement(doc, "a:srgbClr");
|
|
928
|
-
setAttr(srgb, "val", color.hex);
|
|
929
|
-
for (const t of color.transforms) srgb.appendChild(t.cloneNode(true));
|
|
930
|
-
fill.appendChild(srgb);
|
|
931
|
-
insertInOrder(rPr, fill, RPR_FILL_AFTER);
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Bake placeholder-inherited geometry onto the slide (gap 3). A placeholder shape
|
|
935
|
-
* that carries no own `p:spPr/a:xfrm` takes its position/size from the matching
|
|
936
|
-
* source `slideLayout` placeholder, else the `slideMaster` placeholder. Rebinding
|
|
937
|
-
* to the destination master replaces that inheritance, so the placeholder would
|
|
938
|
-
* snap to the destination default (often clipping off-canvas). We deep-clone the
|
|
939
|
-
* effective source `a:xfrm` and write it explicitly onto the shape. Shapes with
|
|
940
|
-
* their own `a:xfrm` are left untouched (explicit geometry is not inherited), and
|
|
941
|
-
* an orphan placeholder with no source match keeps the current fall-back behaviour.
|
|
942
|
-
*/
|
|
943
|
-
function resolvePlaceholderGeometry(slideRoot, ctx) {
|
|
944
|
-
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
945
|
-
for (const sp of elementsByTag(slideRoot, OOXML_NS.p, "sp")) {
|
|
946
|
-
const ph = placeholderOf(sp);
|
|
947
|
-
if (!ph) continue;
|
|
948
|
-
const spPr = firstChild(sp, "p:spPr");
|
|
949
|
-
if (spPr && firstChild(spPr, "a:xfrm")) continue;
|
|
950
|
-
const found = placeholderInheritedXfrm(attr(ph, "type"), attr(ph, "idx") ?? "0", ctx);
|
|
951
|
-
if (!found) continue;
|
|
952
|
-
insertInOrder(getOrAddChild(sp, "p:spPr", SHAPE_AFTER_SPPR$1), found.xfrm.cloneNode(true), SPPR_XFRM_AFTER);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
/**
|
|
956
|
-
* The `a:xfrm` a placeholder inherits from the source layout, then master,
|
|
957
|
-
* tagged with which tier it came from — `null` when neither defines one. The
|
|
958
|
-
* read-model {@link resolveInheritedFrame} (`theme-context.ts`) sibling of
|
|
959
|
-
* {@link placeholderInheritedAnchor}; also the bake source for
|
|
960
|
-
* {@link resolvePlaceholderGeometry}.
|
|
961
|
-
*/
|
|
962
|
-
function placeholderInheritedXfrm(type, idx, ctx) {
|
|
963
|
-
for (const [root, source] of [[ctx.layoutRoot, "layout"], [ctx.masterRoot, "master"]]) {
|
|
964
|
-
if (!root) continue;
|
|
965
|
-
const ph = findPlaceholder(root, type, idx);
|
|
966
|
-
const spPr = ph && firstChild(ph, "p:spPr");
|
|
967
|
-
const xfrm = spPr && firstChild(spPr, "a:xfrm");
|
|
968
|
-
if (xfrm) return {
|
|
969
|
-
xfrm,
|
|
970
|
-
source
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
return null;
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* The vertical anchor (`a:bodyPr/@anchor`) a placeholder inherits from the source
|
|
977
|
-
* layout, then master, or `null`. Resolves per-attribute: the first tier whose
|
|
978
|
-
* placeholder `a:bodyPr` actually sets `@anchor` wins, so a layout `a:bodyPr`
|
|
979
|
-
* present but without `@anchor` does not mask the master's. The read-model
|
|
980
|
-
* {@link TextFrame.resolvedAnchor} sibling of {@link placeholderInheritedXfrm}.
|
|
981
|
-
*/
|
|
982
|
-
function placeholderInheritedAnchor(type, idx, ctx) {
|
|
983
|
-
for (const root of [ctx.layoutRoot, ctx.masterRoot]) {
|
|
984
|
-
if (!root) continue;
|
|
985
|
-
const ph = findPlaceholder(root, type, idx);
|
|
986
|
-
const txBody = ph && firstChild(ph, "p:txBody");
|
|
987
|
-
const bodyPr = txBody && firstChild(txBody, "a:bodyPr");
|
|
988
|
-
const anchor = bodyPr && attr(bodyPr, "anchor");
|
|
989
|
-
if (anchor) return anchor;
|
|
990
|
-
}
|
|
991
|
-
return null;
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Bake the placeholder-inherited vertical anchor onto a lifted shape's text body
|
|
995
|
-
* (the {@link flattenShape} placeholder-demotion path). A placeholder whose own
|
|
996
|
-
* `a:bodyPr` sets no `@anchor` inherits it from the source layout/master placeholder;
|
|
997
|
-
* once {@link demotePlaceholders} strips the `p:ph` the shape would lose that
|
|
998
|
-
* inheritance and fall back to top-anchored, so a vertically-centred title jumps.
|
|
999
|
-
* We resolve the effective anchor from the source chain (via
|
|
1000
|
-
* {@link placeholderInheritedAnchor}) and write it explicitly. Shapes whose
|
|
1001
|
-
* `a:bodyPr` already fixes `@anchor`, and placeholders with no resolvable inherited
|
|
1002
|
-
* anchor, are left untouched. Other `a:bodyPr` knobs (insets, autofit) are left as
|
|
1003
|
-
* authored — they have sane defaults a plain shape keeps.
|
|
1004
|
-
*/
|
|
1005
|
-
function resolvePlaceholderBodyPr(shapeRoot, ctx) {
|
|
1006
|
-
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
1007
|
-
for (const sp of elementsByTag(shapeRoot, OOXML_NS.p, "sp")) {
|
|
1008
|
-
const ph = placeholderOf(sp);
|
|
1009
|
-
if (!ph) continue;
|
|
1010
|
-
const txBody = firstChild(sp, "p:txBody");
|
|
1011
|
-
if (!txBody) continue;
|
|
1012
|
-
const bodyPr = firstChild(txBody, "a:bodyPr");
|
|
1013
|
-
if (bodyPr && attr(bodyPr, "anchor") != null) continue;
|
|
1014
|
-
const anchor = placeholderInheritedAnchor(attr(ph, "type"), attr(ph, "idx") ?? "0", ctx);
|
|
1015
|
-
if (!anchor) continue;
|
|
1016
|
-
setAttr(bodyPr ?? getOrAddChild(txBody, "a:bodyPr", ["a:lstStyle", "a:p"]), "anchor", anchor);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
/** The ordered children of a `CT_TextListStyle` (`a:lstStyle` / a `p:txStyles` style). */
|
|
1020
|
-
const LST_STYLE_LEVELS = [
|
|
1021
|
-
"a:defPPr",
|
|
1022
|
-
"a:lvl1pPr",
|
|
1023
|
-
"a:lvl2pPr",
|
|
1024
|
-
"a:lvl3pPr",
|
|
1025
|
-
"a:lvl4pPr",
|
|
1026
|
-
"a:lvl5pPr",
|
|
1027
|
-
"a:lvl6pPr",
|
|
1028
|
-
"a:lvl7pPr",
|
|
1029
|
-
"a:lvl8pPr",
|
|
1030
|
-
"a:lvl9pPr"
|
|
1031
|
-
];
|
|
1032
|
-
/**
|
|
1033
|
-
* Bake the placeholder-inherited list style onto a lifted shape's text body (the
|
|
1034
|
-
* {@link flattenShape} placeholder-demotion path). A placeholder's per-level
|
|
1035
|
-
* *paragraph* formatting — indent (`marL`/`indent`), bullets (`a:buChar`/
|
|
1036
|
-
* `a:buAutoNum`/`a:buNone`), alignment, and the level `a:defRPr` — is inherited
|
|
1037
|
-
* from the source layout/master placeholder `a:lstStyle` and the master
|
|
1038
|
-
* `p:txStyles` category style. Run colour/size are baked onto each run by the
|
|
1039
|
-
* sibling passes, but paragraph-level defaults live here; once
|
|
1040
|
-
* {@link demotePlaceholders} strips the `p:ph` the shape stops inheriting them, so
|
|
1041
|
-
* bullets and indents would vanish. We materialize the effective list style onto
|
|
1042
|
-
* the shape's `p:txBody/a:lstStyle`.
|
|
826
|
+
* The source style tiers a placeholder resolves against, most specific first: the layout
|
|
827
|
+
* placeholder's `a:lstStyle`, the master placeholder's, then the master `p:txStyles` category
|
|
828
|
+
* style — and, on a notes context only, the notesMaster `p:notesStyle`.
|
|
1043
829
|
*
|
|
1044
|
-
*
|
|
1045
|
-
*
|
|
1046
|
-
* (layout placeholder, then master placeholder, then master category style) is
|
|
1047
|
-
* cloned. This is a whole-element overlay, not the per-attribute merge PowerPoint
|
|
1048
|
-
* does — but explicit paragraph `a:pPr` on the slide's own runs travels with the
|
|
1049
|
-
* shape and still wins, so this only supplies defaults for the inherited case
|
|
1050
|
-
* (paragraphs that set no `a:pPr` of their own). Scheme colours in the cloned
|
|
1051
|
-
* levels are resolved by the later {@link resolveSchemeColors} pass.
|
|
830
|
+
* The one place the chain is spelled out. `placeholderInherited*` differ only in what they pull
|
|
831
|
+
* from each tier, so they all walk this.
|
|
1052
832
|
*/
|
|
1053
|
-
function
|
|
1054
|
-
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
1055
|
-
for (const sp of elementsByTag(shapeRoot, OOXML_NS.p, "sp")) {
|
|
1056
|
-
const ph = placeholderOf(sp);
|
|
1057
|
-
if (!ph) continue;
|
|
1058
|
-
const txBody = firstChild(sp, "p:txBody");
|
|
1059
|
-
if (!txBody) continue;
|
|
1060
|
-
const tiers = placeholderInheritedListStyles(attr(ph, "type"), attr(ph, "idx") ?? "0", ctx);
|
|
1061
|
-
if (tiers.length === 0) continue;
|
|
1062
|
-
const slideLst = firstChild(txBody, "a:lstStyle");
|
|
1063
|
-
const merged = createElement(ownerDocumentOf(txBody), "a:lstStyle");
|
|
1064
|
-
let any = false;
|
|
1065
|
-
for (const level of LST_STYLE_LEVELS) {
|
|
1066
|
-
const src = (slideLst && firstChild(slideLst, level)) ?? tiers.map((t) => firstChild(t, level)).find((e) => !!e) ?? null;
|
|
1067
|
-
if (!src) continue;
|
|
1068
|
-
merged.appendChild(src.cloneNode(true));
|
|
1069
|
-
any = true;
|
|
1070
|
-
}
|
|
1071
|
-
if (!any) continue;
|
|
1072
|
-
if (slideLst) txBody.replaceChild(merged, slideLst);
|
|
1073
|
-
else insertInOrder(txBody, merged, ["a:p"]);
|
|
1074
|
-
}
|
|
1075
|
-
}
|
|
1076
|
-
/**
|
|
1077
|
-
* The source list-style tiers a placeholder inherits paragraph formatting from,
|
|
1078
|
-
* most specific first: layout placeholder `a:lstStyle`, master placeholder
|
|
1079
|
-
* `a:lstStyle`, then the master `p:txStyles` category style (itself a
|
|
1080
|
-
* `CT_TextListStyle`). The paragraph-level sibling of {@link placeholderInheritedDefRPrs}.
|
|
1081
|
-
*/
|
|
1082
|
-
function placeholderInheritedListStyles(type, idx, ctx) {
|
|
833
|
+
function styleTiers(type, idx, ctx) {
|
|
1083
834
|
const tiers = [];
|
|
1084
835
|
if (ctx.layoutRoot) {
|
|
1085
836
|
const layoutPh = findPlaceholder(ctx.layoutRoot, type, idx);
|
|
@@ -1091,73 +842,61 @@ function placeholderInheritedListStyles(type, idx, ctx) {
|
|
|
1091
842
|
const txStyles = firstChild(ctx.masterRoot, "p:txStyles");
|
|
1092
843
|
tiers.push(txStyles && firstChild(txStyles, TX_STYLE_NAME[phCategory(type)]));
|
|
1093
844
|
}
|
|
845
|
+
if (ctx.notesStyle) tiers.push(ctx.notesStyle);
|
|
1094
846
|
return tiers.filter((t) => t !== null);
|
|
1095
847
|
}
|
|
1096
848
|
/**
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
* deck's layout/master placeholder of the same type/idx (wrong inheritance, or a
|
|
1103
|
-
* fallback when the host has none) and could collide with the host slide's own
|
|
1104
|
-
* placeholder of that type. Removing the marker severs both. The `p:nvPr` itself is
|
|
1105
|
-
* kept (it can carry media/custom-data children); only the `p:ph` child goes.
|
|
849
|
+
* The colour *element* a placeholder run inherits from the source style chain. Returns the
|
|
850
|
+
* first tier's colour element that resolves against `ctx` (the `a:srgbClr`/`a:schemeClr`/…
|
|
851
|
+
* inside its `a:solidFill`), or `null` when nothing in the chain defines a resolvable colour.
|
|
852
|
+
* The read-model `Run.resolvedColor` getter feeds this element to `resolveColorElement` for the
|
|
853
|
+
* `effectiveHex`; the flatten path resolves it directly via {@link placeholderInheritedColor}.
|
|
1106
854
|
*/
|
|
1107
|
-
function
|
|
1108
|
-
for (const
|
|
1109
|
-
const
|
|
1110
|
-
const
|
|
1111
|
-
if (
|
|
855
|
+
function placeholderInheritedFill(type, idx, level, ctx) {
|
|
856
|
+
for (const tier of styleTiers(type, idx, ctx)) {
|
|
857
|
+
const fill = lstStyleLevelFill(tier, level);
|
|
858
|
+
const colorEl = fill && firstChildElement(fill);
|
|
859
|
+
if (colorEl && resolveColor(colorEl, ctx)) return colorEl;
|
|
1112
860
|
}
|
|
861
|
+
return null;
|
|
1113
862
|
}
|
|
1114
|
-
/** Inheritable run properties baked under `preserve`: size and weight/slant (not typeface). */
|
|
1115
|
-
const RUN_PROP_NAMES = [
|
|
1116
|
-
"sz",
|
|
1117
|
-
"b",
|
|
1118
|
-
"i"
|
|
1119
|
-
];
|
|
1120
863
|
/**
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
*
|
|
1124
|
-
*
|
|
1125
|
-
*
|
|
1126
|
-
* `a:rPr`. Each property resolves independently up the chain. Typeface (`a:latin`)
|
|
1127
|
-
* is left to re-bind to the destination theme, as gap 1 does for the `fontRef`.
|
|
864
|
+
* The level `a:defRPr` elements a placeholder inherits from the source style chain, in
|
|
865
|
+
* resolution order. Tiers with no `a:defRPr` for `level` are dropped. The shared root for
|
|
866
|
+
* inherited run *size* and *typeface* resolution (the size/face sibling of
|
|
867
|
+
* {@link placeholderInheritedFill}), read directly by the flatten path and the read-model font
|
|
868
|
+
* getters.
|
|
1128
869
|
*/
|
|
1129
|
-
function
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
870
|
+
function placeholderInheritedDefRPrs(type, idx, level, ctx) {
|
|
871
|
+
return styleTiers(type, idx, ctx).map((tier) => lstStyleLevelDefRPr(tier, level)).filter((t) => t !== null);
|
|
872
|
+
}
|
|
873
|
+
/**
|
|
874
|
+
* The source list-style tiers a placeholder inherits paragraph formatting from, most specific
|
|
875
|
+
* first. The paragraph-level sibling of {@link placeholderInheritedDefRPrs} — it hands back the
|
|
876
|
+
* tier elements whole rather than pulling one level's properties out of each.
|
|
877
|
+
*
|
|
878
|
+
* The notes tier is included for consistency with the rest of the chain, though it cannot arise
|
|
879
|
+
* in practice: the only caller is the list-style bake in `flattenShape`, which runs on slide and
|
|
880
|
+
* shape contexts, and `ctx.notesStyle` is set only on a notes context.
|
|
881
|
+
*/
|
|
882
|
+
function placeholderInheritedListStyles(type, idx, ctx) {
|
|
883
|
+
return styleTiers(type, idx, ctx);
|
|
884
|
+
}
|
|
885
|
+
/**
|
|
886
|
+
* The colour a placeholder run inherits from the source style chain, resolved to a
|
|
887
|
+
* literal `{ hex, transforms }`. Thin wrapper over {@link placeholderInheritedFill}
|
|
888
|
+
* for the flatten path, which re-emits the transforms verbatim. Returns `null`
|
|
889
|
+
* when nothing in the chain defines a resolvable colour (the run then re-binds to
|
|
890
|
+
* the destination).
|
|
891
|
+
*/
|
|
892
|
+
function placeholderInheritedColor(type, idx, level, ctx) {
|
|
893
|
+
const colorEl = placeholderInheritedFill(type, idx, level, ctx);
|
|
894
|
+
return colorEl ? resolveColor(colorEl, ctx) : null;
|
|
1154
895
|
}
|
|
1155
896
|
/**
|
|
1156
|
-
* The run size/weight a placeholder run inherits from the source style chain
|
|
1157
|
-
*
|
|
1158
|
-
* `
|
|
1159
|
-
* the first tier that defines it (properties resolve independently). Returns
|
|
1160
|
-
* `null` when no tier defines any of them.
|
|
897
|
+
* The run size/weight a placeholder run inherits from the source style chain, per list level.
|
|
898
|
+
* Each of `sz`/`b`/`i` is taken from the first tier that defines it (properties resolve
|
|
899
|
+
* independently). Returns `null` when no tier defines any of them.
|
|
1161
900
|
*/
|
|
1162
901
|
function placeholderInheritedRunProps(type, idx, level, ctx) {
|
|
1163
902
|
const tiers = placeholderInheritedDefRPrs(type, idx, level, ctx);
|
|
@@ -1174,141 +913,60 @@ function placeholderInheritedRunProps(type, idx, level, ctx) {
|
|
|
1174
913
|
}
|
|
1175
914
|
return any ? props : null;
|
|
1176
915
|
}
|
|
1177
|
-
/** Whether the *slide itself* already fixes a run property (so a rebind cannot change it). */
|
|
1178
|
-
function slideDefinesProp(name, run, pPr, slideLst, level) {
|
|
1179
|
-
const rPr = firstChild(run, "a:rPr");
|
|
1180
|
-
if (rPr && attr(rPr, name) != null) return true;
|
|
1181
|
-
const defRPr = pPr && firstChild(pPr, "a:defRPr");
|
|
1182
|
-
if (defRPr && attr(defRPr, name) != null) return true;
|
|
1183
|
-
const slideDefRPr = lstStyleLevelDefRPr(slideLst, level);
|
|
1184
|
-
return !!(slideDefRPr && attr(slideDefRPr, name) != null);
|
|
1185
|
-
}
|
|
1186
|
-
/** Write each resolved run property onto a run's `a:rPr`, skipping ones the slide already fixes. */
|
|
1187
|
-
function writeRunProps(run, props, pPr, slideLst, level) {
|
|
1188
|
-
let rPr = null;
|
|
1189
|
-
for (const name of RUN_PROP_NAMES) {
|
|
1190
|
-
const value = props[name];
|
|
1191
|
-
if (value == null) continue;
|
|
1192
|
-
if (slideDefinesProp(name, run, pPr, slideLst, level)) continue;
|
|
1193
|
-
rPr ??= getOrAddChild(run, "a:rPr", ["a:t"]);
|
|
1194
|
-
setAttr(rPr, name, value);
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
/** Snapshot all descendant elements of `root` matching a namespace + local name. */
|
|
1198
|
-
function elementsByTag(root, ns, local) {
|
|
1199
|
-
const out = [];
|
|
1200
|
-
for (const el of root.getElementsByTagNameNS(ns, local)) out.push(el);
|
|
1201
|
-
return out;
|
|
1202
|
-
}
|
|
1203
916
|
/**
|
|
1204
|
-
*
|
|
1205
|
-
*
|
|
1206
|
-
* `
|
|
917
|
+
* The `a:xfrm` a placeholder inherits from the source layout, then master,
|
|
918
|
+
* tagged with which tier it came from — `null` when neither defines one. The
|
|
919
|
+
* read-model `resolveInheritedFrame` (`theme-context.ts`) sibling of
|
|
920
|
+
* {@link placeholderInheritedAnchor}; also the bake source for the flatten pass'
|
|
921
|
+
* `resolvePlaceholderGeometry`.
|
|
1207
922
|
*/
|
|
1208
|
-
function
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1219
|
-
/** Replace every `phClr` under `el` with the ref colour (ref transforms first, then the `phClr`'s own). */
|
|
1220
|
-
function substitutePhClr(el, ref) {
|
|
1221
|
-
const doc = ownerDocumentOf(el);
|
|
1222
|
-
for (const phClr of elementsByTag(el, OOXML_NS.a, "schemeClr")) {
|
|
1223
|
-
if (attr(phClr, "val") !== "phClr") continue;
|
|
1224
|
-
const srgb = createElement(doc, "a:srgbClr");
|
|
1225
|
-
setAttr(srgb, "val", ref.hex);
|
|
1226
|
-
for (const t of ref.transforms) srgb.appendChild(t.cloneNode(true));
|
|
1227
|
-
while (phClr.firstChild) srgb.appendChild(phClr.firstChild);
|
|
1228
|
-
replaceInParent(phClr, srgb);
|
|
923
|
+
function placeholderInheritedXfrm(type, idx, ctx) {
|
|
924
|
+
for (const [root, source] of [[ctx.layoutRoot, "layout"], [ctx.masterRoot, "master"]]) {
|
|
925
|
+
if (!root) continue;
|
|
926
|
+
const ph = findPlaceholder(root, type, idx);
|
|
927
|
+
const spPr = ph && firstChild(ph, "p:spPr");
|
|
928
|
+
const xfrm = spPr && firstChild(spPr, "a:xfrm");
|
|
929
|
+
if (xfrm) return {
|
|
930
|
+
xfrm,
|
|
931
|
+
source
|
|
932
|
+
};
|
|
1229
933
|
}
|
|
1230
|
-
|
|
1231
|
-
/** The `idx`-th entry (1-based) of a `fmtScheme` style list, deep-cloned, or `null`. */
|
|
1232
|
-
function fmtEntry(ctx, listName, idx) {
|
|
1233
|
-
const list = ctx.fmtScheme && firstChild(ctx.fmtScheme, listName);
|
|
1234
|
-
if (!list || idx < 1) return null;
|
|
1235
|
-
const entry = childElements$1(list)[idx - 1];
|
|
1236
|
-
return entry ? entry.cloneNode(true) : null;
|
|
1237
|
-
}
|
|
1238
|
-
/**
|
|
1239
|
-
* Build the explicit fill element a `p:style` `a:fillRef` resolves to — the indexed
|
|
1240
|
-
* `fmtScheme` `fillStyleLst`/`bgFillStyleLst` entry (deep-cloned) with its `phClr`
|
|
1241
|
-
* replaced by the ref's resolved colour. Pure: mutates neither the ref nor the
|
|
1242
|
-
* theme. `null` when the ref is absent, `idx` is 0/unset, or the entry or its
|
|
1243
|
-
* colour cannot be resolved. Shared by the flatten path ({@link materializeFill})
|
|
1244
|
-
* and the read-model `resolveStyleFillColor` getter so both see the same fill.
|
|
1245
|
-
*/
|
|
1246
|
-
function styleRefFill(fillRef, ctx) {
|
|
1247
|
-
if (!fillRef) return null;
|
|
1248
|
-
const idx = intAttr(fillRef, "idx");
|
|
1249
|
-
if (idx === null || idx <= 0) return null;
|
|
1250
|
-
const fill = idx >= 1e3 ? fmtEntry(ctx, "a:bgFillStyleLst", idx - 1e3) : fmtEntry(ctx, "a:fillStyleLst", idx);
|
|
1251
|
-
const ref = resolveColor(firstChildElement(fillRef), ctx);
|
|
1252
|
-
if (!fill || !ref) return null;
|
|
1253
|
-
substitutePhClr(fill, ref);
|
|
1254
|
-
return fill;
|
|
934
|
+
return null;
|
|
1255
935
|
}
|
|
1256
936
|
/**
|
|
1257
|
-
*
|
|
1258
|
-
*
|
|
1259
|
-
*
|
|
937
|
+
* The vertical anchor (`a:bodyPr/@anchor`) a placeholder inherits from the source
|
|
938
|
+
* layout, then master, or `null`. Resolves per-attribute: the first tier whose
|
|
939
|
+
* placeholder `a:bodyPr` actually sets `@anchor` wins, so a layout `a:bodyPr`
|
|
940
|
+
* present but without `@anchor` does not mask the master's. The read-model
|
|
941
|
+
* `TextFrame.resolvedAnchor` sibling of {@link placeholderInheritedXfrm}.
|
|
1260
942
|
*/
|
|
1261
|
-
function
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
return ln;
|
|
1270
|
-
}
|
|
1271
|
-
function materializeFill(spPr, fillRef, ctx) {
|
|
1272
|
-
if (!fillRef) return;
|
|
1273
|
-
if (!FILL_CHOICES.some((q) => firstChild(spPr, q))) {
|
|
1274
|
-
const fill = styleRefFill(fillRef, ctx);
|
|
1275
|
-
if (fill) insertInOrder(spPr, fill, SPPR_FILL_AFTER$1);
|
|
1276
|
-
}
|
|
1277
|
-
neutralizeRef(fillRef);
|
|
1278
|
-
}
|
|
1279
|
-
function materializeLine(spPr, lnRef, ctx) {
|
|
1280
|
-
if (!lnRef) return;
|
|
1281
|
-
if (!firstChild(spPr, "a:ln")) {
|
|
1282
|
-
const ln = styleRefLine(lnRef, ctx);
|
|
1283
|
-
if (ln) insertInOrder(spPr, ln, SPPR_LN_AFTER$1);
|
|
1284
|
-
}
|
|
1285
|
-
neutralizeRef(lnRef);
|
|
1286
|
-
}
|
|
1287
|
-
function materializeEffect(spPr, effectRef, ctx) {
|
|
1288
|
-
if (!effectRef) return;
|
|
1289
|
-
const idx = intAttr(effectRef, "idx");
|
|
1290
|
-
if (idx !== null && idx > 0 && !firstChild(spPr, "a:effectLst") && !firstChild(spPr, "a:effectDag")) {
|
|
1291
|
-
const style = fmtEntry(ctx, "a:effectStyleLst", idx);
|
|
1292
|
-
const ref = resolveColor(firstChildElement(effectRef), ctx);
|
|
1293
|
-
if (style && ref) {
|
|
1294
|
-
substitutePhClr(style, ref);
|
|
1295
|
-
for (const child of childElements$1(style)) if (isA(child, "effectLst") || isA(child, "effectDag")) insertInOrder(spPr, child, SPPR_EFFECT_AFTER);
|
|
1296
|
-
else if (isA(child, "scene3d")) insertInOrder(spPr, child, ["a:sp3d", "a:extLst"]);
|
|
1297
|
-
else if (isA(child, "sp3d")) insertInOrder(spPr, child, ["a:extLst"]);
|
|
1298
|
-
}
|
|
943
|
+
function placeholderInheritedAnchor(type, idx, ctx) {
|
|
944
|
+
for (const root of [ctx.layoutRoot, ctx.masterRoot]) {
|
|
945
|
+
if (!root) continue;
|
|
946
|
+
const ph = findPlaceholder(root, type, idx);
|
|
947
|
+
const txBody = ph && firstChild(ph, "p:txBody");
|
|
948
|
+
const bodyPr = txBody && firstChild(txBody, "a:bodyPr");
|
|
949
|
+
const anchor = bodyPr && attr(bodyPr, "anchor");
|
|
950
|
+
if (anchor) return anchor;
|
|
1299
951
|
}
|
|
1300
|
-
|
|
952
|
+
return null;
|
|
1301
953
|
}
|
|
1302
|
-
/**
|
|
1303
|
-
function
|
|
1304
|
-
|
|
1305
|
-
|
|
954
|
+
/** Whether the *slide itself* already fixes this run's colour (so a rebind cannot change it). */
|
|
955
|
+
function slideDefinesColor(run, pPr, slideLst, level) {
|
|
956
|
+
const rPr = firstChild(run, "a:rPr");
|
|
957
|
+
if (rPr && FILL_CHOICES.some((q) => firstChild(rPr, q))) return true;
|
|
958
|
+
const defRPr = pPr && firstChild(pPr, "a:defRPr");
|
|
959
|
+
if (defRPr && firstChild(defRPr, "a:solidFill")) return true;
|
|
960
|
+
return !!lstStyleLevelFill(slideLst, level);
|
|
1306
961
|
}
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
962
|
+
/** Whether the *slide itself* already fixes a run property (so a rebind cannot change it). */
|
|
963
|
+
function slideDefinesProp(name, run, pPr, slideLst, level) {
|
|
964
|
+
const rPr = firstChild(run, "a:rPr");
|
|
965
|
+
if (rPr && attr(rPr, name) != null) return true;
|
|
966
|
+
const defRPr = pPr && firstChild(pPr, "a:defRPr");
|
|
967
|
+
if (defRPr && attr(defRPr, name) != null) return true;
|
|
968
|
+
const slideDefRPr = lstStyleLevelDefRPr(slideLst, level);
|
|
969
|
+
return !!(slideDefRPr && attr(slideDefRPr, name) != null);
|
|
1312
970
|
}
|
|
1313
971
|
//#endregion
|
|
1314
972
|
//#region src/read/oxml/color-transform.ts
|
|
@@ -1503,17 +1161,6 @@ function applyColorTransforms(baseHex, transforms) {
|
|
|
1503
1161
|
* background needs on top of {@link resolveSlideThemeParts} — so a token resolves
|
|
1504
1162
|
* identically whether it is read or baked.
|
|
1505
1163
|
*/
|
|
1506
|
-
const SLIDE_LAYOUT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
1507
|
-
const SLIDE_MASTER_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
1508
|
-
const NOTES_MASTER_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster";
|
|
1509
|
-
const THEME_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
1510
|
-
const OFFICE_DOCUMENT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
1511
|
-
/** Resolve the single relationship of `type` owned by `partName`, or `null`. */
|
|
1512
|
-
function resolveSingleRel(opc, partName, type) {
|
|
1513
|
-
const rels = opc.relationshipsFor(partName);
|
|
1514
|
-
const rel = rels.byType(type)[0];
|
|
1515
|
-
return rel ? rels.resolveTarget(rel.id) : null;
|
|
1516
|
-
}
|
|
1517
1164
|
/** The document element of a part, or `null` when the partname/part is absent. */
|
|
1518
1165
|
function documentElement(opc, partName) {
|
|
1519
1166
|
return partName ? opc.part(partName)?.dom.documentElement ?? null : null;
|
|
@@ -1522,7 +1169,7 @@ function documentElement(opc, partName) {
|
|
|
1522
1169
|
* The presentation's `p:defaultTextStyle` (`presentation.xml`, reached via the
|
|
1523
1170
|
* package `officeDocument` relationship), or `null` when absent. This is
|
|
1524
1171
|
* PowerPoint's lowest-priority text fallback — the bottom tier of a slide run's
|
|
1525
|
-
* size/colour/face/bold resolution (see `
|
|
1172
|
+
* size/colour/face/bold resolution (see `ThemeContext.defaultTextStyle`).
|
|
1526
1173
|
*/
|
|
1527
1174
|
function presentationDefaultTextStyle(opc) {
|
|
1528
1175
|
const root = documentElement(opc, resolveSingleRel(opc, "/", OFFICE_DOCUMENT_REL));
|
|
@@ -2044,73 +1691,15 @@ function readPatternFill(container, ctx) {
|
|
|
2044
1691
|
//#endregion
|
|
2045
1692
|
//#region src/read/api/shapes/oxml.ts
|
|
2046
1693
|
/**
|
|
2047
|
-
* Shape-element plumbing: the
|
|
2048
|
-
*
|
|
1694
|
+
* Shape-element plumbing: the small element lookups every shape kind performs, plus the
|
|
1695
|
+
* shape-scoped re-exports of the shared schema-successor lists.
|
|
2049
1696
|
*
|
|
2050
1697
|
* The successor arrays are the reason a setter can create a missing `a:xfrm` / `a:solidFill` /
|
|
2051
1698
|
* `a:ln` without corrupting the part: `getOrAddChild` inserts before the first listed sibling it
|
|
2052
1699
|
* finds, so each list must name exactly the children that legally *follow* the one being added.
|
|
1700
|
+
* They are declared once in `src/ooxml/sequence.ts` (derived from each complexType's sequence)
|
|
1701
|
+
* and re-exported here so the shape modules keep importing them from one place.
|
|
2053
1702
|
*/
|
|
2054
|
-
const SPPR_AFTER_XFRM = [
|
|
2055
|
-
"a:custGeom",
|
|
2056
|
-
"a:prstGeom",
|
|
2057
|
-
"a:noFill",
|
|
2058
|
-
"a:solidFill",
|
|
2059
|
-
"a:gradFill",
|
|
2060
|
-
"a:blipFill",
|
|
2061
|
-
"a:pattFill",
|
|
2062
|
-
"a:grpFill",
|
|
2063
|
-
"a:ln",
|
|
2064
|
-
"a:effectLst",
|
|
2065
|
-
"a:effectDag",
|
|
2066
|
-
"a:scene3d",
|
|
2067
|
-
"a:sp3d",
|
|
2068
|
-
"a:extLst"
|
|
2069
|
-
];
|
|
2070
|
-
const GRPSPPR_AFTER_XFRM = [
|
|
2071
|
-
"a:noFill",
|
|
2072
|
-
"a:solidFill",
|
|
2073
|
-
"a:gradFill",
|
|
2074
|
-
"a:blipFill",
|
|
2075
|
-
"a:pattFill",
|
|
2076
|
-
"a:grpFill",
|
|
2077
|
-
"a:effectLst",
|
|
2078
|
-
"a:effectDag",
|
|
2079
|
-
"a:scene3d",
|
|
2080
|
-
"a:extLst"
|
|
2081
|
-
];
|
|
2082
|
-
const SHAPE_AFTER_SPPR = ["p:style", "p:txBody"];
|
|
2083
|
-
const SPPR_FILL_AFTER = [
|
|
2084
|
-
"a:ln",
|
|
2085
|
-
"a:effectLst",
|
|
2086
|
-
"a:effectDag",
|
|
2087
|
-
"a:scene3d",
|
|
2088
|
-
"a:sp3d",
|
|
2089
|
-
"a:extLst"
|
|
2090
|
-
];
|
|
2091
|
-
const SPPR_LN_AFTER = [
|
|
2092
|
-
"a:effectLst",
|
|
2093
|
-
"a:effectDag",
|
|
2094
|
-
"a:scene3d",
|
|
2095
|
-
"a:sp3d",
|
|
2096
|
-
"a:extLst"
|
|
2097
|
-
];
|
|
2098
|
-
const GRPSPPR_FILL_AFTER = [
|
|
2099
|
-
"a:effectLst",
|
|
2100
|
-
"a:effectDag",
|
|
2101
|
-
"a:scene3d",
|
|
2102
|
-
"a:extLst"
|
|
2103
|
-
];
|
|
2104
|
-
const LN_FILL_AFTER = [
|
|
2105
|
-
"a:prstDash",
|
|
2106
|
-
"a:custDash",
|
|
2107
|
-
"a:round",
|
|
2108
|
-
"a:bevel",
|
|
2109
|
-
"a:miter",
|
|
2110
|
-
"a:headEnd",
|
|
2111
|
-
"a:tailEnd",
|
|
2112
|
-
"a:extLst"
|
|
2113
|
-
];
|
|
2114
1703
|
/** First `<p:cNvPr>` reached through the shape's non-visual properties wrapper (`p:nv*Pr`). */
|
|
2115
1704
|
function nonVisualCNvPr(element) {
|
|
2116
1705
|
for (let node = element.firstChild; node; node = node.nextSibling) {
|
|
@@ -2118,24 +1707,141 @@ function nonVisualCNvPr(element) {
|
|
|
2118
1707
|
const child = node;
|
|
2119
1708
|
if (child.namespaceURI === OOXML_NS.p && child.localName?.startsWith("nv")) return firstChild(child, "p:cNvPr");
|
|
2120
1709
|
}
|
|
2121
|
-
return null;
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
function emuFrom(parent, qname, attribute) {
|
|
1713
|
+
const element = parent && firstChild(parent, qname);
|
|
1714
|
+
return element ? intValue(attr(element, attribute)) : null;
|
|
1715
|
+
}
|
|
1716
|
+
/**
|
|
1717
|
+
* One EMU coordinate from a shape's own `p:spPr/a:xfrm`, or `null` when any link in that chain
|
|
1718
|
+
* is absent — which is how an inheriting placeholder (one with no `a:xfrm` of its own) reports
|
|
1719
|
+
* that its geometry comes from the layout/master instead. See `placeholderInheritedXfrm` for
|
|
1720
|
+
* resolving what it would inherit.
|
|
1721
|
+
*/
|
|
1722
|
+
function spPrXfrmEmu(sp, container, axis) {
|
|
1723
|
+
const spPr = firstChild(sp, "p:spPr");
|
|
1724
|
+
const xfrm = spPr && firstChild(spPr, "a:xfrm");
|
|
1725
|
+
const el = xfrm && firstChild(xfrm, container);
|
|
1726
|
+
return el ? intValue(attr(el, axis)) : null;
|
|
1727
|
+
}
|
|
1728
|
+
/** Get-or-add `p:spPr/a:xfrm` for shapes whose transform lives in `p:spPr` (`p:sp`, `p:pic`, `p:cxnSp`). */
|
|
1729
|
+
function getOrAddSpPrXfrm(shapeElement) {
|
|
1730
|
+
return getOrAddChild(getOrAddChild(shapeElement, "p:spPr", SHAPE_AFTER_SPPR), "a:xfrm", SPPR_AFTER_XFRM);
|
|
1731
|
+
}
|
|
1732
|
+
//#endregion
|
|
1733
|
+
//#region src/read/api/shapes/geometry.ts
|
|
1734
|
+
/**
|
|
1735
|
+
* Reading geometry out of a shape: transform boxes, rotation and flips, and custom-geometry paths.
|
|
1736
|
+
*
|
|
1737
|
+
* Every value here comes straight off `a:xfrm` / `a:path` attributes, so these are pure element
|
|
1738
|
+
* readers with no theme or relationship context.
|
|
1739
|
+
*/
|
|
1740
|
+
/**
|
|
1741
|
+
* One `a:pt` coordinate as a raw path-unit integer. A guide-name reference
|
|
1742
|
+
* (the `ST_AdjCoordinate` string form) is not produced by authored freeforms;
|
|
1743
|
+
* a non-numeric value degrades to `0` rather than crashing (documented edge).
|
|
1744
|
+
*/
|
|
1745
|
+
function ptAxis(pt, axis) {
|
|
1746
|
+
return (pt ? intValue(attr(pt, axis)) : null) ?? 0;
|
|
1747
|
+
}
|
|
1748
|
+
/** Parse one `<a:path>` into its viewport attrs (with schema defaults) and ordered segments. */
|
|
1749
|
+
function readGeometryPath(path) {
|
|
1750
|
+
const commands = [];
|
|
1751
|
+
for (let node = path.firstChild; node; node = node.nextSibling) {
|
|
1752
|
+
if (node.nodeType !== 1) continue;
|
|
1753
|
+
const seg = node;
|
|
1754
|
+
if (seg.namespaceURI !== OOXML_NS.a) continue;
|
|
1755
|
+
const pts = getElements(seg, "a:pt");
|
|
1756
|
+
switch (seg.localName) {
|
|
1757
|
+
case "moveTo":
|
|
1758
|
+
commands.push({
|
|
1759
|
+
cmd: "moveTo",
|
|
1760
|
+
x: ptAxis(pts[0], "x"),
|
|
1761
|
+
y: ptAxis(pts[0], "y")
|
|
1762
|
+
});
|
|
1763
|
+
break;
|
|
1764
|
+
case "lnTo":
|
|
1765
|
+
commands.push({
|
|
1766
|
+
cmd: "lnTo",
|
|
1767
|
+
x: ptAxis(pts[0], "x"),
|
|
1768
|
+
y: ptAxis(pts[0], "y")
|
|
1769
|
+
});
|
|
1770
|
+
break;
|
|
1771
|
+
case "cubicBezTo":
|
|
1772
|
+
commands.push({
|
|
1773
|
+
cmd: "cubicBezTo",
|
|
1774
|
+
x1: ptAxis(pts[0], "x"),
|
|
1775
|
+
y1: ptAxis(pts[0], "y"),
|
|
1776
|
+
x2: ptAxis(pts[1], "x"),
|
|
1777
|
+
y2: ptAxis(pts[1], "y"),
|
|
1778
|
+
x: ptAxis(pts[2], "x"),
|
|
1779
|
+
y: ptAxis(pts[2], "y")
|
|
1780
|
+
});
|
|
1781
|
+
break;
|
|
1782
|
+
case "quadBezTo":
|
|
1783
|
+
commands.push({
|
|
1784
|
+
cmd: "quadBezTo",
|
|
1785
|
+
x1: ptAxis(pts[0], "x"),
|
|
1786
|
+
y1: ptAxis(pts[0], "y"),
|
|
1787
|
+
x: ptAxis(pts[1], "x"),
|
|
1788
|
+
y: ptAxis(pts[1], "y")
|
|
1789
|
+
});
|
|
1790
|
+
break;
|
|
1791
|
+
case "arcTo":
|
|
1792
|
+
commands.push({
|
|
1793
|
+
cmd: "arcTo",
|
|
1794
|
+
wR: intValue(attr(seg, "wR")) ?? 0,
|
|
1795
|
+
hR: intValue(attr(seg, "hR")) ?? 0,
|
|
1796
|
+
stAng: (intValue(attr(seg, "stAng")) ?? 0) / 6e4,
|
|
1797
|
+
swAng: (intValue(attr(seg, "swAng")) ?? 0) / 6e4
|
|
1798
|
+
});
|
|
1799
|
+
break;
|
|
1800
|
+
case "close": commands.push({ cmd: "close" });
|
|
1801
|
+
}
|
|
1802
|
+
}
|
|
1803
|
+
return {
|
|
1804
|
+
w: intValue(attr(path, "w")) ?? 0,
|
|
1805
|
+
h: intValue(attr(path, "h")) ?? 0,
|
|
1806
|
+
fill: attr(path, "fill") ?? "norm",
|
|
1807
|
+
stroke: boolValue(attr(path, "stroke")) ?? true,
|
|
1808
|
+
commands
|
|
1809
|
+
};
|
|
2122
1810
|
}
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
1811
|
+
/** Validate and round an EMU geometry value; extents (`cx`/`cy`) must be non-negative. */
|
|
1812
|
+
function toEmu(value, attribute, allowNegative) {
|
|
1813
|
+
if (!Number.isFinite(value)) throw new InvalidOptionError("coord/non-finite", `${attribute} must be a finite number of EMU, got ${value}`);
|
|
1814
|
+
if (!allowNegative && value < 0) throw new InvalidOptionError("coord/negative", `${attribute} must be non-negative, got ${value}`);
|
|
1815
|
+
return Math.round(value);
|
|
2126
1816
|
}
|
|
2127
|
-
/**
|
|
2128
|
-
function
|
|
2129
|
-
const
|
|
2130
|
-
|
|
2131
|
-
|
|
1817
|
+
/** A point + extent pair (`a:off`/`a:ext` or `a:chOff`/`a:chExt`) from a transform, or `null` if either is incomplete. */
|
|
1818
|
+
function readBox(xfrm, offName, extName) {
|
|
1819
|
+
const off = firstChild(xfrm, offName);
|
|
1820
|
+
const ext = firstChild(xfrm, extName);
|
|
1821
|
+
const x = off && intValue(attr(off, "x"));
|
|
1822
|
+
const y = off && intValue(attr(off, "y"));
|
|
1823
|
+
const cx = ext && intValue(attr(ext, "cx"));
|
|
1824
|
+
const cy = ext && intValue(attr(ext, "cy"));
|
|
1825
|
+
if (x === null || y === null || cx === null || cy === null) return null;
|
|
1826
|
+
return {
|
|
1827
|
+
x,
|
|
1828
|
+
y,
|
|
1829
|
+
cx,
|
|
1830
|
+
cy
|
|
1831
|
+
};
|
|
2132
1832
|
}
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
return
|
|
1833
|
+
function rotationDegrees(xfrm) {
|
|
1834
|
+
const rot = intValue(attr(xfrm, "rot"));
|
|
1835
|
+
return rot === null ? 0 : rot / 6e4;
|
|
1836
|
+
}
|
|
1837
|
+
function transformFlipH(xfrm) {
|
|
1838
|
+
return boolValue(attr(xfrm, "flipH")) === true;
|
|
1839
|
+
}
|
|
1840
|
+
function transformFlipV(xfrm) {
|
|
1841
|
+
return boolValue(attr(xfrm, "flipV")) === true;
|
|
2136
1842
|
}
|
|
2137
1843
|
//#endregion
|
|
2138
|
-
//#region src/read/group-transform.ts
|
|
1844
|
+
//#region src/read/api/shapes/group-transform.ts
|
|
2139
1845
|
/** Normalise a degree value into [0, 360). */
|
|
2140
1846
|
function normalizeDegrees(value) {
|
|
2141
1847
|
return (value % 360 + 360) % 360;
|
|
@@ -2664,22 +2370,68 @@ var Paragraph = class {
|
|
|
2664
2370
|
return this.#emuAttrPt("indent");
|
|
2665
2371
|
}
|
|
2666
2372
|
/**
|
|
2667
|
-
*
|
|
2668
|
-
*
|
|
2669
|
-
*
|
|
2670
|
-
*
|
|
2671
|
-
*
|
|
2373
|
+
* This paragraph's bullet — its kind, and for a drawn bullet the glyph, the
|
|
2374
|
+
* numbering start, and the bullet's own font/size/colour. `null` when the
|
|
2375
|
+
* paragraph names no bullet at all and inherits one from the list style.
|
|
2376
|
+
*
|
|
2377
|
+
* Structured rather than a tagged string, which is not merely tidier. The
|
|
2378
|
+
* previous `bullet` accessor reported `'none'` / `'char:•'` /
|
|
2379
|
+
* `'autoNum:arabicPeriod'`, and that shape is ambiguous when the glyph is
|
|
2380
|
+
* itself a colon; worse, it reads as a bare glyph, which is exactly how the
|
|
2381
|
+
* script converter first consumed it — `'none'.codePointAt(0)` put a literal
|
|
2382
|
+
* `n` bullet on every converted deck, silently. See {@link BulletDetail}.
|
|
2672
2383
|
*/
|
|
2673
|
-
get
|
|
2384
|
+
get bulletDetail() {
|
|
2674
2385
|
const pPr = firstChild(this.element, "a:pPr");
|
|
2675
2386
|
if (!pPr) return null;
|
|
2676
|
-
if (firstChild(pPr, "a:buNone")) return "none";
|
|
2387
|
+
if (firstChild(pPr, "a:buNone")) return { kind: "none" };
|
|
2388
|
+
const style = this.#bulletStyle(pPr);
|
|
2677
2389
|
const buChar = firstChild(pPr, "a:buChar");
|
|
2678
|
-
if (buChar) return
|
|
2390
|
+
if (buChar) return {
|
|
2391
|
+
kind: "char",
|
|
2392
|
+
char: attr(buChar, "char") ?? "",
|
|
2393
|
+
...style
|
|
2394
|
+
};
|
|
2679
2395
|
const buAutoNum = firstChild(pPr, "a:buAutoNum");
|
|
2680
|
-
if (buAutoNum) return
|
|
2396
|
+
if (buAutoNum) return {
|
|
2397
|
+
kind: "autoNum",
|
|
2398
|
+
scheme: attr(buAutoNum, "type") ?? "",
|
|
2399
|
+
startAt: intValue(attr(buAutoNum, "startAt")),
|
|
2400
|
+
...style
|
|
2401
|
+
};
|
|
2402
|
+
const buBlip = firstChild(pPr, "a:buBlip");
|
|
2403
|
+
if (buBlip) return {
|
|
2404
|
+
kind: "picture",
|
|
2405
|
+
imagePartName: this.#bulletImagePartName(buBlip),
|
|
2406
|
+
...style
|
|
2407
|
+
};
|
|
2681
2408
|
return null;
|
|
2682
2409
|
}
|
|
2410
|
+
/** The `a:buFont` / `a:buSz*` / `a:buClr` siblings of a `a:pPr` bullet choice. */
|
|
2411
|
+
#bulletStyle(pPr) {
|
|
2412
|
+
const buFont = firstChild(pPr, "a:buFont");
|
|
2413
|
+
const buSzPct = firstChild(pPr, "a:buSzPct");
|
|
2414
|
+
const buSzPts = firstChild(pPr, "a:buSzPts");
|
|
2415
|
+
const buClr = firstChild(pPr, "a:buClr");
|
|
2416
|
+
const colorEl = buClr ? firstChildElement(buClr) : null;
|
|
2417
|
+
const pctVal = buSzPct ? intValue(attr(buSzPct, "val")) : null;
|
|
2418
|
+
const ptVal = buSzPts ? intValue(attr(buSzPts, "val")) : null;
|
|
2419
|
+
return {
|
|
2420
|
+
font: buFont ? attr(buFont, "typeface") ?? null : null,
|
|
2421
|
+
sizePct: pctVal === null ? null : pctVal / 1e3,
|
|
2422
|
+
sizePt: ptVal === null ? null : ptVal / 100,
|
|
2423
|
+
color: colorEl?.localName === "srgbClr" ? attr(colorEl, "val") ?? null : null,
|
|
2424
|
+
schemeColor: colorEl?.localName === "schemeClr" ? attr(colorEl, "val") ?? null : null,
|
|
2425
|
+
resolvedColor: this.themeContext ? resolveColorElement(colorEl, this.themeContext) : null
|
|
2426
|
+
};
|
|
2427
|
+
}
|
|
2428
|
+
/** Resolve a picture bullet's `a:buBlip/a:blip/@r:embed` to an absolute partname. */
|
|
2429
|
+
#bulletImagePartName(buBlip) {
|
|
2430
|
+
const blip = firstChild(buBlip, "a:blip");
|
|
2431
|
+
const relId = blip && attr(blip, "r:embed");
|
|
2432
|
+
if (!relId || !this.relationships) return null;
|
|
2433
|
+
return this.relationships.resolveTarget(relId);
|
|
2434
|
+
}
|
|
2683
2435
|
/** Points from a spacing child's `a:spcPts/@val` (hundredths of a point), or `null`. */
|
|
2684
2436
|
#spacingPt(qname) {
|
|
2685
2437
|
const pPr = firstChild(this.element, "a:pPr");
|
|
@@ -2862,7 +2614,7 @@ function setTextBodyText(txBody, value) {
|
|
|
2862
2614
|
p = createElement(doc, "a:p");
|
|
2863
2615
|
txBody.appendChild(p);
|
|
2864
2616
|
}
|
|
2865
|
-
for (const child of
|
|
2617
|
+
for (const child of childElements(p)) if (child.localName === "r" || child.localName === "br" || child.localName === "fld") p.removeChild(child);
|
|
2866
2618
|
const run = createElement(doc, "a:r");
|
|
2867
2619
|
if (rPrTemplate) run.appendChild(rPrTemplate.cloneNode(true));
|
|
2868
2620
|
const t = createElement(doc, "a:t");
|
|
@@ -2874,117 +2626,6 @@ function setTextBodyText(txBody, value) {
|
|
|
2874
2626
|
p.insertBefore(run, endParaRPr);
|
|
2875
2627
|
}
|
|
2876
2628
|
//#endregion
|
|
2877
|
-
//#region src/read/api/shapes/geometry.ts
|
|
2878
|
-
/**
|
|
2879
|
-
* Reading geometry out of a shape: transform boxes, rotation and flips, and custom-geometry paths.
|
|
2880
|
-
*
|
|
2881
|
-
* Every value here comes straight off `a:xfrm` / `a:path` attributes, so these are pure element
|
|
2882
|
-
* readers with no theme or relationship context.
|
|
2883
|
-
*/
|
|
2884
|
-
/**
|
|
2885
|
-
* One `a:pt` coordinate as a raw path-unit integer. A guide-name reference
|
|
2886
|
-
* (the `ST_AdjCoordinate` string form) is not produced by authored freeforms;
|
|
2887
|
-
* a non-numeric value degrades to `0` rather than crashing (documented edge).
|
|
2888
|
-
*/
|
|
2889
|
-
function ptAxis(pt, axis) {
|
|
2890
|
-
return (pt ? intValue(attr(pt, axis)) : null) ?? 0;
|
|
2891
|
-
}
|
|
2892
|
-
/** Parse one `<a:path>` into its viewport attrs (with schema defaults) and ordered segments. */
|
|
2893
|
-
function readGeometryPath(path) {
|
|
2894
|
-
const commands = [];
|
|
2895
|
-
for (let node = path.firstChild; node; node = node.nextSibling) {
|
|
2896
|
-
if (node.nodeType !== 1) continue;
|
|
2897
|
-
const seg = node;
|
|
2898
|
-
if (seg.namespaceURI !== OOXML_NS.a) continue;
|
|
2899
|
-
const pts = getElements(seg, "a:pt");
|
|
2900
|
-
switch (seg.localName) {
|
|
2901
|
-
case "moveTo":
|
|
2902
|
-
commands.push({
|
|
2903
|
-
cmd: "moveTo",
|
|
2904
|
-
x: ptAxis(pts[0], "x"),
|
|
2905
|
-
y: ptAxis(pts[0], "y")
|
|
2906
|
-
});
|
|
2907
|
-
break;
|
|
2908
|
-
case "lnTo":
|
|
2909
|
-
commands.push({
|
|
2910
|
-
cmd: "lnTo",
|
|
2911
|
-
x: ptAxis(pts[0], "x"),
|
|
2912
|
-
y: ptAxis(pts[0], "y")
|
|
2913
|
-
});
|
|
2914
|
-
break;
|
|
2915
|
-
case "cubicBezTo":
|
|
2916
|
-
commands.push({
|
|
2917
|
-
cmd: "cubicBezTo",
|
|
2918
|
-
x1: ptAxis(pts[0], "x"),
|
|
2919
|
-
y1: ptAxis(pts[0], "y"),
|
|
2920
|
-
x2: ptAxis(pts[1], "x"),
|
|
2921
|
-
y2: ptAxis(pts[1], "y"),
|
|
2922
|
-
x: ptAxis(pts[2], "x"),
|
|
2923
|
-
y: ptAxis(pts[2], "y")
|
|
2924
|
-
});
|
|
2925
|
-
break;
|
|
2926
|
-
case "quadBezTo":
|
|
2927
|
-
commands.push({
|
|
2928
|
-
cmd: "quadBezTo",
|
|
2929
|
-
x1: ptAxis(pts[0], "x"),
|
|
2930
|
-
y1: ptAxis(pts[0], "y"),
|
|
2931
|
-
x: ptAxis(pts[1], "x"),
|
|
2932
|
-
y: ptAxis(pts[1], "y")
|
|
2933
|
-
});
|
|
2934
|
-
break;
|
|
2935
|
-
case "arcTo":
|
|
2936
|
-
commands.push({
|
|
2937
|
-
cmd: "arcTo",
|
|
2938
|
-
wR: intValue(attr(seg, "wR")) ?? 0,
|
|
2939
|
-
hR: intValue(attr(seg, "hR")) ?? 0,
|
|
2940
|
-
stAng: (intValue(attr(seg, "stAng")) ?? 0) / 6e4,
|
|
2941
|
-
swAng: (intValue(attr(seg, "swAng")) ?? 0) / 6e4
|
|
2942
|
-
});
|
|
2943
|
-
break;
|
|
2944
|
-
case "close": commands.push({ cmd: "close" });
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
return {
|
|
2948
|
-
w: intValue(attr(path, "w")) ?? 0,
|
|
2949
|
-
h: intValue(attr(path, "h")) ?? 0,
|
|
2950
|
-
fill: attr(path, "fill") ?? "norm",
|
|
2951
|
-
stroke: boolValue(attr(path, "stroke")) ?? true,
|
|
2952
|
-
commands
|
|
2953
|
-
};
|
|
2954
|
-
}
|
|
2955
|
-
/** Validate and round an EMU geometry value; extents (`cx`/`cy`) must be non-negative. */
|
|
2956
|
-
function toEmu(value, attribute, allowNegative) {
|
|
2957
|
-
if (!Number.isFinite(value)) throw new InvalidOptionError("coord/non-finite", `${attribute} must be a finite number of EMU, got ${value}`);
|
|
2958
|
-
if (!allowNegative && value < 0) throw new InvalidOptionError("coord/negative", `${attribute} must be non-negative, got ${value}`);
|
|
2959
|
-
return Math.round(value);
|
|
2960
|
-
}
|
|
2961
|
-
/** A point + extent pair (`a:off`/`a:ext` or `a:chOff`/`a:chExt`) from a transform, or `null` if either is incomplete. */
|
|
2962
|
-
function readBox(xfrm, offName, extName) {
|
|
2963
|
-
const off = firstChild(xfrm, offName);
|
|
2964
|
-
const ext = firstChild(xfrm, extName);
|
|
2965
|
-
const x = off && intValue(attr(off, "x"));
|
|
2966
|
-
const y = off && intValue(attr(off, "y"));
|
|
2967
|
-
const cx = ext && intValue(attr(ext, "cx"));
|
|
2968
|
-
const cy = ext && intValue(attr(ext, "cy"));
|
|
2969
|
-
if (x === null || y === null || cx === null || cy === null) return null;
|
|
2970
|
-
return {
|
|
2971
|
-
x,
|
|
2972
|
-
y,
|
|
2973
|
-
cx,
|
|
2974
|
-
cy
|
|
2975
|
-
};
|
|
2976
|
-
}
|
|
2977
|
-
function rotationDegrees(xfrm) {
|
|
2978
|
-
const rot = intValue(attr(xfrm, "rot"));
|
|
2979
|
-
return rot === null ? 0 : rot / 6e4;
|
|
2980
|
-
}
|
|
2981
|
-
function transformFlipH(xfrm) {
|
|
2982
|
-
return boolValue(attr(xfrm, "flipH")) === true;
|
|
2983
|
-
}
|
|
2984
|
-
function transformFlipV(xfrm) {
|
|
2985
|
-
return boolValue(attr(xfrm, "flipV")) === true;
|
|
2986
|
-
}
|
|
2987
|
-
//#endregion
|
|
2988
2629
|
//#region src/read/api/shapes/base.ts
|
|
2989
2630
|
/**
|
|
2990
2631
|
* The abstract base every shape kind extends.
|
|
@@ -3283,9 +2924,25 @@ var Shape = class {
|
|
|
3283
2924
|
});
|
|
3284
2925
|
}
|
|
3285
2926
|
/**
|
|
2927
|
+
* `true` when the shape sets an explicit no-fill (`spPr/a:noFill`) — a
|
|
2928
|
+
* deliberately transparent surface. The fill-side counterpart of
|
|
2929
|
+
* {@link lineNoFill}, and the only accessor that separates it from a shape
|
|
2930
|
+
* carrying no fill child at all (one inheriting through `p:style/a:fillRef`):
|
|
2931
|
+
* every other fill accessor — {@link fillColor}, {@link fillSchemeColor},
|
|
2932
|
+
* {@link resolvedFill}, {@link gradientFill}, {@link patternFill},
|
|
2933
|
+
* {@link pictureFill} — reports `null` for both. The two paint completely
|
|
2934
|
+
* differently, so a consumer that cannot tell them apart paints a transparent
|
|
2935
|
+
* shape in the theme's accent colour.
|
|
2936
|
+
*/
|
|
2937
|
+
get fillNoFill() {
|
|
2938
|
+
const props = this.properties();
|
|
2939
|
+
return !!(props && firstChild(props, "a:noFill"));
|
|
2940
|
+
}
|
|
2941
|
+
/**
|
|
3286
2942
|
* Set an explicit `<a:noFill/>` on the shape — a transparent surface. This is
|
|
3287
2943
|
* distinct from clearing the fill (`fillColor = null`), which removes the
|
|
3288
2944
|
* `a:solidFill` and lets the fill inherit from the shape's style/placeholder.
|
|
2945
|
+
* Read it back with {@link fillNoFill}.
|
|
3289
2946
|
*/
|
|
3290
2947
|
noFill() {
|
|
3291
2948
|
if (!this.supportsFill) throw new UnsupportedFeatureError("shape/fill-unsupported", `${this.shapeType} shapes do not support a solid fill`);
|
|
@@ -3343,6 +3000,32 @@ var Shape = class {
|
|
|
3343
3000
|
return !!(ln && firstChild(ln, "a:noFill"));
|
|
3344
3001
|
}
|
|
3345
3002
|
/**
|
|
3003
|
+
* The line end cap (`spPr/a:ln/@cap`) as the raw OOXML token — `'flat'`,
|
|
3004
|
+
* `'rnd'` (round) or `'sq'` (square) — or `null` when unset (PowerPoint's
|
|
3005
|
+
* default is `flat`). The write API authors this attribute through
|
|
3006
|
+
* `ShapeLineProps.cap`, so without the accessor a deck this library produced
|
|
3007
|
+
* could not be read back without losing it.
|
|
3008
|
+
*
|
|
3009
|
+
* Not cosmetic on a thick dashed rule: the cap decides whether each dash reads
|
|
3010
|
+
* as a rectangle or a lozenge, and it extends every dash by the stroke width.
|
|
3011
|
+
* SVG's `stroke-linecap` is the exact equivalent (`flat`→`butt`, `rnd`→`round`,
|
|
3012
|
+
* `sq`→`square`).
|
|
3013
|
+
*/
|
|
3014
|
+
get lineCap() {
|
|
3015
|
+
const ln = this.#line();
|
|
3016
|
+
return ln ? attr(ln, "cap") ?? null : null;
|
|
3017
|
+
}
|
|
3018
|
+
/**
|
|
3019
|
+
* The line alignment (`spPr/a:ln/@algn`) as the raw OOXML token — `'ctr'`
|
|
3020
|
+
* (centred on the shape's outline) or `'in'` (inset, drawn wholly inside it) —
|
|
3021
|
+
* or `null` when unset. It shifts a thick outline by half its width, so it
|
|
3022
|
+
* changes where the border sits relative to the fill.
|
|
3023
|
+
*/
|
|
3024
|
+
get lineAlign() {
|
|
3025
|
+
const ln = this.#line();
|
|
3026
|
+
return ln ? attr(ln, "algn") ?? null : null;
|
|
3027
|
+
}
|
|
3028
|
+
/**
|
|
3346
3029
|
* Preset geometry name (`spPr/a:prstGeom/@prst`, e.g. `rect`), or `null` for
|
|
3347
3030
|
* custom geometry or none. Not an auto-shape-only property: PowerPoint gives a
|
|
3348
3031
|
* picture and a connector a preset geometry too (a `p:pic` is `rect` unless it
|
|
@@ -3743,7 +3426,6 @@ var AutoShape = class extends Shape {
|
|
|
3743
3426
|
* blip plumbing (content-type → extension, the SVG extension blip, the recolour effects) lives
|
|
3744
3427
|
* here rather than on the base: no other shape kind carries it.
|
|
3745
3428
|
*/
|
|
3746
|
-
const IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
3747
3429
|
const ASVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
|
|
3748
3430
|
const PIC_AFTER_BLIPFILL = ["p:spPr", "p:style"];
|
|
3749
3431
|
const BLIPFILL_AFTER_BLIP = [
|
|
@@ -3986,7 +3668,7 @@ var Picture = class extends Shape {
|
|
|
3986
3668
|
const opc = this.slide.presentation.opc;
|
|
3987
3669
|
const mediaPartName = opc.reserveMediaPartName(extension);
|
|
3988
3670
|
opc.addPart(mediaPartName, contentType, bytes);
|
|
3989
|
-
const relId = this.slide.relationships.add(
|
|
3671
|
+
const relId = this.slide.relationships.add(IMAGE_REL, relativePartName(this.slide.partName, mediaPartName)).id;
|
|
3990
3672
|
setAttr(this.#getOrAddBlip(), "r:embed", relId);
|
|
3991
3673
|
if (options.fit) this.#applyFit(options.fit, bytes);
|
|
3992
3674
|
this.markDirty();
|
|
@@ -4115,7 +3797,7 @@ var Chart = class {
|
|
|
4115
3797
|
for (let node = plotArea.firstChild; node; node = node.nextSibling) {
|
|
4116
3798
|
if (node.nodeType !== 1) continue;
|
|
4117
3799
|
const element = node;
|
|
4118
|
-
if (element.namespaceURI === OOXML_NS.c &&
|
|
3800
|
+
if (element.namespaceURI === OOXML_NS.c && (element.localName ?? "").endsWith("Ax")) out.push(new ChartAxis(element, this.part));
|
|
4119
3801
|
}
|
|
4120
3802
|
return out;
|
|
4121
3803
|
}
|
|
@@ -4372,7 +4054,7 @@ var ChartSeries = class {
|
|
|
4372
4054
|
/** Cached numeric values (`c:val`); non-numeric or missing points are `null`. */
|
|
4373
4055
|
get values() {
|
|
4374
4056
|
const val = firstChild(this.ser, "c:val");
|
|
4375
|
-
return readPoints(val && findCache(val)).map(
|
|
4057
|
+
return readPoints(val && findCache(val)).map(intValue);
|
|
4376
4058
|
}
|
|
4377
4059
|
/** Cached category labels for this series (`c:cat`), as written. */
|
|
4378
4060
|
get categories() {
|
|
@@ -4460,11 +4142,6 @@ function readPoints(cache) {
|
|
|
4460
4142
|
}
|
|
4461
4143
|
return points;
|
|
4462
4144
|
}
|
|
4463
|
-
function toNumberOrNull$1(value) {
|
|
4464
|
-
if (value === null || value === "") return null;
|
|
4465
|
-
const number = Number(value);
|
|
4466
|
-
return Number.isFinite(number) ? number : null;
|
|
4467
|
-
}
|
|
4468
4145
|
//#endregion
|
|
4469
4146
|
//#region src/read/api/chartex.ts
|
|
4470
4147
|
/** A chartEx graphic-frame's chart, backed by its `cx:chartSpace` part. */
|
|
@@ -4608,7 +4285,7 @@ var ChartExSeries = class {
|
|
|
4608
4285
|
get values() {
|
|
4609
4286
|
const data = this.chart.dataElementById(this.dataId);
|
|
4610
4287
|
const numDim = data && firstChild(data, "cx:numDim");
|
|
4611
|
-
return readLevelPoints(numDim && firstChild(numDim, "cx:lvl")).map(
|
|
4288
|
+
return readLevelPoints(numDim && firstChild(numDim, "cx:lvl")).map(intValue);
|
|
4612
4289
|
}
|
|
4613
4290
|
/**
|
|
4614
4291
|
* Cached category labels (`cx:strDim`'s leaf `cx:lvl` — the first level, which
|
|
@@ -4726,56 +4403,30 @@ function floatValue(value) {
|
|
|
4726
4403
|
const number = Number(value);
|
|
4727
4404
|
return Number.isFinite(number) ? number : null;
|
|
4728
4405
|
}
|
|
4729
|
-
function toNumberOrNull(value) {
|
|
4730
|
-
if (value === null || value === "") return null;
|
|
4731
|
-
const number = Number(value);
|
|
4732
|
-
return Number.isFinite(number) ? number : null;
|
|
4733
|
-
}
|
|
4734
4406
|
//#endregion
|
|
4735
4407
|
//#region src/read/api/table-edit.ts
|
|
4736
4408
|
/**
|
|
4737
|
-
* Ordered mutation of a table cell's `a:tcPr`, and the schema facts every such
|
|
4409
|
+
* Ordered mutation of a table cell's `a:tcPr`, and the cell-scoped schema facts every such
|
|
4410
|
+
* edit needs.
|
|
4738
4411
|
*
|
|
4739
4412
|
* `CT_TableCellProperties` declares its children as a **sequence**, so an append-only
|
|
4740
4413
|
* implementation produces an out-of-order `a:tcPr` — which PowerPoint reports as a corrupt
|
|
4741
4414
|
* file rather than as a bad edit. Every insertion therefore goes through
|
|
4742
|
-
* {@link getOrAddChild} / {@link insertInOrder} with the new element's *successors*, which
|
|
4743
|
-
*
|
|
4744
|
-
*
|
|
4745
|
-
* producing an invalid part.
|
|
4415
|
+
* {@link getOrAddChild} / {@link insertInOrder} with the new element's *successors*, which is
|
|
4416
|
+
* what `TCPR_AFTER` tabulates; it and the sequence it derives from live in
|
|
4417
|
+
* `src/ooxml/sequence.ts` alongside the other complexType orderings.
|
|
4746
4418
|
*
|
|
4747
4419
|
* The attribute values are the other half. A value outside its `ST_` union is equally fatal
|
|
4748
4420
|
* and equally silent, so each is checked against the enum before it is written — the same
|
|
4749
4421
|
* validate-at-the-boundary rule the write path follows, with the difference that here there
|
|
4750
|
-
* is a caller to throw at rather than a deck to degrade.
|
|
4751
|
-
*/
|
|
4752
|
-
/**
|
|
4753
|
-
* `CT_TableCellProperties`' child sequence, in declaration order.
|
|
4754
|
-
* @see ECMA-376 Part 1 §21.1.3.17 (`a:tcPr`)
|
|
4755
|
-
*/
|
|
4756
|
-
const TCPR_SEQUENCE = [
|
|
4757
|
-
"a:lnL",
|
|
4758
|
-
"a:lnR",
|
|
4759
|
-
"a:lnT",
|
|
4760
|
-
"a:lnB",
|
|
4761
|
-
"a:lnTlToBr",
|
|
4762
|
-
"a:lnBlToTr",
|
|
4763
|
-
"a:cell3D",
|
|
4764
|
-
"a:noFill",
|
|
4765
|
-
"a:solidFill",
|
|
4766
|
-
"a:gradFill",
|
|
4767
|
-
"a:blipFill",
|
|
4768
|
-
"a:pattFill",
|
|
4769
|
-
"a:grpFill",
|
|
4770
|
-
"a:headers",
|
|
4771
|
-
"a:extLst"
|
|
4772
|
-
];
|
|
4773
|
-
/**
|
|
4774
|
-
* For each `a:tcPr` child, the children that must follow it — i.e. what a new one is
|
|
4775
|
-
* inserted *before*. Derived from {@link TCPR_SEQUENCE} rather than written out, so the two
|
|
4776
|
-
* cannot disagree and adding a child to the sequence updates every successor list at once.
|
|
4422
|
+
* is a caller to throw at rather than a deck to degrade (see `ooxml/check-enum.ts`).
|
|
4777
4423
|
*/
|
|
4778
|
-
|
|
4424
|
+
/** `ST_TextAnchoringType` — `a:tcPr/@anchor`. */
|
|
4425
|
+
const ANCHOR_VALUES = TEXT_ANCHORS;
|
|
4426
|
+
/** `ST_TextVerticalType` — `a:tcPr/@vert`. */
|
|
4427
|
+
const VERT_VALUES = TEXT_VERTICAL;
|
|
4428
|
+
/** `ST_TextHorzOverflowType` — `a:tcPr/@horzOverflow`. */
|
|
4429
|
+
const HORZ_OVERFLOW_VALUES = TEXT_HORZ_OVERFLOW;
|
|
4779
4430
|
/** The four edges plus the two diagonals, keyed as {@link TableCellEdge} names them. */
|
|
4780
4431
|
const EDGE_QNAMES = {
|
|
4781
4432
|
left: "a:lnL",
|
|
@@ -4785,45 +4436,6 @@ const EDGE_QNAMES = {
|
|
|
4785
4436
|
tlToBr: "a:lnTlToBr",
|
|
4786
4437
|
blToTr: "a:lnBlToTr"
|
|
4787
4438
|
};
|
|
4788
|
-
/** `ST_TextAnchoringType` — `a:tcPr/@anchor`. */
|
|
4789
|
-
const ANCHOR_VALUES = [
|
|
4790
|
-
"t",
|
|
4791
|
-
"ctr",
|
|
4792
|
-
"b",
|
|
4793
|
-
"just",
|
|
4794
|
-
"dist"
|
|
4795
|
-
];
|
|
4796
|
-
/** `ST_TextVerticalType` — `a:tcPr/@vert`. */
|
|
4797
|
-
const VERT_VALUES = [
|
|
4798
|
-
"horz",
|
|
4799
|
-
"vert",
|
|
4800
|
-
"vert270",
|
|
4801
|
-
"wordArtVert",
|
|
4802
|
-
"eaVert",
|
|
4803
|
-
"mongolianVert",
|
|
4804
|
-
"wordArtVertRtl"
|
|
4805
|
-
];
|
|
4806
|
-
/** `ST_TextHorzOverflowType` — `a:tcPr/@horzOverflow`. */
|
|
4807
|
-
const HORZ_OVERFLOW_VALUES = ["clip", "overflow"];
|
|
4808
|
-
/**
|
|
4809
|
-
* Check a value against its `ST_` union, throwing when it is outside.
|
|
4810
|
-
*
|
|
4811
|
-
* Throwing rather than warn-and-drop, which is the opposite of what the *write* path does
|
|
4812
|
-
* with the same enums — and deliberately so. On the write path an option comes from a deck
|
|
4813
|
-
* being built, and dropping one value is better than failing a whole build; here it comes
|
|
4814
|
-
* from a caller editing one attribute, so silently doing nothing would leave them looking at
|
|
4815
|
-
* an unchanged deck with no explanation.
|
|
4816
|
-
*
|
|
4817
|
-
* @param {string} value - the value the caller asked for
|
|
4818
|
-
* @param {readonly string[]} valid - the enum's members
|
|
4819
|
-
* @param {string} attribute - the attribute's name, for the message
|
|
4820
|
-
* @param {InvalidOptionErrorCode} code - the condition to report
|
|
4821
|
-
* @returns {string} `value`, when it is legal
|
|
4822
|
-
*/
|
|
4823
|
-
function checkEnum(value, valid, attribute, code) {
|
|
4824
|
-
if (valid.includes(value)) return value;
|
|
4825
|
-
throw new InvalidOptionError(code, `Invalid ${attribute}: ${JSON.stringify(value)}. Expected one of: ${valid.join(", ")}.`);
|
|
4826
|
-
}
|
|
4827
4439
|
/**
|
|
4828
4440
|
* Get or create a child of `a:tcPr` at its schema position.
|
|
4829
4441
|
* @param {Element} tcPr - the cell's properties element
|
|
@@ -5756,7 +5368,7 @@ var TableCell = class {
|
|
|
5756
5368
|
this.part.markDirty();
|
|
5757
5369
|
return;
|
|
5758
5370
|
}
|
|
5759
|
-
const checked =
|
|
5371
|
+
const checked = checkEnumOrThrow(value, valid, name, code);
|
|
5760
5372
|
setAttr(this.#getOrAddTcPr(), name, checked);
|
|
5761
5373
|
this.part.markDirty();
|
|
5762
5374
|
}
|
|
@@ -5813,6 +5425,27 @@ var TableCell = class {
|
|
|
5813
5425
|
return !!tcPr && FILL_CHOICES.some((q) => firstChild(tcPr, q));
|
|
5814
5426
|
}
|
|
5815
5427
|
/**
|
|
5428
|
+
* `true` when the cell sets an explicit no-fill (`a:tcPr/a:noFill`) — a deliberately
|
|
5429
|
+
* transparent cell showing the table background (or the slide) through. The cell-side
|
|
5430
|
+
* counterpart of {@link import('./shapes.js').AutoShape.fillNoFill}, and what
|
|
5431
|
+
* {@link noFill} writes.
|
|
5432
|
+
*
|
|
5433
|
+
* {@link hasOwnFill} is not this question: it is `true` for *any* `EG_FillProperties`
|
|
5434
|
+
* child, so on its own it cannot separate a suppressed fill from a gradient or an image
|
|
5435
|
+
* one — and every colour accessor ({@link resolvedFill}, {@link fillColor},
|
|
5436
|
+
* {@link fillSchemeColor}) reports `null` for a no-fill cell exactly as it does for a
|
|
5437
|
+
* cell that inherits its shading from the table style. Deriving it as "has a fill of its
|
|
5438
|
+
* own, and no accessor recognises it" instead of reading it has two failure modes: it
|
|
5439
|
+
* folds `a:grpFill` in with `a:noFill`, and its meaning changes silently the day a
|
|
5440
|
+
* further fill kind gets an accessor. The two paint completely differently — an
|
|
5441
|
+
* inherited banding colour versus nothing at all — so a consumer that cannot tell them
|
|
5442
|
+
* apart paints a transparent cell in the style's shading.
|
|
5443
|
+
*/
|
|
5444
|
+
get fillNoFill() {
|
|
5445
|
+
const tcPr = this.#tcPr();
|
|
5446
|
+
return !!tcPr && !!firstChild(tcPr, "a:noFill");
|
|
5447
|
+
}
|
|
5448
|
+
/**
|
|
5816
5449
|
* The cell's picture (image) fill (`a:tcPr/a:blipFill`), or `null` when the cell
|
|
5817
5450
|
* is not image-filled. The cell counterpart of
|
|
5818
5451
|
* {@link import('./shapes.js').AutoShape.pictureFill}: {@link resolvedFill}
|
|
@@ -6168,6 +5801,30 @@ var GroupShape = class extends Shape {
|
|
|
6168
5801
|
"p:graphicFrame"
|
|
6169
5802
|
]);
|
|
6170
5803
|
}
|
|
5804
|
+
/**
|
|
5805
|
+
* The group's own child coordinate space (`p:grpSpPr/a:xfrm/a:chOff` and
|
|
5806
|
+
* `a:chExt`) in EMU, or `null` when the group has no transform or either
|
|
5807
|
+
* element is incomplete.
|
|
5808
|
+
*
|
|
5809
|
+
* Most consumers should not need this: {@link Shape.absoluteFrame} already
|
|
5810
|
+
* composes the whole ancestor chain — child space, group flips and rotations —
|
|
5811
|
+
* and doing that arithmetic per consumer is how a shape in a nested group ends
|
|
5812
|
+
* up subtly displaced. It is here for a *replica* consumer rather than a
|
|
5813
|
+
* *paint* one: rebuilding this group and its children as OOXML needs the
|
|
5814
|
+
* source child space to reproduce the scaling, and without it only groups whose
|
|
5815
|
+
* child space is the identity can be rebuilt.
|
|
5816
|
+
*/
|
|
5817
|
+
get childFrame() {
|
|
5818
|
+
const xfrm = this.xfrm();
|
|
5819
|
+
const box = xfrm && readBox(xfrm, "a:chOff", "a:chExt");
|
|
5820
|
+
if (!box) return null;
|
|
5821
|
+
return {
|
|
5822
|
+
offsetX: box.x,
|
|
5823
|
+
offsetY: box.y,
|
|
5824
|
+
extentX: box.cx,
|
|
5825
|
+
extentY: box.cy
|
|
5826
|
+
};
|
|
5827
|
+
}
|
|
6171
5828
|
/** The shapes nested directly inside this group, in document order. */
|
|
6172
5829
|
get shapes() {
|
|
6173
5830
|
return buildShapes(this.element, this.slide);
|
|
@@ -6248,6 +5905,6 @@ function buildShapes(parent, slide) {
|
|
|
6248
5905
|
return shapes;
|
|
6249
5906
|
}
|
|
6250
5907
|
//#endregion
|
|
6251
|
-
export {
|
|
5908
|
+
export { resolveColor as $, resolveColorElement as A, getOrAddChild as At, placeholderInheritedColor as B, Paragraph as C, childElements as Ct, readPatternFill as D, firstChild as Dt, spPrXfrmEmu as E, escapeXmlAttribute as Et, resolveSlideThemeParts as F, removeAttr as Ft, slideDefinesColor as G, placeholderInheritedRunProps as H, applyColorTransforms as I, removeChildrenByQName as It, fmtEntry as J, slideDefinesProp as K, LST_STYLE_LEVELS as L, replaceInParent as Lt, resolveMasterColorContext as M, intValue as Mt, resolveNotesColorContext as N, ownerDocumentOf as Nt, readPictureFill as O, firstChildElement as Ot, resolveSlideColorContext as P, parseXml as Pt, parseClrScheme as Q, RUN_PROP_NAMES as R, serializeXml as Rt, Shape as S, boolValue as St, TextFrame as T, descendantsByTag as Tt, placeholderInheritedXfrm as U, placeholderInheritedListStyles as V, placeholderOf as W, isA as X, intAttr as Y, parseClrMap as Z, ChartAxis as _, resolveRelativePartName as _t, isGraphicFrame as a, RPR_FILL_AFTER as at, Picture as b, OOXML_NS as bt, wrapShapeElement as c, SPPR_EFFECT_AFTER as ct, TableCell as d, SPPR_SCENE3D_AFTER as dt, resolveSchemeToken as et, TableRow as f, SPPR_SP3D_AFTER as ft, Chart as g, relsPartNameFor as gt, ChartExSeries as h, relativePartName as ht, isConnector as i, FILL_CHOICES as it, resolveLayoutColorContext as j, insertInOrder as jt, readGradientFill as k, getElements as kt, GraphicFrame as l, SPPR_FILL_AFTER as lt, ChartExAxis as m, partNameToZipPath as mt, buildShapes as n, styleRefLine as nt, isGroupShape as o, SHAPE_AFTER_SPPR as ot, ChartEx as p, partNameExtension as pt, SCHEME_SLOTS as q, isAutoShape as r, substitutePhClr as rt, isPicture as s, SPPR_AFTER_XFRM as st, GroupShape as t, styleRefFill as tt, Table as u, SPPR_LN_AFTER as ut, ChartSeries as v, resolveSingleRel as vt, Run as w, createElement as wt, AutoShape as x, attr as xt, Connector as y, zipPathToPartName as yt, placeholderInheritedAnchor as z, setAttr as zt };
|
|
6252
5909
|
|
|
6253
|
-
//# sourceMappingURL=shapes-
|
|
5910
|
+
//# sourceMappingURL=shapes-DMThLsZN.js.map
|