@office-kit/pptx-preview 0.8.1 → 0.9.1

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
@@ -1,2 +1,2 @@
1
- import { a as MONO, c as TIMES, d as substituteFamily, l as defaultMeasurer, n as renderSlideSvg, o as SANS, r as ARIAL, s as SERIF, t as auditTextLayout } from "./src-BMb2-vud.js";
1
+ import { a as MONO, c as TIMES, d as substituteFamily, l as defaultMeasurer, n as renderSlideSvg, o as SANS, r as ARIAL, s as SERIF, t as auditTextLayout } from "./src-C_bsFwWV.js";
2
2
  export { ARIAL, MONO, SANS, SERIF, TIMES, auditTextLayout, defaultMeasurer, renderSlideSvg as renderSlideToSvg, substituteFamily };
package/dist/node.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as MONO, c as TIMES, d as substituteFamily, i as AVG_GLYPH_W_RATIO, l as defaultMeasurer, n as renderSlideSvg, o as SANS, r as ARIAL, s as SERIF, t as auditTextLayout, u as isCjk } from "./src-BMb2-vud.js";
1
+ import { a as MONO, c as TIMES, d as substituteFamily, i as AVG_GLYPH_W_RATIO, l as defaultMeasurer, n as renderSlideSvg, o as SANS, r as ARIAL, s as SERIF, t as auditTextLayout, u as isCjk } from "./src-C_bsFwWV.js";
2
2
  import { getSlideSize } from "@office-kit/pptx";
3
3
  import { Resvg } from "@resvg/resvg-js";
4
4
  import * as fontkit from "fontkit";
@@ -32,6 +32,29 @@ const FALLBACK_DESCENT = .22;
32
32
  const FALLBACK_LINEGAP = .08;
33
33
  const BASELINE_LEADING_DROP = .036;
34
34
  const GRID_NUDGE_X = -.75;
