pptx-kit-preview 0.3.1 → 0.3.2

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/index.js CHANGED
@@ -603,27 +603,23 @@ var patternDef = (pat) => {
603
603
  return `<path d="M0 0L${W} ${H}" stroke="${fg}" stroke-width="${width}"/>`;
604
604
  return `<path d="M${W} 0L0 ${H}" stroke="${fg}" stroke-width="${width}"/>`;
605
605
  };
606
- const dots = (density) => {
607
- const count = Math.max(1, Math.round(density * 4));
606
+ const BAYER4 = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5];
607
+ const screen = (density) => {
608
+ const threshold = density * 16;
608
609
  const out = [];
609
- const grid = count <= 1 ? [[4, 4]] : count === 2 ? [
610
- [2, 2],
611
- [6, 6]
612
- ] : [
613
- [2, 2],
614
- [6, 2],
615
- [2, 6],
616
- [6, 6]
617
- ];
618
- for (const [x, y] of grid.slice(0, count)) {
619
- out.push(`<circle cx="${x}" cy="${y}" r="0.7" fill="${fg}"/>`);
610
+ for (let i = 0; i < 16; i++) {
611
+ if (BAYER4[i] < threshold) {
612
+ const cx = i % 4 * 2;
613
+ const cy = Math.floor(i / 4) * 2;
614
+ out.push(`<rect x="${cx}" y="${cy}" width="2" height="2" fill="${fg}"/>`);
615
+ }
620
616
  }
621
617
  return out.join("");
622
618
  };
623
619
  const pctMatch = /^pct(\d+)$/.exec(preset);
624
620
  if (pctMatch) {
625
- const pct = Math.min(100, Math.max(5, Number.parseInt(pctMatch[1], 10)));
626
- body = dots(pct / 100);
621
+ const pct = Math.min(100, Math.max(0, Number.parseInt(pctMatch[1], 10)));
622
+ body = screen(pct / 100);
627
623
  } else if (preset === "horzBrick" || preset === "ltHorizontal" || preset === "narHorz") {
628
624
  body = stripe("h", 0.8);
629
625
  } else if (preset === "dkHorizontal") {
@@ -657,7 +653,7 @@ var patternDef = (pat) => {
657
653
  } else if (preset === "solidDmnd" || preset === "openDmnd") {
658
654
  body = `<path d="M4 1L7 4 4 7 1 4Z" fill="${preset === "solidDmnd" ? fg : "none"}" stroke="${fg}" stroke-width="0.6"/>`;
659
655
  } else {
660
- body = dots(0.5);
656
+ body = screen(0.5);
661
657
  }
662
658
  const defs = `<defs><pattern id="${id}" patternUnits="userSpaceOnUse" width="${W}" height="${H}"><rect width="${W}" height="${H}" fill="${bg}"/>${body}</pattern></defs>`;
663
659
  return { defs, fillAttr: `url(#${id})` };
@@ -2516,6 +2512,9 @@ var DISPLAY_UNIT_LABEL = {
2516
2512
  billions: "Billions",
2517
2513
  trillions: "Trillions"
2518
2514
  };
2515
+ var DEFAULT_AXIS_COLOR = "#000000";
2516
+ var DEFAULT_GRID_COLOR = "#D9D9D9";
2517
+ var AXIS_TICK_LEN = 5;
2519
2518
  var axisTickAttrs = (style) => {
2520
2519
  const sz = style?.sizePt ?? 10;
2521
2520
  const fill = style?.color ?? "#6B7280";
@@ -2532,10 +2531,15 @@ var renderValueAxis = (f, axis) => {
2532
2531
  })() : niceTicks(axis.min, axis.max);
2533
2532
  const out = [];
2534
2533
  const range = axis.max - axis.min || 1;
2535
- const showGrid = axis.majorGridlines ?? true;
2536
- const gridStroke = axis.majorGridlineColor ?? "#E5E7EB";
2534
+ const fmtTick = (t) => axis.percent ? `${Math.round(t * 100)}%` : formatAxisLabel(
2535
+ axis.displayUnits ? t / DISPLAY_UNIT_DIVISOR[axis.displayUnits] : t,
2536
+ axis.numberFormat
2537
+ );
2538
+ const showGrid = axis.majorGridlines ?? false;
2539
+ const gridStroke = axis.majorGridlineColor ?? DEFAULT_GRID_COLOR;
2540
+ const axisColor = axis.lineColor ?? DEFAULT_AXIS_COLOR;
2537
2541
  const tickMark = axis.majorTickMark ?? "out";
2538
- const tickLen = 3;
2542
+ const tickLen = AXIS_TICK_LEN;
2539
2543
  for (const t of ticks) {
2540
2544
  if (axis.orientation === "vertical") {
2541
2545
  const yp = f.plotY + f.plotH - (t - axis.min) / range * f.plotH;
@@ -2548,19 +2552,14 @@ var renderValueAxis = (f, axis) => {
2548
2552
  const tx1 = tickMark === "in" ? f.plotX : f.plotX - tickLen;
2549
2553
  const tx2 = tickMark === "out" ? f.plotX : tickMark === "cross" ? f.plotX + tickLen : f.plotX + tickLen;
2550
2554
  out.push(
2551
- `<line x1="${px(tx1)}" y1="${px(yp)}" x2="${px(tx2)}" y2="${px(yp)}" stroke="#9CA3AF" stroke-width="0.5"/>`
2555
+ `<line x1="${px(tx1)}" y1="${px(yp)}" x2="${px(tx2)}" y2="${px(yp)}" stroke="${axisColor}" stroke-width="1"/>`
2552
2556
  );
2553
2557
  }
2554
2558
  const labelX = f.plotX - 4;
2555
2559
  const rot = axis.labelRotationDeg ?? 0;
2556
2560
  const transform = rot ? ` transform="rotate(${rot} ${px(labelX)} ${px(yp)})"` : "";
2557
2561
  out.push(
2558
- `<text x="${px(labelX)}" y="${px(yp)}" text-anchor="end" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transform}>${escapeXml2(
2559
- formatAxisLabel(
2560
- axis.displayUnits ? t / DISPLAY_UNIT_DIVISOR[axis.displayUnits] : t,
2561
- axis.numberFormat
2562
- )
2563
- )}</text>`
2562
+ `<text x="${px(labelX)}" y="${px(yp)}" text-anchor="end" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transform}>${escapeXml2(fmtTick(t))}</text>`
2564
2563
  );
2565
2564
  } else {
2566
2565
  const xp = f.plotX + (t - axis.min) / range * f.plotW;
@@ -2574,19 +2573,14 @@ var renderValueAxis = (f, axis) => {
2574
2573
  const ty1 = tickMark === "in" ? baseY : baseY + tickLen;
2575
2574
  const ty2 = tickMark === "out" ? baseY : tickMark === "cross" ? baseY - tickLen : baseY - tickLen;
2576
2575
  out.push(
2577
- `<line x1="${px(xp)}" y1="${px(ty1)}" x2="${px(xp)}" y2="${px(ty2)}" stroke="#9CA3AF" stroke-width="0.5"/>`
2576
+ `<line x1="${px(xp)}" y1="${px(ty1)}" x2="${px(xp)}" y2="${px(ty2)}" stroke="${axisColor}" stroke-width="1"/>`
2578
2577
  );
2579
2578
  }
2580
2579
  const horizLabelY = f.plotY + f.plotH + 12;
2581
2580
  const rotH = axis.labelRotationDeg ?? 0;
2582
2581
  const transformH = rotH ? ` transform="rotate(${rotH} ${px(xp)} ${px(horizLabelY)})"` : "";
2583
2582
  out.push(
2584
- `<text x="${px(xp)}" y="${px(horizLabelY)}" text-anchor="middle" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transformH}>${escapeXml2(
2585
- formatAxisLabel(
2586
- axis.displayUnits ? t / DISPLAY_UNIT_DIVISOR[axis.displayUnits] : t,
2587
- axis.numberFormat
2588
- )
2589
- )}</text>`
2583
+ `<text x="${px(xp)}" y="${px(horizLabelY)}" text-anchor="middle" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transformH}>${escapeXml2(fmtTick(t))}</text>`
2590
2584
  );
2591
2585
  }
2592
2586
  }
@@ -2604,16 +2598,16 @@ var renderValueAxis = (f, axis) => {
2604
2598
  );
2605
2599
  }
2606
2600
  }
