@shbernal/ts-pptx 3.5.0 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{autopage-BYlTzX-l.js → autopage-DkjYZUFD.js} +3 -3
- package/dist/{autopage-BYlTzX-l.js.map → autopage-DkjYZUFD.js.map} +1 -1
- package/dist/browser.d.ts +5 -5
- package/dist/browser.js +4 -4
- package/dist/browser.js.map +1 -1
- package/dist/{diagnostics-BDuR8UkG.d.ts → diagnostics-Du_s75MT.d.ts} +20 -5
- package/dist/diagnostics-Du_s75MT.d.ts.map +1 -0
- package/dist/{errors-CgnueTYL.d.ts → errors-PflMPWBn.d.ts} +2 -2
- package/dist/{errors-CgnueTYL.d.ts.map → errors-PflMPWBn.d.ts.map} +1 -1
- package/dist/{fit-TjpNbMru.js → fit-BKs0zxMI.js} +3 -3
- package/dist/{fit-TjpNbMru.js.map → fit-BKs0zxMI.js.map} +1 -1
- package/dist/{html-dom-yUSdCExA.js → html-dom-B0I0QSbU.js} +4 -4
- package/dist/{html-dom-yUSdCExA.js.map → html-dom-B0I0QSbU.js.map} +1 -1
- package/dist/html.d.ts +3 -3
- package/dist/html.js +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -3
- package/dist/inspect.d.ts +1 -1
- package/dist/inspect.js +4 -5
- package/dist/inspect.js.map +1 -1
- package/dist/math.d.ts +1 -1
- package/dist/measure.d.ts +2 -2
- package/dist/measure.js +1 -1
- package/dist/{namespaces-CcKuzeuE.js → namespaces-BVnNu7sg.js} +74 -3
- package/dist/namespaces-BVnNu7sg.js.map +1 -0
- package/dist/node.d.ts +5 -5
- package/dist/node.js +3 -3
- package/dist/{notes-DNqAggXx.js → notes-RLCwdlTE.js} +289 -313
- package/dist/{notes-DNqAggXx.js.map → notes-RLCwdlTE.js.map} +1 -1
- package/dist/{pres-Dybf7ZUl.d.ts → pres-C5__xkDh.d.ts} +2 -2
- package/dist/{pres-Dybf7ZUl.d.ts.map → pres-C5__xkDh.d.ts.map} +1 -1
- package/dist/{presentation-VfJ_FT7E.js → presentation-Cp7vzlAw.js} +414 -515
- package/dist/presentation-Cp7vzlAw.js.map +1 -0
- package/dist/{presentation-types-CpkTx9Xa.d.ts → presentation-types-DP_8jog7.d.ts} +351 -274
- package/dist/presentation-types-DP_8jog7.d.ts.map +1 -0
- package/dist/read.d.ts +31 -48
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +3 -3
- package/dist/script.d.ts +2 -2
- package/dist/script.d.ts.map +1 -1
- package/dist/script.js +18 -47
- package/dist/script.js.map +1 -1
- package/dist/{shapes-Kt51vry6.js → shapes-BemroU-z.js} +625 -265
- package/dist/shapes-BemroU-z.js.map +1 -0
- package/dist/{slide-B_aInw3N.d.ts → slide-js0QpRV-.d.ts} +1278 -1254
- package/dist/slide-js0QpRV-.d.ts.map +1 -0
- package/dist/{text-D04NUMvA.js → text-CyetbF6p.js} +2039 -1417
- package/dist/text-CyetbF6p.js.map +1 -0
- package/dist/units-1kRzYShk.js +231 -0
- package/dist/units-1kRzYShk.js.map +1 -0
- package/dist/units-internal-Dg95w_YD.js +753 -0
- package/dist/units-internal-Dg95w_YD.js.map +1 -0
- package/dist/zip.d.ts +1 -1
- package/package.json +2 -1
- package/dist/diagnostics-BDuR8UkG.d.ts.map +0 -1
- package/dist/diagnostics-CPVADSL1.js +0 -90
- package/dist/diagnostics-CPVADSL1.js.map +0 -1
- package/dist/namespaces-CcKuzeuE.js.map +0 -1
- package/dist/presentation-VfJ_FT7E.js.map +0 -1
- package/dist/presentation-types-CpkTx9Xa.d.ts.map +0 -1
- package/dist/shapes-Kt51vry6.js.map +0 -1
- package/dist/slide-B_aInw3N.d.ts.map +0 -1
- package/dist/text-D04NUMvA.js.map +0 -1
- package/dist/units-internal-DNvK2I6o.js +0 -896
- package/dist/units-internal-DNvK2I6o.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as EMU_PER_POINT, s as PERCENT_SCALE, t as ANGLE_UNITS_PER_DEGREE, x as warn } from "./units-1kRzYShk.js";
|
|
2
2
|
import { i as PackageReadError, n as InvalidOptionError, o as UnsupportedFeatureError, t as InternalError } from "./errors-Bvln0H-7.js";
|
|
3
|
-
import {
|
|
3
|
+
import { F as TEXT_VERTICAL, L as checkEnumOrThrow, N as TEXT_ANCHORS, P as TEXT_HORZ_OVERFLOW, a as NOTES_MASTER_REL, c as OFFICE_DOCUMENT_REL, h as SLIDE_MASTER_REL, p as SLIDE_LAYOUT_REL, r as IMAGE_REL, t as OOXML_NS, v as THEME_REL, x as getImageSizeFromBytes, y as fitSrcRectPercents } from "./namespaces-BVnNu7sg.js";
|
|
4
4
|
import { DOMParser, MIME_TYPE, XMLSerializer, onErrorStopParsing } from "@xmldom/xmldom";
|
|
5
5
|
/** Parse an XML string strictly: any well-formedness error throws. */
|
|
6
6
|
function parseXml(xml) {
|
|
@@ -458,6 +458,439 @@ function setSolidFill(parent, afterOrder, color) {
|
|
|
458
458
|
fill.appendChild(clr);
|
|
459
459
|
}
|
|
460
460
|
//#endregion
|
|
461
|
+
//#region src/read/oxml/color-transform.ts
|
|
462
|
+
/**
|
|
463
|
+
* Apply DrawingML colour-transform modifiers (`lumMod`/`lumOff`/`shade`/`tint`/
|
|
464
|
+
* `satMod`/`alpha`/…) to a base sRGB colour, returning the *effective* colour a
|
|
465
|
+
* renderer would paint.
|
|
466
|
+
*
|
|
467
|
+
* The read model resolves a `schemeClr`/`srgbClr` reference to its **base** hex
|
|
468
|
+
* and carries the transform children verbatim (so the `theme: 'preserve'` flatten
|
|
469
|
+
* path can re-emit them byte-for-byte). This module is the missing step that
|
|
470
|
+
* turns that base hex + transform list into the final rendered hex, so a consumer
|
|
471
|
+
* never hand-computes a tint/shade again.
|
|
472
|
+
*
|
|
473
|
+
* Colour-space model (verified against PowerPoint output — see
|
|
474
|
+
* `test/read/color-transform.test.js`): the working colour is kept canonical as
|
|
475
|
+
* sRGB between transforms; each transform converts into the space its family
|
|
476
|
+
* operates in, applies, and converts back to sRGB. Transforms apply in **document
|
|
477
|
+
* order** (ECMA-376 §20.1.2.3).
|
|
478
|
+
*
|
|
479
|
+
* - `lumMod` / `lumOff` — luminance (L) in HSL: `L' = L·mod + off`. PowerPoint's
|
|
480
|
+
* "Lighter/Darker N%" family.
|
|
481
|
+
* - `satMod` / `satOff` — saturation (S) in HSL.
|
|
482
|
+
* - `hueMod` / `hueOff` — hue (H) in HSL (`hueMod` scales, `hueOff` offsets by
|
|
483
|
+
* degrees). Not exercised by any current source; implemented for completeness.
|
|
484
|
+
* - `shade` — toward black in **linear** RGB: `c' = c·k`.
|
|
485
|
+
* - `tint` — toward white in **linear** RGB: `c' = c·k + (1−k)`.
|
|
486
|
+
* - `alpha` / `alphaMod` / `alphaOff` — opacity, returned alongside the hex
|
|
487
|
+
* (never folded into RGB).
|
|
488
|
+
* - `comp` / `inv` / `gray` / `gamma` / `invGamma` — pass-through for now (no
|
|
489
|
+
* current source needs them; add when one does).
|
|
490
|
+
*
|
|
491
|
+
* All DrawingML percentage values are stored in thousandths of a percent
|
|
492
|
+
* (`100%` → `100000`), so a modifier's fraction is `val / PERCENT_SCALE`.
|
|
493
|
+
*/
|
|
494
|
+
/**
|
|
495
|
+
* DrawingML percentage → fraction, or `null` when unparseable.
|
|
496
|
+
*
|
|
497
|
+
* `a:ST_Percentage` is a *union* in the Transitional profile: the fixed-point integer form
|
|
498
|
+
* Office writes (`100%` → `100000`) and a decimal string with a literal `%` (`-?[0-9]+(\.[0-9]+)?%`),
|
|
499
|
+
* which is the only form the Strict profile has. Both are read here — a reader that took only the
|
|
500
|
+
* first dropped a schema-legal value silently, and the two are one `endsWith` apart.
|
|
501
|
+
* @param value - the raw attribute value, or `null` when the attribute is absent
|
|
502
|
+
*/
|
|
503
|
+
function parsePercent(value) {
|
|
504
|
+
if (value === null || value === "") return null;
|
|
505
|
+
if (value.endsWith("%")) {
|
|
506
|
+
const n = Number(value.slice(0, -1));
|
|
507
|
+
return Number.isFinite(n) ? n / 100 : null;
|
|
508
|
+
}
|
|
509
|
+
const n = Number(value);
|
|
510
|
+
return Number.isFinite(n) ? n / PERCENT_SCALE : null;
|
|
511
|
+
}
|
|
512
|
+
const clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
513
|
+
/** Parse a 6-hex string (`RRGGBB`, optional `#`) into 0–1 sRGB channels, or `null`. */
|
|
514
|
+
function parseHex(hex) {
|
|
515
|
+
const h = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
516
|
+
if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
|
|
517
|
+
return {
|
|
518
|
+
r: parseInt(h.slice(0, 2), 16) / 255,
|
|
519
|
+
g: parseInt(h.slice(2, 4), 16) / 255,
|
|
520
|
+
b: parseInt(h.slice(4, 6), 16) / 255
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
/** 0–1 sRGB channels → uppercase `RRGGBB`, rounding and clamping each channel. */
|
|
524
|
+
function toHex(c) {
|
|
525
|
+
const ch = (v) => Math.round(clamp01(v) * 255).toString(16).toUpperCase().padStart(2, "0");
|
|
526
|
+
return `${ch(c.r)}${ch(c.g)}${ch(c.b)}`;
|
|
527
|
+
}
|
|
528
|
+
/** sRGB channel (0–1) → linear-light (gamma decode). */
|
|
529
|
+
function srgbToLinear(c) {
|
|
530
|
+
return c <= .04045 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
|
|
531
|
+
}
|
|
532
|
+
/** Linear-light channel (0–1) → sRGB (gamma encode). */
|
|
533
|
+
function linearToSrgb(c) {
|
|
534
|
+
return c <= .0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - .055;
|
|
535
|
+
}
|
|
536
|
+
/** sRGB (0–1) → HSL with H in degrees [0,360), S/L in [0,1]. */
|
|
537
|
+
function rgbToHsl(c) {
|
|
538
|
+
const { r, g, b } = c;
|
|
539
|
+
const max = Math.max(r, g, b);
|
|
540
|
+
const min = Math.min(r, g, b);
|
|
541
|
+
const l = (max + min) / 2;
|
|
542
|
+
const d = max - min;
|
|
543
|
+
if (d === 0) return {
|
|
544
|
+
h: 0,
|
|
545
|
+
s: 0,
|
|
546
|
+
l
|
|
547
|
+
};
|
|
548
|
+
const s = d / (1 - Math.abs(2 * l - 1));
|
|
549
|
+
let h;
|
|
550
|
+
if (max === r) h = (g - b) / d % 6;
|
|
551
|
+
else if (max === g) h = (b - r) / d + 2;
|
|
552
|
+
else h = (r - g) / d + 4;
|
|
553
|
+
h *= 60;
|
|
554
|
+
if (h < 0) h += 360;
|
|
555
|
+
return {
|
|
556
|
+
h,
|
|
557
|
+
s,
|
|
558
|
+
l
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
/** HSL (H degrees, S/L 0–1) → sRGB (0–1). */
|
|
562
|
+
function hslToRgb({ h, s, l }) {
|
|
563
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
564
|
+
const hp = (h % 360 + 360) % 360 / 60;
|
|
565
|
+
const x = c * (1 - Math.abs(hp % 2 - 1));
|
|
566
|
+
const [r, g, b] = hp < 1 ? [
|
|
567
|
+
c,
|
|
568
|
+
x,
|
|
569
|
+
0
|
|
570
|
+
] : hp < 2 ? [
|
|
571
|
+
x,
|
|
572
|
+
c,
|
|
573
|
+
0
|
|
574
|
+
] : hp < 3 ? [
|
|
575
|
+
0,
|
|
576
|
+
c,
|
|
577
|
+
x
|
|
578
|
+
] : hp < 4 ? [
|
|
579
|
+
0,
|
|
580
|
+
x,
|
|
581
|
+
c
|
|
582
|
+
] : hp < 5 ? [
|
|
583
|
+
x,
|
|
584
|
+
0,
|
|
585
|
+
c
|
|
586
|
+
] : [
|
|
587
|
+
c,
|
|
588
|
+
0,
|
|
589
|
+
x
|
|
590
|
+
];
|
|
591
|
+
const m = l - c / 2;
|
|
592
|
+
return {
|
|
593
|
+
r: r + m,
|
|
594
|
+
g: g + m,
|
|
595
|
+
b: b + m
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* An `a:hslClr` triple → 6-hex sRGB, or `null` when any of the three is unparseable.
|
|
600
|
+
*
|
|
601
|
+
* The colour space is the one `lumMod`/`satMod` already work in — DrawingML's HSL is sRGB-HSL,
|
|
602
|
+
* which is what the transform family below is verified against — so this shares that conversion
|
|
603
|
+
* rather than introducing a second one. Units are the schema's: `@hue` is
|
|
604
|
+
* `a:ST_PositiveFixedAngle`, i.e. 60,000ths of a degree, and `@sat`/`@lum` are `a:ST_Percentage`
|
|
605
|
+
* (see {@link parsePercent}). Saturation and luminance are clamped to 0–1; hue wraps.
|
|
606
|
+
* @param hue - raw `@hue`
|
|
607
|
+
* @param sat - raw `@sat`
|
|
608
|
+
* @param lum - raw `@lum`
|
|
609
|
+
*/
|
|
610
|
+
function hslClrToHex(hue, sat, lum) {
|
|
611
|
+
const h = hue === null || hue === "" ? null : Number(hue);
|
|
612
|
+
const s = parsePercent(sat);
|
|
613
|
+
const l = parsePercent(lum);
|
|
614
|
+
if (h === null || !Number.isFinite(h) || s === null || l === null) return null;
|
|
615
|
+
return toHex(hslToRgb({
|
|
616
|
+
h: h / ANGLE_UNITS_PER_DEGREE,
|
|
617
|
+
s: clamp01(s),
|
|
618
|
+
l: clamp01(l)
|
|
619
|
+
}));
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Apply an ordered list of DrawingML colour transforms to a base sRGB hex,
|
|
623
|
+
* returning the effective sRGB hex (and opacity, when an `alpha*` modifier set
|
|
624
|
+
* it). Pure: no DOM, no theme lookup — the caller has already resolved the base
|
|
625
|
+
* colour and extracted each modifier's `{ name, value }`.
|
|
626
|
+
*
|
|
627
|
+
* An unparseable base hex is returned unchanged (with no transforms applied); an
|
|
628
|
+
* unparseable/`null` modifier value is skipped.
|
|
629
|
+
*/
|
|
630
|
+
function applyColorTransforms(baseHex, transforms) {
|
|
631
|
+
const parsed = parseHex(baseHex);
|
|
632
|
+
if (!parsed) return { hex: baseHex };
|
|
633
|
+
let rgb = parsed;
|
|
634
|
+
let alpha;
|
|
635
|
+
for (const { name, value } of transforms) switch (name) {
|
|
636
|
+
case "lumMod":
|
|
637
|
+
case "lumOff": {
|
|
638
|
+
const f = parsePercent(value);
|
|
639
|
+
if (f === null) break;
|
|
640
|
+
const hsl = rgbToHsl(rgb);
|
|
641
|
+
hsl.l = clamp01(name === "lumMod" ? hsl.l * f : hsl.l + f);
|
|
642
|
+
rgb = hslToRgb(hsl);
|
|
643
|
+
break;
|
|
644
|
+
}
|
|
645
|
+
case "satMod":
|
|
646
|
+
case "satOff": {
|
|
647
|
+
const f = parsePercent(value);
|
|
648
|
+
if (f === null) break;
|
|
649
|
+
const hsl = rgbToHsl(rgb);
|
|
650
|
+
hsl.s = clamp01(name === "satMod" ? hsl.s * f : hsl.s + f);
|
|
651
|
+
rgb = hslToRgb(hsl);
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
case "hueMod":
|
|
655
|
+
case "hueOff": {
|
|
656
|
+
const hsl = rgbToHsl(rgb);
|
|
657
|
+
if (name === "hueMod") {
|
|
658
|
+
const f = parsePercent(value);
|
|
659
|
+
if (f === null) break;
|
|
660
|
+
hsl.h = (hsl.h * f % 360 + 360) % 360;
|
|
661
|
+
} else {
|
|
662
|
+
const n = value === null ? NaN : Number(value);
|
|
663
|
+
if (!Number.isFinite(n)) break;
|
|
664
|
+
hsl.h = ((hsl.h + n / ANGLE_UNITS_PER_DEGREE) % 360 + 360) % 360;
|
|
665
|
+
}
|
|
666
|
+
rgb = hslToRgb(hsl);
|
|
667
|
+
break;
|
|
668
|
+
}
|
|
669
|
+
case "shade":
|
|
670
|
+
case "tint": {
|
|
671
|
+
const k = parsePercent(value);
|
|
672
|
+
if (k === null) break;
|
|
673
|
+
const lin = {
|
|
674
|
+
r: srgbToLinear(rgb.r),
|
|
675
|
+
g: srgbToLinear(rgb.g),
|
|
676
|
+
b: srgbToLinear(rgb.b)
|
|
677
|
+
};
|
|
678
|
+
const apply = (c) => clamp01(name === "shade" ? c * k : c * k + (1 - k));
|
|
679
|
+
rgb = {
|
|
680
|
+
r: linearToSrgb(apply(lin.r)),
|
|
681
|
+
g: linearToSrgb(apply(lin.g)),
|
|
682
|
+
b: linearToSrgb(apply(lin.b))
|
|
683
|
+
};
|
|
684
|
+
break;
|
|
685
|
+
}
|
|
686
|
+
case "alpha": {
|
|
687
|
+
const f = parsePercent(value);
|
|
688
|
+
if (f !== null) alpha = clamp01(f);
|
|
689
|
+
break;
|
|
690
|
+
}
|
|
691
|
+
case "alphaMod": {
|
|
692
|
+
const f = parsePercent(value);
|
|
693
|
+
if (f !== null) alpha = clamp01((alpha ?? 1) * f);
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
case "alphaOff": {
|
|
697
|
+
const f = parsePercent(value);
|
|
698
|
+
if (f !== null) alpha = clamp01((alpha ?? 1) + f);
|
|
699
|
+
break;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return alpha === void 0 ? { hex: toHex(rgb) } : {
|
|
703
|
+
hex: toHex(rgb),
|
|
704
|
+
alpha
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
//#endregion
|
|
708
|
+
//#region src/read/oxml/preset-color.ts
|
|
709
|
+
/**
|
|
710
|
+
* The ECMA-376 preset colour table (`a:prstClr/@val` → literal RGB).
|
|
711
|
+
*
|
|
712
|
+
* `ST_PresetColorVal` (§20.1.10.47) enumerates 190 names, and the standard gives each one an
|
|
713
|
+
* RGB triple. They are the HTML/CSS named colours: every value in the enumeration has the same
|
|
714
|
+
* RGB here as the CSS colour of the same name, and the 190 names collapse to the 140 distinct
|
|
715
|
+
* CSS names below through three mechanical spelling rules the enumeration adds on top —
|
|
716
|
+
*
|
|
717
|
+
* - abbreviated prefixes: `dk*`/`lt*`/`med*` alongside `dark*`/`light*`/`medium*`
|
|
718
|
+
* (`dkSlateBlue` is `darkSlateBlue`), and only as a *prefix* — `medium…` is not `mediumium…`;
|
|
719
|
+
* - both spellings of grey: `dimGrey` is `dimGray`;
|
|
720
|
+
* - case: the enumeration is camelCase, the table below is folded to lower case.
|
|
721
|
+
*
|
|
722
|
+
* So the table holds one row per colour rather than one per spelling, and {@link presetColorHex}
|
|
723
|
+
* normalizes a name into it. That is the difference between 140 hand-checked constants and 190,
|
|
724
|
+
* and it makes the abbreviation rule something a reader can see rather than something spread
|
|
725
|
+
* across fifty near-duplicate rows.
|
|
726
|
+
*
|
|
727
|
+
* This is spec data, not observed behaviour: the values come from the enumeration's own
|
|
728
|
+
* documentation, and the table is asserted complete against the schema's 190-value list in
|
|
729
|
+
* `test/read/preset-color.test.js` — a name the reader cannot resolve is a hole in the table,
|
|
730
|
+
* and that test is what finds it.
|
|
731
|
+
*/
|
|
732
|
+
/**
|
|
733
|
+
* Canonical (lower-case, unabbreviated, `gray`-spelled) preset colour name → 6-hex RGB.
|
|
734
|
+
* Reach it through {@link presetColorHex}, which does the normalizing.
|
|
735
|
+
*/
|
|
736
|
+
const PRESET_COLOR_HEX = {
|
|
737
|
+
aliceblue: "F0F8FF",
|
|
738
|
+
antiquewhite: "FAEBD7",
|
|
739
|
+
aqua: "00FFFF",
|
|
740
|
+
aquamarine: "7FFFD4",
|
|
741
|
+
azure: "F0FFFF",
|
|
742
|
+
beige: "F5F5DC",
|
|
743
|
+
bisque: "FFE4C4",
|
|
744
|
+
black: "000000",
|
|
745
|
+
blanchedalmond: "FFEBCD",
|
|
746
|
+
blue: "0000FF",
|
|
747
|
+
blueviolet: "8A2BE2",
|
|
748
|
+
brown: "A52A2A",
|
|
749
|
+
burlywood: "DEB887",
|
|
750
|
+
cadetblue: "5F9EA0",
|
|
751
|
+
chartreuse: "7FFF00",
|
|
752
|
+
chocolate: "D2691E",
|
|
753
|
+
coral: "FF7F50",
|
|
754
|
+
cornflowerblue: "6495ED",
|
|
755
|
+
cornsilk: "FFF8DC",
|
|
756
|
+
crimson: "DC143C",
|
|
757
|
+
cyan: "00FFFF",
|
|
758
|
+
darkblue: "00008B",
|
|
759
|
+
darkcyan: "008B8B",
|
|
760
|
+
darkgoldenrod: "B8860B",
|
|
761
|
+
darkgray: "A9A9A9",
|
|
762
|
+
darkgreen: "006400",
|
|
763
|
+
darkkhaki: "BDB76B",
|
|
764
|
+
darkmagenta: "8B008B",
|
|
765
|
+
darkolivegreen: "556B2F",
|
|
766
|
+
darkorange: "FF8C00",
|
|
767
|
+
darkorchid: "9932CC",
|
|
768
|
+
darkred: "8B0000",
|
|
769
|
+
darksalmon: "E9967A",
|
|
770
|
+
darkseagreen: "8FBC8F",
|
|
771
|
+
darkslateblue: "483D8B",
|
|
772
|
+
darkslategray: "2F4F4F",
|
|
773
|
+
darkturquoise: "00CED1",
|
|
774
|
+
darkviolet: "9400D3",
|
|
775
|
+
deeppink: "FF1493",
|
|
776
|
+
deepskyblue: "00BFFF",
|
|
777
|
+
dimgray: "696969",
|
|
778
|
+
dodgerblue: "1E90FF",
|
|
779
|
+
firebrick: "B22222",
|
|
780
|
+
floralwhite: "FFFAF0",
|
|
781
|
+
forestgreen: "228B22",
|
|
782
|
+
fuchsia: "FF00FF",
|
|
783
|
+
gainsboro: "DCDCDC",
|
|
784
|
+
ghostwhite: "F8F8FF",
|
|
785
|
+
gold: "FFD700",
|
|
786
|
+
goldenrod: "DAA520",
|
|
787
|
+
gray: "808080",
|
|
788
|
+
green: "008000",
|
|
789
|
+
greenyellow: "ADFF2F",
|
|
790
|
+
honeydew: "F0FFF0",
|
|
791
|
+
hotpink: "FF69B4",
|
|
792
|
+
indianred: "CD5C5C",
|
|
793
|
+
indigo: "4B0082",
|
|
794
|
+
ivory: "FFFFF0",
|
|
795
|
+
khaki: "F0E68C",
|
|
796
|
+
lavender: "E6E6FA",
|
|
797
|
+
lavenderblush: "FFF0F5",
|
|
798
|
+
lawngreen: "7CFC00",
|
|
799
|
+
lemonchiffon: "FFFACD",
|
|
800
|
+
lightblue: "ADD8E6",
|
|
801
|
+
lightcoral: "F08080",
|
|
802
|
+
lightcyan: "E0FFFF",
|
|
803
|
+
lightgoldenrodyellow: "FAFAD2",
|
|
804
|
+
lightgray: "D3D3D3",
|
|
805
|
+
lightgreen: "90EE90",
|
|
806
|
+
lightpink: "FFB6C1",
|
|
807
|
+
lightsalmon: "FFA07A",
|
|
808
|
+
lightseagreen: "20B2AA",
|
|
809
|
+
lightskyblue: "87CEFA",
|
|
810
|
+
lightslategray: "778899",
|
|
811
|
+
lightsteelblue: "B0C4DE",
|
|
812
|
+
lightyellow: "FFFFE0",
|
|
813
|
+
lime: "00FF00",
|
|
814
|
+
limegreen: "32CD32",
|
|
815
|
+
linen: "FAF0E6",
|
|
816
|
+
magenta: "FF00FF",
|
|
817
|
+
maroon: "800000",
|
|
818
|
+
mediumaquamarine: "66CDAA",
|
|
819
|
+
mediumblue: "0000CD",
|
|
820
|
+
mediumorchid: "BA55D3",
|
|
821
|
+
mediumpurple: "9370DB",
|
|
822
|
+
mediumseagreen: "3CB371",
|
|
823
|
+
mediumslateblue: "7B68EE",
|
|
824
|
+
mediumspringgreen: "00FA9A",
|
|
825
|
+
mediumturquoise: "48D1CC",
|
|
826
|
+
mediumvioletred: "C71585",
|
|
827
|
+
midnightblue: "191970",
|
|
828
|
+
mintcream: "F5FFFA",
|
|
829
|
+
mistyrose: "FFE4E1",
|
|
830
|
+
moccasin: "FFE4B5",
|
|
831
|
+
navajowhite: "FFDEAD",
|
|
832
|
+
navy: "000080",
|
|
833
|
+
oldlace: "FDF5E6",
|
|
834
|
+
olive: "808000",
|
|
835
|
+
olivedrab: "6B8E23",
|
|
836
|
+
orange: "FFA500",
|
|
837
|
+
orangered: "FF4500",
|
|
838
|
+
orchid: "DA70D6",
|
|
839
|
+
palegoldenrod: "EEE8AA",
|
|
840
|
+
palegreen: "98FB98",
|
|
841
|
+
paleturquoise: "AFEEEE",
|
|
842
|
+
palevioletred: "DB7093",
|
|
843
|
+
papayawhip: "FFEFD5",
|
|
844
|
+
peachpuff: "FFDAB9",
|
|
845
|
+
peru: "CD853F",
|
|
846
|
+
pink: "FFC0CB",
|
|
847
|
+
plum: "DDA0DD",
|
|
848
|
+
powderblue: "B0E0E6",
|
|
849
|
+
purple: "800080",
|
|
850
|
+
red: "FF0000",
|
|
851
|
+
rosybrown: "BC8F8F",
|
|
852
|
+
royalblue: "4169E1",
|
|
853
|
+
saddlebrown: "8B4513",
|
|
854
|
+
salmon: "FA8072",
|
|
855
|
+
sandybrown: "F4A460",
|
|
856
|
+
seagreen: "2E8B57",
|
|
857
|
+
seashell: "FFF5EE",
|
|
858
|
+
sienna: "A0522D",
|
|
859
|
+
silver: "C0C0C0",
|
|
860
|
+
skyblue: "87CEEB",
|
|
861
|
+
slateblue: "6A5ACD",
|
|
862
|
+
slategray: "708090",
|
|
863
|
+
snow: "FFFAFA",
|
|
864
|
+
springgreen: "00FF7F",
|
|
865
|
+
steelblue: "4682B4",
|
|
866
|
+
tan: "D2B48C",
|
|
867
|
+
teal: "008080",
|
|
868
|
+
thistle: "D8BFD8",
|
|
869
|
+
tomato: "FF6347",
|
|
870
|
+
turquoise: "40E0D0",
|
|
871
|
+
violet: "EE82EE",
|
|
872
|
+
wheat: "F5DEB3",
|
|
873
|
+
white: "FFFFFF",
|
|
874
|
+
whitesmoke: "F5F5F5",
|
|
875
|
+
yellow: "FFFF00",
|
|
876
|
+
yellowgreen: "9ACD32"
|
|
877
|
+
};
|
|
878
|
+
/**
|
|
879
|
+
* Resolve an `a:prstClr/@val` preset colour name to its 6-hex RGB, or `null` when the name is
|
|
880
|
+
* not one of the 190 `ST_PresetColorVal` values.
|
|
881
|
+
*
|
|
882
|
+
* Lenient about case for the same reason the rest of the read path is: the input is a file
|
|
883
|
+
* someone else wrote, and a producer that spelled `Black` has still said which colour it meant.
|
|
884
|
+
* A name that is not in the enumeration at all resolves to `null` rather than to a guess, so the
|
|
885
|
+
* caller reports "no colour" instead of painting an invented one.
|
|
886
|
+
* @param name - the raw attribute value, or `null` when the attribute is absent
|
|
887
|
+
*/
|
|
888
|
+
function presetColorHex(name) {
|
|
889
|
+
if (!name) return null;
|
|
890
|
+
const canonical = name.toLowerCase().replace(/grey/g, "gray").replace(/^dk/, "dark").replace(/^lt/, "light").replace(/^med(?!ium)/, "medium");
|
|
891
|
+
return PRESET_COLOR_HEX[canonical] ?? null;
|
|
892
|
+
}
|
|
893
|
+
//#endregion
|
|
461
894
|
//#region src/read/oxml/theme.ts
|
|
462
895
|
/**
|
|
463
896
|
* Theme colour resolution: turning a DrawingML colour reference or a style-matrix reference into
|
|
@@ -551,17 +984,34 @@ function resolveSchemeToken(token, ctx) {
|
|
|
551
984
|
function isA(el, local) {
|
|
552
985
|
return !!el && el.namespaceURI === OOXML_NS.a && el.localName === local;
|
|
553
986
|
}
|
|
554
|
-
/**
|
|
987
|
+
/**
|
|
988
|
+
* The literal RGB of a *non-scheme* colour element, or `null` for one that needs the theme
|
|
989
|
+
* (`a:schemeClr`) or that this reader does not resolve.
|
|
990
|
+
*
|
|
991
|
+
* Four of `a:EG_ColorChoice`'s six members land here. `a:scrgbClr` is the one deliberately left
|
|
992
|
+
* out: its channels are percentages of a colour space the schema does not pin down, so whether
|
|
993
|
+
* `50%` is linear-light or sRGB-encoded decides the answer and neither the schema nor the
|
|
994
|
+
* transform work in `color-transform.ts` settles it. Guessing would produce a colour that is
|
|
995
|
+
* wrong by a gamma curve and says nothing about it, which is worse than reporting no colour.
|
|
996
|
+
* Closing it needs a render oracle — a deck with an `a:scrgbClr` fill authored in PowerPoint
|
|
997
|
+
* and the painted pixel read back — not a reading of the schema.
|
|
998
|
+
*/
|
|
555
999
|
function colorElementHex(color) {
|
|
556
1000
|
if (!color) return null;
|
|
557
1001
|
if (isA(color, "srgbClr")) return attr(color, "val");
|
|
558
1002
|
if (isA(color, "sysClr")) return attr(color, "lastClr") ?? attr(color, "val");
|
|
1003
|
+
if (isA(color, "prstClr")) return presetColorHex(attr(color, "val"));
|
|
1004
|
+
if (isA(color, "hslClr")) return hslClrToHex(attr(color, "hue"), attr(color, "sat"), attr(color, "lum"));
|
|
559
1005
|
return null;
|
|
560
1006
|
}
|
|
561
1007
|
/**
|
|
562
1008
|
* Resolve a DrawingML colour element to a literal `{ hex, transforms }`, routing
|
|
563
1009
|
* `a:schemeClr` through the context. Returns `null` when the base cannot be made
|
|
564
1010
|
* literal (unmapped token, or a colour model we do not flatten).
|
|
1011
|
+
*
|
|
1012
|
+
* Five of `a:EG_ColorChoice`'s six members resolve: `a:srgbClr`, `a:sysClr`, `a:schemeClr`,
|
|
1013
|
+
* `a:prstClr` (through the {@link presetColorHex} table) and `a:hslClr`. `a:scrgbClr` does not —
|
|
1014
|
+
* see {@link colorElementHex} for why that one is a `null` rather than a guess.
|
|
565
1015
|
*/
|
|
566
1016
|
function resolveColor(color, ctx) {
|
|
567
1017
|
if (!color) return null;
|
|
@@ -573,7 +1023,7 @@ function resolveColor(color, ctx) {
|
|
|
573
1023
|
transforms
|
|
574
1024
|
} : null;
|
|
575
1025
|
}
|
|
576
|
-
if (isA(color, "sysClr")) {
|
|
1026
|
+
if (isA(color, "sysClr") || isA(color, "prstClr") || isA(color, "hslClr")) {
|
|
577
1027
|
const hex = colorElementHex(color);
|
|
578
1028
|
return hex ? {
|
|
579
1029
|
hex,
|
|
@@ -1038,186 +1488,6 @@ function slideDefinesProp(name, run, pPr, slideLst, level) {
|
|
|
1038
1488
|
return !!(slideDefRPr && attr(slideDefRPr, name) != null);
|
|
1039
1489
|
}
|
|
1040
1490
|
//#endregion
|
|
1041
|
-
//#region src/read/oxml/color-transform.ts
|
|
1042
|
-
/** DrawingML percentage (`100%` = `100000`) → fraction, or `null` when unparseable. */
|
|
1043
|
-
function pct(value) {
|
|
1044
|
-
if (value === null || value === "") return null;
|
|
1045
|
-
const n = Number(value);
|
|
1046
|
-
return Number.isFinite(n) ? n / 1e5 : null;
|
|
1047
|
-
}
|
|
1048
|
-
const clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
1049
|
-
/** Parse a 6-hex string (`RRGGBB`, optional `#`) into 0–1 sRGB channels, or `null`. */
|
|
1050
|
-
function parseHex(hex) {
|
|
1051
|
-
const h = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
1052
|
-
if (!/^[0-9a-fA-F]{6}$/.test(h)) return null;
|
|
1053
|
-
return {
|
|
1054
|
-
r: parseInt(h.slice(0, 2), 16) / 255,
|
|
1055
|
-
g: parseInt(h.slice(2, 4), 16) / 255,
|
|
1056
|
-
b: parseInt(h.slice(4, 6), 16) / 255
|
|
1057
|
-
};
|
|
1058
|
-
}
|
|
1059
|
-
/** 0–1 sRGB channels → uppercase `RRGGBB`, rounding and clamping each channel. */
|
|
1060
|
-
function toHex(c) {
|
|
1061
|
-
const ch = (v) => Math.round(clamp01(v) * 255).toString(16).toUpperCase().padStart(2, "0");
|
|
1062
|
-
return `${ch(c.r)}${ch(c.g)}${ch(c.b)}`;
|
|
1063
|
-
}
|
|
1064
|
-
/** sRGB channel (0–1) → linear-light (gamma decode). */
|
|
1065
|
-
function srgbToLinear(c) {
|
|
1066
|
-
return c <= .04045 ? c / 12.92 : Math.pow((c + .055) / 1.055, 2.4);
|
|
1067
|
-
}
|
|
1068
|
-
/** Linear-light channel (0–1) → sRGB (gamma encode). */
|
|
1069
|
-
function linearToSrgb(c) {
|
|
1070
|
-
return c <= .0031308 ? c * 12.92 : 1.055 * Math.pow(c, 1 / 2.4) - .055;
|
|
1071
|
-
}
|
|
1072
|
-
/** sRGB (0–1) → HSL with H in degrees [0,360), S/L in [0,1]. */
|
|
1073
|
-
function rgbToHsl(c) {
|
|
1074
|
-
const { r, g, b } = c;
|
|
1075
|
-
const max = Math.max(r, g, b);
|
|
1076
|
-
const min = Math.min(r, g, b);
|
|
1077
|
-
const l = (max + min) / 2;
|
|
1078
|
-
const d = max - min;
|
|
1079
|
-
if (d === 0) return {
|
|
1080
|
-
h: 0,
|
|
1081
|
-
s: 0,
|
|
1082
|
-
l
|
|
1083
|
-
};
|
|
1084
|
-
const s = d / (1 - Math.abs(2 * l - 1));
|
|
1085
|
-
let h;
|
|
1086
|
-
if (max === r) h = (g - b) / d % 6;
|
|
1087
|
-
else if (max === g) h = (b - r) / d + 2;
|
|
1088
|
-
else h = (r - g) / d + 4;
|
|
1089
|
-
h *= 60;
|
|
1090
|
-
if (h < 0) h += 360;
|
|
1091
|
-
return {
|
|
1092
|
-
h,
|
|
1093
|
-
s,
|
|
1094
|
-
l
|
|
1095
|
-
};
|
|
1096
|
-
}
|
|
1097
|
-
/** HSL (H degrees, S/L 0–1) → sRGB (0–1). */
|
|
1098
|
-
function hslToRgb({ h, s, l }) {
|
|
1099
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
1100
|
-
const hp = (h % 360 + 360) % 360 / 60;
|
|
1101
|
-
const x = c * (1 - Math.abs(hp % 2 - 1));
|
|
1102
|
-
const [r, g, b] = hp < 1 ? [
|
|
1103
|
-
c,
|
|
1104
|
-
x,
|
|
1105
|
-
0
|
|
1106
|
-
] : hp < 2 ? [
|
|
1107
|
-
x,
|
|
1108
|
-
c,
|
|
1109
|
-
0
|
|
1110
|
-
] : hp < 3 ? [
|
|
1111
|
-
0,
|
|
1112
|
-
c,
|
|
1113
|
-
x
|
|
1114
|
-
] : hp < 4 ? [
|
|
1115
|
-
0,
|
|
1116
|
-
x,
|
|
1117
|
-
c
|
|
1118
|
-
] : hp < 5 ? [
|
|
1119
|
-
x,
|
|
1120
|
-
0,
|
|
1121
|
-
c
|
|
1122
|
-
] : [
|
|
1123
|
-
c,
|
|
1124
|
-
0,
|
|
1125
|
-
x
|
|
1126
|
-
];
|
|
1127
|
-
const m = l - c / 2;
|
|
1128
|
-
return {
|
|
1129
|
-
r: r + m,
|
|
1130
|
-
g: g + m,
|
|
1131
|
-
b: b + m
|
|
1132
|
-
};
|
|
1133
|
-
}
|
|
1134
|
-
/**
|
|
1135
|
-
* Apply an ordered list of DrawingML colour transforms to a base sRGB hex,
|
|
1136
|
-
* returning the effective sRGB hex (and opacity, when an `alpha*` modifier set
|
|
1137
|
-
* it). Pure: no DOM, no theme lookup — the caller has already resolved the base
|
|
1138
|
-
* colour and extracted each modifier's `{ name, value }`.
|
|
1139
|
-
*
|
|
1140
|
-
* An unparseable base hex is returned unchanged (with no transforms applied); an
|
|
1141
|
-
* unparseable/`null` modifier value is skipped.
|
|
1142
|
-
*/
|
|
1143
|
-
function applyColorTransforms(baseHex, transforms) {
|
|
1144
|
-
const parsed = parseHex(baseHex);
|
|
1145
|
-
if (!parsed) return { hex: baseHex };
|
|
1146
|
-
let rgb = parsed;
|
|
1147
|
-
let alpha;
|
|
1148
|
-
for (const { name, value } of transforms) switch (name) {
|
|
1149
|
-
case "lumMod":
|
|
1150
|
-
case "lumOff": {
|
|
1151
|
-
const f = pct(value);
|
|
1152
|
-
if (f === null) break;
|
|
1153
|
-
const hsl = rgbToHsl(rgb);
|
|
1154
|
-
hsl.l = clamp01(name === "lumMod" ? hsl.l * f : hsl.l + f);
|
|
1155
|
-
rgb = hslToRgb(hsl);
|
|
1156
|
-
break;
|
|
1157
|
-
}
|
|
1158
|
-
case "satMod":
|
|
1159
|
-
case "satOff": {
|
|
1160
|
-
const f = pct(value);
|
|
1161
|
-
if (f === null) break;
|
|
1162
|
-
const hsl = rgbToHsl(rgb);
|
|
1163
|
-
hsl.s = clamp01(name === "satMod" ? hsl.s * f : hsl.s + f);
|
|
1164
|
-
rgb = hslToRgb(hsl);
|
|
1165
|
-
break;
|
|
1166
|
-
}
|
|
1167
|
-
case "hueMod":
|
|
1168
|
-
case "hueOff": {
|
|
1169
|
-
const hsl = rgbToHsl(rgb);
|
|
1170
|
-
if (name === "hueMod") {
|
|
1171
|
-
const f = pct(value);
|
|
1172
|
-
if (f === null) break;
|
|
1173
|
-
hsl.h = (hsl.h * f % 360 + 360) % 360;
|
|
1174
|
-
} else {
|
|
1175
|
-
const n = value === null ? NaN : Number(value);
|
|
1176
|
-
if (!Number.isFinite(n)) break;
|
|
1177
|
-
hsl.h = ((hsl.h + n / 6e4) % 360 + 360) % 360;
|
|
1178
|
-
}
|
|
1179
|
-
rgb = hslToRgb(hsl);
|
|
1180
|
-
break;
|
|
1181
|
-
}
|
|
1182
|
-
case "shade":
|
|
1183
|
-
case "tint": {
|
|
1184
|
-
const k = pct(value);
|
|
1185
|
-
if (k === null) break;
|
|
1186
|
-
const lin = {
|
|
1187
|
-
r: srgbToLinear(rgb.r),
|
|
1188
|
-
g: srgbToLinear(rgb.g),
|
|
1189
|
-
b: srgbToLinear(rgb.b)
|
|
1190
|
-
};
|
|
1191
|
-
const apply = (c) => clamp01(name === "shade" ? c * k : c * k + (1 - k));
|
|
1192
|
-
rgb = {
|
|
1193
|
-
r: linearToSrgb(apply(lin.r)),
|
|
1194
|
-
g: linearToSrgb(apply(lin.g)),
|
|
1195
|
-
b: linearToSrgb(apply(lin.b))
|
|
1196
|
-
};
|
|
1197
|
-
break;
|
|
1198
|
-
}
|
|
1199
|
-
case "alpha": {
|
|
1200
|
-
const f = pct(value);
|
|
1201
|
-
if (f !== null) alpha = clamp01(f);
|
|
1202
|
-
break;
|
|
1203
|
-
}
|
|
1204
|
-
case "alphaMod": {
|
|
1205
|
-
const f = pct(value);
|
|
1206
|
-
if (f !== null) alpha = clamp01((alpha ?? 1) * f);
|
|
1207
|
-
break;
|
|
1208
|
-
}
|
|
1209
|
-
case "alphaOff": {
|
|
1210
|
-
const f = pct(value);
|
|
1211
|
-
if (f !== null) alpha = clamp01((alpha ?? 1) + f);
|
|
1212
|
-
break;
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
return alpha === void 0 ? { hex: toHex(rgb) } : {
|
|
1216
|
-
hex: toHex(rgb),
|
|
1217
|
-
alpha
|
|
1218
|
-
};
|
|
1219
|
-
}
|
|
1220
|
-
//#endregion
|
|
1221
1491
|
//#region src/read/api/theme-context.ts
|
|
1222
1492
|
/**
|
|
1223
1493
|
* Resolve a slide's theme colour context: the slide → slideLayout → slideMaster
|
|
@@ -1459,22 +1729,44 @@ function resolveInheritedRunFontFace(ph, level, pPr, slideLstStyle, ctx) {
|
|
|
1459
1729
|
return null;
|
|
1460
1730
|
}
|
|
1461
1731
|
/**
|
|
1462
|
-
* Whether a run effectively renders
|
|
1463
|
-
* walking the same chain as
|
|
1464
|
-
*
|
|
1732
|
+
* Whether a run effectively renders with the boolean character property `name`
|
|
1733
|
+
* (`b`/`i`) when its own `a:rPr` sets none, walking the same chain as
|
|
1734
|
+
* {@link resolveInheritedRunSize}: the first `a:defRPr/@<name>` in the paragraph →
|
|
1465
1735
|
* slide → (placeholder) layout → master → `p:txStyles` → `p:defaultTextStyle`
|
|
1466
|
-
* chain.
|
|
1467
|
-
*
|
|
1468
|
-
*
|
|
1736
|
+
* chain. The first tier that *states* the attribute wins, and its value is parsed
|
|
1737
|
+
* by {@link boolValue} — the same `xsd:boolean` parser the run's own `bold`/`italic`
|
|
1738
|
+
* getters use — so an unparseable value reports `null` rather than being read as
|
|
1739
|
+
* `false`. `null` when nothing in the chain states it.
|
|
1469
1740
|
*/
|
|
1470
|
-
function
|
|
1741
|
+
function resolveInheritedRunFlag(name, ph, level, pPr, slideLstStyle, ctx) {
|
|
1471
1742
|
for (const defRPr of inheritedRunDefRPrs(ph, level, pPr, slideLstStyle, ctx)) {
|
|
1472
|
-
const
|
|
1473
|
-
if (
|
|
1743
|
+
const raw = attr(defRPr, name);
|
|
1744
|
+
if (raw !== null) return boolValue(raw);
|
|
1474
1745
|
}
|
|
1475
1746
|
return null;
|
|
1476
1747
|
}
|
|
1477
1748
|
/**
|
|
1749
|
+
* Whether a run effectively renders bold when its own `a:rPr` sets no `@b`: the
|
|
1750
|
+
* first `a:defRPr/@b` in the inheritance chain (see {@link resolveInheritedRunFlag}).
|
|
1751
|
+
* A non-placeholder run skips the placeholder tiers but still reaches
|
|
1752
|
+
* `p:defaultTextStyle` (which sets no `@b` in a PowerPoint-written deck, so bold
|
|
1753
|
+
* stays `null` there). `null` when nothing in the chain defines bold.
|
|
1754
|
+
*/
|
|
1755
|
+
function resolveInheritedRunBold(ph, level, pPr, slideLstStyle, ctx) {
|
|
1756
|
+
return resolveInheritedRunFlag("b", ph, level, pPr, slideLstStyle, ctx);
|
|
1757
|
+
}
|
|
1758
|
+
/**
|
|
1759
|
+
* Whether a run effectively renders italic when its own `a:rPr` sets no `@i`: the
|
|
1760
|
+
* first `a:defRPr/@i` in the inheritance chain (see {@link resolveInheritedRunFlag}).
|
|
1761
|
+
* `@b` and `@i` are siblings on `CT_TextCharacterProperties` and a master text
|
|
1762
|
+
* style states them together, so this is the exact twin of
|
|
1763
|
+
* {@link resolveInheritedRunBold} one attribute along. `null` when nothing in the
|
|
1764
|
+
* chain defines italic.
|
|
1765
|
+
*/
|
|
1766
|
+
function resolveInheritedRunItalic(ph, level, pPr, slideLstStyle, ctx) {
|
|
1767
|
+
return resolveInheritedRunFlag("i", ph, level, pPr, slideLstStyle, ctx);
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1478
1770
|
* The vertical anchor a placeholder text frame effectively renders when its own
|
|
1479
1771
|
* `a:bodyPr` sets no `@anchor`: the value inherited from the layout → master
|
|
1480
1772
|
* placeholder `a:bodyPr` (see {@link placeholderInheritedAnchor}). `null` when the
|
|
@@ -1515,8 +1807,9 @@ function resolveInheritedFrame(ph, ctx) {
|
|
|
1515
1807
|
};
|
|
1516
1808
|
}
|
|
1517
1809
|
/**
|
|
1518
|
-
* Resolve a DrawingML colour *element* (`a:
|
|
1519
|
-
* against `ctx` into a full
|
|
1810
|
+
* Resolve a DrawingML colour *element* (every `a:EG_ColorChoice` member except
|
|
1811
|
+
* `a:scrgbClr` — see `read/oxml/theme.ts`) against `ctx` into a full
|
|
1812
|
+
* {@link ResolvedColor} — base hex, raw transform list,
|
|
1520
1813
|
* and the `effectiveHex`/`alpha` after applying those transforms. `null` when the
|
|
1521
1814
|
* element cannot be made literal (unmapped token, or a colour model we do not
|
|
1522
1815
|
* resolve). Shared by the solid-fill and gradient-stop colour reads.
|
|
@@ -1616,14 +1909,17 @@ function readGradientStops(container, ctx) {
|
|
|
1616
1909
|
const gsLst = firstChild(grad, "a:gsLst");
|
|
1617
1910
|
if (!gsLst) return [];
|
|
1618
1911
|
return getElements(gsLst, "a:gs").map((gs) => {
|
|
1912
|
+
const colorEl = firstChildElement(gs);
|
|
1913
|
+
const model = colorEl?.localName ?? null;
|
|
1619
1914
|
const pos = intValue(attr(gs, "pos"));
|
|
1620
|
-
const
|
|
1621
|
-
const
|
|
1622
|
-
const resolved = resolveColorElement(srgb ?? scheme ?? null, ctx);
|
|
1915
|
+
const resolved = resolveColorElement(colorEl, ctx);
|
|
1916
|
+
const rawOf = (local) => colorEl && model === local ? attr(colorEl, "val") : null;
|
|
1623
1917
|
return {
|
|
1624
|
-
position: pos === null ? null : pos /
|
|
1625
|
-
color:
|
|
1626
|
-
schemeColor:
|
|
1918
|
+
position: pos === null ? null : pos / PERCENT_SCALE,
|
|
1919
|
+
color: rawOf("srgbClr"),
|
|
1920
|
+
schemeColor: rawOf("schemeClr"),
|
|
1921
|
+
presetColor: rawOf("prstClr"),
|
|
1922
|
+
resolvedColor: resolved,
|
|
1627
1923
|
effectiveHex: resolved ? resolved.effectiveHex : null,
|
|
1628
1924
|
...resolved?.alpha !== void 0 ? { alpha: resolved.alpha } : {}
|
|
1629
1925
|
};
|
|
@@ -1642,7 +1938,7 @@ function readGradientFill(container, ctx) {
|
|
|
1642
1938
|
const ang = lin ? intValue(attr(lin, "ang")) : null;
|
|
1643
1939
|
return {
|
|
1644
1940
|
kind: lin ? "linear" : path ? "path" : null,
|
|
1645
|
-
angleDeg: ang === null ? null : ang /
|
|
1941
|
+
angleDeg: ang === null ? null : ang / ANGLE_UNITS_PER_DEGREE,
|
|
1646
1942
|
path: path ? attr(path, "path") ?? null : null,
|
|
1647
1943
|
stops: readGradientStops(container, ctx) ?? []
|
|
1648
1944
|
};
|
|
@@ -1664,7 +1960,7 @@ function readGradientFill(container, ctx) {
|
|
|
1664
1960
|
function readRect(rect) {
|
|
1665
1961
|
const edge = (name) => {
|
|
1666
1962
|
const v = intValue(attr(rect, name));
|
|
1667
|
-
return v === null ? 0 : v /
|
|
1963
|
+
return v === null ? 0 : v / PERCENT_SCALE;
|
|
1668
1964
|
};
|
|
1669
1965
|
return {
|
|
1670
1966
|
left: edge("l"),
|
|
@@ -1677,7 +1973,7 @@ function readRect(rect) {
|
|
|
1677
1973
|
function readTile(tile) {
|
|
1678
1974
|
const scale = (name) => {
|
|
1679
1975
|
const v = intValue(attr(tile, name));
|
|
1680
|
-
return v === null ? 1 : v /
|
|
1976
|
+
return v === null ? 1 : v / PERCENT_SCALE;
|
|
1681
1977
|
};
|
|
1682
1978
|
return {
|
|
1683
1979
|
offsetXEmu: intValue(attr(tile, "tx")) ?? 0,
|
|
@@ -1720,7 +2016,7 @@ function readPictureFill(container, rels = null) {
|
|
|
1720
2016
|
srcRect: srcRect ? readRect(srcRect) : null,
|
|
1721
2017
|
fillRect: fillRect ? readRect(fillRect) : null,
|
|
1722
2018
|
tile: tile ? readTile(tile) : null,
|
|
1723
|
-
alpha: alphaMod ? amt === null ? 1 : amt /
|
|
2019
|
+
alpha: alphaMod ? amt === null ? 1 : amt / PERCENT_SCALE : null,
|
|
1724
2020
|
dpi: intValue(attr(blipFill, "dpi")),
|
|
1725
2021
|
rotWithShape: boolValue(attr(blipFill, "rotWithShape"))
|
|
1726
2022
|
};
|
|
@@ -1862,8 +2158,8 @@ function readGeometryPath(path) {
|
|
|
1862
2158
|
cmd: "arcTo",
|
|
1863
2159
|
wR: intValue(attr(seg, "wR")) ?? 0,
|
|
1864
2160
|
hR: intValue(attr(seg, "hR")) ?? 0,
|
|
1865
|
-
stAng: (intValue(attr(seg, "stAng")) ?? 0) /
|
|
1866
|
-
swAng: (intValue(attr(seg, "swAng")) ?? 0) /
|
|
2161
|
+
stAng: (intValue(attr(seg, "stAng")) ?? 0) / ANGLE_UNITS_PER_DEGREE,
|
|
2162
|
+
swAng: (intValue(attr(seg, "swAng")) ?? 0) / ANGLE_UNITS_PER_DEGREE
|
|
1867
2163
|
});
|
|
1868
2164
|
break;
|
|
1869
2165
|
case "close": commands.push({ cmd: "close" });
|
|
@@ -1901,7 +2197,7 @@ function readBox(xfrm, offName, extName) {
|
|
|
1901
2197
|
}
|
|
1902
2198
|
function rotationDegrees(xfrm) {
|
|
1903
2199
|
const rot = intValue(attr(xfrm, "rot"));
|
|
1904
|
-
return rot === null ? 0 : rot /
|
|
2200
|
+
return rot === null ? 0 : rot / ANGLE_UNITS_PER_DEGREE;
|
|
1905
2201
|
}
|
|
1906
2202
|
function transformFlipH(xfrm) {
|
|
1907
2203
|
return boolValue(attr(xfrm, "flipH")) === true;
|
|
@@ -2045,9 +2341,10 @@ var Run = class {
|
|
|
2045
2341
|
inheritedSize;
|
|
2046
2342
|
inheritedFace;
|
|
2047
2343
|
inheritedBold;
|
|
2344
|
+
inheritedItalic;
|
|
2048
2345
|
relationships;
|
|
2049
2346
|
fontRef;
|
|
2050
|
-
constructor(element, part, themeContext, inheritedColor, inheritedSize, inheritedFace, inheritedBold, relationships, fontRef) {
|
|
2347
|
+
constructor(element, part, themeContext, inheritedColor, inheritedSize, inheritedFace, inheritedBold, inheritedItalic, relationships, fontRef) {
|
|
2051
2348
|
this.element = element;
|
|
2052
2349
|
this.part = part;
|
|
2053
2350
|
this.themeContext = themeContext;
|
|
@@ -2055,6 +2352,7 @@ var Run = class {
|
|
|
2055
2352
|
this.inheritedSize = inheritedSize;
|
|
2056
2353
|
this.inheritedFace = inheritedFace;
|
|
2057
2354
|
this.inheritedBold = inheritedBold;
|
|
2355
|
+
this.inheritedItalic = inheritedItalic;
|
|
2058
2356
|
this.relationships = relationships;
|
|
2059
2357
|
this.fontRef = fontRef;
|
|
2060
2358
|
}
|
|
@@ -2284,6 +2582,19 @@ var Run = class {
|
|
|
2284
2582
|
get resolvedBold() {
|
|
2285
2583
|
return this.bold ?? this.inheritedBold?.() ?? null;
|
|
2286
2584
|
}
|
|
2585
|
+
/**
|
|
2586
|
+
* Whether this run effectively renders italic. It is the run's own `@i`
|
|
2587
|
+
* ({@link italic}) when set; otherwise, for a run inside a placeholder, the italic
|
|
2588
|
+
* state it inherits from the placeholder/list-style chain (paragraph `a:defRPr` →
|
|
2589
|
+
* slide `a:lstStyle` → layout → master placeholder `a:lstStyle` → master
|
|
2590
|
+
* `p:txStyles`). `null` when the run sets no `@i` and inherits none — the
|
|
2591
|
+
* resolved counterpart of {@link italic}, and the twin of {@link resolvedBold}:
|
|
2592
|
+
* `@b` and `@i` are siblings a master text style states together, so a deck that
|
|
2593
|
+
* can be authored with an inherited italic can be read back with one.
|
|
2594
|
+
*/
|
|
2595
|
+
get resolvedItalic() {
|
|
2596
|
+
return this.italic ?? this.inheritedItalic?.() ?? null;
|
|
2597
|
+
}
|
|
2287
2598
|
/** Escape hatch: the underlying `a:r` element. After mutating it call {@link markDirty}, or `save()` writes the original bytes. */
|
|
2288
2599
|
get element_() {
|
|
2289
2600
|
return this.element;
|
|
@@ -2352,8 +2663,9 @@ var Paragraph = class {
|
|
|
2352
2663
|
const inheritedSize = this.#inheritedResolver((ph, level, pPr, slideLst, ctx) => resolveInheritedRunSize(ph, level, pPr, slideLst, ctx));
|
|
2353
2664
|
const inheritedFace = this.#inheritedResolver((ph, level, pPr, slideLst, ctx) => resolveInheritedRunFontFace(ph, level, pPr, slideLst, ctx));
|
|
2354
2665
|
const inheritedBold = this.#inheritedResolver((ph, level, pPr, slideLst, ctx) => resolveInheritedRunBold(ph, level, pPr, slideLst, ctx));
|
|
2666
|
+
const inheritedItalic = this.#inheritedResolver((ph, level, pPr, slideLst, ctx) => resolveInheritedRunItalic(ph, level, pPr, slideLst, ctx));
|
|
2355
2667
|
const fontRef = this.inherit?.placeholder.fontRef ?? null;
|
|
2356
|
-
return getElements(this.element, "a:r").map((element) => new Run(element, this.part, this.themeContext, inheritedColor, inheritedSize, inheritedFace, inheritedBold, this.relationships, fontRef));
|
|
2668
|
+
return getElements(this.element, "a:r").map((element) => new Run(element, this.part, this.themeContext, inheritedColor, inheritedSize, inheritedFace, inheritedBold, inheritedItalic, this.relationships, fontRef));
|
|
2357
2669
|
}
|
|
2358
2670
|
/**
|
|
2359
2671
|
* A memoized thunk resolving the colour every run in this paragraph inherits
|
|
@@ -2366,7 +2678,7 @@ var Paragraph = class {
|
|
|
2366
2678
|
}
|
|
2367
2679
|
/**
|
|
2368
2680
|
* Build a memoized per-paragraph thunk for one inherited run property
|
|
2369
|
-
* (colour/size/face), or `undefined` for non-placeholder paragraphs. All runs in
|
|
2681
|
+
* (colour/size/face/bold/italic), or `undefined` for non-placeholder paragraphs. All runs in
|
|
2370
2682
|
* a paragraph share its level and `a:pPr`, so each `resolve` runs at most once
|
|
2371
2683
|
* and only when a run actually lacks its own value and asks.
|
|
2372
2684
|
*/
|
|
@@ -2513,7 +2825,7 @@ var Paragraph = class {
|
|
|
2513
2825
|
#emuAttrPt(name) {
|
|
2514
2826
|
const pPr = firstChild(this.element, "a:pPr");
|
|
2515
2827
|
const emu = pPr ? intValue(attr(pPr, name)) : null;
|
|
2516
|
-
return emu === null ? null : emu /
|
|
2828
|
+
return emu === null ? null : emu / EMU_PER_POINT;
|
|
2517
2829
|
}
|
|
2518
2830
|
/**
|
|
2519
2831
|
* The paragraph's text: run (`a:r`) and field (`a:fld`) text concatenated in
|
|
@@ -2580,7 +2892,7 @@ var TextFrame = class {
|
|
|
2580
2892
|
const insetsPt = {};
|
|
2581
2893
|
const inset = (qn, key) => {
|
|
2582
2894
|
const v = intValue(attr(bodyPr, qn));
|
|
2583
|
-
if (v !== null) insetsPt[key] = v /
|
|
2895
|
+
if (v !== null) insetsPt[key] = v / EMU_PER_POINT;
|
|
2584
2896
|
};
|
|
2585
2897
|
inset("lIns", "left");
|
|
2586
2898
|
inset("rIns", "right");
|
|
@@ -3083,7 +3395,7 @@ var Shape = class {
|
|
|
3083
3395
|
get lineWidthPt() {
|
|
3084
3396
|
const ln = this.#line();
|
|
3085
3397
|
const w = ln ? intValue(attr(ln, "w")) : null;
|
|
3086
|
-
return w === null ? null : w /
|
|
3398
|
+
return w === null ? null : w / EMU_PER_POINT;
|
|
3087
3399
|
}
|
|
3088
3400
|
/**
|
|
3089
3401
|
* The line/border dash style (`spPr/a:ln/a:prstDash/@val`), e.g. `'dash'`,
|
|
@@ -3266,7 +3578,7 @@ var Shape = class {
|
|
|
3266
3578
|
const out = { color: null };
|
|
3267
3579
|
this.#applyEffectColor(out, firstChildElement(glow));
|
|
3268
3580
|
const rad = intValue(attr(glow, "rad"));
|
|
3269
|
-
if (rad !== null) out.radiusPt = rad /
|
|
3581
|
+
if (rad !== null) out.radiusPt = rad / EMU_PER_POINT;
|
|
3270
3582
|
return out;
|
|
3271
3583
|
}
|
|
3272
3584
|
/**
|
|
@@ -3282,14 +3594,14 @@ var Shape = class {
|
|
|
3282
3594
|
const v = intValue(attr(refl, name));
|
|
3283
3595
|
if (v !== null) out[target] = v / div;
|
|
3284
3596
|
};
|
|
3285
|
-
put("blurPt", "blurRad",
|
|
3286
|
-
put("
|
|
3287
|
-
put("angleDeg", "dir",
|
|
3288
|
-
put("fadeAngleDeg", "fadeDir",
|
|
3289
|
-
put("startAlpha", "stA",
|
|
3290
|
-
put("startPos", "stPos",
|
|
3291
|
-
put("endAlpha", "endA",
|
|
3292
|
-
put("endPos", "endPos",
|
|
3597
|
+
put("blurPt", "blurRad", EMU_PER_POINT);
|
|
3598
|
+
put("offsetPt", "dist", EMU_PER_POINT);
|
|
3599
|
+
put("angleDeg", "dir", ANGLE_UNITS_PER_DEGREE);
|
|
3600
|
+
put("fadeAngleDeg", "fadeDir", ANGLE_UNITS_PER_DEGREE);
|
|
3601
|
+
put("startAlpha", "stA", PERCENT_SCALE);
|
|
3602
|
+
put("startPos", "stPos", PERCENT_SCALE);
|
|
3603
|
+
put("endAlpha", "endA", PERCENT_SCALE);
|
|
3604
|
+
put("endPos", "endPos", PERCENT_SCALE);
|
|
3293
3605
|
return out;
|
|
3294
3606
|
}
|
|
3295
3607
|
/**
|
|
@@ -3300,7 +3612,7 @@ var Shape = class {
|
|
|
3300
3612
|
const soft = this.#effect("a:softEdge");
|
|
3301
3613
|
if (!soft) return null;
|
|
3302
3614
|
const rad = intValue(attr(soft, "rad"));
|
|
3303
|
-
return { radiusPt: rad === null ? 0 : rad /
|
|
3615
|
+
return { radiusPt: rad === null ? 0 : rad / EMU_PER_POINT };
|
|
3304
3616
|
}
|
|
3305
3617
|
/**
|
|
3306
3618
|
* The shape's pattern (hatch) fill (`spPr/a:pattFill`), or `null` when the fill
|
|
@@ -3344,13 +3656,13 @@ var Shape = class {
|
|
|
3344
3656
|
/** Decode a shadow element (`a:outerShdw`/`a:innerShdw` share the fields), resolving its colour. */
|
|
3345
3657
|
#readShadow(shdw) {
|
|
3346
3658
|
const out = { color: null };
|
|
3347
|
-
this.#applyEffectColor(out,
|
|
3659
|
+
this.#applyEffectColor(out, firstChildElement(shdw));
|
|
3348
3660
|
const blur = intValue(attr(shdw, "blurRad"));
|
|
3349
3661
|
const dist = intValue(attr(shdw, "dist"));
|
|
3350
3662
|
const dir = intValue(attr(shdw, "dir"));
|
|
3351
|
-
if (blur !== null) out.blurPt = blur /
|
|
3352
|
-
if (dist !== null) out.offsetPt = dist /
|
|
3353
|
-
if (dir !== null) out.angleDeg = dir /
|
|
3663
|
+
if (blur !== null) out.blurPt = blur / EMU_PER_POINT;
|
|
3664
|
+
if (dist !== null) out.offsetPt = dist / EMU_PER_POINT;
|
|
3665
|
+
if (dir !== null) out.angleDeg = dir / ANGLE_UNITS_PER_DEGREE;
|
|
3354
3666
|
return out;
|
|
3355
3667
|
}
|
|
3356
3668
|
/**
|
|
@@ -3656,7 +3968,7 @@ var Picture = class extends Shape {
|
|
|
3656
3968
|
if (!srcRect) return null;
|
|
3657
3969
|
const edge = (name) => {
|
|
3658
3970
|
const v = intValue(attr(srcRect, name));
|
|
3659
|
-
return v === null ? 0 : v /
|
|
3971
|
+
return v === null ? 0 : v / PERCENT_SCALE;
|
|
3660
3972
|
};
|
|
3661
3973
|
return {
|
|
3662
3974
|
left: edge("l"),
|
|
@@ -3701,14 +4013,14 @@ var Picture = class extends Shape {
|
|
|
3701
4013
|
const thresh = intValue(attr(child, "thresh"));
|
|
3702
4014
|
return {
|
|
3703
4015
|
kind: "biLevel",
|
|
3704
|
-
threshold: thresh === null ? null : thresh /
|
|
4016
|
+
threshold: thresh === null ? null : thresh / PERCENT_SCALE
|
|
3705
4017
|
};
|
|
3706
4018
|
}
|
|
3707
4019
|
case "alphaModFix": {
|
|
3708
4020
|
const amt = intValue(attr(child, "amt"));
|
|
3709
4021
|
return {
|
|
3710
4022
|
kind: "alphaModFix",
|
|
3711
|
-
amount: amt === null ? 1 : amt /
|
|
4023
|
+
amount: amt === null ? 1 : amt / PERCENT_SCALE
|
|
3712
4024
|
};
|
|
3713
4025
|
}
|
|
3714
4026
|
}
|
|
@@ -3844,6 +4156,75 @@ var Connector = class extends Shape {
|
|
|
3844
4156
|
}
|
|
3845
4157
|
};
|
|
3846
4158
|
//#endregion
|
|
4159
|
+
//#region src/read/oxml/point-cache.ts
|
|
4160
|
+
/**
|
|
4161
|
+
* The one place a chart point cache is turned into an idx-ordered array.
|
|
4162
|
+
*
|
|
4163
|
+
* Both chart families cache their category/value data the same way: a run of indexed point
|
|
4164
|
+
* elements plus a declared count, which the reader widens back into a dense array so
|
|
4165
|
+
* `series.values[3]` means the point at `idx="3"` and a gap reads as `null`. Classic `c:`
|
|
4166
|
+
* charts and 2016 `cx:` charts spell it differently — `c:ptCount/@val` and a `c:v` child
|
|
4167
|
+
* against `cx:lvl/@ptCount` and the point's own text — and nothing else about the two decodes
|
|
4168
|
+
* differs, so the shape parameterizes over exactly those two differences.
|
|
4169
|
+
*
|
|
4170
|
+
* **Why neither number out of the file is trusted.** `@ptCount` is `xsd:unsignedInt`, so
|
|
4171
|
+
* `4294967295` is schema-valid, and `new Array(n).fill(null)` at that size is not a slow path
|
|
4172
|
+
* but a process kill: V8 answers `FATAL ERROR: invalid table size` and the host dies with no
|
|
4173
|
+
* exception to catch. That was reachable through the public read API — `Presentation.load()`
|
|
4174
|
+
* on a deck with an edited `c:ptCount` returned normally and the first `chart.series` access
|
|
4175
|
+
* killed the process. A single `<c:pt idx="900000000"/>` does the same thing on its own.
|
|
4176
|
+
* Reading a deck is the one part of this library whose input comes from somewhere other than
|
|
4177
|
+
* the calling program, so both numbers are bounded here.
|
|
4178
|
+
*
|
|
4179
|
+
* The array is sized by the points that are really there, not by the declared count. The cost
|
|
4180
|
+
* of that is a *trailing* gap: a cache declaring four points but carrying three (the fourth
|
|
4181
|
+
* cell blank) reads three long rather than four. No value is lost — the dropped slots are the
|
|
4182
|
+
* ones that would have been `null` — and the disagreement is warned about either way.
|
|
4183
|
+
*/
|
|
4184
|
+
/**
|
|
4185
|
+
* The highest point index this reader will honour.
|
|
4186
|
+
*
|
|
4187
|
+
* A cache references a range in the embedded workbook, and a worksheet has 1,048,576 rows, so
|
|
4188
|
+
* a point index at or above that cannot describe data any producer could have charted. Using
|
|
4189
|
+
* the sheet bound rather than a round number keeps the ceiling something a reader can check:
|
|
4190
|
+
* below it every real cache fits, above it the file is wrong by construction. It also caps the
|
|
4191
|
+
* worst-case allocation at about 8 MB of `null` — survivable, and reported — instead of an
|
|
4192
|
+
* unbounded one that takes the process with it.
|
|
4193
|
+
*/
|
|
4194
|
+
const MAX_POINT_INDEX = 1048576;
|
|
4195
|
+
/**
|
|
4196
|
+
* Read indexed cache points into a dense, idx-ordered array.
|
|
4197
|
+
*
|
|
4198
|
+
* The result is as long as the highest `@idx` actually present, and holds `null` at every
|
|
4199
|
+
* index no point claimed. A declared count that disagrees is warned about and not obeyed; a
|
|
4200
|
+
* point indexed past {@link MAX_POINT_INDEX} is warned about and dropped.
|
|
4201
|
+
* @param pts - the point elements, in document order
|
|
4202
|
+
* @param declaredCount - the cache's own count attribute, or `null` when absent
|
|
4203
|
+
* @param valueOf - reads one point's value; the two chart families differ only here
|
|
4204
|
+
* @param label - what to call the count in a diagnostic, e.g. `c:ptCount`
|
|
4205
|
+
*/
|
|
4206
|
+
function readIndexedPoints(pts, declaredCount, valueOf, label) {
|
|
4207
|
+
let count = 0;
|
|
4208
|
+
let dropped = 0;
|
|
4209
|
+
for (const pt of pts) {
|
|
4210
|
+
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4211
|
+
if (idx < 0 || idx >= MAX_POINT_INDEX) {
|
|
4212
|
+
dropped++;
|
|
4213
|
+
continue;
|
|
4214
|
+
}
|
|
4215
|
+
if (idx + 1 > count) count = idx + 1;
|
|
4216
|
+
}
|
|
4217
|
+
if (dropped > 0) warn("chart/point-index-out-of-range", `${label}: ${dropped} cache point(s) are indexed outside 0..1048575, which no worksheet-backed chart can reference; they were dropped`);
|
|
4218
|
+
if (declaredCount !== null && declaredCount !== count) warn("chart/point-count-mismatch", `${label} declares ${declaredCount} point(s) but the cache carries ${pts.length}; reading ${count} rather than allocating the declared size`);
|
|
4219
|
+
const points = new Array(count).fill(null);
|
|
4220
|
+
for (const pt of pts) {
|
|
4221
|
+
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4222
|
+
if (idx < 0 || idx >= count) continue;
|
|
4223
|
+
points[idx] = valueOf(pt);
|
|
4224
|
+
}
|
|
4225
|
+
return points;
|
|
4226
|
+
}
|
|
4227
|
+
//#endregion
|
|
3847
4228
|
//#region src/read/api/chart.ts
|
|
3848
4229
|
/** A chart graphic-frame's chart, backed by its chart part. */
|
|
3849
4230
|
var Chart = class {
|
|
@@ -4117,7 +4498,7 @@ var ChartSeries = class {
|
|
|
4117
4498
|
const dash = firstChild(ln, "a:prstDash");
|
|
4118
4499
|
const solid = readSolid(ln);
|
|
4119
4500
|
return {
|
|
4120
|
-
widthPt: w === null ? null : w /
|
|
4501
|
+
widthPt: w === null ? null : w / EMU_PER_POINT,
|
|
4121
4502
|
dash: dash ? attr(dash, "val") ?? null : null,
|
|
4122
4503
|
color: solid.color,
|
|
4123
4504
|
schemeColor: solid.schemeColor,
|
|
@@ -4211,22 +4592,11 @@ function findCache(container) {
|
|
|
4211
4592
|
}
|
|
4212
4593
|
return firstChild(container, "c:numLit") ?? firstChild(container, "c:strLit");
|
|
4213
4594
|
}
|
|
4214
|
-
/** Read a cache's points (`c:pt[@idx]/c:v`) into an idx-ordered array
|
|
4595
|
+
/** Read a cache's points (`c:pt[@idx]/c:v`) into an idx-ordered array; `c:ptCount` is the declared count. */
|
|
4215
4596
|
function readPoints(cache) {
|
|
4216
4597
|
if (!cache) return [];
|
|
4217
4598
|
const ptCount = firstChild(cache, "c:ptCount");
|
|
4218
|
-
|
|
4219
|
-
const pts = getElements(cache, "c:pt");
|
|
4220
|
-
for (const pt of pts) {
|
|
4221
|
-
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4222
|
-
if (idx + 1 > count) count = idx + 1;
|
|
4223
|
-
}
|
|
4224
|
-
const points = new Array(count).fill(null);
|
|
4225
|
-
for (const pt of pts) {
|
|
4226
|
-
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4227
|
-
points[idx] = firstChild(pt, "c:v")?.textContent ?? null;
|
|
4228
|
-
}
|
|
4229
|
-
return points;
|
|
4599
|
+
return readIndexedPoints(getElements(cache, "c:pt"), ptCount ? intValue(attr(ptCount, "val")) : null, (pt) => firstChild(pt, "c:v")?.textContent ?? null, "c:ptCount");
|
|
4230
4600
|
}
|
|
4231
4601
|
//#endregion
|
|
4232
4602
|
//#region src/read/api/chartex.ts
|
|
@@ -4460,22 +4830,10 @@ var ChartExAxis = class {
|
|
|
4460
4830
|
this.chart.part.markDirty();
|
|
4461
4831
|
}
|
|
4462
4832
|
};
|
|
4463
|
-
/** Read a `cx:lvl`'s points (`cx:pt[@idx]` text) into an idx-ordered array
|
|
4833
|
+
/** Read a `cx:lvl`'s points (`cx:pt[@idx]` text) into an idx-ordered array; `@ptCount` is the declared count. */
|
|
4464
4834
|
function readLevelPoints(lvl) {
|
|
4465
4835
|
if (!lvl) return [];
|
|
4466
|
-
|
|
4467
|
-
const pts = getElements(lvl, "cx:pt");
|
|
4468
|
-
let count = ptCount ?? 0;
|
|
4469
|
-
for (const pt of pts) {
|
|
4470
|
-
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4471
|
-
if (idx + 1 > count) count = idx + 1;
|
|
4472
|
-
}
|
|
4473
|
-
const points = new Array(count).fill(null);
|
|
4474
|
-
for (const pt of pts) {
|
|
4475
|
-
const idx = intValue(attr(pt, "idx")) ?? 0;
|
|
4476
|
-
points[idx] = pt.textContent ?? null;
|
|
4477
|
-
}
|
|
4478
|
-
return points;
|
|
4836
|
+
return readIndexedPoints(getElements(lvl, "cx:pt"), intValue(attr(lvl, "ptCount")), (pt) => pt.textContent ?? null, "cx:lvl/@ptCount");
|
|
4479
4837
|
}
|
|
4480
4838
|
/** Parse an attribute as a boolean (`1`/`true` → true, `0`/`false` → false), or `null` when absent. */
|
|
4481
4839
|
function boolAttr(element, name) {
|
|
@@ -5932,7 +6290,7 @@ var TableCell = class {
|
|
|
5932
6290
|
removeChildrenByQName(tcPr, [qname]);
|
|
5933
6291
|
const doc = ownerDocumentOf(tcPr);
|
|
5934
6292
|
const ln = createElement(doc, qname);
|
|
5935
|
-
if (border.widthPt !== void 0 && border.widthPt !== null) setAttr(ln, "w", String(checkFiniteEmu(border.widthPt *
|
|
6293
|
+
if (border.widthPt !== void 0 && border.widthPt !== null) setAttr(ln, "w", String(checkFiniteEmu(border.widthPt * EMU_PER_POINT, "widthPt", "table/invalid-cell-border")));
|
|
5936
6294
|
if (border.noFill) ln.appendChild(createElement(doc, "a:noFill"));
|
|
5937
6295
|
else if (border.schemeColor || border.color) {
|
|
5938
6296
|
const fill = createElement(doc, "a:solidFill");
|
|
@@ -6122,7 +6480,8 @@ var TableCell = class {
|
|
|
6122
6480
|
/**
|
|
6123
6481
|
* The cell's edge borders (`a:tcPr/a:lnL|lnR|lnT|lnB|lnTlToBr|lnBlToTr`), or
|
|
6124
6482
|
* `null` when the cell defines none. Each present edge decodes to a
|
|
6125
|
-
* {@link CellBorder} (width / dash / resolved + raw
|
|
6483
|
+
* {@link CellBorder} (width / dash / resolved colour + its raw token and transform
|
|
6484
|
+
* list / suppressed flag);
|
|
6126
6485
|
* absent edges are `null`. Cell borders are the biggest visible table gap — a
|
|
6127
6486
|
* replica built only from geometry and fill draws every cell edge-to-edge with
|
|
6128
6487
|
* no rule, so this surfaces the per-side stroke the writer's `border` option emits.
|
|
@@ -6138,8 +6497,9 @@ var TableCell = class {
|
|
|
6138
6497
|
const scheme = this.#fillSchemeColorOf(ln);
|
|
6139
6498
|
const resolved = this.themeColors ? resolveSolidFillColor(ln, this.themeColors) : null;
|
|
6140
6499
|
return {
|
|
6141
|
-
widthPt: w === null ? null : w /
|
|
6500
|
+
widthPt: w === null ? null : w / EMU_PER_POINT,
|
|
6142
6501
|
dash: dash ? attr(dash, "val") ?? null : null,
|
|
6502
|
+
resolvedColor: resolved,
|
|
6143
6503
|
color: resolved ? resolved.effectiveHex : null,
|
|
6144
6504
|
schemeColor: scheme,
|
|
6145
6505
|
noFill: !!firstChild(ln, "a:noFill")
|
|
@@ -6204,7 +6564,7 @@ var TableCell = class {
|
|
|
6204
6564
|
const rig = firstChild(cell3D, "a:lightRig");
|
|
6205
6565
|
const pts = (value) => {
|
|
6206
6566
|
const emu = intValue(value);
|
|
6207
|
-
return emu === null ? null : emu /
|
|
6567
|
+
return emu === null ? null : emu / EMU_PER_POINT;
|
|
6208
6568
|
};
|
|
6209
6569
|
return {
|
|
6210
6570
|
material: attr(cell3D, "prstMaterial") ?? null,
|
|
@@ -6586,6 +6946,6 @@ function findShapeByIdDeep(shapes, id) {
|
|
|
6586
6946
|
}
|
|
6587
6947
|
}
|
|
6588
6948
|
//#endregion
|
|
6589
|
-
export {
|
|
6949
|
+
export { collectElements as $, spPrXfrmEmu as A, resolveRelativePartName as At, LST_STYLE_LEVELS as B, firstChildElement as Bt, Connector as C, SPPR_LN_AFTER as Ct, Paragraph as D, partNameToZipPath as Dt, Shape as E, partNameExtension as Et, resolveLayoutColorContext as F, childElements as Ft, placeholderInheritedRunProps as G, ownerDocumentOf as Gt, placeholderInheritedAnchor as H, getOrAddChild as Ht, resolveMasterColorContext as I, createElement as It, slideDefinesColor as J, removeChildrenByQName as Jt, placeholderInheritedXfrm as K, parseXml as Kt, resolveNotesColorContext as L, descendantsByTag as Lt, readPictureFill as M, zipPathToPartName as Mt, readGradientFill as N, attr as Nt, Run as O, relativePartName as Ot, resolveColorElement as P, boolValue as Pt, carriedDecorations as Q, resolveSlideColorContext as R, escapeXmlAttribute as Rt, ChartSeries as S, SPPR_FILL_AFTER as St, AutoShape as T, SPPR_SP3D_AFTER as Tt, placeholderInheritedColor as U, insertInOrder as Ut, RUN_PROP_NAMES as V, getElements as Vt, placeholderInheritedListStyles as W, intValue as Wt, cSldName as X, serializeXml as Xt, slideDefinesProp as Y, replaceInParent as Yt, cSldOf as Z, setAttr as Zt, ChartEx as _, FILL_CHOICES as _t, isConnector as a, fmtEntry as at, Chart as b, SPPR_AFTER_XFRM as bt, isPicture as c, parseClrMap as ct, Table as d, resolveSchemeToken as dt, firstShapeChild as et, TableCell as f, styleRefFill as ft, DiagramPoint as g, applyColorTransforms as gt, DiagramNode as h, presetColorHex as ht, isAutoShape as i, SCHEME_SLOTS as it, readPatternFill as j, resolveSingleRel as jt, TextFrame as k, relsPartNameFor as kt, wrapShapeElement as l, parseClrScheme as lt, Diagram as m, substitutePhClr as mt, buildShapes as n, nvPrOf as nt, isGraphicFrame as o, intAttr as ot, TableRow as p, styleRefLine as pt, placeholderOf as q, removeAttr as qt, findShapeByIdDeep as r, spTreeOf as rt, isGroupShape as s, isA as st, GroupShape as t, nthShapeChild as tt, GraphicFrame as u, resolveColor as ut, ChartExAxis as v, RPR_FILL_AFTER as vt, Picture as w, SPPR_SCENE3D_AFTER as wt, ChartAxis as x, SPPR_EFFECT_AFTER as xt, ChartExSeries as y, SHAPE_AFTER_SPPR as yt, resolveSlideThemeParts as z, firstChild as zt };
|
|
6590
6950
|
|
|
6591
|
-
//# sourceMappingURL=shapes-
|
|
6951
|
+
//# sourceMappingURL=shapes-BemroU-z.js.map
|