@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,8 +1,9 @@
|
|
|
1
1
|
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-DGE9zh3U.js";
|
|
2
2
|
import { d as emuToInches } from "./units-B-JUKwCS.js";
|
|
3
|
+
import { _ as SLIDE_LAYOUT_REL, b as THEME_REL, d as IMAGE_REL, f as NOTES_MASTER_REL, m as OFFICE_DOCUMENT_REL, p as NOTES_SLIDE_REL, v as SLIDE_MASTER_REL, y as SLIDE_REL } from "./st-enums-CgadEyPM.js";
|
|
3
4
|
import { i as FONT_REL_TYPE, n as FONT_DATA_CONTENT_TYPE, r as FONT_DATA_EXTENSION, t as EMBEDDED_FONT_SLOTS } from "./embedded-fonts-MaynAHa9.js";
|
|
4
5
|
import { ZipWriter, readZip } from "./zip.js";
|
|
5
|
-
import { $ as
|
|
6
|
+
import { $ as resolveColor, A as resolveColorElement, At as getOrAddChild, B as placeholderInheritedColor, Ct as childElements, D as readPatternFill, Dt as firstChild, E as spPrXfrmEmu, Et as escapeXmlAttribute, F as resolveSlideThemeParts, Ft as removeAttr, G as slideDefinesColor, H as placeholderInheritedRunProps, It as removeChildrenByQName, J as fmtEntry, K as slideDefinesProp, L as LST_STYLE_LEVELS, Lt as replaceInParent, M as resolveMasterColorContext, Mt as intValue, N as resolveNotesColorContext, Nt as ownerDocumentOf, O as readPictureFill, Ot as firstChildElement, P as resolveSlideColorContext, Pt as parseXml, Q as parseClrScheme, R as RUN_PROP_NAMES, Rt as serializeXml, St as boolValue, T as TextFrame, Tt as descendantsByTag, U as placeholderInheritedXfrm, V as placeholderInheritedListStyles, W as placeholderOf, X as isA, Y as intAttr, Z as parseClrMap, _t as resolveRelativePartName, at as RPR_FILL_AFTER, b as Picture, bt as OOXML_NS, c as wrapShapeElement, ct as SPPR_EFFECT_AFTER, dt as SPPR_SCENE3D_AFTER, et as resolveSchemeToken, ft as SPPR_SP3D_AFTER, gt as relsPartNameFor, ht as relativePartName, it as FILL_CHOICES, j as resolveLayoutColorContext, jt as insertInOrder, k as readGradientFill, kt as getElements, l as GraphicFrame, lt as SPPR_FILL_AFTER, mt as partNameToZipPath, n as buildShapes, nt as styleRefLine, ot as SHAPE_AFTER_SPPR, pt as partNameExtension, q as SCHEME_SLOTS, rt as substitutePhClr, st as SPPR_AFTER_XFRM, t as GroupShape, tt as styleRefFill, ut as SPPR_LN_AFTER, vt as resolveSingleRel, wt as createElement, x as AutoShape, xt as attr, yt as zipPathToPartName, z as placeholderInheritedAnchor, zt as setAttr } from "./shapes-BWifcHDV.js";
|
|
6
7
|
//#region src/read/opc/content-types.ts
|
|
7
8
|
/**
|
|
8
9
|
* Overlay over `[Content_Types].xml`, used to resolve part content types at
|
|
@@ -420,6 +421,477 @@ var OpcPackage = class OpcPackage {
|
|
|
420
421
|
}
|
|
421
422
|
};
|
|
422
423
|
//#endregion
|
|
424
|
+
//#region src/read/api/ops/flatten.ts
|
|
425
|
+
/**
|
|
426
|
+
* Baking a slide's theme dependencies into the slide itself, for `Presentation.importSlide` /
|
|
427
|
+
* `importShape`.
|
|
428
|
+
*
|
|
429
|
+
* A slide imported into another deck is rebound to the *destination* master, so everything it
|
|
430
|
+
* resolved symbolically against its own theme silently re-resolves against a different one — its
|
|
431
|
+
* scheme colours change, its style-matrix fills change, its placeholders snap to different
|
|
432
|
+
* geometry and type sizes. The `theme: 'preserve'` mode exists to stop that: each pass here
|
|
433
|
+
* resolves a dependency against the **source** theme (via `read/oxml/theme.ts` and
|
|
434
|
+
* `read/oxml/placeholder-inherit.ts`) and writes the answer explicitly onto the slide, so after
|
|
435
|
+
* the rebind there is nothing left to re-resolve.
|
|
436
|
+
*
|
|
437
|
+
* What gets flattened, and why each one matters:
|
|
438
|
+
*
|
|
439
|
+
* 1. **`a:schemeClr` tokens** — rewritten to the literal `a:srgbClr` the source `clrMap` +
|
|
440
|
+
* `clrScheme` produce. Child transforms (`lumMod`/`shade`/`alpha`/…) are carried through
|
|
441
|
+
* untouched, so tints and shades render identically; only the *base* reference is swapped,
|
|
442
|
+
* never the transform math.
|
|
443
|
+
* 2. **Style-matrix refs** — a shape's `p:style` (`lnRef`/`fillRef`/`effectRef`) indexes the
|
|
444
|
+
* theme `fmtScheme`; the indexed entry is resolved into an explicit `spPr` fill/line/effect
|
|
445
|
+
* (with its `phClr` substituted) and the ref neutralized so it cannot re-resolve. The
|
|
446
|
+
* `fontRef` is deliberately left intact so its font can re-bind to the destination theme —
|
|
447
|
+
* the "normalize fonts on attach" bonus.
|
|
448
|
+
* 3. **Placeholder-inherited run colour, size and weight** — a run that sets none of its own
|
|
449
|
+
* takes them from the source placeholder → layout → master text-style chain. Rebinding
|
|
450
|
+
* replaces that chain, so each is resolved from the source styles and written onto the run.
|
|
451
|
+
* Typeface (`a:latin`) is deliberately *not* baked — it re-binds along with `fontRef`.
|
|
452
|
+
* 4. **Placeholder-inherited geometry** — a placeholder with no own `a:xfrm` takes position and
|
|
453
|
+
* size from the matching source layout/master placeholder; without baking it, a title snaps
|
|
454
|
+
* to the destination default and often clips off-canvas.
|
|
455
|
+
*
|
|
456
|
+
* The `restyle` mode is the deliberate inverse ({@link restyleSlide}): it bakes *nothing*, so
|
|
457
|
+
* everything re-resolves against the destination and the slide takes on the new brand.
|
|
458
|
+
*
|
|
459
|
+
* **Why this lives in `api/ops/` and not in `read/oxml/`.** Everything here *mutates* a live
|
|
460
|
+
* part. The modules it calls into resolve and build detached elements but never write, which is
|
|
461
|
+
* what lets the read model's getters share them; keeping the writes on this side of the line is
|
|
462
|
+
* what keeps that true. Callers own marking the part dirty.
|
|
463
|
+
*/
|
|
464
|
+
/**
|
|
465
|
+
* Flatten one slide's theme dependencies in place:
|
|
466
|
+
*
|
|
467
|
+
* 1. carry the background the slide inherited from its source layout/master onto
|
|
468
|
+
* the slide (so it survives rebinding to a different master);
|
|
469
|
+
* 2. materialize `p:bgRef` and style-matrix refs into explicit fills/lines/effects;
|
|
470
|
+
* 3. bake each placeholder's effective geometry (inherited `a:xfrm`) onto the
|
|
471
|
+
* shape so a rebind cannot move or resize it;
|
|
472
|
+
* 4. bake each placeholder run's effective colour and size/weight (inherited from
|
|
473
|
+
* the source layout/master text styles) explicitly onto the run;
|
|
474
|
+
* 5. rewrite every remaining `a:schemeClr` to its literal `a:srgbClr`.
|
|
475
|
+
*
|
|
476
|
+
* Steps run in this order so the inherited/materialized backgrounds are present
|
|
477
|
+
* before the final scheme-colour sweep resolves the colours they carry. The
|
|
478
|
+
* placeholder geometry/colour/size passes have no data dependency on the others.
|
|
479
|
+
* The caller marks the part dirty.
|
|
480
|
+
*/
|
|
481
|
+
function flattenSlide(slideRoot, ctx) {
|
|
482
|
+
applyInheritedBackground(slideRoot, ctx);
|
|
483
|
+
materializeBackground(slideRoot, ctx);
|
|
484
|
+
materializeStyleRefs(slideRoot, ctx);
|
|
485
|
+
resolvePlaceholderGeometry(slideRoot, ctx);
|
|
486
|
+
resolvePlaceholderRunColors(slideRoot, ctx);
|
|
487
|
+
resolvePlaceholderRunSizes(slideRoot, ctx);
|
|
488
|
+
resolveSchemeColors(slideRoot, ctx);
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Flatten a single lifted shape's theme dependencies in place — the shape-scoped
|
|
492
|
+
* subset of {@link flattenSlide} used by `Presentation.importShape` `preserve`
|
|
493
|
+
* mode. It runs every pass that resolves a *shape's* theme references against the
|
|
494
|
+
* source theme (style-matrix refs, placeholder-inherited geometry/colour/size/
|
|
495
|
+
* anchor/list-style, scheme colours) but deliberately **omits** the two
|
|
496
|
+
* slide-scoped background passes (`applyInheritedBackground`/`materializeBackground`):
|
|
497
|
+
* a background belongs to a slide, not to a shape being composed onto a foreign host.
|
|
498
|
+
*
|
|
499
|
+
* Unlike {@link flattenSlide}, this also **demotes** a lifted placeholder to a
|
|
500
|
+
* plain shape ({@link demotePlaceholders}) once everything it inherited is baked.
|
|
501
|
+
* A placeholder makes sense on its own slide, where it resolves against that deck's
|
|
502
|
+
* master/layout; lifted onto a foreign host its surviving `p:ph` would re-resolve
|
|
503
|
+
* against the *host* placeholder of the same type/idx (wrong inheritance, or a
|
|
504
|
+
* fallback when absent) and could collide with the host's own placeholder. The
|
|
505
|
+
* extra bakes here (anchor + list style, on top of the geometry/colour/size the
|
|
506
|
+
* shared passes cover) make the shape self-contained so demotion loses nothing
|
|
507
|
+
* visible. This is why the demotion is scoped to `flattenShape`: `flattenSlide`
|
|
508
|
+
* keeps placeholders as placeholders by design.
|
|
509
|
+
*
|
|
510
|
+
* `shapeRoot` must be an element whose *descendants* include the lifted shape
|
|
511
|
+
* (the passes match via `getElementsByTagNameNS`, which excludes the root element
|
|
512
|
+
* itself) — the caller wraps the imported `p:sp`/`p:pic`/`p:graphicFrame`/`p:grpSp`
|
|
513
|
+
* in a throwaway container before calling. The caller marks the part dirty.
|
|
514
|
+
*/
|
|
515
|
+
function flattenShape(shapeRoot, ctx) {
|
|
516
|
+
materializeStyleRefs(shapeRoot, ctx);
|
|
517
|
+
resolvePlaceholderGeometry(shapeRoot, ctx);
|
|
518
|
+
resolvePlaceholderRunColors(shapeRoot, ctx);
|
|
519
|
+
resolvePlaceholderRunSizes(shapeRoot, ctx);
|
|
520
|
+
resolvePlaceholderBodyPr(shapeRoot, ctx);
|
|
521
|
+
resolvePlaceholderListStyle(shapeRoot, ctx);
|
|
522
|
+
resolveSchemeColors(shapeRoot, ctx);
|
|
523
|
+
demotePlaceholders(shapeRoot);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Restyle a rebound slide in place (the `theme: 'restyle'` mode): the exact
|
|
527
|
+
* inverse of {@link flattenSlide}. It bakes *nothing* — leaving every
|
|
528
|
+
* `a:schemeClr`/style-matrix ref and `p:bg` `bgRef` symbolic is the whole point,
|
|
529
|
+
* so they re-resolve against the *destination* master's `clrMap` + theme once the
|
|
530
|
+
* slide is rebound. The single mutation is dropping the slide's own colour-map
|
|
531
|
+
* override (`p:clrMapOvr/a:overrideClrMapping`): a source override would keep
|
|
532
|
+
* mapping the slide's scheme-token names the source way and defeat the re-brand,
|
|
533
|
+
* so it must yield to the destination master's `p:clrMap`.
|
|
534
|
+
*
|
|
535
|
+
* Caveat carried from the plan: only *symbolic* colours re-brand. Anything the
|
|
536
|
+
* source authored as a literal `a:srgbClr` has no theme reference to re-resolve
|
|
537
|
+
* and stays exactly that colour. The caller marks the part dirty.
|
|
538
|
+
*/
|
|
539
|
+
function restyleSlide(slideRoot) {
|
|
540
|
+
removeChildrenByQName(slideRoot, ["p:clrMapOvr"]);
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* The optional inverse of {@link resolveSchemeColors}, for `restyle`'s
|
|
544
|
+
* force-remap-literals mode (the `Presentation.importSlide` `remapLiterals`
|
|
545
|
+
* option). Every literal `a:srgbClr` whose value equals a *source* `clrScheme`
|
|
546
|
+
* slot is rewritten back to a symbolic `a:schemeClr` so it re-resolves against the
|
|
547
|
+
* destination theme; a literal matching no slot (the common case) is left exactly
|
|
548
|
+
* as authored. Transform children (`lumMod`/`shade`/`alpha`/…) are carried across.
|
|
549
|
+
*
|
|
550
|
+
* Plain `restyle` cannot re-brand a literal — it carries no theme reference. This
|
|
551
|
+
* re-introduces one by matching the literal's RGB against the slots the *source*
|
|
552
|
+
* theme defined, emitting the scheme token that routes through the source `clrMap`
|
|
553
|
+
* (so a literal equal to the source `accent1` slot becomes `schemeClr accent1`,
|
|
554
|
+
* which the destination `clrMap`/theme then re-resolve). Slots are matched in
|
|
555
|
+
* `clrScheme` order, so the first slot defining a given RGB wins when several share
|
|
556
|
+
* it. Opt-in because it deliberately reinterprets authored literals as theme
|
|
557
|
+
* colours — visual QA territory, the same caveat as the rest of `restyle`.
|
|
558
|
+
*/
|
|
559
|
+
function remapLiteralColors(slideRoot, ctx) {
|
|
560
|
+
const slotByHex = reverseClrScheme(ctx.clrScheme);
|
|
561
|
+
if (slotByHex.size === 0) return;
|
|
562
|
+
const tokenBySlot = reverseClrMap(ctx.clrMap);
|
|
563
|
+
const doc = ownerDocumentOf(slideRoot);
|
|
564
|
+
for (const srgb of descendantsByTag(slideRoot, OOXML_NS.a, "srgbClr")) {
|
|
565
|
+
const hex = attr(srgb, "val");
|
|
566
|
+
const slot = hex ? slotByHex.get(hex.toUpperCase()) : void 0;
|
|
567
|
+
if (!slot) continue;
|
|
568
|
+
const scheme = createElement(doc, "a:schemeClr");
|
|
569
|
+
setAttr(scheme, "val", tokenBySlot.get(slot) ?? slot);
|
|
570
|
+
while (srgb.firstChild) scheme.appendChild(srgb.firstChild);
|
|
571
|
+
replaceInParent(srgb, scheme);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/** Reverse a slot → RGB `clrScheme` into RGB → slot, in `SCHEME_SLOTS` order (first slot wins on a shared RGB). */
|
|
575
|
+
function reverseClrScheme(clrScheme) {
|
|
576
|
+
const out = /* @__PURE__ */ new Map();
|
|
577
|
+
for (const slot of SCHEME_SLOTS) {
|
|
578
|
+
const hex = clrScheme.get(slot);
|
|
579
|
+
if (hex && !out.has(hex.toUpperCase())) out.set(hex.toUpperCase(), slot);
|
|
580
|
+
}
|
|
581
|
+
return out;
|
|
582
|
+
}
|
|
583
|
+
/** Reverse a token → slot `clrMap` into slot → token (first token wins on a shared slot). */
|
|
584
|
+
function reverseClrMap(clrMap) {
|
|
585
|
+
const out = /* @__PURE__ */ new Map();
|
|
586
|
+
for (const [token, slot] of clrMap) if (!out.has(slot)) out.set(slot, token);
|
|
587
|
+
return out;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* If the slide has no own `p:bg`, insert (a copy of) the background it inherited
|
|
591
|
+
* from its source layout/master as an explicit `p:cSld/p:bg`. The clone is left
|
|
592
|
+
* unresolved here; the later passes flatten its `bgRef`/`schemeClr` in place.
|
|
593
|
+
*/
|
|
594
|
+
function applyInheritedBackground(slideRoot, ctx) {
|
|
595
|
+
const inherited = ctx.inheritedBackground;
|
|
596
|
+
if (!inherited) return;
|
|
597
|
+
const cSld = firstChild(slideRoot, "p:cSld");
|
|
598
|
+
if (!cSld || firstChild(cSld, "p:bg")) return;
|
|
599
|
+
const bg = ownerDocumentOf(slideRoot).importNode(inherited, true);
|
|
600
|
+
insertInOrder(cSld, bg, [
|
|
601
|
+
"p:spTree",
|
|
602
|
+
"p:custDataLst",
|
|
603
|
+
"p:controls",
|
|
604
|
+
"p:extLst"
|
|
605
|
+
]);
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Resolve every `p:bgRef` (theme-indexed background) under the slide into an
|
|
609
|
+
* explicit `p:bgPr` fill, so the background no longer depends on the destination
|
|
610
|
+
* theme's `fmtScheme`. A `bgPr` background is left for the scheme-colour sweep.
|
|
611
|
+
*/
|
|
612
|
+
function materializeBackground(slideRoot, ctx) {
|
|
613
|
+
const doc = ownerDocumentOf(slideRoot);
|
|
614
|
+
for (const bg of descendantsByTag(slideRoot, OOXML_NS.p, "bg")) {
|
|
615
|
+
const bgRef = firstChild(bg, "p:bgRef");
|
|
616
|
+
if (!bgRef) continue;
|
|
617
|
+
const fill = styleRefFill(bgRef, ctx);
|
|
618
|
+
const bgPr = createElement(doc, "p:bgPr");
|
|
619
|
+
bgPr.appendChild(fill ?? createElement(doc, "a:noFill"));
|
|
620
|
+
bg.replaceChild(bgPr, bgRef);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/** Rewrite every `a:schemeClr` under `root` to a literal `a:srgbClr` when resolvable. */
|
|
624
|
+
function resolveSchemeColors(root, ctx) {
|
|
625
|
+
const doc = ownerDocumentOf(root);
|
|
626
|
+
for (const schemeClr of descendantsByTag(root, OOXML_NS.a, "schemeClr")) {
|
|
627
|
+
const token = attr(schemeClr, "val");
|
|
628
|
+
const hex = token ? resolveSchemeToken(token, ctx) : null;
|
|
629
|
+
if (!hex) continue;
|
|
630
|
+
const srgb = createElement(doc, "a:srgbClr");
|
|
631
|
+
setAttr(srgb, "val", hex);
|
|
632
|
+
while (schemeClr.firstChild) srgb.appendChild(schemeClr.firstChild);
|
|
633
|
+
replaceInParent(schemeClr, srgb);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Bake placeholder-inherited run colours onto the slide. For each placeholder run
|
|
638
|
+
* that defines no colour of its own (nor at paragraph/text-body level on the
|
|
639
|
+
* *slide*), resolve the colour it would inherit from the source
|
|
640
|
+
* `slideLayout`/`slideMaster` text styles and write it explicitly onto the run's
|
|
641
|
+
* `a:rPr`. After this the run's colour cannot change when the slide is rebound to
|
|
642
|
+
* the destination master. `a:fld` runs (dates, slide numbers) are treated like
|
|
643
|
+
* `a:r`. Only colour is resolved; other inheritable run properties are left to
|
|
644
|
+
* re-bind to the destination styles.
|
|
645
|
+
*/
|
|
646
|
+
function resolvePlaceholderRunColors(slideRoot, ctx) {
|
|
647
|
+
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
648
|
+
for (const sp of descendantsByTag(slideRoot, OOXML_NS.p, "sp")) {
|
|
649
|
+
const ph = placeholderOf(sp);
|
|
650
|
+
if (!ph) continue;
|
|
651
|
+
const txBody = firstChild(sp, "p:txBody");
|
|
652
|
+
if (!txBody) continue;
|
|
653
|
+
const type = attr(ph, "type");
|
|
654
|
+
const idx = attr(ph, "idx") ?? "0";
|
|
655
|
+
const slideLst = firstChild(txBody, "a:lstStyle");
|
|
656
|
+
const byLevel = /* @__PURE__ */ new Map();
|
|
657
|
+
for (const p of getElements(txBody, "a:p")) {
|
|
658
|
+
const pPr = firstChild(p, "a:pPr");
|
|
659
|
+
const level = (pPr && intValue(attr(pPr, "lvl"))) ?? 0;
|
|
660
|
+
const runs = [...getElements(p, "a:r"), ...getElements(p, "a:fld")];
|
|
661
|
+
if (runs.length === 0) continue;
|
|
662
|
+
let color = byLevel.get(level);
|
|
663
|
+
if (color === void 0) {
|
|
664
|
+
color = placeholderInheritedColor(type, idx, level, ctx);
|
|
665
|
+
byLevel.set(level, color);
|
|
666
|
+
}
|
|
667
|
+
if (!color) continue;
|
|
668
|
+
for (const run of runs) {
|
|
669
|
+
if (slideDefinesColor(run, pPr, slideLst, level)) continue;
|
|
670
|
+
writeRunColor(run, color);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
/** Write a resolved colour as an explicit `a:solidFill` (with carried transforms) onto a run's `a:rPr`. */
|
|
676
|
+
function writeRunColor(run, color) {
|
|
677
|
+
const doc = ownerDocumentOf(run);
|
|
678
|
+
const rPr = getOrAddChild(run, "a:rPr", ["a:t"]);
|
|
679
|
+
const fill = createElement(doc, "a:solidFill");
|
|
680
|
+
const srgb = createElement(doc, "a:srgbClr");
|
|
681
|
+
setAttr(srgb, "val", color.hex);
|
|
682
|
+
for (const t of color.transforms) srgb.appendChild(t.cloneNode(true));
|
|
683
|
+
fill.appendChild(srgb);
|
|
684
|
+
insertInOrder(rPr, fill, RPR_FILL_AFTER);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Bake placeholder-inherited geometry onto the slide. A placeholder shape
|
|
688
|
+
* that carries no own `p:spPr/a:xfrm` takes its position/size from the matching
|
|
689
|
+
* source `slideLayout` placeholder, else the `slideMaster` placeholder. Rebinding
|
|
690
|
+
* to the destination master replaces that inheritance, so the placeholder would
|
|
691
|
+
* snap to the destination default (often clipping off-canvas). We deep-clone the
|
|
692
|
+
* effective source `a:xfrm` and write it explicitly onto the shape. Shapes with
|
|
693
|
+
* their own `a:xfrm` are left untouched (explicit geometry is not inherited), and
|
|
694
|
+
* an orphan placeholder with no source match keeps the current fall-back behaviour.
|
|
695
|
+
*/
|
|
696
|
+
function resolvePlaceholderGeometry(slideRoot, ctx) {
|
|
697
|
+
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
698
|
+
for (const sp of descendantsByTag(slideRoot, OOXML_NS.p, "sp")) {
|
|
699
|
+
const ph = placeholderOf(sp);
|
|
700
|
+
if (!ph) continue;
|
|
701
|
+
const spPr = firstChild(sp, "p:spPr");
|
|
702
|
+
if (spPr && firstChild(spPr, "a:xfrm")) continue;
|
|
703
|
+
const found = placeholderInheritedXfrm(attr(ph, "type"), attr(ph, "idx") ?? "0", ctx);
|
|
704
|
+
if (!found) continue;
|
|
705
|
+
const target = getOrAddChild(sp, "p:spPr", SHAPE_AFTER_SPPR);
|
|
706
|
+
insertInOrder(target, found.xfrm.cloneNode(true), SPPR_AFTER_XFRM);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Bake the placeholder-inherited vertical anchor onto a lifted shape's text body
|
|
711
|
+
* (the {@link flattenShape} placeholder-demotion path). A placeholder whose own
|
|
712
|
+
* `a:bodyPr` sets no `@anchor` inherits it from the source layout/master placeholder;
|
|
713
|
+
* once {@link demotePlaceholders} strips the `p:ph` the shape would lose that
|
|
714
|
+
* inheritance and fall back to top-anchored, so a vertically-centred title jumps.
|
|
715
|
+
* We resolve the effective anchor from the source chain and write it explicitly.
|
|
716
|
+
* Shapes whose `a:bodyPr` already fixes `@anchor`, and placeholders with no
|
|
717
|
+
* resolvable inherited anchor, are left untouched. Other `a:bodyPr` knobs (insets,
|
|
718
|
+
* autofit) are left as authored — they have sane defaults a plain shape keeps.
|
|
719
|
+
*/
|
|
720
|
+
function resolvePlaceholderBodyPr(shapeRoot, ctx) {
|
|
721
|
+
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
722
|
+
for (const sp of descendantsByTag(shapeRoot, OOXML_NS.p, "sp")) {
|
|
723
|
+
const ph = placeholderOf(sp);
|
|
724
|
+
if (!ph) continue;
|
|
725
|
+
const txBody = firstChild(sp, "p:txBody");
|
|
726
|
+
if (!txBody) continue;
|
|
727
|
+
const bodyPr = firstChild(txBody, "a:bodyPr");
|
|
728
|
+
if (bodyPr && attr(bodyPr, "anchor") != null) continue;
|
|
729
|
+
const anchor = placeholderInheritedAnchor(attr(ph, "type"), attr(ph, "idx") ?? "0", ctx);
|
|
730
|
+
if (!anchor) continue;
|
|
731
|
+
setAttr(bodyPr ?? getOrAddChild(txBody, "a:bodyPr", ["a:lstStyle", "a:p"]), "anchor", anchor);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Bake the placeholder-inherited list style onto a lifted shape's text body (the
|
|
736
|
+
* {@link flattenShape} placeholder-demotion path). A placeholder's per-level
|
|
737
|
+
* *paragraph* formatting — indent (`marL`/`indent`), bullets (`a:buChar`/
|
|
738
|
+
* `a:buAutoNum`/`a:buNone`), alignment, and the level `a:defRPr` — is inherited
|
|
739
|
+
* from the source layout/master placeholder `a:lstStyle` and the master
|
|
740
|
+
* `p:txStyles` category style. Run colour/size are baked onto each run by the
|
|
741
|
+
* sibling passes, but paragraph-level defaults live here; once
|
|
742
|
+
* {@link demotePlaceholders} strips the `p:ph` the shape stops inheriting them, so
|
|
743
|
+
* bullets and indents would vanish. We materialize the effective list style onto
|
|
744
|
+
* the shape's `p:txBody/a:lstStyle`.
|
|
745
|
+
*
|
|
746
|
+
* Resolution is per *level*, most specific tier wins whole: the slide's own level
|
|
747
|
+
* (if it defines one) is kept verbatim, else the first source tier that defines it
|
|
748
|
+
* (layout placeholder, then master placeholder, then master category style) is
|
|
749
|
+
* cloned. This is a whole-element overlay, not the per-attribute merge PowerPoint
|
|
750
|
+
* does — but explicit paragraph `a:pPr` on the slide's own runs travels with the
|
|
751
|
+
* shape and still wins, so this only supplies defaults for the inherited case
|
|
752
|
+
* (paragraphs that set no `a:pPr` of their own). Scheme colours in the cloned
|
|
753
|
+
* levels are resolved by the later `resolveSchemeColors` pass.
|
|
754
|
+
*/
|
|
755
|
+
function resolvePlaceholderListStyle(shapeRoot, ctx) {
|
|
756
|
+
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
757
|
+
for (const sp of descendantsByTag(shapeRoot, OOXML_NS.p, "sp")) {
|
|
758
|
+
const ph = placeholderOf(sp);
|
|
759
|
+
if (!ph) continue;
|
|
760
|
+
const txBody = firstChild(sp, "p:txBody");
|
|
761
|
+
if (!txBody) continue;
|
|
762
|
+
const tiers = placeholderInheritedListStyles(attr(ph, "type"), attr(ph, "idx") ?? "0", ctx);
|
|
763
|
+
if (tiers.length === 0) continue;
|
|
764
|
+
const slideLst = firstChild(txBody, "a:lstStyle");
|
|
765
|
+
const merged = createElement(ownerDocumentOf(txBody), "a:lstStyle");
|
|
766
|
+
let any = false;
|
|
767
|
+
for (const level of LST_STYLE_LEVELS) {
|
|
768
|
+
const src = (slideLst && firstChild(slideLst, level)) ?? tiers.map((t) => firstChild(t, level)).find((e) => !!e) ?? null;
|
|
769
|
+
if (!src) continue;
|
|
770
|
+
merged.appendChild(src.cloneNode(true));
|
|
771
|
+
any = true;
|
|
772
|
+
}
|
|
773
|
+
if (!any) continue;
|
|
774
|
+
if (slideLst) txBody.replaceChild(merged, slideLst);
|
|
775
|
+
else insertInOrder(txBody, merged, ["a:p"]);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Strip the `p:ph` marker from every placeholder shape in a lifted subtree so it
|
|
780
|
+
* becomes a self-contained ordinary shape (the {@link flattenShape} path only). By
|
|
781
|
+
* the time this runs, the shape's inherited geometry, colour, run size, anchor, and
|
|
782
|
+
* list style are all baked explicitly, so it no longer needs — and must not keep —
|
|
783
|
+
* its placeholder identity: a surviving `p:ph` would re-resolve against the *host*
|
|
784
|
+
* deck's layout/master placeholder of the same type/idx (wrong inheritance, or a
|
|
785
|
+
* fallback when the host has none) and could collide with the host slide's own
|
|
786
|
+
* placeholder of that type. Removing the marker severs both. The `p:nvPr` itself is
|
|
787
|
+
* kept (it can carry media/custom-data children); only the `p:ph` child goes.
|
|
788
|
+
*/
|
|
789
|
+
function demotePlaceholders(shapeRoot) {
|
|
790
|
+
for (const sp of descendantsByTag(shapeRoot, OOXML_NS.p, "sp")) {
|
|
791
|
+
const nvSpPr = firstChild(sp, "p:nvSpPr");
|
|
792
|
+
const nvPr = nvSpPr && firstChild(nvSpPr, "p:nvPr");
|
|
793
|
+
if (nvPr) removeChildrenByQName(nvPr, ["p:ph"]);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
/**
|
|
797
|
+
* Bake placeholder-inherited run size/weight onto the slide. Mirrors
|
|
798
|
+
* {@link resolvePlaceholderRunColors}: for each placeholder run that sets no
|
|
799
|
+
* `sz`/`b`/`i` of its own (nor at paragraph/text-body level on the *slide*),
|
|
800
|
+
* resolve the value it would inherit from the source `slideLayout`/`slideMaster`
|
|
801
|
+
* text styles — per paragraph list level — and write it explicitly onto the run's
|
|
802
|
+
* `a:rPr`. Each property resolves independently up the chain. Typeface (`a:latin`)
|
|
803
|
+
* is left to re-bind to the destination theme, as the colour pass does for the `fontRef`.
|
|
804
|
+
*/
|
|
805
|
+
function resolvePlaceholderRunSizes(slideRoot, ctx) {
|
|
806
|
+
if (!ctx.layoutRoot && !ctx.masterRoot) return;
|
|
807
|
+
for (const sp of descendantsByTag(slideRoot, OOXML_NS.p, "sp")) {
|
|
808
|
+
const ph = placeholderOf(sp);
|
|
809
|
+
if (!ph) continue;
|
|
810
|
+
const txBody = firstChild(sp, "p:txBody");
|
|
811
|
+
if (!txBody) continue;
|
|
812
|
+
const type = attr(ph, "type");
|
|
813
|
+
const idx = attr(ph, "idx") ?? "0";
|
|
814
|
+
const slideLst = firstChild(txBody, "a:lstStyle");
|
|
815
|
+
const byLevel = /* @__PURE__ */ new Map();
|
|
816
|
+
for (const p of getElements(txBody, "a:p")) {
|
|
817
|
+
const pPr = firstChild(p, "a:pPr");
|
|
818
|
+
const level = (pPr && intValue(attr(pPr, "lvl"))) ?? 0;
|
|
819
|
+
const runs = [...getElements(p, "a:r"), ...getElements(p, "a:fld")];
|
|
820
|
+
if (runs.length === 0) continue;
|
|
821
|
+
let props = byLevel.get(level);
|
|
822
|
+
if (props === void 0) {
|
|
823
|
+
props = placeholderInheritedRunProps(type, idx, level, ctx);
|
|
824
|
+
byLevel.set(level, props);
|
|
825
|
+
}
|
|
826
|
+
if (!props) continue;
|
|
827
|
+
for (const run of runs) writeRunProps(run, props, pPr, slideLst, level);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
/** Write each resolved run property onto a run's `a:rPr`, skipping ones the slide already fixes. */
|
|
832
|
+
function writeRunProps(run, props, pPr, slideLst, level) {
|
|
833
|
+
let rPr = null;
|
|
834
|
+
for (const name of RUN_PROP_NAMES) {
|
|
835
|
+
const value = props[name];
|
|
836
|
+
if (value == null) continue;
|
|
837
|
+
if (slideDefinesProp(name, run, pPr, slideLst, level)) continue;
|
|
838
|
+
rPr ??= getOrAddChild(run, "a:rPr", ["a:t"]);
|
|
839
|
+
setAttr(rPr, name, value);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Resolve each shape's `p:style` `lnRef`/`fillRef`/`effectRef` into explicit
|
|
844
|
+
* `spPr` children (using the theme `fmtScheme`), then neutralize the ref. The
|
|
845
|
+
* `fontRef` is intentionally left for the destination theme to re-resolve.
|
|
846
|
+
*/
|
|
847
|
+
function materializeStyleRefs(root, ctx) {
|
|
848
|
+
if (!ctx.fmtScheme) return;
|
|
849
|
+
for (const style of descendantsByTag(root, OOXML_NS.p, "style")) {
|
|
850
|
+
const shape = style.parentNode;
|
|
851
|
+
if (!shape) continue;
|
|
852
|
+
const spPr = getOrAddChild(shape, "p:spPr", SHAPE_AFTER_SPPR);
|
|
853
|
+
materializeFill(spPr, firstChild(style, "a:fillRef"), ctx);
|
|
854
|
+
materializeLine(spPr, firstChild(style, "a:lnRef"), ctx);
|
|
855
|
+
materializeEffect(spPr, firstChild(style, "a:effectRef"), ctx);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
function materializeFill(spPr, fillRef, ctx) {
|
|
859
|
+
if (!fillRef) return;
|
|
860
|
+
if (!FILL_CHOICES.some((q) => firstChild(spPr, q))) {
|
|
861
|
+
const fill = styleRefFill(fillRef, ctx);
|
|
862
|
+
if (fill) insertInOrder(spPr, fill, SPPR_FILL_AFTER);
|
|
863
|
+
}
|
|
864
|
+
neutralizeRef(fillRef);
|
|
865
|
+
}
|
|
866
|
+
function materializeLine(spPr, lnRef, ctx) {
|
|
867
|
+
if (!lnRef) return;
|
|
868
|
+
if (!firstChild(spPr, "a:ln")) {
|
|
869
|
+
const ln = styleRefLine(lnRef, ctx);
|
|
870
|
+
if (ln) insertInOrder(spPr, ln, SPPR_LN_AFTER);
|
|
871
|
+
}
|
|
872
|
+
neutralizeRef(lnRef);
|
|
873
|
+
}
|
|
874
|
+
function materializeEffect(spPr, effectRef, ctx) {
|
|
875
|
+
if (!effectRef) return;
|
|
876
|
+
const idx = intAttr(effectRef, "idx");
|
|
877
|
+
if (idx !== null && idx > 0 && !firstChild(spPr, "a:effectLst") && !firstChild(spPr, "a:effectDag")) {
|
|
878
|
+
const style = fmtEntry(ctx, "a:effectStyleLst", idx);
|
|
879
|
+
const ref = resolveColor(firstChildElement(effectRef), ctx);
|
|
880
|
+
if (style && ref) {
|
|
881
|
+
substitutePhClr(style, ref);
|
|
882
|
+
for (const child of childElements(style)) if (isA(child, "effectLst") || isA(child, "effectDag")) insertInOrder(spPr, child, SPPR_EFFECT_AFTER);
|
|
883
|
+
else if (isA(child, "scene3d")) insertInOrder(spPr, child, SPPR_SCENE3D_AFTER);
|
|
884
|
+
else if (isA(child, "sp3d")) insertInOrder(spPr, child, SPPR_SP3D_AFTER);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
neutralizeRef(effectRef);
|
|
888
|
+
}
|
|
889
|
+
/** Strip a style-matrix ref to `idx="0"` with no colour child so it contributes nothing. */
|
|
890
|
+
function neutralizeRef(ref) {
|
|
891
|
+
setAttr(ref, "idx", "0");
|
|
892
|
+
for (const child of childElements(ref)) ref.removeChild(child);
|
|
893
|
+
}
|
|
894
|
+
//#endregion
|
|
423
895
|
//#region src/read/api/slide-background.ts
|
|
424
896
|
/**
|
|
425
897
|
* Read a slide's effective background (`p:cSld/p:bg`), decoded into a typed union.
|
|
@@ -460,20 +932,13 @@ function decodeBackgroundFill(container, ctx, rels) {
|
|
|
460
932
|
relId: picture.relId,
|
|
461
933
|
partName: picture.partName
|
|
462
934
|
};
|
|
463
|
-
const patt =
|
|
464
|
-
if (patt) {
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
return {
|
|
471
|
-
type: "pattern",
|
|
472
|
-
preset: attr(patt, "prst") ?? null,
|
|
473
|
-
foreground: wrapColor("a:fgClr"),
|
|
474
|
-
background: wrapColor("a:bgClr")
|
|
475
|
-
};
|
|
476
|
-
}
|
|
935
|
+
const patt = readPatternFill(container, ctx);
|
|
936
|
+
if (patt) return {
|
|
937
|
+
type: "pattern",
|
|
938
|
+
preset: patt.preset,
|
|
939
|
+
foreground: patt.foreground,
|
|
940
|
+
background: patt.background
|
|
941
|
+
};
|
|
477
942
|
return { type: "none" };
|
|
478
943
|
}
|
|
479
944
|
/**
|
|
@@ -528,13 +993,6 @@ function readSlideBackground(bg, source, ctx, rels, themeRels) {
|
|
|
528
993
|
}
|
|
529
994
|
//#endregion
|
|
530
995
|
//#region src/read/api/notes.ts
|
|
531
|
-
/** One EMU coordinate from a placeholder's `p:spPr/a:xfrm` child, or `null` when absent. */
|
|
532
|
-
function xfrmEmu$1(sp, container, axis) {
|
|
533
|
-
const spPr = firstChild(sp, "p:spPr");
|
|
534
|
-
const xfrm = spPr && firstChild(spPr, "a:xfrm");
|
|
535
|
-
const el = xfrm && firstChild(xfrm, container);
|
|
536
|
-
return el ? intValue(attr(el, axis)) : null;
|
|
537
|
-
}
|
|
538
996
|
/**
|
|
539
997
|
* One placeholder shape (`p:sp`) of a notes slide. A notes slide holds a fixed set:
|
|
540
998
|
* the slide thumbnail (`type="sldImg"`), the notes body (`type="body"`), and the
|
|
@@ -584,19 +1042,19 @@ var NotesPlaceholder = class {
|
|
|
584
1042
|
}
|
|
585
1043
|
/** Left edge in EMU (`a:off/@x`), or `null` when geometry is inherited (no own `a:xfrm`). */
|
|
586
1044
|
get left() {
|
|
587
|
-
return
|
|
1045
|
+
return spPrXfrmEmu(this.sp, "a:off", "x");
|
|
588
1046
|
}
|
|
589
1047
|
/** Top edge in EMU (`a:off/@y`), or `null` when geometry is inherited. */
|
|
590
1048
|
get top() {
|
|
591
|
-
return
|
|
1049
|
+
return spPrXfrmEmu(this.sp, "a:off", "y");
|
|
592
1050
|
}
|
|
593
1051
|
/** Width in EMU (`a:ext/@cx`), or `null` when geometry is inherited. */
|
|
594
1052
|
get width() {
|
|
595
|
-
return
|
|
1053
|
+
return spPrXfrmEmu(this.sp, "a:ext", "cx");
|
|
596
1054
|
}
|
|
597
1055
|
/** Height in EMU (`a:ext/@cy`), or `null` when geometry is inherited. */
|
|
598
1056
|
get height() {
|
|
599
|
-
return
|
|
1057
|
+
return spPrXfrmEmu(this.sp, "a:ext", "cy");
|
|
600
1058
|
}
|
|
601
1059
|
/**
|
|
602
1060
|
* The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
|
|
@@ -862,8 +1320,6 @@ function readTagsForPart(opc, ownerPartName) {
|
|
|
862
1320
|
}
|
|
863
1321
|
//#endregion
|
|
864
1322
|
//#region src/read/api/chrome.ts
|
|
865
|
-
const SLIDE_MASTER_REL$4 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
866
|
-
const THEME_REL$1 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
867
1323
|
const THEME_COLOR_SLOTS = [
|
|
868
1324
|
"dk1",
|
|
869
1325
|
"lt1",
|
|
@@ -973,23 +1429,10 @@ var Theme = class {
|
|
|
973
1429
|
return themeElements ? firstChild(themeElements, "a:clrScheme") : null;
|
|
974
1430
|
}
|
|
975
1431
|
};
|
|
976
|
-
/** One EMU coordinate from a placeholder's `p:spPr/a:xfrm` child, or `null` when absent. */
|
|
977
|
-
function xfrmEmu(sp, container, axis) {
|
|
978
|
-
const spPr = firstChild(sp, "p:spPr");
|
|
979
|
-
const xfrm = spPr && firstChild(spPr, "a:xfrm");
|
|
980
|
-
const el = xfrm && firstChild(xfrm, container);
|
|
981
|
-
return el ? intValue(attr(el, axis)) : null;
|
|
982
|
-
}
|
|
983
|
-
/** The `p:ph` element of a shape (`p:sp/p:nvSpPr/p:nvPr/p:ph`), or `null` when it is not a placeholder. */
|
|
984
|
-
function placeholderElement(sp) {
|
|
985
|
-
const nvSpPr = firstChild(sp, "p:nvSpPr");
|
|
986
|
-
const nvPr = nvSpPr && firstChild(nvSpPr, "p:nvPr");
|
|
987
|
-
return nvPr ? firstChild(nvPr, "p:ph") : null;
|
|
988
|
-
}
|
|
989
1432
|
/** The placeholder shapes (`p:sp` carrying a `p:ph`) of a master/layout `p:spTree`, in document order. */
|
|
990
1433
|
function placeholderShapes(spTree) {
|
|
991
1434
|
if (!spTree) return [];
|
|
992
|
-
return getElements(spTree, "p:sp").filter((sp) =>
|
|
1435
|
+
return getElements(spTree, "p:sp").filter((sp) => placeholderOf(sp) !== null);
|
|
993
1436
|
}
|
|
994
1437
|
/**
|
|
995
1438
|
* One placeholder shape (`p:sp` with a `p:ph`) of a slide master or slide layout —
|
|
@@ -1015,12 +1458,12 @@ var Placeholder = class {
|
|
|
1015
1458
|
}
|
|
1016
1459
|
/** Placeholder type (`p:ph/@type`: `title` | `body` | `sldNum` | …), or `null` when absent (a body placeholder). */
|
|
1017
1460
|
get type() {
|
|
1018
|
-
const ph =
|
|
1461
|
+
const ph = placeholderOf(this.sp);
|
|
1019
1462
|
return ph ? attr(ph, "type") : null;
|
|
1020
1463
|
}
|
|
1021
1464
|
/** Placeholder index (`p:ph/@idx`), or `null` when unset. */
|
|
1022
1465
|
get idx() {
|
|
1023
|
-
const ph =
|
|
1466
|
+
const ph = placeholderOf(this.sp);
|
|
1024
1467
|
return ph ? attr(ph, "idx") : null;
|
|
1025
1468
|
}
|
|
1026
1469
|
/** Shape name (`p:cNvPr/@name`), or `''` when unnamed. */
|
|
@@ -1035,19 +1478,19 @@ var Placeholder = class {
|
|
|
1035
1478
|
}
|
|
1036
1479
|
/** Left edge in EMU (`a:off/@x`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1037
1480
|
get left() {
|
|
1038
|
-
return
|
|
1481
|
+
return spPrXfrmEmu(this.sp, "a:off", "x");
|
|
1039
1482
|
}
|
|
1040
1483
|
/** Top edge in EMU (`a:off/@y`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1041
1484
|
get top() {
|
|
1042
|
-
return
|
|
1485
|
+
return spPrXfrmEmu(this.sp, "a:off", "y");
|
|
1043
1486
|
}
|
|
1044
1487
|
/** Width in EMU (`a:ext/@cx`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1045
1488
|
get width() {
|
|
1046
|
-
return
|
|
1489
|
+
return spPrXfrmEmu(this.sp, "a:ext", "cx");
|
|
1047
1490
|
}
|
|
1048
1491
|
/** Height in EMU (`a:ext/@cy`), or `null` when the placeholder carries no own `a:xfrm`. */
|
|
1049
1492
|
get height() {
|
|
1050
|
-
return
|
|
1493
|
+
return spPrXfrmEmu(this.sp, "a:ext", "cy");
|
|
1051
1494
|
}
|
|
1052
1495
|
/**
|
|
1053
1496
|
* The placeholder's text as a navigable {@link TextFrame} (`p:txBody`), or `null`
|
|
@@ -1093,7 +1536,7 @@ var SlideMaster = class {
|
|
|
1093
1536
|
}
|
|
1094
1537
|
/** The master's theme (via its `theme` relationship), or `null` when absent. */
|
|
1095
1538
|
get theme() {
|
|
1096
|
-
const themePart = this.#relTarget(THEME_REL
|
|
1539
|
+
const themePart = this.#relTarget(THEME_REL);
|
|
1097
1540
|
return themePart ? new Theme(themePart) : null;
|
|
1098
1541
|
}
|
|
1099
1542
|
/**
|
|
@@ -1137,7 +1580,7 @@ var SlideMaster = class {
|
|
|
1137
1580
|
get background() {
|
|
1138
1581
|
const bg = backgroundElementOf(this.part.dom.documentElement);
|
|
1139
1582
|
if (!bg) return null;
|
|
1140
|
-
const themePart = this.#relTarget(THEME_REL
|
|
1583
|
+
const themePart = this.#relTarget(THEME_REL);
|
|
1141
1584
|
return readSlideBackground(bg, "master", this.themeContext(), this.opc.relationshipsFor(this.partName), themePart ? this.opc.relationshipsFor(themePart.partName) : null);
|
|
1142
1585
|
}
|
|
1143
1586
|
/**
|
|
@@ -1197,7 +1640,7 @@ var SlideLayout = class {
|
|
|
1197
1640
|
/** The master this layout is built on (via its `slideMaster` relationship), or `null` when absent. */
|
|
1198
1641
|
get master() {
|
|
1199
1642
|
const rels = this.opc.relationshipsFor(this.partName);
|
|
1200
|
-
const rel = rels.byType(SLIDE_MASTER_REL
|
|
1643
|
+
const rel = rels.byType(SLIDE_MASTER_REL)[0];
|
|
1201
1644
|
const part = rel ? this.opc.part(rels.resolveTarget(rel.id)) : null;
|
|
1202
1645
|
return part ? new SlideMaster(this.opc, part) : null;
|
|
1203
1646
|
}
|
|
@@ -1368,7 +1811,8 @@ function buildTransitionElement(doc, input, speed, withDur) {
|
|
|
1368
1811
|
if (withDur && typeof input.durationMs === "number") setAttr(transition, "p14:dur", String(Math.round(input.durationMs)));
|
|
1369
1812
|
if (input.advanceOnClick === false) setAttr(transition, "advClick", "0");
|
|
1370
1813
|
if (typeof input.advanceAfterMs === "number") setAttr(transition, "advTm", String(Math.round(input.advanceAfterMs)));
|
|
1371
|
-
const
|
|
1814
|
+
const prefix = input.namespace ?? "p";
|
|
1815
|
+
const type = createElement(doc, `${prefix}:${input.type}`);
|
|
1372
1816
|
for (const [name, value] of Object.entries(input.variant ?? {})) setAttr(type, name, value);
|
|
1373
1817
|
transition.appendChild(type);
|
|
1374
1818
|
return transition;
|
|
@@ -1598,7 +2042,8 @@ function getOrCreateMainSeqChildTnLst(root, doc) {
|
|
|
1598
2042
|
}
|
|
1599
2043
|
let tmRootChildLst = timing.getElementsByTagNameNS(P_NS, "childTnLst")[0];
|
|
1600
2044
|
if (!tmRootChildLst) {
|
|
1601
|
-
const
|
|
2045
|
+
const fresh = importScaffold(doc, TIMING_SCAFFOLD);
|
|
2046
|
+
const freshTnLst = firstChild(fresh, "p:tnLst");
|
|
1602
2047
|
if (freshTnLst) insertInOrder(timing, freshTnLst, ["p:bldLst", "p:extLst"]);
|
|
1603
2048
|
tmRootChildLst = timing.getElementsByTagNameNS(P_NS, "childTnLst")[0];
|
|
1604
2049
|
}
|
|
@@ -1670,9 +2115,6 @@ function carryShapeAnimations(sourceRoot, targetRoot, spidMap) {
|
|
|
1670
2115
|
}
|
|
1671
2116
|
//#endregion
|
|
1672
2117
|
//#region src/read/api/slide.ts
|
|
1673
|
-
const IMAGE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
1674
|
-
const NOTES_SLIDE_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
|
|
1675
|
-
const SLIDE_LAYOUT_REL_TYPE = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
1676
2118
|
/** Recognize a handful of common raster formats from their leading bytes. */
|
|
1677
2119
|
function sniffImageType(bytes) {
|
|
1678
2120
|
const b = bytes;
|
|
@@ -1947,7 +2389,7 @@ var Slide = class {
|
|
|
1947
2389
|
* slide-number field round-trip either way.
|
|
1948
2390
|
*/
|
|
1949
2391
|
get notesSlide() {
|
|
1950
|
-
const notesRel = this.relationships.byType(
|
|
2392
|
+
const notesRel = this.relationships.byType(NOTES_SLIDE_REL)[0];
|
|
1951
2393
|
if (!notesRel) return null;
|
|
1952
2394
|
const notesPart = this.presentation.opc.part(this.relationships.resolveTarget(notesRel.id));
|
|
1953
2395
|
if (!notesPart) return null;
|
|
@@ -1960,7 +2402,7 @@ var Slide = class {
|
|
|
1960
2402
|
* {@link master} and {@link theme} through it (`slide.layout.master.theme`).
|
|
1961
2403
|
*/
|
|
1962
2404
|
get layout() {
|
|
1963
|
-
const rel = this.relationships.byType(
|
|
2405
|
+
const rel = this.relationships.byType(SLIDE_LAYOUT_REL)[0];
|
|
1964
2406
|
if (!rel) return null;
|
|
1965
2407
|
const part = this.presentation.opc.part(this.relationships.resolveTarget(rel.id));
|
|
1966
2408
|
return part ? new SlideLayout(this.presentation.opc, part) : null;
|
|
@@ -2167,7 +2609,7 @@ var Slide = class {
|
|
|
2167
2609
|
const opc = this.presentation.opc;
|
|
2168
2610
|
const mediaPartName = opc.reserveMediaPartName(extension);
|
|
2169
2611
|
opc.addPart(mediaPartName, contentType, image);
|
|
2170
|
-
const relId = this.relationships.add(
|
|
2612
|
+
const relId = this.relationships.add(IMAGE_REL, relativePartName(this.partName, mediaPartName)).id;
|
|
2171
2613
|
const id = this.#nextShapeId();
|
|
2172
2614
|
const pic = buildPicture(doc, {
|
|
2173
2615
|
id,
|
|
@@ -2240,7 +2682,8 @@ function buildTextBox(doc, spec) {
|
|
|
2240
2682
|
const cNvPr = append(nvSpPr, "p:cNvPr");
|
|
2241
2683
|
setAttr(cNvPr, "id", String(spec.id));
|
|
2242
2684
|
setAttr(cNvPr, "name", spec.name);
|
|
2243
|
-
|
|
2685
|
+
const cNvSpPr = append(nvSpPr, "p:cNvSpPr");
|
|
2686
|
+
setAttr(cNvSpPr, "txBox", "1");
|
|
2244
2687
|
append(nvSpPr, "p:nvPr");
|
|
2245
2688
|
const spPr = append(sp, "p:spPr");
|
|
2246
2689
|
const xfrm = append(spPr, "a:xfrm");
|
|
@@ -2277,10 +2720,12 @@ function buildPicture(doc, spec) {
|
|
|
2277
2720
|
const cNvPr = append(nvPicPr, "p:cNvPr");
|
|
2278
2721
|
setAttr(cNvPr, "id", String(spec.id));
|
|
2279
2722
|
setAttr(cNvPr, "name", spec.name);
|
|
2280
|
-
|
|
2723
|
+
const picLocks = append(append(nvPicPr, "p:cNvPicPr"), "a:picLocks");
|
|
2724
|
+
setAttr(picLocks, "noChangeAspect", "1");
|
|
2281
2725
|
append(nvPicPr, "p:nvPr");
|
|
2282
2726
|
const blipFill = append(pic, "p:blipFill");
|
|
2283
|
-
|
|
2727
|
+
const blip = append(blipFill, "a:blip");
|
|
2728
|
+
setAttr(blip, "r:embed", spec.relId);
|
|
2284
2729
|
append(append(blipFill, "a:stretch"), "a:fillRect");
|
|
2285
2730
|
const spPr = append(pic, "p:spPr");
|
|
2286
2731
|
const xfrm = append(spPr, "a:xfrm");
|
|
@@ -2404,7 +2849,7 @@ function readCustomProperties(opc) {
|
|
|
2404
2849
|
return out;
|
|
2405
2850
|
}
|
|
2406
2851
|
//#endregion
|
|
2407
|
-
//#region src/read/
|
|
2852
|
+
//#region src/read/oxml/slide-dom.ts
|
|
2408
2853
|
/** The `p:cSld@name` of a slide/layout/master part (`''` when absent). */
|
|
2409
2854
|
function cSldName(part) {
|
|
2410
2855
|
const root = part?.dom.documentElement;
|
|
@@ -2468,7 +2913,7 @@ function collectElements(node, out) {
|
|
|
2468
2913
|
for (let child = node.firstChild; child; child = child.nextSibling) if (child.nodeType === 1) collectElements(child, out);
|
|
2469
2914
|
}
|
|
2470
2915
|
//#endregion
|
|
2471
|
-
//#region src/read/api/rescale.ts
|
|
2916
|
+
//#region src/read/api/ops/rescale.ts
|
|
2472
2917
|
/** Build the EMU transform mapping source-canvas coordinates onto the target canvas (see {@link ImportSlideOptions.rescale}). */
|
|
2473
2918
|
function computeRescale(source, target, mode) {
|
|
2474
2919
|
if (mode === "stretch") return {
|
|
@@ -2514,7 +2959,6 @@ function rescaleSpTree(spTree, t) {
|
|
|
2514
2959
|
case "graphicFrame":
|
|
2515
2960
|
rescaleXfrm(firstChild(el, "p:xfrm"), t);
|
|
2516
2961
|
rescaleTableGrid(el, t);
|
|
2517
|
-
break;
|
|
2518
2962
|
}
|
|
2519
2963
|
}
|
|
2520
2964
|
}
|
|
@@ -2557,7 +3001,7 @@ function rescaleTableGrid(graphicFrame, t) {
|
|
|
2557
3001
|
}
|
|
2558
3002
|
}
|
|
2559
3003
|
//#endregion
|
|
2560
|
-
//#region src/read/api/table-styles.ts
|
|
3004
|
+
//#region src/read/api/ops/table-styles.ts
|
|
2561
3005
|
const TABLE_STYLES_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/tableStyles";
|
|
2562
3006
|
const TABLE_STYLES_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml";
|
|
2563
3007
|
const TABLE_STYLES_DEFAULT_GUID = "{5C22544A-7EE6-4342-B048-85BDC9FD1C3A}";
|
|
@@ -2662,7 +3106,7 @@ function ensureTableStylesPart(dest) {
|
|
|
2662
3106
|
return part;
|
|
2663
3107
|
}
|
|
2664
3108
|
//#endregion
|
|
2665
|
-
//#region src/read/api/master-registry.ts
|
|
3109
|
+
//#region src/read/api/ops/master-registry.ts
|
|
2666
3110
|
/**
|
|
2667
3111
|
* Slide-master bookkeeping in `presentation.xml` and in a master's own
|
|
2668
3112
|
* `p:sldLayoutIdLst`: registering a master so renderers treat it as active,
|
|
@@ -2674,9 +3118,6 @@ function ensureTableStylesPart(dest) {
|
|
|
2674
3118
|
* they take a structural {@link DeckTarget} rather than a `Presentation`. The
|
|
2675
3119
|
* source-side half that drives most of them is `part-copy.ts`.
|
|
2676
3120
|
*/
|
|
2677
|
-
const SLIDE_MASTER_REL$3 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
2678
|
-
const SLIDE_LAYOUT_REL$3 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
2679
|
-
const MIN_SLIDE_MASTER_ID = 2147483648;
|
|
2680
3121
|
/**
|
|
2681
3122
|
* Wire a freshly-copied slide master into `presentation.xml`: add a
|
|
2682
3123
|
* presentation→master relationship and a `p:sldMasterId` entry in
|
|
@@ -2688,8 +3129,8 @@ const MIN_SLIDE_MASTER_ID = 2147483648;
|
|
|
2688
3129
|
function registerMaster(dest, masterPartName) {
|
|
2689
3130
|
const presPart = dest.presentationPart;
|
|
2690
3131
|
const presRels = dest.opc.relationshipsFor(presPart.partName);
|
|
2691
|
-
for (const rel of presRels.byType(SLIDE_MASTER_REL
|
|
2692
|
-
const relId = presRels.add(SLIDE_MASTER_REL
|
|
3132
|
+
for (const rel of presRels.byType(SLIDE_MASTER_REL)) if (presRels.resolveTarget(rel.id) === masterPartName) return;
|
|
3133
|
+
const relId = presRels.add(SLIDE_MASTER_REL, relativePartName(presPart.partName, masterPartName)).id;
|
|
2693
3134
|
const root = presPart.dom.documentElement;
|
|
2694
3135
|
if (!root) throw new PackageReadError("package/part-has-no-root", "presentation.xml has no document element to register a master in");
|
|
2695
3136
|
const lst = getOrAddChild(root, "p:sldMasterIdLst", [
|
|
@@ -2751,7 +3192,7 @@ function promoteMasters(dest, masterPartNames) {
|
|
|
2751
3192
|
* allocation so ids appended earlier in the same import are counted.
|
|
2752
3193
|
*/
|
|
2753
3194
|
function nextMasterLayoutId(dest) {
|
|
2754
|
-
let max =
|
|
3195
|
+
let max = 2147483647;
|
|
2755
3196
|
const presPart = dest.presentationPart;
|
|
2756
3197
|
const root = presPart.dom.documentElement;
|
|
2757
3198
|
const masterLst = root && firstChild(root, "p:sldMasterIdLst");
|
|
@@ -2760,7 +3201,7 @@ function nextMasterLayoutId(dest) {
|
|
|
2760
3201
|
if (id !== null && id > max) max = id;
|
|
2761
3202
|
}
|
|
2762
3203
|
const presRels = dest.opc.relationshipsFor(presPart.partName);
|
|
2763
|
-
for (const rel of presRels.byType(SLIDE_MASTER_REL
|
|
3204
|
+
for (const rel of presRels.byType(SLIDE_MASTER_REL)) {
|
|
2764
3205
|
const masterRoot = dest.opc.part(presRels.resolveTarget(rel.id))?.dom.documentElement;
|
|
2765
3206
|
const layoutLst = masterRoot && firstChild(masterRoot, "p:sldLayoutIdLst");
|
|
2766
3207
|
if (!layoutLst) continue;
|
|
@@ -2790,7 +3231,7 @@ function addLayoutToMaster(dest, masterPartName, layoutPartName) {
|
|
|
2790
3231
|
const masterPart = dest.opc.part(masterPartName);
|
|
2791
3232
|
const root = masterPart?.dom.documentElement;
|
|
2792
3233
|
if (!masterPart || !root) return;
|
|
2793
|
-
const relId = dest.opc.relationshipsFor(masterPartName).add(SLIDE_LAYOUT_REL
|
|
3234
|
+
const relId = dest.opc.relationshipsFor(masterPartName).add(SLIDE_LAYOUT_REL, relativePartName(masterPartName, layoutPartName)).id;
|
|
2794
3235
|
const lst = getOrAddChild(root, "p:sldLayoutIdLst", [
|
|
2795
3236
|
"p:transition",
|
|
2796
3237
|
"p:timing",
|
|
@@ -2805,10 +3246,7 @@ function addLayoutToMaster(dest, masterPartName, layoutPartName) {
|
|
|
2805
3246
|
masterPart.markDirty();
|
|
2806
3247
|
}
|
|
2807
3248
|
//#endregion
|
|
2808
|
-
//#region src/read/api/part-copy.ts
|
|
2809
|
-
const SLIDE_LAYOUT_REL$2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
2810
|
-
const SLIDE_MASTER_REL$2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
2811
|
-
const NOTES_SLIDE_REL$2 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
|
|
3249
|
+
//#region src/read/api/ops/part-copy.ts
|
|
2812
3250
|
const SLIDE_MASTER_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml";
|
|
2813
3251
|
const SLIDE_LAYOUT_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml";
|
|
2814
3252
|
/**
|
|
@@ -2833,8 +3271,8 @@ function copyPart(ctx, sourcePartName) {
|
|
|
2833
3271
|
const sourceRels = ctx.source.relationshipsFor(sourcePartName);
|
|
2834
3272
|
const targetRels = ctx.dest.opc.relationshipsFor(newPartName);
|
|
2835
3273
|
for (const rel of sourceRels) {
|
|
2836
|
-
if (rel.type ===
|
|
2837
|
-
if (isMaster && rel.type ===
|
|
3274
|
+
if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") continue;
|
|
3275
|
+
if (isMaster && rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") continue;
|
|
2838
3276
|
if (rel.targetMode === "External") {
|
|
2839
3277
|
targetRels.addWithId(rel.id, rel.type, rel.target, "External");
|
|
2840
3278
|
continue;
|
|
@@ -2857,13 +3295,76 @@ function copyPart(ctx, sourcePartName) {
|
|
|
2857
3295
|
* accumulates exactly the imported layouts.
|
|
2858
3296
|
*/
|
|
2859
3297
|
function linkLayoutIntoMaster(ctx, layoutSourceRels, layoutPartName) {
|
|
2860
|
-
const masterRel = layoutSourceRels.byType(SLIDE_MASTER_REL
|
|
3298
|
+
const masterRel = layoutSourceRels.byType(SLIDE_MASTER_REL)[0];
|
|
2861
3299
|
if (!masterRel) return;
|
|
2862
3300
|
const masterPartName = ctx.registry.get(layoutSourceRels.resolveTarget(masterRel.id));
|
|
2863
3301
|
if (!masterPartName) return;
|
|
2864
3302
|
addLayoutToMaster(ctx.dest, masterPartName, layoutPartName);
|
|
2865
3303
|
}
|
|
2866
3304
|
//#endregion
|
|
3305
|
+
//#region src/read/api/ops/carried-rels.ts
|
|
3306
|
+
/**
|
|
3307
|
+
* Relationship rewriting for content carried between packages.
|
|
3308
|
+
*
|
|
3309
|
+
* When a shape or decoration is deep-copied out of a source deck, every relationship
|
|
3310
|
+
* reference it carries (`r:embed` on a picture fill, `r:id` on a chart frame, `r:link` on a
|
|
3311
|
+
* linked image, …) still names an id in the *source* part's relationship list. Left alone
|
|
3312
|
+
* those ids either dangle or — worse — silently resolve to whatever the destination part
|
|
3313
|
+
* happens to have under the same id. This rewrites each one to a fresh relationship on the
|
|
3314
|
+
* destination part, copying the referenced part into the destination package on first sight.
|
|
3315
|
+
*
|
|
3316
|
+
* Both functions are pure with respect to the deck: everything they touch arrives as an
|
|
3317
|
+
* argument. They were private methods on `Presentation` and used no instance state, which
|
|
3318
|
+
* made the import subsystem look more entangled with the read model than it is.
|
|
3319
|
+
*/
|
|
3320
|
+
/**
|
|
3321
|
+
* Rewrite every relationship reference inside a carried subtree to a fresh destination-local id.
|
|
3322
|
+
* @param {Element} node - root of the carried subtree (rewritten in place)
|
|
3323
|
+
* @param {ImportContext} ctx - the open import (destination deck + source package + copy registry)
|
|
3324
|
+
* @param {Relationships} sourceRels - relationships of the source part the subtree came from
|
|
3325
|
+
* @param {string} destPartName - partname of the destination part the subtree is being placed in
|
|
3326
|
+
* @param {Relationships} destRels - relationships of that destination part
|
|
3327
|
+
* @param {Map<string, string>} relIdMap - per-call cache (source part + source id → new id) deduping references shared within one import
|
|
3328
|
+
*/
|
|
3329
|
+
function rewriteCarriedRels(node, ctx, sourceRels, destPartName, destRels, relIdMap) {
|
|
3330
|
+
const elements = [];
|
|
3331
|
+
collectElements(node, elements);
|
|
3332
|
+
for (const el of elements) {
|
|
3333
|
+
const refs = [];
|
|
3334
|
+
const attrs = el.attributes;
|
|
3335
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
3336
|
+
const a = attrs.item(i);
|
|
3337
|
+
if (!a || a.namespaceURI !== OOXML_NS.r || !a.value) continue;
|
|
3338
|
+
if (!sourceRels.get(a.value)) continue;
|
|
3339
|
+
refs.push({
|
|
3340
|
+
local: a.localName ?? a.name,
|
|
3341
|
+
id: a.value
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
for (const { local, id } of refs) setAttr(el, `r:${local}`, carryRel(ctx, sourceRels, id, destPartName, destRels, relIdMap));
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
/**
|
|
3348
|
+
* Resolve one carried relationship to a fresh destination-local id, copying its internal target.
|
|
3349
|
+
* @param {ImportContext} ctx - the open import
|
|
3350
|
+
* @param {Relationships} sourceRels - relationships of the source part
|
|
3351
|
+
* @param {string} id - the source relationship id to carry
|
|
3352
|
+
* @param {string} destPartName - partname of the destination part
|
|
3353
|
+
* @param {Relationships} destRels - relationships of that destination part
|
|
3354
|
+
* @param {Map<string, string>} relIdMap - per-call dedupe cache
|
|
3355
|
+
* @return {string} the new relationship id on the destination part
|
|
3356
|
+
*/
|
|
3357
|
+
function carryRel(ctx, sourceRels, id, destPartName, destRels, relIdMap) {
|
|
3358
|
+
const key = `${sourceRels.sourcePartName}|${id}`;
|
|
3359
|
+
const cached = relIdMap.get(key);
|
|
3360
|
+
if (cached) return cached;
|
|
3361
|
+
const rel = sourceRels.get(id);
|
|
3362
|
+
if (!rel) throw new InvalidOptionError("relationship/not-found", `Relationships of ${sourceRels.sourcePartName}: no relationship with id ${id}`);
|
|
3363
|
+
const newId = rel.targetMode === "External" ? destRels.add(rel.type, rel.target, "External").id : destRels.add(rel.type, relativePartName(destPartName, copyPart(ctx, sourceRels.resolveTarget(id)))).id;
|
|
3364
|
+
relIdMap.set(key, newId);
|
|
3365
|
+
return newId;
|
|
3366
|
+
}
|
|
3367
|
+
//#endregion
|
|
2867
3368
|
//#region src/read/api/ops/embedded-fonts.ts
|
|
2868
3369
|
/**
|
|
2869
3370
|
* Merging embedded font faces into a deck's `p:embeddedFontLst`.
|
|
@@ -3047,6 +3548,277 @@ function mergeEmbeddedFontEntries(dest, entries) {
|
|
|
3047
3548
|
if (copiedAny) presPart.markDirty();
|
|
3048
3549
|
}
|
|
3049
3550
|
//#endregion
|
|
3551
|
+
//#region src/read/api/ops/flatten-context.ts
|
|
3552
|
+
/**
|
|
3553
|
+
* The theme context a source slide is flattened against.
|
|
3554
|
+
*
|
|
3555
|
+
* `preserve`-mode import bakes a slide's *source* look into its XML — scheme colours and
|
|
3556
|
+
* style-matrix fills resolved to literals — so it keeps that look after being attached to a
|
|
3557
|
+
* different deck's master. Doing so needs the source subgraph's colour map, colour scheme,
|
|
3558
|
+
* format scheme, and the background the slide inherits but does not itself carry. This
|
|
3559
|
+
* gathers exactly that.
|
|
3560
|
+
*
|
|
3561
|
+
* Pure with respect to the destination deck: it reads only the source package. It was a
|
|
3562
|
+
* private method on `Presentation` that used no instance state.
|
|
3563
|
+
*/
|
|
3564
|
+
/** The `p:cSld/p:bg` element of a slide/layout/master root, or `null`. */
|
|
3565
|
+
function backgroundOf(root) {
|
|
3566
|
+
const cSld = firstChild(root, "p:cSld");
|
|
3567
|
+
return cSld ? firstChild(cSld, "p:bg") : null;
|
|
3568
|
+
}
|
|
3569
|
+
/**
|
|
3570
|
+
* The background the slide effectively inherits from its source subgraph: the layout's
|
|
3571
|
+
* `p:bg`, else the master's. Returns `null` when the slide carries its own `p:bg` (it stays
|
|
3572
|
+
* on the slide and is flattened directly) or when none exists.
|
|
3573
|
+
*/
|
|
3574
|
+
function effectiveBackground(sourceOpc, slideRoot, layoutPartName, masterPartName) {
|
|
3575
|
+
if (slideRoot && backgroundOf(slideRoot)) return null;
|
|
3576
|
+
const layoutRoot = layoutPartName ? sourceOpc.part(layoutPartName)?.dom.documentElement ?? null : null;
|
|
3577
|
+
const masterRoot = masterPartName ? sourceOpc.part(masterPartName)?.dom.documentElement ?? null : null;
|
|
3578
|
+
return (layoutRoot && backgroundOf(layoutRoot)) ?? (masterRoot && backgroundOf(masterRoot)) ?? null;
|
|
3579
|
+
}
|
|
3580
|
+
/**
|
|
3581
|
+
* Gather the flatten context for a source slide: walk slide → layout → master → theme,
|
|
3582
|
+
* reading the effective colour map (the slide's `clrMapOvr` override, or the master
|
|
3583
|
+
* `clrMap`), the theme `clrScheme`, and the theme `fmtScheme`.
|
|
3584
|
+
* @param {OpcPackage} sourceOpc - the source package to read
|
|
3585
|
+
* @param {string} slidePartName - partname of the source slide
|
|
3586
|
+
* @return {FlattenContext} the context {@link flattenSlide} / {@link flattenShape} resolve against
|
|
3587
|
+
*/
|
|
3588
|
+
function sourceFlattenContext(sourceOpc, slidePartName) {
|
|
3589
|
+
const parts = resolveSlideThemeParts(sourceOpc, slidePartName);
|
|
3590
|
+
const themeElements = parts.themeElements;
|
|
3591
|
+
return {
|
|
3592
|
+
clrMap: parts.clrMap,
|
|
3593
|
+
clrScheme: parts.clrScheme,
|
|
3594
|
+
fmtScheme: themeElements ? firstChild(themeElements, "a:fmtScheme") : null,
|
|
3595
|
+
inheritedBackground: effectiveBackground(sourceOpc, parts.slideRoot, parts.layoutPartName, parts.masterPartName),
|
|
3596
|
+
layoutRoot: parts.layoutRoot,
|
|
3597
|
+
masterRoot: parts.masterRoot
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
3600
|
+
//#endregion
|
|
3601
|
+
//#region src/read/api/ops/part-index.ts
|
|
3602
|
+
/**
|
|
3603
|
+
* Walking a deck's master/layout spine by partname.
|
|
3604
|
+
*
|
|
3605
|
+
* The `p:sldMasterIdLst` / `p:sldLayoutIdLst` entries hold relationship ids, not partnames, so
|
|
3606
|
+
* every traversal has to resolve through the owning part's relationships. These are the
|
|
3607
|
+
* source-side reads that do it — used against a *foreign* presentation as often as against this
|
|
3608
|
+
* one, which is why they take the deck rather than living on it.
|
|
3609
|
+
*/
|
|
3610
|
+
/** Master partnames in `p:sldMasterIdLst` order. */
|
|
3611
|
+
function slideMasterPartNames(pres) {
|
|
3612
|
+
const root = pres.presentationPart.dom.documentElement;
|
|
3613
|
+
const lst = root && firstChild(root, "p:sldMasterIdLst");
|
|
3614
|
+
if (!lst) return [];
|
|
3615
|
+
const rels = pres.opc.relationshipsFor(pres.presentationPart.partName);
|
|
3616
|
+
const out = [];
|
|
3617
|
+
for (const entry of getElements(lst, "p:sldMasterId")) {
|
|
3618
|
+
const relId = attr(entry, "r:id");
|
|
3619
|
+
if (relId) out.push(rels.resolveTarget(relId));
|
|
3620
|
+
}
|
|
3621
|
+
return out;
|
|
3622
|
+
}
|
|
3623
|
+
/** A master's layout partnames in `p:sldLayoutIdLst` order. */
|
|
3624
|
+
function layoutPartNamesOf(pres, masterPartName) {
|
|
3625
|
+
const root = pres.opc.part(masterPartName)?.dom.documentElement;
|
|
3626
|
+
const lst = root && firstChild(root, "p:sldLayoutIdLst");
|
|
3627
|
+
if (!lst) return [];
|
|
3628
|
+
const rels = pres.opc.relationshipsFor(masterPartName);
|
|
3629
|
+
const out = [];
|
|
3630
|
+
for (const entry of getElements(lst, "p:sldLayoutId")) {
|
|
3631
|
+
const relId = attr(entry, "r:id");
|
|
3632
|
+
if (relId) out.push(rels.resolveTarget(relId));
|
|
3633
|
+
}
|
|
3634
|
+
return out;
|
|
3635
|
+
}
|
|
3636
|
+
//#endregion
|
|
3637
|
+
//#region src/read/api/ops/import-slide.ts
|
|
3638
|
+
/**
|
|
3639
|
+
* The three ways a slide can be brought across from another deck.
|
|
3640
|
+
*
|
|
3641
|
+
* {@link Presentation.importSlide} picks one by `options.theme`, and the difference between
|
|
3642
|
+
* them is entirely about what happens to the slide's *look*:
|
|
3643
|
+
*
|
|
3644
|
+
* - **`copy`** — not here. It copies the source theme subgraph wholesale via `copyPart`, so
|
|
3645
|
+
* the slide keeps its look by keeping its own theme; nothing needs rewriting.
|
|
3646
|
+
* - **`preserve`** ({@link importSlidePreserve}) — rebind to this deck's master, then bake
|
|
3647
|
+
* the source theme into the slide XML. The slide looks the same as it did; it just no
|
|
3648
|
+
* longer depends on the theme it came from.
|
|
3649
|
+
* - **`restyle`** ({@link importSlideRestyle}) — rebind and bake *nothing*, so the slide's
|
|
3650
|
+
* symbolic theme references re-resolve against the destination theme and it re-brands.
|
|
3651
|
+
*
|
|
3652
|
+
* The two share {@link importSlideRebind} and are exact inverses on top of it: preserve
|
|
3653
|
+
* resolves every reference it can, restyle resolves none. Any baking added to the shared
|
|
3654
|
+
* rebind would break restyle by pinning the slide to its source look, which is why the
|
|
3655
|
+
* rebind carries no theme handling of its own — not even the inherited background.
|
|
3656
|
+
*
|
|
3657
|
+
* These were private methods on `Presentation`, where they made the read model's own surface
|
|
3658
|
+
* (open a deck, walk slides, read shapes) hard to see: the import machinery was roughly two
|
|
3659
|
+
* thirds of the class. They live here as free functions taking the destination deck, matching
|
|
3660
|
+
* the other `ops/` modules.
|
|
3661
|
+
*/
|
|
3662
|
+
/**
|
|
3663
|
+
* Import a slide in `preserve` mode: rebind it to this deck's master/layout, then flatten its
|
|
3664
|
+
* source theme into the slide XML (scheme colours + style-matrix fills baked to literals).
|
|
3665
|
+
*
|
|
3666
|
+
* The flatten context is gathered from the *source* subgraph, so it can be read before or
|
|
3667
|
+
* after the rebind; the rebind injects any carried decorations before we flatten, so a single
|
|
3668
|
+
* sweep resolves the theme references on the slide's own content and on the carried
|
|
3669
|
+
* decorations together.
|
|
3670
|
+
* @param {Presentation} dest - the destination deck
|
|
3671
|
+
* @param {ImportContext} ctx - the open import out of the source package
|
|
3672
|
+
* @param {Presentation} source - the source deck
|
|
3673
|
+
* @param {Slide} sourceSlide - the slide being imported
|
|
3674
|
+
* @param {boolean} carryGraphics - bake the source master/layout decorations onto the slide
|
|
3675
|
+
* @return {string} partname of the new slide part in the destination package
|
|
3676
|
+
*/
|
|
3677
|
+
function importSlidePreserve(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
3678
|
+
const flattenCtx = sourceFlattenContext(source.opc, sourceSlide.partName);
|
|
3679
|
+
const { newPartName, slideRoot, newPart } = importSlideRebind(dest, ctx, source, sourceSlide, carryGraphics);
|
|
3680
|
+
flattenSlide(slideRoot, flattenCtx);
|
|
3681
|
+
newPart.markDirty();
|
|
3682
|
+
return newPartName;
|
|
3683
|
+
}
|
|
3684
|
+
/**
|
|
3685
|
+
* Import a slide in `restyle` mode: rebind it to this deck's master/layout and then
|
|
3686
|
+
* {@link restyleSlide} it — drop its colour map override but bake *nothing*, so its symbolic
|
|
3687
|
+
* theme references re-resolve against the destination theme and the slide re-brands.
|
|
3688
|
+
*
|
|
3689
|
+
* The deliberate inverse of `preserve`: no flatten, no inherited-background bake, no
|
|
3690
|
+
* placeholder colour/size/geometry bake — every one of those would pin the slide to its
|
|
3691
|
+
* source look, the opposite of re-branding. Carried decorations are left symbolic too, so
|
|
3692
|
+
* they re-brand along with the slide.
|
|
3693
|
+
*
|
|
3694
|
+
* With `remapLiterals` it additionally force-remaps the slide's source-theme literal colours
|
|
3695
|
+
* back to symbolic scheme colours and copies any referenced source table style into this deck
|
|
3696
|
+
* — the two things plain `restyle` cannot re-brand.
|
|
3697
|
+
* @param {Presentation} dest - the destination deck
|
|
3698
|
+
* @param {ImportContext} ctx - the open import out of the source package
|
|
3699
|
+
* @param {Presentation} source - the source deck
|
|
3700
|
+
* @param {Slide} sourceSlide - the slide being imported
|
|
3701
|
+
* @param {boolean} carryGraphics - bake the source master/layout decorations onto the slide
|
|
3702
|
+
* @param {boolean} remapLiterals - additionally remap source-theme literal colours to scheme tokens
|
|
3703
|
+
* @return {string} partname of the new slide part in the destination package
|
|
3704
|
+
*/
|
|
3705
|
+
function importSlideRestyle(dest, ctx, source, sourceSlide, carryGraphics, remapLiterals) {
|
|
3706
|
+
const { newPartName, slideRoot, newPart } = importSlideRebind(dest, ctx, source, sourceSlide, carryGraphics);
|
|
3707
|
+
restyleSlide(slideRoot);
|
|
3708
|
+
if (remapLiterals) {
|
|
3709
|
+
const parts = resolveSlideThemeParts(source.opc, sourceSlide.partName);
|
|
3710
|
+
remapLiteralColors(slideRoot, {
|
|
3711
|
+
clrMap: parts.clrMap,
|
|
3712
|
+
clrScheme: parts.clrScheme
|
|
3713
|
+
});
|
|
3714
|
+
copySourceTableStyles(dest, source.opc, slideRoot);
|
|
3715
|
+
}
|
|
3716
|
+
newPart.markDirty();
|
|
3717
|
+
return newPartName;
|
|
3718
|
+
}
|
|
3719
|
+
/**
|
|
3720
|
+
* The rebind shared by `preserve` and `restyle`: copy the slide bytes into a fresh part,
|
|
3721
|
+
* rebuild its relationships (drop notes, repoint the `slideLayout` rel at this deck's
|
|
3722
|
+
* existing layout, copy every other internal target — media/charts — and pass externals
|
|
3723
|
+
* through), and optionally bake the source master/layout decorations onto the slide. Returns
|
|
3724
|
+
* the new part, its name, and its live root element for the caller's mode-specific pass.
|
|
3725
|
+
*
|
|
3726
|
+
* This carries *no* theme baking of its own — not even the inherited background.
|
|
3727
|
+
* `preserve` adds that via {@link flattenSlide}'s context; `restyle` must not, so the
|
|
3728
|
+
* background stays symbolic and re-brands.
|
|
3729
|
+
* @param {Presentation} dest - the destination deck
|
|
3730
|
+
* @param {ImportContext} ctx - the open import out of the source package
|
|
3731
|
+
* @param {Presentation} source - the source deck
|
|
3732
|
+
* @param {Slide} sourceSlide - the slide being imported
|
|
3733
|
+
* @param {boolean} carryGraphics - bake the source master/layout decorations onto the slide
|
|
3734
|
+
* @return {RebindResult} the new part, its partname, and its live root element
|
|
3735
|
+
*/
|
|
3736
|
+
function importSlideRebind(dest, ctx, source, sourceSlide, carryGraphics) {
|
|
3737
|
+
const destLayout = destinationLayoutPartName(dest);
|
|
3738
|
+
const sourcePart = source.opc.part(sourceSlide.partName);
|
|
3739
|
+
if (!sourcePart) throw new PackageReadError("package/part-missing", `importSlide: source package has no part ${sourceSlide.partName}`);
|
|
3740
|
+
const newPartName = dest.opc.reservePartNameLike(sourceSlide.partName);
|
|
3741
|
+
const newPart = dest.opc.addPart(newPartName, sourcePart.contentType, sourcePart.bytes);
|
|
3742
|
+
const slideRoot = newPart.dom.documentElement;
|
|
3743
|
+
if (!slideRoot) throw new PackageReadError("package/part-has-no-root", `Imported slide ${newPartName} has no root element`);
|
|
3744
|
+
const sourceRels = source.opc.relationshipsFor(sourceSlide.partName);
|
|
3745
|
+
const targetRels = dest.opc.relationshipsFor(newPartName);
|
|
3746
|
+
for (const rel of sourceRels) {
|
|
3747
|
+
if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide") continue;
|
|
3748
|
+
if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout") {
|
|
3749
|
+
targetRels.addWithId(rel.id, SLIDE_LAYOUT_REL, relativePartName(newPartName, destLayout));
|
|
3750
|
+
continue;
|
|
3751
|
+
}
|
|
3752
|
+
if (rel.targetMode === "External") {
|
|
3753
|
+
targetRels.addWithId(rel.id, rel.type, rel.target, "External");
|
|
3754
|
+
continue;
|
|
3755
|
+
}
|
|
3756
|
+
const newTarget = copyPart(ctx, sourceRels.resolveTarget(rel.id));
|
|
3757
|
+
targetRels.addWithId(rel.id, rel.type, relativePartName(newPartName, newTarget));
|
|
3758
|
+
}
|
|
3759
|
+
if (carryGraphics) carryMasterGraphics(dest, ctx, slideRoot, newPartName, sourceSlide.partName);
|
|
3760
|
+
return {
|
|
3761
|
+
newPartName,
|
|
3762
|
+
slideRoot,
|
|
3763
|
+
newPart
|
|
3764
|
+
};
|
|
3765
|
+
}
|
|
3766
|
+
/**
|
|
3767
|
+
* Bake the source `slideLayout`/`slideMaster` shape-tree decorations onto the imported slide
|
|
3768
|
+
* (the `carryMasterGraphics` path). Every shape on those trees *except* placeholders is
|
|
3769
|
+
* deep-copied into the slide's `p:spTree` ahead of its own content — master decorations
|
|
3770
|
+
* first, then layout, then the slide's shapes — so document (z-)order keeps the master
|
|
3771
|
+
* furthest back. Each decoration's media and other relationship targets are copied into this
|
|
3772
|
+
* package and its `r:embed`/`r:id`/… references rewritten to fresh slide-local ids. The
|
|
3773
|
+
* injected shapes are left for the caller's {@link flattenSlide} pass to resolve any theme
|
|
3774
|
+
* references they carry.
|
|
3775
|
+
* @param {Presentation} dest - the destination deck
|
|
3776
|
+
* @param {ImportContext} ctx - the open import out of the source package
|
|
3777
|
+
* @param {Element} slideRoot - root element of the new slide part (mutated in place)
|
|
3778
|
+
* @param {string} newPartName - partname of the new slide part
|
|
3779
|
+
* @param {string} slidePartName - partname of the source slide, for resolving its layout/master
|
|
3780
|
+
*/
|
|
3781
|
+
function carryMasterGraphics(dest, ctx, slideRoot, newPartName, slidePartName) {
|
|
3782
|
+
const sourceOpc = ctx.source;
|
|
3783
|
+
const layoutPartName = resolveSingleRel(sourceOpc, slidePartName, SLIDE_LAYOUT_REL);
|
|
3784
|
+
const masterPartName = layoutPartName ? resolveSingleRel(sourceOpc, layoutPartName, SLIDE_MASTER_REL) : null;
|
|
3785
|
+
const cSld = firstChild(slideRoot, "p:cSld");
|
|
3786
|
+
const spTree = cSld && firstChild(cSld, "p:spTree");
|
|
3787
|
+
if (!spTree) return;
|
|
3788
|
+
const doc = ownerDocumentOf(slideRoot);
|
|
3789
|
+
const slideRels = dest.opc.relationshipsFor(newPartName);
|
|
3790
|
+
const relIdMap = /* @__PURE__ */ new Map();
|
|
3791
|
+
const anchor = firstShapeChild(spTree);
|
|
3792
|
+
for (const partName of [masterPartName, layoutPartName]) {
|
|
3793
|
+
if (!partName) continue;
|
|
3794
|
+
const decorations = carriedDecorations(sourceOpc.part(partName)?.dom.documentElement ?? null);
|
|
3795
|
+
if (decorations.length === 0) continue;
|
|
3796
|
+
const sourceRels = sourceOpc.relationshipsFor(partName);
|
|
3797
|
+
for (const deco of decorations) {
|
|
3798
|
+
const imported = doc.importNode(deco, true);
|
|
3799
|
+
rewriteCarriedRels(imported, ctx, sourceRels, newPartName, slideRels, relIdMap);
|
|
3800
|
+
spTree.insertBefore(imported, anchor);
|
|
3801
|
+
}
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
/**
|
|
3805
|
+
* The partname of the layout this deck's slides should attach to in `preserve` mode: the
|
|
3806
|
+
* first layout of the first slide master. Throws when the deck has no master/layout to attach
|
|
3807
|
+
* to (a deck ts-pptx always provides).
|
|
3808
|
+
* @param {Presentation} dest - the destination deck
|
|
3809
|
+
* @return {string} partname of the destination layout
|
|
3810
|
+
*/
|
|
3811
|
+
function destinationLayoutPartName(dest) {
|
|
3812
|
+
const presRels = dest.opc.relationshipsFor(dest.presentationPart.partName);
|
|
3813
|
+
const masterRel = presRels.byType(SLIDE_MASTER_REL)[0];
|
|
3814
|
+
if (!masterRel) throw new InvalidOptionError("import/destination-missing-master", "importSlide preserve mode requires a slide master in the destination deck");
|
|
3815
|
+
const masterPartName = presRels.resolveTarget(masterRel.id);
|
|
3816
|
+
const masterRels = dest.opc.relationshipsFor(masterPartName);
|
|
3817
|
+
const layoutRel = masterRels.byType(SLIDE_LAYOUT_REL)[0];
|
|
3818
|
+
if (!layoutRel) throw new InvalidOptionError("import/destination-missing-layout", "importSlide preserve mode requires a slide layout in the destination deck");
|
|
3819
|
+
return masterRels.resolveTarget(layoutRel.id);
|
|
3820
|
+
}
|
|
3821
|
+
//#endregion
|
|
3050
3822
|
//#region src/read/api/ops/notes-master.ts
|
|
3051
3823
|
/**
|
|
3052
3824
|
* Carrying speaker notes and the deck's single notes master across an import or an append.
|
|
@@ -3058,10 +3830,6 @@ function mergeEmbeddedFontEntries(dest, entries) {
|
|
|
3058
3830
|
* `Presentation`-to-`Presentation` path and the generator-XML path funnel through
|
|
3059
3831
|
* `registerNotesMaster`: they differ only in how the *part* comes into being.
|
|
3060
3832
|
*/
|
|
3061
|
-
const SLIDE_REL$1 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
|
|
3062
|
-
const NOTES_SLIDE_REL$1 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
|
|
3063
|
-
const NOTES_MASTER_REL$1 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster";
|
|
3064
|
-
const THEME_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme";
|
|
3065
3833
|
const NOTES_MASTER_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml";
|
|
3066
3834
|
const THEME_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.theme+xml";
|
|
3067
3835
|
const textEncoder$1 = new TextEncoder();
|
|
@@ -3084,24 +3852,24 @@ const textEncoder$1 = new TextEncoder();
|
|
|
3084
3852
|
*/
|
|
3085
3853
|
function carryNotes(dest, source, ctx, sourceSlidePartName, newSlidePartName) {
|
|
3086
3854
|
const sourceSlideRels = source.opc.relationshipsFor(sourceSlidePartName);
|
|
3087
|
-
const notesRel = sourceSlideRels.byType(NOTES_SLIDE_REL
|
|
3855
|
+
const notesRel = sourceSlideRels.byType(NOTES_SLIDE_REL)[0];
|
|
3088
3856
|
if (!notesRel) return;
|
|
3089
3857
|
const sourceNotesPartName = sourceSlideRels.resolveTarget(notesRel.id);
|
|
3090
3858
|
const sourceNotesPart = source.opc.part(sourceNotesPartName);
|
|
3091
3859
|
if (!sourceNotesPart) return;
|
|
3092
3860
|
const newNotesPartName = dest.opc.reservePartNameLike(sourceNotesPartName);
|
|
3093
3861
|
dest.opc.addPart(newNotesPartName, sourceNotesPart.contentType, sourceNotesPart.bytes);
|
|
3094
|
-
dest.opc.relationshipsFor(newSlidePartName).add(NOTES_SLIDE_REL
|
|
3862
|
+
dest.opc.relationshipsFor(newSlidePartName).add(NOTES_SLIDE_REL, relativePartName(newSlidePartName, newNotesPartName));
|
|
3095
3863
|
const notesSourceRels = source.opc.relationshipsFor(sourceNotesPartName);
|
|
3096
3864
|
const notesTargetRels = dest.opc.relationshipsFor(newNotesPartName);
|
|
3097
3865
|
for (const rel of notesSourceRels) {
|
|
3098
|
-
if (rel.type ===
|
|
3099
|
-
notesTargetRels.addWithId(rel.id, SLIDE_REL
|
|
3866
|
+
if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide") {
|
|
3867
|
+
notesTargetRels.addWithId(rel.id, SLIDE_REL, relativePartName(newNotesPartName, newSlidePartName));
|
|
3100
3868
|
continue;
|
|
3101
3869
|
}
|
|
3102
|
-
if (rel.type ===
|
|
3870
|
+
if (rel.type === "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster") {
|
|
3103
3871
|
const notesMaster = ensureNotesMaster(dest, ctx, notesSourceRels.resolveTarget(rel.id));
|
|
3104
|
-
notesTargetRels.addWithId(rel.id, NOTES_MASTER_REL
|
|
3872
|
+
notesTargetRels.addWithId(rel.id, NOTES_MASTER_REL, relativePartName(newNotesPartName, notesMaster));
|
|
3105
3873
|
continue;
|
|
3106
3874
|
}
|
|
3107
3875
|
if (rel.targetMode === "External") {
|
|
@@ -3123,7 +3891,7 @@ function carryNotes(dest, source, ctx, sourceSlidePartName, newSlidePartName) {
|
|
|
3123
3891
|
function ensureNotesMaster(dest, ctx, sourceNotesMasterPartName) {
|
|
3124
3892
|
const presPart = dest.presentationPart;
|
|
3125
3893
|
const presRels = dest.opc.relationshipsFor(presPart.partName);
|
|
3126
|
-
const existing = presRels.byType(NOTES_MASTER_REL
|
|
3894
|
+
const existing = presRels.byType(NOTES_MASTER_REL)[0];
|
|
3127
3895
|
if (existing) return presRels.resolveTarget(existing.id);
|
|
3128
3896
|
return registerNotesMaster(dest, copyPart(ctx, sourceNotesMasterPartName));
|
|
3129
3897
|
}
|
|
@@ -3139,7 +3907,7 @@ function ensureNotesMaster(dest, ctx, sourceNotesMasterPartName) {
|
|
|
3139
3907
|
*/
|
|
3140
3908
|
function registerNotesMaster(dest, notesMasterPartName) {
|
|
3141
3909
|
const presPart = dest.presentationPart;
|
|
3142
|
-
const relId = dest.opc.relationshipsFor(presPart.partName).add(NOTES_MASTER_REL
|
|
3910
|
+
const relId = dest.opc.relationshipsFor(presPart.partName).add(NOTES_MASTER_REL, relativePartName(presPart.partName, notesMasterPartName)).id;
|
|
3143
3911
|
const root = presPart.dom.documentElement;
|
|
3144
3912
|
if (!root) throw new PackageReadError("package/part-has-no-root", "presentation.xml has no document element to register a notes master in");
|
|
3145
3913
|
const lst = getOrAddChild(root, "p:notesMasterIdLst", [
|
|
@@ -3173,7 +3941,7 @@ function registerNotesMaster(dest, notesMasterPartName) {
|
|
|
3173
3941
|
function ensureNotesMasterFromXml(dest, master) {
|
|
3174
3942
|
const presPart = dest.presentationPart;
|
|
3175
3943
|
const presRels = dest.opc.relationshipsFor(presPart.partName);
|
|
3176
|
-
const existing = presRels.byType(NOTES_MASTER_REL
|
|
3944
|
+
const existing = presRels.byType(NOTES_MASTER_REL)[0];
|
|
3177
3945
|
if (existing) return presRels.resolveTarget(existing.id);
|
|
3178
3946
|
const masterPartName = dest.opc.reservePartNameLike("/ppt/notesMasters/notesMaster1.xml");
|
|
3179
3947
|
dest.opc.addPart(masterPartName, NOTES_MASTER_CONTENT_TYPE, textEncoder$1.encode(master.xml));
|
|
@@ -3183,48 +3951,6 @@ function ensureNotesMasterFromXml(dest, master) {
|
|
|
3183
3951
|
return registerNotesMaster(dest, masterPartName);
|
|
3184
3952
|
}
|
|
3185
3953
|
//#endregion
|
|
3186
|
-
//#region src/read/api/ops/part-index.ts
|
|
3187
|
-
/**
|
|
3188
|
-
* Walking a deck's master/layout spine by partname.
|
|
3189
|
-
*
|
|
3190
|
-
* The `p:sldMasterIdLst` / `p:sldLayoutIdLst` entries hold relationship ids, not partnames, so
|
|
3191
|
-
* every traversal has to resolve through the owning part's relationships. These are the
|
|
3192
|
-
* source-side reads that do it — used against a *foreign* presentation as often as against this
|
|
3193
|
-
* one, which is why they take the deck rather than living on it.
|
|
3194
|
-
*/
|
|
3195
|
-
/** Master partnames in `p:sldMasterIdLst` order. */
|
|
3196
|
-
function slideMasterPartNames(pres) {
|
|
3197
|
-
const root = pres.presentationPart.dom.documentElement;
|
|
3198
|
-
const lst = root && firstChild(root, "p:sldMasterIdLst");
|
|
3199
|
-
if (!lst) return [];
|
|
3200
|
-
const rels = pres.opc.relationshipsFor(pres.presentationPart.partName);
|
|
3201
|
-
const out = [];
|
|
3202
|
-
for (const entry of getElements(lst, "p:sldMasterId")) {
|
|
3203
|
-
const relId = attr(entry, "r:id");
|
|
3204
|
-
if (relId) out.push(rels.resolveTarget(relId));
|
|
3205
|
-
}
|
|
3206
|
-
return out;
|
|
3207
|
-
}
|
|
3208
|
-
/** A master's layout partnames in `p:sldLayoutIdLst` order. */
|
|
3209
|
-
function layoutPartNamesOf(pres, masterPartName) {
|
|
3210
|
-
const root = pres.opc.part(masterPartName)?.dom.documentElement;
|
|
3211
|
-
const lst = root && firstChild(root, "p:sldLayoutIdLst");
|
|
3212
|
-
if (!lst) return [];
|
|
3213
|
-
const rels = pres.opc.relationshipsFor(masterPartName);
|
|
3214
|
-
const out = [];
|
|
3215
|
-
for (const entry of getElements(lst, "p:sldLayoutId")) {
|
|
3216
|
-
const relId = attr(entry, "r:id");
|
|
3217
|
-
if (relId) out.push(rels.resolveTarget(relId));
|
|
3218
|
-
}
|
|
3219
|
-
return out;
|
|
3220
|
-
}
|
|
3221
|
-
/** Resolve the single relationship of `type` owned by `partName`, or `null`. */
|
|
3222
|
-
function resolveSingleRel(opc, partName, type) {
|
|
3223
|
-
const rels = opc.relationshipsFor(partName);
|
|
3224
|
-
const rel = rels.byType(type)[0];
|
|
3225
|
-
return rel ? rels.resolveTarget(rel.id) : null;
|
|
3226
|
-
}
|
|
3227
|
-
//#endregion
|
|
3228
3954
|
//#region src/read/api/ops/prune.ts
|
|
3229
3955
|
/**
|
|
3230
3956
|
* Orphan pruning after a part stops being referenced.
|
|
@@ -3290,8 +4016,6 @@ function isReferenced(pres, partName) {
|
|
|
3290
4016
|
* scales it again on top of the first. It is owned by the calling `Presentation` and threaded
|
|
3291
4017
|
* through here, so it lives as long as the deck does rather than as long as one call.
|
|
3292
4018
|
*/
|
|
3293
|
-
const SLIDE_LAYOUT_REL$1 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
3294
|
-
const SLIDE_MASTER_REL$1 = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
3295
4019
|
/**
|
|
3296
4020
|
* Rescale an imported slide's geometry onto this deck's canvas (the `rescale`
|
|
3297
4021
|
* option of {@link Presentation.importSlide}). Rewrites every top-level shape/group/
|
|
@@ -3306,8 +4030,8 @@ function rescaleImportedGeometry(dest, rescaledParts, slidePartName, theme, sour
|
|
|
3306
4030
|
const transform = computeRescale(source, target, mode);
|
|
3307
4031
|
rescalePartGeometry(dest, rescaledParts, slidePartName, transform);
|
|
3308
4032
|
if (theme === void 0 || theme === "copy") {
|
|
3309
|
-
const layout = resolveSingleRel(dest.opc, slidePartName, SLIDE_LAYOUT_REL
|
|
3310
|
-
const master = layout ? resolveSingleRel(dest.opc, layout, SLIDE_MASTER_REL
|
|
4033
|
+
const layout = resolveSingleRel(dest.opc, slidePartName, SLIDE_LAYOUT_REL);
|
|
4034
|
+
const master = layout ? resolveSingleRel(dest.opc, layout, SLIDE_MASTER_REL) : null;
|
|
3311
4035
|
if (layout) rescalePartGeometry(dest, rescaledParts, layout, transform);
|
|
3312
4036
|
if (master) rescalePartGeometry(dest, rescaledParts, master, transform);
|
|
3313
4037
|
}
|
|
@@ -3334,14 +4058,14 @@ function rescalePartGeometry(dest, rescaledParts, partName, transform) {
|
|
|
3334
4058
|
* Read-model entry point: `Presentation` wraps an `OpcPackage` and exposes a
|
|
3335
4059
|
* navigable, typed view of the deck (slides → shapes → text), backed by the
|
|
3336
4060
|
* live DOM so the same nodes can later be mutated.
|
|
4061
|
+
*
|
|
4062
|
+
* The class holds the model and the deck-level plumbing every operation needs — the part
|
|
4063
|
+
* index, the slide list, the copy registry that lets repeated imports from one source share
|
|
4064
|
+
* what they already copied, and the `p:sldIdLst` wiring. The operations themselves live in
|
|
4065
|
+
* `ops/`, one job per module, taking this deck as an argument. The slide-import machinery
|
|
4066
|
+
* used to sit here as ~280 lines of private methods and made that split hard to see: the
|
|
4067
|
+
* read model's own surface was outnumbered by one feature's internals.
|
|
3337
4068
|
*/
|
|
3338
|
-
const OFFICE_DOCUMENT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
|
|
3339
|
-
const SLIDE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide";
|
|
3340
|
-
const SLIDE_LAYOUT_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout";
|
|
3341
|
-
const SLIDE_MASTER_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideMaster";
|
|
3342
|
-
const NOTES_SLIDE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide";
|
|
3343
|
-
const NOTES_MASTER_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesMaster";
|
|
3344
|
-
const IMAGE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
|
|
3345
4069
|
const HYPERLINK_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink";
|
|
3346
4070
|
const CHART_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart";
|
|
3347
4071
|
const PACKAGE_REL = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/package";
|
|
@@ -3375,8 +4099,6 @@ const PRESENTATION_SLD_ID_LST_SUCCESSORS = [
|
|
|
3375
4099
|
"p:modifyVerifier",
|
|
3376
4100
|
"p:extLst"
|
|
3377
4101
|
];
|
|
3378
|
-
/** ST_SlideId minimum (ECMA-376): slide ids live in [256, 2147483647]. */
|
|
3379
|
-
const MIN_SLIDE_ID = 256;
|
|
3380
4102
|
var Presentation = class Presentation {
|
|
3381
4103
|
opc;
|
|
3382
4104
|
#presentationPart;
|
|
@@ -3674,13 +4396,14 @@ var Presentation = class Presentation {
|
|
|
3674
4396
|
throw new InvalidOptionError("import/slide-size-mismatch", `importSlide requires equal slide sizes (pass { rescale: 'fit' | 'stretch' } to rescale); target is ${fmt(target)}, source is ${fmt(incoming)}`);
|
|
3675
4397
|
}
|
|
3676
4398
|
if (sizesDiffer && options.rescale && (!target || !incoming)) throw new InvalidOptionError("import/slide-size-unknown", "importSlide rescale requires both decks to declare a slide size (p:sldSz)");
|
|
3677
|
-
const
|
|
4399
|
+
const importCtx = this.#importContext(source.opc);
|
|
4400
|
+
const newPartName = options.theme === "preserve" ? importSlidePreserve(this, importCtx, source, sourceSlide, options.carryMasterGraphics === true) : options.theme === "restyle" ? importSlideRestyle(this, importCtx, source, sourceSlide, options.carryMasterGraphics === true, options.remapLiterals === true) : copyPart(importCtx, sourceSlide.partName);
|
|
3678
4401
|
const newPart = this.opc.part(newPartName);
|
|
3679
4402
|
if (!newPart) throw new InternalError("import/part-went-missing", `Imported slide part went missing: ${newPartName}`);
|
|
3680
4403
|
if (sizesDiffer && options.rescale && target && incoming) rescaleImportedGeometry(this, this.#rescaledParts, newPartName, options.theme, incoming, target, options.rescale === true ? "fit" : options.rescale);
|
|
3681
4404
|
const slide = this.#insertSlidePart(newPart, options.at);
|
|
3682
|
-
if (options.importNotes) carryNotes(this, source,
|
|
3683
|
-
if (options.embedFonts) carryEmbeddedFonts(this, source,
|
|
4405
|
+
if (options.importNotes) carryNotes(this, source, importCtx, sourceSlide.partName, newPartName);
|
|
4406
|
+
if (options.embedFonts) carryEmbeddedFonts(this, source, importCtx);
|
|
3684
4407
|
return slide;
|
|
3685
4408
|
}
|
|
3686
4409
|
/**
|
|
@@ -3971,14 +4694,14 @@ var Presentation = class Presentation {
|
|
|
3971
4694
|
const sourceRels = sourceOpc.relationshipsFor(source.partName);
|
|
3972
4695
|
const targetRels = this.opc.relationshipsFor(target.partName);
|
|
3973
4696
|
const relIdMap = /* @__PURE__ */ new Map();
|
|
3974
|
-
const ctx = theme === "preserve" ?
|
|
4697
|
+
const ctx = theme === "preserve" ? sourceFlattenContext(sourceOpc, source.partName) : null;
|
|
3975
4698
|
const importCtx = this.#importContext(sourceOpc);
|
|
3976
4699
|
const extLst = firstChild(spTree, "p:extLst");
|
|
3977
4700
|
const anchor = options.at == null ? extLst : nthShapeChild(spTree, options.at) ?? extLst;
|
|
3978
4701
|
const result = [];
|
|
3979
4702
|
for (const shapeEl of sourceElements) {
|
|
3980
4703
|
const imported = targetDoc.importNode(shapeEl, true);
|
|
3981
|
-
|
|
4704
|
+
rewriteCarriedRels(imported, importCtx, sourceRels, target.partName, targetRels, relIdMap);
|
|
3982
4705
|
if (ctx) {
|
|
3983
4706
|
const holder = createElement(targetDoc, "p:spTree");
|
|
3984
4707
|
holder.appendChild(imported);
|
|
@@ -4014,215 +4737,6 @@ var Presentation = class Presentation {
|
|
|
4014
4737
|
return result;
|
|
4015
4738
|
}
|
|
4016
4739
|
/**
|
|
4017
|
-
* Import a slide in `preserve` mode: rebind it to this deck's master/layout
|
|
4018
|
-
* (see {@link #importSlideRebind}), then flatten its source theme into the slide
|
|
4019
|
-
* XML (scheme colours + style-matrix fills baked to literals). Returns the new
|
|
4020
|
-
* partname.
|
|
4021
|
-
*
|
|
4022
|
-
* The flatten context is gathered from the *source* subgraph, so it can be read
|
|
4023
|
-
* before or after the rebind; the rebind injects any carried decorations before
|
|
4024
|
-
* we flatten, so a single sweep resolves the theme references on the slide's own
|
|
4025
|
-
* content and on the carried decorations together.
|
|
4026
|
-
*/
|
|
4027
|
-
#importSlidePreserve(source, sourceSlide, carryGraphics) {
|
|
4028
|
-
const ctx = this.#sourceFlattenContext(source.opc, sourceSlide.partName);
|
|
4029
|
-
const { newPartName, slideRoot, newPart } = this.#importSlideRebind(source, sourceSlide, carryGraphics);
|
|
4030
|
-
flattenSlide(slideRoot, ctx);
|
|
4031
|
-
newPart.markDirty();
|
|
4032
|
-
return newPartName;
|
|
4033
|
-
}
|
|
4034
|
-
/**
|
|
4035
|
-
* Import a slide in `restyle` mode: rebind it to this deck's master/layout (see
|
|
4036
|
-
* {@link #importSlideRebind}) and then {@link restyleSlide} it — drop its colour
|
|
4037
|
-
* map override but bake *nothing*, so its symbolic theme references re-resolve
|
|
4038
|
-
* against the destination theme and the slide re-brands. Returns the new
|
|
4039
|
-
* partname.
|
|
4040
|
-
*
|
|
4041
|
-
* The deliberate inverse of `preserve`: no flatten, no inherited-background
|
|
4042
|
-
* bake, no placeholder colour/size/geometry bake — every one of those would pin
|
|
4043
|
-
* the slide to its source look, the opposite of re-branding. Carried
|
|
4044
|
-
* decorations are left symbolic too, so they re-brand along with the slide.
|
|
4045
|
-
*
|
|
4046
|
-
* With `remapLiterals` it additionally force-remaps the slide's source-theme
|
|
4047
|
-
* literal colours back to symbolic scheme colours and copies any referenced
|
|
4048
|
-
* source table style into this deck — the two things plain `restyle` cannot
|
|
4049
|
-
* re-brand (see {@link ImportSlideOptions.remapLiterals}).
|
|
4050
|
-
*/
|
|
4051
|
-
#importSlideRestyle(source, sourceSlide, carryGraphics, remapLiterals) {
|
|
4052
|
-
const { newPartName, slideRoot, newPart } = this.#importSlideRebind(source, sourceSlide, carryGraphics);
|
|
4053
|
-
restyleSlide(slideRoot);
|
|
4054
|
-
if (remapLiterals) {
|
|
4055
|
-
const parts = resolveSlideThemeParts(source.opc, sourceSlide.partName);
|
|
4056
|
-
remapLiteralColors(slideRoot, {
|
|
4057
|
-
clrMap: parts.clrMap,
|
|
4058
|
-
clrScheme: parts.clrScheme
|
|
4059
|
-
});
|
|
4060
|
-
copySourceTableStyles(this, source.opc, slideRoot);
|
|
4061
|
-
}
|
|
4062
|
-
newPart.markDirty();
|
|
4063
|
-
return newPartName;
|
|
4064
|
-
}
|
|
4065
|
-
/**
|
|
4066
|
-
* The rebind shared by `preserve` and `restyle`: copy the slide bytes into a
|
|
4067
|
-
* fresh part, rebuild its relationships (drop notes, repoint the `slideLayout`
|
|
4068
|
-
* rel at this deck's existing layout, copy every other internal target —
|
|
4069
|
-
* media/charts — and pass externals through), and optionally bake the source
|
|
4070
|
-
* master/layout decorations onto the slide. Returns the new part, its name, and
|
|
4071
|
-
* its live root element for the caller's mode-specific pass (flatten vs restyle).
|
|
4072
|
-
*
|
|
4073
|
-
* This carries *no* theme baking of its own — not even the inherited background.
|
|
4074
|
-
* `preserve` adds that via {@link flattenSlide}'s context; `restyle` must not,
|
|
4075
|
-
* so the background stays symbolic and re-brands.
|
|
4076
|
-
*/
|
|
4077
|
-
#importSlideRebind(source, sourceSlide, carryGraphics) {
|
|
4078
|
-
const destLayout = this.#destinationLayoutPartName();
|
|
4079
|
-
const sourcePart = source.opc.part(sourceSlide.partName);
|
|
4080
|
-
if (!sourcePart) throw new PackageReadError("package/part-missing", `importSlide: source package has no part ${sourceSlide.partName}`);
|
|
4081
|
-
const newPartName = this.opc.reservePartNameLike(sourceSlide.partName);
|
|
4082
|
-
const newPart = this.opc.addPart(newPartName, sourcePart.contentType, sourcePart.bytes);
|
|
4083
|
-
const slideRoot = newPart.dom.documentElement;
|
|
4084
|
-
if (!slideRoot) throw new PackageReadError("package/part-has-no-root", `Imported slide ${newPartName} has no root element`);
|
|
4085
|
-
const ctx = this.#importContext(source.opc);
|
|
4086
|
-
const sourceRels = source.opc.relationshipsFor(sourceSlide.partName);
|
|
4087
|
-
const targetRels = this.opc.relationshipsFor(newPartName);
|
|
4088
|
-
for (const rel of sourceRels) {
|
|
4089
|
-
if (rel.type === NOTES_SLIDE_REL) continue;
|
|
4090
|
-
if (rel.type === SLIDE_LAYOUT_REL) {
|
|
4091
|
-
targetRels.addWithId(rel.id, SLIDE_LAYOUT_REL, relativePartName(newPartName, destLayout));
|
|
4092
|
-
continue;
|
|
4093
|
-
}
|
|
4094
|
-
if (rel.targetMode === "External") {
|
|
4095
|
-
targetRels.addWithId(rel.id, rel.type, rel.target, "External");
|
|
4096
|
-
continue;
|
|
4097
|
-
}
|
|
4098
|
-
const newTarget = copyPart(ctx, sourceRels.resolveTarget(rel.id));
|
|
4099
|
-
targetRels.addWithId(rel.id, rel.type, relativePartName(newPartName, newTarget));
|
|
4100
|
-
}
|
|
4101
|
-
if (carryGraphics) this.#carryMasterGraphics(ctx, slideRoot, newPartName, sourceSlide.partName);
|
|
4102
|
-
return {
|
|
4103
|
-
newPartName,
|
|
4104
|
-
slideRoot,
|
|
4105
|
-
newPart
|
|
4106
|
-
};
|
|
4107
|
-
}
|
|
4108
|
-
/**
|
|
4109
|
-
* Bake the source `slideLayout`/`slideMaster` shape-tree decorations onto the
|
|
4110
|
-
* imported slide (the `carryMasterGraphics` path). Every shape on those trees
|
|
4111
|
-
* *except* placeholders is deep-copied into the slide's `p:spTree` ahead of its
|
|
4112
|
-
* own content — master decorations first, then layout, then the slide's shapes —
|
|
4113
|
-
* so document (z-)order keeps the master furthest back. Each decoration's media
|
|
4114
|
-
* and other relationship targets are copied into this package and its
|
|
4115
|
-
* `r:embed`/`r:id`/… references rewritten to fresh slide-local ids. The injected
|
|
4116
|
-
* shapes are left for the caller's {@link flattenSlide} pass to resolve any
|
|
4117
|
-
* theme references they carry.
|
|
4118
|
-
*/
|
|
4119
|
-
#carryMasterGraphics(ctx, slideRoot, newPartName, slidePartName) {
|
|
4120
|
-
const sourceOpc = ctx.source;
|
|
4121
|
-
const layoutPartName = resolveSingleRel(sourceOpc, slidePartName, SLIDE_LAYOUT_REL);
|
|
4122
|
-
const masterPartName = layoutPartName ? resolveSingleRel(sourceOpc, layoutPartName, SLIDE_MASTER_REL) : null;
|
|
4123
|
-
const cSld = firstChild(slideRoot, "p:cSld");
|
|
4124
|
-
const spTree = cSld && firstChild(cSld, "p:spTree");
|
|
4125
|
-
if (!spTree) return;
|
|
4126
|
-
const doc = ownerDocumentOf(slideRoot);
|
|
4127
|
-
const slideRels = this.opc.relationshipsFor(newPartName);
|
|
4128
|
-
const relIdMap = /* @__PURE__ */ new Map();
|
|
4129
|
-
const anchor = firstShapeChild(spTree);
|
|
4130
|
-
for (const partName of [masterPartName, layoutPartName]) {
|
|
4131
|
-
if (!partName) continue;
|
|
4132
|
-
const decorations = carriedDecorations(sourceOpc.part(partName)?.dom.documentElement ?? null);
|
|
4133
|
-
if (decorations.length === 0) continue;
|
|
4134
|
-
const sourceRels = sourceOpc.relationshipsFor(partName);
|
|
4135
|
-
for (const deco of decorations) {
|
|
4136
|
-
const imported = doc.importNode(deco, true);
|
|
4137
|
-
this.#rewriteCarriedRels(imported, ctx, sourceRels, newPartName, slideRels, relIdMap);
|
|
4138
|
-
spTree.insertBefore(imported, anchor);
|
|
4139
|
-
}
|
|
4140
|
-
}
|
|
4141
|
-
}
|
|
4142
|
-
/**
|
|
4143
|
-
* Rewrite every relationship reference (`r:embed`, `r:id`, `r:link`, …) inside a
|
|
4144
|
-
* carried decoration so it points at a fresh slide-local relationship, copying
|
|
4145
|
-
* the referenced part into this package on first sight. `relIdMap` (keyed by
|
|
4146
|
-
* source part + source rel id) dedupes references shared within one import call.
|
|
4147
|
-
*/
|
|
4148
|
-
#rewriteCarriedRels(node, ctx, sourceRels, newPartName, slideRels, relIdMap) {
|
|
4149
|
-
const elements = [];
|
|
4150
|
-
collectElements(node, elements);
|
|
4151
|
-
for (const el of elements) {
|
|
4152
|
-
const refs = [];
|
|
4153
|
-
const attrs = el.attributes;
|
|
4154
|
-
for (let i = 0; i < attrs.length; i++) {
|
|
4155
|
-
const a = attrs.item(i);
|
|
4156
|
-
if (!a || a.namespaceURI !== OOXML_NS.r || !a.value) continue;
|
|
4157
|
-
if (!sourceRels.get(a.value)) continue;
|
|
4158
|
-
refs.push({
|
|
4159
|
-
local: a.localName ?? a.name,
|
|
4160
|
-
id: a.value
|
|
4161
|
-
});
|
|
4162
|
-
}
|
|
4163
|
-
for (const { local, id } of refs) setAttr(el, `r:${local}`, this.#carryRel(ctx, sourceRels, id, newPartName, slideRels, relIdMap));
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
|
-
/** Resolve a carried decoration's source relationship to a fresh slide-local id, copying its internal target. */
|
|
4167
|
-
#carryRel(ctx, sourceRels, id, newPartName, slideRels, relIdMap) {
|
|
4168
|
-
const key = `${sourceRels.sourcePartName}|${id}`;
|
|
4169
|
-
const cached = relIdMap.get(key);
|
|
4170
|
-
if (cached) return cached;
|
|
4171
|
-
const rel = sourceRels.get(id);
|
|
4172
|
-
if (!rel) throw new InvalidOptionError("relationship/not-found", `Relationships of ${sourceRels.sourcePartName}: no relationship with id ${id}`);
|
|
4173
|
-
const newId = rel.targetMode === "External" ? slideRels.add(rel.type, rel.target, "External").id : slideRels.add(rel.type, relativePartName(newPartName, copyPart(ctx, sourceRels.resolveTarget(id)))).id;
|
|
4174
|
-
relIdMap.set(key, newId);
|
|
4175
|
-
return newId;
|
|
4176
|
-
}
|
|
4177
|
-
/**
|
|
4178
|
-
* The partname of the layout this deck's slides should attach to in `preserve`
|
|
4179
|
-
* mode: the first layout of the first slide master. Throws when the deck has no
|
|
4180
|
-
* master/layout to attach to (a deck ts-pptx always provides).
|
|
4181
|
-
*/
|
|
4182
|
-
#destinationLayoutPartName() {
|
|
4183
|
-
const presRels = this.opc.relationshipsFor(this.presentationPart.partName);
|
|
4184
|
-
const masterRel = presRels.byType(SLIDE_MASTER_REL)[0];
|
|
4185
|
-
if (!masterRel) throw new InvalidOptionError("import/destination-missing-master", "importSlide preserve mode requires a slide master in the destination deck");
|
|
4186
|
-
const masterPartName = presRels.resolveTarget(masterRel.id);
|
|
4187
|
-
const masterRels = this.opc.relationshipsFor(masterPartName);
|
|
4188
|
-
const layoutRel = masterRels.byType(SLIDE_LAYOUT_REL)[0];
|
|
4189
|
-
if (!layoutRel) throw new InvalidOptionError("import/destination-missing-layout", "importSlide preserve mode requires a slide layout in the destination deck");
|
|
4190
|
-
return masterRels.resolveTarget(layoutRel.id);
|
|
4191
|
-
}
|
|
4192
|
-
/**
|
|
4193
|
-
* Gather the flatten context for a source slide: walk slide → layout → master →
|
|
4194
|
-
* theme, reading the effective colour map (the slide's `clrMapOvr` override, or
|
|
4195
|
-
* the master `clrMap`), the theme `clrScheme`, and the theme `fmtScheme`.
|
|
4196
|
-
*/
|
|
4197
|
-
#sourceFlattenContext(sourceOpc, slidePartName) {
|
|
4198
|
-
const parts = resolveSlideThemeParts(sourceOpc, slidePartName);
|
|
4199
|
-
const themeElements = parts.themeElements;
|
|
4200
|
-
return {
|
|
4201
|
-
clrMap: parts.clrMap,
|
|
4202
|
-
clrScheme: parts.clrScheme,
|
|
4203
|
-
fmtScheme: themeElements ? firstChild(themeElements, "a:fmtScheme") : null,
|
|
4204
|
-
inheritedBackground: this.#effectiveBackground(sourceOpc, parts.slideRoot, parts.layoutPartName, parts.masterPartName),
|
|
4205
|
-
layoutRoot: parts.layoutRoot,
|
|
4206
|
-
masterRoot: parts.masterRoot
|
|
4207
|
-
};
|
|
4208
|
-
}
|
|
4209
|
-
/**
|
|
4210
|
-
* The background the slide effectively inherits from its source subgraph: the
|
|
4211
|
-
* layout's `p:bg`, else the master's. Returns `null` when the slide carries its
|
|
4212
|
-
* own `p:bg` (it stays on the slide and is flattened directly) or none exists.
|
|
4213
|
-
*/
|
|
4214
|
-
#effectiveBackground(sourceOpc, slideRoot, layoutPartName, masterPartName) {
|
|
4215
|
-
if (slideRoot && this.#backgroundOf(slideRoot)) return null;
|
|
4216
|
-
const layoutRoot = layoutPartName ? sourceOpc.part(layoutPartName)?.dom.documentElement ?? null : null;
|
|
4217
|
-
const masterRoot = masterPartName ? sourceOpc.part(masterPartName)?.dom.documentElement ?? null : null;
|
|
4218
|
-
return (layoutRoot && this.#backgroundOf(layoutRoot)) ?? (masterRoot && this.#backgroundOf(masterRoot)) ?? null;
|
|
4219
|
-
}
|
|
4220
|
-
/** The `p:cSld/p:bg` element of a slide/layout/master root, or `null`. */
|
|
4221
|
-
#backgroundOf(root) {
|
|
4222
|
-
const cSld = firstChild(root, "p:cSld");
|
|
4223
|
-
return cSld ? firstChild(cSld, "p:bg") : null;
|
|
4224
|
-
}
|
|
4225
|
-
/**
|
|
4226
4740
|
* Open an import out of `source`: this deck as the destination, paired with the
|
|
4227
4741
|
* copy registry for that package (created on first use). The registry is held on
|
|
4228
4742
|
* the class rather than in the context because it must outlive any one call —
|
|
@@ -4268,7 +4782,7 @@ var Presentation = class Presentation {
|
|
|
4268
4782
|
}
|
|
4269
4783
|
/** A slide id one past the highest existing, but at least ST_SlideId's minimum. */
|
|
4270
4784
|
#nextSlideId(sldIds) {
|
|
4271
|
-
let max =
|
|
4785
|
+
let max = 255;
|
|
4272
4786
|
for (const sldId of sldIds) {
|
|
4273
4787
|
const id = intValue(attr(sldId, "id"));
|
|
4274
4788
|
if (id !== null && id > max) max = id;
|
|
@@ -4283,4 +4797,4 @@ var Presentation = class Presentation {
|
|
|
4283
4797
|
//#endregion
|
|
4284
4798
|
export { Placeholder as a, Theme as c, NotesSlide as d, OpcPackage as f, ContentTypes as h, Slide as i, readTagsForPart as l, Part as m, readCoreProperties as n, SlideLayout as o, Relationships as p, readCustomProperties as r, SlideMaster as s, Presentation as t, NotesPlaceholder as u };
|
|
4285
4799
|
|
|
4286
|
-
//# sourceMappingURL=presentation-
|
|
4800
|
+
//# sourceMappingURL=presentation-DAcOfzsP.js.map
|