2607
- if (axis.lineColor !== void 0) {
2608
- if (axis.orientation === "vertical") {
2609
- out.push(
2610
- `<line x1="${px(f.plotX)}" y1="${px(f.plotY)}" x2="${px(f.plotX)}" y2="${px(f.plotY + f.plotH)}" stroke="${axis.lineColor}" stroke-width="0.75"/>`
2611
- );
2612
- } else {
2613
- out.push(
2614
- `<line x1="${px(f.plotX)}" y1="${px(f.plotY + f.plotH)}" x2="${px(f.plotX + f.plotW)}" y2="${px(f.plotY + f.plotH)}" stroke="${axis.lineColor}" stroke-width="0.75"/>`
2615
- );
2616
- }
2601
+ if (axis.orientation === "vertical") {
2602
+ out.push(
2603
+ `<line x1="${px(f.plotX)}" y1="${px(f.plotY)}" x2="${px(f.plotX)}" y2="${px(f.plotY + f.plotH)}" stroke="${axisColor}" stroke-width="1"/>`
2604
+ );
2605
+ } else {
2606
+ const zeroY = f.plotY + f.plotH - (0 - axis.min) / range * f.plotH;
2607
+ const spineY = axis.min <= 0 && axis.max >= 0 ? zeroY : f.plotY + f.plotH;
2608
+ out.push(
2609
+ `<line x1="${px(f.plotX)}" y1="${px(spineY)}" x2="${px(f.plotX + f.plotW)}" y2="${px(spineY)}" stroke="${axisColor}" stroke-width="1"/>`
2610
+ );
2617
2611
  }
2618
2612
  return out.join("");
2619
2613
  };
