pptx-glimpse 0.1.4 → 0.2.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.
Files changed (3) hide show
  1. package/dist/index.cjs +355 -25
  2. package/dist/index.js +355 -25
  3. package/package.json +2 -2
package/dist/index.cjs CHANGED
@@ -2078,14 +2078,16 @@ function measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa) {
2078
2078
  const codePoint = char.codePointAt(0);
2079
2079
  const isEa = isCjkCodePoint(codePoint);
2080
2080
  const metrics = isEa && eaMetrics ? eaMetrics : latinMetrics;
2081
+ let charWidth;
2081
2082
  if (metrics) {
2082
- totalWidth += measureCharMetrics(char, codePoint, baseSizePx, metrics);
2083
+ charWidth = measureCharMetrics(char, codePoint, baseSizePx, metrics);
2083
2084
  } else {
2084
- totalWidth += measureCharHeuristic(codePoint, baseSizePx);
2085
+ charWidth = measureCharHeuristic(codePoint, baseSizePx);
2085
2086
  }
2086
- }
2087
- if (bold) {
2088
- totalWidth *= BOLD_FACTOR;
2087
+ if (bold && !isEa) {
2088
+ charWidth *= BOLD_FACTOR;
2089
+ }
2090
+ totalWidth += charWidth;
2089
2091
  }
2090
2092
  return totalWidth;
2091
2093
  }
@@ -3135,7 +3137,7 @@ function renderTextDecorations(x, y, segmentWidth, fontSizePx, props) {
3135
3137
  }
3136
3138
  return lines;
3137
3139
  }
3138
- function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, fontResolver) {
3140
+ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, fontResolver, vert) {
3139
3141
  const fontSize = (props.fontSize ?? defaultFontSize) * fontScale;
3140
3142
  const fontSizePx = fontSize * PX_PER_PT2;
3141
3143
  const parts = [];
@@ -3168,7 +3170,50 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
3168
3170
  }
3169
3171
  totalWidth += segWidth;
3170
3172
  };
3171
- if (needsScriptSplit(props)) {
3173
+ const processCjkUpright = (segText, fontFamily, fontFamilyEa) => {
3174
+ if (segText.length === 0) return;
3175
+ const font = fontResolver.resolveFont(fontFamily, fontFamilyEa);
3176
+ const fillAttrs = buildPathFillAttrs(props);
3177
+ for (const char of segText) {
3178
+ const charWidth = getTextMeasurer().measureTextWidth(
3179
+ char,
3180
+ fontSize,
3181
+ props.bold,
3182
+ fontFamily,
3183
+ fontFamilyEa
3184
+ );
3185
+ if (font) {
3186
+ const charX = x + totalWidth;
3187
+ const path = font.getPath(char, charX, effectiveY, fontSizePx);
3188
+ const pathData = path.toPathData(2);
3189
+ if (pathData && pathData.length > 0) {
3190
+ const cx = charX + charWidth / 2;
3191
+ const cy = effectiveY - fontSizePx * (font.ascender + font.descender) / 2 / font.unitsPerEm;
3192
+ parts.push(
3193
+ `<g transform="rotate(-90, ${cx.toFixed(2)}, ${cy.toFixed(2)})"><path d="${pathData}" ${fillAttrs}/></g>`
3194
+ );
3195
+ }
3196
+ }
3197
+ if (props.underline || props.strikethrough) {
3198
+ parts.push(
3199
+ ...renderTextDecorations(x + totalWidth, effectiveY, charWidth, fontSizePx, props)
3200
+ );
3201
+ }
3202
+ totalWidth += charWidth;
3203
+ }
3204
+ };
3205
+ if (vert === "eaVert") {
3206
+ const scriptParts = splitByScript(processedText);
3207
+ for (const part of scriptParts) {
3208
+ const ff = part.isEa ? props.fontFamilyEa ?? props.fontFamily : props.fontFamily;
3209
+ const ffEa = part.isEa ? props.fontFamilyEa : props.fontFamilyEa;
3210
+ if (part.isEa) {
3211
+ processCjkUpright(part.text, ff, ffEa);
3212
+ } else {
3213
+ processSegment(part.text, ff, ffEa);
3214
+ }
3215
+ }
3216
+ } else if (needsScriptSplit(props)) {
3172
3217
  const scriptParts = splitByScript(processedText);
3173
3218
  for (const part of scriptParts) {
3174
3219
  const ff = part.isEa ? props.fontFamilyEa : props.fontFamily;
@@ -3334,7 +3379,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
3334
3379
  currentY,
3335
3380
  fontScale,
3336
3381
  defaultFontSize,
3337
- fontResolver
3382
+ fontResolver,
3383
+ bodyProperties.vert
3338
3384
  );
3339
3385
  if (result.svg) elements.push(result.svg);
3340
3386
  currentX += result.width;
@@ -3381,7 +3427,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
3381
3427
  currentY,
3382
3428
  fontScale,
3383
3429
  defaultFontSize,
3384
- fontResolver
3430
+ fontResolver,
3431
+ bodyProperties.vert
3385
3432
  );
3386
3433
  if (result.svg) elements.push(result.svg);
3387
3434
  currentX += result.width;
@@ -3824,6 +3871,15 @@ function renderChart(element) {
3824
3871
  case "radar":
3825
3872
  parts.push(renderRadarChart(chart, plotX, plotY, plotW, plotH));
3826
3873
  break;
3874
+ case "stock":
3875
+ parts.push(renderStockChart(chart, plotX, plotY, plotW, plotH));
3876
+ break;
3877
+ case "surface":
3878
+ parts.push(renderSurfaceChart(chart, plotX, plotY, plotW, plotH));
3879
+ break;
3880
+ case "ofPie":
3881
+ parts.push(renderOfPieChart(chart, plotX, plotY, plotW, plotH));
3882
+ break;
3827
3883
  }
3828
3884
  }
3829
3885
  if (chart.legend && chart.series.length > 0) {
@@ -4195,9 +4251,237 @@ function renderRadarChart(chart, x, y, w, h) {
4195
4251
  }
4196
4252
  return parts.join("");
4197
4253
  }
