@shbernal/ts-pptx 3.3.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{autopage-D1sqK6j7.js → autopage-BYlTzX-l.js} +8 -5
- package/dist/autopage-BYlTzX-l.js.map +1 -0
- package/dist/browser.d.ts +5 -5
- package/dist/browser.js +11 -11
- package/dist/browser.js.map +1 -1
- package/dist/{diagnostics-B2yWpcjB.d.ts → diagnostics-BDuR8UkG.d.ts} +39 -7
- package/dist/diagnostics-BDuR8UkG.d.ts.map +1 -0
- package/dist/{diagnostics-H1QzgRSw.js → diagnostics-CPVADSL1.js} +29 -3
- package/dist/{diagnostics-H1QzgRSw.js.map → diagnostics-CPVADSL1.js.map} +1 -1
- package/dist/{errors-fB1KDJKQ.d.ts → errors-CgnueTYL.d.ts} +3 -3
- package/dist/{errors-fB1KDJKQ.d.ts.map → errors-CgnueTYL.d.ts.map} +1 -1
- package/dist/{fit-CYJ1jkQI.js → fit-TjpNbMru.js} +3 -3
- package/dist/{fit-CYJ1jkQI.js.map → fit-TjpNbMru.js.map} +1 -1
- package/dist/{html-dom-CzewEdE1.js → html-dom-yUSdCExA.js} +5 -5
- package/dist/html-dom-yUSdCExA.js.map +1 -0
- package/dist/html.d.ts +3 -3
- package/dist/html.js +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +16 -33
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts +28 -4
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +43 -10
- package/dist/inspect.js.map +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/measure.d.ts +2 -2
- package/dist/measure.js +1 -1
- package/dist/{st-enums-bVnezkMJ.js → namespaces-CcKuzeuE.js} +291 -200
- package/dist/namespaces-CcKuzeuE.js.map +1 -0
- package/dist/node.d.ts +5 -5
- package/dist/node.js +7 -19
- package/dist/node.js.map +1 -1
- package/dist/{notes-SCvpJz0l.js → notes-DNqAggXx.js} +221 -140
- package/dist/notes-DNqAggXx.js.map +1 -0
- package/dist/{pres-Cb-1xDne.d.ts → pres-Dybf7ZUl.d.ts} +2 -2
- package/dist/{pres-Cb-1xDne.d.ts.map → pres-Dybf7ZUl.d.ts.map} +1 -1
- package/dist/{presentation-DeKTJGHf.js → presentation-VfJ_FT7E.js} +306 -234
- package/dist/presentation-VfJ_FT7E.js.map +1 -0
- package/dist/{presentation-types-B50aOUFI.d.ts → presentation-types-CpkTx9Xa.d.ts} +352 -16
- package/dist/presentation-types-CpkTx9Xa.d.ts.map +1 -0
- package/dist/read.d.ts +4 -4
- package/dist/read.js +3 -3
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +128 -63
- package/dist/script.js.map +1 -1
- package/dist/{shapes-BXVgsk0F.js → shapes-Kt51vry6.js} +717 -54
- package/dist/shapes-Kt51vry6.js.map +1 -0
- package/dist/{slide-DWw9UoI6.d.ts → slide-B_aInw3N.d.ts} +208 -180
- package/dist/slide-B_aInw3N.d.ts.map +1 -0
- package/dist/{text-uM-zGton.js → text-D04NUMvA.js} +3563 -3437
- package/dist/text-D04NUMvA.js.map +1 -0
- package/dist/{units-internal-CGf4VrXy.js → units-internal-DNvK2I6o.js} +54 -34
- package/dist/{units-internal-CGf4VrXy.js.map → units-internal-DNvK2I6o.js.map} +1 -1
- package/dist/zip.d.ts +1 -1
- package/package.json +4 -4
- package/dist/autopage-D1sqK6j7.js.map +0 -1
- package/dist/diagnostics-B2yWpcjB.d.ts.map +0 -1
- package/dist/html-dom-CzewEdE1.js.map +0 -1
- package/dist/notes-SCvpJz0l.js.map +0 -1
- package/dist/presentation-DeKTJGHf.js.map +0 -1
- package/dist/presentation-types-B50aOUFI.d.ts.map +0 -1
- package/dist/shapes-BXVgsk0F.js.map +0 -1
- package/dist/slide-DWw9UoI6.d.ts.map +0 -1
- package/dist/st-enums-bVnezkMJ.js.map +0 -1
- package/dist/text-uM-zGton.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { r as warn } from "./diagnostics-CPVADSL1.js";
|
|
2
2
|
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-Bvln0H-7.js";
|
|
3
|
-
import {
|
|
3
|
+
import { M as TEXT_HORZ_OVERFLOW, N as TEXT_VERTICAL, P as checkEnumOrThrow, a as NOTES_MASTER_REL, c as OFFICE_DOCUMENT_REL, h as SLIDE_MASTER_REL, j as TEXT_ANCHORS, p as SLIDE_LAYOUT_REL, r as IMAGE_REL, t as OOXML_NS, v as THEME_REL, x as getImageSizeFromBytes, y as fitSrcRectPercents } from "./namespaces-CcKuzeuE.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) {
|
|
@@ -14,24 +14,6 @@ function parseXml(xml) {
|
|
|
14
14
|
function serializeXml(doc) {
|
|
15
15
|
return new XMLSerializer().serializeToString(doc);
|
|
16
16
|
}
|
|
17
|
-
/** Canonical OOXML prefix → namespace URI registry. */
|
|
18
|
-
const OOXML_NS = Object.freeze({
|
|
19
|
-
a: "http://schemas.openxmlformats.org/drawingml/2006/main",
|
|
20
|
-
c: "http://schemas.openxmlformats.org/drawingml/2006/chart",
|
|
21
|
-
cp: "http://schemas.openxmlformats.org/package/2006/metadata/core-properties",
|
|
22
|
-
ct: "http://schemas.openxmlformats.org/package/2006/content-types",
|
|
23
|
-
cx: "http://schemas.microsoft.com/office/drawing/2014/chartex",
|
|
24
|
-
dc: "http://purl.org/dc/elements/1.1/",
|
|
25
|
-
dcterms: "http://purl.org/dc/terms/",
|
|
26
|
-
dgm: "http://schemas.openxmlformats.org/drawingml/2006/diagram",
|
|
27
|
-
ep: "http://schemas.openxmlformats.org/officeDocument/2006/extended-properties",
|
|
28
|
-
mc: "http://schemas.openxmlformats.org/markup-compatibility/2006",
|
|
29
|
-
p: "http://schemas.openxmlformats.org/presentationml/2006/main",
|
|
30
|
-
p14: "http://schemas.microsoft.com/office/powerpoint/2010/main",
|
|
31
|
-
p188: "http://schemas.microsoft.com/office/powerpoint/2018/8/main",
|
|
32
|
-
pr: "http://schemas.openxmlformats.org/package/2006/relationships",
|
|
33
|
-
r: "http://schemas.openxmlformats.org/officeDocument/2006/relationships"
|
|
34
|
-
});
|
|
35
17
|
function splitQName(qname) {
|
|
36
18
|
const colon = qname.indexOf(":");
|
|
37
19
|
if (colon < 0) throw new InvalidOptionError("oxml/invalid-qname", `Expected a prefixed qname like "p:sld", got: ${qname}`);
|
|
@@ -686,6 +668,94 @@ function intAttr(el, name) {
|
|
|
686
668
|
return Number.isFinite(n) ? n : null;
|
|
687
669
|
}
|
|
688
670
|
//#endregion
|
|
671
|
+
//#region src/read/oxml/slide-dom.ts
|
|
672
|
+
/**
|
|
673
|
+
* The `p:cSld` of a slide/layout/master/notes part root.
|
|
674
|
+
*
|
|
675
|
+
* Takes a nullable root and returns a nullable element so a caller can chain from
|
|
676
|
+
* `part?.dom.documentElement` without a guard of its own — which is what a dozen callers were
|
|
677
|
+
* writing by hand, each in a slightly different spelling (`root && …`, `root ? … : null`).
|
|
678
|
+
*/
|
|
679
|
+
function cSldOf(root) {
|
|
680
|
+
return root ? firstChild(root, "p:cSld") : null;
|
|
681
|
+
}
|
|
682
|
+
/** The `p:spTree` of a slide/layout/master/notes part root, through its `p:cSld`. */
|
|
683
|
+
function spTreeOf(root) {
|
|
684
|
+
const cSld = cSldOf(root);
|
|
685
|
+
return cSld ? firstChild(cSld, "p:spTree") : null;
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* The `p:nvPr` of a shape, whichever `*nvPr` wrapper its kind uses.
|
|
689
|
+
*
|
|
690
|
+
* The wrapper's name varies by shape kind — `p:nvSpPr` on a `p:sp`, `p:nvPicPr` on a `p:pic`,
|
|
691
|
+
* `p:nvGraphicFramePr`, `p:nvCxnSpPr`, `p:nvGrpSpPr` — but it is always the shape's first child
|
|
692
|
+
* element, and `p:nvPr` is always inside it. Taking the first child element rather than naming
|
|
693
|
+
* the wrapper is what lets one helper serve every kind; the callers that named `p:nvSpPr`
|
|
694
|
+
* silently returned null on a picture.
|
|
695
|
+
*/
|
|
696
|
+
function nvPrOf(shape) {
|
|
697
|
+
const nv = firstChildElement(shape);
|
|
698
|
+
return nv ? firstChild(nv, "p:nvPr") : null;
|
|
699
|
+
}
|
|
700
|
+
/** The `p:cSld@name` of a slide/layout/master part (`''` when absent). */
|
|
701
|
+
function cSldName(part) {
|
|
702
|
+
const cSld = cSldOf(part?.dom.documentElement);
|
|
703
|
+
return (cSld && attr(cSld, "name")) ?? "";
|
|
704
|
+
}
|
|
705
|
+
/** Whether `el` is a `p:spTree`'s own group properties (`p:nvGrpSpPr`/`p:grpSpPr`), not a shape. */
|
|
706
|
+
function isSpTreeProperty(el) {
|
|
707
|
+
return el.namespaceURI === OOXML_NS.p && (el.localName === "nvGrpSpPr" || el.localName === "grpSpPr");
|
|
708
|
+
}
|
|
709
|
+
/** Whether a `p:spTree` child is a placeholder shape (its `*nvPr` carries a `p:ph`). */
|
|
710
|
+
function isPlaceholderShape(shape) {
|
|
711
|
+
const nvPr = nvPrOf(shape);
|
|
712
|
+
return !!(nvPr && firstChild(nvPr, "p:ph"));
|
|
713
|
+
}
|
|
714
|
+
/** The decorative shapes on a layout/master `p:spTree`: every shape child except placeholders. */
|
|
715
|
+
function carriedDecorations(root) {
|
|
716
|
+
const spTree = spTreeOf(root);
|
|
717
|
+
if (!spTree) return [];
|
|
718
|
+
const out = [];
|
|
719
|
+
for (let node = spTree.firstChild; node; node = node.nextSibling) {
|
|
720
|
+
if (node.nodeType !== 1) continue;
|
|
721
|
+
const el = node;
|
|
722
|
+
if (isSpTreeProperty(el) || isPlaceholderShape(el)) continue;
|
|
723
|
+
out.push(el);
|
|
724
|
+
}
|
|
725
|
+
return out;
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* The `n`-th shape child of a `p:spTree` in document (z-)order, skipping the
|
|
729
|
+
* tree's own `nvGrpSpPr`/`grpSpPr` and any trailing `p:extLst`. Returns `null`
|
|
730
|
+
* when `n` is past the last shape (the caller then appends).
|
|
731
|
+
*/
|
|
732
|
+
function nthShapeChild(spTree, n) {
|
|
733
|
+
let i = 0;
|
|
734
|
+
for (let node = spTree.firstChild; node; node = node.nextSibling) {
|
|
735
|
+
if (node.nodeType !== 1) continue;
|
|
736
|
+
const el = node;
|
|
737
|
+
if (isSpTreeProperty(el)) continue;
|
|
738
|
+
if (el.namespaceURI === OOXML_NS.p && el.localName === "extLst") continue;
|
|
739
|
+
if (i === n) return el;
|
|
740
|
+
i++;
|
|
741
|
+
}
|
|
742
|
+
return null;
|
|
743
|
+
}
|
|
744
|
+
/** The first shape child of a `p:spTree` (skipping `nvGrpSpPr`/`grpSpPr`), or `null`. */
|
|
745
|
+
function firstShapeChild(spTree) {
|
|
746
|
+
for (let node = spTree.firstChild; node; node = node.nextSibling) {
|
|
747
|
+
if (node.nodeType !== 1) continue;
|
|
748
|
+
const el = node;
|
|
749
|
+
if (!isSpTreeProperty(el)) return el;
|
|
750
|
+
}
|
|
751
|
+
return null;
|
|
752
|
+
}
|
|
753
|
+
/** Collect `node` and all its descendant elements (document order) into `out`. */
|
|
754
|
+
function collectElements(node, out) {
|
|
755
|
+
out.push(node);
|
|
756
|
+
for (let child = node.firstChild; child; child = child.nextSibling) if (child.nodeType === 1) collectElements(child, out);
|
|
757
|
+
}
|
|
758
|
+
//#endregion
|
|
689
759
|
//#region src/read/oxml/placeholder-inherit.ts
|
|
690
760
|
/**
|
|
691
761
|
* Resolving what a placeholder shape *inherits* from its source layout/master chain.
|
|
@@ -733,8 +803,7 @@ const RUN_PROP_NAMES = [
|
|
|
733
803
|
];
|
|
734
804
|
/** The `p:ph` element of a shape (`p:sp/p:nvSpPr/p:nvPr/p:ph`), or `null`. */
|
|
735
805
|
function placeholderOf(sp) {
|
|
736
|
-
const
|
|
737
|
-
const nvPr = nvSpPr && firstChild(nvSpPr, "p:nvPr");
|
|
806
|
+
const nvPr = nvPrOf(sp);
|
|
738
807
|
return nvPr ? firstChild(nvPr, "p:ph") : null;
|
|
739
808
|
}
|
|
740
809
|
/**
|
|
@@ -2460,6 +2529,16 @@ var Paragraph = class {
|
|
|
2460
2529
|
}
|
|
2461
2530
|
return out;
|
|
2462
2531
|
}
|
|
2532
|
+
/**
|
|
2533
|
+
* Replace **this paragraph's** content with a single run, preserving the character
|
|
2534
|
+
* formatting (`a:rPr`) of its first existing run and leaving its `a:pPr` (level,
|
|
2535
|
+
* alignment, bullet) alone. Sibling paragraphs are untouched — the difference from
|
|
2536
|
+
* {@link TextFrame.text}, which collapses the whole body to one paragraph.
|
|
2537
|
+
*/
|
|
2538
|
+
set text(value) {
|
|
2539
|
+
setParagraphText(this.element, value);
|
|
2540
|
+
this.part.markDirty();
|
|
2541
|
+
}
|
|
2463
2542
|
/** Escape hatch: the underlying `a:p` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
2464
2543
|
get element_() {
|
|
2465
2544
|
return this.element;
|
|
@@ -2603,8 +2682,6 @@ function setTextBodyText(txBody, value) {
|
|
|
2603
2682
|
const doc = txBody.ownerDocument;
|
|
2604
2683
|
if (!doc) throw new InternalError("oxml/node-has-no-document", "Cannot set text: text body has no owner document");
|
|
2605
2684
|
const paragraphs = getElements(txBody, "a:p");
|
|
2606
|
-
const firstRun = paragraphs[0] && firstChild(paragraphs[0], "a:r");
|
|
2607
|
-
const rPrTemplate = firstRun && firstChild(firstRun, "a:rPr");
|
|
2608
2685
|
for (let i = paragraphs.length - 1; i >= 1; i--) {
|
|
2609
2686
|
const extra = paragraphs[i];
|
|
2610
2687
|
if (extra) txBody.removeChild(extra);
|
|
@@ -2614,6 +2691,21 @@ function setTextBodyText(txBody, value) {
|
|
|
2614
2691
|
p = createElement(doc, "a:p");
|
|
2615
2692
|
txBody.appendChild(p);
|
|
2616
2693
|
}
|
|
2694
|
+
setParagraphText(p, value);
|
|
2695
|
+
}
|
|
2696
|
+
/**
|
|
2697
|
+
* Replace **one paragraph's** content with a single run, preserving the `a:rPr` of that
|
|
2698
|
+
* paragraph's first existing run and leaving its `a:pPr` (level, alignment, bullet) alone.
|
|
2699
|
+
* Sibling paragraphs are untouched, which is the whole difference from
|
|
2700
|
+
* {@link setTextBodyText} and the reason this exists: a SmartArt drawing cache packs several
|
|
2701
|
+
* nodes' text into one `dsp:txBody`, so collapsing the body there would delete the other
|
|
2702
|
+
* nodes' strings. Does **not** mark any part dirty — the caller owns the `Part`.
|
|
2703
|
+
*/
|
|
2704
|
+
function setParagraphText(p, value) {
|
|
2705
|
+
const doc = p.ownerDocument;
|
|
2706
|
+
if (!doc) throw new InternalError("oxml/node-has-no-document", "Cannot set text: paragraph has no owner document");
|
|
2707
|
+
const firstRun = firstChild(p, "a:r");
|
|
2708
|
+
const rPrTemplate = firstRun && firstChild(firstRun, "a:rPr");
|
|
2617
2709
|
for (const child of childElements(p)) if (child.localName === "r" || child.localName === "br" || child.localName === "fld") p.removeChild(child);
|
|
2618
2710
|
const run = createElement(doc, "a:r");
|
|
2619
2711
|
if (rPrTemplate) run.appendChild(rPrTemplate.cloneNode(true));
|
|
@@ -2649,6 +2741,21 @@ var Shape = class {
|
|
|
2649
2741
|
this.host = host;
|
|
2650
2742
|
}
|
|
2651
2743
|
/**
|
|
2744
|
+
* The transform element carrying this shape's geometry, or `null` if inherited.
|
|
2745
|
+
*
|
|
2746
|
+
* Defaults to `p:spPr/a:xfrm`, which is where three of the four shape kinds keep it
|
|
2747
|
+
* (`p:sp`, `p:pic`, `p:cxnSp`). {@link GraphicFrame} overrides it: a `p:graphicFrame`
|
|
2748
|
+
* has no `p:spPr` and carries a `p:xfrm` of its own directly.
|
|
2749
|
+
*/
|
|
2750
|
+
xfrm() {
|
|
2751
|
+
const spPr = firstChild(this.element, "p:spPr");
|
|
2752
|
+
return spPr ? firstChild(spPr, "a:xfrm") : null;
|
|
2753
|
+
}
|
|
2754
|
+
/** The transform element, creating it (and its container) in document order if absent. */
|
|
2755
|
+
getOrAddXfrm() {
|
|
2756
|
+
return getOrAddSpPrXfrm(this.element);
|
|
2757
|
+
}
|
|
2758
|
+
/**
|
|
2652
2759
|
* Mark the owning host's part dirty so `save()` reserializes it. Public
|
|
2653
2760
|
* because {@link element_} hands out the live DOM node: the hatch and the
|
|
2654
2761
|
* obligation that comes with it belong on the same object.
|
|
@@ -3366,13 +3473,6 @@ var Shape = class {
|
|
|
3366
3473
|
/** An auto shape, text box, or placeholder (`p:sp`). The only kind that holds text. */
|
|
3367
3474
|
var AutoShape = class extends Shape {
|
|
3368
3475
|
shapeType = "autoShape";
|
|
3369
|
-
xfrm() {
|
|
3370
|
-
const spPr = firstChild(this.element, "p:spPr");
|
|
3371
|
-
return spPr ? firstChild(spPr, "a:xfrm") : null;
|
|
3372
|
-
}
|
|
3373
|
-
getOrAddXfrm() {
|
|
3374
|
-
return getOrAddSpPrXfrm(this.element);
|
|
3375
|
-
}
|
|
3376
3476
|
get hasTextFrame() {
|
|
3377
3477
|
return firstChild(this.element, "p:txBody") !== null;
|
|
3378
3478
|
}
|
|
@@ -3394,8 +3494,7 @@ var AutoShape = class extends Shape {
|
|
|
3394
3494
|
* PowerPoint does. Use {@link Slide.placeholder} to find a placeholder by type.
|
|
3395
3495
|
*/
|
|
3396
3496
|
get placeholder() {
|
|
3397
|
-
const
|
|
3398
|
-
const nvPr = nvSpPr && firstChild(nvSpPr, "p:nvPr");
|
|
3497
|
+
const nvPr = nvPrOf(this.element);
|
|
3399
3498
|
const ph = nvPr && firstChild(nvPr, "p:ph");
|
|
3400
3499
|
return ph ? {
|
|
3401
3500
|
type: attr(ph, "type"),
|
|
@@ -3470,13 +3569,6 @@ function recolorColorOf(color) {
|
|
|
3470
3569
|
/** A picture (`p:pic`). */
|
|
3471
3570
|
var Picture = class extends Shape {
|
|
3472
3571
|
shapeType = "picture";
|
|
3473
|
-
xfrm() {
|
|
3474
|
-
const spPr = firstChild(this.element, "p:spPr");
|
|
3475
|
-
return spPr ? firstChild(spPr, "a:xfrm") : null;
|
|
3476
|
-
}
|
|
3477
|
-
getOrAddXfrm() {
|
|
3478
|
-
return getOrAddSpPrXfrm(this.element);
|
|
3479
|
-
}
|
|
3480
3572
|
get supportsFill() {
|
|
3481
3573
|
return false;
|
|
3482
3574
|
}
|
|
@@ -3719,13 +3811,6 @@ var Picture = class extends Shape {
|
|
|
3719
3811
|
/** A connector / line (`p:cxnSp`). */
|
|
3720
3812
|
var Connector = class extends Shape {
|
|
3721
3813
|
shapeType = "connector";
|
|
3722
|
-
xfrm() {
|
|
3723
|
-
const spPr = firstChild(this.element, "p:spPr");
|
|
3724
|
-
return spPr ? firstChild(spPr, "a:xfrm") : null;
|
|
3725
|
-
}
|
|
3726
|
-
getOrAddXfrm() {
|
|
3727
|
-
return getOrAddSpPrXfrm(this.element);
|
|
3728
|
-
}
|
|
3729
3814
|
/**
|
|
3730
3815
|
* The connector's **start**-point shape binding (`p:nvCxnSpPr/p:cNvCxnSpPr/a:stCxn`),
|
|
3731
3816
|
* or `null` when the start point is unbound (a bare `p:cNvCxnSpPr`, i.e. a
|
|
@@ -4405,6 +4490,548 @@ function floatValue(value) {
|
|
|
4405
4490
|
return Number.isFinite(number) ? number : null;
|
|
4406
4491
|
}
|
|
4407
4492
|
//#endregion
|
|
4493
|
+
//#region src/read/api/diagram.ts
|
|
4494
|
+
/**
|
|
4495
|
+
* Read proxies for a SmartArt diagram (`dgm:dataModel`) hosted in a `p:graphicFrame`.
|
|
4496
|
+
*
|
|
4497
|
+
* SmartArt is a four-part construct, and the frame itself holds none of it: the
|
|
4498
|
+
* `a:graphicData` carries only a `dgm:relIds` with four relationship ids pointing at the
|
|
4499
|
+
* data, layout, quick-style and colours parts under `/ppt/diagrams/`. The **data** part is
|
|
4500
|
+
* the one with content in it — a flat `dgm:ptLst` of points plus a `dgm:cxnLst` of
|
|
4501
|
+
* connections that give the points their tree — and it is what this surface decodes. The
|
|
4502
|
+
* other three are generated presets (`urn:microsoft.com/office/officeart/…`) describing how
|
|
4503
|
+
* PowerPoint *draws* the graph rather than what it says; they surface as parts, not getters.
|
|
4504
|
+
*
|
|
4505
|
+
* The text is ordinary DrawingML: `dgm:pt/dgm:t` is an `a:CT_TextBody`, the same type a
|
|
4506
|
+
* shape's `p:txBody` is, so a point's text reads back through the same
|
|
4507
|
+
* {@link TextFrame}/`Paragraph`/`Run` proxies as any other text on the slide, run formatting
|
|
4508
|
+
* included.
|
|
4509
|
+
*
|
|
4510
|
+
* **Not every point is content.** A saved data model interleaves the user's nodes with
|
|
4511
|
+
* points the layout engine generated (`pres`), the diagram root (`doc`), and the transition
|
|
4512
|
+
* points sitting on each edge (`parTrans`/`sibTrans`, which carry text only where a layout
|
|
4513
|
+
* labels its arrows). {@link Diagram.text} applies exactly that filter; {@link Diagram.points}
|
|
4514
|
+
* applies none, so a consumer that needs the raw graph gets it.
|
|
4515
|
+
*
|
|
4516
|
+
* **The structure is in the connections, and {@link Diagram.nodes} is the view of it.** A
|
|
4517
|
+
* `parOf` edge names the parent in `@srcId`, the child in `@destId` and the child's sibling
|
|
4518
|
+
* position in `@srcOrd`; `points` order is document order and says nothing about depth. Three
|
|
4519
|
+
* decisions that tree makes, each of which could defensibly have gone the other way:
|
|
4520
|
+
*
|
|
4521
|
+
* - **`asst` points are in it.** An assistant is user content, and an org chart merely draws
|
|
4522
|
+
* it off the main branch rather than under it. {@link DiagramPoint.type} carries the
|
|
4523
|
+
* distinction, so folding it silently in with `node` would hide something a consumer can
|
|
4524
|
+
* act on while omitting it would lose authored text.
|
|
4525
|
+
* - **Transition points are not.** A `parTrans`/`sibTrans` labels an *edge*, not a node, and
|
|
4526
|
+
* reaches its edge through {@link DiagramConnection.parentTransitionId} /
|
|
4527
|
+
* `siblingTransitionId`. Hanging one off `children` would make the tree disagree with what
|
|
4528
|
+
* PowerPoint's own text pane shows.
|
|
4529
|
+
* - **A cycle in `parOf` throws.** It is a corrupt model rather than a shape the format can
|
|
4530
|
+
* express, and the walk would not terminate; see `diagram/parent-edge-cycle`.
|
|
4531
|
+
*
|
|
4532
|
+
* **A point's text is stored twice.** The data model is what PowerPoint reads, but a deck
|
|
4533
|
+
* also carries a *drawing* part (`dsp:drawing`) holding a copy of every drawn string, and
|
|
4534
|
+
* every renderer without a SmartArt layout engine — LibreOffice, Google Slides, thumbnailers
|
|
4535
|
+
* — paints that copy and nothing else. {@link DiagramPoint.drawnShape} is the link between
|
|
4536
|
+
* the two, and {@link DiagramPoint.text}'s setter is what keeps them in step.
|
|
4537
|
+
*
|
|
4538
|
+
* Authoring a SmartArt graphic from nothing is out of scope: the layout part is a
|
|
4539
|
+
* constraint-solver program PowerPoint executes, and the presentation tree it generates is
|
|
4540
|
+
* not derivable from the user's content. Editing the text of one that already exists is
|
|
4541
|
+
* supported — see {@link DiagramPoint.text}.
|
|
4542
|
+
*/
|
|
4543
|
+
const POINT_TYPES = [
|
|
4544
|
+
"node",
|
|
4545
|
+
"asst",
|
|
4546
|
+
"doc",
|
|
4547
|
+
"pres",
|
|
4548
|
+
"parTrans",
|
|
4549
|
+
"sibTrans"
|
|
4550
|
+
];
|
|
4551
|
+
const CONNECTION_TYPES = [
|
|
4552
|
+
"parOf",
|
|
4553
|
+
"presOf",
|
|
4554
|
+
"presParOf",
|
|
4555
|
+
"unknownRelationship"
|
|
4556
|
+
];
|
|
4557
|
+
/** The MS extension (`dgm:extLst/a:ext`) whose `@relId` names the fallback drawing part. */
|
|
4558
|
+
const DRAWING_EXT_URI = "http://schemas.microsoft.com/office/drawing/2008/diagram";
|
|
4559
|
+
/**
|
|
4560
|
+
* The node → drawn-shape index of one diagram: the `presOf` edges of the data model and the
|
|
4561
|
+
* `dsp:sp` elements of the drawing part, both keyed for lookup.
|
|
4562
|
+
*
|
|
4563
|
+
* Built once per {@link Diagram}, and only when a point is actually asked what draws it — the
|
|
4564
|
+
* drawing part is a second XML DOM and a consumer reading node text should not pay to parse
|
|
4565
|
+
* it. The consequence of that memo: a consumer who *adds* or *removes* shapes in the drawing
|
|
4566
|
+
* part through the escape hatches must re-reach the diagram to see them here.
|
|
4567
|
+
*/
|
|
4568
|
+
var DrawingIndex = class {
|
|
4569
|
+
drawingPart;
|
|
4570
|
+
themeContext;
|
|
4571
|
+
/** `presOf` arms out of each source point, in `@srcOrd` order. */
|
|
4572
|
+
#arms = /* @__PURE__ */ new Map();
|
|
4573
|
+
/** Each `dsp:sp` by the `pres` modelId it draws. */
|
|
4574
|
+
#shapes = /* @__PURE__ */ new Map();
|
|
4575
|
+
constructor(root, drawingPart, themeContext) {
|
|
4576
|
+
this.drawingPart = drawingPart;
|
|
4577
|
+
this.themeContext = themeContext;
|
|
4578
|
+
const cxnLst = root && firstChild(root, "dgm:cxnLst");
|
|
4579
|
+
for (const cxn of cxnLst ? getElements(cxnLst, "dgm:cxn") : []) {
|
|
4580
|
+
if (attr(cxn, "type") !== "presOf") continue;
|
|
4581
|
+
const srcId = attr(cxn, "srcId");
|
|
4582
|
+
const destId = attr(cxn, "destId");
|
|
4583
|
+
if (!srcId || !destId) continue;
|
|
4584
|
+
const arm = {
|
|
4585
|
+
destId,
|
|
4586
|
+
destinationOrder: intValue(attr(cxn, "destOrd")) ?? 0,
|
|
4587
|
+
sourceOrder: intValue(attr(cxn, "srcOrd")) ?? 0
|
|
4588
|
+
};
|
|
4589
|
+
const list = this.#arms.get(srcId);
|
|
4590
|
+
if (list) list.push(arm);
|
|
4591
|
+
else this.#arms.set(srcId, [arm]);
|
|
4592
|
+
}
|
|
4593
|
+
for (const list of this.#arms.values()) list.sort((a, b) => a.sourceOrder - b.sourceOrder);
|
|
4594
|
+
const drawingRoot = drawingPart?.dom.documentElement ?? null;
|
|
4595
|
+
for (const sp of drawingRoot ? descendantsByTag(drawingRoot, OOXML_NS.dsp, "sp") : []) {
|
|
4596
|
+
const modelId = attr(sp, "modelId");
|
|
4597
|
+
if (modelId !== null && !this.#shapes.has(modelId)) this.#shapes.set(modelId, sp);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
/**
|
|
4601
|
+
* Resolve one authored point to the `pres` point that presents it and to the drawn
|
|
4602
|
+
* paragraph carrying its text.
|
|
4603
|
+
*
|
|
4604
|
+
* A point commonly has **several** `presOf` arms — an org-chart node's box and the
|
|
4605
|
+
* connector beneath it are two presentations of one node — so the arm is chosen by what it
|
|
4606
|
+
* reaches rather than by its `@srcOrd`: the first, in `@srcOrd` order, whose `pres` point
|
|
4607
|
+
* draws a `dsp:sp` that has a `dsp:txBody`. That selector produced a unique hit for all 90
|
|
4608
|
+
* authored points measured across five layout families, and it degrades to "no arm" rather
|
|
4609
|
+
* than to "the wrong arm" on a layout that was not.
|
|
4610
|
+
*/
|
|
4611
|
+
resolve(modelId) {
|
|
4612
|
+
const arms = (modelId === null ? void 0 : this.#arms.get(modelId)) ?? [];
|
|
4613
|
+
const first = arms[0];
|
|
4614
|
+
if (!first) return {
|
|
4615
|
+
presentationId: null,
|
|
4616
|
+
drawnShape: null
|
|
4617
|
+
};
|
|
4618
|
+
for (const arm of arms) {
|
|
4619
|
+
const sp = this.#shapes.get(arm.destId);
|
|
4620
|
+
const txBody = sp && firstChild(sp, "dsp:txBody");
|
|
4621
|
+
if (!sp || !txBody || !this.drawingPart) continue;
|
|
4622
|
+
const paragraph = getElements(txBody, "a:p")[arm.destinationOrder];
|
|
4623
|
+
return {
|
|
4624
|
+
presentationId: arm.destId,
|
|
4625
|
+
drawnShape: paragraph ? {
|
|
4626
|
+
part: this.drawingPart,
|
|
4627
|
+
modelId: arm.destId,
|
|
4628
|
+
paragraphIndex: arm.destinationOrder,
|
|
4629
|
+
textFrame: new TextFrame(txBody, this.drawingPart, this.themeContext),
|
|
4630
|
+
element_: sp
|
|
4631
|
+
} : null
|
|
4632
|
+
};
|
|
4633
|
+
}
|
|
4634
|
+
return {
|
|
4635
|
+
presentationId: first.destId,
|
|
4636
|
+
drawnShape: null
|
|
4637
|
+
};
|
|
4638
|
+
}
|
|
4639
|
+
};
|
|
4640
|
+
/** One point (`dgm:pt`) of a diagram's data model. */
|
|
4641
|
+
var DiagramPoint = class {
|
|
4642
|
+
pt;
|
|
4643
|
+
part;
|
|
4644
|
+
themeContext;
|
|
4645
|
+
relationships;
|
|
4646
|
+
drawing;
|
|
4647
|
+
constructor(pt, part, themeContext, relationships, drawing) {
|
|
4648
|
+
this.pt = pt;
|
|
4649
|
+
this.part = part;
|
|
4650
|
+
this.themeContext = themeContext;
|
|
4651
|
+
this.relationships = relationships;
|
|
4652
|
+
this.drawing = drawing;
|
|
4653
|
+
}
|
|
4654
|
+
/** This point's id (`@modelId`), which connections reference. */
|
|
4655
|
+
get modelId() {
|
|
4656
|
+
return attr(this.pt, "modelId");
|
|
4657
|
+
}
|
|
4658
|
+
/** What kind of point this is (`@type`); `node` when absent, its schema default. */
|
|
4659
|
+
get type() {
|
|
4660
|
+
const raw = attr(this.pt, "type");
|
|
4661
|
+
return raw !== null && POINT_TYPES.includes(raw) ? raw : "node";
|
|
4662
|
+
}
|
|
4663
|
+
/**
|
|
4664
|
+
* For a transition point, the edge it labels (`@cxnId`), or `null`. The schema's default
|
|
4665
|
+
* is the literal `0`, which means "none" rather than naming a point, so it reads `null`.
|
|
4666
|
+
*/
|
|
4667
|
+
get connectionId() {
|
|
4668
|
+
return idAttr(this.pt, "cxnId");
|
|
4669
|
+
}
|
|
4670
|
+
/**
|
|
4671
|
+
* Whether this point is an unfilled prompt (`dgm:prSet/@phldr`) — a node PowerPoint draws
|
|
4672
|
+
* with a grey placeholder string and treats as empty. Excluded from {@link Diagram.text}:
|
|
4673
|
+
* a layout that stores its prompt in `dgm:t` would otherwise extract as content.
|
|
4674
|
+
*/
|
|
4675
|
+
get isPlaceholder() {
|
|
4676
|
+
const prSet = this.#prSet();
|
|
4677
|
+
return prSet ? boolValue(attr(prSet, "phldr")) === true : false;
|
|
4678
|
+
}
|
|
4679
|
+
/** The prompt string a placeholder point displays (`dgm:prSet/@phldrT`), or `null`. */
|
|
4680
|
+
get placeholderText() {
|
|
4681
|
+
const prSet = this.#prSet();
|
|
4682
|
+
return prSet ? attr(prSet, "phldrT") : null;
|
|
4683
|
+
}
|
|
4684
|
+
/**
|
|
4685
|
+
* This point's text body (`dgm:t`) as a {@link TextFrame}, or `null` when the point
|
|
4686
|
+
* carries none. It is an `a:CT_TextBody` like any other, so `paragraphs`, `runs` and the
|
|
4687
|
+
* `resolved*` getters all apply.
|
|
4688
|
+
*
|
|
4689
|
+
* **Writing through this edits the data model only**, and leaves the drawing cache
|
|
4690
|
+
* holding the old string — which is what PowerPoint-less renderers draw. It is the escape
|
|
4691
|
+
* hatch for run-level formatting, so it does exactly what it is told and nothing more;
|
|
4692
|
+
* {@link text}'s setter is the path that mirrors.
|
|
4693
|
+
*/
|
|
4694
|
+
get textFrame() {
|
|
4695
|
+
const t = firstChild(this.pt, "dgm:t");
|
|
4696
|
+
return t ? new TextFrame(t, this.part, this.themeContext, void 0, this.relationships) : null;
|
|
4697
|
+
}
|
|
4698
|
+
/**
|
|
4699
|
+
* The generated `pres` point that presents this one (`dgm:cxn[@type="presOf"]/@destId`),
|
|
4700
|
+
* or `null` when nothing presents it — the normal state of an unlabelled transition point.
|
|
4701
|
+
*
|
|
4702
|
+
* Where a point has several `presOf` arms the one named here is the arm that draws text;
|
|
4703
|
+
* see {@link drawnShape}, which resolves both.
|
|
4704
|
+
*/
|
|
4705
|
+
get presentationId() {
|
|
4706
|
+
return this.drawing?.().resolve(this.modelId).presentationId ?? null;
|
|
4707
|
+
}
|
|
4708
|
+
/**
|
|
4709
|
+
* The paragraph of the fallback drawing part (`dsp:drawing`) that carries this point's
|
|
4710
|
+
* text, or `null` when nothing draws it.
|
|
4711
|
+
*
|
|
4712
|
+
* The mapping runs through the `pres` point rather than directly: a `dsp:sp/@modelId` is
|
|
4713
|
+
* always a `pres` point's id, so a lookup keyed on the authored point's own id finds
|
|
4714
|
+
* nothing at all. It is many-to-one in both directions — one shape draws several points
|
|
4715
|
+
* (`@destOrd` says which paragraph is whose, and it can contradict document order), and
|
|
4716
|
+
* one point has several presentations, of which at most one holds text.
|
|
4717
|
+
*
|
|
4718
|
+
* `null` is a defined outcome, not a failure, and covers four real cases: the package
|
|
4719
|
+
* carries no drawing part; the point has no `presOf` edge (every unlabelled
|
|
4720
|
+
* `parTrans`/`sibTrans`); the `pres` point draws no `dsp:sp`; and the shape it draws has
|
|
4721
|
+
* no `dsp:txBody` (a connector, or a picture node's filled rectangle).
|
|
4722
|
+
*/
|
|
4723
|
+
get drawnShape() {
|
|
4724
|
+
return this.drawing?.().resolve(this.modelId).drawnShape ?? null;
|
|
4725
|
+
}
|
|
4726
|
+
/** This point's text, paragraphs joined by `\n`; `''` when it carries none. */
|
|
4727
|
+
get text() {
|
|
4728
|
+
return this.textFrame?.text ?? "";
|
|
4729
|
+
}
|
|
4730
|
+
/**
|
|
4731
|
+
* Replace this point's text, in the data model **and** in the drawing cache.
|
|
4732
|
+
*
|
|
4733
|
+
* A diagram stores every string twice. `dgm:t` is what PowerPoint reads, and it recomputes
|
|
4734
|
+
* the drawing from the data model on open; the `dsp:drawing` part is what every other
|
|
4735
|
+
* renderer paints, and none of them recompute anything. Writing only the first is an edit
|
|
4736
|
+
* that is invisible in LibreOffice, Google Slides, thumbnailers and web previews until
|
|
4737
|
+
* someone opens the deck in PowerPoint and saves it.
|
|
4738
|
+
*
|
|
4739
|
+
* The data-model write always happens. The mirror is best-effort: when the point resolves
|
|
4740
|
+
* to no drawn paragraph (see {@link drawnShape} for the four ways that occurs) the cache
|
|
4741
|
+
* keeps the old string and a `diagram/drawing-cache-not-updated` diagnostic says so.
|
|
4742
|
+
* Throwing instead would make the common case unusable in order to report the rare one,
|
|
4743
|
+
* and returning a status would be a shape no other text setter here has.
|
|
4744
|
+
*
|
|
4745
|
+
* Two things it deliberately does not do. It does **not** recompute geometry: the drawn
|
|
4746
|
+
* shape keeps its cached position and size, so a much longer string overflows its box in
|
|
4747
|
+
* those same renderers until PowerPoint re-lays the diagram out. And it does not *create*
|
|
4748
|
+
* a place to draw text — a transition point whose layout has no label slot has no
|
|
4749
|
+
* `dsp:sp`, and PowerPoint strips text put on one, so the honest outcome is the
|
|
4750
|
+
* diagnostic. Like every other `text` setter here it collapses the point to one run,
|
|
4751
|
+
* keeping the first run's `a:rPr`; use {@link textFrame} for run-level work.
|
|
4752
|
+
*/
|
|
4753
|
+
set text(value) {
|
|
4754
|
+
const frame = this.textFrame;
|
|
4755
|
+
if (!frame) {
|
|
4756
|
+
warn("diagram/point-has-no-text-body", `SmartArt point ${this.#label()} (type ${this.type}) has no dgm:t, so it is not a point text can be written to; nothing was changed`);
|
|
4757
|
+
return;
|
|
4758
|
+
}
|
|
4759
|
+
frame.text = value;
|
|
4760
|
+
const drawn = this.drawnShape;
|
|
4761
|
+
const paragraph = drawn?.textFrame.paragraphs[drawn.paragraphIndex];
|
|
4762
|
+
if (!drawn || !paragraph) {
|
|
4763
|
+
warn("diagram/drawing-cache-not-updated", `SmartArt point ${this.#label()} resolves to no drawn paragraph, so ${this.part.partName} now holds the new text but the drawing cache still holds the old one; PowerPoint redraws it on open, other renderers do not`);
|
|
4764
|
+
return;
|
|
4765
|
+
}
|
|
4766
|
+
setParagraphText(paragraph.element_, value);
|
|
4767
|
+
drawn.part.markDirty();
|
|
4768
|
+
}
|
|
4769
|
+
/** Escape hatch: the underlying `dgm:pt` element. After mutating it call {@link Diagram.markDirty}. */
|
|
4770
|
+
get element_() {
|
|
4771
|
+
return this.pt;
|
|
4772
|
+
}
|
|
4773
|
+
#prSet() {
|
|
4774
|
+
return firstChild(this.pt, "dgm:prSet");
|
|
4775
|
+
}
|
|
4776
|
+
/** How a diagnostic names this point: its `@modelId`, which is all a corrupt point may have. */
|
|
4777
|
+
#label() {
|
|
4778
|
+
return this.modelId ?? "(no modelId)";
|
|
4779
|
+
}
|
|
4780
|
+
};
|
|
4781
|
+
/**
|
|
4782
|
+
* One user-authored point in its place in the diagram's tree — a `node` or an `asst`, never a
|
|
4783
|
+
* generated `pres` point or a transition label. See {@link Diagram.nodes}.
|
|
4784
|
+
*/
|
|
4785
|
+
var DiagramNode = class {
|
|
4786
|
+
point;
|
|
4787
|
+
parent;
|
|
4788
|
+
level;
|
|
4789
|
+
/** Child nodes, ordered by their `parOf` edge's `@srcOrd`. Empty for a leaf. */
|
|
4790
|
+
children = [];
|
|
4791
|
+
constructor(point, parent, level) {
|
|
4792
|
+
this.point = point;
|
|
4793
|
+
this.parent = parent;
|
|
4794
|
+
this.level = level;
|
|
4795
|
+
}
|
|
4796
|
+
};
|
|
4797
|
+
/** Sort key for a root with no `parOf` edge at all, so it lands after the ordered ones. */
|
|
4798
|
+
const MAX_ORDER = Number.MAX_SAFE_INTEGER;
|
|
4799
|
+
/** A SmartArt graphic frame's diagram, backed by its `dgm:dataModel` part. */
|
|
4800
|
+
var Diagram = class {
|
|
4801
|
+
part;
|
|
4802
|
+
relIds;
|
|
4803
|
+
opc;
|
|
4804
|
+
frameRelationships;
|
|
4805
|
+
themeContext;
|
|
4806
|
+
#relationships;
|
|
4807
|
+
#docPrSetCache;
|
|
4808
|
+
#drawing;
|
|
4809
|
+
constructor(part, relIds, opc, frameRelationships, themeContext) {
|
|
4810
|
+
this.part = part;
|
|
4811
|
+
this.relIds = relIds;
|
|
4812
|
+
this.opc = opc;
|
|
4813
|
+
this.frameRelationships = frameRelationships;
|
|
4814
|
+
this.themeContext = themeContext;
|
|
4815
|
+
}
|
|
4816
|
+
/** Partname of the diagram data part. */
|
|
4817
|
+
get partName() {
|
|
4818
|
+
return this.part.partName;
|
|
4819
|
+
}
|
|
4820
|
+
/**
|
|
4821
|
+
* Every point (`dgm:ptLst/dgm:pt`) in document order, unfiltered — generated `pres`
|
|
4822
|
+
* points and the `doc` root included. Filter on {@link DiagramPoint.type} for the user's
|
|
4823
|
+
* nodes; {@link text} is the shortcut for the common case, {@link nodes} the structured one.
|
|
4824
|
+
*
|
|
4825
|
+
* Built per access like every other read proxy (`docs/reference/pptx-read.md`, "Object
|
|
4826
|
+
* model"), so `points[0] !== points[0]` while both wrap the same `dgm:pt`. Key a `Map`
|
|
4827
|
+
* on {@link DiagramPoint.modelId}, not on the point.
|
|
4828
|
+
*/
|
|
4829
|
+
get points() {
|
|
4830
|
+
const root = this.#root();
|
|
4831
|
+
const ptLst = root && firstChild(root, "dgm:ptLst");
|
|
4832
|
+
if (!ptLst) return [];
|
|
4833
|
+
const relationships = this.#relationships ??= this.opc.relationshipsFor(this.part.partName);
|
|
4834
|
+
const drawing = () => this.#drawingIndex();
|
|
4835
|
+
return getElements(ptLst, "dgm:pt").map((pt) => new DiagramPoint(pt, this.part, this.themeContext, relationships, drawing));
|
|
4836
|
+
}
|
|
4837
|
+
/**
|
|
4838
|
+
* The authored tree — every `node` and `asst` point in its place, roots in `@srcOrd`
|
|
4839
|
+
* order — as a view over {@link points} and {@link connections} rather than a replacement
|
|
4840
|
+
* for either.
|
|
4841
|
+
*
|
|
4842
|
+
* A root is a content point whose `parOf` parent is not itself content, which in a
|
|
4843
|
+
* PowerPoint-authored model means the `doc` point. Transition points are not members; see
|
|
4844
|
+
* this module's header for that decision and the two beside it.
|
|
4845
|
+
*
|
|
4846
|
+
* @throws PackageReadError `diagram/parent-edge-cycle` when the `parOf` edges make a
|
|
4847
|
+
* point its own ancestor, which is a corrupt model rather than a diagram shape.
|
|
4848
|
+
*/
|
|
4849
|
+
get nodes() {
|
|
4850
|
+
const content = /* @__PURE__ */ new Map();
|
|
4851
|
+
for (const point of this.points) {
|
|
4852
|
+
const modelId = point.modelId;
|
|
4853
|
+
if (modelId !== null && (point.type === "node" || point.type === "asst")) content.set(modelId, point);
|
|
4854
|
+
}
|
|
4855
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
4856
|
+
const edgeTo = /* @__PURE__ */ new Map();
|
|
4857
|
+
for (const connection of this.connections) {
|
|
4858
|
+
const { sourceId, destinationId } = connection;
|
|
4859
|
+
if (connection.type !== "parOf" || sourceId === null || destinationId === null) continue;
|
|
4860
|
+
if (!content.has(destinationId)) continue;
|
|
4861
|
+
const edge = {
|
|
4862
|
+
parentId: sourceId,
|
|
4863
|
+
childId: destinationId,
|
|
4864
|
+
order: connection.sourceOrder ?? 0
|
|
4865
|
+
};
|
|
4866
|
+
const list = childrenOf.get(sourceId);
|
|
4867
|
+
if (list) list.push(edge);
|
|
4868
|
+
else childrenOf.set(sourceId, [edge]);
|
|
4869
|
+
edgeTo.set(destinationId, edge);
|
|
4870
|
+
}
|
|
4871
|
+
for (const list of childrenOf.values()) list.sort((a, b) => a.order - b.order);
|
|
4872
|
+
const roots = [...content].filter(([modelId]) => {
|
|
4873
|
+
const parentId = edgeTo.get(modelId)?.parentId;
|
|
4874
|
+
return parentId === void 0 || !content.has(parentId);
|
|
4875
|
+
});
|
|
4876
|
+
roots.sort(([a], [b]) => (edgeTo.get(a)?.order ?? MAX_ORDER) - (edgeTo.get(b)?.order ?? MAX_ORDER));
|
|
4877
|
+
const placed = /* @__PURE__ */ new Set();
|
|
4878
|
+
const build = (point, parent, level, path) => {
|
|
4879
|
+
const modelId = point.modelId;
|
|
4880
|
+
if (modelId !== null && path.has(modelId)) throw this.#cycleError(modelId);
|
|
4881
|
+
const node = new DiagramNode(point, parent, level);
|
|
4882
|
+
if (modelId === null) return node;
|
|
4883
|
+
placed.add(modelId);
|
|
4884
|
+
path.add(modelId);
|
|
4885
|
+
for (const edge of childrenOf.get(modelId) ?? []) {
|
|
4886
|
+
const child = content.get(edge.childId);
|
|
4887
|
+
if (child) node.children.push(build(child, node, level + 1, path));
|
|
4888
|
+
}
|
|
4889
|
+
path.delete(modelId);
|
|
4890
|
+
return node;
|
|
4891
|
+
};
|
|
4892
|
+
const tree = roots.map(([, point]) => build(point, null, 0, /* @__PURE__ */ new Set()));
|
|
4893
|
+
if (placed.size !== content.size) {
|
|
4894
|
+
const orphan = [...content.keys()].find((modelId) => !placed.has(modelId));
|
|
4895
|
+
throw this.#cycleError(orphan ?? "(unidentified)");
|
|
4896
|
+
}
|
|
4897
|
+
return tree;
|
|
4898
|
+
}
|
|
4899
|
+
/**
|
|
4900
|
+
* The point with this `@modelId`, or `null` — the lookup every connection getter implies,
|
|
4901
|
+
* since a `DiagramConnection` names its ends by id and nothing else resolves them.
|
|
4902
|
+
*/
|
|
4903
|
+
point(modelId) {
|
|
4904
|
+
return this.points.find((candidate) => candidate.modelId === modelId) ?? null;
|
|
4905
|
+
}
|
|
4906
|
+
/** Every edge (`dgm:cxnLst/dgm:cxn`) in document order. */
|
|
4907
|
+
get connections() {
|
|
4908
|
+
const root = this.#root();
|
|
4909
|
+
const cxnLst = root && firstChild(root, "dgm:cxnLst");
|
|
4910
|
+
if (!cxnLst) return [];
|
|
4911
|
+
return getElements(cxnLst, "dgm:cxn").map((cxn) => {
|
|
4912
|
+
const rawType = attr(cxn, "type");
|
|
4913
|
+
return {
|
|
4914
|
+
modelId: attr(cxn, "modelId"),
|
|
4915
|
+
type: rawType !== null && CONNECTION_TYPES.includes(rawType) ? rawType : "parOf",
|
|
4916
|
+
sourceId: attr(cxn, "srcId"),
|
|
4917
|
+
destinationId: attr(cxn, "destId"),
|
|
4918
|
+
sourceOrder: intValue(attr(cxn, "srcOrd")),
|
|
4919
|
+
destinationOrder: intValue(attr(cxn, "destOrd")),
|
|
4920
|
+
parentTransitionId: idAttr(cxn, "parTransId"),
|
|
4921
|
+
siblingTransitionId: idAttr(cxn, "sibTransId"),
|
|
4922
|
+
presentationId: attr(cxn, "presId") || null
|
|
4923
|
+
};
|
|
4924
|
+
});
|
|
4925
|
+
}
|
|
4926
|
+
/**
|
|
4927
|
+
* The diagram's text in document order, blocks joined by `\n` — the counterpart to
|
|
4928
|
+
* `TextFrame.text` for a construct that has no single text body.
|
|
4929
|
+
*
|
|
4930
|
+
* Contributing points are the ones a user can type into: everything except the generated
|
|
4931
|
+
* `pres` points, the `doc` root, and any point flagged as an unfilled placeholder. A
|
|
4932
|
+
* point with no text contributes no blank line. Transition labels (`parTrans`/`sibTrans`)
|
|
4933
|
+
* are included where a layout has them, which is why this is not simply the node list.
|
|
4934
|
+
*/
|
|
4935
|
+
get text() {
|
|
4936
|
+
return this.points.filter((point) => point.type !== "pres" && point.type !== "doc" && !point.isPlaceholder).map((point) => point.text).filter((text) => text.length > 0).join("\n");
|
|
4937
|
+
}
|
|
4938
|
+
/**
|
|
4939
|
+
* The layout preset the diagram is drawn with (the `doc` point's `dgm:prSet/@loTypeId`),
|
|
4940
|
+
* e.g. `urn:microsoft.com/office/officeart/2005/8/layout/hList1`. This is what names the
|
|
4941
|
+
* SmartArt *kind*; {@link layoutPart} is that preset's definition.
|
|
4942
|
+
*/
|
|
4943
|
+
get layoutTypeId() {
|
|
4944
|
+
return this.#docPrSetAttr("loTypeId");
|
|
4945
|
+
}
|
|
4946
|
+
/** The quick-style preset (the `doc` point's `dgm:prSet/@qsTypeId`). */
|
|
4947
|
+
get quickStyleTypeId() {
|
|
4948
|
+
return this.#docPrSetAttr("qsTypeId");
|
|
4949
|
+
}
|
|
4950
|
+
/** The colour preset (the `doc` point's `dgm:prSet/@csTypeId`). */
|
|
4951
|
+
get colorsTypeId() {
|
|
4952
|
+
return this.#docPrSetAttr("csTypeId");
|
|
4953
|
+
}
|
|
4954
|
+
/** The layout definition part (`dgm:relIds/@r:lo`), or `null` when missing from the package. */
|
|
4955
|
+
get layoutPart() {
|
|
4956
|
+
return this.#relPart("r:lo");
|
|
4957
|
+
}
|
|
4958
|
+
/** The quick-style part (`dgm:relIds/@r:qs`), or `null` when missing from the package. */
|
|
4959
|
+
get quickStylePart() {
|
|
4960
|
+
return this.#relPart("r:qs");
|
|
4961
|
+
}
|
|
4962
|
+
/** The colours part (`dgm:relIds/@r:cs`), or `null` when missing from the package. */
|
|
4963
|
+
get colorsPart() {
|
|
4964
|
+
return this.#relPart("r:cs");
|
|
4965
|
+
}
|
|
4966
|
+
/**
|
|
4967
|
+
* The fallback drawing part (`dsp:drawing`) that renders the diagram for a consumer with
|
|
4968
|
+
* no SmartArt layout engine, or `null` when the deck carries none. It is named by an MS
|
|
4969
|
+
* extension on the data model rather than by `dgm:relIds`, so a slide holding two
|
|
4970
|
+
* diagrams still resolves each to its own drawing.
|
|
4971
|
+
*/
|
|
4972
|
+
get drawingPart() {
|
|
4973
|
+
const root = this.#root();
|
|
4974
|
+
const extLst = root && firstChild(root, "dgm:extLst");
|
|
4975
|
+
if (!extLst) return null;
|
|
4976
|
+
for (const ext of getElements(extLst, "a:ext")) {
|
|
4977
|
+
if (attr(ext, "uri") !== DRAWING_EXT_URI) continue;
|
|
4978
|
+
const dataModelExt = firstChildElement(ext);
|
|
4979
|
+
if (!dataModelExt || dataModelExt.localName !== "dataModelExt") continue;
|
|
4980
|
+
return this.#partForRelId(attr(dataModelExt, "relId"));
|
|
4981
|
+
}
|
|
4982
|
+
return null;
|
|
4983
|
+
}
|
|
4984
|
+
/** Escape hatch: the underlying `dgm:dataModel` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
4985
|
+
get element_() {
|
|
4986
|
+
return this.part.dom.documentElement;
|
|
4987
|
+
}
|
|
4988
|
+
/** Mark the diagram data part dirty so `save()` reserializes it. Call after mutating {@link element_}. */
|
|
4989
|
+
markDirty() {
|
|
4990
|
+
this.part.markDirty();
|
|
4991
|
+
}
|
|
4992
|
+
#root() {
|
|
4993
|
+
return this.part.dom.documentElement;
|
|
4994
|
+
}
|
|
4995
|
+
#cycleError(modelId) {
|
|
4996
|
+
return new PackageReadError("diagram/parent-edge-cycle", `Diagram ${this.part.partName} has a parOf cycle reaching point ${modelId}, so its points do not form a tree`);
|
|
4997
|
+
}
|
|
4998
|
+
/**
|
|
4999
|
+
* The `doc` point's `dgm:prSet`, which carries all three preset ids — resolved once
|
|
5000
|
+
* rather than once per getter, since each lookup is a scan of the whole `ptLst`.
|
|
5001
|
+
* `undefined` means "not looked up yet"; `null` means "looked up, not there".
|
|
5002
|
+
*/
|
|
5003
|
+
#docPrSet() {
|
|
5004
|
+
if (this.#docPrSetCache !== void 0) return this.#docPrSetCache;
|
|
5005
|
+
const root = this.#root();
|
|
5006
|
+
const ptLst = root && firstChild(root, "dgm:ptLst");
|
|
5007
|
+
const doc = ptLst && getElements(ptLst, "dgm:pt").find((pt) => attr(pt, "type") === "doc");
|
|
5008
|
+
return this.#docPrSetCache = doc && firstChild(doc, "dgm:prSet") || null;
|
|
5009
|
+
}
|
|
5010
|
+
#docPrSetAttr(name) {
|
|
5011
|
+
const prSet = this.#docPrSet();
|
|
5012
|
+
return prSet ? attr(prSet, name) : null;
|
|
5013
|
+
}
|
|
5014
|
+
/**
|
|
5015
|
+
* The drawing index, built on first use and kept — see {@link DrawingIndex} for what that
|
|
5016
|
+
* memo costs a consumer who mutates the drawing part's shape tree by hand.
|
|
5017
|
+
*/
|
|
5018
|
+
#drawingIndex() {
|
|
5019
|
+
return this.#drawing ??= new DrawingIndex(this.#root(), this.drawingPart, this.themeContext);
|
|
5020
|
+
}
|
|
5021
|
+
#relPart(qname) {
|
|
5022
|
+
return this.#partForRelId(attr(this.relIds, qname));
|
|
5023
|
+
}
|
|
5024
|
+
#partForRelId(relId) {
|
|
5025
|
+
if (!relId) return null;
|
|
5026
|
+
return this.opc.part(this.frameRelationships.resolveTarget(relId)) ?? null;
|
|
5027
|
+
}
|
|
5028
|
+
};
|
|
5029
|
+
/** An `ST_ModelId` attribute whose schema default `0` means "unset" rather than naming a point. */
|
|
5030
|
+
function idAttr(element, name) {
|
|
5031
|
+
const raw = attr(element, name);
|
|
5032
|
+
return raw === null || raw === "" || raw === "0" ? null : raw;
|
|
5033
|
+
}
|
|
5034
|
+
//#endregion
|
|
4408
5035
|
//#region src/read/api/table-edit.ts
|
|
4409
5036
|
/**
|
|
4410
5037
|
* Ordered mutation of a table cell's `a:tcPr`, and the cell-scoped schema facts every such
|
|
@@ -5675,16 +6302,20 @@ var TableCell = class {
|
|
|
5675
6302
|
//#endregion
|
|
5676
6303
|
//#region src/read/api/shapes/graphic-frame.ts
|
|
5677
6304
|
/**
|
|
5678
|
-
* A graphic frame (`p:graphicFrame`) — the host element for a table, a chart,
|
|
6305
|
+
* A graphic frame (`p:graphicFrame`) — the host element for a table, a chart, a chartEx
|
|
6306
|
+
* chart, or a SmartArt diagram.
|
|
5679
6307
|
*
|
|
5680
|
-
* Which one it holds is told by the `a:graphicData/@uri`, so the accessors below each check
|
|
5681
|
-
* URI before wrapping the payload
|
|
5682
|
-
*
|
|
6308
|
+
* Which one it holds is told by the `a:graphicData/@uri`, so the accessors below each check
|
|
6309
|
+
* that URI before wrapping the payload. {@link GraphicFrame.graphicDataUri} reports it raw,
|
|
6310
|
+
* which is the only signal for the frames this library still does not model (OLE, ink):
|
|
6311
|
+
* without it a consumer cannot tell "this frame holds content I cannot reach" from "this
|
|
6312
|
+
* frame holds a chart with nothing in it", since every predicate answers `false` for both.
|
|
5683
6313
|
*/
|
|
5684
6314
|
const A_TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";
|
|
5685
6315
|
const A_CHART_URI = "http://schemas.openxmlformats.org/drawingml/2006/chart";
|
|
5686
6316
|
const A_CHARTEX_URI = "http://schemas.microsoft.com/office/drawing/2014/chartex";
|
|
5687
|
-
|
|
6317
|
+
const A_DIAGRAM_URI = "http://schemas.openxmlformats.org/drawingml/2006/diagram";
|
|
6318
|
+
/** A graphic frame (`p:graphicFrame`) — host for tables, charts and SmartArt diagrams. */
|
|
5688
6319
|
var GraphicFrame = class extends Shape {
|
|
5689
6320
|
shapeType = "graphicFrame";
|
|
5690
6321
|
xfrm() {
|
|
@@ -5708,6 +6339,22 @@ var GraphicFrame = class extends Shape {
|
|
|
5708
6339
|
get hasChartEx() {
|
|
5709
6340
|
return this.#graphicDataUri() === A_CHARTEX_URI;
|
|
5710
6341
|
}
|
|
6342
|
+
/** Whether this frame hosts a SmartArt diagram (`a:graphicData/@uri` is the diagram URI). */
|
|
6343
|
+
get hasDiagram() {
|
|
6344
|
+
return this.#graphicDataUri() === A_DIAGRAM_URI;
|
|
6345
|
+
}
|
|
6346
|
+
/**
|
|
6347
|
+
* The `a:graphicData/@uri` verbatim — the namespace naming what this frame hosts, or
|
|
6348
|
+
* `null` when the frame carries no `a:graphicData`.
|
|
6349
|
+
*
|
|
6350
|
+
* Every `has*` predicate is a comparison against this, so a frame whose URI matches none
|
|
6351
|
+
* of them is one the read model does not decode (an OLE object, an ink `p:contentPart`).
|
|
6352
|
+
* Reading the URI is what lets a consumer say *which* construct it could not reach
|
|
6353
|
+
* instead of inferring loss from four `false`s.
|
|
6354
|
+
*/
|
|
6355
|
+
get graphicDataUri() {
|
|
6356
|
+
return this.#graphicDataUri();
|
|
6357
|
+
}
|
|
5711
6358
|
/** The hosted table, or `null` when this frame is not a table. */
|
|
5712
6359
|
get table() {
|
|
5713
6360
|
if (!this.hasTable) return null;
|
|
@@ -5742,6 +6389,22 @@ var GraphicFrame = class extends Shape {
|
|
|
5742
6389
|
const part = this.host.opc.part(partName);
|
|
5743
6390
|
return part ? new ChartEx(part) : null;
|
|
5744
6391
|
}
|
|
6392
|
+
/**
|
|
6393
|
+
* The hosted SmartArt diagram, or `null` when this frame is not a diagram or its data
|
|
6394
|
+
* part is missing. The payload child is `dgm:relIds`, which names four parts by
|
|
6395
|
+
* relationship id; the `@r:dm` one is the data model this resolves.
|
|
6396
|
+
*/
|
|
6397
|
+
get diagram() {
|
|
6398
|
+
if (!this.hasDiagram) return null;
|
|
6399
|
+
const graphicData = this.#graphicData();
|
|
6400
|
+
const relIds = graphicData && firstChild(graphicData, "dgm:relIds");
|
|
6401
|
+
if (!relIds) return null;
|
|
6402
|
+
const relId = attr(relIds, "r:dm");
|
|
6403
|
+
if (!relId) return null;
|
|
6404
|
+
const partName = this.host.relationships.resolveTarget(relId);
|
|
6405
|
+
const part = this.host.opc.part(partName);
|
|
6406
|
+
return part ? new Diagram(part, relIds, this.host.opc, this.host.relationships, this.host.themeContext()) : null;
|
|
6407
|
+
}
|
|
5745
6408
|
#graphicData() {
|
|
5746
6409
|
const graphic = firstChild(this.element, "a:graphic");
|
|
5747
6410
|
return graphic ? firstChild(graphic, "a:graphicData") : null;
|
|
@@ -5923,6 +6586,6 @@ function findShapeByIdDeep(shapes, id) {
|
|
|
5923
6586
|
}
|
|
5924
6587
|
}
|
|
5925
6588
|
//#endregion
|
|
5926
|
-
export {
|
|
6589
|
+
export { carriedDecorations as $, spPrXfrmEmu as A, resolveSingleRel as At, applyColorTransforms as B, getElements as Bt, Connector as C, SPPR_SCENE3D_AFTER as Ct, Paragraph as D, relativePartName as Dt, Shape as E, partNameToZipPath as Et, resolveLayoutColorContext as F, createElement as Ft, placeholderInheritedListStyles as G, parseXml as Gt, RUN_PROP_NAMES as H, insertInOrder as Ht, resolveMasterColorContext as I, descendantsByTag as It, placeholderOf as J, replaceInParent as Jt, placeholderInheritedRunProps as K, removeAttr as Kt, resolveNotesColorContext as L, escapeXmlAttribute as Lt, readPictureFill as M, attr as Mt, readGradientFill as N, boolValue as Nt, Run as O, relsPartNameFor as Ot, resolveColorElement as P, childElements as Pt, cSldOf as Q, resolveSlideColorContext as R, firstChild as Rt, ChartSeries as S, SPPR_LN_AFTER as St, AutoShape as T, partNameExtension as Tt, placeholderInheritedAnchor as U, intValue as Ut, LST_STYLE_LEVELS as V, getOrAddChild as Vt, placeholderInheritedColor as W, ownerDocumentOf as Wt, slideDefinesProp as X, setAttr as Xt, slideDefinesColor as Y, serializeXml as Yt, cSldName as Z, ChartEx as _, RPR_FILL_AFTER as _t, isConnector as a, SCHEME_SLOTS as at, Chart as b, SPPR_EFFECT_AFTER as bt, isPicture as c, isA as ct, Table as d, resolveColor as dt, collectElements as et, TableCell as f, resolveSchemeToken as ft, DiagramPoint as g, FILL_CHOICES as gt, DiagramNode as h, substitutePhClr as ht, isAutoShape as i, spTreeOf as it, readPatternFill as j, zipPathToPartName as jt, TextFrame as k, resolveRelativePartName as kt, wrapShapeElement as l, parseClrMap as lt, Diagram as m, styleRefLine as mt, buildShapes as n, nthShapeChild as nt, isGraphicFrame as o, fmtEntry as ot, TableRow as p, styleRefFill as pt, placeholderInheritedXfrm as q, removeChildrenByQName as qt, findShapeByIdDeep as r, nvPrOf as rt, isGroupShape as s, intAttr as st, GroupShape as t, firstShapeChild as tt, GraphicFrame as u, parseClrScheme as ut, ChartExAxis as v, SHAPE_AFTER_SPPR as vt, Picture as w, SPPR_SP3D_AFTER as wt, ChartAxis as x, SPPR_FILL_AFTER as xt, ChartExSeries as y, SPPR_AFTER_XFRM as yt, resolveSlideThemeParts as z, firstChildElement as zt };
|
|
5927
6590
|
|
|
5928
|
-
//# sourceMappingURL=shapes-
|
|
6591
|
+
//# sourceMappingURL=shapes-Kt51vry6.js.map
|