35
+ const EAST_ASIAN_CHAR = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹏＀-⦆¢-₩]/;
36
+ const CLOSING_PUNCT = /^[、。,.・)」』】〉》”’!?:;ー〜…,.!?:;)\]}]+$/;
37
+ const OPENING_PUNCT = /^[(「『【〈《“‘([{]+$/;
38
+ const splitEastAsianBreakables = (word) => {
39
+ if (!EAST_ASIAN_CHAR.test(word)) return [word];
40
+ const parts = [];
41
+ let latin = "";
42
+ for (const ch of word) if (EAST_ASIAN_CHAR.test(ch)) {
43
+ if (latin !== "") {
44
+ parts.push(latin);
45
+ latin = "";
46
+ }
47
+ parts.push(ch);
48
+ } else latin += ch;
49
+ if (latin !== "") parts.push(latin);
50
+ const glued = [];
51
+ for (const part of parts) {
52
+ const prev = glued.at(-1);
53
+ if (prev !== void 0 && (CLOSING_PUNCT.test(part) || OPENING_PUNCT.test(prev))) glued[glued.length - 1] = prev + part;
54
+ else glued.push(part);
55
+ }
56
+ return glued;
57
+ };
35
58
  const specOf = (piece) => ({
36
59
  family: piece.family,
37
60
  sizePx: piece.sizePx,
@@ -113,23 +136,25 @@ const layoutCore = (input, measure) => {
113
136
  });
114
137
  continue;
115
138
  }
116
- for (const seg of piece.text.match(/\s+|\S+/g) ?? []) {
117
- const isSpace = /^\s+$/.test(seg);
118
- const w = mWidth(seg, specOf(piece));
119
- if (input.wrap && !isSpace && w > avail - bulletLead && [...seg].length > 1) for (const ch of seg) tokens.push({
120
- text: ch,
121
- piece,
122
- isSpace: false,
123
- isBreak: false,
124
- width: mWidth(ch, specOf(piece))
125
- });
126
- else tokens.push({
127
- text: seg,
128
- piece,
129
- isSpace,
130
- isBreak: false,
131
- width: w
132
- });
139
+ for (const word of piece.text.match(/\s+|\S+/g) ?? []) {
140
+ const isSpace = /^\s+$/.test(word);
141
+ for (const seg of isSpace ? [word] : splitEastAsianBreakables(word)) {
142
+ const w = mWidth(seg, specOf(piece));
143
+ if (input.wrap && !isSpace && w > avail - bulletLead && [...seg].length > 1) for (const ch of seg) tokens.push({
144
+ text: ch,
145
+ piece,
146
+ isSpace: false,
147
+ isBreak: false,
148
+ width: mWidth(ch, specOf(piece))
149
+ });
150
+ else tokens.push({
151
+ text: seg,
152
+ piece,
153
+ isSpace,
154
+ isBreak: false,
155
+ width: w
156
+ });
157
+ }
133
158
  }
134
159
  }
135
160
  const wrapped = wrapTokens(tokens, input.wrap, wrapRight - firstLeft - bulletLead, avail);
@@ -2628,16 +2653,20 @@ const renderValueAxis = (f, axis) => {
2628
2653
  const tickLen = AXIS_TICK_LEN;
2629
2654
  for (const t of ticks) if (axis.orientation === "vertical") {
2630
2655
  const yp = f.plotY + f.plotH - (t - axis.min) / range * f.plotH;
2656
+ const onRight = axis.side === "right";
2657
+ const edgeX = onRight ? f.plotX + f.plotW : f.plotX;
2631
2658
  if (showGrid) out.push(`<line x1="${px(f.plotX)}" y1="${px(yp)}" x2="${px(f.plotX + f.plotW)}" y2="${px(yp)}" stroke="${gridStroke}" stroke-width="0.5"/>`);
2632
2659
  if (tickMark !== "none") {
2633
- const tx1 = tickMark === "in" ? f.plotX : f.plotX - tickLen;
2634
- const tx2 = tickMark === "out" ? f.plotX : tickMark === "cross" ? f.plotX + tickLen : f.plotX + tickLen;
2660
+ const outward = onRight ? tickLen : -5;
2661
+ const inward = onRight ? -5 : tickLen;
2662
+ const tx1 = tickMark === "in" ? edgeX : edgeX + outward;
2663
+ const tx2 = tickMark === "out" ? edgeX : edgeX + inward;
2635
2664
  out.push(`<line x1="${px(tx1)}" y1="${px(yp)}" x2="${px(tx2)}" y2="${px(yp)}" stroke="${axisColor}" stroke-width="1"/>`);
2636
2665
  }
2637
- const labelX = f.plotX - 4;
2666
+ const labelX = onRight ? edgeX + 4 : edgeX - 4;
2638
2667
  const rot = axis.labelRotationDeg ?? 0;
2639
2668
  const transform = rot ? ` transform="rotate(${rot} ${px(labelX)} ${px(yp)})"` : "";
2640
- out.push(`<text x="${px(labelX)}" y="${px(yp)}" text-anchor="end" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transform}>${escapeXml(fmtTick(t))}</text>`);
2669
+ out.push(`<text x="${px(labelX)}" y="${px(yp)}" text-anchor="${onRight ? "start" : "end"}" dominant-baseline="middle" ${axisTickAttrs(axis.labelStyle)}${transform}>${escapeXml(fmtTick(t))}</text>`);
2641
2670
  } else {
2642
2671
  const xp = f.plotX + (t - axis.min) / range * f.plotW;
2643
2672
  if (showGrid) out.push(`<line x1="${px(xp)}" y1="${px(f.plotY)}" x2="${px(xp)}" y2="${px(f.plotY + f.plotH)}" stroke="${gridStroke}" stroke-width="0.5"/>`);
@@ -2768,6 +2797,12 @@ const renderChartTitle = (f, title, style) => {
2768
2797
  const fontStyleAttr = style?.italic ? " font-style=\"italic\"" : "";
2769
2798
  return `<text x="${px(f.x + f.w / 2)}" y="${px(f.titleY)}" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="${chartFontPx(sz)}" fill="${fill}" font-weight="${weight}"${fontStyleAttr}>${escapeXml(title)}</text>`;
2770
2799
  };
2800
+ const FULLWIDTH_CHAR_PATTERN = /[\u1100-\u115F\u2E80-\u303E\u3041-\u33FF\u3400-\u4DBF\u4E00-\u9FFF\uA000-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFF60\uFFE0-\uFFE6]/u;
2801
+ const approxLegendTextPx = (text, fontPx) => {
2802
+ let units = 0;
2803
+ for (const ch of text) units += FULLWIDTH_CHAR_PATTERN.test(ch) ? 1 : .55;
2804
+ return units * fontPx;
2805
+ };
2771
2806
  const renderChartLegend = (f, names, colors, position = "b", textStyle, markerSymbols) => {
2772
2807
  if (names.length === 0) return "";
2773
2808
  const sz = textStyle?.sizePt ?? 11;
@@ -2785,33 +2820,27 @@ const renderChartLegend = (f, names, colors, position = "b", textStyle, markerSy
2785
2820
  return `<rect x="${px(swatchX)}" y="${px(swatchY)}" width="9" height="9" fill="${color}"/>`;
2786
2821
  };
2787
2822
  const out = [];
2788
- if (position === "b") {
2789
- const itemPx = Math.min(140, f.w / names.length);
2790
- const totalW = itemPx * names.length;
2791
- const startX = f.x + (f.w - totalW) / 2;
2792
- for (let i = 0; i < names.length; i++) {
2793
- const swatchX = startX + i * itemPx + 4;
2794
- const swatchY = f.legendY - 4;
2795
- const labelX = swatchX + 14;
2796
- out.push(swatch(i, swatchX, swatchY), `<text x="${px(labelX)}" y="${px(f.legendY)}" dominant-baseline="middle" ${textAttrs}>${escapeXml(names[i] ?? `Series ${i + 1}`)}</text>`);
2797
- }
2798
- return out.join("");
2799
- }
2800
- if (position === "t") {
2801
- const itemPx = Math.min(140, f.w / names.length);
2802
- const totalW = itemPx * names.length;
2803
- const startX = f.x + (f.w - totalW) / 2;
2804
- const yTop = f.y + 4;
2823
+ if (position === "b" || position === "t") {
2824
+ const swatchGapPx = 14;
2825
+ const itemGapPx = 12;
2826
+ const labelWidths = names.map((name, i) => approxLegendTextPx(name ?? `Series ${i + 1}`, sz * PX_PER_PT));
2827
+ const naturalTotal = labelWidths.reduce((sum, w) => sum + swatchGapPx + w, 0) + itemGapPx * (names.length - 1);
2828
+ const scale = Math.min(1, f.w / Math.max(1, naturalTotal));
2829
+ const effAttrs = scale < 1 ? `font-family="sans-serif" font-size="${chartFontPx(sz * scale)}" fill="${fill}"${weight}${italic}` : textAttrs;
2830
+ const rowY = position === "b" ? f.legendY : f.y + 12;
2831
+ let cursor = f.x + Math.max(0, (f.w - naturalTotal * scale) / 2);
2805
2832
  for (let i = 0; i < names.length; i++) {
2806
- const cx = startX + i * itemPx;
2807
- out.push(swatch(i, cx + 4, yTop), `<text x="${px(cx + 18)}" y="${px(yTop + 8)}" dominant-baseline="middle" ${textAttrs}>${escapeXml(names[i] ?? `Series ${i + 1}`)}</text>`);
2833
+ out.push(swatch(i, cursor, rowY - 4), `<text x="${px(cursor + swatchGapPx * scale)}" y="${px(rowY)}" dominant-baseline="middle" ${effAttrs}>${escapeXml(names[i] ?? `Series ${i + 1}`)}</text>`);
2834
+ cursor += (swatchGapPx + (labelWidths[i] ?? 0) + itemGapPx) * scale;
2808
2835
  }
2809
2836
  return out.join("");
2810
2837
  }
2811
2838
  const lineH = 14;
2812
2839
  const totalH = names.length * lineH;
2813
2840
  const yStart = position === "tr" ? f.y + 12 : Math.max(f.y + 12, f.y + (f.h - totalH) / 2);
2814
- const xCol = position === "l" ? f.x + 6 : position === "tr" ? f.x + f.w - 100 : f.x + f.w - 100;
2841
+ const maxLabelPx = Math.max(0, ...names.map((name, i) => approxLegendTextPx(name ?? `Series ${i + 1}`, sz * PX_PER_PT)));
2842
+ const rightColW = Math.min(f.w * .45, 14 + maxLabelPx + 4);
2843
+ const xCol = position === "l" ? f.x + 6 : f.x + f.w - rightColW;
2815
2844
  for (let i = 0; i < names.length; i++) {
2816
2845
  const yp = yStart + i * lineH;
2817
2846
  out.push(swatch(i, xCol, yp - 4), `<text x="${px(xCol + 14)}" y="${px(yp + 4)}" dominant-baseline="middle" ${textAttrs}>${escapeXml(names[i] ?? `Series ${i + 1}`)}</text>`);
@@ -3614,7 +3643,72 @@ const renderChart = (shape, x, y, w, h, transform, theme) => {
3614
3643
  for (const s of spec.series) for (const v of s.values) if (v !== null && Number.isFinite(v)) finiteCount++;
3615
3644
  let plot = "";
3616
3645
  let axes = "";
3617
- if (isCartesian) {
3646
+ const isCombo = isCartesian && spec.kind !== "bar" && spec.series.some((s) => s.chartKind !== void 0 && s.chartKind !== spec.kind || s.secondaryAxis === true);
3647
+ if (isCombo) {
3648
+ const baked = spec.series.map((s, i) => ({
3649
+ ...s,
3650
+ color: s.color ?? colors[i % colors.length] ?? "#888"
3651
+ }));
3652
+ const primarySeries = baked.filter((s) => s.secondaryAxis !== true);
3653
+ const secondarySeries = baked.filter((s) => s.secondaryAxis === true);
3654
+ const primaryScale = seriesMinMax({
3655
+ ...spec,
3656
+ series: primarySeries
3657
+ });
3658
+ const { valueAxis: _primaryOnlyAxis, ...specWithoutAxis } = spec;
3659
+ const secondaryScale = secondarySeries.length > 0 ? seriesMinMax({
3660
+ ...specWithoutAxis,
3661
+ series: secondarySeries
3662
+ }) : null;
3663
+ const N = pointCount(spec);
3664
+ if (!spec.valueAxisHidden) axes = renderValueAxis(f, {
3665
+ orientation: "vertical",
3666
+ min: primaryScale.min,
3667
+ max: primaryScale.max,
3668
+ majorUnit: spec.valueAxis?.majorUnit ?? primaryScale.step,
3669
+ ...spec.valueAxis?.numberFormat !== void 0 ? { numberFormat: spec.valueAxis.numberFormat } : {},
3670
+ ...spec.valueAxisMajorGridlines !== void 0 ? { majorGridlines: spec.valueAxisMajorGridlines } : {},
3671
+ ...spec.valueAxisLabelStyle !== void 0 ? { labelStyle: spec.valueAxisLabelStyle } : {}
3672
+ });
3673
+ if (secondaryScale) axes += renderValueAxis(f, {
3674
+ orientation: "vertical",
3675
+ side: "right",
3676
+ min: secondaryScale.min,
3677
+ max: secondaryScale.max,
3678
+ majorUnit: secondaryScale.step,
3679
+ majorGridlines: false,
3680
+ ...spec.valueAxisLabelStyle !== void 0 ? { labelStyle: spec.valueAxisLabelStyle } : {}
3681
+ });
3682
+ if (N > 0 && !(spec.categoryAxisHidden || spec.categoryAxisTickLabelPos === "none")) axes += renderCategoryAxis(f, "horizontal", spec.categories, N, spec.categoryAxisTickLabelSkip ?? 1, spec.categoryAxisLabelStyle, spec.categoryAxisLabelRotationDeg, spec.categoryAxisLabelAlign, spec.categoryAxisLineColor);
3683
+ const groups = /* @__PURE__ */ new Map();
3684
+ for (const s of baked) {
3685
+ const kind = s.chartKind ?? spec.kind;
3686
+ const secondary = s.secondaryAxis === true;
3687
+ const key = `${kind}|${secondary ? "1" : "0"}`;
3688
+ const group = groups.get(key);
3689
+ if (group) group.series.push(s);
3690
+ else groups.set(key, {
3691
+ kind,
3692
+ secondary,
3693
+ series: [s]
3694
+ });
3695
+ }
3696
+ const paintOrder = (g) => (g.secondary ? 2 : 0) + (g.kind === "line" || g.kind === "area" ? 1 : 0);
3697
+ for (const group of [...groups.values()].sort((a, b) => paintOrder(a) - paintOrder(b))) {
3698
+ const scale = group.secondary && secondaryScale ? secondaryScale : primaryScale;
3699
+ const groupSpec = {
3700
+ ...spec,
3701
+ series: group.series,
3702
+ valueAxis: {
3703
+ min: scale.min,
3704
+ max: scale.max
3705
+ }
3706
+ };
3707
+ if (group.kind === "line" || group.kind === "area") plot += renderLineChart(f, groupSpec, colors, group.kind === "area");
3708
+ else plot += renderColumnChart(f, groupSpec, colors);
3709
+ }
3710
+ }
3711
+ if (!isCombo && isCartesian) {
3618
3712
  const { min, max, step } = seriesMinMax(spec);
3619
3713
  const N = pointCount(spec);
3620
3714
  const majorUnit = spec.valueAxis?.majorUnit ?? step;
@@ -3648,7 +3742,7 @@ const renderChart = (shape, x, y, w, h, transform, theme) => {
3648
3742
  const labelsHidden = spec.categoryAxisHidden || spec.categoryAxisTickLabelPos === "none";
3649
3743
  if (N > 0 && !labelsHidden) axes += renderCategoryAxis(f, spec.kind === "bar" ? "vertical" : "horizontal", spec.categories, N, spec.categoryAxisTickLabelSkip ?? 1, spec.categoryAxisLabelStyle, spec.categoryAxisLabelRotationDeg, spec.categoryAxisLabelAlign, spec.categoryAxisLineColor);
3650
3744
  }
3651
- switch (spec.kind) {
3745
+ if (!isCombo) switch (spec.kind) {
3652
3746
  case "column":
3653
3747
  case "bar":
3654
3748
  plot = spec.kind === "column" ? renderColumnChart(f, spec, colors) : renderBarChart(f, spec, colors);
@@ -4591,4 +4685,4 @@ const auditTextLayout = (pres, options = {}) => {
4591
4685
  //#endregion
4592
4686
  export { MONO as a, TIMES as c, substituteFamily as d, AVG_GLYPH_W_RATIO as i, defaultMeasurer as l, renderSlideSvg as n, SANS as o, ARIAL as r, SERIF as s, auditTextLayout as t, isCjk as u };
4593
4687
 
4594
- //# sourceMappingURL=src-BMb2-vud.js.map
4688
+ //# sourceMappingURL=src-C_bsFwWV.js.map