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