4254
+ function renderStockChart(chart, x, y, w, h) {
4255
+ const parts = [];
4256
+ const { series, categories } = chart;
4257
+ if (series.length < 3) return "";
4258
+ const highSeries = series[0];
4259
+ const lowSeries = series[1];
4260
+ const closeSeries = series[2];
4261
+ const catCount = categories.length || highSeries.values.length;
4262
+ if (catCount === 0) return "";
4263
+ let maxVal = 0;
4264
+ let minVal = Infinity;
4265
+ for (const s of [highSeries, lowSeries, closeSeries]) {
4266
+ for (const v of s.values) {
4267
+ maxVal = Math.max(maxVal, v);
4268
+ minVal = Math.min(minVal, v);
4269
+ }
4270
+ }
4271
+ if (maxVal === minVal) return "";
4272
+ parts.push(
4273
+ `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4274
+ );
4275
+ parts.push(
4276
+ `<line x1="${round3(x)}" y1="${round3(y)}" x2="${round3(x)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4277
+ );
4278
+ for (let c = 0; c < catCount; c++) {
4279
+ const label = categories[c] ?? "";
4280
+ const labelX = x + (c + 0.5) * (w / catCount);
4281
+ parts.push(
4282
+ `<text x="${round3(labelX)}" y="${round3(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
4283
+ );
4284
+ }
4285
+ const range = maxVal - minVal;
4286
+ for (let c = 0; c < catCount; c++) {
4287
+ const cx = x + (c + 0.5) * (w / catCount);
4288
+ const highVal = highSeries.values[c] ?? 0;
4289
+ const lowVal = lowSeries.values[c] ?? 0;
4290
+ const closeVal = closeSeries.values[c] ?? 0;
4291
+ const highY = y + h - (highVal - minVal) / range * h;
4292
+ const lowY = y + h - (lowVal - minVal) / range * h;
4293
+ const closeY = y + h - (closeVal - minVal) / range * h;
4294
+ parts.push(
4295
+ `<line x1="${round3(cx)}" y1="${round3(highY)}" x2="${round3(cx)}" y2="${round3(lowY)}" stroke="#404040" stroke-width="2"/>`
4296
+ );
4297
+ const tickW = w / catCount * 0.2;
4298
+ parts.push(
4299
+ `<line x1="${round3(cx)}" y1="${round3(closeY)}" x2="${round3(cx + tickW)}" y2="${round3(closeY)}" stroke="#404040" stroke-width="2"/>`
4300
+ );
4301
+ }
4302
+ return parts.join("");
4303
+ }
4304
+ function renderSurfaceChart(chart, x, y, w, h) {
4305
+ const parts = [];
4306
+ const { series, categories } = chart;
4307
+ if (series.length === 0) return "";
4308
+ const rows = series.length;
4309
+ const cols = categories.length || Math.max(...series.map((s) => s.values.length));
4310
+ if (cols === 0) return "";
4311
+ let minVal = Infinity;
4312
+ let maxVal = -Infinity;
4313
+ for (const s of series) {
4314
+ for (const v of s.values) {
4315
+ minVal = Math.min(minVal, v);
4316
+ maxVal = Math.max(maxVal, v);
4317
+ }
4318
+ }
4319
+ if (minVal === maxVal) maxVal = minVal + 1;
4320
+ const cellW = w / cols;
4321
+ const cellH = h / rows;
4322
+ for (let r = 0; r < rows; r++) {
4323
+ for (let c = 0; c < cols; c++) {
4324
+ const val = series[r].values[c] ?? 0;
4325
+ const t = (val - minVal) / (maxVal - minVal);
4326
+ const color = heatmapColor(t);
4327
+ const cx = x + c * cellW;
4328
+ const cy = y + r * cellH;
4329
+ parts.push(
4330
+ `<rect x="${round3(cx)}" y="${round3(cy)}" width="${round3(cellW)}" height="${round3(cellH)}" fill="${color}" stroke="#FFFFFF" stroke-width="0.5"/>`
4331
+ );
4332
+ }
4333
+ }
4334
+ for (let c = 0; c < cols; c++) {
4335
+ const label = categories[c] ?? "";
4336
+ if (label) {
4337
+ const labelX = x + (c + 0.5) * cellW;
4338
+ parts.push(
4339
+ `<text x="${round3(labelX)}" y="${round3(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
4340
+ );
4341
+ }
4342
+ }
4343
+ for (let r = 0; r < rows; r++) {
4344
+ const label = series[r].name ?? "";
4345
+ if (label) {
4346
+ const labelY = y + (r + 0.5) * cellH;
4347
+ parts.push(
4348
+ `<text x="${round3(x - 5)}" y="${round3(labelY + 4)}" text-anchor="end" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
4349
+ );
4350
+ }
4351
+ }
4352
+ return parts.join("");
4353
+ }
4354
+ function heatmapColor(t) {
4355
+ const clamped = Math.max(0, Math.min(1, t));
4356
+ let r, g, b;
4357
+ if (clamped < 0.25) {
4358
+ const s = clamped / 0.25;
4359
+ r = 0;
4360
+ g = Math.round(s * 255);
4361
+ b = 255;
4362
+ } else if (clamped < 0.5) {
4363
+ const s = (clamped - 0.25) / 0.25;
4364
+ r = 0;
4365
+ g = 255;
4366
+ b = Math.round((1 - s) * 255);
4367
+ } else if (clamped < 0.75) {
4368
+ const s = (clamped - 0.5) / 0.25;
4369
+ r = Math.round(s * 255);
4370
+ g = 255;
4371
+ b = 0;
4372
+ } else {
4373
+ const s = (clamped - 0.75) / 0.25;
4374
+ r = 255;
4375
+ g = Math.round((1 - s) * 255);
4376
+ b = 0;
4377
+ }
4378
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
4379
+ }
4380
+ function renderOfPieChart(chart, x, y, w, h) {
4381
+ const parts = [];
4382
+ const series = chart.series[0];
4383
+ if (!series || series.values.length === 0) return "";
4384
+ const total = series.values.reduce((sum, v) => sum + v, 0);
4385
+ if (total === 0) return "";
4386
+ const splitPos = chart.splitPos ?? 2;
4387
+ const secondPieSize = chart.secondPieSize ?? 75;
4388
+ const isBarOfPie = chart.ofPieType === "bar";
4389
+ const splitIdx = Math.max(0, series.values.length - splitPos);
4390
+ const primaryValues = series.values.slice(0, splitIdx);
4391
+ const secondaryValues = series.values.slice(splitIdx);
4392
+ const secondaryTotal = secondaryValues.reduce((sum, v) => sum + v, 0);
4393
+ const pieW = w * 0.45;
4394
+ const pieCx = x + pieW / 2;
4395
+ const pieCy = y + h / 2;
4396
+ const pieR = Math.min(pieW, h) / 2 * 0.85;
4397
+ let currentAngle = -Math.PI / 2;
4398
+ for (let i = 0; i < primaryValues.length; i++) {
4399
+ const val = primaryValues[i];
4400
+ const sliceAngle = val / total * 2 * Math.PI;
4401
+ const color = getPieSliceColor(i, chart);
4402
+ const x1 = pieCx + pieR * Math.cos(currentAngle);
4403
+ const y1 = pieCy + pieR * Math.sin(currentAngle);
4404
+ const x2 = pieCx + pieR * Math.cos(currentAngle + sliceAngle);
4405
+ const y2 = pieCy + pieR * Math.sin(currentAngle + sliceAngle);
4406
+ const largeArc = sliceAngle > Math.PI ? 1 : 0;
4407
+ parts.push(
4408
+ `<path d="M${round3(pieCx)},${round3(pieCy)} L${round3(x1)},${round3(y1)} A${round3(pieR)},${round3(pieR)} 0 ${largeArc},1 ${round3(x2)},${round3(y2)} Z" ${fillAttr(color)}/>`
4409
+ );
4410
+ currentAngle += sliceAngle;
4411
+ }
4412
+ const otherAngleStart = currentAngle;
4413
+ const otherSliceAngle = secondaryTotal / total * 2 * Math.PI;
4414
+ const otherColor = { hex: "#D9D9D9", alpha: 1 };
4415
+ if (primaryValues.length === 0 && secondaryValues.length > 0) {
4416
+ parts.push(
4417
+ `<circle cx="${round3(pieCx)}" cy="${round3(pieCy)}" r="${round3(pieR)}" ${fillAttr(otherColor)}/>`
4418
+ );
4419
+ } else if (secondaryTotal > 0) {
4420
+ const x1 = pieCx + pieR * Math.cos(otherAngleStart);
4421
+ const y1 = pieCy + pieR * Math.sin(otherAngleStart);
4422
+ const x2 = pieCx + pieR * Math.cos(otherAngleStart + otherSliceAngle);
4423
+ const y2 = pieCy + pieR * Math.sin(otherAngleStart + otherSliceAngle);
4424
+ const largeArc = otherSliceAngle > Math.PI ? 1 : 0;
4425
+ parts.push(
4426
+ `<path d="M${round3(pieCx)},${round3(pieCy)} L${round3(x1)},${round3(y1)} A${round3(pieR)},${round3(pieR)} 0 ${largeArc},1 ${round3(x2)},${round3(y2)} Z" ${fillAttr(otherColor)}/>`
4427
+ );
4428
+ }
4429
+ const secW = w * 0.25;
4430
+ const secH = h * (secondPieSize / 100) * 0.85;
4431
+ const secX = x + w * 0.65;
4432
+ const secCy = y + h / 2;
4433
+ const lineStartX = pieCx + pieR * Math.cos(otherAngleStart);
4434
+ const lineStartY = pieCy + pieR * Math.sin(otherAngleStart);
4435
+ const lineEndStartX = pieCx + pieR * Math.cos(otherAngleStart + otherSliceAngle);
4436
+ const lineEndStartY = pieCy + pieR * Math.sin(otherAngleStart + otherSliceAngle);
4437
+ parts.push(
4438
+ `<line x1="${round3(lineStartX)}" y1="${round3(lineStartY)}" x2="${round3(secX)}" y2="${round3(secCy - secH / 2)}" stroke="#A6A6A6" stroke-width="1"/>`
4439
+ );
4440
+ parts.push(
4441
+ `<line x1="${round3(lineEndStartX)}" y1="${round3(lineEndStartY)}" x2="${round3(secX)}" y2="${round3(secCy + secH / 2)}" stroke="#A6A6A6" stroke-width="1"/>`
4442
+ );
4443
+ if (isBarOfPie) {
4444
+ let barY = secCy - secH / 2;
4445
+ for (let i = 0; i < secondaryValues.length; i++) {
4446
+ const val = secondaryValues[i];
4447
+ const barH = secondaryTotal > 0 ? val / secondaryTotal * secH : 0;
4448
+ const color = getPieSliceColor(splitIdx + i, chart);
4449
+ parts.push(
4450
+ `<rect x="${round3(secX)}" y="${round3(barY)}" width="${round3(secW)}" height="${round3(barH)}" ${fillAttr(color)}/>`
4451
+ );
4452
+ barY += barH;
4453
+ }
4454
+ } else {
4455
+ const secPieCx = secX + secW / 2;
4456
+ const secR = Math.min(secW, secH) / 2;
4457
+ let secAngle = -Math.PI / 2;
4458
+ if (secondaryValues.length === 1) {
4459
+ const color = getPieSliceColor(splitIdx, chart);
4460
+ parts.push(
4461
+ `<circle cx="${round3(secPieCx)}" cy="${round3(secCy)}" r="${round3(secR)}" ${fillAttr(color)}/>`
4462
+ );
4463
+ } else {
4464
+ for (let i = 0; i < secondaryValues.length; i++) {
4465
+ const val = secondaryValues[i];
4466
+ const sliceAngle = secondaryTotal > 0 ? val / secondaryTotal * 2 * Math.PI : 0;
4467
+ const color = getPieSliceColor(splitIdx + i, chart);
4468
+ const sx1 = secPieCx + secR * Math.cos(secAngle);
4469
+ const sy1 = secCy + secR * Math.sin(secAngle);
4470
+ const sx2 = secPieCx + secR * Math.cos(secAngle + sliceAngle);
4471
+ const sy2 = secCy + secR * Math.sin(secAngle + sliceAngle);
4472
+ const largeArc = sliceAngle > Math.PI ? 1 : 0;
4473
+ parts.push(
4474
+ `<path d="M${round3(secPieCx)},${round3(secCy)} L${round3(sx1)},${round3(sy1)} A${round3(secR)},${round3(secR)} 0 ${largeArc},1 ${round3(sx2)},${round3(sy2)} Z" ${fillAttr(color)}/>`
4475
+ );
4476
+ secAngle += sliceAngle;
4477
+ }
4478
+ }
4479
+ }
4480
+ return parts.join("");
4481
+ }
4198
4482
  function renderLegend(chart, chartW, chartH, position) {
4199
4483
  const parts = [];
4200
- const entries2 = chart.chartType === "pie" || chart.chartType === "doughnut" ? chart.categories.map((cat, i) => ({
4484
+ const entries2 = chart.chartType === "pie" || chart.chartType === "doughnut" || chart.chartType === "ofPie" ? chart.categories.map((cat, i) => ({
4201
4485
  label: cat,
4202
4486
  color: getPieSliceColor(i, chart)
4203
4487
  })) : chart.series.map((s, i) => ({
@@ -5594,7 +5878,11 @@ var CHART_TYPE_MAP = [
5594
5878
  ["bubbleChart", "bubble"],
5595
5879
  ["areaChart", "area"],
5596
5880
  ["area3DChart", "area"],
5597
- ["radarChart", "radar"]
5881
+ ["radarChart", "radar"],
5882
+ ["stockChart", "stock"],
5883
+ ["surfaceChart", "surface"],
5884
+ ["surface3DChart", "surface"],
5885
+ ["ofPieChart", "ofPie"]
5598
5886
  ];
5599
5887
  function parseChart(chartXml, colorResolver) {
5600
5888
  const parsed = parseXml(chartXml);
@@ -5605,7 +5893,17 @@ function parseChart(chartXml, colorResolver) {
5605
5893
  const plotArea = chart.plotArea;
5606
5894
  if (!plotArea) return null;
5607
5895
  const title = parseChartTitle(chart.title);
5608
- const { chartType, series, categories, barDirection, holeSize, radarStyle } = parseChartTypeAndData(plotArea, colorResolver);
5896
+ const {
5897
+ chartType,
5898
+ series,
5899
+ categories,
5900
+ barDirection,
5901
+ holeSize,
5902
+ radarStyle,
5903
+ ofPieType,
5904
+ secondPieSize,
5905
+ splitPos
5906
+ } = parseChartTypeAndData(plotArea, colorResolver);
5609
5907
  if (!chartType) return null;
5610
5908
  const legend = parseLegend(chart.legend);
5611
5909
  return {
@@ -5616,6 +5914,9 @@ function parseChart(chartXml, colorResolver) {
5616
5914
  ...barDirection !== void 0 && { barDirection },
5617
5915
  ...holeSize !== void 0 && { holeSize },
5618
5916
  ...radarStyle !== void 0 && { radarStyle },
5917
+ ...ofPieType !== void 0 && { ofPieType },
5918
+ ...secondPieSize !== void 0 && { secondPieSize },
5919
+ ...splitPos !== void 0 && { splitPos },
5619
5920
  legend
5620
5921
  };
5621
5922
  }
@@ -5634,15 +5935,23 @@ function parseChartTypeAndData(plotArea, colorResolver) {
5634
5935
  const holeSize = chartType === "doughnut" ? Number(holeSizeNode?.["@_val"] ?? 50) : void 0;
5635
5936
  const radarStyleNode = chartNode.radarStyle;
5636
5937
  const radarStyle = chartType === "radar" ? radarStyleNode?.["@_val"] ?? "standard" : void 0;
5637
- return { chartType, series, categories, barDirection, holeSize, radarStyle };
5638
- }
5639
- const knownTags = new Set(CHART_TYPE_MAP.map(([tag]) => tag));
5640
- const chartTags = ["surfaceChart", "surface3DChart", "stockChart", "ofPieChart"];
5641
- for (const tag of chartTags) {
5642
- if (!knownTags.has(tag) && plotArea[tag]) {
5643
- warn(`chart.${tag}`, `chart type "${tag}" not implemented`);
5644
- break;
5645
- }
5938
+ const ofPieTypeNode = chartNode.ofPieType;
5939
+ const ofPieType = chartType === "ofPie" ? ofPieTypeNode?.["@_val"] ?? "pie" : void 0;
5940
+ const secondPieSizeNode = chartNode.secondPieSize;
5941
+ const secondPieSize = chartType === "ofPie" ? Number(secondPieSizeNode?.["@_val"] ?? 75) : void 0;
5942
+ const splitPosNode = chartNode.splitPos;
5943
+ const splitPos = chartType === "ofPie" ? Number(splitPosNode?.["@_val"] ?? 2) : void 0;
5944
+ return {
5945
+ chartType,
5946
+ series,
5947
+ categories,
5948
+ barDirection,
5949
+ holeSize,
5950
+ radarStyle,
5951
+ ofPieType,
5952
+ secondPieSize,
5953
+ splitPos
5954
+ };
5646
5955
  }
5647
5956
  return { chartType: null, series: [], categories: [] };
5648
5957
  }
@@ -7385,7 +7694,23 @@ function findMatchingPlaceholderStyle(placeholderType, placeholderIdx, styles) {
7385
7694
  if (byIdx?.lstStyle) return byIdx.lstStyle;
7386
7695
  }
7387
7696
  const byType = styles.find((s) => s.placeholderType === placeholderType);
7388
- return byType?.lstStyle;
7697
+ if (byType?.lstStyle) return byType.lstStyle;
7698
+ const fallbackType = getPlaceholderFallbackType(placeholderType);
7699
+ if (fallbackType) {
7700
+ const byFallback = styles.find((s) => s.placeholderType === fallbackType);
7701
+ return byFallback?.lstStyle;
7702
+ }
7703
+ return void 0;
7704
+ }
7705
+ function getPlaceholderFallbackType(type) {
7706
+ switch (type) {
7707
+ case "ctrTitle":
7708
+ return "title";
7709
+ case "subTitle":
7710
+ return "body";
7711
+ default:
7712
+ return void 0;
7713
+ }
7389
7714
  }
7390
7715
  function getTxStyleForPlaceholder(placeholderType, txStyles) {
7391
7716
  if (!txStyles) return void 0;
@@ -7616,11 +7941,16 @@ var OpentypeTextMeasurer = class {
7616
7941
  const fontSizePx = fontSizePt * PX_PER_PT3;
7617
7942
  const scale = fontSizePx / font.unitsPerEm;
7618
7943
  let totalWidth = 0;
7944
+ const chars = [...text];
7619
7945
  const glyphs = font.stringToGlyphs(text);
7620
- for (const glyph of glyphs) {
7621
- totalWidth += (glyph.advanceWidth ?? font.unitsPerEm * 0.6) * scale;
7946
+ for (let i = 0; i < glyphs.length; i++) {
7947
+ let charWidth = (glyphs[i].advanceWidth ?? font.unitsPerEm * 0.6) * scale;
7948
+ const codePoint = chars[i]?.codePointAt(0);
7949
+ if (bold && (codePoint === void 0 || !isCjkCodePoint(codePoint))) {
7950
+ charWidth *= BOLD_FACTOR2;
7951
+ }
7952
+ totalWidth += charWidth;
7622
7953
  }
7623
- if (bold) totalWidth *= BOLD_FACTOR2;
7624
7954
  return totalWidth;
7625
7955
  }
7626
7956
  getLineHeightRatio(fontFamily, fontFamilyEa) {
package/dist/index.js CHANGED
@@ -2035,14 +2035,16 @@ function measureTextWidth(text, fontSizePt, bold, fontFamily, fontFamilyEa) {
2035
2035
  const codePoint = char.codePointAt(0);
2036
2036
  const isEa = isCjkCodePoint(codePoint);
2037
2037
  const metrics = isEa && eaMetrics ? eaMetrics : latinMetrics;
2038
+ let charWidth;
2038
2039
  if (metrics) {
2039
- totalWidth += measureCharMetrics(char, codePoint, baseSizePx, metrics);
2040
+ charWidth = measureCharMetrics(char, codePoint, baseSizePx, metrics);
2040
2041
  } else {
2041
- totalWidth += measureCharHeuristic(codePoint, baseSizePx);
2042
+ charWidth = measureCharHeuristic(codePoint, baseSizePx);
2042
2043
  }
2043
- }
2044
- if (bold) {
2045
- totalWidth *= BOLD_FACTOR;
2044
+ if (bold && !isEa) {
2045
+ charWidth *= BOLD_FACTOR;
2046
+ }
2047
+ totalWidth += charWidth;
2046
2048
  }
2047
2049
  return totalWidth;
2048
2050
  }
@@ -3092,7 +3094,7 @@ function renderTextDecorations(x, y, segmentWidth, fontSizePx, props) {
3092
3094
  }
3093
3095
  return lines;
3094
3096
  }
3095
- function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, fontResolver) {
3097
+ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, fontResolver, vert) {
3096
3098
  const fontSize = (props.fontSize ?? defaultFontSize) * fontScale;
3097
3099
  const fontSizePx = fontSize * PX_PER_PT2;
3098
3100
  const parts = [];
@@ -3125,7 +3127,50 @@ function renderSegmentAsPath(text, props, x, y, fontScale, defaultFontSize, font
3125
3127
  }
3126
3128
  totalWidth += segWidth;
3127
3129
  };
3128
- if (needsScriptSplit(props)) {
3130
+ const processCjkUpright = (segText, fontFamily, fontFamilyEa) => {
3131
+ if (segText.length === 0) return;
3132
+ const font = fontResolver.resolveFont(fontFamily, fontFamilyEa);
3133
+ const fillAttrs = buildPathFillAttrs(props);
3134
+ for (const char of segText) {
3135
+ const charWidth = getTextMeasurer().measureTextWidth(
3136
+ char,
3137
+ fontSize,
3138
+ props.bold,
3139
+ fontFamily,
3140
+ fontFamilyEa
3141
+ );
3142
+ if (font) {
3143
+ const charX = x + totalWidth;
3144
+ const path = font.getPath(char, charX, effectiveY, fontSizePx);
3145
+ const pathData = path.toPathData(2);
3146
+ if (pathData && pathData.length > 0) {
3147
+ const cx = charX + charWidth / 2;
3148
+ const cy = effectiveY - fontSizePx * (font.ascender + font.descender) / 2 / font.unitsPerEm;
3149
+ parts.push(
3150
+ `<g transform="rotate(-90, ${cx.toFixed(2)}, ${cy.toFixed(2)})"><path d="${pathData}" ${fillAttrs}/></g>`
3151
+ );
3152
+ }
3153
+ }
3154
+ if (props.underline || props.strikethrough) {
3155
+ parts.push(
3156
+ ...renderTextDecorations(x + totalWidth, effectiveY, charWidth, fontSizePx, props)
3157
+ );
3158
+ }
3159
+ totalWidth += charWidth;
3160
+ }
3161
+ };
3162
+ if (vert === "eaVert") {
3163
+ const scriptParts = splitByScript(processedText);
3164
+ for (const part of scriptParts) {
3165
+ const ff = part.isEa ? props.fontFamilyEa ?? props.fontFamily : props.fontFamily;
3166
+ const ffEa = part.isEa ? props.fontFamilyEa : props.fontFamilyEa;
3167
+ if (part.isEa) {
3168
+ processCjkUpright(part.text, ff, ffEa);
3169
+ } else {
3170
+ processSegment(part.text, ff, ffEa);
3171
+ }
3172
+ }
3173
+ } else if (needsScriptSplit(props)) {
3129
3174
  const scriptParts = splitByScript(processedText);
3130
3175
  for (const part of scriptParts) {
3131
3176
  const ff = part.isEa ? props.fontFamilyEa : props.fontFamily;
@@ -3291,7 +3336,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
3291
3336
  currentY,
3292
3337
  fontScale,
3293
3338
  defaultFontSize,
3294
- fontResolver
3339
+ fontResolver,
3340
+ bodyProperties.vert
3295
3341
  );
3296
3342
  if (result.svg) elements.push(result.svg);
3297
3343
  currentX += result.width;
@@ -3338,7 +3384,8 @@ function renderTextBodyAsPath(textBody, transform, fontResolver) {
3338
3384
  currentY,
3339
3385
  fontScale,
3340
3386
  defaultFontSize,
3341
- fontResolver
3387
+ fontResolver,
3388
+ bodyProperties.vert
3342
3389
  );
3343
3390
  if (result.svg) elements.push(result.svg);
3344
3391
  currentX += result.width;
@@ -3781,6 +3828,15 @@ function renderChart(element) {
3781
3828
  case "radar":
3782
3829
  parts.push(renderRadarChart(chart, plotX, plotY, plotW, plotH));
3783
3830
  break;
3831
+ case "stock":
3832
+ parts.push(renderStockChart(chart, plotX, plotY, plotW, plotH));
3833
+ break;
3834
+ case "surface":
3835
+ parts.push(renderSurfaceChart(chart, plotX, plotY, plotW, plotH));
3836
+ break;
3837
+ case "ofPie":
3838
+ parts.push(renderOfPieChart(chart, plotX, plotY, plotW, plotH));
3839
+ break;
3784
3840
  }
3785
3841
  }
3786
3842
  if (chart.legend && chart.series.length > 0) {
@@ -4152,9 +4208,237 @@ function renderRadarChart(chart, x, y, w, h) {
4152
4208
  }
4153
4209
  return parts.join("");
4154
4210
  }
4211
+ function renderStockChart(chart, x, y, w, h) {
4212
+ const parts = [];
4213
+ const { series, categories } = chart;
4214
+ if (series.length < 3) return "";
4215
+ const highSeries = series[0];
4216
+ const lowSeries = series[1];
4217
+ const closeSeries = series[2];
4218
+ const catCount = categories.length || highSeries.values.length;
4219
+ if (catCount === 0) return "";
4220
+ let maxVal = 0;
4221
+ let minVal = Infinity;
4222
+ for (const s of [highSeries, lowSeries, closeSeries]) {
4223
+ for (const v of s.values) {
4224
+ maxVal = Math.max(maxVal, v);
4225
+ minVal = Math.min(minVal, v);
4226
+ }
4227
+ }
4228
+ if (maxVal === minVal) return "";
4229
+ parts.push(
4230
+ `<line x1="${round3(x)}" y1="${round3(y + h)}" x2="${round3(x + w)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4231
+ );
4232
+ parts.push(
4233
+ `<line x1="${round3(x)}" y1="${round3(y)}" x2="${round3(x)}" y2="${round3(y + h)}" stroke="#D9D9D9" stroke-width="1"/>`
4234
+ );
4235
+ for (let c = 0; c < catCount; c++) {
4236
+ const label = categories[c] ?? "";
4237
+ const labelX = x + (c + 0.5) * (w / catCount);
4238
+ parts.push(
4239
+ `<text x="${round3(labelX)}" y="${round3(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
4240
+ );
4241
+ }
4242
+ const range = maxVal - minVal;
4243
+ for (let c = 0; c < catCount; c++) {
4244
+ const cx = x + (c + 0.5) * (w / catCount);
4245
+ const highVal = highSeries.values[c] ?? 0;
4246
+ const lowVal = lowSeries.values[c] ?? 0;
4247
+ const closeVal = closeSeries.values[c] ?? 0;
4248
+ const highY = y + h - (highVal - minVal) / range * h;
4249
+ const lowY = y + h - (lowVal - minVal) / range * h;
4250
+ const closeY = y + h - (closeVal - minVal) / range * h;
4251
+ parts.push(
4252
+ `<line x1="${round3(cx)}" y1="${round3(highY)}" x2="${round3(cx)}" y2="${round3(lowY)}" stroke="#404040" stroke-width="2"/>`
4253
+ );
4254
+ const tickW = w / catCount * 0.2;
4255
+ parts.push(
4256
+ `<line x1="${round3(cx)}" y1="${round3(closeY)}" x2="${round3(cx + tickW)}" y2="${round3(closeY)}" stroke="#404040" stroke-width="2"/>`
4257
+ );
4258
+ }
4259
+ return parts.join("");
4260
+ }
4261
+ function renderSurfaceChart(chart, x, y, w, h) {
4262
+ const parts = [];
4263
+ const { series, categories } = chart;
4264
+ if (series.length === 0) return "";
4265
+ const rows = series.length;
4266
+ const cols = categories.length || Math.max(...series.map((s) => s.values.length));
4267
+ if (cols === 0) return "";
4268
+ let minVal = Infinity;
4269
+ let maxVal = -Infinity;
4270
+ for (const s of series) {
4271
+ for (const v of s.values) {
4272
+ minVal = Math.min(minVal, v);
4273
+ maxVal = Math.max(maxVal, v);
4274
+ }
4275
+ }
4276
+ if (minVal === maxVal) maxVal = minVal + 1;
4277
+ const cellW = w / cols;
4278
+ const cellH = h / rows;
4279
+ for (let r = 0; r < rows; r++) {
4280
+ for (let c = 0; c < cols; c++) {
4281
+ const val = series[r].values[c] ?? 0;
4282
+ const t = (val - minVal) / (maxVal - minVal);
4283
+ const color = heatmapColor(t);
4284
+ const cx = x + c * cellW;
4285
+ const cy = y + r * cellH;
4286
+ parts.push(
4287
+ `<rect x="${round3(cx)}" y="${round3(cy)}" width="${round3(cellW)}" height="${round3(cellH)}" fill="${color}" stroke="#FFFFFF" stroke-width="0.5"/>`
4288
+ );
4289
+ }
4290
+ }
4291
+ for (let c = 0; c < cols; c++) {
4292
+ const label = categories[c] ?? "";
4293
+ if (label) {
4294
+ const labelX = x + (c + 0.5) * cellW;
4295
+ parts.push(
4296
+ `<text x="${round3(labelX)}" y="${round3(y + h + 15)}" text-anchor="middle" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
4297
+ );
4298
+ }
4299
+ }
4300
+ for (let r = 0; r < rows; r++) {
4301
+ const label = series[r].name ?? "";
4302
+ if (label) {
4303
+ const labelY = y + (r + 0.5) * cellH;
4304
+ parts.push(
4305
+ `<text x="${round3(x - 5)}" y="${round3(labelY + 4)}" text-anchor="end" font-size="10" fill="#595959">${escapeXml2(label)}</text>`
4306
+ );
4307
+ }
4308
+ }
4309
+ return parts.join("");
4310
+ }
4311
+ function heatmapColor(t) {
4312
+ const clamped = Math.max(0, Math.min(1, t));
4313
+ let r, g, b;
4314
+ if (clamped < 0.25) {
4315
+ const s = clamped / 0.25;
4316
+ r = 0;
4317
+ g = Math.round(s * 255);
4318
+ b = 255;
4319
+ } else if (clamped < 0.5) {
4320
+ const s = (clamped - 0.25) / 0.25;
4321
+ r = 0;
4322
+ g = 255;
4323
+ b = Math.round((1 - s) * 255);
4324
+ } else if (clamped < 0.75) {
4325
+ const s = (clamped - 0.5) / 0.25;
4326
+ r = Math.round(s * 255);
4327
+ g = 255;
4328
+ b = 0;
4329
+ } else {
4330
+ const s = (clamped - 0.75) / 0.25;
4331
+ r = 255;
4332
+ g = Math.round((1 - s) * 255);
4333
+ b = 0;
4334
+ }
4335
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
4336
+ }
4337
+ function renderOfPieChart(chart, x, y, w, h) {
4338
+ const parts = [];
4339
+ const series = chart.series[0];
4340
+ if (!series || series.values.length === 0) return "";
4341
+ const total = series.values.reduce((sum, v) => sum + v, 0);
4342
+ if (total === 0) return "";
4343
+ const splitPos = chart.splitPos ?? 2;
4344
+ const secondPieSize = chart.secondPieSize ?? 75;
4345
+ const isBarOfPie = chart.ofPieType === "bar";
4346
+ const splitIdx = Math.max(0, series.values.length - splitPos);
4347
+ const primaryValues = series.values.slice(0, splitIdx);
4348
+ const secondaryValues = series.values.slice(splitIdx);
4349
+ const secondaryTotal = secondaryValues.reduce((sum, v) => sum + v, 0);
4350
+ const pieW = w * 0.45;
4351
+ const pieCx = x + pieW / 2;
4352
+ const pieCy = y + h / 2;
4353
+ const pieR = Math.min(pieW, h) / 2 * 0.85;
4354
+ let currentAngle = -Math.PI / 2;
4355
+ for (let i = 0; i < primaryValues.length; i++) {
4356
+ const val = primaryValues[i];
4357
+ const sliceAngle = val / total * 2 * Math.PI;
4358
+ const color = getPieSliceColor(i, chart);
4359
+ const x1 = pieCx + pieR * Math.cos(currentAngle);
4360
+ const y1 = pieCy + pieR * Math.sin(currentAngle);
4361
+ const x2 = pieCx + pieR * Math.cos(currentAngle + sliceAngle);
4362
+ const y2 = pieCy + pieR * Math.sin(currentAngle + sliceAngle);
4363
+ const largeArc = sliceAngle > Math.PI ? 1 : 0;
4364
+ parts.push(
4365
+ `<path d="M${round3(pieCx)},${round3(pieCy)} L${round3(x1)},${round3(y1)} A${round3(pieR)},${round3(pieR)} 0 ${largeArc},1 ${round3(x2)},${round3(y2)} Z" ${fillAttr(color)}/>`
4366
+ );
4367
+ currentAngle += sliceAngle;
4368
+ }
4369
+ const otherAngleStart = currentAngle;
4370
+ const otherSliceAngle = secondaryTotal / total * 2 * Math.PI;
4371
+ const otherColor = { hex: "#D9D9D9", alpha: 1 };
4372
+ if (primaryValues.length === 0 && secondaryValues.length > 0) {
4373
+ parts.push(
4374
+ `<circle cx="${round3(pieCx)}" cy="${round3(pieCy)}" r="${round3(pieR)}" ${fillAttr(otherColor)}/>`
4375
+ );
4376
+ } else if (secondaryTotal > 0) {
4377
+ const x1 = pieCx + pieR * Math.cos(otherAngleStart);
4378
+ const y1 = pieCy + pieR * Math.sin(otherAngleStart);
4379
+ const x2 = pieCx + pieR * Math.cos(otherAngleStart + otherSliceAngle);
4380
+ const y2 = pieCy + pieR * Math.sin(otherAngleStart + otherSliceAngle);
4381
+ const largeArc = otherSliceAngle > Math.PI ? 1 : 0;
4382
+ parts.push(
4383
+ `<path d="M${round3(pieCx)},${round3(pieCy)} L${round3(x1)},${round3(y1)} A${round3(pieR)},${round3(pieR)} 0 ${largeArc},1 ${round3(x2)},${round3(y2)} Z" ${fillAttr(otherColor)}/>`
4384
+ );
4385
+ }
4386
+ const secW = w * 0.25;
4387
+ const secH = h * (secondPieSize / 100) * 0.85;
4388
+ const secX = x + w * 0.65;
4389
+ const secCy = y + h / 2;
4390
+ const lineStartX = pieCx + pieR * Math.cos(otherAngleStart);
4391
+ const lineStartY = pieCy + pieR * Math.sin(otherAngleStart);
4392
+ const lineEndStartX = pieCx + pieR * Math.cos(otherAngleStart + otherSliceAngle);
4393
+ const lineEndStartY = pieCy + pieR * Math.sin(otherAngleStart + otherSliceAngle);
4394
+ parts.push(
4395
+ `<line x1="${round3(lineStartX)}" y1="${round3(lineStartY)}" x2="${round3(secX)}" y2="${round3(secCy - secH / 2)}" stroke="#A6A6A6" stroke-width="1"/>`
4396
+ );
4397
+ parts.push(
4398
+ `<line x1="${round3(lineEndStartX)}" y1="${round3(lineEndStartY)}" x2="${round3(secX)}" y2="${round3(secCy + secH / 2)}" stroke="#A6A6A6" stroke-width="1"/>`
4399
+ );
4400
+ if (isBarOfPie) {
4401
+ let barY = secCy - secH / 2;
4402
+ for (let i = 0; i < secondaryValues.length; i++) {
4403
+ const val = secondaryValues[i];
4404
+ const barH = secondaryTotal > 0 ? val / secondaryTotal * secH : 0;
4405
+ const color = getPieSliceColor(splitIdx + i, chart);
4406
+ parts.push(
4407
+ `<rect x="${round3(secX)}" y="${round3(barY)}" width="${round3(secW)}" height="${round3(barH)}" ${fillAttr(color)}/>`
4408
+ );
4409
+ barY += barH;
4410
+ }
4411
+ } else {
4412
+ const secPieCx = secX + secW / 2;
4413
+ const secR = Math.min(secW, secH) / 2;
4414
+ let secAngle = -Math.PI / 2;
4415
+ if (secondaryValues.length === 1) {
4416
+ const color = getPieSliceColor(splitIdx, chart);
4417
+ parts.push(
4418
+ `<circle cx="${round3(secPieCx)}" cy="${round3(secCy)}" r="${round3(secR)}" ${fillAttr(color)}/>`
4419
+ );
4420
+ } else {
4421
+ for (let i = 0; i < secondaryValues.length; i++) {
4422
+ const val = secondaryValues[i];
4423
+ const sliceAngle = secondaryTotal > 0 ? val / secondaryTotal * 2 * Math.PI : 0;
4424
+ const color = getPieSliceColor(splitIdx + i, chart);
4425
+ const sx1 = secPieCx + secR * Math.cos(secAngle);
4426
+ const sy1 = secCy + secR * Math.sin(secAngle);
4427
+ const sx2 = secPieCx + secR * Math.cos(secAngle + sliceAngle);
4428
+ const sy2 = secCy + secR * Math.sin(secAngle + sliceAngle);
4429
+ const largeArc = sliceAngle > Math.PI ? 1 : 0;
4430
+ parts.push(
4431
+ `<path d="M${round3(secPieCx)},${round3(secCy)} L${round3(sx1)},${round3(sy1)} A${round3(secR)},${round3(secR)} 0 ${largeArc},1 ${round3(sx2)},${round3(sy2)} Z" ${fillAttr(color)}/>`
4432
+ );
4433
+ secAngle += sliceAngle;
4434
+ }
4435
+ }
4436
+ }
4437
+ return parts.join("");
4438
+ }
4155
4439
  function renderLegend(chart, chartW, chartH, position) {
4156
4440
  const parts = [];
4157
- const entries2 = chart.chartType === "pie" || chart.chartType === "doughnut" ? chart.categories.map((cat, i) => ({
4441
+ const entries2 = chart.chartType === "pie" || chart.chartType === "doughnut" || chart.chartType === "ofPie" ? chart.categories.map((cat, i) => ({
4158
4442
  label: cat,
4159
4443
  color: getPieSliceColor(i, chart)
4160
4444
  })) : chart.series.map((s, i) => ({
@@ -5551,7 +5835,11 @@ var CHART_TYPE_MAP = [
5551
5835
  ["bubbleChart", "bubble"],
5552
5836
  ["areaChart", "area"],
5553
5837
  ["area3DChart", "area"],
5554
- ["radarChart", "radar"]
5838
+ ["radarChart", "radar"],
5839
+ ["stockChart", "stock"],
5840
+ ["surfaceChart", "surface"],
5841
+ ["surface3DChart", "surface"],
5842
+ ["ofPieChart", "ofPie"]
5555
5843
  ];
5556
5844
  function parseChart(chartXml, colorResolver) {
5557
5845
  const parsed = parseXml(chartXml);
@@ -5562,7 +5850,17 @@ function parseChart(chartXml, colorResolver) {
5562
5850
  const plotArea = chart.plotArea;
5563
5851
  if (!plotArea) return null;
5564
5852
  const title = parseChartTitle(chart.title);
5565
- const { chartType, series, categories, barDirection, holeSize, radarStyle } = parseChartTypeAndData(plotArea, colorResolver);
5853
+ const {
5854
+ chartType,
5855
+ series,
5856
+ categories,
5857
+ barDirection,
5858
+ holeSize,
5859
+ radarStyle,
5860
+ ofPieType,
5861
+ secondPieSize,
5862
+ splitPos
5863
+ } = parseChartTypeAndData(plotArea, colorResolver);
5566
5864
  if (!chartType) return null;
5567
5865
  const legend = parseLegend(chart.legend);
5568
5866
  return {
@@ -5573,6 +5871,9 @@ function parseChart(chartXml, colorResolver) {
5573
5871
  ...barDirection !== void 0 && { barDirection },
5574
5872
  ...holeSize !== void 0 && { holeSize },
5575
5873
  ...radarStyle !== void 0 && { radarStyle },
5874
+ ...ofPieType !== void 0 && { ofPieType },
5875
+ ...secondPieSize !== void 0 && { secondPieSize },
5876
+ ...splitPos !== void 0 && { splitPos },
5576
5877
  legend
5577
5878
  };
5578
5879
  }
@@ -5591,15 +5892,23 @@ function parseChartTypeAndData(plotArea, colorResolver) {
5591
5892
  const holeSize = chartType === "doughnut" ? Number(holeSizeNode?.["@_val"] ?? 50) : void 0;
5592
5893
  const radarStyleNode = chartNode.radarStyle;
5593
5894
  const radarStyle = chartType === "radar" ? radarStyleNode?.["@_val"] ?? "standard" : void 0;
5594
- return { chartType, series, categories, barDirection, holeSize, radarStyle };
5595
- }
5596
- const knownTags = new Set(CHART_TYPE_MAP.map(([tag]) => tag));
5597
- const chartTags = ["surfaceChart", "surface3DChart", "stockChart", "ofPieChart"];
5598
- for (const tag of chartTags) {
5599
- if (!knownTags.has(tag) && plotArea[tag]) {
5600
- warn(`chart.${tag}`, `chart type "${tag}" not implemented`);
5601
- break;
5602
- }
5895
+ const ofPieTypeNode = chartNode.ofPieType;
5896
+ const ofPieType = chartType === "ofPie" ? ofPieTypeNode?.["@_val"] ?? "pie" : void 0;
5897
+ const secondPieSizeNode = chartNode.secondPieSize;
5898
+ const secondPieSize = chartType === "ofPie" ? Number(secondPieSizeNode?.["@_val"] ?? 75) : void 0;
5899
+ const splitPosNode = chartNode.splitPos;
5900
+ const splitPos = chartType === "ofPie" ? Number(splitPosNode?.["@_val"] ?? 2) : void 0;
5901
+ return {
5902
+ chartType,
5903
+ series,
5904
+ categories,
5905
+ barDirection,
5906
+ holeSize,
5907
+ radarStyle,
5908
+ ofPieType,
5909
+ secondPieSize,
5910
+ splitPos
5911
+ };
5603
5912
  }
5604
5913
  return { chartType: null, series: [], categories: [] };
5605
5914
  }
@@ -7342,7 +7651,23 @@ function findMatchingPlaceholderStyle(placeholderType, placeholderIdx, styles) {
7342
7651
  if (byIdx?.lstStyle) return byIdx.lstStyle;
7343
7652
  }
7344
7653
  const byType = styles.find((s) => s.placeholderType === placeholderType);
7345
- return byType?.lstStyle;
7654
+ if (byType?.lstStyle) return byType.lstStyle;
7655
+ const fallbackType = getPlaceholderFallbackType(placeholderType);
7656
+ if (fallbackType) {
7657
+ const byFallback = styles.find((s) => s.placeholderType === fallbackType);
7658
+ return byFallback?.lstStyle;
7659
+ }
7660
+ return void 0;
7661
+ }
7662
+ function getPlaceholderFallbackType(type) {
7663
+ switch (type) {
7664
+ case "ctrTitle":
7665
+ return "title";
7666
+ case "subTitle":
7667
+ return "body";
7668
+ default:
7669
+ return void 0;
7670
+ }
7346
7671
  }
7347
7672
  function getTxStyleForPlaceholder(placeholderType, txStyles) {
7348
7673
  if (!txStyles) return void 0;
@@ -7573,11 +7898,16 @@ var OpentypeTextMeasurer = class {
7573
7898
  const fontSizePx = fontSizePt * PX_PER_PT3;
7574
7899
  const scale = fontSizePx / font.unitsPerEm;
7575
7900
  let totalWidth = 0;
7901
+ const chars = [...text];
7576
7902
  const glyphs = font.stringToGlyphs(text);
7577
- for (const glyph of glyphs) {
7578
- totalWidth += (glyph.advanceWidth ?? font.unitsPerEm * 0.6) * scale;
7903
+ for (let i = 0; i < glyphs.length; i++) {
7904
+ let charWidth = (glyphs[i].advanceWidth ?? font.unitsPerEm * 0.6) * scale;
7905
+ const codePoint = chars[i]?.codePointAt(0);
7906
+ if (bold && (codePoint === void 0 || !isCjkCodePoint(codePoint))) {
7907
+ charWidth *= BOLD_FACTOR2;
7908
+ }
7909
+ totalWidth += charWidth;
7579
7910
  }
7580
- if (bold) totalWidth *= BOLD_FACTOR2;
7581
7911
  return totalWidth;
7582
7912
  }
7583
7913
  getLineHeightRatio(fontFamily, fontFamilyEa) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pptx-glimpse",
3
- "version": "0.1.4",
3
+ "version": "0.2.1",
4
4
  "description": "A Node.js library to render PPTX as SVG / PNG.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -62,7 +62,7 @@
62
62
  "author": "hirokisakabe",
63
63
  "license": "MIT",
64
64
  "dependencies": {
65
- "fast-xml-parser": "^4.5.0",
65
+ "fast-xml-parser": "^5.3.6",
66
66
  "fflate": "^0.8.2",
67
67
  "opentype.js": "^1.3.4",
68
68
  "sharp": "^0.34.5"