@shbernal/ts-pptx 1.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/{autopage-tmfjRzL4.js → autopage-MxulehqQ.js} +3 -4
- package/dist/{autopage-tmfjRzL4.js.map → autopage-MxulehqQ.js.map} +1 -1
- package/dist/browser.d.ts +14 -6
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +12 -4
- package/dist/browser.js.map +1 -1
- package/dist/{diagnostics-DzY9Tyfm.d.ts → diagnostics-BqBTmA3a.d.ts} +95 -23
- package/dist/diagnostics-BqBTmA3a.d.ts.map +1 -0
- package/dist/{enums-D3aF4QGb.d.ts → enums-oLLeaSW3.d.ts} +2 -1
- package/dist/{enums-D3aF4QGb.d.ts.map → enums-oLLeaSW3.d.ts.map} +1 -1
- package/dist/{errors-DRLpOpgQ.d.ts → errors-BENA-Xuw.d.ts} +3 -3
- package/dist/{errors-DRLpOpgQ.d.ts.map → errors-BENA-Xuw.d.ts.map} +1 -1
- package/dist/errors-DGE9zh3U.js.map +1 -1
- package/dist/{fit-CbxyHN69.js → fit-DGSwMMNs.js} +0 -0
- package/dist/{fit-CbxyHN69.js.map → fit-DGSwMMNs.js.map} +1 -1
- package/dist/{html-dom-wVa3TiG7.js → html-dom--J-Xuucf.js} +19 -5
- package/dist/html-dom--J-Xuucf.js.map +1 -0
- package/dist/html.d.ts +3 -3
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +6 -3
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +6 -6
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +1 -1
- package/dist/inspect.js +2 -2
- package/dist/inspect.js.map +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/math.js +2 -1
- package/dist/math.js.map +1 -1
- package/dist/measure.d.ts +2 -2
- package/dist/measure.js +1 -1
- package/dist/node.d.ts +14 -6
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +11 -3
- package/dist/node.js.map +1 -1
- package/dist/{pres-5x1bjCzk.d.ts → pres-2WsBGimU.d.ts} +3 -3
- package/dist/{pres-5x1bjCzk.d.ts.map → pres-2WsBGimU.d.ts.map} +1 -1
- package/dist/{presentation-RGOiun5P.d.ts → presentation-CTVPpiwP.d.ts} +912 -485
- package/dist/presentation-CTVPpiwP.d.ts.map +1 -0
- package/dist/{presentation-B0JWdTN1.js → presentation-DAcOfzsP.js} +880 -366
- package/dist/presentation-DAcOfzsP.js.map +1 -0
- package/dist/read.d.ts +4 -4
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +2 -2
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +0 -0
- package/dist/script.js.map +1 -1
- package/dist/{shapes-BZU8UTW2.js → shapes-BWifcHDV.js} +1520 -996
- package/dist/shapes-BWifcHDV.js.map +1 -0
- package/dist/{slide-B6mDaM-P.d.ts → slide-YuSCGa45.d.ts} +409 -103
- package/dist/slide-YuSCGa45.d.ts.map +1 -0
- package/dist/{image-size-C5Z1sKof.js → st-enums-CgadEyPM.js} +235 -4
- package/dist/st-enums-CgadEyPM.js.map +1 -0
- package/dist/{text-WRUbOy2r.js → text-120VFSDm.js} +1027 -369
- package/dist/text-120VFSDm.js.map +1 -0
- package/dist/{units-internal-7JcMfYti.js → units-internal-CiFWZWk4.js} +2 -1
- package/dist/units-internal-CiFWZWk4.js.map +1 -0
- package/dist/zip.d.ts +2 -2
- package/dist/zip.js.map +1 -1
- package/package.json +38 -36
- package/dist/diagnostics-DzY9Tyfm.d.ts.map +0 -1
- package/dist/html-dom-wVa3TiG7.js.map +0 -1
- package/dist/image-size-C5Z1sKof.js.map +0 -1
- package/dist/presentation-B0JWdTN1.js.map +0 -1
- package/dist/presentation-RGOiun5P.d.ts.map +0 -1
- package/dist/shapes-BZU8UTW2.js.map +0 -1
- package/dist/slide-B6mDaM-P.d.ts.map +0 -1
- package/dist/text-WRUbOy2r.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
2
|
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-DGE9zh3U.js";
|
|
3
|
-
import {
|
|
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-CgadEyPM.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
|
-
"p:extLst"
|
|
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;
|
|
645
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;
|
|
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,337 +823,80 @@ function lstStyleLevelFill(listStyle, level) {
|
|
|
827
823
|
return defRPr ? firstChild(defRPr, "a:solidFill") : null;
|
|
828
824
|
}
|
|
829
825
|
/**
|
|
830
|
-
* The
|
|
831
|
-
*
|
|
832
|
-
*
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
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.
|
|
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`.
|
|
829
|
+
*
|
|
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.
|
|
868
832
|
*/
|
|
869
|
-
function
|
|
833
|
+
function styleTiers(type, idx, ctx) {
|
|
870
834
|
const tiers = [];
|
|
871
835
|
if (ctx.layoutRoot) {
|
|
872
836
|
const layoutPh = findPlaceholder(ctx.layoutRoot, type, idx);
|
|
873
|
-
tiers.push(layoutPh &&
|
|
837
|
+
tiers.push(layoutPh && placeholderLstStyle(layoutPh));
|
|
874
838
|
}
|
|
875
839
|
if (ctx.masterRoot) {
|
|
876
840
|
const masterPh = findPlaceholder(ctx.masterRoot, type, idx);
|
|
877
|
-
tiers.push(masterPh &&
|
|
841
|
+
tiers.push(masterPh && placeholderLstStyle(masterPh));
|
|
878
842
|
const txStyles = firstChild(ctx.masterRoot, "p:txStyles");
|
|
879
|
-
|
|
880
|
-
tiers.push(styleEl && lstStyleLevelDefRPr(styleEl, level));
|
|
843
|
+
tiers.push(txStyles && firstChild(txStyles, TX_STYLE_NAME[phCategory(type)]));
|
|
881
844
|
}
|
|
882
|
-
if (ctx.notesStyle) tiers.push(
|
|
845
|
+
if (ctx.notesStyle) tiers.push(ctx.notesStyle);
|
|
883
846
|
return tiers.filter((t) => t !== null);
|
|
884
847
|
}
|
|
885
848
|
/**
|
|
886
|
-
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
*
|
|
890
|
-
*
|
|
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}.
|
|
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}.
|
|
961
854
|
*/
|
|
962
|
-
function
|
|
963
|
-
for (const
|
|
964
|
-
|
|
965
|
-
const
|
|
966
|
-
|
|
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;
|
|
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;
|
|
990
860
|
}
|
|
991
861
|
return null;
|
|
992
862
|
}
|
|
993
863
|
/**
|
|
994
|
-
*
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
*
|
|
998
|
-
*
|
|
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.
|
|
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.
|
|
1004
869
|
*/
|
|
1005
|
-
function
|
|
1006
|
-
|
|
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`.
|
|
1043
|
-
*
|
|
1044
|
-
* Resolution is per *level*, most specific tier wins whole: the slide's own level
|
|
1045
|
-
* (if it defines one) is kept verbatim, else the first source tier that defines it
|
|
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.
|
|
1052
|
-
*/
|
|
1053
|
-
function resolvePlaceholderListStyle(shapeRoot, ctx) {
|
|
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) {
|
|
1083
|
-
const tiers = [];
|
|
1084
|
-
if (ctx.layoutRoot) {
|
|
1085
|
-
const layoutPh = findPlaceholder(ctx.layoutRoot, type, idx);
|
|
1086
|
-
tiers.push(layoutPh && placeholderLstStyle(layoutPh));
|
|
1087
|
-
}
|
|
1088
|
-
if (ctx.masterRoot) {
|
|
1089
|
-
const masterPh = findPlaceholder(ctx.masterRoot, type, idx);
|
|
1090
|
-
tiers.push(masterPh && placeholderLstStyle(masterPh));
|
|
1091
|
-
const txStyles = firstChild(ctx.masterRoot, "p:txStyles");
|
|
1092
|
-
tiers.push(txStyles && firstChild(txStyles, TX_STYLE_NAME[phCategory(type)]));
|
|
1093
|
-
}
|
|
1094
|
-
return tiers.filter((t) => t !== null);
|
|
870
|
+
function placeholderInheritedDefRPrs(type, idx, level, ctx) {
|
|
871
|
+
return styleTiers(type, idx, ctx).map((tier) => lstStyleLevelDefRPr(tier, level)).filter((t) => t !== null);
|
|
1095
872
|
}
|
|
1096
873
|
/**
|
|
1097
|
-
*
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
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.
|
|
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.
|
|
1106
881
|
*/
|
|
1107
|
-
function
|
|
1108
|
-
|
|
1109
|
-
const nvSpPr = firstChild(sp, "p:nvSpPr");
|
|
1110
|
-
const nvPr = nvSpPr && firstChild(nvSpPr, "p:nvPr");
|
|
1111
|
-
if (nvPr) removeChildrenByQName(nvPr, ["p:ph"]);
|
|
1112
|
-
}
|
|
882
|
+
function placeholderInheritedListStyles(type, idx, ctx) {
|
|
883
|
+
return styleTiers(type, idx, ctx);
|
|
1113
884
|
}
|
|
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
885
|
/**
|
|
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`.
|
|
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).
|
|
1128
891
|
*/
|
|
1129
|
-
function
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
const ph = placeholderOf(sp);
|
|
1133
|
-
if (!ph) continue;
|
|
1134
|
-
const txBody = firstChild(sp, "p:txBody");
|
|
1135
|
-
if (!txBody) continue;
|
|
1136
|
-
const type = attr(ph, "type");
|
|
1137
|
-
const idx = attr(ph, "idx") ?? "0";
|
|
1138
|
-
const slideLst = firstChild(txBody, "a:lstStyle");
|
|
1139
|
-
const byLevel = /* @__PURE__ */ new Map();
|
|
1140
|
-
for (const p of getElements(txBody, "a:p")) {
|
|
1141
|
-
const pPr = firstChild(p, "a:pPr");
|
|
1142
|
-
const level = (pPr && intValue(attr(pPr, "lvl"))) ?? 0;
|
|
1143
|
-
const runs = [...getElements(p, "a:r"), ...getElements(p, "a:fld")];
|
|
1144
|
-
if (runs.length === 0) continue;
|
|
1145
|
-
let props = byLevel.get(level);
|
|
1146
|
-
if (props === void 0) {
|
|
1147
|
-
props = placeholderInheritedRunProps(type, idx, level, ctx);
|
|
1148
|
-
byLevel.set(level, props);
|
|
1149
|
-
}
|
|
1150
|
-
if (!props) continue;
|
|
1151
|
-
for (const run of runs) writeRunProps(run, props, pPr, slideLst, level);
|
|
1152
|
-
}
|
|
1153
|
-
}
|
|
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
|
|
@@ -1484,7 +1142,6 @@ function applyColorTransforms(baseHex, transforms) {
|
|
|
1484
1142
|
if (f !== null) alpha = clamp01((alpha ?? 1) + f);
|
|
1485
1143
|
break;
|
|
1486
1144
|
}
|
|
1487
|
-
default: break;
|
|
1488
1145
|
}
|
|
1489
1146
|
return alpha === void 0 ? { hex: toHex(rgb) } : {
|
|
1490
1147
|
hex: toHex(rgb),
|
|
@@ -1504,17 +1161,6 @@ function applyColorTransforms(baseHex, transforms) {
|
|
|
1504
1161
|
* background needs on top of {@link resolveSlideThemeParts} — so a token resolves
|
|
1505
1162
|
* identically whether it is read or baked.
|
|
1506
1163
|
*/
|
|
1507
|
-
const SLIDE_LAYOUT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
1508
|
-
const SLIDE_MASTER_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
1509
|
-
const NOTES_MASTER_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster";
|
|
1510
|
-
const THEME_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
1511
|
-
const OFFICE_DOCUMENT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
1512
|
-
/** Resolve the single relationship of `type` owned by `partName`, or `null`. */
|
|
1513
|
-
function resolveSingleRel(opc, partName, type) {
|
|
1514
|
-
const rels = opc.relationshipsFor(partName);
|
|
1515
|
-
const rel = rels.byType(type)[0];
|
|
1516
|
-
return rel ? rels.resolveTarget(rel.id) : null;
|
|
1517
|
-
}
|
|
1518
1164
|
/** The document element of a part, or `null` when the partname/part is absent. */
|
|
1519
1165
|
function documentElement(opc, partName) {
|
|
1520
1166
|
return partName ? opc.part(partName)?.dom.documentElement ?? null : null;
|
|
@@ -1523,7 +1169,7 @@ function documentElement(opc, partName) {
|
|
|
1523
1169
|
* The presentation's `p:defaultTextStyle` (`presentation.xml`, reached via the
|
|
1524
1170
|
* package `officeDocument` relationship), or `null` when absent. This is
|
|
1525
1171
|
* PowerPoint's lowest-priority text fallback — the bottom tier of a slide run's
|
|
1526
|
-
* size/colour/face/bold resolution (see `
|
|
1172
|
+
* size/colour/face/bold resolution (see `ThemeContext.defaultTextStyle`).
|
|
1527
1173
|
*/
|
|
1528
1174
|
function presentationDefaultTextStyle(opc) {
|
|
1529
1175
|
const root = documentElement(opc, resolveSingleRel(opc, "/", OFFICE_DOCUMENT_REL));
|
|
@@ -1999,112 +1645,203 @@ function readPictureFill(container, rels = null) {
|
|
|
1999
1645
|
const fillRect = stretch && firstChild(stretch, "a:fillRect");
|
|
2000
1646
|
const tile = firstChild(blipFill, "a:tile");
|
|
2001
1647
|
return {
|
|
2002
|
-
relId,
|
|
2003
|
-
partName: resolvePartName(relId, rels),
|
|
2004
|
-
mode: stretch ? "stretch" : tile ? "tile" : null,
|
|
2005
|
-
srcRect: srcRect ? readRect(srcRect) : null,
|
|
2006
|
-
fillRect: fillRect ? readRect(fillRect) : null,
|
|
2007
|
-
tile: tile ? readTile(tile) : null,
|
|
2008
|
-
alpha: alphaMod ? amt === null ? 1 : amt / 1e5 : null,
|
|
2009
|
-
dpi: intValue(attr(blipFill, "dpi")),
|
|
2010
|
-
rotWithShape: boolValue(attr(blipFill, "rotWithShape"))
|
|
1648
|
+
relId,
|
|
1649
|
+
partName: resolvePartName(relId, rels),
|
|
1650
|
+
mode: stretch ? "stretch" : tile ? "tile" : null,
|
|
1651
|
+
srcRect: srcRect ? readRect(srcRect) : null,
|
|
1652
|
+
fillRect: fillRect ? readRect(fillRect) : null,
|
|
1653
|
+
tile: tile ? readTile(tile) : null,
|
|
1654
|
+
alpha: alphaMod ? amt === null ? 1 : amt / 1e5 : null,
|
|
1655
|
+
dpi: intValue(attr(blipFill, "dpi")),
|
|
1656
|
+
rotWithShape: boolValue(attr(blipFill, "rotWithShape"))
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
//#endregion
|
|
1660
|
+
//#region src/read/api/pattern-fill.ts
|
|
1661
|
+
/**
|
|
1662
|
+
* Shared pattern-fill reader for DrawingML colour-bearing containers: a shape's `p:spPr`, a
|
|
1663
|
+
* slide's `p:bg/p:bgPr`, and a table's `a:tblPr` / cell's `a:tcPr`.
|
|
1664
|
+
*
|
|
1665
|
+
* `a:pattFill` is a preset hatch name plus two wrapped colours, and decoding it is three
|
|
1666
|
+
* lines — which is exactly why it had been open-coded in each place that needed it. Sharing
|
|
1667
|
+
* it is what keeps the three from drifting into different answers for the same element, the
|
|
1668
|
+
* same reason {@link import('./gradient.js').readGradientFill} and
|
|
1669
|
+
* {@link import('./picture-fill.js').readPictureFill} are shared.
|
|
1670
|
+
*/
|
|
1671
|
+
/**
|
|
1672
|
+
* Read a container's `a:pattFill`, or `null` when its fill is not a pattern.
|
|
1673
|
+
* @param {Element} container - the fill-bearing parent (`p:spPr`, `p:bgPr`, `a:tblPr`, `a:tcPr`, …)
|
|
1674
|
+
* @param {ColorContext} ctx - the theme colour context each colour resolves against
|
|
1675
|
+
* @returns {PatternFill | null} the decoded hatch, or `null`
|
|
1676
|
+
*/
|
|
1677
|
+
function readPatternFill(container, ctx) {
|
|
1678
|
+
const patt = firstChild(container, "a:pattFill");
|
|
1679
|
+
if (!patt) return null;
|
|
1680
|
+
const wrapColor = (qname) => {
|
|
1681
|
+
const wrap = firstChild(patt, qname);
|
|
1682
|
+
const colorEl = wrap && firstChildElement(wrap);
|
|
1683
|
+
return colorEl ? resolveColorElement(colorEl, ctx) : null;
|
|
1684
|
+
};
|
|
1685
|
+
return {
|
|
1686
|
+
preset: attr(patt, "prst") ?? null,
|
|
1687
|
+
foreground: wrapColor("a:fgClr"),
|
|
1688
|
+
background: wrapColor("a:bgClr")
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
//#endregion
|
|
1692
|
+
//#region src/read/api/shapes/oxml.ts
|
|
1693
|
+
/**
|
|
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.
|
|
1696
|
+
*
|
|
1697
|
+
* The successor arrays are the reason a setter can create a missing `a:xfrm` / `a:solidFill` /
|
|
1698
|
+
* `a:ln` without corrupting the part: `getOrAddChild` inserts before the first listed sibling it
|
|
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.
|
|
1702
|
+
*/
|
|
1703
|
+
/** First `<p:cNvPr>` reached through the shape's non-visual properties wrapper (`p:nv*Pr`). */
|
|
1704
|
+
function nonVisualCNvPr(element) {
|
|
1705
|
+
for (let node = element.firstChild; node; node = node.nextSibling) {
|
|
1706
|
+
if (node.nodeType !== 1) continue;
|
|
1707
|
+
const child = node;
|
|
1708
|
+
if (child.namespaceURI === OOXML_NS.p && child.localName?.startsWith("nv")) return firstChild(child, "p:cNvPr");
|
|
1709
|
+
}
|
|
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
|
|
2011
1809
|
};
|
|
2012
1810
|
}
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
*
|
|
2019
|
-
* The successor arrays are the reason a setter can create a missing `a:xfrm` / `a:solidFill` /
|
|
2020
|
-
* `a:ln` without corrupting the part: `getOrAddChild` inserts before the first listed sibling it
|
|
2021
|
-
* finds, so each list must name exactly the children that legally *follow* the one being added.
|
|
2022
|
-
*/
|
|
2023
|
-
const SPPR_AFTER_XFRM = [
|
|
2024
|
-
"a:custGeom",
|
|
2025
|
-
"a:prstGeom",
|
|
2026
|
-
"a:noFill",
|
|
2027
|
-
"a:solidFill",
|
|
2028
|
-
"a:gradFill",
|
|
2029
|
-
"a:blipFill",
|
|
2030
|
-
"a:pattFill",
|
|
2031
|
-
"a:grpFill",
|
|
2032
|
-
"a:ln",
|
|
2033
|
-
"a:effectLst",
|
|
2034
|
-
"a:effectDag",
|
|
2035
|
-
"a:scene3d",
|
|
2036
|
-
"a:sp3d",
|
|
2037
|
-
"a:extLst"
|
|
2038
|
-
];
|
|
2039
|
-
const GRPSPPR_AFTER_XFRM = [
|
|
2040
|
-
"a:noFill",
|
|
2041
|
-
"a:solidFill",
|
|
2042
|
-
"a:gradFill",
|
|
2043
|
-
"a:blipFill",
|
|
2044
|
-
"a:pattFill",
|
|
2045
|
-
"a:grpFill",
|
|
2046
|
-
"a:effectLst",
|
|
2047
|
-
"a:effectDag",
|
|
2048
|
-
"a:scene3d",
|
|
2049
|
-
"a:extLst"
|
|
2050
|
-
];
|
|
2051
|
-
const SHAPE_AFTER_SPPR = ["p:style", "p:txBody"];
|
|
2052
|
-
const SPPR_FILL_AFTER = [
|
|
2053
|
-
"a:ln",
|
|
2054
|
-
"a:effectLst",
|
|
2055
|
-
"a:effectDag",
|
|
2056
|
-
"a:scene3d",
|
|
2057
|
-
"a:sp3d",
|
|
2058
|
-
"a:extLst"
|
|
2059
|
-
];
|
|
2060
|
-
const SPPR_LN_AFTER = [
|
|
2061
|
-
"a:effectLst",
|
|
2062
|
-
"a:effectDag",
|
|
2063
|
-
"a:scene3d",
|
|
2064
|
-
"a:sp3d",
|
|
2065
|
-
"a:extLst"
|
|
2066
|
-
];
|
|
2067
|
-
const GRPSPPR_FILL_AFTER = [
|
|
2068
|
-
"a:effectLst",
|
|
2069
|
-
"a:effectDag",
|
|
2070
|
-
"a:scene3d",
|
|
2071
|
-
"a:extLst"
|
|
2072
|
-
];
|
|
2073
|
-
const LN_FILL_AFTER = [
|
|
2074
|
-
"a:prstDash",
|
|
2075
|
-
"a:custDash",
|
|
2076
|
-
"a:round",
|
|
2077
|
-
"a:bevel",
|
|
2078
|
-
"a:miter",
|
|
2079
|
-
"a:headEnd",
|
|
2080
|
-
"a:tailEnd",
|
|
2081
|
-
"a:extLst"
|
|
2082
|
-
];
|
|
2083
|
-
/** First `<p:cNvPr>` reached through the shape's non-visual properties wrapper (`p:nv*Pr`). */
|
|
2084
|
-
function nonVisualCNvPr(element) {
|
|
2085
|
-
for (let node = element.firstChild; node; node = node.nextSibling) {
|
|
2086
|
-
if (node.nodeType !== 1) continue;
|
|
2087
|
-
const child = node;
|
|
2088
|
-
if (child.namespaceURI === OOXML_NS.p && child.localName?.startsWith("nv")) return firstChild(child, "p:cNvPr");
|
|
2089
|
-
}
|
|
2090
|
-
return null;
|
|
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);
|
|
2091
1816
|
}
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
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
|
+
};
|
|
2095
1832
|
}
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
for (let node = parent.firstChild; node; node = node.nextSibling) if (node.nodeType === 1) out.push(node);
|
|
2100
|
-
return out;
|
|
1833
|
+
function rotationDegrees(xfrm) {
|
|
1834
|
+
const rot = intValue(attr(xfrm, "rot"));
|
|
1835
|
+
return rot === null ? 0 : rot / 6e4;
|
|
2101
1836
|
}
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
1837
|
+
function transformFlipH(xfrm) {
|
|
1838
|
+
return boolValue(attr(xfrm, "flipH")) === true;
|
|
1839
|
+
}
|
|
1840
|
+
function transformFlipV(xfrm) {
|
|
1841
|
+
return boolValue(attr(xfrm, "flipV")) === true;
|
|
2105
1842
|
}
|
|
2106
1843
|
//#endregion
|
|
2107
|
-
//#region src/read/group-transform.ts
|
|
1844
|
+
//#region src/read/api/shapes/group-transform.ts
|
|
2108
1845
|
/** Normalise a degree value into [0, 360). */
|
|
2109
1846
|
function normalizeDegrees(value) {
|
|
2110
1847
|
return (value % 360 + 360) % 360;
|
|
@@ -2633,22 +2370,68 @@ var Paragraph = class {
|
|
|
2633
2370
|
return this.#emuAttrPt("indent");
|
|
2634
2371
|
}
|
|
2635
2372
|
/**
|
|
2636
|
-
*
|
|
2637
|
-
*
|
|
2638
|
-
*
|
|
2639
|
-
*
|
|
2640
|
-
*
|
|
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}.
|
|
2641
2383
|
*/
|
|
2642
|
-
get
|
|
2384
|
+
get bulletDetail() {
|
|
2643
2385
|
const pPr = firstChild(this.element, "a:pPr");
|
|
2644
2386
|
if (!pPr) return null;
|
|
2645
|
-
if (firstChild(pPr, "a:buNone")) return "none";
|
|
2387
|
+
if (firstChild(pPr, "a:buNone")) return { kind: "none" };
|
|
2388
|
+
const style = this.#bulletStyle(pPr);
|
|
2646
2389
|
const buChar = firstChild(pPr, "a:buChar");
|
|
2647
|
-
if (buChar) return
|
|
2390
|
+
if (buChar) return {
|
|
2391
|
+
kind: "char",
|
|
2392
|
+
char: attr(buChar, "char") ?? "",
|
|
2393
|
+
...style
|
|
2394
|
+
};
|
|
2648
2395
|
const buAutoNum = firstChild(pPr, "a:buAutoNum");
|
|
2649
|
-
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
|
+
};
|
|
2650
2408
|
return null;
|
|
2651
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
|
+
}
|
|
2652
2435
|
/** Points from a spacing child's `a:spcPts/@val` (hundredths of a point), or `null`. */
|
|
2653
2436
|
#spacingPt(qname) {
|
|
2654
2437
|
const pPr = firstChild(this.element, "a:pPr");
|
|
@@ -2831,7 +2614,7 @@ function setTextBodyText(txBody, value) {
|
|
|
2831
2614
|
p = createElement(doc, "a:p");
|
|
2832
2615
|
txBody.appendChild(p);
|
|
2833
2616
|
}
|
|
2834
|
-
for (const child of [...childElements
|
|
2617
|
+
for (const child of [...childElements(p)]) if (child.localName === "r" || child.localName === "br" || child.localName === "fld") p.removeChild(child);
|
|
2835
2618
|
const run = createElement(doc, "a:r");
|
|
2836
2619
|
if (rPrTemplate) run.appendChild(rPrTemplate.cloneNode(true));
|
|
2837
2620
|
const t = createElement(doc, "a:t");
|
|
@@ -2843,119 +2626,6 @@ function setTextBodyText(txBody, value) {
|
|
|
2843
2626
|
p.insertBefore(run, endParaRPr);
|
|
2844
2627
|
}
|
|
2845
2628
|
//#endregion
|
|
2846
|
-
//#region src/read/api/shapes/geometry.ts
|
|
2847
|
-
/**
|
|
2848
|
-
* Reading geometry out of a shape: transform boxes, rotation and flips, and custom-geometry paths.
|
|
2849
|
-
*
|
|
2850
|
-
* Every value here comes straight off `a:xfrm` / `a:path` attributes, so these are pure element
|
|
2851
|
-
* readers with no theme or relationship context.
|
|
2852
|
-
*/
|
|
2853
|
-
/**
|
|
2854
|
-
* One `a:pt` coordinate as a raw path-unit integer. A guide-name reference
|
|
2855
|
-
* (the `ST_AdjCoordinate` string form) is not produced by authored freeforms;
|
|
2856
|
-
* a non-numeric value degrades to `0` rather than crashing (documented edge).
|
|
2857
|
-
*/
|
|
2858
|
-
function ptAxis(pt, axis) {
|
|
2859
|
-
return (pt ? intValue(attr(pt, axis)) : null) ?? 0;
|
|
2860
|
-
}
|
|
2861
|
-
/** Parse one `<a:path>` into its viewport attrs (with schema defaults) and ordered segments. */
|
|
2862
|
-
function readGeometryPath(path) {
|
|
2863
|
-
const commands = [];
|
|
2864
|
-
for (let node = path.firstChild; node; node = node.nextSibling) {
|
|
2865
|
-
if (node.nodeType !== 1) continue;
|
|
2866
|
-
const seg = node;
|
|
2867
|
-
if (seg.namespaceURI !== OOXML_NS.a) continue;
|
|
2868
|
-
const pts = getElements(seg, "a:pt");
|
|
2869
|
-
switch (seg.localName) {
|
|
2870
|
-
case "moveTo":
|
|
2871
|
-
commands.push({
|
|
2872
|
-
cmd: "moveTo",
|
|
2873
|
-
x: ptAxis(pts[0], "x"),
|
|
2874
|
-
y: ptAxis(pts[0], "y")
|
|
2875
|
-
});
|
|
2876
|
-
break;
|
|
2877
|
-
case "lnTo":
|
|
2878
|
-
commands.push({
|
|
2879
|
-
cmd: "lnTo",
|
|
2880
|
-
x: ptAxis(pts[0], "x"),
|
|
2881
|
-
y: ptAxis(pts[0], "y")
|
|
2882
|
-
});
|
|
2883
|
-
break;
|
|
2884
|
-
case "cubicBezTo":
|
|
2885
|
-
commands.push({
|
|
2886
|
-
cmd: "cubicBezTo",
|
|
2887
|
-
x1: ptAxis(pts[0], "x"),
|
|
2888
|
-
y1: ptAxis(pts[0], "y"),
|
|
2889
|
-
x2: ptAxis(pts[1], "x"),
|
|
2890
|
-
y2: ptAxis(pts[1], "y"),
|
|
2891
|
-
x: ptAxis(pts[2], "x"),
|
|
2892
|
-
y: ptAxis(pts[2], "y")
|
|
2893
|
-
});
|
|
2894
|
-
break;
|
|
2895
|
-
case "quadBezTo":
|
|
2896
|
-
commands.push({
|
|
2897
|
-
cmd: "quadBezTo",
|
|
2898
|
-
x1: ptAxis(pts[0], "x"),
|
|
2899
|
-
y1: ptAxis(pts[0], "y"),
|
|
2900
|
-
x: ptAxis(pts[1], "x"),
|
|
2901
|
-
y: ptAxis(pts[1], "y")
|
|
2902
|
-
});
|
|
2903
|
-
break;
|
|
2904
|
-
case "arcTo":
|
|
2905
|
-
commands.push({
|
|
2906
|
-
cmd: "arcTo",
|
|
2907
|
-
wR: intValue(attr(seg, "wR")) ?? 0,
|
|
2908
|
-
hR: intValue(attr(seg, "hR")) ?? 0,
|
|
2909
|
-
stAng: (intValue(attr(seg, "stAng")) ?? 0) / 6e4,
|
|
2910
|
-
swAng: (intValue(attr(seg, "swAng")) ?? 0) / 6e4
|
|
2911
|
-
});
|
|
2912
|
-
break;
|
|
2913
|
-
case "close":
|
|
2914
|
-
commands.push({ cmd: "close" });
|
|
2915
|
-
break;
|
|
2916
|
-
}
|
|
2917
|
-
}
|
|
2918
|
-
return {
|
|
2919
|
-
w: intValue(attr(path, "w")) ?? 0,
|
|
2920
|
-
h: intValue(attr(path, "h")) ?? 0,
|
|
2921
|
-
fill: attr(path, "fill") ?? "norm",
|
|
2922
|
-
stroke: boolValue(attr(path, "stroke")) ?? true,
|
|
2923
|
-
commands
|
|
2924
|
-
};
|
|
2925
|
-
}
|
|
2926
|
-
/** Validate and round an EMU geometry value; extents (`cx`/`cy`) must be non-negative. */
|
|
2927
|
-
function toEmu(value, attribute, allowNegative) {
|
|
2928
|
-
if (!Number.isFinite(value)) throw new InvalidOptionError("coord/non-finite", `${attribute} must be a finite number of EMU, got ${value}`);
|
|
2929
|
-
if (!allowNegative && value < 0) throw new InvalidOptionError("coord/negative", `${attribute} must be non-negative, got ${value}`);
|
|
2930
|
-
return Math.round(value);
|
|
2931
|
-
}
|
|
2932
|
-
/** A point + extent pair (`a:off`/`a:ext` or `a:chOff`/`a:chExt`) from a transform, or `null` if either is incomplete. */
|
|
2933
|
-
function readBox(xfrm, offName, extName) {
|
|
2934
|
-
const off = firstChild(xfrm, offName);
|
|
2935
|
-
const ext = firstChild(xfrm, extName);
|
|
2936
|
-
const x = off && intValue(attr(off, "x"));
|
|
2937
|
-
const y = off && intValue(attr(off, "y"));
|
|
2938
|
-
const cx = ext && intValue(attr(ext, "cx"));
|
|
2939
|
-
const cy = ext && intValue(attr(ext, "cy"));
|
|
2940
|
-
if (x === null || y === null || cx === null || cy === null) return null;
|
|
2941
|
-
return {
|
|
2942
|
-
x,
|
|
2943
|
-
y,
|
|
2944
|
-
cx,
|
|
2945
|
-
cy
|
|
2946
|
-
};
|
|
2947
|
-
}
|
|
2948
|
-
function rotationDegrees(xfrm) {
|
|
2949
|
-
const rot = intValue(attr(xfrm, "rot"));
|
|
2950
|
-
return rot === null ? 0 : rot / 6e4;
|
|
2951
|
-
}
|
|
2952
|
-
function transformFlipH(xfrm) {
|
|
2953
|
-
return boolValue(attr(xfrm, "flipH")) === true;
|
|
2954
|
-
}
|
|
2955
|
-
function transformFlipV(xfrm) {
|
|
2956
|
-
return boolValue(attr(xfrm, "flipV")) === true;
|
|
2957
|
-
}
|
|
2958
|
-
//#endregion
|
|
2959
2629
|
//#region src/read/api/shapes/base.ts
|
|
2960
2630
|
/**
|
|
2961
2631
|
* The abstract base every shape kind extends.
|
|
@@ -3254,9 +2924,25 @@ var Shape = class {
|
|
|
3254
2924
|
});
|
|
3255
2925
|
}
|
|
3256
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
|
+
/**
|
|
3257
2942
|
* Set an explicit `<a:noFill/>` on the shape — a transparent surface. This is
|
|
3258
2943
|
* distinct from clearing the fill (`fillColor = null`), which removes the
|
|
3259
2944
|
* `a:solidFill` and lets the fill inherit from the shape's style/placeholder.
|
|
2945
|
+
* Read it back with {@link fillNoFill}.
|
|
3260
2946
|
*/
|
|
3261
2947
|
noFill() {
|
|
3262
2948
|
if (!this.supportsFill) throw new UnsupportedFeatureError("shape/fill-unsupported", `${this.shapeType} shapes do not support a solid fill`);
|
|
@@ -3314,6 +3000,32 @@ var Shape = class {
|
|
|
3314
3000
|
return !!(ln && firstChild(ln, "a:noFill"));
|
|
3315
3001
|
}
|
|
3316
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
|
+
/**
|
|
3317
3029
|
* Preset geometry name (`spPr/a:prstGeom/@prst`, e.g. `rect`), or `null` for
|
|
3318
3030
|
* custom geometry or none. Not an auto-shape-only property: PowerPoint gives a
|
|
3319
3031
|
* picture and a connector a preset geometry too (a `p:pic` is `rect` unless it
|
|
@@ -3491,19 +3203,7 @@ var Shape = class {
|
|
|
3491
3203
|
*/
|
|
3492
3204
|
get patternFill() {
|
|
3493
3205
|
const props = this.properties();
|
|
3494
|
-
|
|
3495
|
-
if (!patt) return null;
|
|
3496
|
-
const ctx = this.slide.themeContext();
|
|
3497
|
-
const resolveWrap = (qname) => {
|
|
3498
|
-
const wrap = firstChild(patt, qname);
|
|
3499
|
-
const colorEl = wrap && firstChildElement(wrap);
|
|
3500
|
-
return colorEl ? resolveColorElement(colorEl, ctx) : null;
|
|
3501
|
-
};
|
|
3502
|
-
return {
|
|
3503
|
-
preset: attr(patt, "prst") ?? null,
|
|
3504
|
-
foreground: resolveWrap("a:fgClr"),
|
|
3505
|
-
background: resolveWrap("a:bgClr")
|
|
3506
|
-
};
|
|
3206
|
+
return props ? readPatternFill(props, this.slide.themeContext()) : null;
|
|
3507
3207
|
}
|
|
3508
3208
|
/**
|
|
3509
3209
|
* The shape's picture (image) fill (`spPr/a:blipFill`), or `null` when the fill
|
|
@@ -3726,7 +3426,6 @@ var AutoShape = class extends Shape {
|
|
|
3726
3426
|
* blip plumbing (content-type → extension, the SVG extension blip, the recolour effects) lives
|
|
3727
3427
|
* here rather than on the base: no other shape kind carries it.
|
|
3728
3428
|
*/
|
|
3729
|
-
const IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
3730
3429
|
const ASVG_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
|
|
3731
3430
|
const PIC_AFTER_BLIPFILL = ["p:spPr", "p:style"];
|
|
3732
3431
|
const BLIPFILL_AFTER_BLIP = [
|
|
@@ -3969,7 +3668,7 @@ var Picture = class extends Shape {
|
|
|
3969
3668
|
const opc = this.slide.presentation.opc;
|
|
3970
3669
|
const mediaPartName = opc.reserveMediaPartName(extension);
|
|
3971
3670
|
opc.addPart(mediaPartName, contentType, bytes);
|
|
3972
|
-
const relId = this.slide.relationships.add(
|
|
3671
|
+
const relId = this.slide.relationships.add(IMAGE_REL, relativePartName(this.slide.partName, mediaPartName)).id;
|
|
3973
3672
|
setAttr(this.#getOrAddBlip(), "r:embed", relId);
|
|
3974
3673
|
if (options.fit) this.#applyFit(options.fit, bytes);
|
|
3975
3674
|
this.markDirty();
|
|
@@ -4355,7 +4054,7 @@ var ChartSeries = class {
|
|
|
4355
4054
|
/** Cached numeric values (`c:val`); non-numeric or missing points are `null`. */
|
|
4356
4055
|
get values() {
|
|
4357
4056
|
const val = firstChild(this.ser, "c:val");
|
|
4358
|
-
return readPoints(val && findCache(val)).map(
|
|
4057
|
+
return readPoints(val && findCache(val)).map(intValue);
|
|
4359
4058
|
}
|
|
4360
4059
|
/** Cached category labels for this series (`c:cat`), as written. */
|
|
4361
4060
|
get categories() {
|
|
@@ -4441,12 +4140,7 @@ function readPoints(cache) {
|
|
|
4441
4140
|
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4442
4141
|
points[idx] = firstChild(pt, "c:v")?.textContent ?? null;
|
|
4443
4142
|
}
|
|
4444
|
-
return points;
|
|
4445
|
-
}
|
|
4446
|
-
function toNumberOrNull$1(value) {
|
|
4447
|
-
if (value === null || value === "") return null;
|
|
4448
|
-
const number = Number(value);
|
|
4449
|
-
return Number.isFinite(number) ? number : null;
|
|
4143
|
+
return points;
|
|
4450
4144
|
}
|
|
4451
4145
|
//#endregion
|
|
4452
4146
|
//#region src/read/api/chartex.ts
|
|
@@ -4591,7 +4285,7 @@ var ChartExSeries = class {
|
|
|
4591
4285
|
get values() {
|
|
4592
4286
|
const data = this.chart.dataElementById(this.dataId);
|
|
4593
4287
|
const numDim = data && firstChild(data, "cx:numDim");
|
|
4594
|
-
return readLevelPoints(numDim && firstChild(numDim, "cx:lvl")).map(
|
|
4288
|
+
return readLevelPoints(numDim && firstChild(numDim, "cx:lvl")).map(intValue);
|
|
4595
4289
|
}
|
|
4596
4290
|
/**
|
|
4597
4291
|
* Cached category labels (`cx:strDim`'s leaf `cx:lvl` — the first level, which
|
|
@@ -4709,10 +4403,391 @@ function floatValue(value) {
|
|
|
4709
4403
|
const number = Number(value);
|
|
4710
4404
|
return Number.isFinite(number) ? number : null;
|
|
4711
4405
|
}
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4406
|
+
//#endregion
|
|
4407
|
+
//#region src/read/api/table-edit.ts
|
|
4408
|
+
/**
|
|
4409
|
+
* Ordered mutation of a table cell's `a:tcPr`, and the cell-scoped schema facts every such
|
|
4410
|
+
* edit needs.
|
|
4411
|
+
*
|
|
4412
|
+
* `CT_TableCellProperties` declares its children as a **sequence**, so an append-only
|
|
4413
|
+
* implementation produces an out-of-order `a:tcPr` — which PowerPoint reports as a corrupt
|
|
4414
|
+
* file rather than as a bad edit. Every insertion therefore goes through
|
|
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.
|
|
4418
|
+
*
|
|
4419
|
+
* The attribute values are the other half. A value outside its `ST_` union is equally fatal
|
|
4420
|
+
* and equally silent, so each is checked against the enum before it is written — the same
|
|
4421
|
+
* validate-at-the-boundary rule the write path follows, with the difference that here there
|
|
4422
|
+
* is a caller to throw at rather than a deck to degrade (see `ooxml/check-enum.ts`).
|
|
4423
|
+
*/
|
|
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;
|
|
4430
|
+
/** The four edges plus the two diagonals, keyed as {@link TableCellEdge} names them. */
|
|
4431
|
+
const EDGE_QNAMES = {
|
|
4432
|
+
left: "a:lnL",
|
|
4433
|
+
right: "a:lnR",
|
|
4434
|
+
top: "a:lnT",
|
|
4435
|
+
bottom: "a:lnB",
|
|
4436
|
+
tlToBr: "a:lnTlToBr",
|
|
4437
|
+
blToTr: "a:lnBlToTr"
|
|
4438
|
+
};
|
|
4439
|
+
/**
|
|
4440
|
+
* Get or create a child of `a:tcPr` at its schema position.
|
|
4441
|
+
* @param {Element} tcPr - the cell's properties element
|
|
4442
|
+
* @param {string} qname - the child to get or create
|
|
4443
|
+
* @returns {Element} the existing or newly-inserted child
|
|
4444
|
+
*/
|
|
4445
|
+
function tcPrChild(tcPr, qname) {
|
|
4446
|
+
return getOrAddChild(tcPr, qname, TCPR_AFTER[qname] ?? []);
|
|
4447
|
+
}
|
|
4448
|
+
/**
|
|
4449
|
+
* Insert an already-built subtree into `a:tcPr` at its schema position.
|
|
4450
|
+
* @param {Element} tcPr - the cell's properties element
|
|
4451
|
+
* @param {string} qname - the qname of the node being inserted (decides where it goes)
|
|
4452
|
+
* @param {Element} node - the subtree to insert
|
|
4453
|
+
*/
|
|
4454
|
+
function insertTcPrChild(tcPr, qname, node) {
|
|
4455
|
+
insertInOrder(tcPr, node, TCPR_AFTER[qname] ?? []);
|
|
4456
|
+
}
|
|
4457
|
+
/**
|
|
4458
|
+
* Round a measurement to whole EMU, rejecting a value that cannot be written.
|
|
4459
|
+
* `NaN`/`Infinity` would reach the attribute verbatim and make the part invalid, which is
|
|
4460
|
+
* exactly the "silent coercion of invalid input" the project's policy rules out.
|
|
4461
|
+
*/
|
|
4462
|
+
function checkFiniteEmu(value, field, code) {
|
|
4463
|
+
if (!Number.isFinite(value)) throw new InvalidOptionError(code, `${field} must be a finite number of EMU, got: ${String(value)}`);
|
|
4464
|
+
return Math.round(value);
|
|
4465
|
+
}
|
|
4466
|
+
//#endregion
|
|
4467
|
+
//#region src/read/api/table-structure.ts
|
|
4468
|
+
/**
|
|
4469
|
+
* Structural edits to an existing `a:tbl`: adding and removing rows and columns, and
|
|
4470
|
+
* merging and unmerging cells.
|
|
4471
|
+
*
|
|
4472
|
+
* The one fact that makes this tractable is that a stored table's grid is already
|
|
4473
|
+
* **rectangular**. Unlike the write path — which receives lopsided row arrays and has to
|
|
4474
|
+
* build the merge grid itself (`gen/slide/objects/table.ts` STEP 3) — every `a:tr` in a
|
|
4475
|
+
* conformant `a:tbl` holds exactly one `a:tc` per grid column, with the covered half of each
|
|
4476
|
+
* span present and flagged. So "the cell at (r, c)" is simply the c-th `a:tc` of the r-th
|
|
4477
|
+
* `a:tr`, and an edit's whole job is keeping that rectangle true.
|
|
4478
|
+
*
|
|
4479
|
+
* Three things have to stay in step, and every function here is written around them:
|
|
4480
|
+
*
|
|
4481
|
+
* 1. `a:tblGrid/a:gridCol` count == each row's `a:tc` count.
|
|
4482
|
+
* 2. A cell with `@gridSpan="n"` is followed by exactly `n-1` cells carrying `@hMerge="1"`,
|
|
4483
|
+
* and a cell with `@rowSpan="n"` is under-hung by `n-1` cells carrying `@vMerge="1"`, in
|
|
4484
|
+
* the same columns.
|
|
4485
|
+
* 3. A covered cell carries no span attributes of its own — its extent is the origin's.
|
|
4486
|
+
*
|
|
4487
|
+
* Breaking any of them produces a table PowerPoint reports as a corrupt file rather than as
|
|
4488
|
+
* a bad edit, which is why the span bookkeeping here is explicit rather than incidental.
|
|
4489
|
+
*/
|
|
4490
|
+
/** Every `a:tr` of a table, in document order. */
|
|
4491
|
+
function rowsOf(tbl) {
|
|
4492
|
+
return getElements(tbl, "a:tr");
|
|
4493
|
+
}
|
|
4494
|
+
/** Every `a:tc` of a row, in document (left-to-right) order. */
|
|
4495
|
+
function cellsOf(tr) {
|
|
4496
|
+
return getElements(tr, "a:tc");
|
|
4497
|
+
}
|
|
4498
|
+
/** The table's `a:tblGrid`, or a thrown error — `CT_Table` requires one. */
|
|
4499
|
+
function gridOf(tbl) {
|
|
4500
|
+
const grid = firstChild(tbl, "a:tblGrid");
|
|
4501
|
+
if (!grid) throw new InvalidOptionError("table/column-index-out-of-range", "This table has no a:tblGrid, so its columns cannot be edited");
|
|
4502
|
+
return grid;
|
|
4503
|
+
}
|
|
4504
|
+
/** A cell's `@gridSpan`, defaulting to 1. */
|
|
4505
|
+
function gridSpanOf(tc) {
|
|
4506
|
+
return intValue(attr(tc, "gridSpan")) ?? 1;
|
|
4507
|
+
}
|
|
4508
|
+
/** A cell's `@rowSpan`, defaulting to 1. */
|
|
4509
|
+
function rowSpanOf(tc) {
|
|
4510
|
+
return intValue(attr(tc, "rowSpan")) ?? 1;
|
|
4511
|
+
}
|
|
4512
|
+
/** Whether a cell is the covered half of a horizontal merge. */
|
|
4513
|
+
function isHMerge(tc) {
|
|
4514
|
+
return attr(tc, "hMerge") === "1" || attr(tc, "hMerge") === "true";
|
|
4515
|
+
}
|
|
4516
|
+
/** Whether a cell is the covered half of a vertical merge. */
|
|
4517
|
+
function isVMerge(tc) {
|
|
4518
|
+
return attr(tc, "vMerge") === "1" || attr(tc, "vMerge") === "true";
|
|
4519
|
+
}
|
|
4520
|
+
/**
|
|
4521
|
+
* Set a span attribute, removing it when the span collapses to 1 — the schema default, and
|
|
4522
|
+
* what PowerPoint itself writes for an unmerged cell.
|
|
4523
|
+
*/
|
|
4524
|
+
function setSpan(tc, name, value) {
|
|
4525
|
+
if (value <= 1) removeAttr(tc, name);
|
|
4526
|
+
else setAttr(tc, name, String(value));
|
|
4527
|
+
}
|
|
4528
|
+
/**
|
|
4529
|
+
* Walk left from `col` to the origin of the horizontal run containing it.
|
|
4530
|
+
* A run is an origin followed by its `hMerge` continuations, so the origin is the first
|
|
4531
|
+
* cell at or left of `col` that is not one.
|
|
4532
|
+
* @returns the origin's column index, or `col` itself when the cell stands alone
|
|
4533
|
+
*/
|
|
4534
|
+
function hRunOrigin(cells, col) {
|
|
4535
|
+
let idx = col;
|
|
4536
|
+
while (idx > 0) {
|
|
4537
|
+
const cell = cells[idx];
|
|
4538
|
+
if (!cell || !isHMerge(cell)) break;
|
|
4539
|
+
idx -= 1;
|
|
4540
|
+
}
|
|
4541
|
+
return idx;
|
|
4542
|
+
}
|
|
4543
|
+
/**
|
|
4544
|
+
* Walk up from `row` to the origin of the vertical run containing `(row, col)`.
|
|
4545
|
+
* @returns the origin's row index, or `row` itself when the cell stands alone
|
|
4546
|
+
*/
|
|
4547
|
+
function vRunOrigin(rows, row, col) {
|
|
4548
|
+
let idx = row;
|
|
4549
|
+
while (idx > 0) {
|
|
4550
|
+
const cell = rows[idx]?.[col];
|
|
4551
|
+
if (!cell || !isVMerge(cell)) break;
|
|
4552
|
+
idx -= 1;
|
|
4553
|
+
}
|
|
4554
|
+
return idx;
|
|
4555
|
+
}
|
|
4556
|
+
/** A fresh, empty `a:tc`, shaped the way PowerPoint writes one. */
|
|
4557
|
+
function makeCell(doc) {
|
|
4558
|
+
const tc = createElement(doc, "a:tc");
|
|
4559
|
+
const txBody = createElement(doc, "a:txBody");
|
|
4560
|
+
txBody.appendChild(createElement(doc, "a:bodyPr"));
|
|
4561
|
+
txBody.appendChild(createElement(doc, "a:lstStyle"));
|
|
4562
|
+
txBody.appendChild(createElement(doc, "a:p"));
|
|
4563
|
+
tc.appendChild(txBody);
|
|
4564
|
+
tc.appendChild(createElement(doc, "a:tcPr"));
|
|
4565
|
+
return tc;
|
|
4566
|
+
}
|
|
4567
|
+
/** The grid as a row-major array of `a:tc` elements. */
|
|
4568
|
+
function gridOfCells(tbl) {
|
|
4569
|
+
return rowsOf(tbl).map((tr) => cellsOf(tr));
|
|
4570
|
+
}
|
|
4571
|
+
/** Guard an index against `[0, limit]` (inclusive upper bound, for insertion points). */
|
|
4572
|
+
function checkIndex(index, limit, what, inclusive) {
|
|
4573
|
+
const max = inclusive ? limit : limit - 1;
|
|
4574
|
+
if (!Number.isInteger(index) || index < 0 || index > max) throw new InvalidOptionError(what === "row" ? "table/row-index-out-of-range" : "table/column-index-out-of-range", `${what} index ${String(index)} is out of range (expected 0..${max})`);
|
|
4575
|
+
return index;
|
|
4576
|
+
}
|
|
4577
|
+
/**
|
|
4578
|
+
* Insert a row at `index` (default: append).
|
|
4579
|
+
*
|
|
4580
|
+
* The subtle case is inserting **through** a vertical merge. If the row currently at `index`
|
|
4581
|
+
* holds a `vMerge` continuation in some column, the span it belongs to straddles the
|
|
4582
|
+
* insertion point — so the new row must continue that span rather than interrupt it: the
|
|
4583
|
+
* origin's `@rowSpan` grows by one and the new cell is another continuation. Interrupting it
|
|
4584
|
+
* instead would leave an origin claiming more rows than it has continuations, which is the
|
|
4585
|
+
* corrupt-file case.
|
|
4586
|
+
*/
|
|
4587
|
+
function insertRow(tbl, index) {
|
|
4588
|
+
const rows = rowsOf(tbl);
|
|
4589
|
+
const at = index === void 0 ? rows.length : checkIndex(index, rows.length, "row", true);
|
|
4590
|
+
const grid = gridOfCells(tbl);
|
|
4591
|
+
const colCount = getElements(gridOf(tbl), "a:gridCol").length;
|
|
4592
|
+
const doc = ownerDocumentOf(tbl);
|
|
4593
|
+
const tr = createElement(doc, "a:tr");
|
|
4594
|
+
setAttr(tr, "h", "0");
|
|
4595
|
+
for (let col = 0; col < colCount; col++) {
|
|
4596
|
+
const tc = makeCell(doc);
|
|
4597
|
+
const displaced = grid[at]?.[col];
|
|
4598
|
+
if (displaced && isVMerge(displaced)) {
|
|
4599
|
+
const origin = grid[vRunOrigin(grid, at, col)]?.[col];
|
|
4600
|
+
if (origin) setSpan(origin, "rowSpan", rowSpanOf(origin) + 1);
|
|
4601
|
+
setAttr(tc, "vMerge", "1");
|
|
4602
|
+
if (isHMerge(displaced)) setAttr(tc, "hMerge", "1");
|
|
4603
|
+
const span = gridSpanOf(displaced);
|
|
4604
|
+
if (span > 1) setSpan(tc, "gridSpan", span);
|
|
4605
|
+
}
|
|
4606
|
+
tr.appendChild(tc);
|
|
4607
|
+
}
|
|
4608
|
+
const before = rows[at] ?? null;
|
|
4609
|
+
tbl.insertBefore(tr, before);
|
|
4610
|
+
return tr;
|
|
4611
|
+
}
|
|
4612
|
+
/**
|
|
4613
|
+
* Remove the row at `index`.
|
|
4614
|
+
*
|
|
4615
|
+
* Two span cases, and they pull in opposite directions. A cell in this row that *continues*
|
|
4616
|
+
* a span from above shortens that span by one. A cell in this row that *starts* one cannot
|
|
4617
|
+
* simply take its span away — the rows below still hold its continuations — so the first
|
|
4618
|
+
* continuation is promoted to origin and inherits the remaining extent. Its content is gone
|
|
4619
|
+
* with the row, which is inherent to removing a row rather than a choice made here.
|
|
4620
|
+
*/
|
|
4621
|
+
function removeRow(tbl, index) {
|
|
4622
|
+
const rows = rowsOf(tbl);
|
|
4623
|
+
const at = checkIndex(index, rows.length, "row", false);
|
|
4624
|
+
const grid = gridOfCells(tbl);
|
|
4625
|
+
const row = grid[at] ?? [];
|
|
4626
|
+
for (let col = 0; col < row.length; col++) {
|
|
4627
|
+
const tc = row[col];
|
|
4628
|
+
if (!tc) continue;
|
|
4629
|
+
if (isVMerge(tc)) {
|
|
4630
|
+
const origin = grid[vRunOrigin(grid, at, col)]?.[col];
|
|
4631
|
+
if (origin) setSpan(origin, "rowSpan", rowSpanOf(origin) - 1);
|
|
4632
|
+
continue;
|
|
4633
|
+
}
|
|
4634
|
+
const span = rowSpanOf(tc);
|
|
4635
|
+
if (span > 1) {
|
|
4636
|
+
const heir = grid[at + 1]?.[col];
|
|
4637
|
+
if (heir) {
|
|
4638
|
+
removeAttr(heir, "vMerge");
|
|
4639
|
+
setSpan(heir, "rowSpan", span - 1);
|
|
4640
|
+
setSpan(heir, "gridSpan", gridSpanOf(tc));
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4644
|
+
const tr = rows[at];
|
|
4645
|
+
if (tr) tbl.removeChild(tr);
|
|
4646
|
+
}
|
|
4647
|
+
/**
|
|
4648
|
+
* Insert a column at `index` (default: append), `widthEmu` wide.
|
|
4649
|
+
*
|
|
4650
|
+
* Mirrors {@link insertRow}'s split case on the other axis: when the cell currently at
|
|
4651
|
+
* `index` is an `hMerge` continuation, the insertion point falls inside a horizontal span,
|
|
4652
|
+
* so the span widens by one and the new cell joins it as another continuation.
|
|
4653
|
+
*/
|
|
4654
|
+
function insertColumn(tbl, index, widthEmu = 914400) {
|
|
4655
|
+
const grid = gridOf(tbl);
|
|
4656
|
+
const cols = getElements(grid, "a:gridCol");
|
|
4657
|
+
const at = index === void 0 ? cols.length : checkIndex(index, cols.length, "column", true);
|
|
4658
|
+
const doc = ownerDocumentOf(tbl);
|
|
4659
|
+
const gridCol = createElement(doc, "a:gridCol");
|
|
4660
|
+
setAttr(gridCol, "w", String(Math.round(widthEmu)));
|
|
4661
|
+
grid.insertBefore(gridCol, cols[at] ?? null);
|
|
4662
|
+
for (const tr of rowsOf(tbl)) {
|
|
4663
|
+
const cells = cellsOf(tr);
|
|
4664
|
+
const tc = makeCell(doc);
|
|
4665
|
+
const displaced = cells[at];
|
|
4666
|
+
if (displaced && isHMerge(displaced)) {
|
|
4667
|
+
const origin = cells[hRunOrigin(cells, at)];
|
|
4668
|
+
if (origin) setSpan(origin, "gridSpan", gridSpanOf(origin) + 1);
|
|
4669
|
+
setAttr(tc, "hMerge", "1");
|
|
4670
|
+
if (isVMerge(displaced)) setAttr(tc, "vMerge", "1");
|
|
4671
|
+
const span = rowSpanOf(displaced);
|
|
4672
|
+
if (span > 1) setSpan(tc, "rowSpan", span);
|
|
4673
|
+
}
|
|
4674
|
+
tr.insertBefore(tc, cells[at] ?? firstChild(tr, "a:extLst"));
|
|
4675
|
+
}
|
|
4676
|
+
return gridCol;
|
|
4677
|
+
}
|
|
4678
|
+
/**
|
|
4679
|
+
* Remove the column at `index`.
|
|
4680
|
+
*
|
|
4681
|
+
* When the column falls inside a horizontal span, the span shrinks by one and a
|
|
4682
|
+
* *continuation* is removed rather than the origin — so the merged region narrows but keeps
|
|
4683
|
+
* its content. Only a column whose cell stands alone loses that cell outright.
|
|
4684
|
+
*/
|
|
4685
|
+
function removeColumn(tbl, index) {
|
|
4686
|
+
const grid = gridOf(tbl);
|
|
4687
|
+
const cols = getElements(grid, "a:gridCol");
|
|
4688
|
+
const at = checkIndex(index, cols.length, "column", false);
|
|
4689
|
+
const col = cols[at];
|
|
4690
|
+
if (col) grid.removeChild(col);
|
|
4691
|
+
for (const tr of rowsOf(tbl)) {
|
|
4692
|
+
const cells = cellsOf(tr);
|
|
4693
|
+
const originIdx = hRunOrigin(cells, at);
|
|
4694
|
+
const origin = cells[originIdx];
|
|
4695
|
+
if (!origin) continue;
|
|
4696
|
+
const span = gridSpanOf(origin);
|
|
4697
|
+
if (span > 1) {
|
|
4698
|
+
setSpan(origin, "gridSpan", span - 1);
|
|
4699
|
+
const victim = cells[originIdx + span - 1];
|
|
4700
|
+
if (victim) tr.removeChild(victim);
|
|
4701
|
+
continue;
|
|
4702
|
+
}
|
|
4703
|
+
const tc = cells[at];
|
|
4704
|
+
if (tc) tr.removeChild(tc);
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
/**
|
|
4708
|
+
* Merge the rectangle `(row1, col1)`–`(row2, col2)` into one cell.
|
|
4709
|
+
*
|
|
4710
|
+
* The top-left cell becomes the origin and keeps its content; every other cell in the
|
|
4711
|
+
* rectangle becomes a covered cell — flagged, stripped of its own span attributes, and
|
|
4712
|
+
* emptied, since a covered cell is never rendered.
|
|
4713
|
+
*
|
|
4714
|
+
* A rectangle whose boundary **cuts through** an existing merge is rejected rather than
|
|
4715
|
+
* silently widened. Widening would be the friendlier-looking choice and the wrong one: the
|
|
4716
|
+
* caller asked for a specific region, and quietly returning a different one is how a layout
|
|
4717
|
+
* ends up subtly wrong with nothing to point at.
|
|
4718
|
+
*/
|
|
4719
|
+
function mergeCells(tbl, row1, col1, row2, col2) {
|
|
4720
|
+
const grid = gridOfCells(tbl);
|
|
4721
|
+
const rowCount = grid.length;
|
|
4722
|
+
const colCount = getElements(gridOf(tbl), "a:gridCol").length;
|
|
4723
|
+
const r1 = Math.min(row1, row2);
|
|
4724
|
+
const r2 = Math.max(row1, row2);
|
|
4725
|
+
const c1 = Math.min(col1, col2);
|
|
4726
|
+
const c2 = Math.max(col1, col2);
|
|
4727
|
+
checkIndex(r1, rowCount, "row", false);
|
|
4728
|
+
checkIndex(r2, rowCount, "row", false);
|
|
4729
|
+
checkIndex(c1, colCount, "column", false);
|
|
4730
|
+
checkIndex(c2, colCount, "column", false);
|
|
4731
|
+
if (r1 === r2 && c1 === c2) throw new InvalidOptionError("table/merge-range-invalid", "A merge needs at least two cells; the given range covers one");
|
|
4732
|
+
for (let r = r1; r <= r2; r++) for (let c = c1; c <= c2; c++) {
|
|
4733
|
+
if (!grid[r]?.[c]) continue;
|
|
4734
|
+
const originRow = vRunOrigin(grid, r, c);
|
|
4735
|
+
const originCol = hRunOrigin(grid[r] ?? [], c);
|
|
4736
|
+
const origin = grid[originRow]?.[originCol];
|
|
4737
|
+
if (!origin) continue;
|
|
4738
|
+
if (originRow < r1 || originCol < c1) throw new InvalidOptionError("table/merge-range-invalid", `The range (${r1},${c1})-(${r2},${c2}) starts inside an existing merged cell at (${originRow},${originCol}); unmerge it first`);
|
|
4739
|
+
if (originRow + rowSpanOf(origin) - 1 > r2 || originCol + gridSpanOf(origin) - 1 > c2) throw new InvalidOptionError("table/merge-range-invalid", `The range (${r1},${c1})-(${r2},${c2}) cuts through an existing merged cell at (${originRow},${originCol}); unmerge it first`);
|
|
4740
|
+
}
|
|
4741
|
+
const origin = grid[r1]?.[c1];
|
|
4742
|
+
if (!origin) return;
|
|
4743
|
+
setSpan(origin, "gridSpan", c2 - c1 + 1);
|
|
4744
|
+
setSpan(origin, "rowSpan", r2 - r1 + 1);
|
|
4745
|
+
removeAttr(origin, "hMerge");
|
|
4746
|
+
removeAttr(origin, "vMerge");
|
|
4747
|
+
for (let r = r1; r <= r2; r++) for (let c = c1; c <= c2; c++) {
|
|
4748
|
+
if (r === r1 && c === c1) continue;
|
|
4749
|
+
const tc = grid[r]?.[c];
|
|
4750
|
+
if (!tc) continue;
|
|
4751
|
+
removeAttr(tc, "gridSpan");
|
|
4752
|
+
removeAttr(tc, "rowSpan");
|
|
4753
|
+
if (c > c1) setAttr(tc, "hMerge", "1");
|
|
4754
|
+
else removeAttr(tc, "hMerge");
|
|
4755
|
+
if (r > r1) setAttr(tc, "vMerge", "1");
|
|
4756
|
+
else removeAttr(tc, "vMerge");
|
|
4757
|
+
emptyCellText(tc);
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
/**
|
|
4761
|
+
* Split the merged cell whose origin is `(row, col)` back into individual cells.
|
|
4762
|
+
* The origin keeps its content; the cells it covered come back empty, which is what they
|
|
4763
|
+
* already were.
|
|
4764
|
+
*/
|
|
4765
|
+
function unmergeCell(tbl, row, col) {
|
|
4766
|
+
const grid = gridOfCells(tbl);
|
|
4767
|
+
checkIndex(row, grid.length, "row", false);
|
|
4768
|
+
checkIndex(col, getElements(gridOf(tbl), "a:gridCol").length, "column", false);
|
|
4769
|
+
const origin = grid[row]?.[col];
|
|
4770
|
+
if (!origin) return;
|
|
4771
|
+
if (isHMerge(origin) || isVMerge(origin)) throw new InvalidOptionError("table/merge-range-invalid", `The cell at (${row},${col}) is a covered cell, not a merge origin; unmerge the origin at (${vRunOrigin(grid, row, col)},${hRunOrigin(grid[row] ?? [], col)}) instead`);
|
|
4772
|
+
const rowSpan = rowSpanOf(origin);
|
|
4773
|
+
const gridSpan = gridSpanOf(origin);
|
|
4774
|
+
if (rowSpan === 1 && gridSpan === 1) return;
|
|
4775
|
+
removeAttr(origin, "gridSpan");
|
|
4776
|
+
removeAttr(origin, "rowSpan");
|
|
4777
|
+
for (let r = row; r < row + rowSpan; r++) for (let c = col; c < col + gridSpan; c++) {
|
|
4778
|
+
if (r === row && c === col) continue;
|
|
4779
|
+
const tc = grid[r]?.[c];
|
|
4780
|
+
if (!tc) continue;
|
|
4781
|
+
removeAttr(tc, "hMerge");
|
|
4782
|
+
removeAttr(tc, "vMerge");
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
/** Reduce a cell's text body to a single empty paragraph. */
|
|
4786
|
+
function emptyCellText(tc) {
|
|
4787
|
+
const txBody = firstChild(tc, "a:txBody");
|
|
4788
|
+
if (!txBody) return;
|
|
4789
|
+
for (const p of getElements(txBody, "a:p")) txBody.removeChild(p);
|
|
4790
|
+
txBody.appendChild(createElement(ownerDocumentOf(txBody), "a:p"));
|
|
4716
4791
|
}
|
|
4717
4792
|
//#endregion
|
|
4718
4793
|
//#region src/read/api/table-style-resolve.ts
|
|
@@ -4893,6 +4968,63 @@ var Table = class {
|
|
|
4893
4968
|
const id = (tblPr && firstChild(tblPr, "a:tableStyleId"))?.textContent?.trim();
|
|
4894
4969
|
return id ? id : null;
|
|
4895
4970
|
}
|
|
4971
|
+
/**
|
|
4972
|
+
* The table's own background (`a:tblPr` fill) resolved against the slide's theme colour
|
|
4973
|
+
* context to a literal hex, or `null` when the table carries no solid background.
|
|
4974
|
+
*
|
|
4975
|
+
* This is the table-level counterpart of {@link TableCell.resolvedFill}, and it is a
|
|
4976
|
+
* genuinely different thing from a cell fill: a `a:tblPr` fill sits *behind* the grid, so
|
|
4977
|
+
* a cell with no fill of its own shows it through. Reports `null` for a non-solid choice
|
|
4978
|
+
* (`a:blipFill`/`a:gradFill`/`a:pattFill`/`a:noFill`) — read {@link pictureFill} for an
|
|
4979
|
+
* image background — and `null` with no theme context.
|
|
4980
|
+
*/
|
|
4981
|
+
get resolvedFill() {
|
|
4982
|
+
if (!this.themeColors) return null;
|
|
4983
|
+
const tblPr = firstChild(this.tbl, "a:tblPr");
|
|
4984
|
+
if (!tblPr || !FILL_CHOICES.some((q) => firstChild(tblPr, q))) return null;
|
|
4985
|
+
return resolveSolidFillColor(tblPr, this.themeColors);
|
|
4986
|
+
}
|
|
4987
|
+
/**
|
|
4988
|
+
* The table's picture background (`a:tblPr/a:blipFill`), or `null` when the table is not
|
|
4989
|
+
* image-backed. The table-level twin of {@link TableCell.pictureFill}, and needed for the
|
|
4990
|
+
* same reason: {@link resolvedFill} decodes only solid colours, so without this an
|
|
4991
|
+
* image-backed table is indistinguishable from an unfilled one.
|
|
4992
|
+
*/
|
|
4993
|
+
get pictureFill() {
|
|
4994
|
+
const tblPr = firstChild(this.tbl, "a:tblPr");
|
|
4995
|
+
return tblPr ? readPictureFill(tblPr, this.rels ?? null) : null;
|
|
4996
|
+
}
|
|
4997
|
+
/**
|
|
4998
|
+
* The table's gradient background (`a:tblPr/a:gradFill`), or `null` when it is not
|
|
4999
|
+
* gradient-filled. Needed for the same reason {@link pictureFill} is:
|
|
5000
|
+
* {@link resolvedFill} decodes only solid colours.
|
|
5001
|
+
*/
|
|
5002
|
+
get gradientFill() {
|
|
5003
|
+
if (!this.themeColors) return null;
|
|
5004
|
+
const tblPr = firstChild(this.tbl, "a:tblPr");
|
|
5005
|
+
return tblPr ? readGradientFill(tblPr, this.themeColors) : null;
|
|
5006
|
+
}
|
|
5007
|
+
/**
|
|
5008
|
+
* The table's pattern (hatch) background (`a:tblPr/a:pattFill`), or `null` when it is not
|
|
5009
|
+
* pattern-filled.
|
|
5010
|
+
*/
|
|
5011
|
+
get patternFill() {
|
|
5012
|
+
if (!this.themeColors) return null;
|
|
5013
|
+
const tblPr = firstChild(this.tbl, "a:tblPr");
|
|
5014
|
+
return tblPr ? readPatternFill(tblPr, this.themeColors) : null;
|
|
5015
|
+
}
|
|
5016
|
+
/**
|
|
5017
|
+
* The raw `schemeClr` token of the table's own background
|
|
5018
|
+
* (`a:tblPr/a:solidFill/a:schemeClr/@val`), or `null` for an absent or `srgbClr` fill.
|
|
5019
|
+
* {@link resolvedFill} is the literal it resolves to; this is the unresolved reference,
|
|
5020
|
+
* which is what a replica should carry so the copy still tracks its theme.
|
|
5021
|
+
*/
|
|
5022
|
+
get fillSchemeColor() {
|
|
5023
|
+
const tblPr = firstChild(this.tbl, "a:tblPr");
|
|
5024
|
+
const fill = tblPr && firstChild(tblPr, "a:solidFill");
|
|
5025
|
+
const scheme = fill && firstChild(fill, "a:schemeClr");
|
|
5026
|
+
return scheme ? attr(scheme, "val") : null;
|
|
5027
|
+
}
|
|
4896
5028
|
/** Whether the first row is styled as a header (`a:tblPr/@firstRow`). */
|
|
4897
5029
|
get firstRowHeader() {
|
|
4898
5030
|
return this.#tblPrFlag("firstRow");
|
|
@@ -4909,6 +5041,83 @@ var Table = class {
|
|
|
4909
5041
|
cell(rowIndex, columnIndex) {
|
|
4910
5042
|
return this.rows[rowIndex]?.cells[columnIndex] ?? null;
|
|
4911
5043
|
}
|
|
5044
|
+
/**
|
|
5045
|
+
* Insert a row, defaulting to the bottom of the table. Returns the new {@link TableRow}.
|
|
5046
|
+
*
|
|
5047
|
+
* The new row is auto-height (`a:tr/@h="0"`) and its cells are empty. Inserting *through*
|
|
5048
|
+
* a vertical merge extends that merge rather than interrupting it: the span's origin
|
|
5049
|
+
* grows by a row and the new cell joins as a continuation, because an origin claiming
|
|
5050
|
+
* more rows than it has continuations is a table PowerPoint reports as corrupt.
|
|
5051
|
+
* @param {number} [index] - where to insert, `0`..`rowCount`; appended when omitted
|
|
5052
|
+
* @throws {InvalidOptionError} when `index` is outside that range
|
|
5053
|
+
*/
|
|
5054
|
+
addRow(index) {
|
|
5055
|
+
const tr = insertRow(this.tbl, index);
|
|
5056
|
+
this.part.markDirty();
|
|
5057
|
+
return new TableRow(tr, this.part, this.themeColors, this.#styleContext(), rowsOf(this.tbl).indexOf(tr), this.rels);
|
|
5058
|
+
}
|
|
5059
|
+
/**
|
|
5060
|
+
* Remove the row at `index`, with its content.
|
|
5061
|
+
*
|
|
5062
|
+
* A cell in the row that *continues* a vertical merge shortens that merge. A cell that
|
|
5063
|
+
* *starts* one hands the region to its first continuation, which becomes the new origin —
|
|
5064
|
+
* the merged region survives one row shorter, and only the removed row's own text is lost.
|
|
5065
|
+
* @throws {InvalidOptionError} when `index` is out of range
|
|
5066
|
+
*/
|
|
5067
|
+
removeRow(index) {
|
|
5068
|
+
removeRow(this.tbl, index);
|
|
5069
|
+
this.part.markDirty();
|
|
5070
|
+
}
|
|
5071
|
+
/**
|
|
5072
|
+
* Insert a column, defaulting to the right-hand end. Updates `a:tblGrid` **and** every
|
|
5073
|
+
* row, which is the pair that has to stay in step.
|
|
5074
|
+
*
|
|
5075
|
+
* The mirror of {@link addRow}'s merge case: inserting inside a horizontal merge widens
|
|
5076
|
+
* it instead of splitting it.
|
|
5077
|
+
* @param {number} [index] - where to insert, `0`..`columnCount`; appended when omitted
|
|
5078
|
+
* @param {number} [widthEmu] - the new column's width; defaults to 1 inch
|
|
5079
|
+
* @throws {InvalidOptionError} when `index` is outside that range
|
|
5080
|
+
*/
|
|
5081
|
+
addColumn(index, widthEmu) {
|
|
5082
|
+
insertColumn(this.tbl, index, widthEmu);
|
|
5083
|
+
this.part.markDirty();
|
|
5084
|
+
}
|
|
5085
|
+
/**
|
|
5086
|
+
* Remove the column at `index`, from `a:tblGrid` and from every row.
|
|
5087
|
+
*
|
|
5088
|
+
* Inside a horizontal merge the region narrows by one and keeps its content: a covered
|
|
5089
|
+
* cell is dropped rather than the origin. Elsewhere the column's cells go with it.
|
|
5090
|
+
* @throws {InvalidOptionError} when `index` is out of range
|
|
5091
|
+
*/
|
|
5092
|
+
removeColumn(index) {
|
|
5093
|
+
removeColumn(this.tbl, index);
|
|
5094
|
+
this.part.markDirty();
|
|
5095
|
+
}
|
|
5096
|
+
/**
|
|
5097
|
+
* Merge the rectangle between two cell positions into one cell. The top-left cell keeps
|
|
5098
|
+
* its content; the rest become covered cells and are emptied, since a covered cell is
|
|
5099
|
+
* never rendered.
|
|
5100
|
+
*
|
|
5101
|
+
* A rectangle that cuts through an existing merge is **rejected**, not silently widened
|
|
5102
|
+
* to fit — the caller asked for a specific region, and quietly producing a different one
|
|
5103
|
+
* is how a layout ends up wrong with nothing to point at. Unmerge first.
|
|
5104
|
+
* @throws {InvalidOptionError} when an index is out of range, the range covers one cell,
|
|
5105
|
+
* or it partially overlaps an existing merge
|
|
5106
|
+
*/
|
|
5107
|
+
mergeCells(row1, col1, row2, col2) {
|
|
5108
|
+
mergeCells(this.tbl, row1, col1, row2, col2);
|
|
5109
|
+
this.part.markDirty();
|
|
5110
|
+
}
|
|
5111
|
+
/**
|
|
5112
|
+
* Split the merged cell whose **origin** is `(row, col)` back into individual cells.
|
|
5113
|
+
* A no-op on a cell that is not merged; addressing a covered cell instead of the origin
|
|
5114
|
+
* throws, and the message names the origin to use.
|
|
5115
|
+
* @throws {InvalidOptionError} when an index is out of range or the cell is a covered cell
|
|
5116
|
+
*/
|
|
5117
|
+
unmergeCell(row, col) {
|
|
5118
|
+
unmergeCell(this.tbl, row, col);
|
|
5119
|
+
this.part.markDirty();
|
|
5120
|
+
}
|
|
4912
5121
|
/** Escape hatch: the underlying `a:tbl` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
4913
5122
|
get element_() {
|
|
4914
5123
|
return this.tbl;
|
|
@@ -4999,6 +5208,183 @@ var TableCell = class {
|
|
|
4999
5208
|
return firstChild(this.tc, "a:tcPr");
|
|
5000
5209
|
}
|
|
5001
5210
|
/**
|
|
5211
|
+
* The cell's `a:tcPr`, creating it when absent.
|
|
5212
|
+
*
|
|
5213
|
+
* `CT_TableCell` sequences `a:txBody` then `a:tcPr` then `a:extLst`, so a newly created
|
|
5214
|
+
* one is inserted before `a:extLst` — appending it blindly would put it after and make
|
|
5215
|
+
* the part invalid. Every setter below goes through here for that reason.
|
|
5216
|
+
*/
|
|
5217
|
+
#getOrAddTcPr() {
|
|
5218
|
+
return getOrAddChild(this.tc, "a:tcPr", ["a:extLst"]);
|
|
5219
|
+
}
|
|
5220
|
+
/**
|
|
5221
|
+
* Set (or clear) the cell's vertical text anchor (`a:tcPr/@anchor`).
|
|
5222
|
+
* `null` removes the attribute, leaving PowerPoint's default of top.
|
|
5223
|
+
* @throws {InvalidOptionError} when the value is outside `ST_TextAnchoringType`
|
|
5224
|
+
*/
|
|
5225
|
+
setAnchor(value) {
|
|
5226
|
+
this.#setTcPrAttr("anchor", value, ANCHOR_VALUES, "table/invalid-cell-anchor");
|
|
5227
|
+
}
|
|
5228
|
+
/**
|
|
5229
|
+
* Set (or clear) the cell's text direction (`a:tcPr/@vert`), e.g. `'vert270'`.
|
|
5230
|
+
* `null` removes the attribute, leaving the default horizontal text.
|
|
5231
|
+
* @throws {InvalidOptionError} when the value is outside `ST_TextVerticalType`
|
|
5232
|
+
*/
|
|
5233
|
+
setVerticalText(value) {
|
|
5234
|
+
this.#setTcPrAttr("vert", value, VERT_VALUES, "table/invalid-cell-vert");
|
|
5235
|
+
}
|
|
5236
|
+
/**
|
|
5237
|
+
* Set (or clear) `a:tcPr/@horzOverflow` — what a single glyph too wide for the cell does.
|
|
5238
|
+
* `null` removes the attribute; so does `'clip'` in effect, since it is the schema
|
|
5239
|
+
* default, but the attribute is still written as asked.
|
|
5240
|
+
* @throws {InvalidOptionError} when the value is outside `ST_TextHorzOverflowType`
|
|
5241
|
+
*/
|
|
5242
|
+
setHorzOverflow(value) {
|
|
5243
|
+
this.#setTcPrAttr("horzOverflow", value, HORZ_OVERFLOW_VALUES, "table/invalid-cell-overflow");
|
|
5244
|
+
}
|
|
5245
|
+
/**
|
|
5246
|
+
* Centre the cell's text block horizontally, or stop doing so (`a:tcPr/@anchorCtr`).
|
|
5247
|
+
* `false` removes the attribute rather than writing `"0"`, since `false` is the schema
|
|
5248
|
+
* default and the two are indistinguishable to a renderer.
|
|
5249
|
+
*/
|
|
5250
|
+
setAnchorCtr(value) {
|
|
5251
|
+
const tcPr = this.#getOrAddTcPr();
|
|
5252
|
+
if (value) setAttr(tcPr, "anchorCtr", "1");
|
|
5253
|
+
else removeAttr(tcPr, "anchorCtr");
|
|
5254
|
+
this.part.markDirty();
|
|
5255
|
+
}
|
|
5256
|
+
/**
|
|
5257
|
+
* Set the cell's text insets in EMU (`a:tcPr/@marL`/`@marR`/`@marT`/`@marB`).
|
|
5258
|
+
*
|
|
5259
|
+
* Partial: only the sides named are touched, so `{ left: 0 }` flushes the text left and
|
|
5260
|
+
* leaves the other three alone. A side given as `null` has its attribute removed, which
|
|
5261
|
+
* returns it to the schema default (91440 EMU left/right, 45720 top/bottom) — not to
|
|
5262
|
+
* zero. Pass `{}` to change nothing.
|
|
5263
|
+
* @throws {InvalidOptionError} when a value is not a finite number
|
|
5264
|
+
*/
|
|
5265
|
+
setMarginsEmu(margins) {
|
|
5266
|
+
const tcPr = this.#getOrAddTcPr();
|
|
5267
|
+
const sides = [
|
|
5268
|
+
["marL", margins.left],
|
|
5269
|
+
["marR", margins.right],
|
|
5270
|
+
["marT", margins.top],
|
|
5271
|
+
["marB", margins.bottom]
|
|
5272
|
+
];
|
|
5273
|
+
for (const [name, value] of sides) {
|
|
5274
|
+
if (value === void 0) continue;
|
|
5275
|
+
if (value === null) removeAttr(tcPr, name);
|
|
5276
|
+
else setAttr(tcPr, name, String(checkFiniteEmu(value, name, "table/invalid-cell-margin")));
|
|
5277
|
+
}
|
|
5278
|
+
this.part.markDirty();
|
|
5279
|
+
}
|
|
5280
|
+
/**
|
|
5281
|
+
* Set (or clear) one of the cell's six borders — the four edges and the two diagonals.
|
|
5282
|
+
*
|
|
5283
|
+
* `null` removes the element entirely, which is "inherit", and is a different thing from
|
|
5284
|
+
* `{ noFill: true }`, which writes an explicit no-border and suppresses whatever would
|
|
5285
|
+
* otherwise be inherited. The element is inserted at its schema position rather than
|
|
5286
|
+
* appended: `CT_TableCellProperties` is a sequence, and an out-of-order `a:tcPr` is
|
|
5287
|
+
* reported by PowerPoint as a corrupt file rather than as a bad edit.
|
|
5288
|
+
*
|
|
5289
|
+
* Colour is either `color` (6-hex, `#` optional) or `schemeColor` (a theme token); giving
|
|
5290
|
+
* both prefers the token, matching how the read side reports them.
|
|
5291
|
+
* @throws {InvalidOptionError} when the width or colour cannot be written
|
|
5292
|
+
*/
|
|
5293
|
+
setBorder(edge, border) {
|
|
5294
|
+
const qname = EDGE_QNAMES[edge];
|
|
5295
|
+
if (!qname) throw new InvalidOptionError("table/invalid-cell-border", `Unknown table cell edge: ${JSON.stringify(edge)}. Expected one of: ${Object.keys(EDGE_QNAMES).join(", ")}.`);
|
|
5296
|
+
if (border === null) {
|
|
5297
|
+
const tcPr = this.#tcPr();
|
|
5298
|
+
if (!tcPr || !firstChild(tcPr, qname)) return;
|
|
5299
|
+
removeChildrenByQName(tcPr, [qname]);
|
|
5300
|
+
this.part.markDirty();
|
|
5301
|
+
return;
|
|
5302
|
+
}
|
|
5303
|
+
const tcPr = this.#getOrAddTcPr();
|
|
5304
|
+
removeChildrenByQName(tcPr, [qname]);
|
|
5305
|
+
const doc = ownerDocumentOf(tcPr);
|
|
5306
|
+
const ln = createElement(doc, qname);
|
|
5307
|
+
if (border.widthPt !== void 0 && border.widthPt !== null) setAttr(ln, "w", String(checkFiniteEmu(border.widthPt * 12700, "widthPt", "table/invalid-cell-border")));
|
|
5308
|
+
if (border.noFill) ln.appendChild(createElement(doc, "a:noFill"));
|
|
5309
|
+
else if (border.schemeColor || border.color) {
|
|
5310
|
+
const fill = createElement(doc, "a:solidFill");
|
|
5311
|
+
const scheme = border.schemeColor;
|
|
5312
|
+
const clr = createElement(doc, scheme ? "a:schemeClr" : "a:srgbClr");
|
|
5313
|
+
setAttr(clr, "val", scheme ? scheme : normalizeHex(border.color));
|
|
5314
|
+
fill.appendChild(clr);
|
|
5315
|
+
ln.appendChild(fill);
|
|
5316
|
+
}
|
|
5317
|
+
if (border.dash) {
|
|
5318
|
+
const dash = createElement(doc, "a:prstDash");
|
|
5319
|
+
setAttr(dash, "val", border.dash);
|
|
5320
|
+
ln.appendChild(dash);
|
|
5321
|
+
}
|
|
5322
|
+
insertTcPrChild(tcPr, qname, ln);
|
|
5323
|
+
this.part.markDirty();
|
|
5324
|
+
}
|
|
5325
|
+
/**
|
|
5326
|
+
* Replace the cell's fill with a solid colour (`a:tcPr/a:solidFill`), or clear it.
|
|
5327
|
+
*
|
|
5328
|
+
* `null` removes the `a:solidFill` and lets the cell inherit from the table style again.
|
|
5329
|
+
* That is not the same as {@link noFill}, which writes an explicit `a:noFill` and so
|
|
5330
|
+
* suppresses the inherited shading. Any competing fill choice (`a:gradFill`,
|
|
5331
|
+
* `a:blipFill`, …) is dropped first — `EG_FillProperties` admits one.
|
|
5332
|
+
* @throws {InvalidOptionError} when `color` is not a 6-digit hex string
|
|
5333
|
+
*/
|
|
5334
|
+
setFillColor(color) {
|
|
5335
|
+
this.#setFill(color === null ? null : {
|
|
5336
|
+
qname: "a:srgbClr",
|
|
5337
|
+
val: normalizeHex(color)
|
|
5338
|
+
});
|
|
5339
|
+
}
|
|
5340
|
+
/**
|
|
5341
|
+
* Replace the cell's fill with a theme colour token (`a:solidFill/a:schemeClr/@val`), or
|
|
5342
|
+
* clear it. Preferred over {@link setFillColor} when the deck's theme should keep
|
|
5343
|
+
* driving the colour.
|
|
5344
|
+
*/
|
|
5345
|
+
setFillSchemeColor(token) {
|
|
5346
|
+
this.#setFill(token === null ? null : {
|
|
5347
|
+
qname: "a:schemeClr",
|
|
5348
|
+
val: token
|
|
5349
|
+
});
|
|
5350
|
+
}
|
|
5351
|
+
/**
|
|
5352
|
+
* Write an explicit `<a:noFill/>` on the cell — a transparent cell that shows the table
|
|
5353
|
+
* background (or the slide) through. Distinct from `setFillColor(null)`, which removes
|
|
5354
|
+
* the fill and lets the table style's shading apply again.
|
|
5355
|
+
*/
|
|
5356
|
+
noFill() {
|
|
5357
|
+
const tcPr = this.#getOrAddTcPr();
|
|
5358
|
+
removeChildrenByQName(tcPr, FILL_CHOICES);
|
|
5359
|
+
tcPrChild(tcPr, "a:noFill");
|
|
5360
|
+
this.part.markDirty();
|
|
5361
|
+
}
|
|
5362
|
+
/** Set or clear one `a:tcPr` attribute, vetting it against its schema enum first. */
|
|
5363
|
+
#setTcPrAttr(name, value, valid, code) {
|
|
5364
|
+
if (value === null) {
|
|
5365
|
+
const tcPr = this.#tcPr();
|
|
5366
|
+
if (!tcPr) return;
|
|
5367
|
+
removeAttr(tcPr, name);
|
|
5368
|
+
this.part.markDirty();
|
|
5369
|
+
return;
|
|
5370
|
+
}
|
|
5371
|
+
const checked = checkEnumOrThrow(value, valid, name, code);
|
|
5372
|
+
setAttr(this.#getOrAddTcPr(), name, checked);
|
|
5373
|
+
this.part.markDirty();
|
|
5374
|
+
}
|
|
5375
|
+
/** Replace (or remove) the cell's solid fill, keeping `EG_FillProperties` single-valued. */
|
|
5376
|
+
#setFill(color) {
|
|
5377
|
+
if (color === null) {
|
|
5378
|
+
const tcPr = this.#tcPr();
|
|
5379
|
+
if (!tcPr || !firstChild(tcPr, "a:solidFill")) return;
|
|
5380
|
+
removeChildrenByQName(tcPr, ["a:solidFill"]);
|
|
5381
|
+
this.part.markDirty();
|
|
5382
|
+
return;
|
|
5383
|
+
}
|
|
5384
|
+
setSolidFill(this.#getOrAddTcPr(), TCPR_AFTER["a:solidFill"] ?? [], color);
|
|
5385
|
+
this.part.markDirty();
|
|
5386
|
+
}
|
|
5387
|
+
/**
|
|
5002
5388
|
* The cell's solid fill resolved against the table's theme colour context to a
|
|
5003
5389
|
* literal hex — the table-cell counterpart of
|
|
5004
5390
|
* {@link import('./shapes.js').AutoShape.resolvedFill}. The cell's own
|
|
@@ -5020,11 +5406,25 @@ var TableCell = class {
|
|
|
5020
5406
|
*/
|
|
5021
5407
|
get resolvedFill() {
|
|
5022
5408
|
if (!this.themeColors) return null;
|
|
5023
|
-
|
|
5024
|
-
if (tcPr && FILL_CHOICES.some((q) => firstChild(tcPr, q))) return resolveSolidFillColor(tcPr, this.themeColors);
|
|
5409
|
+
if (this.hasOwnFill) return resolveSolidFillColor(this.#tcPr(), this.themeColors);
|
|
5025
5410
|
return this.style ? this.#styleFill() : null;
|
|
5026
5411
|
}
|
|
5027
5412
|
/**
|
|
5413
|
+
* Whether the cell carries a fill of its **own** (`a:tcPr` holds an `EG_FillProperties`
|
|
5414
|
+
* child), as opposed to inheriting one from the table style's header/banding rules.
|
|
5415
|
+
*
|
|
5416
|
+
* This is the flag that disambiguates {@link resolvedFill}, which deliberately reports
|
|
5417
|
+
* either — it answers "what colour does this cell render as", and both sources are valid
|
|
5418
|
+
* answers to that. Anything that has to reproduce the cell rather than describe it needs
|
|
5419
|
+
* to know which: baking an inherited banding colour into a copy makes the copy look right
|
|
5420
|
+
* until someone changes its table style, and then nothing moves. `false` for a cell whose
|
|
5421
|
+
* `a:tcPr` is empty or absent, whatever the style graph would render it as.
|
|
5422
|
+
*/
|
|
5423
|
+
get hasOwnFill() {
|
|
5424
|
+
const tcPr = this.#tcPr();
|
|
5425
|
+
return !!tcPr && FILL_CHOICES.some((q) => firstChild(tcPr, q));
|
|
5426
|
+
}
|
|
5427
|
+
/**
|
|
5028
5428
|
* The cell's picture (image) fill (`a:tcPr/a:blipFill`), or `null` when the cell
|
|
5029
5429
|
* is not image-filled. The cell counterpart of
|
|
5030
5430
|
* {@link import('./shapes.js').AutoShape.pictureFill}: {@link resolvedFill}
|
|
@@ -5045,6 +5445,24 @@ var TableCell = class {
|
|
|
5045
5445
|
return resolveTableCellStyleFill(s.style.element_, s.flags, this.rowIndex, this.colIndex, s.rowCount, s.colCount, s.ctx);
|
|
5046
5446
|
}
|
|
5047
5447
|
/**
|
|
5448
|
+
* The cell's gradient fill (`a:tcPr/a:gradFill`), or `null` when the cell is not
|
|
5449
|
+
* gradient-filled. The cell twin of {@link Table.gradientFill}, and needed for the same
|
|
5450
|
+
* reason {@link pictureFill} is: {@link resolvedFill} reports `null` for every non-solid
|
|
5451
|
+
* choice, so without this a gradient cell is indistinguishable from an unfilled one.
|
|
5452
|
+
*/
|
|
5453
|
+
get gradientFill() {
|
|
5454
|
+
const tcPr = this.#tcPr();
|
|
5455
|
+
return tcPr && this.themeColors ? readGradientFill(tcPr, this.themeColors) : null;
|
|
5456
|
+
}
|
|
5457
|
+
/**
|
|
5458
|
+
* The cell's pattern (hatch) fill (`a:tcPr/a:pattFill`), or `null` when the cell is not
|
|
5459
|
+
* pattern-filled.
|
|
5460
|
+
*/
|
|
5461
|
+
get patternFill() {
|
|
5462
|
+
const tcPr = this.#tcPr();
|
|
5463
|
+
return tcPr && this.themeColors ? readPatternFill(tcPr, this.themeColors) : null;
|
|
5464
|
+
}
|
|
5465
|
+
/**
|
|
5048
5466
|
* The raw `schemeClr` token of the cell's solid fill (`a:tcPr/a:solidFill/a:schemeClr/@val`),
|
|
5049
5467
|
* e.g. `accent1`/`bg1`, or `null` when the fill is absent or an explicit `srgbClr`.
|
|
5050
5468
|
* The resolved literal is {@link resolvedFill}; this is the unresolved reference.
|
|
@@ -5111,6 +5529,88 @@ var TableCell = class {
|
|
|
5111
5529
|
return (tcPr && attr(tcPr, "anchor")) ?? null;
|
|
5112
5530
|
}
|
|
5113
5531
|
/**
|
|
5532
|
+
* Whether the cell's whole text **block** is centred horizontally within it
|
|
5533
|
+
* (`a:tcPr/@anchorCtr`). `false` when unset — that is the schema default, and it is
|
|
5534
|
+
* what PowerPoint writes nothing for.
|
|
5535
|
+
*
|
|
5536
|
+
* Distinct from a paragraph's `align`, which decides where each *line* sits inside the
|
|
5537
|
+
* text block; this decides where the block sits inside the cell.
|
|
5538
|
+
*/
|
|
5539
|
+
get anchorCtr() {
|
|
5540
|
+
const tcPr = this.#tcPr();
|
|
5541
|
+
return tcPr ? attr(tcPr, "anchorCtr") === "1" || attr(tcPr, "anchorCtr") === "true" : false;
|
|
5542
|
+
}
|
|
5543
|
+
/**
|
|
5544
|
+
* The cell's 3-D bevel (`a:tcPr/a:cell3D`), or `null` when it has none.
|
|
5545
|
+
*
|
|
5546
|
+
* `CT_Cell3D` requires an `a:bevel`, so a present `cell3D` always reports a bevel;
|
|
5547
|
+
* `lightRig` is `null` when the cell leaves the scene lighting to the renderer. Sizes are
|
|
5548
|
+
* reported in **points** (the attributes are EMU), matching how the write option takes them.
|
|
5549
|
+
*/
|
|
5550
|
+
get cell3D() {
|
|
5551
|
+
const tcPr = this.#tcPr();
|
|
5552
|
+
const cell3D = tcPr && firstChild(tcPr, "a:cell3D");
|
|
5553
|
+
if (!cell3D) return null;
|
|
5554
|
+
const bevel = firstChild(cell3D, "a:bevel");
|
|
5555
|
+
const rig = firstChild(cell3D, "a:lightRig");
|
|
5556
|
+
const pts = (value) => {
|
|
5557
|
+
const emu = intValue(value);
|
|
5558
|
+
return emu === null ? null : emu / 12700;
|
|
5559
|
+
};
|
|
5560
|
+
return {
|
|
5561
|
+
material: attr(cell3D, "prstMaterial") ?? null,
|
|
5562
|
+
bevel: {
|
|
5563
|
+
preset: bevel ? attr(bevel, "prst") ?? null : null,
|
|
5564
|
+
widthPt: bevel ? pts(attr(bevel, "w")) : null,
|
|
5565
|
+
heightPt: bevel ? pts(attr(bevel, "h")) : null
|
|
5566
|
+
},
|
|
5567
|
+
lightRig: rig ? {
|
|
5568
|
+
rig: attr(rig, "rig") ?? null,
|
|
5569
|
+
dir: attr(rig, "dir") ?? null
|
|
5570
|
+
} : null
|
|
5571
|
+
};
|
|
5572
|
+
}
|
|
5573
|
+
/**
|
|
5574
|
+
* The cell's unique identifier (`a:tc/@id`), or `null` when it has none.
|
|
5575
|
+
*
|
|
5576
|
+
* This is what {@link headerIds} references: a data cell names the header cells that
|
|
5577
|
+
* govern it by their `@id`, which is how a complex table tells a screen reader what a
|
|
5578
|
+
* value means. Unrelated to the shape-level `p:cNvPr/@id` — it is an `xs:string` scoped
|
|
5579
|
+
* to the table, not a slide-wide numeric id.
|
|
5580
|
+
*
|
|
5581
|
+
* **PowerPoint does not write this and strips it on save**, so it will read `null` on
|
|
5582
|
+
* any deck PowerPoint has saved, however it was produced. It is surfaced for decks from
|
|
5583
|
+
* other producers. There is deliberately no write-API counterpart — see
|
|
5584
|
+
* `test/read/fixtures/authoring/probe-table-cell-a11y-and-3d.ps1` for the measurement.
|
|
5585
|
+
*/
|
|
5586
|
+
get id() {
|
|
5587
|
+
return attr(this.tc, "id") ?? null;
|
|
5588
|
+
}
|
|
5589
|
+
/**
|
|
5590
|
+
* The `@id`s of the header cells that govern this cell
|
|
5591
|
+
* (`a:tcPr/a:headers/a:header/@val`), in document order. Empty when the cell declares no
|
|
5592
|
+
* header association — which is the common case, since only a complex table needs one.
|
|
5593
|
+
*
|
|
5594
|
+
* Carries the same caveat as {@link id}: PowerPoint strips `a:headers` on save, so this
|
|
5595
|
+
* is empty on any PowerPoint-saved deck. `Table.firstRowHeader` (`a:tblPr/@firstRow`) is
|
|
5596
|
+
* the header marker PowerPoint does keep.
|
|
5597
|
+
*/
|
|
5598
|
+
get headerIds() {
|
|
5599
|
+
const tcPr = this.#tcPr();
|
|
5600
|
+
const headers = tcPr && firstChild(tcPr, "a:headers");
|
|
5601
|
+
if (!headers) return [];
|
|
5602
|
+
return getElements(headers, "a:header").map((header) => attr(header, "val")).filter((val) => val !== null && val !== "");
|
|
5603
|
+
}
|
|
5604
|
+
/**
|
|
5605
|
+
* How the cell treats a glyph too wide for its text width (`a:tcPr/@horzOverflow`):
|
|
5606
|
+
* `'clip'` cuts it at the cell edge, `'overflow'` lets it draw past. `null` when unset
|
|
5607
|
+
* (PowerPoint clips). Not a wrap flag — cell text always wraps to the column width.
|
|
5608
|
+
*/
|
|
5609
|
+
get horzOverflow() {
|
|
5610
|
+
const tcPr = this.#tcPr();
|
|
5611
|
+
return (tcPr && attr(tcPr, "horzOverflow")) ?? null;
|
|
5612
|
+
}
|
|
5613
|
+
/**
|
|
5114
5614
|
* The cell's text insets in EMU (`a:tcPr/@marL`/`@marR`/`@marT`/`@marB`), or
|
|
5115
5615
|
* `null` when the cell sets none. Each side is `null` when only some are set.
|
|
5116
5616
|
*/
|
|
@@ -5280,6 +5780,30 @@ var GroupShape = class extends Shape {
|
|
|
5280
5780
|
"p:graphicFrame"
|
|
5281
5781
|
]);
|
|
5282
5782
|
}
|
|
5783
|
+
/**
|
|
5784
|
+
* The group's own child coordinate space (`p:grpSpPr/a:xfrm/a:chOff` and
|
|
5785
|
+
* `a:chExt`) in EMU, or `null` when the group has no transform or either
|
|
5786
|
+
* element is incomplete.
|
|
5787
|
+
*
|
|
5788
|
+
* Most consumers should not need this: {@link Shape.absoluteFrame} already
|
|
5789
|
+
* composes the whole ancestor chain — child space, group flips and rotations —
|
|
5790
|
+
* and doing that arithmetic per consumer is how a shape in a nested group ends
|
|
5791
|
+
* up subtly displaced. It is here for a *replica* consumer rather than a
|
|
5792
|
+
* *paint* one: rebuilding this group and its children as OOXML needs the
|
|
5793
|
+
* source child space to reproduce the scaling, and without it only groups whose
|
|
5794
|
+
* child space is the identity can be rebuilt.
|
|
5795
|
+
*/
|
|
5796
|
+
get childFrame() {
|
|
5797
|
+
const xfrm = this.xfrm();
|
|
5798
|
+
const box = xfrm && readBox(xfrm, "a:chOff", "a:chExt");
|
|
5799
|
+
if (!box) return null;
|
|
5800
|
+
return {
|
|
5801
|
+
offsetX: box.x,
|
|
5802
|
+
offsetY: box.y,
|
|
5803
|
+
extentX: box.cx,
|
|
5804
|
+
extentY: box.cy
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5283
5807
|
/** The shapes nested directly inside this group, in document order. */
|
|
5284
5808
|
get shapes() {
|
|
5285
5809
|
return buildShapes(this.element, this.slide);
|
|
@@ -5360,6 +5884,6 @@ function buildShapes(parent, slide) {
|
|
|
5360
5884
|
return shapes;
|
|
5361
5885
|
}
|
|
5362
5886
|
//#endregion
|
|
5363
|
-
export {
|
|
5887
|
+
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 };
|
|
5364
5888
|
|
|
5365
|
-
//# sourceMappingURL=shapes-
|
|
5889
|
+
//# sourceMappingURL=shapes-BWifcHDV.js.map
|