@topmindspace/tms-skills 0.1.3 → 0.1.4

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.
@@ -1859,7 +1859,7 @@ var STYLE_DATA_COLORS = {"business-blue":["1a73e8","3526bb","21a199","b77148","9
1859
1859
  var STYLE_DATA_COLORS_DARK = {"business-blue":["8ab4f8","7966e0","88e7e5","d4ad91","cbce83"],"apple-mono":["f5f5f7","2997ff","2dd4bf","ff9f0a","bf5af2"],"mckinsey":["7fb2e5","6f6bdb","8ce3d8","d1a694","cbc586"],"brand-red":["ff5a66","e09e66","e788cb","91d4c2","83b4ce"],"warm-sand":["d9b36a","c2d571","df9391","97b5ce","8e89c7"],"deep-teal":["4fc3b0","7aaecd","97d8a4","c99cac","c29c8f"],"graphite-dark":["4da3ff","6c66e0","88e7dc","d4a691","cec983"],"indigo-violet":["a5a4f0","a967e0","89c3e7","d3c792","aece83"],"spectrum":["8ba0f8","9066e0","88d6e7","d4ba91","bdce83"]};
1860
1860
  var GRID = {"columns":12,"gutter":0.16,"colW":0.8644,"x":[0.6,1.6244,2.6488,3.6732,4.6976,5.722,6.7464,7.7708,8.7952,9.8196,10.844,11.8684,12.733],"y":[2.35,3.27,4.19,5.11,6.03,6.9],"safe":{"top":0.4,"bottom":0.4,"left":0.6,"right":0.6}};
1861
1861
  var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"so-what 结论条、核心结论框"},{"id":"T9","name":"SO WHAT 标签","role":"caption","usage":"so-what 标签行"},{"id":"T10","name":"SO WHAT 正文/业务含义","role":"body","usage":"so-what 正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
1862
- var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级:① 优化内容(列表化/精炼/合并/拆页/换组合版式)→ ② 调整容器尺寸与行宽 → ③ 有限度缩小字号(见 fontShrink)→ ④ 仍装不下则拆页。禁止无限缩字号硬塞,也禁止把「精简」当第一手段砍掉决策必需信息。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
1862
+ var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级(禁静默截断):① 重构承载(列表/卡片/表/图/组合版式)→ ② 拆页/分章(议程→主张→证据卡→明细;01a/01b;信息须完整)→ ③ 换布局形态(V1–V4/多列/卡栅格)→ ④ 有限 fontShrink(阶梯内、模式 floor 以上、最多 4 档)→ ⑤ 禁止:静默截断、砍 so-what/证据/口径、为审美稀疏删决策必需信息。单页字数预算是预警不是删字许可证。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
1863
1863
  var REG = {"bar":{"html":"svg-bars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160}},"hbar":{"html":"svg-hbars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":100}},"stack":{"html":"svg-stack","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160},"nativeOpt":{"barGrouping":"stacked"}},"stackline":{"html":"svg-stackline","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":40},"nativeOpt":{"barGrouping":"percentStacked"}},"line":{"html":"svg-line","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"dualline":{"html":"svg-dualline","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"area":{"html":"svg-area","pptx":"native","nativeType":"area","dataTable":"notes","min":{"vbHeightMin":160}},"donut":{"html":"svg-donut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"multidonut":{"html":"svg-mdonut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"pie":{"html":"svg-pie","pptx":"native","nativeType":"pie","dataTable":"notes","min":{"pxWidthMin":140}},"radar":{"html":"svg-radar","pptx":"native","nativeType":"radar","dataTable":"notes","min":{"pxWidthMin":220}},"scatter":{"html":"svg-scatter","pptx":"native","nativeType":"scatter","dataTable":"notes","min":{"vbHeightMin":180}},"bubble":{"html":"svg-bubble","pptx":"native","nativeType":"bubble","dataTable":"notes","min":{"vbHeightMin":180}},"waterfall":{"html":"svg-wf","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":170},"nativeTrick":"stacked+hiddenBase+connector"},"gauge":{"html":"svg-gauge","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":180},"nativeTrick":"doughnut+firstSliceAng+hiddenRemainder"},"pareto":{"html":"svg-pareto","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":180},"nativeTrick":"multiType+bar+line"},"gantt":{"html":"svg-gantt","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":130},"path":{"forbidPresetShapes":true}},"vsbar":{"html":"svg-vsbar","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"progress":{"html":"svg-progress","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"sparkline":{"html":"svg-spark","pptx":"shape","dataTable":"off","min":{"vbHeightMin":20},"path":{"forbidPresetShapes":true}},"funnel":{"html":"svg-funnel","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"minSamplePoints":8,"forbidPresetShapes":true}},"treemap":{"html":"svg-treemap","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":170},"path":{"forbidPresetShapes":true}},"marimekko":{"html":"svg-marimekko","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"boxplot":{"html":"svg-boxplot","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"network":{"html":"svg-network","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"forbidPresetShapes":true}},"sankey":{"html":"svg-sankey","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"minSamplePoints":16,"trackBothEdges":true,"forbidPresetShapes":true}},"streamgraph":{"html":"svg-stream","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"minSamplePoints":24,"trackBothEdges":true,"forbidPresetShapes":true}},"slope":{"html":"svg-slope","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"dumbbell":{"html":"svg-dumbbell","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"lollipop":{"html":"svg-lollipop","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"dotplot":{"html":"svg-dotplot","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":120},"path":{"forbidPresetShapes":true}},"bulletchart":{"html":"svg-bullet","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"waffle":{"html":"svg-waffle","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"radialbar":{"html":"svg-radialbar","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":200},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"rose":{"html":"svg-rose","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":180},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"candlestick":{"html":"svg-candle","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}}};
1864
1864
  var DEEP = {"triggers":["高保真","1:1","一比一","精确还原","按图还原","正式交付","不能偏移","像素级","严格对照","逐页验收"],"complexCharts":["sankey","streamgraph","treemap","marimekko","boxplot","network","rose","radialbar","candlestick","funnel"],"checks":{"anchors":true,"containerOverflow":true,"renderCompare":true,"manifest":true},"renderCompare":{"engine":"soffice","fallback":"powerpoint-com","skipIfMissing":true,"dpi":96}};
1865
1865
  var IMG = {"layouts":["full","half","bleed","grid","compare","wall"],"multiLayouts":["grid","compare","wall"],"fitEnum":["cover","contain"],"fitDefault":"cover","formats":["png","jpeg","jpg","webp","svg"],"ratioDefault":{"full":"3:1","half":"4:3","bleed":"21:9","grid":"4:3","compare":"4:3","wall":"1:1"},"ratioCssClass":{"full":"media--r3-1","half":"media--r4-3","bleed":"media--r21-9","grid":"media--r4-3","compare":"media--r4-3","wall":"media--r1-1"},"recommendedSizePx":{"full":"2400×800","half":"1400×1050","bleed":"2560×1100","grid":"900×675","compare":"1400×1050","wall":"480×480"},"maxInlineBytes":1572864,"maxTotalInlineBytes":8388608,"maxPerPage":6,"placeholderLabel":"配图占位","placeholderHintPrefix":"建议 ","placeholderHintSuffix":"px","zeroExternal":true,"qualityRule":"位图优先提供 ≥2× 显示尺寸;截图用 16:9 或 4:3 锁定比例;数据图不要用截图(应用内联 SVG 图表,可随主题变色)。"};