@@ -2642,7 +2636,7 @@ var renderCategoryAxis = (f, orientation, cats, pointCount2, skip = 1, labelStyl
2642
2636
  const truncLen = labelRotationDeg && Math.abs(labelRotationDeg) >= 30 ? 28 : 14;
2643
2637
  for (let i = 0; i < pointCount2; i++) {
2644
2638
  if (skip > 1 && i % skip !== 0) continue;
2645
- const cy = f.plotY + (i + 0.5) * step;
2639
+ const cy = f.plotY + (pointCount2 - 1 - i + 0.5) * step;
2646
2640
  const lx = f.plotX - 4;
2647
2641
  const truncated = labels[i] !== void 0 && labels[i].length > truncLen ? `${labels[i].slice(0, truncLen - 2)}\u2026` : labels[i] ?? "";
2648
2642
  const transform = labelRotationDeg && labelRotationDeg !== 0 ? ` transform="rotate(${labelRotationDeg} ${px(lx)} ${px(cy)})"` : "";
@@ -2651,27 +2645,63 @@ var renderCategoryAxis = (f, orientation, cats, pointCount2, skip = 1, labelStyl
2651
2645
  );
2652
2646
  }
2653
2647
  }
2654
- if (lineColor !== void 0) {
2655
- if (orientation === "horizontal") {
2648
+ const axisColor = lineColor ?? DEFAULT_AXIS_COLOR;
2649
+ if (orientation === "horizontal") {
2650
+ const baseY = f.plotY + f.plotH;
2651
+ out.push(
2652
+ `<line x1="${px(f.plotX)}" y1="${px(baseY)}" x2="${px(f.plotX + f.plotW)}" y2="${px(baseY)}" stroke="${axisColor}" stroke-width="1"/>`
2653
+ );
2654
+ const stepB = pointCount2 > 0 ? f.plotW / pointCount2 : 0;
2655
+ for (let i = 0; i <= pointCount2; i++) {
2656
+ const bx = f.plotX + i * stepB;
2656
2657
  out.push(
2657
- `<line x1="${px(f.plotX)}" y1="${px(f.plotY + f.plotH)}" x2="${px(f.plotX + f.plotW)}" y2="${px(f.plotY + f.plotH)}" stroke="${lineColor}" stroke-width="0.75"/>`
2658
+ `<line x1="${px(bx)}" y1="${px(baseY)}" x2="${px(bx)}" y2="${px(baseY + AXIS_TICK_LEN)}" stroke="${axisColor}" stroke-width="1"/>`
2658
2659
  );
2659
- } else {
2660
+ }
2661
+ } else {
2662
+ out.push(
2663
+ `<line x1="${px(f.plotX)}" y1="${px(f.plotY)}" x2="${px(f.plotX)}" y2="${px(f.plotY + f.plotH)}" stroke="${axisColor}" stroke-width="1"/>`
2664
+ );
2665
+ const stepB = pointCount2 > 0 ? f.plotH / pointCount2 : 0;
2666
+ for (let i = 0; i <= pointCount2; i++) {
2667
+ const by = f.plotY + i * stepB;
2660
2668
  out.push(
2661
- `<line x1="${px(f.plotX)}" y1="${px(f.plotY)}" x2="${px(f.plotX)}" y2="${px(f.plotY + f.plotH)}" stroke="${lineColor}" stroke-width="0.75"/>`
2669
+ `<line x1="${px(f.plotX - AXIS_TICK_LEN)}" y1="${px(by)}" x2="${px(f.plotX)}" y2="${px(by)}" stroke="${axisColor}" stroke-width="1"/>`
2662
2670
  );
2663
2671
  }
2664
2672
  }
2665
2673
  return out.join("");
2666
2674
  };
2667
2675
  var seriesMinMax = (spec) => {
2676
+ const isStacked = spec.grouping === "stacked" || spec.grouping === "percentStacked";
2677
+ const isPercent = spec.grouping === "percentStacked";
2668
2678
  let min = Infinity;
2669
2679
  let max = -Infinity;
2670
- for (const s of spec.series) {
2671
- for (const v of s.values) {
2672
- if (v !== null && Number.isFinite(v)) {
2673
- if (v < min) min = v;
2674
- if (v > max) max = v;
2680
+ if (isPercent) {
2681
+ min = 0;
2682
+ max = 1;
2683
+ } else if (isStacked) {
2684
+ const N = spec.series.reduce((n, s) => Math.max(n, s.values.length), 0);
2685
+ for (let c = 0; c < N; c++) {
2686
+ let pos = 0;
2687
+ let neg = 0;
2688
+ for (const s of spec.series) {
2689
+ const v = s.values[c];
2690
+ if (v !== null && v !== void 0 && Number.isFinite(v)) {
2691
+ if (v >= 0) pos += v;
2692
+ else neg += v;
2693
+ }
2694
+ }
2695
+ if (pos > max) max = pos;
2696
+ if (neg < min) min = neg;
2697
+ }
2698
+ } else {
2699
+ for (const s of spec.series) {
2700
+ for (const v of s.values) {
2701
+ if (v !== null && Number.isFinite(v)) {
2702
+ if (v < min) min = v;
2703
+ if (v > max) max = v;
2704
+ }
2675
2705
  }
2676
2706
  }
2677
2707
  }
@@ -2679,6 +2709,9 @@ var seriesMinMax = (spec) => {
2679
2709
  if (!Number.isFinite(max)) max = 1;
2680
2710
  if (max === min) max = min + 1;
2681
2711
  if (min > 0) min = 0;
2712
+ if (isPercent) {
2713
+ return { min: 0, max: 1, step: 0.2 };
2714
+ }
2682
2715
  const step = niceStep(max - min);
2683
2716
  max = (Math.floor(max / step) + 1) * step;
2684
2717
  if (spec.valueAxis?.min !== void 0) min = spec.valueAxis.min;
@@ -2704,9 +2737,9 @@ var renderChartLegend = (f, names, colors, position = "b", textStyle, markerSymb
2704
2737
  const swatch = (i, swatchX, swatchY) => {
2705
2738
  const color = colors[i % colors.length];
2706
2739
  const sym = markerSymbols?.[i];
2707
- if (sym && sym !== "none" && sym !== "auto") {
2740
+ if (markerSymbols !== void 0 && sym !== "none") {
2708
2741
  const r = 4.5;
2709
- return seriesMarker(sym, swatchX + r, swatchY + r, r, color);
2742
+ return seriesMarker(autoMarkerSymbol(sym, i), swatchX + r, swatchY + r, r, color);
2710
2743
  }
2711
2744
  return `<rect x="${px(swatchX)}" y="${px(swatchY)}" width="9" height="9" fill="${color}"/>`;
2712
2745
  };
@@ -2769,24 +2802,7 @@ var renderColumnChart = (f, spec, colors) => {
2769
2802
  const grouping = spec.grouping ?? "clustered";
2770
2803
  const isStacked = grouping === "stacked" || grouping === "percentStacked";
2771
2804
  const isPercent = grouping === "percentStacked";
2772
- let { min, max } = seriesMinMax(spec);
2773
- if (isStacked) {
2774
- let sumMin = Infinity;
2775
- let sumMax = -Infinity;
2776
- for (let c = 0; c < N; c++) {
2777
- let pos = 0;
2778
- let neg = 0;
2779
- for (const s of spec.series) {
2780
- const v = s.values[c] ?? 0;
2781
- if (v >= 0) pos += v;
2782
- else neg += v;
2783
- }
2784
- if (neg < sumMin) sumMin = neg;
2785
- if (pos > sumMax) sumMax = pos;
2786
- }
2787
- min = isPercent ? 0 : Math.min(0, sumMin);
2788
- max = isPercent ? 1 : Math.max(1, sumMax);
2789
- }
2805
+ const { min, max } = seriesMinMax(spec);
2790
2806
  const range = max - min || 1;
2791
2807
  const groupW = f.plotW / N;
2792
2808
  const gapPctC = (spec.gapWidthPct ?? 150) / 100;
@@ -2819,7 +2835,7 @@ var renderColumnChart = (f, spec, colors) => {
2819
2835
  const y1 = f.plotY + f.plotH - (Math.min(stackedTop, stackedBase) - min) / range * f.plotH;
2820
2836
  const h = Math.abs(y1 - y0);
2821
2837
  out.push(
2822
- `<rect x="${px(x0)}" y="${px(y0)}" width="${px(barW)}" height="${px(h)}" fill="${colors[s % colors.length]}"/>`
2838
+ `<rect x="${px(x0)}" y="${px(y0)}" width="${px(barW)}" height="${px(h)}" fill="${spec.series[s]?.color ?? colors[s % colors.length]}"/>`
2823
2839
  );
2824
2840
  if (showLabelFor(s) && Math.abs(v) > 0) {
2825
2841
  const labelY = (y0 + y1) / 2 + 3;
@@ -2993,6 +3009,17 @@ var smoothPath = (pts) => {
2993
3009
  }
2994
3010
  return parts.join(" ");
2995
3011
  };
3012
+ var AUTO_MARKER_SYMBOLS = [
3013
+ "diamond",
3014
+ "square",
3015
+ "triangle",
3016
+ "x",
3017
+ "star",
3018
+ "dot",
3019
+ "plus",
3020
+ "dash"
3021
+ ];
3022
+ var autoMarkerSymbol = (symbol, seriesIdx) => symbol !== void 0 && symbol !== "auto" ? symbol : AUTO_MARKER_SYMBOLS[seriesIdx % AUTO_MARKER_SYMBOLS.length];
2996
3023
  var seriesMarker = (symbol, cx, cy, r, color) => {
2997
3024
  switch (symbol) {
2998
3025
  case "square":
@@ -3054,24 +3081,7 @@ var renderBarChart = (f, spec, colors) => {
3054
3081
  const grouping = spec.grouping ?? "clustered";
3055
3082
  const isStacked = grouping === "stacked" || grouping === "percentStacked";
3056
3083
  const isPercent = grouping === "percentStacked";
3057
- let { min, max } = seriesMinMax(spec);
3058
- if (isStacked) {
3059
- let sumMin = Infinity;
3060
- let sumMax = -Infinity;
3061
- for (let c = 0; c < N; c++) {
3062
- let pos = 0;
3063
- let neg = 0;
3064
- for (const s of spec.series) {
3065
- const v = s.values[c] ?? 0;
3066
- if (v >= 0) pos += v;
3067
- else neg += v;
3068
- }
3069
- if (neg < sumMin) sumMin = neg;
3070
- if (pos > sumMax) sumMax = pos;
3071
- }
3072
- min = isPercent ? 0 : Math.min(0, sumMin);
3073
- max = isPercent ? 1 : Math.max(1, sumMax);
3074
- }
3084
+ const { min, max } = seriesMinMax(spec);
3075
3085
  const range = max - min || 1;
3076
3086
  const groupH = f.plotH / N;
3077
3087
  const gapPctB = (spec.gapWidthPct ?? 150) / 100;
@@ -3096,12 +3106,12 @@ var renderBarChart = (f, spec, colors) => {
3096
3106
  }
3097
3107
  const base = v >= 0 ? posAcc : negAcc;
3098
3108
  const stackedTop = base + v;
3099
- const y0 = f.plotY + c * groupH + (groupH - barH) / 2;
3109
+ const y0 = f.plotY + (N - 1 - c) * groupH + (groupH - barH) / 2;
3100
3110
  const x0 = f.plotX + (Math.min(base, stackedTop) - min) / range * f.plotW;
3101
3111
  const x1 = f.plotX + (Math.max(base, stackedTop) - min) / range * f.plotW;
3102
3112
  const w = Math.abs(x1 - x0);
3103
3113
  out.push(
3104
- `<rect x="${px(x0)}" y="${px(y0)}" width="${px(w)}" height="${px(barH)}" fill="${colors[s % colors.length]}"/>`
3114
+ `<rect x="${px(x0)}" y="${px(y0)}" width="${px(w)}" height="${px(barH)}" fill="${spec.series[s]?.color ?? colors[s % colors.length]}"/>`
3105
3115
  );
3106
3116
  if (showLabelForBar(s) && Math.abs(v) > 0) {
3107
3117
  const labelX = (x0 + x1) / 2;
@@ -3115,11 +3125,11 @@ var renderBarChart = (f, spec, colors) => {
3115
3125
  }
3116
3126
  } else {
3117
3127
  const clusterH = barH * clusterUnitsB;
3118
- const clusterStartY = f.plotY + c * groupH + (groupH - clusterH) / 2;
3128
+ const clusterStartY = f.plotY + (N - 1 - c) * groupH + (groupH - clusterH) / 2;
3119
3129
  const strideB = barH * (1 - overlapPctB);
3120
3130
  for (let s = 0; s < spec.series.length; s++) {
3121
3131
  const v = spec.series[s]?.values[c] ?? 0;
3122
- const y0 = clusterStartY + s * strideB;
3132
+ const y0 = clusterStartY + (Sb - 1 - s) * strideB;
3123
3133
  const tip = f.plotX + (v - min) / range * f.plotW;
3124
3134
  const x0 = Math.min(tip, baseX);
3125
3135
  const w = Math.abs(tip - baseX);
@@ -3167,24 +3177,7 @@ var renderLineChart = (f, spec, colors, fill) => {
3167
3177
  const grouping = spec.grouping ?? "clustered";
3168
3178
  const isStacked = grouping === "stacked" || grouping === "percentStacked";
3169
3179
  const isPercent = grouping === "percentStacked";
3170
- let { min, max } = seriesMinMax(spec);
3171
- if (isStacked) {
3172
- let sumMin = Infinity;
3173
- let sumMax = -Infinity;
3174
- for (let c = 0; c < N; c++) {
3175
- let pos = 0;
3176
- let neg = 0;
3177
- for (const s of spec.series) {
3178
- const v = s.values[c] ?? 0;
3179
- if (v >= 0) pos += v;
3180
- else neg += v;
3181
- }
3182
- if (neg < sumMin) sumMin = neg;
3183
- if (pos > sumMax) sumMax = pos;
3184
- }
3185
- min = isPercent ? 0 : Math.min(0, sumMin);
3186
- max = isPercent ? 1 : Math.max(1, sumMax);
3187
- }
3180
+ const { min, max } = seriesMinMax(spec);
3188
3181
  const range = max - min || 1;
3189
3182
  const step = N > 1 ? f.plotW / (N - 1) : 0;
3190
3183
  const baseY = f.plotY + f.plotH - (0 - min) / range * f.plotH;
@@ -3245,7 +3238,7 @@ var renderLineChart = (f, spec, colors, fill) => {
3245
3238
  })();
3246
3239
  if (fill) {
3247
3240
  const back = basePts.slice().reverse().map(([xp, yp]) => `L${px(xp)},${px(yp)}`).join(" ");
3248
- out.push(`<path d="${dPath} ${back} Z" fill="${color}" fill-opacity="0.55" stroke="none"/>`);
3241
+ out.push(`<path d="${dPath} ${back} Z" fill="${color}" stroke="none"/>`);
3249
3242
  }
3250
3243
  const lineWPx = series.lineWidthEmu ? Math.max(0.3, series.lineWidthEmu / EMU_PER_PX) : 1.5;
3251
3244
  const dashAttr = series.lineDash ? (() => {
@@ -3259,8 +3252,9 @@ var renderLineChart = (f, spec, colors, fill) => {
3259
3252
  `<path d="${dPath}" fill="none" stroke="${color}" stroke-width="${lineWPx.toFixed(2)}" stroke-linejoin="round" stroke-linecap="round"${dashAttr}/>`
3260
3253
  );
3261
3254
  if (!isStacked) {
3262
- const symbol = series.markerSymbol ?? "auto";
3263
- if (symbol !== "none") {
3255
+ const explicitSymbol = series.markerSymbol !== void 0 && series.markerSymbol !== "auto" && series.markerSymbol !== "none";
3256
+ if (series.markerSymbol !== "none" && !fill && (spec.lineMarkers === true || explicitSymbol)) {
3257
+ const symbol = autoMarkerSymbol(series.markerSymbol, s);
3264
3258
  const size = series.markerSizePt ?? 5;
3265
3259
  const r = Math.max(1, size * 0.5);
3266
3260
  for (const [xp, yp] of pts) {
@@ -3502,7 +3496,7 @@ var renderScatterChart = (f, spec, colors) => {
3502
3496
  const drawMarker = sym === "none" ? false : showMarker || sym !== void 0 && sym !== "auto";
3503
3497
  if (drawMarker) {
3504
3498
  const r = Math.max(1.5, (series.markerSizePt ?? 5) * 0.5);
3505
- const glyph = sym && sym !== "auto" && sym !== "none" ? sym : "circle";
3499
+ const glyph = autoMarkerSymbol(sym, s);
3506
3500
  for (const [xp, yp] of proj) out.push(seriesMarker(glyph, xp, yp, r, color));
3507
3501
  }
3508
3502
  }
@@ -3625,7 +3619,7 @@ var renderRadarChart = (f, spec, colors) => {
3625
3619
  );
3626
3620
  if (showMarker) {
3627
3621
  const r = Math.max(1.5, (series.markerSizePt ?? 5) * 0.5);
3628
- const glyph = series.markerSymbol && series.markerSymbol !== "auto" && series.markerSymbol !== "none" ? series.markerSymbol : "circle";
3622
+ const glyph = autoMarkerSymbol(series.markerSymbol, s);
3629
3623
  for (const [xp, yp] of proj) out.push(seriesMarker(glyph, xp, yp, r, color));
3630
3624
  }
3631
3625
  }
@@ -3661,7 +3655,9 @@ var renderChart = (shape, x, y, w, h, transform, theme) => {
3661
3655
  const hiddenSet = new Set(spec.legend?.hiddenIndices ?? []);
3662
3656
  const seriesNamesForLegend = allNamesForLegend.filter((_, i) => !hiddenSet.has(i));
3663
3657
  const seriesColorsForLegend = allColorsForLegend.filter((_, i) => !hiddenSet.has(i));
3664
- const markerSymbolsForLegend = spec.kind === "line" || spec.kind === "area" ? spec.series.map((s) => s.markerSymbol).filter((_, i) => !hiddenSet.has(i)) : void 0;
3658
+ const explicitMarker = (sym) => sym !== void 0 && sym !== "auto" && sym !== "none";
3659
+ const showsMarkers = spec.kind === "line" && (spec.lineMarkers === true || spec.series.some((s) => explicitMarker(s.markerSymbol)));
3660
+ const markerSymbolsForLegend = showsMarkers ? spec.series.map((s) => s.markerSymbol).filter((_, i) => !hiddenSet.has(i)) : void 0;
3665
3661
  let finiteCount = 0;
3666
3662
  for (const s of spec.series) {
3667
3663
  for (const v of s.values) if (v !== null && Number.isFinite(v)) finiteCount++;
@@ -3684,7 +3680,8 @@ var renderChart = (shape, x, y, w, h, transform, theme) => {
3684
3680
  ...spec.valueAxisLabelRotationDeg !== void 0 ? { labelRotationDeg: spec.valueAxisLabelRotationDeg } : {},
3685
3681
  ...spec.valueAxis?.displayUnits !== void 0 ? { displayUnits: spec.valueAxis.displayUnits } : {}
3686
3682
  };
3687
- const valueAxis = spec.kind === "bar" ? { orientation: "horizontal", min, max, ...axisExtras } : { orientation: "vertical", min, max, ...axisExtras };
3683
+ const isPercentStacked = spec.grouping === "percentStacked";
3684
+ const valueAxis = spec.kind === "bar" ? { orientation: "horizontal", min, max, percent: isPercentStacked, ...axisExtras } : { orientation: "vertical", min, max, percent: isPercentStacked, ...axisExtras };
3688
3685
  if (!spec.valueAxisHidden) axes = renderValueAxis(f, valueAxis);
3689
3686
  const labelsHidden = spec.categoryAxisHidden || spec.categoryAxisTickLabelPos === "none";
3690
3687
  if (N > 0 && !labelsHidden) {
@@ -3749,8 +3746,11 @@ var renderChart = (shape, x, y, w, h, transform, theme) => {
3749
3746
  `<g${transform}>`,
3750
3747
  // Chart-area backdrop honors <c:chartSpace><c:spPr><a:solidFill> /
3751
3748
  // <a:ln>. plot-area gets its own tinted rect + border when
3752
- // <c:plotArea><c:spPr> authors them.
3753
- `<rect x="${px(f.x)}" y="${px(f.y)}" width="${px(f.w)}" height="${px(f.h)}" fill="${spec.chartAreaFill ?? "#FFFFFF"}" stroke="${spec.chartAreaStrokeColor ?? "#E5E7EB"}" stroke-width="0.6"${spec.roundedCorners ? ' rx="6" ry="6"' : ""}/>`,
3749
+ // <c:plotArea><c:spPr> authors them. PowerPoint draws no chart-area
3750
+ // border unless the chartSpace authors an <a:ln>, so the default is
3751
+ // `none` — an invented light-gray frame is the most visible single
3752
+ // divergence from PowerPoint's actual rendering.
3753
+ `<rect x="${px(f.x)}" y="${px(f.y)}" width="${px(f.w)}" height="${px(f.h)}" fill="${spec.chartAreaFill ?? "#FFFFFF"}" stroke="${spec.chartAreaStrokeColor ?? "none"}" stroke-width="0.6"${spec.roundedCorners ? ' rx="6" ry="6"' : ""}/>`,
3754
3754
  spec.plotAreaFill || spec.plotAreaStrokeColor ? `<rect x="${px(f.plotX)}" y="${px(f.plotY)}" width="${px(f.plotW)}" height="${px(f.plotH)}" fill="${spec.plotAreaFill ?? "none"}" stroke="${spec.plotAreaStrokeColor ?? "none"}" stroke-width="0.6"/>` : "",
3755
3755
  renderChartTitle(f, spec.title ?? "", spec.titleStyle),
3756
3756
  axes,