@@ -2056,6 +2056,16 @@ function modeFloorPtA(mode) {
2056
2056
  var m = String(mode || 'presentation');
2057
2057
  return (floors[m] != null) ? floors[m] : 10;
2058
2058
  }
2059
+ function snapFont(pt, ladder, floor) {
2060
+ var lad = (ladder || FONT_LADDER).filter(function (v) { return v >= (floor == null ? 0 : floor); });
2061
+ if (!lad.length) return floor == null ? pt : Math.max(floor, pt);
2062
+ var best = lad[0], bd = Math.abs(lad[0] - pt);
2063
+ for (var i = 1; i < lad.length; i++) {
2064
+ var d = Math.abs(lad[i] - pt);
2065
+ if (d < bd) { bd = d; best = lad[i]; }
2066
+ }
2067
+ return best;
2068
+ }
2059
2069
  function fitFont(items, widthIn, availIn, opts) {
2060
2070
  opts = opts || {};
2061
2071
  var max = (opts.max == null) ? 99 : opts.max, lf = opts.lineFactor || 1.45;
@@ -2400,7 +2410,7 @@ function imageLayoutShapes(sh, S, layout, img, items, points, isPh, fit, y0, y1)
2400
2410
  sh.push(shape('rect', rxI, lyI + rowHI / 2 - 0.06, 0.12, 0.12, S.accent));
2401
2411
  sh.push(txSp(rxI + 0.26, lyI, rwI - 0.3, rowHI - 0.04,
2402
2412
  [[{ t: (pt[0] || '') + ' ', sz: fzI, b: 1, col: S.ink, font: S.font },
2403
- { t: pt[1] || '', sz: fzI - 1, col: S.body, font: S.font }]], { anchor: 't' }));
2413
+ { t: pt[1] || '', sz: snapFont(fzI - 1), col: S.body, font: S.font }]], { anchor: 't' }));
2404
2414
  });
2405
2415
  capY = iy + Math.max(ihh, ih * 0.5);
2406
2416
  } else if (layout === 'bleed') {
@@ -2449,24 +2459,55 @@ function hbarRows(sh, ec, S, y0, y1, dcols, x0, w) {
2449
2459
  }
2450
2460
  /* 竖柱 / 堆叠柱 */
2451
2461
  function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
2452
- var labels = ec.labels || [], values = ec.values || [];
2462
+ /* Multi-series aware (mirrors lineShapes): ec.series[{name,values}] or single ec.values. */
2463
+ var labels = ec.labels || [];
2464
+ var series = (ec.series && ec.series.length)
2465
+ ? ec.series
2466
+ : [{ name: '数值', values: ec.values || [] }];
2453
2467
  var nb = Math.max(1, labels.length);
2468
+ var ns = Math.max(1, series.length);
2454
2469
  var baseY = y + h, plotW = w, step = plotW / nb;
2455
- var bw = Math.min(1.1, step * 0.55);
2456
- var vmax = ec.max || Math.max.apply(null, values.concat([1]));
2457
- var top = values.length ? Math.max.apply(null, values) : 0;
2470
+ var cluster = Math.min(1.1, step * (ns > 1 ? 0.78 : 0.55));
2471
+ var bw = cluster / ns;
2472
+ var all = [];
2473
+ series.forEach(function (se) {
2474
+ (se.values || []).forEach(function (v) { all.push(Number(v) || 0); });
2475
+ });
2476
+ var vmax = ec.max || Math.max.apply(null, all.concat([1]));
2477
+ var top = all.length ? Math.max.apply(null, all) : 0;
2458
2478
  sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
2459
- values.forEach(function (v, i) {
2460
- var hh = Math.max(0.05, v / vmax * (h - 0.5));
2461
- var bx = x + i * step + (step - bw) / 2;
2462
- var hot = v === top;
2463
- var dc = dcols ? dcols[i % 5] : null;
2464
- sh.push(shape('roundRect', bx, baseY - hh, bw, hh, dc || (hot ? S.accent : S.faint), { adj: 6000 }));
2465
- sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
2466
- [[{ t: valText(v, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
2467
- sh.push(txSp(bx - 0.3, baseY + 0.08, bw + 0.6, 0.32,
2468
- [[{ t: labels[i], sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2479
+ series.forEach(function (se, si) {
2480
+ var vals = se.values || [];
2481
+ var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
2482
+ vals.forEach(function (v, i) {
2483
+ var num = Number(v) || 0;
2484
+ var hh = Math.max(0.05, num / vmax * (h - 0.5));
2485
+ var bx = x + i * step + (step - cluster) / 2 + si * bw;
2486
+ var hot = ns === 1 && num === top;
2487
+ sh.push(shape('roundRect', bx, baseY - hh, Math.max(0.04, bw * 0.92), hh,
2488
+ hot ? (dcols ? dcols[i % 5] : S.accent) : col, { adj: 6000 }));
2489
+ if (ns === 1) {
2490
+ sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
2491
+ [[{ t: valText(num, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
2492
+ }
2493
+ });
2469
2494
  });
2495
+ labels.forEach(function (lb, i) {
2496
+ var lx = x + i * step;
2497
+ sh.push(txSp(lx, baseY + 0.08, step, 0.32,
2498
+ [[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2499
+ });
2500
+ /* Multi-series legend along top of plot (keeps category labels; avoids blank chart). */
2501
+ if (ns > 1) {
2502
+ var lx2 = x;
2503
+ series.forEach(function (se, si) {
2504
+ var col2 = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
2505
+ sh.push(shape('rect', lx2, y, 0.12, 0.12, col2));
2506
+ sh.push(txSp(lx2 + 0.16, y - 0.02, 1.4, 0.24,
2507
+ [[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]]));
2508
+ lx2 += 1.6;
2509
+ });
2510
+ }
2470
2511
  }
2471
2512
  /* 折线 / 双折线 / 面积 */
2472
2513
  function lineShapes(sh, ec, S, x, y, w, h, dcols) {
@@ -2873,8 +2914,11 @@ function infoApprox(sh, sec, S, y0, y1) {
2873
2914
  totals.push(tt);
2874
2915
  }
2875
2916
  var maxTot = Math.max.apply(null, totals.concat([1]));
2876
- var plotH3 = P.y1 - P.y0;
2877
- var cyMid = (P.y0 + P.y1) / 2;
2917
+ /* Reserve legend INSIDE the plot box so 6-series pages do not crush the annotation band. */
2918
+ var legH = Math.min(1.2, Math.max(0.28, series.length * 0.22 + 0.08));
2919
+ var plotBottom = P.y1 - legH;
2920
+ var plotH3 = Math.max(0.6, plotBottom - P.y0);
2921
+ var cyMid = P.y0 + plotH3 / 2;
2878
2922
  var base = totals.map(function (tv) { return cyMid - tv / maxTot * plotH3 / 2; });
2879
2923
  series.forEach(function (se, si) {
2880
2924
  var upper = base.map(function (bv, i3) { return bv + (values[si][i3] || 0) / maxTot * plotH3 / 2; });
@@ -2884,12 +2928,12 @@ function infoApprox(sh, sec, S, y0, y1) {
2884
2928
  sh.push(shape('rect', MX + i4 * stepX, (base[i4] + base[i4 + 1]) / 2, stepX, hgt, palette[si % palette.length]));
2885
2929
  }
2886
2930
  base = upper;
2887
- sh.push(shape('rect', MX, P.y1 + 0.06 + si * 0.26, 0.12, 0.12, palette[si % palette.length]));
2888
- sh.push(txSp(MX + 0.16, P.y1 + si * 0.26, 1.5, 0.26,
2931
+ sh.push(shape('rect', MX, plotBottom + 0.04 + si * 0.2, 0.12, 0.12, palette[si % palette.length]));
2932
+ sh.push(txSp(MX + 0.16, plotBottom + 0.02 + si * 0.2, 1.5, 0.2,
2889
2933
  [[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]], { anchor: 'ctr' }));
2890
2934
  });
2891
2935
  labels.forEach(function (lb, i5) {
2892
- sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, P.y1 - 0.02, 0.8, 0.24,
2936
+ sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, plotBottom - 0.22, 0.8, 0.2,
2893
2937
  [[{ t: String(lb), sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2894
2938
  });
2895
2939
  }
@@ -3081,8 +3125,8 @@ function slidesOf(model, S) {
3081
3125
  var iy = iR.y;
3082
3126
  var capH2 = img.caption ? 0.32 : 0;
3083
3127
  var ih = Math.max(1.2, iR.h);
3084
- imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
3085
- if (img.caption) sh.push(txSp(MX, iy + ih + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3128
+ var capYImg = imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
3129
+ if (img.caption) sh.push(txSp(MX, capYImg + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3086
3130
  } else if (type === 'donut') {
3087
3131
  var dcn = sec.chart || {};
3088
3132
  if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme));
@@ -3399,7 +3443,7 @@ function slidesOf(model, S) {
3399
3443
  sh.push(shape('rect', x, ly2 + rowHP / 2 - 0.06, 0.12, 0.12, S.accent));
3400
3444
  sh.push(txSp(x + 0.28, ly2, w - 0.4, rowHP - 0.04,
3401
3445
  [[{ t: pt[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
3402
- { t: pt[1], sz: fzP - 1, col: S.body, font: S.font }]]));
3446
+ { t: pt[1], sz: snapFont(fzP - 1), col: S.body, font: S.font }]]));
3403
3447
  });
3404
3448
  };
3405
3449
  if (lt2 === 'table') {
@@ -3520,7 +3564,7 @@ function slidesOf(model, S) {
3520
3564
  sh.push(shape('rect', ptsR.x, y11 + rowHPt / 2 - P2.markSize / 2, P2.markSize, P2.markSize, S.accent));
3521
3565
  sh.push(txSp(ptsR.x + 0.32, y11, textW, rowHPt - 0.04,
3522
3566
  [[{ t: p[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
3523
- { t: p[1], sz: fzP - 1, col: S.body, font: S.font }]]));
3567
+ { t: p[1], sz: snapFont(fzP - 1), col: S.body, font: S.font }]]));
3524
3568
  });
3525
3569
  if (hasM) {
3526
3570
  var gx2 = PW - MX - nm2 * mw2;
@@ -1869,7 +1869,7 @@ var STYLE_DATA_COLORS = {"business-blue":["1a73e8","3526bb","21a199","b77148","9
1869
1869
  var STYLE_DATA_COLORS_DARK = {"business-blue":["8ab4f8","7966e0","88e7e5","d4ad91","cbce83"],"apple-mono":["f5f5f7","2997ff","2dd4bf","ff9f0a","bf5af2"],"mckinsey":["7fb2e5","6f6bdb","8ce3d8","d1a694","cbc586"],"brand-red":["ff5a66","e09e66","e788cb","91d4c2","83b4ce"],"warm-sand":["d9b36a","c2d571","df9391","97b5ce","8e89c7"],"deep-teal":["4fc3b0","7aaecd","97d8a4","c99cac","c29c8f"],"graphite-dark":["4da3ff","6c66e0","88e7dc","d4a691","cec983"],"indigo-violet":["a5a4f0","a967e0","89c3e7","d3c792","aece83"],"spectrum":["8ba0f8","9066e0","88d6e7","d4ba91","bdce83"]};
1870
1870
  var GRID = {"columns":12,"gutter":0.16,"colW":0.8644,"x":[0.6,1.6244,2.6488,3.6732,4.6976,5.722,6.7464,7.7708,8.7952,9.8196,10.844,11.8684,12.733],"y":[2.35,3.27,4.19,5.11,6.03,6.9],"safe":{"top":0.4,"bottom":0.4,"left":0.6,"right":0.6}};
1871
1871
  var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"so-what 结论条、核心结论框"},{"id":"T9","name":"SO WHAT 标签","role":"caption","usage":"so-what 标签行"},{"id":"T10","name":"SO WHAT 正文/业务含义","role":"body","usage":"so-what 正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
1872
- var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级:① 优化内容(列表化/精炼/合并/拆页/换组合版式)→ ② 调整容器尺寸与行宽 → ③ 有限度缩小字号(见 fontShrink)→ ④ 仍装不下则拆页。禁止无限缩字号硬塞,也禁止把「精简」当第一手段砍掉决策必需信息。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
1872
+ var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级(禁静默截断):① 重构承载(列表/卡片/表/图/组合版式)→ ② 拆页/分章(议程→主张→证据卡→明细;01a/01b;信息须完整)→ ③ 换布局形态(V1–V4/多列/卡栅格)→ ④ 有限 fontShrink(阶梯内、模式 floor 以上、最多 4 档)→ ⑤ 禁止:静默截断、砍 so-what/证据/口径、为审美稀疏删决策必需信息。单页字数预算是预警不是删字许可证。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
1873
1873
  var REG = {"bar":{"html":"svg-bars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160}},"hbar":{"html":"svg-hbars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":100}},"stack":{"html":"svg-stack","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160},"nativeOpt":{"barGrouping":"stacked"}},"stackline":{"html":"svg-stackline","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":40},"nativeOpt":{"barGrouping":"percentStacked"}},"line":{"html":"svg-line","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"dualline":{"html":"svg-dualline","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"area":{"html":"svg-area","pptx":"native","nativeType":"area","dataTable":"notes","min":{"vbHeightMin":160}},"donut":{"html":"svg-donut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"multidonut":{"html":"svg-mdonut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"pie":{"html":"svg-pie","pptx":"native","nativeType":"pie","dataTable":"notes","min":{"pxWidthMin":140}},"radar":{"html":"svg-radar","pptx":"native","nativeType":"radar","dataTable":"notes","min":{"pxWidthMin":220}},"scatter":{"html":"svg-scatter","pptx":"native","nativeType":"scatter","dataTable":"notes","min":{"vbHeightMin":180}},"bubble":{"html":"svg-bubble","pptx":"native","nativeType":"bubble","dataTable":"notes","min":{"vbHeightMin":180}},"waterfall":{"html":"svg-wf","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":170},"nativeTrick":"stacked+hiddenBase+connector"},"gauge":{"html":"svg-gauge","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":180},"nativeTrick":"doughnut+firstSliceAng+hiddenRemainder"},"pareto":{"html":"svg-pareto","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":180},"nativeTrick":"multiType+bar+line"},"gantt":{"html":"svg-gantt","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":130},"path":{"forbidPresetShapes":true}},"vsbar":{"html":"svg-vsbar","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"progress":{"html":"svg-progress","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"sparkline":{"html":"svg-spark","pptx":"shape","dataTable":"off","min":{"vbHeightMin":20},"path":{"forbidPresetShapes":true}},"funnel":{"html":"svg-funnel","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"minSamplePoints":8,"forbidPresetShapes":true}},"treemap":{"html":"svg-treemap","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":170},"path":{"forbidPresetShapes":true}},"marimekko":{"html":"svg-marimekko","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"boxplot":{"html":"svg-boxplot","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"network":{"html":"svg-network","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"forbidPresetShapes":true}},"sankey":{"html":"svg-sankey","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"minSamplePoints":16,"trackBothEdges":true,"forbidPresetShapes":true}},"streamgraph":{"html":"svg-stream","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"minSamplePoints":24,"trackBothEdges":true,"forbidPresetShapes":true}},"slope":{"html":"svg-slope","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"dumbbell":{"html":"svg-dumbbell","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"lollipop":{"html":"svg-lollipop","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"dotplot":{"html":"svg-dotplot","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":120},"path":{"forbidPresetShapes":true}},"bulletchart":{"html":"svg-bullet","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"waffle":{"html":"svg-waffle","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"radialbar":{"html":"svg-radialbar","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":200},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"rose":{"html":"svg-rose","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":180},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"candlestick":{"html":"svg-candle","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}}};
1874
1874
  var DEEP = {"triggers":["高保真","1:1","一比一","精确还原","按图还原","正式交付","不能偏移","像素级","严格对照","逐页验收"],"complexCharts":["sankey","streamgraph","treemap","marimekko","boxplot","network","rose","radialbar","candlestick","funnel"],"checks":{"anchors":true,"containerOverflow":true,"renderCompare":true,"manifest":true},"renderCompare":{"engine":"soffice","fallback":"powerpoint-com","skipIfMissing":true,"dpi":96}};
1875
1875
  var IMG = {"layouts":["full","half","bleed","grid","compare","wall"],"multiLayouts":["grid","compare","wall"],"fitEnum":["cover","contain"],"fitDefault":"cover","formats":["png","jpeg","jpg","webp","svg"],"ratioDefault":{"full":"3:1","half":"4:3","bleed":"21:9","grid":"4:3","compare":"4:3","wall":"1:1"},"ratioCssClass":{"full":"media--r3-1","half":"media--r4-3","bleed":"media--r21-9","grid":"media--r4-3","compare":"media--r4-3","wall":"media--r1-1"},"recommendedSizePx":{"full":"2400×800","half":"1400×1050","bleed":"2560×1100","grid":"900×675","compare":"1400×1050","wall":"480×480"},"maxInlineBytes":1572864,"maxTotalInlineBytes":8388608,"maxPerPage":6,"placeholderLabel":"配图占位","placeholderHintPrefix":"建议 ","placeholderHintSuffix":"px","zeroExternal":true,"qualityRule":"位图优先提供 ≥2× 显示尺寸;截图用 16:9 或 4:3 锁定比例;数据图不要用截图(应用内联 SVG 图表,可随主题变色)。"};
@@ -2066,6 +2066,16 @@ function modeFloorPtA(mode) {
2066
2066
  var m = String(mode || 'presentation');
2067
2067
  return (floors[m] != null) ? floors[m] : 10;
2068
2068
  }
2069
+ function snapFont(pt, ladder, floor) {
2070
+ var lad = (ladder || FONT_LADDER).filter(function (v) { return v >= (floor == null ? 0 : floor); });
2071
+ if (!lad.length) return floor == null ? pt : Math.max(floor, pt);
2072
+ var best = lad[0], bd = Math.abs(lad[0] - pt);
2073
+ for (var i = 1; i < lad.length; i++) {
2074
+ var d = Math.abs(lad[i] - pt);
2075
+ if (d < bd) { bd = d; best = lad[i]; }
2076
+ }
2077
+ return best;
2078
+ }
2069
2079
  function fitFont(items, widthIn, availIn, opts) {
2070
2080
  opts = opts || {};
2071
2081
  var max = (opts.max == null) ? 99 : opts.max, lf = opts.lineFactor || 1.45;
@@ -2410,7 +2420,7 @@ function imageLayoutShapes(sh, S, layout, img, items, points, isPh, fit, y0, y1)
2410
2420
  sh.push(shape('rect', rxI, lyI + rowHI / 2 - 0.06, 0.12, 0.12, S.accent));
2411
2421
  sh.push(txSp(rxI + 0.26, lyI, rwI - 0.3, rowHI - 0.04,
2412
2422
  [[{ t: (pt[0] || '') + ' ', sz: fzI, b: 1, col: S.ink, font: S.font },
2413
- { t: pt[1] || '', sz: fzI - 1, col: S.body, font: S.font }]], { anchor: 't' }));
2423
+ { t: pt[1] || '', sz: snapFont(fzI - 1), col: S.body, font: S.font }]], { anchor: 't' }));
2414
2424
  });
2415
2425
  capY = iy + Math.max(ihh, ih * 0.5);
2416
2426
  } else if (layout === 'bleed') {
@@ -2459,24 +2469,55 @@ function hbarRows(sh, ec, S, y0, y1, dcols, x0, w) {
2459
2469
  }
2460
2470
  /* 竖柱 / 堆叠柱 */
2461
2471
  function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
2462
- var labels = ec.labels || [], values = ec.values || [];
2472
+ /* Multi-series aware (mirrors lineShapes): ec.series[{name,values}] or single ec.values. */
2473
+ var labels = ec.labels || [];
2474
+ var series = (ec.series && ec.series.length)
2475
+ ? ec.series
2476
+ : [{ name: '数值', values: ec.values || [] }];
2463
2477
  var nb = Math.max(1, labels.length);
2478
+ var ns = Math.max(1, series.length);
2464
2479
  var baseY = y + h, plotW = w, step = plotW / nb;
2465
- var bw = Math.min(1.1, step * 0.55);
2466
- var vmax = ec.max || Math.max.apply(null, values.concat([1]));
2467
- var top = values.length ? Math.max.apply(null, values) : 0;
2480
+ var cluster = Math.min(1.1, step * (ns > 1 ? 0.78 : 0.55));
2481
+ var bw = cluster / ns;
2482
+ var all = [];
2483
+ series.forEach(function (se) {
2484
+ (se.values || []).forEach(function (v) { all.push(Number(v) || 0); });
2485
+ });
2486
+ var vmax = ec.max || Math.max.apply(null, all.concat([1]));
2487
+ var top = all.length ? Math.max.apply(null, all) : 0;
2468
2488
  sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
2469
- values.forEach(function (v, i) {
2470
- var hh = Math.max(0.05, v / vmax * (h - 0.5));
2471
- var bx = x + i * step + (step - bw) / 2;
2472
- var hot = v === top;
2473
- var dc = dcols ? dcols[i % 5] : null;
2474
- sh.push(shape('roundRect', bx, baseY - hh, bw, hh, dc || (hot ? S.accent : S.faint), { adj: 6000 }));
2475
- sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
2476
- [[{ t: valText(v, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
2477
- sh.push(txSp(bx - 0.3, baseY + 0.08, bw + 0.6, 0.32,
2478
- [[{ t: labels[i], sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2489
+ series.forEach(function (se, si) {
2490
+ var vals = se.values || [];
2491
+ var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
2492
+ vals.forEach(function (v, i) {
2493
+ var num = Number(v) || 0;
2494
+ var hh = Math.max(0.05, num / vmax * (h - 0.5));
2495
+ var bx = x + i * step + (step - cluster) / 2 + si * bw;
2496
+ var hot = ns === 1 && num === top;
2497
+ sh.push(shape('roundRect', bx, baseY - hh, Math.max(0.04, bw * 0.92), hh,
2498
+ hot ? (dcols ? dcols[i % 5] : S.accent) : col, { adj: 6000 }));
2499
+ if (ns === 1) {
2500
+ sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
2501
+ [[{ t: valText(num, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
2502
+ }
2503
+ });
2479
2504
  });
2505
+ labels.forEach(function (lb, i) {
2506
+ var lx = x + i * step;
2507
+ sh.push(txSp(lx, baseY + 0.08, step, 0.32,
2508
+ [[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2509
+ });
2510
+ /* Multi-series legend along top of plot (keeps category labels; avoids blank chart). */
2511
+ if (ns > 1) {
2512
+ var lx2 = x;
2513
+ series.forEach(function (se, si) {
2514
+ var col2 = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
2515
+ sh.push(shape('rect', lx2, y, 0.12, 0.12, col2));
2516
+ sh.push(txSp(lx2 + 0.16, y - 0.02, 1.4, 0.24,
2517
+ [[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]]));
2518
+ lx2 += 1.6;
2519
+ });
2520
+ }
2480
2521
  }
2481
2522
  /* 折线 / 双折线 / 面积 */
2482
2523
  function lineShapes(sh, ec, S, x, y, w, h, dcols) {
@@ -2883,8 +2924,11 @@ function infoApprox(sh, sec, S, y0, y1) {
2883
2924
  totals.push(tt);
2884
2925
  }
2885
2926
  var maxTot = Math.max.apply(null, totals.concat([1]));
2886
- var plotH3 = P.y1 - P.y0;
2887
- var cyMid = (P.y0 + P.y1) / 2;
2927
+ /* Reserve legend INSIDE the plot box so 6-series pages do not crush the annotation band. */
2928
+ var legH = Math.min(1.2, Math.max(0.28, series.length * 0.22 + 0.08));
2929
+ var plotBottom = P.y1 - legH;
2930
+ var plotH3 = Math.max(0.6, plotBottom - P.y0);
2931
+ var cyMid = P.y0 + plotH3 / 2;
2888
2932
  var base = totals.map(function (tv) { return cyMid - tv / maxTot * plotH3 / 2; });
2889
2933
  series.forEach(function (se, si) {
2890
2934
  var upper = base.map(function (bv, i3) { return bv + (values[si][i3] || 0) / maxTot * plotH3 / 2; });
@@ -2894,12 +2938,12 @@ function infoApprox(sh, sec, S, y0, y1) {
2894
2938
  sh.push(shape('rect', MX + i4 * stepX, (base[i4] + base[i4 + 1]) / 2, stepX, hgt, palette[si % palette.length]));
2895
2939
  }
2896
2940
  base = upper;
2897
- sh.push(shape('rect', MX, P.y1 + 0.06 + si * 0.26, 0.12, 0.12, palette[si % palette.length]));
2898
- sh.push(txSp(MX + 0.16, P.y1 + si * 0.26, 1.5, 0.26,
2941
+ sh.push(shape('rect', MX, plotBottom + 0.04 + si * 0.2, 0.12, 0.12, palette[si % palette.length]));
2942
+ sh.push(txSp(MX + 0.16, plotBottom + 0.02 + si * 0.2, 1.5, 0.2,
2899
2943
  [[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]], { anchor: 'ctr' }));
2900
2944
  });
2901
2945
  labels.forEach(function (lb, i5) {
2902
- sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, P.y1 - 0.02, 0.8, 0.24,
2946
+ sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, plotBottom - 0.22, 0.8, 0.2,
2903
2947
  [[{ t: String(lb), sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2904
2948
  });
2905
2949
  }
@@ -3091,8 +3135,8 @@ function slidesOf(model, S) {
3091
3135
  var iy = iR.y;
3092
3136
  var capH2 = img.caption ? 0.32 : 0;
3093
3137
  var ih = Math.max(1.2, iR.h);
3094
- imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
3095
- if (img.caption) sh.push(txSp(MX, iy + ih + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3138
+ var capYImg = imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
3139
+ if (img.caption) sh.push(txSp(MX, capYImg + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3096
3140
  } else if (type === 'donut') {
3097
3141
  var dcn = sec.chart || {};
3098
3142
  if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme));
@@ -3409,7 +3453,7 @@ function slidesOf(model, S) {
3409
3453
  sh.push(shape('rect', x, ly2 + rowHP / 2 - 0.06, 0.12, 0.12, S.accent));
3410
3454
  sh.push(txSp(x + 0.28, ly2, w - 0.4, rowHP - 0.04,
3411
3455
  [[{ t: pt[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
3412
- { t: pt[1], sz: fzP - 1, col: S.body, font: S.font }]]));
3456
+ { t: pt[1], sz: snapFont(fzP - 1), col: S.body, font: S.font }]]));
3413
3457
  });
3414
3458
  };
3415
3459
  if (lt2 === 'table') {
@@ -3530,7 +3574,7 @@ function slidesOf(model, S) {
3530
3574
  sh.push(shape('rect', ptsR.x, y11 + rowHPt / 2 - P2.markSize / 2, P2.markSize, P2.markSize, S.accent));
3531
3575
  sh.push(txSp(ptsR.x + 0.32, y11, textW, rowHPt - 0.04,
3532
3576
  [[{ t: p[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
3533
- { t: p[1], sz: fzP - 1, col: S.body, font: S.font }]]));
3577
+ { t: p[1], sz: snapFont(fzP - 1), col: S.body, font: S.font }]]));
3534
3578
  });
3535
3579
  if (hasM) {
3536
3580
  var gx2 = PW - MX - nm2 * mw2;
@@ -2148,7 +2148,7 @@ var STYLE_DATA_COLORS = {"business-blue":["1a73e8","3526bb","21a199","b77148","9
2148
2148
  var STYLE_DATA_COLORS_DARK = {"business-blue":["8ab4f8","7966e0","88e7e5","d4ad91","cbce83"],"apple-mono":["f5f5f7","2997ff","2dd4bf","ff9f0a","bf5af2"],"mckinsey":["7fb2e5","6f6bdb","8ce3d8","d1a694","cbc586"],"brand-red":["ff5a66","e09e66","e788cb","91d4c2","83b4ce"],"warm-sand":["d9b36a","c2d571","df9391","97b5ce","8e89c7"],"deep-teal":["4fc3b0","7aaecd","97d8a4","c99cac","c29c8f"],"graphite-dark":["4da3ff","6c66e0","88e7dc","d4a691","cec983"],"indigo-violet":["a5a4f0","a967e0","89c3e7","d3c792","aece83"],"spectrum":["8ba0f8","9066e0","88d6e7","d4ba91","bdce83"]};
2149
2149
  var GRID = {"columns":12,"gutter":0.16,"colW":0.8644,"x":[0.6,1.6244,2.6488,3.6732,4.6976,5.722,6.7464,7.7708,8.7952,9.8196,10.844,11.8684,12.733],"y":[2.35,3.27,4.19,5.11,6.03,6.9],"safe":{"top":0.4,"bottom":0.4,"left":0.6,"right":0.6}};
2150
2150
  var TYPO = {"levels":[{"id":"C0","name":"封面/章节幕标题","role":"coverTitle","usage":"封面主标题、章节幕标题"},{"id":"T1","name":"页码/章节徽章","role":"caption","usage":"页码徽章、章节编号"},{"id":"T2","name":"页面主标题/结论标题","role":"h1","usage":"每页顶部结论句(research 行动标题)"},{"id":"T3","name":"副标题/语境说明","role":"lead","usage":"主标题下方导语"},{"id":"T4","name":"模块标题/图表标题","role":"h2","usage":"卡片标题、图表标题、面板标题"},{"id":"T5","name":"证据编号/轻量标签","role":"micro","usage":"Exhibit 编号、状态标签"},{"id":"T6","name":"证据块标题/小节标题","role":"h2","usage":"分栏小标题、证据块标题"},{"id":"T7","name":"正文解释段落","role":"body","usage":"证据解释、管理解读正文、表格正文"},{"id":"T8","name":"结论条文字","role":"body","usage":"so-what 结论条、核心结论框"},{"id":"T9","name":"SO WHAT 标签","role":"caption","usage":"so-what 标签行"},{"id":"T10","name":"SO WHAT 正文/业务含义","role":"body","usage":"so-what 正文、行动含义、表格行动项"},{"id":"T11","name":"图表轴/图例/刻度/微标签","role":"micro","usage":"坐标轴、图例、刻度、单位(禁用于表格正文与完整短句)"},{"id":"T12","name":"图表数据标签","role":"caption","usage":"折线点值、柱形标签、百分比标注"},{"id":"T13","name":"关键 KPI 大数字","role":"h1","usage":"KPI 大数字、hero 数值"},{"id":"T14","name":"注释/口径/来源/页脚","role":"micro","usage":"footnote、来源行、页脚、小页码"}],"tableSemanticRule":{"body":["T7","T10"],"microAllowed":["T11"],"microForbiddenFor":["表格正文","行动项","风险项","解释句","建议句","长项目符号","完整短句"]}};
2151
- var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级:① 优化内容(列表化/精炼/合并/拆页/换组合版式)→ ② 调整容器尺寸与行宽 → ③ 有限度缩小字号(见 fontShrink)→ ④ 仍装不下则拆页。禁止无限缩字号硬塞,也禁止把「精简」当第一手段砍掉决策必需信息。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
2151
+ var CONT = {"pad":{"card":0.18,"panel":0.2,"cell":0.08,"band":0.16,"soWhat":0.16,"kpi":0.18,"chart":0.1,"head":0.05,"footer":0.05,"node":0.06},"minPad":0.06,"textMetrics":{"lineFactor":1.45,"emAsciiRatio":0.52},"overflowRule":"溢出处置优先级(禁静默截断):① 重构承载(列表/卡片/表/图/组合版式)→ ② 拆页/分章(议程→主张→证据卡→明细;01a/01b;信息须完整)→ ③ 换布局形态(V1–V4/多列/卡栅格)→ ④ 有限 fontShrink(阶梯内、模式 floor 以上、最多 4 档)→ ⑤ 禁止:静默截断、砍 so-what/证据/口径、为审美稀疏删决策必需信息。单页字数预算是预警不是删字许可证。","fontShrink":{"ladder":[15,14,13.5,13,12.5,12,11.5,11,10.5,10,9.5,9,8.5],"maxShrinkSteps":4,"floorPt":{"presentation":10,"research":9,"architecture":10},"preferContentFirst":true,"warnBelow":9.5},"continuousTextRule":"语义连续的一句话 / so-what 主句 / 结论句 / 表格单元格正文不得拆成多个独立文本框(避免异常空格、断句、基线漂移);局部加粗高亮必须用同一文本框内富文本。"};
2152
2152
  var REG = {"bar":{"html":"svg-bars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160}},"hbar":{"html":"svg-hbars","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":100}},"stack":{"html":"svg-stack","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":160},"nativeOpt":{"barGrouping":"stacked"}},"stackline":{"html":"svg-stackline","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":40},"nativeOpt":{"barGrouping":"percentStacked"}},"line":{"html":"svg-line","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"dualline":{"html":"svg-dualline","pptx":"native","nativeType":"line","dataTable":"notes","min":{"vbHeightMin":160}},"area":{"html":"svg-area","pptx":"native","nativeType":"area","dataTable":"notes","min":{"vbHeightMin":160}},"donut":{"html":"svg-donut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"multidonut":{"html":"svg-mdonut","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":140}},"pie":{"html":"svg-pie","pptx":"native","nativeType":"pie","dataTable":"notes","min":{"pxWidthMin":140}},"radar":{"html":"svg-radar","pptx":"native","nativeType":"radar","dataTable":"notes","min":{"pxWidthMin":220}},"scatter":{"html":"svg-scatter","pptx":"native","nativeType":"scatter","dataTable":"notes","min":{"vbHeightMin":180}},"bubble":{"html":"svg-bubble","pptx":"native","nativeType":"bubble","dataTable":"notes","min":{"vbHeightMin":180}},"waterfall":{"html":"svg-wf","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":170},"nativeTrick":"stacked+hiddenBase+connector"},"gauge":{"html":"svg-gauge","pptx":"native","nativeType":"doughnut","dataTable":"notes","min":{"pxWidthMin":180},"nativeTrick":"doughnut+firstSliceAng+hiddenRemainder"},"pareto":{"html":"svg-pareto","pptx":"native","nativeType":"bar","dataTable":"notes","min":{"vbHeightMin":180},"nativeTrick":"multiType+bar+line"},"gantt":{"html":"svg-gantt","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":130},"path":{"forbidPresetShapes":true}},"vsbar":{"html":"svg-vsbar","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"progress":{"html":"svg-progress","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":100},"path":{"forbidPresetShapes":true}},"sparkline":{"html":"svg-spark","pptx":"shape","dataTable":"off","min":{"vbHeightMin":20},"path":{"forbidPresetShapes":true}},"funnel":{"html":"svg-funnel","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"minSamplePoints":8,"forbidPresetShapes":true}},"treemap":{"html":"svg-treemap","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":170},"path":{"forbidPresetShapes":true}},"marimekko":{"html":"svg-marimekko","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"boxplot":{"html":"svg-boxplot","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"network":{"html":"svg-network","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"forbidPresetShapes":true}},"sankey":{"html":"svg-sankey","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":200},"path":{"minSamplePoints":16,"trackBothEdges":true,"forbidPresetShapes":true}},"streamgraph":{"html":"svg-stream","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":160},"path":{"minSamplePoints":24,"trackBothEdges":true,"forbidPresetShapes":true}},"slope":{"html":"svg-slope","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}},"dumbbell":{"html":"svg-dumbbell","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"lollipop":{"html":"svg-lollipop","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"dotplot":{"html":"svg-dotplot","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":120},"path":{"forbidPresetShapes":true}},"bulletchart":{"html":"svg-bullet","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":140},"path":{"forbidPresetShapes":true}},"waffle":{"html":"svg-waffle","pptx":"shape","dataTable":"notes","min":{"vbHeightMin":160},"path":{"forbidPresetShapes":true}},"radialbar":{"html":"svg-radialbar","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":200},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"rose":{"html":"svg-rose","pptx":"shape","dataTable":"notes","min":{"pxWidthMin":180},"path":{"minSamplePoints":16,"forbidPresetShapes":true}},"candlestick":{"html":"svg-candle","pptx":"shape","dataTable":"appendix","min":{"vbHeightMin":180},"path":{"forbidPresetShapes":true}}};
2153
2153
  var DEEP = {"triggers":["高保真","1:1","一比一","精确还原","按图还原","正式交付","不能偏移","像素级","严格对照","逐页验收"],"complexCharts":["sankey","streamgraph","treemap","marimekko","boxplot","network","rose","radialbar","candlestick","funnel"],"checks":{"anchors":true,"containerOverflow":true,"renderCompare":true,"manifest":true},"renderCompare":{"engine":"soffice","fallback":"powerpoint-com","skipIfMissing":true,"dpi":96}};
2154
2154
  var IMG = {"layouts":["full","half","bleed","grid","compare","wall"],"multiLayouts":["grid","compare","wall"],"fitEnum":["cover","contain"],"fitDefault":"cover","formats":["png","jpeg","jpg","webp","svg"],"ratioDefault":{"full":"3:1","half":"4:3","bleed":"21:9","grid":"4:3","compare":"4:3","wall":"1:1"},"ratioCssClass":{"full":"media--r3-1","half":"media--r4-3","bleed":"media--r21-9","grid":"media--r4-3","compare":"media--r4-3","wall":"media--r1-1"},"recommendedSizePx":{"full":"2400×800","half":"1400×1050","bleed":"2560×1100","grid":"900×675","compare":"1400×1050","wall":"480×480"},"maxInlineBytes":1572864,"maxTotalInlineBytes":8388608,"maxPerPage":6,"placeholderLabel":"配图占位","placeholderHintPrefix":"建议 ","placeholderHintSuffix":"px","zeroExternal":true,"qualityRule":"位图优先提供 ≥2× 显示尺寸;截图用 16:9 或 4:3 锁定比例;数据图不要用截图(应用内联 SVG 图表,可随主题变色)。"};
@@ -2345,6 +2345,16 @@ function modeFloorPtA(mode) {
2345
2345
  var m = String(mode || 'presentation');
2346
2346
  return (floors[m] != null) ? floors[m] : 10;
2347
2347
  }
2348
+ function snapFont(pt, ladder, floor) {
2349
+ var lad = (ladder || FONT_LADDER).filter(function (v) { return v >= (floor == null ? 0 : floor); });
2350
+ if (!lad.length) return floor == null ? pt : Math.max(floor, pt);
2351
+ var best = lad[0], bd = Math.abs(lad[0] - pt);
2352
+ for (var i = 1; i < lad.length; i++) {
2353
+ var d = Math.abs(lad[i] - pt);
2354
+ if (d < bd) { bd = d; best = lad[i]; }
2355
+ }
2356
+ return best;
2357
+ }
2348
2358
  function fitFont(items, widthIn, availIn, opts) {
2349
2359
  opts = opts || {};
2350
2360
  var max = (opts.max == null) ? 99 : opts.max, lf = opts.lineFactor || 1.45;
@@ -2689,7 +2699,7 @@ function imageLayoutShapes(sh, S, layout, img, items, points, isPh, fit, y0, y1)
2689
2699
  sh.push(shape('rect', rxI, lyI + rowHI / 2 - 0.06, 0.12, 0.12, S.accent));
2690
2700
  sh.push(txSp(rxI + 0.26, lyI, rwI - 0.3, rowHI - 0.04,
2691
2701
  [[{ t: (pt[0] || '') + ' ', sz: fzI, b: 1, col: S.ink, font: S.font },
2692
- { t: pt[1] || '', sz: fzI - 1, col: S.body, font: S.font }]], { anchor: 't' }));
2702
+ { t: pt[1] || '', sz: snapFont(fzI - 1), col: S.body, font: S.font }]], { anchor: 't' }));
2693
2703
  });
2694
2704
  capY = iy + Math.max(ihh, ih * 0.5);
2695
2705
  } else if (layout === 'bleed') {
@@ -2738,24 +2748,55 @@ function hbarRows(sh, ec, S, y0, y1, dcols, x0, w) {
2738
2748
  }
2739
2749
  /* 竖柱 / 堆叠柱 */
2740
2750
  function vbarShapes(sh, ec, S, x, y, w, h, dcols) {
2741
- var labels = ec.labels || [], values = ec.values || [];
2751
+ /* Multi-series aware (mirrors lineShapes): ec.series[{name,values}] or single ec.values. */
2752
+ var labels = ec.labels || [];
2753
+ var series = (ec.series && ec.series.length)
2754
+ ? ec.series
2755
+ : [{ name: '数值', values: ec.values || [] }];
2742
2756
  var nb = Math.max(1, labels.length);
2757
+ var ns = Math.max(1, series.length);
2743
2758
  var baseY = y + h, plotW = w, step = plotW / nb;
2744
- var bw = Math.min(1.1, step * 0.55);
2745
- var vmax = ec.max || Math.max.apply(null, values.concat([1]));
2746
- var top = values.length ? Math.max.apply(null, values) : 0;
2759
+ var cluster = Math.min(1.1, step * (ns > 1 ? 0.78 : 0.55));
2760
+ var bw = cluster / ns;
2761
+ var all = [];
2762
+ series.forEach(function (se) {
2763
+ (se.values || []).forEach(function (v) { all.push(Number(v) || 0); });
2764
+ });
2765
+ var vmax = ec.max || Math.max.apply(null, all.concat([1]));
2766
+ var top = all.length ? Math.max.apply(null, all) : 0;
2747
2767
  sh.push(shape('rect', x, baseY, plotW, 0.02, S.line));
2748
- values.forEach(function (v, i) {
2749
- var hh = Math.max(0.05, v / vmax * (h - 0.5));
2750
- var bx = x + i * step + (step - bw) / 2;
2751
- var hot = v === top;
2752
- var dc = dcols ? dcols[i % 5] : null;
2753
- sh.push(shape('roundRect', bx, baseY - hh, bw, hh, dc || (hot ? S.accent : S.faint), { adj: 6000 }));
2754
- sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
2755
- [[{ t: valText(v, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
2756
- sh.push(txSp(bx - 0.3, baseY + 0.08, bw + 0.6, 0.32,
2757
- [[{ t: labels[i], sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2768
+ series.forEach(function (se, si) {
2769
+ var vals = se.values || [];
2770
+ var col = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
2771
+ vals.forEach(function (v, i) {
2772
+ var num = Number(v) || 0;
2773
+ var hh = Math.max(0.05, num / vmax * (h - 0.5));
2774
+ var bx = x + i * step + (step - cluster) / 2 + si * bw;
2775
+ var hot = ns === 1 && num === top;
2776
+ sh.push(shape('roundRect', bx, baseY - hh, Math.max(0.04, bw * 0.92), hh,
2777
+ hot ? (dcols ? dcols[i % 5] : S.accent) : col, { adj: 6000 }));
2778
+ if (ns === 1) {
2779
+ sh.push(txSp(bx - 0.3, baseY - hh - 0.4, bw + 0.6, 0.32,
2780
+ [[{ t: valText(num, ec.unit), sz: 12.5, b: 1, col: hot ? S.accent : S.body, font: S.font }]], { align: 'ctr' }));
2781
+ }
2782
+ });
2758
2783
  });
2784
+ labels.forEach(function (lb, i) {
2785
+ var lx = x + i * step;
2786
+ sh.push(txSp(lx, baseY + 0.08, step, 0.32,
2787
+ [[{ t: lb, sz: 10.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
2788
+ });
2789
+ /* Multi-series legend along top of plot (keeps category labels; avoids blank chart). */
2790
+ if (ns > 1) {
2791
+ var lx2 = x;
2792
+ series.forEach(function (se, si) {
2793
+ var col2 = dcols ? dcols[si % 5] : (si === 0 ? S.accent : S.faint);
2794
+ sh.push(shape('rect', lx2, y, 0.12, 0.12, col2));
2795
+ sh.push(txSp(lx2 + 0.16, y - 0.02, 1.4, 0.24,
2796
+ [[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]]));
2797
+ lx2 += 1.6;
2798
+ });
2799
+ }
2759
2800
  }
2760
2801
  /* 折线 / 双折线 / 面积 */
2761
2802
  function lineShapes(sh, ec, S, x, y, w, h, dcols) {
@@ -3162,8 +3203,11 @@ function infoApprox(sh, sec, S, y0, y1) {
3162
3203
  totals.push(tt);
3163
3204
  }
3164
3205
  var maxTot = Math.max.apply(null, totals.concat([1]));
3165
- var plotH3 = P.y1 - P.y0;
3166
- var cyMid = (P.y0 + P.y1) / 2;
3206
+ /* Reserve legend INSIDE the plot box so 6-series pages do not crush the annotation band. */
3207
+ var legH = Math.min(1.2, Math.max(0.28, series.length * 0.22 + 0.08));
3208
+ var plotBottom = P.y1 - legH;
3209
+ var plotH3 = Math.max(0.6, plotBottom - P.y0);
3210
+ var cyMid = P.y0 + plotH3 / 2;
3167
3211
  var base = totals.map(function (tv) { return cyMid - tv / maxTot * plotH3 / 2; });
3168
3212
  series.forEach(function (se, si) {
3169
3213
  var upper = base.map(function (bv, i3) { return bv + (values[si][i3] || 0) / maxTot * plotH3 / 2; });
@@ -3173,12 +3217,12 @@ function infoApprox(sh, sec, S, y0, y1) {
3173
3217
  sh.push(shape('rect', MX + i4 * stepX, (base[i4] + base[i4 + 1]) / 2, stepX, hgt, palette[si % palette.length]));
3174
3218
  }
3175
3219
  base = upper;
3176
- sh.push(shape('rect', MX, P.y1 + 0.06 + si * 0.26, 0.12, 0.12, palette[si % palette.length]));
3177
- sh.push(txSp(MX + 0.16, P.y1 + si * 0.26, 1.5, 0.26,
3220
+ sh.push(shape('rect', MX, plotBottom + 0.04 + si * 0.2, 0.12, 0.12, palette[si % palette.length]));
3221
+ sh.push(txSp(MX + 0.16, plotBottom + 0.02 + si * 0.2, 1.5, 0.2,
3178
3222
  [[{ t: String(se.name || ('系列' + (si + 1))), sz: 10, col: S.body, font: S.font }]], { anchor: 'ctr' }));
3179
3223
  });
3180
3224
  labels.forEach(function (lb, i5) {
3181
- sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, P.y1 - 0.02, 0.8, 0.24,
3225
+ sh.push(txSp(MX + (nPts > 1 ? i5 / (nPts - 1) : 0.5) * CW - 0.4, plotBottom - 0.22, 0.8, 0.2,
3182
3226
  [[{ t: String(lb), sz: 9.5, col: S.faint, font: S.font }]], { align: 'ctr' }));
3183
3227
  });
3184
3228
  }
@@ -3370,8 +3414,8 @@ function slidesOf(model, S) {
3370
3414
  var iy = iR.y;
3371
3415
  var capH2 = img.caption ? 0.32 : 0;
3372
3416
  var ih = Math.max(1.2, iR.h);
3373
- imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
3374
- if (img.caption) sh.push(txSp(MX, iy + ih + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3417
+ var capYImg = imageLayoutShapes(sh, S, layout, img, items, sec.points || [], isPh, fit, iy, iy + ih);
3418
+ if (img.caption) sh.push(txSp(MX, capYImg + 0.04, CW, capH2, [[{ t: img.caption, sz: 10.5, col: S.faint, font: S.font }]]));
3375
3419
  } else if (type === 'donut') {
3376
3420
  var dcn = sec.chart || {};
3377
3421
  if (dcn.labels && dcn.values) donutChart(sh, dcn, S, dcn.colors || donutColors(model.style, S, model.theme));
@@ -3688,7 +3732,7 @@ function slidesOf(model, S) {
3688
3732
  sh.push(shape('rect', x, ly2 + rowHP / 2 - 0.06, 0.12, 0.12, S.accent));
3689
3733
  sh.push(txSp(x + 0.28, ly2, w - 0.4, rowHP - 0.04,
3690
3734
  [[{ t: pt[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
3691
- { t: pt[1], sz: fzP - 1, col: S.body, font: S.font }]]));
3735
+ { t: pt[1], sz: snapFont(fzP - 1), col: S.body, font: S.font }]]));
3692
3736
  });
3693
3737
  };
3694
3738
  if (lt2 === 'table') {
@@ -3809,7 +3853,7 @@ function slidesOf(model, S) {
3809
3853
  sh.push(shape('rect', ptsR.x, y11 + rowHPt / 2 - P2.markSize / 2, P2.markSize, P2.markSize, S.accent));
3810
3854
  sh.push(txSp(ptsR.x + 0.32, y11, textW, rowHPt - 0.04,
3811
3855
  [[{ t: p[0] + ' ', sz: fzP, b: 1, col: S.ink, font: S.font },
3812
- { t: p[1], sz: fzP - 1, col: S.body, font: S.font }]]));
3856
+ { t: p[1], sz: snapFont(fzP - 1), col: S.body, font: S.font }]]));
3813
3857
  });
3814
3858
  if (hasM) {
3815
3859
  var gx2 = PW - MX - nm2 * mw2;